docks-kit 0.1.4 → 0.2.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.
package/README.md CHANGED
@@ -105,7 +105,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
105
105
  |----------|-----|-------------|
106
106
  | Linux | ✅ native | ✅ native |
107
107
  | 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) |
108
+ | 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
109
 
110
110
  Details: `docks-kit docs platforms`.
111
111
 
@@ -1,4 +1,4 @@
1
- model = "gpt-5.5"
1
+ model = "gpt-5.6-sol"
2
2
  model_reasoning_effort = "xhigh"
3
3
  plan_mode_reasoning_effort = "xhigh"
4
4
  model_reasoning_summary = "detailed"
package/SoT/models.json CHANGED
@@ -16,9 +16,12 @@
16
16
  ]
17
17
  },
18
18
  "codex": {
19
- "verified": "2026-07-08",
19
+ "verified": "2026-07-09",
20
20
  "models": [
21
- { "id": "gpt-5.5", "kind": "id", "note": "Codex default — the kit SoT pin" },
21
+ { "id": "gpt-5.6-sol", "kind": "id", "note": "GPT-5.6 Solfrontier, recommended default; the kit SoT pin" },
22
+ { "id": "gpt-5.6-terra", "kind": "id", "note": "GPT-5.6 Terra — balanced tier" },
23
+ { "id": "gpt-5.6-luna", "kind": "id", "note": "GPT-5.6 Luna — fast/light tier" },
24
+ { "id": "gpt-5.5", "kind": "id", "note": "previous generation" },
22
25
  { "id": "gpt-5.5-codex", "kind": "id", "note": "codex-tuned gpt-5.5" },
23
26
  { "id": "gpt-5.1", "kind": "id", "note": "previous generation" },
24
27
  { "id": "gpt-5", "kind": "id", "note": "previous generation" },
@@ -3,6 +3,7 @@
3
3
  "tools": {
4
4
  "jq": { "kind": "required", "note": "deployed Claude hooks/statusline assets" },
5
5
  "curl": { "kind": "required", "note": "installers + usage fetch" },
6
+ "git": { "kind": "check", "note": "plugin marketplaces (claude/codex clone them) + kit checkout updates" },
6
7
  "node": { "kind": "check", "note": "hosts npm globals (agent-browser, LSP servers)" },
7
8
  "npm": { "kind": "check", "note": "npm-global installer" },
8
9
  "claude": { "kind": "check", "floor": "2.1.170", "note": "kit floor — `best` alias + Fable 5 need >=2.1.170 (mirrors settings minimumVersion)" },
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,6 +21,9 @@ AI-assisted dev environment on every machine.
21
21
  ## Design rules
22
22
 
23
23
  - **Idempotent**: every sync step is safe to re-run; no-change syncs are no-ops.
24
+ - **Quiet on no-ops**: by default a re-run prints only real changes, warnings
25
+ (with a platform-correct install command when a tool is missing), and the
26
+ summary; `--verbose` / `-v` also prints the no-op confirmations.
24
27
  - **Additive by default**: user-only keys/plugins/skills survive a plain sync.
25
28
  Reconciliation toward the SoT is explicit: `--reconcile` (settings) and
26
29
  `--prune` (plugins/marketplaces/skills).
@@ -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`
@@ -38,7 +38,8 @@ spawning (npm), toolchain gate branches (`.github/workflows/parity.yml`,
38
38
  Deployed hook/statusline assets stay shell scripts by design — Claude Code on
39
39
  Windows executes them through its own Git Bash.
40
40
 
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.
41
+ **Status: supported** — real-machine verified 2026-07-09 (Claude Code loads
42
+ the synced `%USERPROFILE%\.claude`; full sync, self-update, plugin passes,
43
+ universal skills, and agent-browser all green on native PowerShell). Needs
44
+ git on PATH for plugin marketplaces; rtk installs natively via its Windows
45
+ 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)) {
@@ -37,6 +37,76 @@ explicit removed-engine diagnostic and exits 2 with the recovery tag message.
37
37
  - **Backups precede mutation.** Deployed settings/config files write `.bak`
38
38
  backups before replacement.
39
39
 
40
+ ## Output Policy (log UX contract)
41
+
42
+ The default run reads like a change report: only actual changes, actionable
43
+ warnings, and the summary. Status-quo confirmations exist but are opt-in.
44
+ (Rationale + full call-site audit: `docs/plans/active/cli-log-ux-overhaul.md`.)
45
+
46
+ ### Channels and levels
47
+
48
+ | Emitter | Channel | Default | `--verbose` | Prefix / form |
49
+ |---|---|---|---|---|
50
+ | `error(msg)` | stderr | shown | shown | `[err]` red (`\x1b[1;31m`) |
51
+ | `warn(msg)` | stderr | shown | shown | `[warn]` yellow (`\x1b[1;33m`) |
52
+ | `change(msg)` | stderr | shown | shown | `[ok]` green (`\x1b[1;32m`) — ONLY after an operation actually mutated |
53
+ | `verbose(msg)` | stderr | hidden | shown | `[ok]` green — no-op confirmations ("already …", "present", "up to date", "left as-is"), skips |
54
+ | `data(line)` | stdout | shown | shown | bare — dry-run report lines, `status --json`, summary block, usage text |
55
+
56
+ - stdout is data, stderr is logs — the logger NEVER writes to stdout
57
+ (`engineCapture` depends on it).
58
+ - **Dry-run is a complete inspection report**: `[dry-run]` lines are `data`,
59
+ printed unfiltered at every verbosity.
60
+ - Prefixes and ANSI codes are stable golden surface; the level controls
61
+ visibility, not the prefix.
62
+
63
+ ### Change detection
64
+
65
+ Every mutating operation reports `changed: boolean`. A changed outcome logs
66
+ via `change`; an unchanged outcome logs via `verbose`. Operations that today
67
+ rewrite deployed files unconditionally may skip a provably-identical rewrite —
68
+ each such skip is an intentional behavior change named in its golden diff.
69
+
70
+ ### Missing dependencies
71
+
72
+ Exactly one deduplicated warn per missing tool per run, uniform shape:
73
+ `[warn] <tool> not installed — <platform-correct install command>`, sourced
74
+ from the dependency registry (`deps.ts`). Required tools keep their current
75
+ exit behavior — the error carries the same install hint.
76
+
77
+ ### Summary and next steps
78
+
79
+ `model` and `toolchain` modes have no separate summary block by design —
80
+ their stdout IS the deliverable (`deployed:`/`SoT:` + catalog lines for
81
+ `model`; the report table for `toolchain check`), so nothing prints after
82
+ it. The schema below applies to `sync` only.
83
+
84
+ The `--- Sync complete ---` block (stdout, `data`) prints on every run,
85
+ including dry-run, with the per-target inventory lines (`Claude:`/`Hooks:`/
86
+ `RTK:`/`Plugins:`/`Codex:`/`Skills:`). Next-step advice lines print only when
87
+ their trigger changed this run (plugins changed → `/reload-plugins` line;
88
+ hooks/env changed → restart line; skills changed → discovery line) or under
89
+ `--verbose`.
90
+
91
+ ### Platform seam
92
+
93
+ All platform branching routes through `os.ts` — the only engine module that
94
+ reads `process.platform`, with one named exemption: `exec.ts`'s path/exec
95
+ primitives (`commandExists` PATHEXT resolution and `X_OK` probing) stay
96
+ self-contained because they sit below the seam. `deps.ts` install hints
97
+ default their platform from `os.ts` and keep the parameter injectable for
98
+ tests.
99
+
100
+ ### Verbosity plumbing
101
+
102
+ `--verbose` / `-v` on the public `sync`, `model`, and `toolchain` commands;
103
+ `DOCKS_KIT_VERBOSE=1` selects it on the harness-private raw channel (same
104
+ `${VAR:-default}` contract as the other `Ctx` env globals). The default
105
+ service factory returns a raw Logger. `runEngineNative` wraps the default or
106
+ injected Logger with explicit delegates and owns the sole `ctx.verbose` gate.
107
+ There is no factory-level verbosity callback, module-global verbosity flag, or
108
+ active logger binding.
109
+
40
110
  ## Module Map
41
111
 
42
112
  | Module | Owns |
@@ -53,8 +123,11 @@ explicit removed-engine diagnostic and exits 2 with the recovery tag message.
53
123
  | `modes.ts` | direct `model` and `toolchain` modes |
54
124
  | `models.ts` | model catalog listing and validation |
55
125
  | `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 |
126
+ | `exec.ts` | path helpers, command probes, capture/spawn wrappers, Windows command resolution, change-detecting write/copy helpers |
127
+ | `logger.ts` | Logger shape + stable raw stdout/stderr sink factory; the run-scoped verbosity gate lives in `index.ts` |
128
+ | `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` |
129
+ | `os.ts` | platform capability seam — the single `process.platform` reader (`platformName`, `isWindows`, `isLinux`, shell-rc applicability) |
130
+ | `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
131
 
59
132
  ## Windows Specifics
60
133
 
@@ -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
  }