T-ZF8U-harness-install-claude-verb
Status: closed/done · Impact: medium · Complexity: medium
D-0014 makes onboarding a CLI concern, including installing the control
plane: after bun add/npm i, one verb wires the Claude harness into a
consumer project. Today that wiring is hand-edited .claude/settings.json
(the dev repo’s own stanza is the only exemplar), and nothing detects a
registered plugin that has drifted from the installed package. This task
adds the install verb and the doctor check that keeps the two in
agreement.
| Location | Role today |
|---|---|
.claude/settings.json | the dev repo’s hand-written extraKnownMarketplaces stanza (source: directory, path: ./plugin) + enabledPlugins {"sdlc@sdlc": true} — the shape the verb must produce |
plugin/lib/services/plugin/ | the plugin service noun: info-report.ts (resolves plugin root, CLAUDE_PLUGIN_ROOT-first), install-permissions op — the natural home for the new op |
plugin/lib/services/project/ops/doctor.ts | sdlc project doctor: probes git/gh/claude binaries and runtime; no plugin-registration check |
plugin/lib/services/project/ops/setup.ts | sdlc project setup: scaffolds docs/planning/<plural>/; the install verb slots beside it in onboarding order |
plugin/skills/info/SKILL.md | /sdlc:info diagnoses “which install won” — the interactive counterpart of the doctor check |
D-0014’s open question: the installed plugin’s home — repo-local copy
(leading candidate, .sdlc/plugin/) vs a marketplace pointer into
node_modules — is decided by the pilot (T-521G); the verb must
support both so the pilot can compare.
Proposed
Section titled “Proposed”sdlc harness install claude [--mode copy|node-modules] [--project-root …]
wires a consumer project idempotently: copy (default) materializes the
plugin tree at <projectRoot>/.sdlc/plugin/ with a provenance stamp and
registers that path; node-modules registers
./node_modules/@sksizer/sdlc/plugin directly. sdlc project doctor
gains a same-install check: the registered marketplace path’s plugin
version must equal the running CLI’s, and (copy mode) the materialized
copy must not have drifted from its stamp. A future
sdlc harness install <other> is a sibling op module, nothing more.
Approach
Section titled “Approach”- Add op
plugin/lib/services/harness/ops/install-claude.ts(path: ['harness','install','claude'], newharnessservice noun). Behavior: a. Resolve the source plugin root (CLAUDE_PLUGIN_ROOT→ else package-relative via the existinginfo-report.tsresolution). b.--mode copy(default): rsync-style copy of the plugin tree to<projectRoot>/.sdlc/plugin/(delete-extraneous, preserve executable bits); write.sdlc/plugin/.installed.json{version, sourcePath, installedAt, fileCount}. c. Merge — never overwrite —.claude/settings.json: add thesdlcmarketplace (source: directory, path per mode) andenabledPlugins["sdlc@sdlc"] = true, preserving all unrelated keys; report a stable stdout marker (HARNESS: claude installed (<mode>) at <path>). d. Idempotent: a second run with identical inputs makes no file changes and says so. - Ensure
.sdlc/is gitignored in the consumer (append if missing — same mechanismproject setupuses for its scaffolding). - Extend
sdlc project doctor: read.claude/settings.json; if ansdlcmarketplace is registered, compare the registered path’s.claude-plugin/plugin.jsonversion against the running CLI’s version — mismatch is a FAIL line naming both paths and versions; in copy mode also flag drift when the tree’s file count differs from the.installed.jsonstamp. Unregistered harness stays a neutral INFO (CLI-only installs are legitimate). - Tests: fixture project dir — install (copy) writes stanza + tree +
stamp; re-run is a no-op;
--mode node-moduleswrites the pointer stanza only; settings merge preserves pre-existing hooks/keys; doctor passes on a fresh install, fails on a version-tampered copy. - Document the onboarding sequence (
bun add -d @sksizer/sdlc→sdlc project setup→sdlc harness install claude) in the README distribution section.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/services/harness/ops/install-claude.ts | new | the install op (both modes, settings merge, provenance stamp, idempotence) |
plugin/lib/services/harness/tests/ | new | op tests per Approach step 4 |
plugin/lib/services/project/ops/doctor.ts | modify | same-install check (version parity + copy-drift) |
plugin/lib/services/project/tests/ | modify | doctor same-install cases (pass, version mismatch, drifted copy) |
plugin/lib/services/project/ops/setup.ts | modify | .sdlc/ gitignore assurance shared with the install op (extract or reuse helper) |
README.md | modify | consumer onboarding sequence |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: in a fixture project,
sdlc harness install claudeexits 0, materializes.sdlc/plugin/with.installed.json, and.claude/settings.jsongains the marketplace + enabledPlugins entries with all pre-existing keys intact. - AC-2: an immediate second run reports idempotence and produces zero file diffs (asserted on the fixture tree).
- AC-3:
--mode node-moduleswrites a stanza pointing at./node_modules/@sksizer/sdlc/pluginand materializes nothing under.sdlc/. - AC-4:
sdlc project doctorpasses after a fresh install and emits a FAIL line naming both versions when the registered copy’splugin.jsonversion is tampered. - AC-5: the op registry lists
harness install claudeinsdlc --helpoutput.
Out of scope
Section titled “Out of scope”- Choosing between the two modes —
T-521Gpilots both and records the default in D-0014. - Uninstall/upgrade verbs (upgrade = re-run install after bumping the dep; a dedicated verb can come later if the pilot shows the need).
- Other harness targets (
gemini, …) — the op layout leaves room; no speculative work. - Claude-side verification that the plugin actually loads from the
registered path — pilot concern (
T-521G).
Dependencies
Section titled “Dependencies”- none hard to start (developable against the dev repo’s own plugin
tree). Meaningful end-to-end only with an installed artifact —
T-XTGT— and decided byT-521G.
Discovery context
Section titled “Discovery context”- D-0014 Migration step 5 and the “Where the installed plugin lives” open question (repo-local copy as leading candidate).