Files
“mkczarkowski” 1414b2e6c2 feat(cli): tool-switch migration flow with sentinel cleanup
Wave 2.1 PR2 closes the UX gap where students who switch tools would
silently accumulate orphan artifacts from their old profile. When
resolveToolProfile settles on a new tool and a prior tool's manifest
exists under a different manifestDir, the CLI now prompts once per
orphan: migrate (recommended), remove 10x artifacts, or keep both
(don't ask again).

- findOrphanedManifests in writer.ts returns structured OrphanInfo
  (profile, manifestPath, parsed manifest). Corrupt manifests are
  skipped so migration never moves files without a file list.
- ToolConfig gains acknowledgedOrphans: string[] with shape validation
  on read; readToolConfig drops the field when it isn't a string array.
- tool-switch.ts implements migrateArtifacts and deleteArtifacts:
    - isSafeName gate on every manifest entry (skills, prompts, configs)
      before any filesystem operation.
    - moveIfSafe refuses symlinked sources, compares existing destinations
      byte-for-byte (readFileSync + Buffer.equals) so two distinct binary
      payloads that decode to U+FFFD never collapse as equal.
    - EXDEV cross-device fallback writes to <to>.tmp then renames into
      place; post-copy rmSync failure is reported via summary.skipped
      while still counting the file as moved. Non-EXDEV rename errors
      propagate.
    - deleteArtifacts scopes removal to the 10x-written surface — a
      Copilot orphan no longer wipes .github/workflows; only manifest-
      listed files plus the now-empty skills/prompts/config-templates
      subdirs are removed. Partial-failure contract documented.
- removeRulesBlockWithMarkers in sentinel-migration.ts strips the 10x
  block from the old rules file, collapsing the splice to at most one
  blank line and handling CRLF endings. Symmetric to
  applyRulesBlockWithMarkers; apply + remove round-trips cleanly.
- tool-prompt.ts wires handleToolSwitch into resolveToolProfile for TTY
  flows; the "keep" branch spreads the existing config so unknown
  fields (future CLI versions, hand-edits) survive. The first-run
  prompt's saveToolConfig also spreads existing config.
- commands/get.ts keeps the legacy verbose orphan warning for non-TTY
  paths (CI, Docker); TTY flows get the interactive prompt instead.
- config.ts: writeJsonAtomic shared tmp+rename helper used by both
  saveAuth and saveToolConfig — saveToolConfig is now atomic too, so a
  crash mid-write can no longer corrupt acknowledgedOrphans state.
- fs-utils.ts: readFileOrNull hoisted out of tool-switch for reuse.

Plans:
- thoughts/shared/plans/2026-04-17-wave2.1-cli-polish.md (Phases 3-4)
- thoughts/shared/plans/2026-04-19-wave2.1-cli-polish-review-fixes.md
- thoughts/shared/plans/2026-04-20-wave2.1-cli-polish-review-fixes-review-fixes.md

bun test: 328 pass, 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 10:16:25 +02:00
..