docks-kit 0.1.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +7 -5
  3. package/cli/docs/flags.md +1 -0
  4. package/cli/docs/install.md +9 -11
  5. package/cli/docs/overview.md +6 -0
  6. package/cli/docs/platforms.md +9 -10
  7. package/cli/src/commands/model.ts +7 -3
  8. package/cli/src/commands/sync.ts +14 -3
  9. package/cli/src/commands/toolchain.ts +7 -3
  10. package/cli/src/engine-native/DESIGN.md +79 -2
  11. package/cli/src/engine-native/claudeModel.ts +9 -3
  12. package/cli/src/engine-native/claudeSync.ts +220 -116
  13. package/cli/src/engine-native/codexSync.ts +131 -77
  14. package/cli/src/engine-native/codexToml.ts +12 -5
  15. package/cli/src/engine-native/deps.ts +325 -0
  16. package/cli/src/engine-native/exec.ts +35 -2
  17. package/cli/src/engine-native/index.ts +48 -13
  18. package/cli/src/engine-native/logger.ts +35 -0
  19. package/cli/src/engine-native/models.ts +22 -23
  20. package/cli/src/engine-native/modes.ts +30 -14
  21. package/cli/src/engine-native/os.ts +29 -0
  22. package/cli/src/engine-native/parseArgs.ts +19 -17
  23. package/cli/src/engine-native/services.ts +96 -0
  24. package/cli/src/engine-native/skillsSync.ts +77 -61
  25. package/cli/src/engine-native/toolchain.ts +50 -68
  26. package/cli/src/engine.ts +11 -2
  27. package/cli/src/generated/sotPayload.ts +41 -0
  28. package/cli/src/kitHome.ts +15 -11
  29. package/cli/src/main.ts +3 -2
  30. package/cli/src/manifests.ts +17 -15
  31. package/cli/src/payload.ts +28 -0
  32. package/cli/src/services.ts +34 -0
  33. package/docks-kit +6 -6
  34. package/package.json +2 -3
  35. package/SoT/.agents/skills.txt +0 -14
  36. package/SoT/.claude/CLAUDE.md +0 -146
  37. package/SoT/.claude/fetch-usage.sh +0 -66
  38. package/SoT/.claude/hooks/notify.sh +0 -14
  39. package/SoT/.claude/mcp-servers.json +0 -10
  40. package/SoT/.claude/settings.json +0 -235
  41. package/SoT/.claude/statusline.sh +0 -175
  42. package/SoT/.codex/AGENTS.md +0 -75
  43. package/SoT/.codex/agents/.gitkeep +0 -1
  44. package/SoT/.codex/config.toml +0 -45
  45. package/SoT/.codex/plugins/marketplace.json +0 -50
  46. package/SoT/.codex/rules/docks.rules +0 -116
  47. package/SoT/models.json +0 -28
  48. package/SoT/toolchain.json +0 -27
  49. package/cli/src/engine-native/output.ts +0 -20
  50. package/notification.mp3 +0 -0
package/AGENTS.md CHANGED
@@ -22,7 +22,7 @@ Tool-specific instructions live alongside this file:
22
22
  | `install.sh` | Global installer |
23
23
  | `.github/workflows/release-cli.yml` | `cli-v*` release binaries + npm publish |
24
24
  | `README.md` | Front door |
25
- | `package.json` / `bun.lock` | npm package: `bin` = `cli/src/main.ts`; bundles `cli/` + `SoT/` |
25
+ | `package.json` / `bun.lock` | npm package: `bin` = `cli/src/main.ts`; bundles `cli/` with the generated in-memory SoT payload |
26
26
  | `SoT/.agents/skills.txt` | Universal-skill manifest. One [agentskills.io](https://agentskills.io) slug per line; EngineNative runs `npx skills add` for each missing entry into `~/.agents/skills/`, where Codex et al. discover it natively and Claude Code follows a symlink at `~/.claude/skills/` |
27
27
  | `notification.mp3` | Audio asset for Notification hooks (consumed by Claude Code today; tool-agnostic file) |
28
28
  | `docs/plans/` | Multi-commit work-item plans (`active/` with status in frontmatter, plus `finished/` archive). Convention: `docs/plans/AGENTS.md` |
package/README.md CHANGED
@@ -25,8 +25,9 @@ see `./docks-kit docs install`.
25
25
 
26
26
  **No-Bun recovery path**:
27
27
 
28
- Download the platform release binary from GitHub Releases and run it from a
29
- kit checkout, or set `DOCKS_KIT_HOME` to the checkout containing `SoT/`.
28
+ Download the platform release binary from GitHub Releases and run it directly.
29
+ The executable carries the generated sync payload; no checkout or adjacent
30
+ `SoT/` directory is required.
30
31
 
31
32
  Prerequisites for source/global installs: Bun plus `jq` and `curl` for sync
32
33
  preflight; Node/npm for npm-global tools.
@@ -93,6 +94,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
93
94
  | `SoT/models.json` | Kit-verified model catalog |
94
95
  | `SoT/toolchain.json` | Verified-version floors |
95
96
  | `cli/src/engine-native/` | EngineNative sync/model/toolchain implementation |
97
+ | `cli/src/generated/sotPayload.ts` | Generated in-memory payload used by standalone and npm installs |
96
98
  | `cli/` | docks-kit CLI (Effect-TS on Bun) + bundled docs topics |
97
99
  | `docks-kit` | Launcher (binary → bun-from-source) |
98
100
  | `install.sh` | Global installer (Bun bootstrap + `bun add -g`) |
@@ -105,7 +107,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
105
107
  |----------|-----|-------------|
106
108
  | Linux | ✅ native | ✅ native |
107
109
  | macOS (x64/arm64) | ✅ native | ✅ native |
108
- | Windows | ✅ native binary (`.exe` and `bun add -g` CI-verified) | ✅ EngineNative (TS, default engine — no Git Bash); real-machine verify pending — RTK hooks are native too (rtk ≥0.37.2) |
110
+ | Windows | ✅ native binary (`.exe` and `bun add -g` CI-verified) | ✅ EngineNative (TS, default engine — no Git Bash); real-machine verified 2026-07-09 — RTK hooks are native too (rtk ≥0.37.2) |
109
111
 
110
112
  Details: `docks-kit docs platforms`.
111
113
 
@@ -113,8 +115,8 @@ Details: `docks-kit docs platforms`.
113
115
 
114
116
  Tagging `cli-v*` builds five standalone binaries (+ SHA256SUMS) and attaches
115
117
  them to the GitHub release; npm publish runs when the `NPM_TOKEN` secret is
116
- configured. Package bundles the CLI + SoT, so npm releases are versioned config
117
- snapshots.
118
+ configured. Package bundles the CLI + generated payload, so npm releases are
119
+ versioned config snapshots without shipping the authoring `SoT/` tree.
118
120
 
119
121
  ## Deeper docs
120
122
 
package/cli/docs/flags.md CHANGED
@@ -20,6 +20,7 @@ docks-kit sync claude agents # two
20
20
  | `--prune` | Uninstall kit-managed installs not in SoT: plugins, marketplaces, universal skills |
21
21
  | `--skip-rtk` | Skip optional tool bootstrap (RTK, bubblewrap) |
22
22
  | `--yes` | Auto-accept toolchain above-verified prompts (containers/CI) |
23
+ | `--verbose` / `-v` | Also print no-op confirmations (already in sync, up to date, left as-is); accepted on `sync`, `model`, and `toolchain` |
23
24
 
24
25
  ## Per-tool flags
25
26
 
@@ -21,10 +21,10 @@ docks-kit sync
21
21
  bunx docks-kit status
22
22
  ```
23
23
 
24
- The npm package bundles the CLI and the SoT — releases are versioned config
25
- snapshots. Kit-home resolution: `DOCKS_KIT_HOME` env nearest ancestor
26
- directory containing `SoT/` + `package.json` (a checkout wins over the bundled
27
- copy) the package's own bundled SoT.
24
+ The npm package bundles the CLI and its generated in-memory payload — releases
25
+ are versioned config snapshots without publishing the authoring `SoT/` tree.
26
+ Kit-home resolution remains available for checkout/package update behavior and
27
+ display paths, but sync reads do not depend on it.
28
28
 
29
29
  ## 3. curl installer (Unix-only)
30
30
 
@@ -44,12 +44,10 @@ kit before. The installer bootstraps Bun when absent, runs
44
44
  Two supported paths (CI-verified on windows-2025, native PowerShell):
45
45
 
46
46
  - **Compiled binary** — `docks-kit-windows-x64.exe` release asset. No Bun,
47
- no Git Bash: the exe embeds the runtime and EngineNative runs in-process.
48
- It still needs the SoT it deploys — run it from inside a kit checkout, or
49
- point `DOCKS_KIT_HOME` at one.
47
+ no Git Bash: the exe embeds the runtime and generated payload, and
48
+ EngineNative runs in-process from any working directory.
50
49
  - **`bun add -g docks-kit`** — bun creates a working shim for the
51
- `#!/usr/bin/env bun` bin; outside a checkout the package's own bundled
52
- SoT is used.
50
+ `#!/usr/bin/env bun` bin; the package carries the same generated payload.
53
51
 
54
52
  `install.sh` is not a Windows path.
55
53
 
@@ -72,8 +70,8 @@ nudges when the checkout is stale (silent offline / detached / no git).
72
70
  ## No-Bun recovery
73
71
 
74
72
  No Bun or constrained sandbox: download the platform release binary from GitHub
75
- Releases and run it from inside a kit checkout, or set `DOCKS_KIT_HOME` to the
76
- checkout containing `SoT/`.
73
+ Releases and run it directly. No checkout or `DOCKS_KIT_HOME` is required for
74
+ sync/config reads.
77
75
 
78
76
  ## Prerequisites
79
77
 
@@ -14,6 +14,7 @@ AI-assisted dev environment on every machine.
14
14
  | `SoT/.agents/` | Universal agent skills manifest (agentskills.io standard) |
15
15
  | `SoT/models.json` | Kit-verified model catalog (see `docks-kit docs models`) |
16
16
  | `SoT/toolchain.json` | Verified-version floors for external tools (see `docks-kit docs toolchain`) |
17
+ | `cli/src/generated/sotPayload.ts` | Deterministic generated payload embedded in standalone/npm execution |
17
18
  | `cli/src/engine-native/` | EngineNative mutation logic for sync/model/toolchain |
18
19
  | `cli/` | This CLI (Effect-TS on Bun) plus bundled docs |
19
20
  | `docks-kit` | Launcher: compiled binary → bun-from-source, with Bun auto-install |
@@ -21,6 +22,9 @@ AI-assisted dev environment on every machine.
21
22
  ## Design rules
22
23
 
23
24
  - **Idempotent**: every sync step is safe to re-run; no-change syncs are no-ops.
25
+ - **Quiet on no-ops**: by default a re-run prints only real changes, warnings
26
+ (with a platform-correct install command when a tool is missing), and the
27
+ summary; `--verbose` / `-v` also prints the no-op confirmations.
24
28
  - **Additive by default**: user-only keys/plugins/skills survive a plain sync.
25
29
  Reconciliation toward the SoT is explicit: `--reconcile` (settings) and
26
30
  `--prune` (plugins/marketplaces/skills).
@@ -28,6 +32,8 @@ AI-assisted dev environment on every machine.
28
32
  changes, and a later flag-less sync reverts them (`docks-kit docs modifiers`).
29
33
  - **Engine/CLI split**: the CLI adds UX (typed flags, pickers, docs, JSON);
30
34
  EngineNative owns mutation. No-Bun recovery is a platform release binary.
35
+ - **Authoring/runtime split**: changes begin in `SoT/`; build/prepack freshness
36
+ checks keep the generated in-memory payload byte-identical for every runtime.
31
37
 
32
38
  ## Where to go next
33
39
 
@@ -4,7 +4,7 @@
4
4
  |----------|-----------------|-------------|-------|
5
5
  | Linux | native | EngineNative (TS) | Primary target |
6
6
  | macOS | native (x64 + arm64) | EngineNative (TS) | Release binary or Bun source path |
7
- | Windows | native (`.exe` / `bun add -g`, CI-verified) | EngineNative (TS) — no Git Bash | Real-machine verify pending |
7
+ | Windows | native (`.exe` / `bun add -g`, CI-verified) | EngineNative (TS) — no Git Bash | Real-machine verified 2026-07-09 |
8
8
 
9
9
  EngineNative is the only supported engine on all platforms. `DOCKS_KIT_ENGINE=bash`
10
10
  now exits with the removed-engine message and points at the `bash-engine-final`
@@ -16,12 +16,10 @@ Supported entrypoints (both verified in CI on windows-2025 under native
16
16
  PowerShell — see `.github/workflows/windows-entrypoints.yml`):
17
17
 
18
18
  - **`docks-kit-windows-x64.exe`** (release asset) — the no-toolchain path.
19
- No Bun, no Git Bash; the exe embeds the runtime and EngineNative runs
20
- in-process. It still needs the SoT it deploys: run from a kit checkout or
21
- set `DOCKS_KIT_HOME`.
19
+ No Bun, no Git Bash; the exe embeds the runtime and generated payload, and
20
+ EngineNative runs in-process from any working directory.
22
21
  - **`bun add -g docks-kit`** — bun creates a working Windows shim for the
23
- `#!/usr/bin/env bun` bin; outside a checkout kit-home resolves to the
24
- package's own bundled SoT.
22
+ `#!/usr/bin/env bun` bin; the package carries the same generated payload.
25
23
  - `install.sh` is **Unix-only** and not a Windows path.
26
24
 
27
25
  The managed tools all run natively on Windows: Claude Code (requires Git
@@ -38,7 +36,8 @@ spawning (npm), toolchain gate branches (`.github/workflows/parity.yml`,
38
36
  Deployed hook/statusline assets stay shell scripts by design — Claude Code on
39
37
  Windows executes them through its own Git Bash.
40
38
 
41
- **Status: supported, pending one manual gate** — a real-machine interactive
42
- verify (Claude Code loads the synced `%USERPROFILE%\.claude`, the rtk hook
43
- fires, hook/statusline commands run or are knowingly skipped) tracked as
44
- step 9 of the `windows-support` plan.
39
+ **Status: supported** — real-machine verified 2026-07-09 (Claude Code loads
40
+ the synced `%USERPROFILE%\.claude`; full sync, self-update, plugin passes,
41
+ universal skills, and agent-browser all green on native PowerShell). Needs
42
+ git on PATH for plugin marketplaces; rtk installs natively via its Windows
43
+ release (the kit's auto-install is Unix-only).
@@ -13,26 +13,30 @@ const value = Args.text({ name: "value" }).pipe(
13
13
  const dryRun = Options.boolean("dry-run").pipe(
14
14
  Options.withDescription("Preview without applying")
15
15
  )
16
+ const verbose = Options.boolean("verbose").pipe(
17
+ Options.withAlias("v"),
18
+ Options.withDescription("Also print no-op confirmations (already in sync, up to date)")
19
+ )
16
20
 
17
21
  const KEEP = "__keep__"
18
22
 
19
23
  export const modelCommand = Command.make(
20
24
  "model",
21
- { tool, value, dryRun },
25
+ { tool, value, dryRun, verbose },
22
26
  (config) =>
23
27
  Effect.gen(function* () {
24
28
  if (config.tool !== "claude" && config.tool !== "codex") {
25
29
  return yield* bail(`Unknown tool '${config.tool}' (valid: claude, codex)`)
26
30
  }
27
31
  const t = config.tool as Tool
28
- const dry = config.dryRun ? ["--dry-run"] : []
32
+ const dry = [...(config.dryRun ? ["--dry-run"] : []), ...(config.verbose ? ["--verbose"] : [])]
29
33
 
30
34
  if (Option.isSome(config.value)) {
31
35
  return yield* engine(["model", t, config.value.value, ...dry])
32
36
  }
33
37
 
34
38
  // No value: show current (engine prints deployed + SoT + catalog) …
35
- yield* engine(["model", t])
39
+ yield* engine(["model", t, ...(config.verbose ? ["--verbose"] : [])])
36
40
 
37
41
  // … and offer an interactive picker when attached to a terminal.
38
42
  if (!process.stdin.isTTY || !process.stdout.isTTY) return
@@ -4,13 +4,15 @@ import { spawnSync } from "node:child_process"
4
4
  import { existsSync } from "node:fs"
5
5
  import { join } from "node:path"
6
6
  import { bail, engine } from "../engine"
7
+ import type { Logger } from "../engine-native/logger"
7
8
  import { kitHome } from "../kitHome"
8
9
  import { modelCatalog, type Tool } from "../manifests"
10
+ import { LoggerService } from "../services"
9
11
 
10
12
  /** Best-effort update autodetection: nudge (never block, never fail) when
11
13
  * the kit checkout is behind its upstream. Silent on detached HEADs, no
12
14
  * upstream, no network, no git. */
13
- const updateNudge = (): void => {
15
+ const updateNudge = (logger: Logger): void => {
14
16
  try {
15
17
  const home = kitHome()
16
18
  if (!existsSync(join(home, ".git"))) return
@@ -21,7 +23,7 @@ const updateNudge = (): void => {
21
23
  })
22
24
  const behind = (res.stdout ?? "").trim()
23
25
  if (res.status === 0 && behind !== "" && behind !== "0") {
24
- process.stderr.write(`\x1b[1;33m[warn]\x1b[0m kit checkout is ${behind} commit(s) behind its upstream — run: docks-kit update\n`)
26
+ logger.warn(`kit checkout is ${behind} commit(s) behind its upstream — run: docks-kit update`)
25
27
  }
26
28
  } catch {
27
29
  // nudge only — a sync must never fail because the update check did
@@ -75,6 +77,10 @@ const skipRtk = Options.boolean("skip-rtk").pipe(
75
77
  const yes = Options.boolean("yes").pipe(
76
78
  Options.withDescription("Auto-accept toolchain prompts (containers/CI)")
77
79
  )
80
+ const verbose = Options.boolean("verbose").pipe(
81
+ Options.withAlias("v"),
82
+ Options.withDescription("Also print no-op confirmations (already in sync, up to date, left as-is)")
83
+ )
78
84
  const claudeModel = Options.text("claude-model").pipe(
79
85
  Options.withDescription("Deploy-time modifier: set deployed Claude model (see `docks-kit models claude`)"),
80
86
  Options.optional
@@ -106,6 +112,7 @@ export const syncCommand = Command.make(
106
112
  prune,
107
113
  skipRtk,
108
114
  yes,
115
+ verbose,
109
116
  claudeModel,
110
117
  claudeCompactWindow,
111
118
  claudePermissive,
@@ -138,6 +145,7 @@ export const syncCommand = Command.make(
138
145
  if (config.prune) args.push("--prune")
139
146
  if (config.skipRtk) args.push("--skip-rtk")
140
147
  if (config.yes) args.push("--yes")
148
+ if (config.verbose) args.push("--verbose")
141
149
  if (config.claudePermissive) args.push("--claude-permissive")
142
150
  Option.map(config.claudeModel, (m) => args.push(`--claude-model=${m}`))
143
151
  Option.map(config.claudeCompactWindow, (w) => args.push(`--claude-compact-window=${w}`))
@@ -152,7 +160,10 @@ export const syncCommand = Command.make(
152
160
 
153
161
  // Not on --dry-run: the nudge's git fetch writes FETCH_HEAD/remote
154
162
  // refs, and a preview command must not mutate the checkout.
155
- if (!config.dryRun) yield* Effect.sync(updateNudge)
163
+ if (!config.dryRun) {
164
+ const logger = yield* LoggerService
165
+ yield* Effect.sync(() => updateNudge(logger))
166
+ }
156
167
  yield* engine(args)
157
168
  })
158
169
  ).pipe(
@@ -15,15 +15,19 @@ const tool = Args.text({ name: "tool" }).pipe(
15
15
  const yes = Options.boolean("yes").pipe(
16
16
  Options.withDescription("Auto-accept above-verified installs")
17
17
  )
18
+ const verbose = Options.boolean("verbose").pipe(
19
+ Options.withAlias("v"),
20
+ Options.withDescription("Also print no-op confirmations (present, up to date)")
21
+ )
18
22
 
19
- export const toolchainCommand = Command.make("toolchain", { op, tool, yes }, (config) =>
23
+ export const toolchainCommand = Command.make("toolchain", { op, tool, yes, verbose }, (config) =>
20
24
  Effect.gen(function* () {
21
25
  const operation = Option.getOrElse(config.op, () => "check")
22
- const flags = config.yes ? ["--yes"] : []
26
+ const flags = [...(config.yes ? ["--yes"] : []), ...(config.verbose ? ["--verbose"] : [])]
23
27
 
24
28
  switch (operation) {
25
29
  case "check":
26
- return yield* engine(["toolchain", "check"])
30
+ return yield* engine(["toolchain", "check", ...(config.verbose ? ["--verbose"] : [])])
27
31
  case "ensure": {
28
32
  const t = Option.getOrUndefined(config.tool)
29
33
  if (t === undefined || !MANAGED.includes(t)) {
@@ -36,6 +36,79 @@ explicit removed-engine diagnostic and exits 2 with the recovery tag message.
36
36
  shell script.
37
37
  - **Backups precede mutation.** Deployed settings/config files write `.bak`
38
38
  backups before replacement.
39
+ - **Runtime payload is in memory.** `SoT/` remains the reviewed authoring tree;
40
+ generated payload freshness gates build/package production, and EngineNative
41
+ never needs an adjacent runtime `SoT/` directory.
42
+
43
+ ## Output Policy (log UX contract)
44
+
45
+ The default run reads like a change report: only actual changes, actionable
46
+ warnings, and the summary. Status-quo confirmations exist but are opt-in.
47
+ (Rationale + full call-site audit: `docs/plans/active/cli-log-ux-overhaul.md`.)
48
+
49
+ ### Channels and levels
50
+
51
+ | Emitter | Channel | Default | `--verbose` | Prefix / form |
52
+ |---|---|---|---|---|
53
+ | `error(msg)` | stderr | shown | shown | `[err]` red (`\x1b[1;31m`) |
54
+ | `warn(msg)` | stderr | shown | shown | `[warn]` yellow (`\x1b[1;33m`) |
55
+ | `change(msg)` | stderr | shown | shown | `[ok]` green (`\x1b[1;32m`) — ONLY after an operation actually mutated |
56
+ | `verbose(msg)` | stderr | hidden | shown | `[ok]` green — no-op confirmations ("already …", "present", "up to date", "left as-is"), skips |
57
+ | `data(line)` | stdout | shown | shown | bare — dry-run report lines, `status --json`, summary block, usage text |
58
+
59
+ - stdout is data, stderr is logs — the logger NEVER writes to stdout
60
+ (`engineCapture` depends on it).
61
+ - **Dry-run is a complete inspection report**: `[dry-run]` lines are `data`,
62
+ printed unfiltered at every verbosity.
63
+ - Prefixes and ANSI codes are stable golden surface; the level controls
64
+ visibility, not the prefix.
65
+
66
+ ### Change detection
67
+
68
+ Every mutating operation reports `changed: boolean`. A changed outcome logs
69
+ via `change`; an unchanged outcome logs via `verbose`. Operations that today
70
+ rewrite deployed files unconditionally may skip a provably-identical rewrite —
71
+ each such skip is an intentional behavior change named in its golden diff.
72
+
73
+ ### Missing dependencies
74
+
75
+ Exactly one deduplicated warn per missing tool per run, uniform shape:
76
+ `[warn] <tool> not installed — <platform-correct install command>`, sourced
77
+ from the dependency registry (`deps.ts`). Required tools keep their current
78
+ exit behavior — the error carries the same install hint.
79
+
80
+ ### Summary and next steps
81
+
82
+ `model` and `toolchain` modes have no separate summary block by design —
83
+ their stdout IS the deliverable (`deployed:`/`SoT:` + catalog lines for
84
+ `model`; the report table for `toolchain check`), so nothing prints after
85
+ it. The schema below applies to `sync` only.
86
+
87
+ The `--- Sync complete ---` block (stdout, `data`) prints on every run,
88
+ including dry-run, with the per-target inventory lines (`Claude:`/`Hooks:`/
89
+ `RTK:`/`Plugins:`/`Codex:`/`Skills:`). Next-step advice lines print only when
90
+ their trigger changed this run (plugins changed → `/reload-plugins` line;
91
+ hooks/env changed → restart line; skills changed → discovery line) or under
92
+ `--verbose`.
93
+
94
+ ### Platform seam
95
+
96
+ All platform branching routes through `os.ts` — the only engine module that
97
+ reads `process.platform`, with one named exemption: `exec.ts`'s path/exec
98
+ primitives (`commandExists` PATHEXT resolution and `X_OK` probing) stay
99
+ self-contained because they sit below the seam. `deps.ts` install hints
100
+ default their platform from `os.ts` and keep the parameter injectable for
101
+ tests.
102
+
103
+ ### Verbosity plumbing
104
+
105
+ `--verbose` / `-v` on the public `sync`, `model`, and `toolchain` commands;
106
+ `DOCKS_KIT_VERBOSE=1` selects it on the harness-private raw channel (same
107
+ `${VAR:-default}` contract as the other `Ctx` env globals). The default
108
+ service factory returns a raw Logger. `runEngineNative` wraps the default or
109
+ injected Logger with explicit delegates and owns the sole `ctx.verbose` gate.
110
+ There is no factory-level verbosity callback, module-global verbosity flag, or
111
+ active logger binding.
39
112
 
40
113
  ## Module Map
41
114
 
@@ -43,6 +116,7 @@ explicit removed-engine diagnostic and exits 2 with the recovery tag message.
43
116
  |---|---|
44
117
  | `parseArgs.ts` | engine usage, target selection, flag parsing, legacy rename hints, preflight, model flag validation |
45
118
  | `index.ts` | sync orchestration, target dispatch, run summary and next-step blocks |
119
+ | `../payload.ts` | generated text/byte payload reads and presentation-only source labels |
46
120
  | `claudeSync.ts` | Claude pipeline: RTK, assets, settings merge, deploy-time modifiers, `~/.claude.json`, connector env, removed artifacts, plugins, optional plugins, LSP binaries |
47
121
  | `settings.ts` | pure Claude settings merge/reconcile semantics and permission-array union |
48
122
  | `claudeModel.ts` | deployed Claude model modifier and direct `model claude` write path |
@@ -53,8 +127,11 @@ explicit removed-engine diagnostic and exits 2 with the recovery tag message.
53
127
  | `modes.ts` | direct `model` and `toolchain` modes |
54
128
  | `models.ts` | model catalog listing and validation |
55
129
  | `jq.ts` | JSON helpers that preserve jq-style merge/order/stringify behavior where the deployed file contract needs it |
56
- | `exec.ts` | path helpers, command probes, capture/spawn wrappers, Windows command resolution |
57
- | `output.ts` | stable stdout/stderr emitters |
130
+ | `exec.ts` | path helpers, command probes, capture/spawn wrappers, Windows command resolution, change-detecting write/copy helpers |
131
+ | `logger.ts` | Logger shape + stable raw stdout/stderr sink factory; the run-scoped verbosity gate lives in `index.ts` |
132
+ | `deps.ts` | external-tool registry: identity, requirement class, presence probe, platform-correct install hints, per-manager missing-tool dedup; callers supply the current run Logger to `warnMissing` |
133
+ | `os.ts` | platform capability seam — the single `process.platform` reader (`platformName`, `isWindows`, `isLinux`, shell-rc applicability) |
134
+ | `services.ts` | shared raw-Logger + DependencyManager + Platform factory; wrapped in Effect Layers at `cli/src/services.ts`, with the run-scoped Logger gate applied only by `runEngineNative` |
58
135
 
59
136
  ## Windows Specifics
60
137
 
@@ -7,9 +7,9 @@ import { readFileSync, renameSync, writeFileSync } from "node:fs"
7
7
 
8
8
  import type { Ctx } from "./index"
9
9
  import { isObject, jqStringify, parseJson } from "./jq"
10
- import { echo, err, log, warn } from "./output"
11
10
 
12
11
  export function syncClaudeModel(ctx: Ctx, model: string): void {
12
+ const { change, echo, err, verbose, warn } = ctx.services.logger
13
13
  const userSettings = p(ctx.home, ".claude", "settings.json")
14
14
 
15
15
  if (model === "") return
@@ -42,7 +42,13 @@ export function syncClaudeModel(ctx: Ctx, model: string): void {
42
42
  doc["model"] = model
43
43
  }
44
44
  }
45
- writeFileSync(`${userSettings}.tmp`, jqStringify(doc))
45
+ const out = jqStringify(doc)
46
+ if (out === text) {
47
+ verbose(`Model: deployed settings model already ${model === "default" ? "unset (account default)" : model}`)
48
+ return
49
+ }
50
+ writeFileSync(`${userSettings}.tmp`, out)
46
51
  renameSync(`${userSettings}.tmp`, userSettings)
47
- log(`Model: deployed settings model set to ${model} (SoT unchanged; flag-less sync reverts)`)
52
+ change(`Model: deployed settings model set to ${model} (SoT unchanged; flag-less sync reverts)`)
53
+ ctx.nextStepTriggers.claudeRestart = true
48
54
  }