T-ZEPK-harness-install-dev-link-mode
Status: closed/done · Impact: medium · Complexity: medium
Make the live-source dev loop (D-0014 point 12) a one-liner: from a source
checkout, sdlc harness install claude in a peer project should register
the harness against this checkout’s source dir so the peer’s skills track
live edits — with no copy, no rebuild. Detect the source-vs-installed case
automatically (the same signal the launcher already uses) and let an
explicit --mode override it.
T-ZF8U-harness-install-claude-verb shipped sdlc harness install claude
with two modes, both of which produce a portable, committable
registration; neither points at a sibling source checkout, so live-source
consumers can’t get the skills without copying.
| Location | Role today |
|---|---|
plugin/lib/services/harness/ops/install-claude.ts | The op: --mode copy (materialize into .sdlc/plugin/) / --mode node-modules (point at node_modules/@sksizer/sdlc/plugin); merges the sdlc marketplace + enabledPlugins["sdlc@sdlc"] into .claude/settings.json |
plugin/lib/services/harness/ops/_install.ts#resolvePluginRoot | Resolves the running harness root — the abstract anchor a link mode reuses |
plugin/lib/util/runtime.ts#moduleExt | .ts (source) vs .js (built artifact) — the dev-vs-installed signal, already used by the launcher |
plugin/lib/services/project/ops/doctor.ts | Same-install check: registered path’s version vs running CLI’s; copy-drift vs the stamp |
Proposed
Section titled “Proposed”A third --mode link (with --dev as sugar) registers the running
harness root’s absolute path as a source: directory marketplace. The op
auto-detects the default mode: running from source → link; from an
installed artifact → copy. Because link writes a machine-specific
path, its stanza goes in .claude/settings.local.json (gitignored), never
the committed .claude/settings.json. Switching modes is idempotent and
mutually exclusive. doctor treats a link install as always-current.
Approach
Section titled “Approach”- Detect dev-vs-installed. Source when
moduleExt(import.meta.url)is.tsORresolvePluginRoot()’s path is not under anode_modules/segment; else installed. Expose as a small helper so the op and doctor share it. --mode link(+--devalias). Marketplace path = the absolute path fromresolvePluginRoot()(the abstract harness root — do NOT hardcodeplugin/, so this survives the D-0001plugin/→ per-targetplugin/claude/move). No file materialization, no stamp.- Default = detected mode when
--modeabsent; explicit--modeoverrides. Report the resolved mode + path (HARNESS: claude installed (link) at <abs path>). - Route link config to
.claude/settings.local.json(create if absent; same merge/preserve semantics as the committed file). copy / node-modules stay in.claude/settings.json. Ensure.gitignorecoverssettings.local.jsonif the project doesn’t already. - Idempotent + mutually exclusive. On any install, remove a prior
sdlcmarketplace entry from both settings files before writing the chosen one, so switching copy↔link↔node-modules leaves exactly one registration. A no-op re-run makes zero changes. - doctor. A link registration reports a healthy
dev/linkedstate: version is the live source’s (always current), the copy-drift check is skipped. Unregistered stays neutral INFO. - Tests + README: link install writes the local-settings stanza pointing at the source root; auto-detect picks link from a source run and copy from a simulated installed run; mode-switch removes the prior entry; doctor passes on a link install. Document the peer-project dev loop.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/services/harness/ops/install-claude.ts | modify | link mode + --dev; auto-detect default; local-settings routing; mode-switch cleanup |
plugin/lib/services/harness/ops/_install.ts | modify | shared isSourceInstall() detection helper; local-settings path constant |
plugin/lib/services/project/ops/doctor.ts | modify | link install → always-current dev/linked state |
plugin/lib/services/harness/tests/ | modify | link mode, auto-detect both ways, mode-switch, local-settings target |
plugin/lib/services/project/tests/ | modify | doctor on a link install |
README.md | modify | the peer-project live-source dev loop |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: run from a source checkout with no
--mode,harness install claude --project-root <peer>registers the checkout’s harness root as asource: directorymarketplace in<peer>/.claude/settings.local.json(notsettings.json), enablessdlc@sdlc, and prints thelinkmarker. - AC-2:
--mode copyfrom the same source run still produces the portable.sdlc/plugin/copy install in the committedsettings.json(explicit override wins). - AC-3: auto-detect picks
copywhen the op runs from an installed artifact (simulated: plugin root undernode_modules,.jsmodule). - AC-4: switching modes (copy→link and back) leaves exactly one
sdlcmarketplace registration across both settings files; a no-op re-run changes nothing. - AC-5:
project doctoron a link install reports healthy (no drift), naming the linked source path. - AC-6:
bunx tsc --noEmit,bun test plugin/lib/services/harness plugin/lib/services/project, andgate runtime-agnosticall clean.
Out of scope
Section titled “Out of scope”- The derived-plugin transform pipeline (D-0014 point 4): once
plugin/claudeis generated from a canonical agnostic source,linkshould point at the build output with freshness handling (rebuild-on-edit / doctor staleness). This task links the harness root as it resolves today (hand-authored); it must not hardcodeplugin/, but building the transform + freshness is a later task. - The
SDLC_HOMElauncher override (D-0014 point 12) and a PATH-install helper (sdlc dev-link) — sibling live-source ergonomics, separate tasks. - Non-Claude harness targets.
Dependencies
Section titled “Dependencies”- Builds directly on T-ZF8U-harness-install-claude-verb (the op, modes, and settings-merge it extends) — shipped via #699. No blocking deps.