159 lines
8.3 KiB
Markdown
159 lines
8.3 KiB
Markdown
# Install UX Hardening — IUH-M01 Session Notes
|
|
|
|
## Session: 2026-04-05 (agent-ad6b6696)
|
|
|
|
### Plan
|
|
|
|
**Manifest schema decision:**
|
|
|
|
- Version 1 JSON at `~/.config/mosaic/.install-manifest.json` (mode 0600)
|
|
- Written by `tools/install.sh` after successful install
|
|
- Fields: version, installedAt, cliVersion, frameworkVersion, mutations{directories, npmGlobalPackages, npmrcLines, shellProfileEdits, runtimeAssetCopies}
|
|
- Uninstall reads it; if missing → heuristic mode (warn user)
|
|
|
|
**File list:**
|
|
|
|
- NEW: `packages/mosaic/src/runtime/install-manifest.ts` — read/write helpers + types
|
|
- NEW: `packages/mosaic/src/runtime/install-manifest.spec.ts` — unit tests
|
|
- NEW: `packages/mosaic/src/commands/uninstall.ts` — command implementation
|
|
- NEW: `packages/mosaic/src/commands/uninstall.spec.ts` — unit tests
|
|
- MOD: `packages/mosaic/src/cli.ts` — register `uninstall` command
|
|
- MOD: `tools/install.sh` — write manifest on success + add `--uninstall` path
|
|
|
|
**Runtime asset list (from mosaic-link-runtime-assets / framework/install.sh):**
|
|
|
|
- `~/.claude/CLAUDE.md` (source: `$MOSAIC_HOME/runtime/claude/CLAUDE.md`)
|
|
- `~/.claude/settings.json` (source: `$MOSAIC_HOME/runtime/claude/settings.json`)
|
|
- `~/.claude/hooks-config.json` (source: `$MOSAIC_HOME/runtime/claude/hooks-config.json`)
|
|
- `~/.claude/context7-integration.md` (source: `$MOSAIC_HOME/runtime/claude/context7-integration.md`)
|
|
- `~/.config/opencode/AGENTS.md` (source: `$MOSAIC_HOME/runtime/opencode/AGENTS.md`)
|
|
- `~/.codex/instructions.md` (source: `$MOSAIC_HOME/runtime/codex/instructions.md`)
|
|
|
|
**Reversal logic:**
|
|
|
|
1. If `.mosaic-bak-<stamp>` exists for a file → restore it
|
|
2. Else if managed copy exists → remove it
|
|
3. Never touch files not in the known list
|
|
|
|
**npmrc reversal:**
|
|
|
|
- Only remove line `@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/`
|
|
- If manifest has the line, use that as authoritative; else check heuristically
|
|
|
|
**PATH reversal:**
|
|
|
|
- Check install.sh: it does NOT add PATH entries to shell profiles (framework/install.sh migration removes old `$MOSAIC_HOME/bin` PATH entries in v0/v1→v2 migration, but new install does NOT add PATH)
|
|
- ASSUMPTION: No PATH edits in current install (v0.0.24+). Shell profiles not modified by current install.
|
|
- The `$PREFIX/bin` is mentioned in a warning but NOT added to shell profiles by install.sh.
|
|
- shellProfileEdits array will be empty for new installs; heuristic mode also skips it.
|
|
|
|
**Test strategy:**
|
|
|
|
- Unit test manifest read/write with temp dir mocking
|
|
- Unit test command registration
|
|
- Unit test dry-run flag (no actual fs mutations)
|
|
- Unit test --keep-data skips protected paths
|
|
- Unit test heuristic mode warning
|
|
|
|
**Implementation order:**
|
|
|
|
1. install-manifest.ts helpers
|
|
2. install-manifest.spec.ts tests
|
|
3. uninstall.ts command
|
|
4. uninstall.spec.ts tests
|
|
5. cli.ts registration
|
|
6. tools/install.sh manifest writing + --uninstall path
|
|
|
|
ASSUMPTION: No PATH modifications in current install.sh (v0.0.24). Framework v0/v1→v2 migration cleaned old PATH entries but current install does not add new ones.
|
|
ASSUMPTION: `--uninstall` in install.sh handles framework + cli + npmrc only; gateway teardown deferred to `mosaic gateway uninstall`.
|
|
ASSUMPTION: Pi settings.json edits (skills paths) added by framework/install.sh are NOT reversed in this iteration — too risky to touch user Pi config without manifest evidence. Noted as follow-up.
|
|
|
|
---
|
|
|
|
## Session 2 — 2026-04-05 (orchestrator resume)
|
|
|
|
### IUH-M01 completion summary
|
|
|
|
- **PR:** #429 merged as `25cada77`
|
|
- **CI:** green (Woodpecker)
|
|
- **Issue:** #425 closed
|
|
- **Files:** +1205 lines across 4 new + 2 modified + 1 docs
|
|
- **Tests:** 14 new, 170 total passing
|
|
|
|
### Follow-ups captured from worker report
|
|
|
|
1. **Pi settings.json reversal deferred** — worker flagged as too risky without manifest evidence. Future IUH task should add manifest entries for Pi settings mutations. Not blocking M02/M03.
|
|
2. **Pre-existing `cli-smoke.spec.ts` failure** — `@mosaicstack/brain` package entry resolution fails in Vitest. Unrelated to IUH-M01. Worth a separate issue later.
|
|
3. **`pr-create.sh` wrapper bug with multiline bodies** — wrapper evals body args as shell when they contain newlines/paths. Worker fell back to Gitea REST API. Same class of bug I hit earlier with `issue-create.sh`. Worth a tooling-team issue to fix both wrappers.
|
|
|
|
### Mission doc sync
|
|
|
|
cli-unification docs that were archived before the M01 subagent ran did not travel into the M01 PR (they were local, stashed before pull). Re-applying now:
|
|
|
|
- `docs/archive/missions/cli-unification-20260404/` (the old manifest + tasks)
|
|
- `docs/MISSION-MANIFEST.md` (new install-ux-hardening content)
|
|
- `docs/TASKS.md` (new install-ux-hardening content)
|
|
|
|
Committing as `docs: scaffold install-ux-hardening mission + archive cli-unification`.
|
|
|
|
### Next action
|
|
|
|
Delegate IUH-M02 to a sonnet subagent in an isolated worktree.
|
|
|
|
---
|
|
|
|
## Session 3: 2026-04-05 (agent-a6ff34a5) — IUH-M02 Wizard Remediation
|
|
|
|
### Plan
|
|
|
|
**AC-3: Password masking + confirmation**
|
|
|
|
- New `packages/mosaic/src/prompter/masked-prompt.ts` — raw-mode stdin reader that suppresses echo, handles backspace/Ctrl+C/Enter.
|
|
- `bootstrapFirstUser` in `packages/mosaic/src/commands/gateway/install.ts`: replace `rl.question('Admin password...')` with `promptMaskedPassword()`, require confirm pass, keep min-8 validation.
|
|
- Headless path: when `MOSAIC_ASSUME_YES=1` or `!process.stdin.isTTY`, read `MOSAIC_ADMIN_PASSWORD` env var directly.
|
|
|
|
**AC-4a: Hooks preview stage**
|
|
|
|
- New `packages/mosaic/src/stages/hooks-preview.ts` — reads `hooks-config.json` from `state.sourceDir` or `state.mosaicHome`, displays each top-level hook category with name/trigger/command preview, prompts "Install these hooks? [Y/n]", stores result in `state.hooks`.
|
|
- `packages/mosaic/src/types.ts` — add `hooks?: { accepted: boolean; acceptedAt?: string }` to `WizardState`.
|
|
- `packages/mosaic/src/wizard.ts` — insert `hooksPreviewStage` between `runtimeSetupStage` and `skillsSelectStage`; skip if no claude runtime detected.
|
|
|
|
**AC-4b: `mosaic config hooks` subcommands**
|
|
|
|
- Add `hooks` subcommand group to `packages/mosaic/src/commands/config.ts`:
|
|
- `list`: reads `~/.claude/hooks-config.json`, shows hook names and enabled/disabled status
|
|
- `disable <name>`: prefixes matching hook key with `_disabled_` in the JSON
|
|
- `enable <name>`: removes `_disabled_` prefix if present
|
|
|
|
**AC-5: Headless install path**
|
|
|
|
- `runConfigWizard`: detect headless mode (`MOSAIC_ASSUME_YES=1` or `!process.stdin.isTTY`), read env vars with defaults, validate required vars, skip prompts entirely.
|
|
- `bootstrapFirstUser`: detect headless mode, read `MOSAIC_ADMIN_NAME/EMAIL/PASSWORD`, validate, proceed without prompts.
|
|
- Document env vars in `packages/mosaic/README.md` (create if absent).
|
|
|
|
### File list
|
|
|
|
NEW:
|
|
|
|
- `packages/mosaic/src/prompter/masked-prompt.ts`
|
|
- `packages/mosaic/src/prompter/masked-prompt.spec.ts`
|
|
- `packages/mosaic/src/stages/hooks-preview.ts`
|
|
- `packages/mosaic/src/stages/hooks-preview.spec.ts`
|
|
|
|
MODIFIED:
|
|
|
|
- `packages/mosaic/src/types.ts` — extend WizardState
|
|
- `packages/mosaic/src/wizard.ts` — wire hooksPreviewStage
|
|
- `packages/mosaic/src/commands/gateway/install.ts` — masked password + headless path
|
|
- `packages/mosaic/src/commands/config.ts` — add hooks subcommands
|
|
- `packages/mosaic/src/commands/config.spec.ts` — extend tests
|
|
- `packages/mosaic/README.md` — document env vars
|
|
|
|
### Assumptions
|
|
|
|
ASSUMPTION: `hooks-config.json` location is `<sourceDir>/framework/runtime/claude/hooks-config.json` during wizard (sourceDir is package root). Fall back to `<mosaicHome>/runtime/claude/hooks-config.json` for installed config.
|
|
ASSUMPTION: The `hooks` subcommands under `config` operate on `~/.claude/hooks-config.json` (the installed copy), not the package source.
|
|
ASSUMPTION: For the hooks preview stage, the "name" field displayed per hook entry is the top-level event key (e.g. "PostToolUse") plus the matcher from nested hooks array. This is the most user-readable representation given the hooks-config.json structure.
|
|
ASSUMPTION: `config hooks list/enable/disable` use `CLAUDE_HOME` env or `~/.claude` as the target directory for hooks files.
|
|
ASSUMPTION: The headless TTY detection (`!process.stdin.isTTY`) is sufficient; `MOSAIC_ASSUME_YES=1` is an explicit override for cases where stdin is a TTY but the user still wants non-interactive (e.g., scripted installs with piped terminal).
|