docks-kit 0.14.1 → 0.14.3

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.
@@ -11,9 +11,9 @@ import { homedir } from "node:os"
11
11
  import { kitHome } from "../kitHome"
12
12
  import { makeEngineServices, type EngineServices, type Logger } from "./services"
13
13
  import type { BunRuntimeState } from "./bun"
14
- import { claudeNextSteps, claudeSummary, claudeSync } from "./claudeSync"
14
+ import { claudeNextSteps, claudeSummary, claudeSync, type ClaudeRuntimeState } from "./claudeSync"
15
15
  import { codexNextSteps, codexSummary, codexSync } from "./codexSync"
16
- import { skillsNextSteps, skillsSummary, skillsSync } from "./skillsSync"
16
+ import { skillsNextSteps, skillsSummary, skillsSync, type SkillsState } from "./skillsSync"
17
17
  import { modeModel, modeToolchain } from "./modes"
18
18
  import { ExitError, parseArgs, validateModifierFlags } from "./parseArgs"
19
19
 
@@ -30,7 +30,7 @@ export interface Ctx {
30
30
  readonly agentsDir: string
31
31
  dryRun: boolean
32
32
  verbose: boolean
33
- skipRtk: boolean
33
+ skipBubblewrap: boolean
34
34
  skipPluginRefresh?: boolean
35
35
  reconcile: boolean
36
36
  prune: boolean
@@ -71,7 +71,7 @@ function makeCtx(services: EngineServices): Ctx {
71
71
  agentsDir: env["AGENTS_DIR"] !== undefined && env["AGENTS_DIR"] !== "" ? env["AGENTS_DIR"] : p(home, ".agents"),
72
72
  dryRun: env["DRY_RUN"] === "1",
73
73
  verbose: env["DOCKS_KIT_VERBOSE"] === "1",
74
- skipRtk: env["SKIP_RTK"] === "1",
74
+ skipBubblewrap: env["SKIP_BUBBLEWRAP"] === "1",
75
75
  skipPluginRefresh: false,
76
76
  reconcile: env["RECONCILE"] === "1",
77
77
  prune: env["PRUNE"] === "1",
@@ -95,17 +95,40 @@ function makeCtx(services: EngineServices): Ctx {
95
95
  }
96
96
 
97
97
  function engineSync(ctx: Ctx, args: ReadonlyArray<string>): number {
98
- const { echo } = ctx.services.logger
98
+ const { clearProgress, echo, progress } = ctx.services.logger
99
99
  parseArgs(ctx, args)
100
100
  validateModifierFlags(ctx)
101
101
 
102
102
  const claudeRan = ctx.syncClaude
103
- const claudeRuntime = claudeRan ? claudeSync(ctx) : undefined
103
+ let claudeRuntime: ClaudeRuntimeState | undefined
104
+ if (claudeRan) {
105
+ progress("Syncing Claude...")
106
+ try {
107
+ claudeRuntime = claudeSync(ctx)
108
+ } finally {
109
+ clearProgress()
110
+ }
111
+ }
104
112
 
105
113
  const codexRan = ctx.syncCodex
106
- if (codexRan) codexSync(ctx)
114
+ if (codexRan) {
115
+ progress("Syncing Codex...")
116
+ try {
117
+ codexSync(ctx)
118
+ } finally {
119
+ clearProgress()
120
+ }
121
+ }
107
122
 
108
- const skillsState = ctx.syncAgents ? skillsSync(ctx) : undefined
123
+ let skillsState: SkillsState | undefined
124
+ if (ctx.syncAgents) {
125
+ progress("Syncing skills...")
126
+ try {
127
+ skillsState = skillsSync(ctx)
128
+ } finally {
129
+ clearProgress()
130
+ }
131
+ }
109
132
 
110
133
  echo("")
111
134
  echo("--- Sync complete ---")
@@ -133,6 +156,8 @@ export function runEngineNative(argv: ReadonlyArray<string>, services?: EngineSe
133
156
  const baseLogger = baseServices.logger
134
157
  const logger: Logger = {
135
158
  change: (msg) => baseLogger.change(msg),
159
+ progress: (msg) => baseLogger.progress(msg),
160
+ clearProgress: () => baseLogger.clearProgress(),
136
161
  verbose: (msg) => {
137
162
  if (ctx.verbose) baseLogger.verbose(msg)
138
163
  },
@@ -1,13 +1,19 @@
1
1
  /**
2
- * Leveled stderr logger + stdout data writer — the Output Policy contract in
3
- * DESIGN.md. Filtering is explicit and synchronous: engine code is imperative,
4
- * so fiber-scoped Effect log levels cannot see these writes. The prefixes and
5
- * ANSI codes are stable golden surface; the level controls visibility only.
2
+ * Leveled stderr logger, transient progress writer, and stdout data writer —
3
+ * the Output Policy contract in DESIGN.md. Filtering is explicit and
4
+ * synchronous: engine code is imperative, so fiber-scoped Effect log levels
5
+ * cannot see these writes. Transient progress is active only through an
6
+ * injected progress sink or an interactive stderr. The prefixes and ANSI codes
7
+ * are stable golden surface; the level controls visibility only.
6
8
  */
7
9
 
8
10
  export interface Logger {
9
11
  /** `[ok]` green — an operation actually mutated something. Always visible. */
10
12
  readonly change: (msg: string) => void
13
+ /** Dim, transient single-line status for blocking work. */
14
+ readonly progress: (msg: string) => void
15
+ /** Erase a pending transient status line. */
16
+ readonly clearProgress: () => void
11
17
  /** `[ok]` green — status-quo confirmation; visible only with verbosity on. */
12
18
  readonly verbose: (msg: string) => void
13
19
  readonly warn: (msg: string) => void
@@ -18,18 +24,75 @@ export interface Logger {
18
24
 
19
25
  export interface LoggerSinks {
20
26
  readonly stderr?: (chunk: string) => void
27
+ readonly progress?: (chunk: string) => void
21
28
  readonly stdout?: (chunk: string) => void
22
29
  }
23
30
 
31
+ interface WritableStreamLike {
32
+ write: (chunk: string) => unknown
33
+ on?: (event: "error", listener: (error: unknown) => void) => unknown
34
+ }
35
+
36
+ const epipeGuarded = new WeakSet<WritableStreamLike>()
37
+
38
+ /**
39
+ * A downstream reader may close the pipe early (`docks-kit toolchain check |
40
+ * head`). That is a normal end of consumption, not a CLI failure, so both the
41
+ * synchronous throw and the asynchronous error event are ignored for EPIPE.
42
+ */
43
+ export function writeIgnoringEpipe(stream: WritableStreamLike, chunk: string): void {
44
+ if (!epipeGuarded.has(stream)) {
45
+ epipeGuarded.add(stream)
46
+ stream.on?.("error", (error) => {
47
+ if ((error as NodeJS.ErrnoException | null)?.code !== "EPIPE") throw error
48
+ })
49
+ }
50
+ try {
51
+ stream.write(chunk)
52
+ } catch (error) {
53
+ if ((error as NodeJS.ErrnoException | null)?.code !== "EPIPE") throw error
54
+ }
55
+ }
56
+
24
57
  export function makeLogger(sinks: LoggerSinks): Logger {
25
- const errWrite = sinks.stderr ?? ((chunk: string) => void process.stderr.write(chunk))
26
- const outWrite = sinks.stdout ?? ((chunk: string) => void process.stdout.write(chunk))
27
- const ok = (msg: string): void => errWrite(`\x1b[1;32m[ok]\x1b[0m ${msg}\n`)
58
+ const errWrite = sinks.stderr ?? ((chunk: string) => writeIgnoringEpipe(process.stderr, chunk))
59
+ const outWrite = sinks.stdout ?? ((chunk: string) => writeIgnoringEpipe(process.stdout, chunk))
60
+ const progressWrite =
61
+ sinks.progress ??
62
+ (sinks.stderr === undefined && process.stderr.isTTY === true
63
+ ? (chunk: string) => writeIgnoringEpipe(process.stderr, chunk)
64
+ : undefined)
65
+ let progressPending = false
66
+
67
+ const clearProgress = (): void => {
68
+ if (!progressPending || progressWrite === undefined) return
69
+ progressWrite("\r\x1b[2K")
70
+ progressPending = false
71
+ }
72
+ const ok = (msg: string): void => {
73
+ clearProgress()
74
+ errWrite(`\x1b[1;32m[ok]\x1b[0m ${msg}\n`)
75
+ }
28
76
  return {
29
77
  change: ok,
30
78
  verbose: ok,
31
- warn: (msg) => errWrite(`\x1b[1;33m[warn]\x1b[0m ${msg}\n`),
32
- err: (msg) => errWrite(`\x1b[1;31m[err]\x1b[0m ${msg}\n`),
33
- echo: (line) => outWrite(`${line}\n`)
79
+ progress: (msg) => {
80
+ if (progressWrite === undefined) return
81
+ progressWrite(`\r\x1b[2K\x1b[2m${msg}\x1b[0m`)
82
+ progressPending = true
83
+ },
84
+ clearProgress,
85
+ warn: (msg) => {
86
+ clearProgress()
87
+ errWrite(`\x1b[1;33m[warn]\x1b[0m ${msg}\n`)
88
+ },
89
+ err: (msg) => {
90
+ clearProgress()
91
+ errWrite(`\x1b[1;31m[err]\x1b[0m ${msg}\n`)
92
+ },
93
+ echo: (line) => {
94
+ clearProgress()
95
+ outWrite(`${line}\n`)
96
+ }
34
97
  }
35
98
  }
@@ -11,10 +11,8 @@ import { syncCodexModel } from "./codexToml"
11
11
  import type { Ctx } from "./index"
12
12
  import { isObject, parseJson, type Json } from "./jq"
13
13
  import { printModels, validateClaudeModel, validateCodexModel } from "./models"
14
- import { ensureRtk } from "./claudeSync"
15
- import { ensureSessionRelayCli } from "./sessionRelayCli"
16
14
  import { bunBootstrap } from "./bun"
17
- import { agentBrowserInstall, effectSolutionsInstall } from "./skillsSync"
15
+ import { effectSolutionsInstall } from "./skillsSync"
18
16
  import { ensure, report } from "./toolchain"
19
17
 
20
18
  export function modeModel(ctx: Ctx, args: ReadonlyArray<string>): number {
@@ -135,18 +133,12 @@ export function modeToolchain(ctx: Ctx, args: ReadonlyArray<string>): number {
135
133
  return 2
136
134
  }
137
135
  switch (tool) {
138
- case "rtk":
139
- return ensureRtk(ctx, "cannot download RTK installer; toolchain ensure rtk aborted", 1)
140
136
  case "bun":
141
137
  return bunBootstrap(ctx, ctx.services).kind === "ready" ? 0 : 1
142
138
  case "effect-solutions":
143
139
  return ensure(ctx, "effect-solutions", effectSolutionsInstall(ctx))
144
- case "agent-browser":
145
- return ensure(ctx, "agent-browser", agentBrowserInstall)
146
- case "session-relay":
147
- return ensureSessionRelayCli(ctx)
148
140
  default:
149
- err("toolchain ensure supports managed tools only (rtk, bun, effect-solutions, agent-browser, session-relay)")
141
+ err("toolchain ensure supports managed tools only (bun, effect-solutions)")
150
142
  return 2
151
143
  }
152
144
  }
@@ -50,7 +50,7 @@ function usage(ctx: Ctx): void {
50
50
  echo(
51
51
  " --prune uninstall kit-managed installs not in SoT (plugins, marketplaces, skills in SoT/.agents/skills.txt)"
52
52
  )
53
- echo(" --skip-rtk skip optional tool bootstrap (RTK, bubblewrap)")
53
+ echo(" --skip-bubblewrap skip optional bubblewrap bootstrap (Codex Linux sandbox)")
54
54
  echo(" --skip-plugin-refresh install missing plugins but skip refresh-only updates")
55
55
  echo(" --yes auto-accept toolchain prompts (containers/CI)")
56
56
  echo(" --verbose also print no-op confirmations (already in sync, up to date, left as-is)")
@@ -147,8 +147,8 @@ export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
147
147
  case "--dry-run":
148
148
  ctx.dryRun = true
149
149
  continue
150
- case "--skip-rtk":
151
- ctx.skipRtk = true
150
+ case "--skip-bubblewrap":
151
+ ctx.skipBubblewrap = true
152
152
  continue
153
153
  case "--skip-plugin-refresh":
154
154
  ctx.skipPluginRefresh = true
@@ -217,8 +217,8 @@ export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
217
217
  case "--n8n":
218
218
  err("--n8n was renamed to --claude-plugin=n8n")
219
219
  throw new ExitError(2)
220
- case "--no-rtk":
221
- err("--no-rtk was renamed to --skip-rtk")
220
+ case "--skip-rtk":
221
+ err("--skip-rtk was renamed to --skip-bubblewrap")
222
222
  throw new ExitError(2)
223
223
  case "-h":
224
224
  case "--help":
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * EngineNative `sync agents` pipeline: universal-skill bootstrap
3
3
  * (`npx skills@<pin> add`), Claude symlink healing, --prune reconcile against
4
- * the kit-managed snapshot, agent-browser/effect-solutions toolchain callbacks,
5
- * and the snapshot write.
4
+ * the kit-managed snapshot, the effect-solutions toolchain callback, and the
5
+ * snapshot write.
6
6
  */
7
7
  import { spawnSync } from "node:child_process"
8
8
  import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, statSync, symlinkSync } from "node:fs"
@@ -28,7 +28,6 @@ export function skillsSync(ctx: Ctx): SkillsState {
28
28
 
29
29
  syncUniversal(ctx, state, skillsDir, manifest)
30
30
  if (ctx.prune) reconcileRemovals(ctx, manifest, snapshot)
31
- syncAgentBrowserCli(ctx, manifest)
32
31
  syncEffectSolutionsCli(ctx)
33
32
  updateSnapshot(ctx, manifest, snapshot)
34
33
  return state
@@ -57,7 +56,7 @@ function readSlugs(file: string): Array<string> {
57
56
  }
58
57
 
59
58
  function syncUniversal(ctx: Ctx, state: SkillsState, skillsDir: string, manifest: string): void {
60
- const { change, echo, verbose, warn } = ctx.services.logger
59
+ const { change, clearProgress, echo, progress, verbose, warn } = ctx.services.logger
61
60
  if (ctx.services.deps.probe("npx").state === "missing") {
62
61
  ctx.services.deps.warnMissing("npx", ctx.services.logger, "skipping universal skills bootstrap")
63
62
  return
@@ -87,9 +86,11 @@ function syncUniversal(ctx: Ctx, state: SkillsState, skillsDir: string, manifest
87
86
  continue
88
87
  }
89
88
 
89
+ progress(`Installing universal skill ${slug}...`)
90
90
  const res = spawnSync("npx", ["--yes", skillsCli(ctx), "add", slug, "-g", "-y", "-a", "claude-code", "codex"], {
91
91
  stdio: "ignore"
92
92
  })
93
+ clearProgress()
93
94
  if (res.error === undefined && res.status === 0) {
94
95
  added++
95
96
  } else {
@@ -217,55 +218,12 @@ function linkOrCopyWithWarnings(target: string, link: string, services: EngineSe
217
218
 
218
219
  // ------------------------------------------------- toolchain callbacks ----
219
220
 
220
- /** skills::_agent_browser_install. */
221
- export function agentBrowserInstall(mode: "install" | "upgrade", version: string, services: EngineServices): number {
222
- const { change, verbose, warn } = services.logger
223
- const verb = mode === "upgrade" ? "Upgrading" : "Installing"
224
- const pkg = version !== "" ? `agent-browser@${version}` : "agent-browser"
225
- const installFlags = services.platform.isLinux() ? ["--with-deps"] : []
226
-
227
- verbose(`${verb} agent-browser CLI via npm${version !== "" ? ` (pinned ${version})` : ""}...`)
228
- if (spawnSync("npm", ["install", "-g", pkg], { stdio: "ignore" }).status !== 0) {
229
- warn(`npm install -g ${pkg} failed. Try manually: npm install -g ${pkg}`)
230
- return 1
231
- }
232
-
233
- if (mode === "install") {
234
- warn("Downloading Chrome for Testing (~175 MB; sudo may be requested for system libs on Linux)...")
235
- if (spawnSync("agent-browser", ["install", ...installFlags], { stdio: "inherit" }).status !== 0) {
236
- warn(`agent-browser install failed. Re-run manually: agent-browser install ${installFlags.join(" ")}`)
237
- return 1
238
- }
239
- }
240
- const out = services.deps.version("agent-browser")
241
- const fields = (out.split("\n")[0] ?? "").trim().split(/[ \t]+/)
242
- const version2 = out !== "" ? fields[fields.length - 1] ?? "version unknown" : "version unknown"
243
- change(`agent-browser CLI ready (${version2})`)
244
- return 0
245
- }
246
-
247
- function syncAgentBrowserCli(ctx: Ctx, manifest: string): void {
248
- const { warn } = ctx.services.logger
249
- if (!manifest.split("\n").includes("vercel-labs/agent-browser")) return
250
-
251
- if (ctx.services.deps.probe("npm").state === "missing") {
252
- if (!ctx.dryRun) {
253
- ctx.services.deps.warnMissing("npm", ctx.services.logger, "cannot auto-install agent-browser CLI; re-run sync after installing")
254
- }
255
- return
256
- }
257
-
258
- if (ensure(ctx, "agent-browser", agentBrowserInstall) !== 0) {
259
- warn("agent-browser bootstrap failed — continuing sync")
260
- }
261
- }
262
-
263
221
  /** skills::_effect_solutions_install. */
264
222
  export function effectSolutionsInstall(
265
223
  ctx: Ctx
266
224
  ): (mode: "install" | "upgrade", version: string, services: EngineServices) => number {
267
225
  return (mode, version, services) => {
268
- const { change, verbose, warn } = services.logger
226
+ const { change, clearProgress, progress, verbose, warn } = services.logger
269
227
  const verb = mode === "upgrade" ? "Upgrading" : "Installing"
270
228
  const pkg = `effect-solutions@${version !== "" ? version : "latest"}`
271
229
 
@@ -274,7 +232,10 @@ export function effectSolutionsInstall(
274
232
  const bun = bunState.executable
275
233
 
276
234
  verbose(`${verb} effect-solutions CLI via bun${version !== "" ? ` (pinned ${version})` : ""}...`)
277
- if (spawnSync(bun, ["add", "-g", pkg], { stdio: "ignore" }).status !== 0) {
235
+ progress(`${verb} effect-solutions CLI...`)
236
+ const installResult = spawnSync(bun, ["add", "-g", pkg], { stdio: "ignore" })
237
+ clearProgress()
238
+ if (installResult.status !== 0) {
278
239
  warn(`bun add -g ${pkg} failed. Try manually: bun add -g ${pkg}`)
279
240
  return 1
280
241
  }
@@ -294,10 +255,13 @@ export function effectSolutionsInstall(
294
255
  }
295
256
 
296
257
  function syncEffectSolutionsCli(ctx: Ctx): void {
297
- const { warn } = ctx.services.logger
258
+ const { clearProgress, progress, warn } = ctx.services.logger
298
259
  if (!/"effect-kit@docks"[ \t]*:[ \t]*true/.test(payloadText("SoT/.claude/settings.json"))) return
299
260
 
300
- if (ensure(ctx, "effect-solutions", effectSolutionsInstall(ctx)) !== 0) {
261
+ progress("Checking effect-solutions CLI...")
262
+ const result = ensure(ctx, "effect-solutions", effectSolutionsInstall(ctx))
263
+ clearProgress()
264
+ if (result !== 0) {
301
265
  warn("effect-solutions bootstrap failed — continuing sync")
302
266
  }
303
267
  }
@@ -305,7 +269,7 @@ function syncEffectSolutionsCli(ctx: Ctx): void {
305
269
  // ----------------------------------------------------- prune + snapshot ----
306
270
 
307
271
  function reconcileRemovals(ctx: Ctx, manifest: string, snapshot: string): void {
308
- const { change, echo, warn } = ctx.services.logger
272
+ const { change, clearProgress, echo, progress, warn } = ctx.services.logger
309
273
  if (!existsSync(snapshot)) {
310
274
  if (ctx.dryRun) {
311
275
  echo(
@@ -325,9 +289,11 @@ function reconcileRemovals(ctx: Ctx, manifest: string, snapshot: string): void {
325
289
  echo(`[dry-run] kit-managed skill no longer in SoT — would remove: ${base}`)
326
290
  continue
327
291
  }
292
+ progress(`Removing universal skill ${base}...`)
328
293
  const res = spawnSync("npx", ["--yes", skillsCli(ctx), "remove", "--global", base, "-y"], {
329
294
  stdio: "ignore"
330
295
  })
296
+ clearProgress()
331
297
  if (res.error === undefined && res.status === 0) {
332
298
  removed++
333
299
  } else {
@@ -50,15 +50,10 @@ function firstLineField(out: string, index: number): string {
50
50
  export function installedVersion(ctx: Ctx, tool: ToolId): string {
51
51
  const version = (): string => ctx.services.deps.version(tool)
52
52
  switch (tool) {
53
- case "rtk":
54
- return firstLineField(version(), 1)
55
53
  case "claude":
56
54
  return firstLineField(version(), 0)
57
55
  case "codex":
58
- case "agent-browser":
59
56
  return firstLineField(version(), -1)
60
- case "session-relay":
61
- return firstLineField(version(), 1)
62
57
  case "git":
63
58
  return firstLineField(version(), 2)
64
59
  case "node":
@@ -72,6 +67,13 @@ export function installedVersion(ctx: Ctx, tool: ToolId): string {
72
67
  case "effect-solutions":
73
68
  case "npm":
74
69
  return version()
70
+ case "bwrap":
71
+ return firstLineField(version(), 1)
72
+ case "ffplay":
73
+ return firstLineField(version(), 2).replace(/-.*$/, "")
74
+ case "intelephense":
75
+ case "typescript-language-server":
76
+ return version().trim()
75
77
  default:
76
78
  return ""
77
79
  }
@@ -1,21 +1,21 @@
1
1
  // Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
2
2
  // Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
3
3
 
4
- export const GENERATED_PACKAGE_VERSION = "0.14.1"
4
+ export const GENERATED_PACKAGE_VERSION = "0.14.3"
5
5
 
6
6
  export const GENERATED_PAYLOAD_TEXT = {
7
7
  "SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
8
8
  "SoT/models.json": "{\n \"$comment\": \"Kit-verified model catalog — single source for EngineNative validators, the docks-kit CLI (models/model commands, pickers, and bare-flag help), and docs. Entries are research-proofed: update an entry and its tool-level `verified` date when a model ships or retires. Deploy-time model flags remain permissive.\",\n \"claude\": {\n \"verified\": \"2026-07-27\",\n \"models\": [\n { \"id\": \"best\", \"kind\": \"alias\", \"note\": \"Fable 5 where the org has access, latest Opus otherwise (Claude Code >=2.1.170)\" },\n { \"id\": \"opus\", \"kind\": \"alias\", \"note\": \"latest Opus — the kit SoT default (Opus 5 on the Anthropic API from Claude Code >=2.1.219; Opus 4.6 on Microsoft Foundry)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5 — advisor opt-in default; needs org access + Claude Code >=2.1.170\" },\n { \"id\": \"sonnet\", \"kind\": \"alias\", \"note\": \"latest Sonnet (currently Sonnet 5)\" },\n { \"id\": \"haiku\", \"kind\": \"alias\", \"note\": \"latest Haiku (currently Haiku 4.5)\" },\n { \"id\": \"default\", \"kind\": \"alias\", \"note\": \"engine pseudo-value: deletes the deployed model key so the account default applies\" },\n { \"id\": \"claude-fable-5\", \"kind\": \"id\", \"note\": \"Fable 5\" },\n { \"id\": \"claude-opus-5\", \"kind\": \"id\", \"note\": \"Opus 5 — needs Claude Code >=2.1.219\" },\n { \"id\": \"claude-opus-4-8\", \"kind\": \"id\", \"note\": \"Opus 4.8\" },\n { \"id\": \"claude-sonnet-5\", \"kind\": \"id\", \"note\": \"Sonnet 5\" },\n { \"id\": \"claude-haiku-4-5-20251001\", \"kind\": \"id\", \"note\": \"Haiku 4.5\" }\n ]\n },\n \"codex\": {\n \"verified\": \"2026-07-16\",\n \"models\": [\n { \"id\": \"gpt-5.6-sol\", \"kind\": \"id\", \"note\": \"GPT-5.6 Sol — frontier, recommended default; the kit SoT pin\" },\n { \"id\": \"gpt-5.6-terra\", \"kind\": \"id\", \"note\": \"GPT-5.6 Terra — balanced tier\" },\n { \"id\": \"gpt-5.6-luna\", \"kind\": \"id\", \"note\": \"GPT-5.6 Luna — fast/light tier\" },\n { \"id\": \"gpt-5.5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5.5\" },\n { \"id\": \"gpt-5.1\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5\" }\n ]\n }\n}\n",
9
- "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | managed-release (dedicated source-pinned release installer) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade); managed-release uses exact. `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"contextual POSIX installer transport for RTK/Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts npm globals (agent-browser, LSP servers)\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.219\", \"note\": \"kit floor — lets the `opus` alias select the newest Opus its provider offers (Anthropic API: 5; Foundry: 4.6) and subsumes Fable 5's >=2.1.170 requirement (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound (previously unchecked)\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"note\": \"Codex Linux sandbox runtime\" },\n \"intelephense\": { \"kind\": \"check\", \"verified\": \"1.18.5\", \"note\": \"php-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claude::sync_lsp_servers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"rtk\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.43.0\", \"verified\": \"0.43.0\", \"pinnable\": true,\n \"note\": \"PreToolUse hook — supply-chain review before unverified upgrades; installer honors RTK_VERSION=vX.Y.Z pin\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"agent-browser\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.32.0\", \"pinnable\": true,\n \"note\": \"browser-automation CLI (npm global), gated by the verified pin; first install also downloads Chrome for Testing\" },\n \"session-relay\": { \"kind\": \"managed-release\", \"policy\": \"exact\", \"verified\": \"0.16.0\", \"repository\": \"DocksDocks/docks\", \"tag\": \"session-relay--v0.16.0\", \"plugin_id\": \"session-relay@docks\", \"plugin_version\": \"0.16.0\", \"install_path\": \"~/.local/bin/session-relay\",\n \"assets\": {\n \"x86_64-unknown-linux-musl\": \"b3ca082dc5ea51e8322be407cdb4bbcaaa05d80bd62c3553f82ab98c1a95498a\",\n \"aarch64-unknown-linux-musl\": \"816b6b8bd2d2c2518ea359a5a21502213347b387a1cc576a0fb9cf541e5646ed\",\n \"aarch64-apple-darwin\": \"da8b114216c3f2301ad582df8e59b49e91953abcc1112b510466b31637fda825\"\n } },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.15\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` on every agents sync (universal-skill install/remove) — pinned, never @latest\" }\n }\n}\n",
9
+ "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade). `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"POSIX installer transport for the Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts the npm globals installed for the Claude LSP plugins\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer; also backs the intelephense version probe (`npm ls -g`)\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.219\", \"note\": \"kit floor — lets the `opus` alias select the newest Opus its provider offers (Anthropic API: 5; Foundry: 4.6) and subsumes Fable 5's >=2.1.170 requirement (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound; distro-installed, so no kit floor applies\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"floor\": \"0.9.0\",\n \"note\": \"Codex Linux sandbox runtime; sync installs it via the distro package manager, so the floor is the kit-tested baseline (Ubuntu 24.04 LTS) and no verified pin applies\" },\n \"intelephense\": { \"kind\": \"check\", \"floor\": \"1.18.5\", \"verified\": \"1.18.5\",\n \"note\": \"php-lsp server; `verified` pins claudeSync syncLspServers' npm install. Version comes from `npm ls -g` — its own --version prints minified source\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"floor\": \"5.3.0\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claudeSync syncLspServers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"floor\": \"6.0.3\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claudeSync syncLspServers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"floor\": \"1.3.14\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.5.3\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.22\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` when SoT/.agents/skills.txt names a slug (it is empty by default) — pinned, never @latest\" }\n }\n}\n",
10
10
  "SoT/.claude/CLAUDE.md": "## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\n**1. Persistence.** Keep going until the user's query is completely resolved. Only yield when sure the problem is solved. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done (\"I'll…\"), do that work now.\n\n**2. Default to parallel.** Whenever you have multiple independent operations (reads, greps, web fetches, independent edits), invoke them in a single response with multiple tool-use blocks. Sequential calls only when output of one operation is required as input to the next.\n\n**3. Multi-pass search.** First-pass search often misses — vary the wording (colleague-questions over keywords) before concluding something doesn't exist.\n\n**4. Trace symbols.** Before modifying a symbol, trace it to its definitions and all usages. Don't assume a function's behavior or a type's shape from the call site alone.\n\n**5. Linter-loop 3-strike rule.** Don't loop more than 3 times fixing linter errors on the same file. On the third attempt, stop and ask the user — repeated failure usually means the diagnosis is wrong, not the code.\n\n**6. Read-before-Edit TTL.** If you haven't read a file with the Read tool in the last ~5 messages, re-read it before editing. Cached file content goes stale silently when the user edits between turns.\n\n**7. Big-file rule.** For files >1000 lines, prefer Grep + scoped Read (`offset` + `limit`) over reading the entire file. Whole-file reads bloat context; targeted reads keep the working set small.\n\n**8. Todo hygiene.** Use TaskCreate for items with meaningful outcome (≥5 min, distinct deliverable). Never include operational sub-actions (linting, testing, searching, examining the codebase) as their own todos — those are sub-steps in service of higher-level tasks. Mark complete immediately when done, never in batches.\n\n**9. Literal-instruction rule.** Current frontier models follow instructions literally — they do not silently generalize from intent. Phrase requirements as explicit checklists with success criteria, not narrative.\n\n**10. Context hygiene.** Prefer `/clear` at task boundaries and `/rewind` for wrong-path detours over carrying rot forward (corrections accumulate noise; rewinds preserve the prefix and discard the bad branch). On a continuing task, run `/compact` with steering before context quality degrades. Never stop, summarize, or suggest a new session on account of context limits.\n\n**11. Autonomy calibration.** For minor choices (naming, formatting, default values, which of two equivalent approaches), pick a reasonable option and note it — don't ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; don't apply fixes until asked. Don't close a finished task with \"Want me to also…?\" — run the obvious verification, then stop cleanly.\n\n**12. Capability triggering.** When the answer depends on current or version-specific information, search or fetch before answering — never answer from memory. When work fans out across independent items (many files to read, many tests to run, many candidates to check), delegate to parallel subagents; never spawn one for work you can complete directly. For verification, prefer a fresh-context subagent over self-critique. On tasks longer than a few turns, keep a running notes file and re-read it before each phase.\n\n<constraint>\nTreat the 12 heuristics above as protocol, not preference. If a turn ends without honoring an applicable one (e.g., lint-loop guard not respected, edit without re-read), self-correct in the next turn before continuing.\n</constraint>\n\nReuse before invention: inventory existing code, components, conventions, and dependencies; extend them instead of creating a parallel pattern. Load only the narrow skills supported by the task and repository evidence. If a request establishes a new React/Tailwind system and no convention exists, prefer current shadcn/ui `base-*` components backed by Base UI; otherwise preserve the existing stack. Treat probe, production access, publish, push, release, and deploy as literal current-request effects—never infer external authority from a plan, schedule, review, or old receipt.\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
11
11
  "SoT/.claude/mcp-servers.json": "{\n \"mcpServers\": {}\n}\n",
12
- "SoT/.claude/settings.json": "{\n \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n \"minimumVersion\": \"2.1.219\",\n \"model\": \"opus\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": true,\n \"skillListingMaxDescChars\": 2048,\n \"respectGitignore\": true,\n \"cleanupPeriodDays\": 14,\n \"skillListingBudgetFraction\": 0.05,\n \"env\": {\n \"CLAUDE_CODE_MAX_OUTPUT_TOKENS\": \"64000\",\n \"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR\": \"1\",\n \"CLAUDE_CODE_AUTO_COMPACT_WINDOW\": \"468000\",\n \"CLAUDE_CODE_NO_FLICKER\": \"1\"\n },\n \"permissions\": {\n \"defaultMode\": \"auto\",\n \"allow\": [\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebFetch\",\n \"WebSearch\",\n \"Edit(./)\",\n \"Bash(git *)\",\n \"Bash(git add *)\",\n \"Bash(git commit *)\",\n \"Bash(git status *)\",\n \"Bash(git diff *)\",\n \"Bash(git log *)\",\n \"Bash(git branch *)\",\n \"Bash(git checkout *)\",\n \"Bash(git switch *)\",\n \"Bash(git stash *)\",\n \"Bash(git fetch *)\",\n \"Bash(git pull *)\",\n \"Bash(git tag *)\",\n \"Bash(git show *)\",\n \"Bash(git blame *)\",\n \"Bash(git worktree *)\",\n \"Bash(gh *)\",\n \"Bash(pnpm *)\",\n \"Bash(npm *)\",\n \"Bash(npx *)\",\n \"Bash(node *)\",\n \"Bash(docker *)\",\n \"Bash(docker-compose *)\",\n \"Bash(rtk *)\",\n \"Bash(ls *)\",\n \"Bash(cat *)\",\n \"Bash(find *)\",\n \"Bash(grep *)\",\n \"Bash(head *)\",\n \"Bash(tail *)\",\n \"Bash(wc *)\",\n \"Bash(sort *)\",\n \"Bash(uniq *)\",\n \"Bash(diff *)\",\n \"Bash(which *)\",\n \"Bash(pwd *)\",\n \"Bash(date *)\",\n \"Bash(mkdir *)\",\n \"Bash(basename *)\",\n \"Bash(dirname *)\",\n \"Bash(realpath *)\",\n \"Bash(jq *)\",\n \"Bash(curl *)\",\n \"Bash(tree *)\",\n \"Bash(sed *)\",\n \"Bash(awk *)\",\n \"Bash(cut *)\",\n \"Bash(tr *)\",\n \"Bash(tee *)\",\n \"Bash(echo *)\",\n \"Bash(printf *)\",\n \"Bash(env *)\",\n \"Bash(printenv *)\",\n \"Bash(uname *)\",\n \"Bash(file *)\",\n \"Bash(stat *)\",\n \"Bash(du *)\",\n \"Bash(id *)\",\n \"Bash(whoami *)\",\n \"Bash(php *)\",\n \"Bash(composer *)\",\n \"Bash(python3 *)\",\n \"Bash(python *)\",\n \"Bash(pip *)\",\n \"Bash(pip3 *)\"\n ],\n \"deny\": [\n \"Read(**/.env)\",\n \"Read(**/.env.local)\",\n \"Read(**/secrets/**)\",\n \"Read(**/*.key)\",\n \"Read(**/*.pem)\",\n \"Read(**/*.p12)\",\n \"Read(**/.credentials*)\",\n \"Edit(**/.env)\",\n \"Edit(**/.env.local)\",\n \"Edit(**/secrets/**)\",\n \"Bash(sudo *)\",\n \"Bash(rm -rf /)\",\n \"Bash(rm -rf / *)\",\n \"Bash(rm -rf ~)\",\n \"Bash(rm -rf ~ *)\",\n \"Bash(rm -rf $HOME)\",\n \"Bash(rm -rf $HOME *)\",\n \"Bash(> /dev *)\",\n \"Bash(dd if= *)\",\n \"Bash(mkfs *)\",\n \"Bash(eval *)\",\n \"Bash(chmod 777 *)\",\n \"Bash(chmod -R 777 *)\",\n \"Bash(git push --force origin main *)\",\n \"Bash(git push --force origin master *)\",\n \"Bash(git push -f origin main *)\",\n \"Bash(git push -f origin master *)\"\n ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(docker system prune *)\"\n ]\n },\n \"hooks\": {\n \"SessionStart\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_SESSION_START__\"],\n \"timeout\": 5\n }\n ]\n }\n ],\n \"Notification\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_NOTIFY__\"],\n \"timeout\": 10,\n \"async\": true\n }\n ]\n }\n ],\n \"PreToolUse\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"rtk hook claude\"\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo '{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PostToolUseFailure\\\",\\\"additionalContext\\\":\\\"Last bash command failed. Repository / file state may have shifted \\u2014 re-read affected files before retrying. If the failure is a missing dependency or env mismatch, surface it to the user rather than retrying blindly.\\\"}}'\",\n \"timeout\": 5\n }\n ]\n }\n ],\n \"SubagentStop\": [\n {\n \"hooks\": [\n {\n \"type\": \"prompt\",\n \"prompt\": \"You are a quality gate for subagent outputs in a multi-agent code-analysis pipeline.\\n\\nEvaluate the subagent's `last_assistant_message` field (in the JSON below) against these requirements:\\n\\n1. ALLOW (return `{}`): Mode-selection or no-issues responses. Examples: \\\"Which mode do you prefer\\\", \\\"select an option\\\", \\\"no issues / problems / violations / blockers found\\\".\\n\\n2. ALLOW (return `{}`): Output contains at least one concrete file:line citation \\u2014 e.g. `src/auth.ts:42`, `lib/db.ts:100-115`, or path references that include line numbers.\\n\\n3. BLOCK (return `{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"<one-line explanation>\\\"}`): Output claims about code or findings WITHOUT concrete file:line citations. Vague references like \\\"the auth handler\\\" or \\\"near the database code\\\" are not acceptable as the only evidence.\\n\\nSubagent invocation JSON:\\n$ARGUMENTS\\n\\nReturn ONLY the JSON decision (no commentary, no markdown fences).\",\n \"timeout\": 30\n }\n ]\n }\n ]\n },\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_STATUSLINE__\",\n \"refreshInterval\": 5\n },\n \"enabledPlugins\": {\n \"docks@docks\": true,\n \"session-relay@docks\": true,\n \"plan-lifecycle@docks\": true,\n \"effect-kit@docks\": true,\n \"php-lsp@claude-plugins-official\": true,\n \"typescript-lsp@claude-plugins-official\": true\n },\n \"extraKnownMarketplaces\": {\n \"docks\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"DocksDocks/docks\"\n }\n }\n },\n \"alwaysThinkingEnabled\": true,\n \"showThinkingSummaries\": true,\n \"viewMode\": \"default\",\n \"theme\": \"dark-daltonized\",\n \"skipDangerousModePermissionPrompt\": true\n}\n",
12
+ "SoT/.claude/settings.json": "{\n \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n \"minimumVersion\": \"2.1.219\",\n \"model\": \"opus\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": true,\n \"skillListingMaxDescChars\": 2048,\n \"respectGitignore\": true,\n \"cleanupPeriodDays\": 14,\n \"skillListingBudgetFraction\": 0.05,\n \"env\": {\n \"CLAUDE_CODE_MAX_OUTPUT_TOKENS\": \"64000\",\n \"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR\": \"1\",\n \"CLAUDE_CODE_AUTO_COMPACT_WINDOW\": \"468000\",\n \"CLAUDE_CODE_NO_FLICKER\": \"1\"\n },\n \"permissions\": {\n \"defaultMode\": \"auto\",\n \"allow\": [\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebFetch\",\n \"WebSearch\",\n \"Edit(./)\",\n \"Bash(git *)\",\n \"Bash(git add *)\",\n \"Bash(git commit *)\",\n \"Bash(git status *)\",\n \"Bash(git diff *)\",\n \"Bash(git log *)\",\n \"Bash(git branch *)\",\n \"Bash(git checkout *)\",\n \"Bash(git switch *)\",\n \"Bash(git stash *)\",\n \"Bash(git fetch *)\",\n \"Bash(git pull *)\",\n \"Bash(git tag *)\",\n \"Bash(git show *)\",\n \"Bash(git blame *)\",\n \"Bash(git worktree *)\",\n \"Bash(gh *)\",\n \"Bash(pnpm *)\",\n \"Bash(npm *)\",\n \"Bash(npx *)\",\n \"Bash(node *)\",\n \"Bash(docker *)\",\n \"Bash(docker-compose *)\",\n \"Bash(ls *)\",\n \"Bash(cat *)\",\n \"Bash(find *)\",\n \"Bash(grep *)\",\n \"Bash(head *)\",\n \"Bash(tail *)\",\n \"Bash(wc *)\",\n \"Bash(sort *)\",\n \"Bash(uniq *)\",\n \"Bash(diff *)\",\n \"Bash(which *)\",\n \"Bash(pwd *)\",\n \"Bash(date *)\",\n \"Bash(mkdir *)\",\n \"Bash(basename *)\",\n \"Bash(dirname *)\",\n \"Bash(realpath *)\",\n \"Bash(jq *)\",\n \"Bash(curl *)\",\n \"Bash(tree *)\",\n \"Bash(sed *)\",\n \"Bash(awk *)\",\n \"Bash(cut *)\",\n \"Bash(tr *)\",\n \"Bash(tee *)\",\n \"Bash(echo *)\",\n \"Bash(printf *)\",\n \"Bash(env *)\",\n \"Bash(printenv *)\",\n \"Bash(uname *)\",\n \"Bash(file *)\",\n \"Bash(stat *)\",\n \"Bash(du *)\",\n \"Bash(id *)\",\n \"Bash(whoami *)\",\n \"Bash(php *)\",\n \"Bash(composer *)\",\n \"Bash(python3 *)\",\n \"Bash(python *)\",\n \"Bash(pip *)\",\n \"Bash(pip3 *)\"\n ],\n \"deny\": [\n \"Read(**/.env)\",\n \"Read(**/.env.local)\",\n \"Read(**/secrets/**)\",\n \"Read(**/*.key)\",\n \"Read(**/*.pem)\",\n \"Read(**/*.p12)\",\n \"Read(**/.credentials*)\",\n \"Edit(**/.env)\",\n \"Edit(**/.env.local)\",\n \"Edit(**/secrets/**)\",\n \"Bash(sudo *)\",\n \"Bash(rm -rf /)\",\n \"Bash(rm -rf / *)\",\n \"Bash(rm -rf ~)\",\n \"Bash(rm -rf ~ *)\",\n \"Bash(rm -rf $HOME)\",\n \"Bash(rm -rf $HOME *)\",\n \"Bash(> /dev *)\",\n \"Bash(dd if= *)\",\n \"Bash(mkfs *)\",\n \"Bash(eval *)\",\n \"Bash(chmod 777 *)\",\n \"Bash(chmod -R 777 *)\",\n \"Bash(git push --force origin main *)\",\n \"Bash(git push --force origin master *)\",\n \"Bash(git push -f origin main *)\",\n \"Bash(git push -f origin master *)\"\n ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(docker system prune *)\"\n ]\n },\n \"hooks\": {\n \"SessionStart\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_SESSION_START__\"],\n \"timeout\": 5\n }\n ]\n }\n ],\n \"Notification\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_NOTIFY__\"],\n \"timeout\": 10,\n \"async\": true\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo '{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PostToolUseFailure\\\",\\\"additionalContext\\\":\\\"Last bash command failed. Repository / file state may have shifted \\u2014 re-read affected files before retrying. If the failure is a missing dependency or env mismatch, surface it to the user rather than retrying blindly.\\\"}}'\",\n \"timeout\": 5\n }\n ]\n }\n ],\n \"SubagentStop\": [\n {\n \"hooks\": [\n {\n \"type\": \"prompt\",\n \"prompt\": \"You are a quality gate for subagent outputs in a multi-agent code-analysis pipeline.\\n\\nEvaluate the subagent's `last_assistant_message` field (in the JSON below) against these requirements:\\n\\n1. ALLOW (return `{}`): Mode-selection or no-issues responses. Examples: \\\"Which mode do you prefer\\\", \\\"select an option\\\", \\\"no issues / problems / violations / blockers found\\\".\\n\\n2. ALLOW (return `{}`): Output contains at least one concrete file:line citation \\u2014 e.g. `src/auth.ts:42`, `lib/db.ts:100-115`, or path references that include line numbers.\\n\\n3. BLOCK (return `{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"<one-line explanation>\\\"}`): Output claims about code or findings WITHOUT concrete file:line citations. Vague references like \\\"the auth handler\\\" or \\\"near the database code\\\" are not acceptable as the only evidence.\\n\\nSubagent invocation JSON:\\n$ARGUMENTS\\n\\nReturn ONLY the JSON decision (no commentary, no markdown fences).\",\n \"timeout\": 30\n }\n ]\n }\n ]\n },\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_STATUSLINE__\",\n \"refreshInterval\": 5\n },\n \"enabledPlugins\": {\n \"docks@docks\": true,\n \"plan-lifecycle@docks\": true,\n \"effect-kit@docks\": true,\n \"php-lsp@claude-plugins-official\": true,\n \"typescript-lsp@claude-plugins-official\": true\n },\n \"extraKnownMarketplaces\": {\n \"docks\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"DocksDocks/docks\"\n }\n }\n },\n \"alwaysThinkingEnabled\": true,\n \"showThinkingSummaries\": true,\n \"viewMode\": \"default\",\n \"theme\": \"dark-daltonized\",\n \"skipDangerousModePermissionPrompt\": true\n}\n",
13
13
  "SoT/.claude/bin/statusline.mjs": "const ESC = \"\\x1b[\"\nconst PIPE = `${ESC}90m | ${ESC}0m`\nconst DOT = `${ESC}90m • ${ESC}0m`\nconst DIM = `${ESC}2m${ESC}38;2;156;162;175m`\n\nfunction isRecord(value) {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction finitePercentage(value) {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 && value <= 100\n ? value\n : undefined\n}\n\nfunction roundHalfEven(value) {\n const lower = Math.floor(value)\n const fraction = value - lower\n if (fraction < 0.5) return lower\n if (fraction > 0.5) return lower + 1\n return lower % 2 === 0 ? lower : lower + 1\n}\n\nfunction pathBasename(path) {\n const parts = path.split(/[\\\\/]+/).filter((part) => part !== \"\")\n return parts.at(-1) ?? \"\"\n}\n\nfunction modelName(input) {\n const model = isRecord(input.model) && typeof input.model.display_name === \"string\"\n ? input.model.display_name\n : \"\"\n const suffix = model.indexOf(\" (\")\n return suffix === -1 ? model : model.slice(0, suffix)\n}\n\nfunction workingDirectory(input, cwd) {\n if (isRecord(input.workspace) && typeof input.workspace.current_dir === \"string\" && input.workspace.current_dir !== \"\") {\n return input.workspace.current_dir\n }\n if (typeof input.cwd === \"string\" && input.cwd !== \"\") return input.cwd\n return cwd\n}\n\nfunction compactWindow(env, total) {\n const raw = env.CLAUDE_CODE_AUTO_COMPACT_WINDOW\n if (typeof raw !== \"string\" || !/^[0-9]+$/.test(raw)) return total\n const parsed = Number(raw)\n return Number.isSafeInteger(parsed) && parsed >= 1000 && parsed < total ? parsed : total\n}\n\nfunction formatTokensK(value) {\n if (value < 1000) return `${value}k`\n if (value % 1000 === 0) return `${value / 1000}M`\n return `${(roundHalfEven(value / 100) / 10).toFixed(1)}M`\n}\n\nfunction contextSegment(input, env) {\n if (!isRecord(input.context_window)) return \"\"\n const used = finitePercentage(input.context_window.used_percentage)\n if (used === undefined) return \"\"\n\n const total = input.context_window.context_window_size\n if (typeof total !== \"number\" || !Number.isFinite(total) || total <= 0) {\n return `${ESC}38;2;130;160;230mctx ${roundHalfEven(used)}%${ESC}0m`\n }\n\n const usedK = roundHalfEven((used / 100) * (total / 1000))\n const effectiveK = Math.trunc(compactWindow(env, total) / 1000)\n if (effectiveK <= 0) return `${ESC}38;2;130;160;230mctx ${roundHalfEven(used)}%${ESC}0m`\n const effectivePercentage = roundHalfEven((usedK / effectiveK) * 100)\n return `${ESC}38;2;130;160;230mctx ${effectivePercentage}%${ESC}0m ${DIM}(${formatTokensK(usedK)}/${formatTokensK(effectiveK)})${ESC}0m`\n}\n\nfunction resetDelta(value, nowMs) {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) return \"\"\n const seconds = Math.trunc(value) - Math.floor(nowMs / 1000)\n if (seconds <= 0) return \"now\"\n const days = Math.trunc(seconds / 86_400)\n if (days > 0) return `${days}d`\n const hours = Math.trunc((seconds % 86_400) / 3_600)\n if (hours > 0) return `${hours}h`\n return `${Math.trunc((seconds % 3_600) / 60)}m`\n}\n\nfunction quotaWindow(value, label, color, nowMs) {\n if (!isRecord(value)) return \"\"\n const used = finitePercentage(value.used_percentage)\n if (used === undefined) return \"\"\n const delta = resetDelta(value.resets_at, nowMs)\n const reset = delta === \"\" ? \"\" : ` ${DIM}(${delta})${ESC}0m`\n return `${ESC}38;2;${color}m${label} ${roundHalfEven(used)}%${ESC}0m${reset}`\n}\n\nfunction decodeStdout(result) {\n const stdout = result?.stdout\n if (stdout === undefined || stdout === null) return \"\"\n return typeof stdout === \"string\" ? stdout : stdout.toString()\n}\n\nfunction resolveBranch(directory, which, spawnSync) {\n const git = which(\"git\")\n if (typeof git !== \"string\" || git === \"\") return \"\"\n const commands = [\n [git, \"-C\", directory, \"symbolic-ref\", \"--short\", \"HEAD\"],\n [git, \"-C\", directory, \"rev-parse\", \"--short\", \"HEAD\"]\n ]\n for (const command of commands) {\n try {\n const result = spawnSync(command, { stdin: \"ignore\", stdout: \"pipe\", stderr: \"ignore\" })\n if (result?.success === true) return decodeStdout(result).trim()\n } catch {\n return \"\"\n }\n }\n return \"\"\n}\n\nexport function formatStatusline(input, options = {}) {\n if (!isRecord(input)) return \"\"\n const env = isRecord(options.env) ? options.env : process.env\n const nowMs = typeof options.nowMs === \"number\" ? options.nowMs : Date.now()\n const cwd = typeof options.cwd === \"string\" ? options.cwd : process.cwd()\n const branch = typeof options.branch === \"string\" ? options.branch : \"\"\n const directory = workingDirectory(input, cwd)\n\n const model = `${ESC}38;5;208m${ESC}1m${modelName(input)}${ESC}22m${ESC}0m`\n const folder = `${ESC}1m${ESC}38;2;76;208;222m${pathBasename(directory)}${ESC}22m${ESC}0m`\n const branchSegment = branch === \"\" ? \"\" : `${DOT}${ESC}1m${ESC}38;2;192;103;222m${branch}${ESC}22m${ESC}0m`\n const context = contextSegment(input, env)\n\n const rateLimits = isRecord(input.rate_limits) ? input.rate_limits : {}\n const fiveHour = quotaWindow(rateLimits.five_hour, \"5h\", \"100;200;200\", nowMs)\n const sevenDay = quotaWindow(rateLimits.seven_day, \"7d\", \"230;180;90\", nowMs)\n const quota = fiveHour === \"\" && sevenDay === \"\"\n ? \"\"\n : `${PIPE}${fiveHour}${fiveHour !== \"\" && sevenDay !== \"\" ? DOT : \"\"}${sevenDay}`\n\n return `${model}${PIPE}${folder}${branchSegment}${context === \"\" ? \"\" : `${PIPE}${context}`}${quota}`\n}\n\nexport async function main(options = {}) {\n const readStdin = options.readStdin ?? (() => Bun.stdin.text())\n const writeStdout = options.writeStdout ?? ((value) => process.stdout.write(value))\n let raw\n try {\n raw = await readStdin()\n } catch {\n return 0\n }\n\n let input\n try {\n input = JSON.parse(raw)\n } catch {\n return 0\n }\n if (!isRecord(input)) return 0\n\n const cwd = typeof options.cwd === \"string\" ? options.cwd : process.cwd()\n const directory = workingDirectory(input, cwd)\n const branch = resolveBranch(\n directory,\n options.which ?? ((name) => Bun.which(name)),\n options.spawnSync ?? ((argv, spawnOptions) => Bun.spawnSync(argv, spawnOptions))\n )\n const output = formatStatusline(input, {\n env: options.env ?? process.env,\n nowMs: options.nowMs ?? Date.now(),\n cwd,\n branch\n })\n if (output !== \"\") writeStdout(`${output}\\n`)\n return 0\n}\n\nif (import.meta.main) process.exit(await main())\n",
14
14
  "SoT/.claude/bin/session-start.mjs": "import { readFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\n\nfunction isRecord(value) {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction nonEmpty(value, fallback) {\n return typeof value === \"string\" && value !== \"\" ? value : fallback\n}\n\nfunction pad(value) {\n return String(value).padStart(2, \"0\")\n}\n\nfunction configuredEffort(home, readText) {\n try {\n const parsed = JSON.parse(readText(`${home}/.claude/settings.json`))\n return isRecord(parsed) ? nonEmpty(parsed.effortLevel, \"default\") : \"default\"\n } catch {\n return \"default\"\n }\n}\n\nfunction localZone(now) {\n const part = new Intl.DateTimeFormat(\"en-US\", { timeZoneName: \"short\" })\n .formatToParts(now)\n .find((value) => value.type === \"timeZoneName\")\n return part?.value ?? \"\"\n}\n\nexport function sessionStartLines(options = {}) {\n const env = isRecord(options.env) ? options.env : process.env\n const now = options.now instanceof Date ? options.now : new Date()\n const home = typeof options.home === \"string\" ? options.home : homedir()\n const readText = options.readText ?? ((path) => readFileSync(path, \"utf8\"))\n const weekday = new Intl.DateTimeFormat(\"en-US\", { weekday: \"long\" }).format(now)\n const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`\n const time = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`\n const effort = nonEmpty(env.CLAUDE_CODE_EFFORT_LEVEL, configuredEffort(home, readText))\n const context = env.CLAUDE_CODE_DISABLE_1M_CONTEXT === \"1\" ? \"200K\" : \"1M\"\n const compactWindow = nonEmpty(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW, \"full\")\n const subagent = nonEmpty(env.CLAUDE_CODE_SUBAGENT_MODEL, \"default\")\n return [\n `[CONTEXT] Current date: ${weekday}, ${date} ${time} ${localZone(now)}`,\n `[CONFIG] Context: ${context} | Compact-window: ${compactWindow} | Effort: ${effort} | Thinking: adaptive | Subagent: ${subagent}`\n ]\n}\n\nexport async function main(options = {}) {\n const writeStdout = options.writeStdout ?? ((value) => process.stdout.write(value))\n writeStdout(`${sessionStartLines(options).join(\"\\n\")}\\n`)\n return 0\n}\n\nif (import.meta.main) process.exit(await main())\n",
15
15
  "SoT/.claude/bin/notify.mjs": "const DEFAULT_SOUND = `${import.meta.dir}/../notification.mp3`\n\nexport function selectPlayer(options = {}) {\n const platform = options.platform ?? process.platform\n const sound = options.sound ?? DEFAULT_SOUND\n const which = options.which ?? ((name) => Bun.which(name))\n if (platform === \"darwin\") {\n const afplay = which(\"afplay\")\n if (typeof afplay === \"string\" && afplay !== \"\") return [afplay, sound]\n }\n const ffplay = which(\"ffplay\")\n if (typeof ffplay === \"string\" && ffplay !== \"\") {\n return [ffplay, \"-nodisp\", \"-autoexit\", \"-loglevel\", \"quiet\", sound]\n }\n const paplay = which(\"paplay\")\n if (typeof paplay === \"string\" && paplay !== \"\") return [paplay, sound]\n const aplay = which(\"aplay\")\n if (typeof aplay === \"string\" && aplay !== \"\") return [aplay, \"-q\", sound]\n return undefined\n}\n\nexport async function main(options = {}) {\n const sound = options.sound ?? DEFAULT_SOUND\n const fileExists = options.fileExists ?? ((path) => Bun.file(path).exists())\n if (!await fileExists(sound)) return 0\n const command = selectPlayer({ ...options, sound })\n if (command === undefined) return 0\n const spawnSync = options.spawnSync ?? ((argv, spawnOptions) => Bun.spawnSync(argv, spawnOptions))\n const result = spawnSync(command, { stdin: \"ignore\", stdout: \"ignore\", stderr: \"ignore\" })\n return typeof result.exitCode === \"number\" ? result.exitCode : 1\n}\n\nif (import.meta.main) process.exit(await main())\n",
16
16
  "SoT/.codex/AGENTS.md": "# AGENTS.md\n\n## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\nModel-agnostic operating rules for coding-agent work.\n\n1. Persistence. Keep going until the user's request is actually handled. Only yield when the problem is solved or a concrete blocker is identified. Resolve in the fewest useful tool loops — once you can answer the core request with evidence, answer. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done, do that work now.\n2. Default to parallel. When multiple reads, searches, inspections, or independent checks can run without depending on each other, run them together.\n3. Multi-pass search. First-pass search often misses — vary the wording before concluding something does not exist.\n4. Trace symbols. Before modifying a symbol, trace its definition and usages. Do not infer behavior from one call site.\n5. Linter-loop 3-strike rule. Do not loop more than 3 times fixing the same lint/test failure without reassessing the diagnosis.\n6. Read-before-edit TTL. If you have not read a file recently, re-read it before editing. User edits can make cached context stale.\n7. Big-file rule. For files over 1000 lines, prefer targeted search plus scoped reads over whole-file reads.\n8. Task hygiene. Track meaningful deliverables, not operational sub-steps. Mark work complete as soon as it is done.\n9. Literal-instruction rule. Treat explicit user requirements as checklists with success criteria. Do not silently broaden scope.\n10. Context hygiene. Prefer a fresh session at task boundaries over carrying stale context; preserve useful state before quality decays. Never stop, summarize, or suggest a new session on account of context limits.\n11. Autonomy calibration. For minor choices (naming, formatting, defaults, equivalent approaches), pick a reasonable option and note it — do not ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; do not apply fixes until asked.\n12. Capability triggering. Search or fetch current documentation when the answer depends on current or version-specific information. When work fans out across independent items, parallelize or delegate; never delegate work you can complete directly. For verification, prefer a fresh-context check over self-critique. On long tasks, keep running notes and re-read them between phases.\n\n<constraint>\nTreat these heuristics as protocol. If a turn violates an applicable rule, self-correct before continuing.\n</constraint>\n\nReuse before invention: inventory existing code, components, conventions, and dependencies; extend them instead of creating a parallel pattern. Load only the narrow skills supported by the task and repository evidence. If a request establishes a new React/Tailwind system and no convention exists, prefer current shadcn/ui `base-*` components backed by Base UI; otherwise preserve the existing stack. Treat probe, production access, publish, push, release, and deploy as literal current-request effects—never infer external authority from a plan, schedule, review, or old receipt.\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
17
- "SoT/.codex/config.toml": "model = \"gpt-5.6-sol\"\nmodel_reasoning_effort = \"high\"\nplan_mode_reasoning_effort = \"high\"\nmodel_reasoning_summary = \"concise\"\nmodel_verbosity = \"low\"\npersonality = \"pragmatic\"\nweb_search = \"live\"\nproject_doc_max_bytes = 131072\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\napprovals_reviewer = \"auto_review\"\n\n[sandbox_workspace_write]\nnetwork_access = true\n\n[features]\nmemories = true\n\n[memories]\ndedicated_tools = true\nmax_rollout_age_days = 30\n\n[agents]\nmax_threads = 12\nmax_depth = 2\n\n[tui]\nstatus_line_use_colors = true\nstatus_line = [\n \"model-with-reasoning\",\n \"current-dir\",\n \"git-branch\",\n \"context-used\",\n \"five-hour-limit\",\n \"weekly-limit\",\n]\n\n[plugins.\"docks@docks\"]\nenabled = true\n\n[plugins.\"session-relay@docks\"]\nenabled = true\n\n[plugins.\"plan-lifecycle@docks\"]\nenabled = true\n\n[plugins.\"effect-kit@docks\"]\nenabled = true\n",
18
- "SoT/.codex/plugins/marketplace.json": "{\n \"name\": \"docks\",\n \"interface\": {\n \"displayName\": \"DocksDocks\"\n },\n \"plugins\": [\n {\n \"name\": \"docks\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/docks\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"session-relay\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/session-relay\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"effect-kit\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/effect-kit\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"plan-lifecycle\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/plan-lifecycle\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n }\n ]\n}\n",
17
+ "SoT/.codex/config.toml": "model = \"gpt-5.6-sol\"\nmodel_reasoning_effort = \"high\"\nplan_mode_reasoning_effort = \"high\"\nmodel_reasoning_summary = \"concise\"\nmodel_verbosity = \"low\"\npersonality = \"pragmatic\"\nweb_search = \"live\"\nproject_doc_max_bytes = 131072\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\napprovals_reviewer = \"auto_review\"\n\n[sandbox_workspace_write]\nnetwork_access = true\n\n[features]\nmemories = true\n\n[memories]\ndedicated_tools = true\nmax_rollout_age_days = 30\n\n[agents]\nmax_threads = 12\nmax_depth = 2\n\n[tui]\nstatus_line_use_colors = true\nstatus_line = [\n \"model-with-reasoning\",\n \"current-dir\",\n \"git-branch\",\n \"context-used\",\n \"five-hour-limit\",\n \"weekly-limit\",\n]\n\n[plugins.\"docks@docks\"]\nenabled = true\n\n[plugins.\"plan-lifecycle@docks\"]\nenabled = true\n\n[plugins.\"effect-kit@docks\"]\nenabled = true\n",
18
+ "SoT/.codex/plugins/marketplace.json": "{\n \"name\": \"docks\",\n \"interface\": {\n \"displayName\": \"DocksDocks\"\n },\n \"plugins\": [\n {\n \"name\": \"docks\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/docks\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"effect-kit\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/effect-kit\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"plan-lifecycle\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/plan-lifecycle\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n }\n ]\n}\n",
19
19
  "SoT/.codex/rules/docks.rules": "prefix_rule(pattern=[\"pwd\"], decision=\"allow\")\nprefix_rule(pattern=[\"ls\"], decision=\"allow\")\nprefix_rule(pattern=[\"cat\"], decision=\"allow\")\nprefix_rule(pattern=[\"head\"], decision=\"allow\")\nprefix_rule(pattern=[\"tail\"], decision=\"allow\")\nprefix_rule(pattern=[\"wc\"], decision=\"allow\")\nprefix_rule(pattern=[\"nl\"], decision=\"allow\")\nprefix_rule(pattern=[\"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"sort\"], decision=\"allow\")\nprefix_rule(pattern=[\"uniq\"], decision=\"allow\")\nprefix_rule(pattern=[\"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"which\"], decision=\"allow\")\nprefix_rule(pattern=[\"date\"], decision=\"allow\")\nprefix_rule(pattern=[\"basename\"], decision=\"allow\")\nprefix_rule(pattern=[\"dirname\"], decision=\"allow\")\nprefix_rule(pattern=[\"realpath\"], decision=\"allow\")\nprefix_rule(pattern=[\"readlink\"], decision=\"allow\")\nprefix_rule(pattern=[\"jq\"], decision=\"allow\")\nprefix_rule(pattern=[\"tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"cut\"], decision=\"allow\")\nprefix_rule(pattern=[\"tr\"], decision=\"allow\")\nprefix_rule(pattern=[\"echo\"], decision=\"allow\")\nprefix_rule(pattern=[\"printf\"], decision=\"allow\")\nprefix_rule(pattern=[\"printenv\"], decision=\"allow\")\nprefix_rule(pattern=[\"uname\"], decision=\"allow\")\nprefix_rule(pattern=[\"file\"], decision=\"allow\")\nprefix_rule(pattern=[\"stat\"], decision=\"allow\")\nprefix_rule(pattern=[\"du\"], decision=\"allow\")\nprefix_rule(pattern=[\"id\"], decision=\"allow\")\nprefix_rule(pattern=[\"whoami\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"log\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"show\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"blame\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"rev-parse\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-files\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"--show-current\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"-vv\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"mv\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"gh\", \"pr\", \"view\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"list\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"checks\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"docker\", \"ps\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"push\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"reset\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"clean\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"merge\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"rebase\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"checkout\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"mv\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chmod\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chown\"], decision=\"prompt\")\nprefix_rule(pattern=[\"kill\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pkill\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"npm\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"npm\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"uninstall\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"docker\", \"run\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"volume\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"system\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"stop\"], decision=\"prompt\")\n\n# `tail -f`/`--follow` never returns and hangs the agent (bare `tail` stays allowed above).\nprefix_rule(pattern=[\"tail\", \"-f\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tail\", \"--follow\"], decision=\"prompt\")\n# rg and `sed -n` are prompt, NOT allow: argv-prefix matching cannot gate their\n# code-exec forms (rg --pre=CMD or a reordered --pre; sed -n 'e CMD' / -ni) while a\n# shorter allow prefix would auto-approve the whole command.\nprefix_rule(pattern=[\"rg\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-n\"], decision=\"prompt\")\nprefix_rule(pattern=[\"find\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-i\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"--in-place\"], decision=\"prompt\")\nprefix_rule(pattern=[\"awk\"], decision=\"prompt\")\nprefix_rule(pattern=[\"xargs\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tee\"], decision=\"prompt\")\nprefix_rule(pattern=[\"curl\"], decision=\"prompt\")\nprefix_rule(pattern=[\"env\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"sudo\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"eval\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"mkfs\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"dd\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"master\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"master\"], decision=\"forbidden\")\n"
20
20
  } as const
21
21
 
@@ -40,4 +40,4 @@ export const GENERATED_PAYLOAD_PATHS = [
40
40
  "notification.mp3"
41
41
  ] as const
42
42
 
43
- export const GENERATED_PAYLOAD_HASH = "65eac78bfc9193237ae14053d38a77c5b5a5c5fd49eb3670d3a31ed7ec3f9a48"
43
+ export const GENERATED_PAYLOAD_HASH = "dd259569b864fe7c2e1c54e8e0155d529b1e2f0f2b515f32178976c55ff9e7e3"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docks-kit",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "Portable AI coding agent config kit — SoT sync engine + typed CLI for Claude Code, Codex, and universal agent skills",
5
5
  "type": "module",
6
6
  "license": "MIT",