docks-kit 0.17.1 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/AGENTS.md +36 -32
  2. package/cli/docs/omp-models.md +88 -53
  3. package/cli/src/argv.ts +38 -469
  4. package/cli/src/argvSurface.ts +371 -0
  5. package/cli/src/argvValidate.ts +122 -0
  6. package/cli/src/commands/docs.ts +57 -42
  7. package/cli/src/commands/harnesses.ts +38 -45
  8. package/cli/src/commands/model.ts +48 -53
  9. package/cli/src/commands/models.ts +24 -26
  10. package/cli/src/commands/omp.ts +132 -112
  11. package/cli/src/commands/plugins.ts +20 -20
  12. package/cli/src/commands/skills.ts +22 -20
  13. package/cli/src/commands/status.ts +87 -80
  14. package/cli/src/commands/sync.ts +113 -87
  15. package/cli/src/commands/toolchain.ts +21 -21
  16. package/cli/src/commands/update.ts +139 -117
  17. package/cli/src/efforts.ts +35 -34
  18. package/cli/src/engine-native/DESIGN.md +30 -30
  19. package/cli/src/engine-native/bun.ts +77 -55
  20. package/cli/src/engine-native/claudeLsp.ts +154 -0
  21. package/cli/src/engine-native/claudeOptionalPlugins.ts +113 -0
  22. package/cli/src/engine-native/claudePluginPasses.ts +348 -0
  23. package/cli/src/engine-native/claudeRemovals.ts +195 -0
  24. package/cli/src/engine-native/claudeRetired.ts +4 -4
  25. package/cli/src/engine-native/claudeRuntime.ts +94 -68
  26. package/cli/src/engine-native/claudeSettings.ts +174 -0
  27. package/cli/src/engine-native/claudeSettingsModifiers.ts +50 -54
  28. package/cli/src/engine-native/claudeSync.ts +176 -456
  29. package/cli/src/engine-native/codexConfig.ts +158 -0
  30. package/cli/src/engine-native/codexHooks.ts +91 -0
  31. package/cli/src/engine-native/codexPlugins.ts +290 -0
  32. package/cli/src/engine-native/codexStatus.ts +24 -0
  33. package/cli/src/engine-native/codexSync.ts +110 -568
  34. package/cli/src/engine-native/codexToml.ts +140 -138
  35. package/cli/src/engine-native/deps.ts +144 -107
  36. package/cli/src/engine-native/engineCtx.ts +126 -0
  37. package/cli/src/engine-native/exec.ts +98 -87
  38. package/cli/src/engine-native/failures.ts +3 -3
  39. package/cli/src/engine-native/harnesses.ts +62 -72
  40. package/cli/src/engine-native/index.ts +34 -315
  41. package/cli/src/engine-native/jq.ts +25 -24
  42. package/cli/src/engine-native/logger.ts +98 -100
  43. package/cli/src/engine-native/models.ts +50 -45
  44. package/cli/src/engine-native/modes.ts +83 -80
  45. package/cli/src/engine-native/ompFileDeploy.ts +111 -0
  46. package/cli/src/engine-native/ompMarketplace.ts +136 -0
  47. package/cli/src/engine-native/ompOverlay.ts +67 -64
  48. package/cli/src/engine-native/ompPaths.ts +49 -38
  49. package/cli/src/engine-native/ompPlugins.ts +170 -0
  50. package/cli/src/engine-native/ompSync.ts +64 -408
  51. package/cli/src/engine-native/ompYaml.ts +43 -41
  52. package/cli/src/engine-native/os/darwin.ts +5 -5
  53. package/cli/src/engine-native/os/index.ts +15 -15
  54. package/cli/src/engine-native/os/linux.ts +5 -5
  55. package/cli/src/engine-native/os/posix.ts +28 -24
  56. package/cli/src/engine-native/os/targets.ts +17 -17
  57. package/cli/src/engine-native/os/types.ts +31 -31
  58. package/cli/src/engine-native/os/windows.ts +61 -61
  59. package/cli/src/engine-native/parseArgs.ts +122 -372
  60. package/cli/src/engine-native/parseHelp.ts +66 -0
  61. package/cli/src/engine-native/parseModifiers.ts +222 -0
  62. package/cli/src/engine-native/services.ts +34 -34
  63. package/cli/src/engine-native/settings.ts +15 -15
  64. package/cli/src/engine-native/sharedTypes.d.ts +46 -0
  65. package/cli/src/engine-native/skillsInstall.ts +105 -0
  66. package/cli/src/engine-native/skillsLinks.ts +203 -0
  67. package/cli/src/engine-native/skillsManifest.ts +16 -0
  68. package/cli/src/engine-native/skillsPrune.ts +108 -0
  69. package/cli/src/engine-native/skillsSync.ts +28 -362
  70. package/cli/src/engine-native/syncConcurrency.ts +55 -0
  71. package/cli/src/engine-native/syncDispatch.ts +141 -0
  72. package/cli/src/engine-native/toolchain.ts +65 -62
  73. package/cli/src/engine.ts +65 -57
  74. package/cli/src/generated/sotPayload.ts +7 -7
  75. package/cli/src/kitHome.ts +44 -40
  76. package/cli/src/main.ts +63 -56
  77. package/cli/src/manifests.ts +53 -54
  78. package/cli/src/md.d.ts +2 -2
  79. package/cli/src/payload.ts +9 -9
  80. package/cli/src/services.ts +21 -13
  81. package/cli/tsconfig.json +4 -1
  82. package/package.json +9 -2
  83. package/cli/src/engine-native/claudePlugins.ts +0 -510
@@ -7,12 +7,12 @@
7
7
  * install; this registry owns WHICH external tools exist, whether they are
8
8
  * required, and the one-line command that installs a missing one.
9
9
  */
10
- import { homedir } from "node:os"
11
- import { isAbsolute } from "node:path"
10
+ import { homedir } from "node:os";
11
+ import { isAbsolute } from "node:path";
12
12
 
13
- import { capture, commandExists, p, which } from "./exec"
14
- import { isObject, parseJson } from "./jq"
15
- import { hostOs, platformName, rawPlatform } from "./os"
13
+ import { capture, commandExists, p, which } from "./exec";
14
+ import { isObject, parseJson } from "./jq";
15
+ import { hostOs, platformName, rawPlatform } from "./os";
16
16
 
17
17
  export type ToolId =
18
18
  | "git"
@@ -35,71 +35,74 @@ export type ToolId =
35
35
  | "apt-get"
36
36
  | "dnf"
37
37
  | "pacman"
38
- | "zypper"
38
+ | "zypper";
39
39
 
40
40
  /** required = the engine aborts when missing; optional = warn + degrade. */
41
- export type Requirement = "required" | "optional"
41
+ export type Requirement = "required" | "optional";
42
42
 
43
43
  export type ProbeResult =
44
44
  | { readonly state: "present"; readonly path?: string }
45
- | { readonly state: "missing" }
45
+ | { readonly state: "missing" };
46
46
 
47
47
  export interface ProbeExecutor {
48
- readonly commandExists: (name: string) => boolean
49
- readonly capture: (cmd: string, args: ReadonlyArray<string>) => Promise<string>
50
- readonly which: (name: string) => string
48
+ readonly commandExists: (name: string) => boolean;
49
+ readonly capture: (cmd: string, args: ReadonlyArray<string>) => Promise<string>;
50
+ readonly which: (name: string) => string;
51
51
  }
52
52
 
53
53
  export interface DependencySpec {
54
- readonly id: ToolId
55
- readonly requirement: Requirement
56
- readonly versionArgs: ReadonlyArray<string>
54
+ readonly id: ToolId;
55
+ readonly requirement: Requirement;
56
+ readonly versionArgs: ReadonlyArray<string>;
57
57
  /** Platform-correct one-line install command (param injectable for tests). */
58
- readonly installHint: (platform?: NodeJS.Platform) => string
59
- readonly resolve?: (exec: ProbeExecutor, platform: NodeJS.Platform) => ProbeResult
60
- readonly version?: (exec: ProbeExecutor) => Promise<string>
58
+ readonly installHint: (platform?: NodeJS.Platform) => string;
59
+ readonly resolve?: (exec: ProbeExecutor, platform: NodeJS.Platform) => ProbeResult;
60
+ readonly version?: (exec: ProbeExecutor) => Promise<string>;
61
61
  }
62
62
 
63
63
  interface SpecOptions {
64
- readonly versionArgs?: ReadonlyArray<string>
65
- readonly resolve?: (exec: ProbeExecutor, platform: NodeJS.Platform) => ProbeResult
66
- readonly version?: (exec: ProbeExecutor) => Promise<string>
64
+ readonly versionArgs?: ReadonlyArray<string>;
65
+ readonly resolve?: (exec: ProbeExecutor, platform: NodeJS.Platform) => ProbeResult;
66
+ readonly version?: (exec: ProbeExecutor) => Promise<string>;
67
67
  }
68
68
 
69
69
  const spec = (
70
70
  id: ToolId,
71
71
  requirement: Requirement,
72
72
  installHint: (platform?: NodeJS.Platform) => string,
73
- options: SpecOptions = {}
73
+ options: SpecOptions = {},
74
74
  ): DependencySpec => ({
75
75
  id,
76
76
  requirement,
77
77
  versionArgs: options.versionArgs ?? ["--version"],
78
78
  installHint,
79
79
  resolve: options.resolve,
80
- version: options.version
81
- })
80
+ version: options.version,
81
+ });
82
82
 
83
83
  // Presence is the injected executor's verdict; `which` only resolves WHERE the
84
84
  // tool is, which on Windows is the `.cmd`/`.exe` candidate rather than the name.
85
- const pathProbe = (id: string): ((exec: ProbeExecutor) => ProbeResult) =>
85
+ const pathProbe =
86
+ (id: string): ((exec: ProbeExecutor) => ProbeResult) =>
86
87
  (exec) => {
87
- if (!exec.commandExists(id)) return { state: "missing" }
88
- const path = exec.which(id)
89
- return path === "" ? { state: "present" } : { state: "present", path }
90
- }
88
+ if (!exec.commandExists(id)) return { state: "missing" };
89
+ const path = exec.which(id);
90
+ return path === "" ? { state: "present" } : { state: "present", path };
91
+ };
91
92
 
92
- const versionProbe = (
93
- id: string,
94
- versionArgs: ReadonlyArray<string> = ["--version"],
95
- parse: (out: string) => string = (out) => out
96
- ): ((exec: ProbeExecutor) => Promise<string>) =>
97
- async (exec) => parse(await exec.capture(id, versionArgs))
93
+ const versionProbe =
94
+ (
95
+ id: string,
96
+ versionArgs: ReadonlyArray<string> = ["--version"],
97
+ parse: (out: string) => string = (out) => out,
98
+ ): ((exec: ProbeExecutor) => Promise<string>) =>
99
+ async (exec) =>
100
+ parse(await exec.capture(id, versionArgs));
98
101
 
99
102
  const home = (): string => {
100
- const envHome = process.env["HOME"]
101
- return envHome !== undefined && envHome !== "" ? envHome : homedir()
102
- }
103
+ const envHome = process.env["HOME"];
104
+ return envHome !== undefined && envHome !== "" ? envHome : homedir();
105
+ };
103
106
 
104
107
  /**
105
108
  * The resolved path gets persisted into global direct-exec hooks, so a
@@ -107,156 +110,190 @@ const home = (): string => {
107
110
  * break outside the sync working directory.
108
111
  */
109
112
  const findBun = (exec: ProbeExecutor): { command: string; path: string } | undefined => {
110
- const onPath = exec.which("bun")
113
+ const onPath = exec.which("bun");
111
114
  if (onPath !== "" && isAbsolute(onPath)) {
112
- return { command: "bun", path: onPath }
115
+ return { command: "bun", path: onPath };
113
116
  }
114
117
  const root =
115
118
  process.env["BUN_INSTALL"] !== undefined && process.env["BUN_INSTALL"] !== ""
116
119
  ? process.env["BUN_INSTALL"]!
117
- : p(home(), ".bun")
120
+ : p(home(), ".bun");
118
121
  for (const candidate of [p(root, "bin", "bun"), p(home(), ".bun", "bin", "bun")]) {
119
- const found = exec.which(candidate)
120
- if (found !== "" && isAbsolute(found)) return { command: found, path: found }
122
+ const found = exec.which(candidate);
123
+ if (found !== "" && isAbsolute(found)) return { command: found, path: found };
121
124
  }
122
- return undefined
123
- }
125
+ return undefined;
126
+ };
124
127
 
125
128
  const resolveBun = (exec: ProbeExecutor): ProbeResult => {
126
- const bun = findBun(exec)
127
- return bun === undefined
128
- ? { state: "missing" }
129
- : { state: "present", path: bun.path }
130
- }
129
+ const bun = findBun(exec);
130
+ return bun === undefined ? { state: "missing" } : { state: "present", path: bun.path };
131
+ };
131
132
 
132
- const npmGlobalCache = new WeakMap<ProbeExecutor, Promise<{ [k: string]: string }>>()
133
+ const npmGlobalCache = new WeakMap<ProbeExecutor, Promise<{ [k: string]: string }>>();
133
134
 
134
135
  const npmGlobalVersions = (exec: ProbeExecutor): Promise<{ [k: string]: string }> => {
135
- const hit = npmGlobalCache.get(exec)
136
- if (hit !== undefined) return hit
136
+ const hit = npmGlobalCache.get(exec);
137
+ if (hit !== undefined) return hit;
137
138
  const pending = (async (): Promise<{ [k: string]: string }> => {
138
- const out: { [k: string]: string } = {}
139
+ const out: { [k: string]: string } = {};
139
140
  if (exec.commandExists("npm")) {
140
- const doc = parseJson(await exec.capture("npm", ["ls", "-g", "--depth=0", "--json"]))
141
- const deps = doc !== undefined && isObject(doc) && isObject(doc["dependencies"]) ? doc["dependencies"] : {}
141
+ const doc = parseJson(await exec.capture("npm", ["ls", "-g", "--depth=0", "--json"]));
142
+ const deps =
143
+ doc !== undefined && isObject(doc) && isObject(doc["dependencies"])
144
+ ? doc["dependencies"]
145
+ : {};
142
146
  for (const [name, value] of Object.entries(deps)) {
143
- if (isObject(value) && typeof value["version"] === "string") out[name] = value["version"]
147
+ if (isObject(value) && typeof value["version"] === "string") out[name] = value["version"];
144
148
  }
145
149
  }
146
- return out
147
- })()
148
- npmGlobalCache.set(exec, pending)
149
- return pending
150
- }
150
+ return out;
151
+ })();
152
+ npmGlobalCache.set(exec, pending);
153
+ return pending;
154
+ };
151
155
 
152
- const versionNpmGlobal = (pkg: string) => async (exec: ProbeExecutor): Promise<string> =>
153
- (await npmGlobalVersions(exec))[pkg] ?? ""
156
+ const versionNpmGlobal =
157
+ (pkg: string) =>
158
+ async (exec: ProbeExecutor): Promise<string> =>
159
+ (await npmGlobalVersions(exec))[pkg] ?? "";
154
160
 
155
- export const defaultProbeExecutor: ProbeExecutor = { commandExists, capture, which }
161
+ export const defaultProbeExecutor: ProbeExecutor = { commandExists, capture, which };
156
162
 
157
163
  export const DEPENDENCIES: Record<ToolId, DependencySpec> = {
158
164
  git: spec(
159
165
  "git",
160
166
  "optional",
161
167
  (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("git"),
162
- { version: versionProbe("git") }
168
+ { version: versionProbe("git") },
163
169
  ),
164
170
  jq: spec("jq", "optional", (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("jq"), {
165
- version: versionProbe("jq")
166
- }),
167
- curl: spec("curl", "optional", (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("curl"), {
168
- version: versionProbe("curl")
169
- }),
170
- node: spec("node", "optional", () => "install Node.js via https://nodejs.org (or your package manager)", {
171
- version: versionProbe("node")
171
+ version: versionProbe("jq"),
172
172
  }),
173
- npm: spec("npm", "optional", () => "ships with Node.js — install via https://nodejs.org (or your package manager)"),
174
- npx: spec("npx", "optional", () => "ships with Node.js — install via https://nodejs.org (or your package manager)"),
173
+ curl: spec(
174
+ "curl",
175
+ "optional",
176
+ (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("curl"),
177
+ {
178
+ version: versionProbe("curl"),
179
+ },
180
+ ),
181
+ node: spec(
182
+ "node",
183
+ "optional",
184
+ () => "install Node.js via https://nodejs.org (or your package manager)",
185
+ {
186
+ version: versionProbe("node"),
187
+ },
188
+ ),
189
+ npm: spec(
190
+ "npm",
191
+ "optional",
192
+ () => "ships with Node.js — install via https://nodejs.org (or your package manager)",
193
+ ),
194
+ npx: spec(
195
+ "npx",
196
+ "optional",
197
+ () => "ships with Node.js — install via https://nodejs.org (or your package manager)",
198
+ ),
175
199
  claude: spec(
176
200
  "claude",
177
201
  "optional",
178
202
  (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("claude"),
179
- { version: versionProbe("claude") }
203
+ { version: versionProbe("claude") },
180
204
  ),
181
205
  codex: spec(
182
206
  "codex",
183
207
  "optional",
184
208
  (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("codex"),
185
- { version: versionProbe("codex") }
209
+ { version: versionProbe("codex") },
186
210
  ),
187
211
  omp: spec(
188
212
  "omp",
189
213
  "optional",
190
- () => "install omp from https://github.com/can1357/oh-my-pi (an existing install self-updates with `omp update`)",
191
- { version: versionProbe("omp", ["--version"], (out) => out.trim().replace(/^omp[/v]/, "")) }
214
+ () =>
215
+ "install omp from https://github.com/can1357/oh-my-pi (an existing install self-updates with `omp update`)",
216
+ { version: versionProbe("omp", ["--version"], (out) => out.trim().replace(/^omp[/v]/, "")) },
192
217
  ),
193
- bun: spec(
194
- "bun",
218
+ bun: spec("bun", "optional", () => "curl -fsSL https://bun.sh/install | bash", {
219
+ resolve: resolveBun,
220
+ version: async (exec) => {
221
+ const bun = findBun(exec);
222
+ if (bun === undefined) return "";
223
+ return await exec.capture(bun.command, ["--version"]);
224
+ },
225
+ }),
226
+ bwrap: spec(
227
+ "bwrap",
195
228
  "optional",
196
- () => "curl -fsSL https://bun.sh/install | bash",
229
+ () => "sudo apt install -y bubblewrap (or dnf/pacman/zypper equivalent)",
197
230
  {
198
- resolve: resolveBun,
199
- version: async (exec) => {
200
- const bun = findBun(exec)
201
- if (bun === undefined) return ""
202
- return await exec.capture(bun.command, ["--version"])
203
- }
204
- }
231
+ version: versionProbe("bwrap"),
232
+ },
205
233
  ),
206
- bwrap: spec("bwrap", "optional", () => "sudo apt install -y bubblewrap (or dnf/pacman/zypper equivalent)", {
207
- version: versionProbe("bwrap")
208
- }),
209
234
  ffplay: spec(
210
235
  "ffplay",
211
236
  "optional",
212
237
  (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("ffplay"),
213
- { versionArgs: ["-version"], version: versionProbe("ffplay", ["-version"]), resolve: pathProbe("ffplay") }
238
+ {
239
+ versionArgs: ["-version"],
240
+ version: versionProbe("ffplay", ["-version"]),
241
+ resolve: pathProbe("ffplay"),
242
+ },
214
243
  ),
215
244
  intelephense: spec("intelephense", "optional", () => "npm install -g intelephense", {
216
245
  resolve: pathProbe("intelephense"),
217
- version: versionNpmGlobal("intelephense")
246
+ version: versionNpmGlobal("intelephense"),
218
247
  }),
219
248
  "typescript-language-server": spec(
220
249
  "typescript-language-server",
221
250
  "optional",
222
251
  () => "npm install -g typescript-language-server typescript",
223
- { resolve: pathProbe("typescript-language-server"), version: versionProbe("typescript-language-server") }
252
+ {
253
+ resolve: pathProbe("typescript-language-server"),
254
+ version: versionProbe("typescript-language-server"),
255
+ },
224
256
  ),
225
257
  tsc: spec("tsc", "optional", () => "npm install -g typescript", {
226
258
  resolve: pathProbe("tsc"),
227
- version: versionProbe("tsc")
259
+ version: versionProbe("tsc"),
228
260
  }),
229
261
  "rust-analyzer": spec("rust-analyzer", "optional", () => "rustup component add rust-analyzer", {
230
262
  resolve: pathProbe("rust-analyzer"),
231
- version: versionProbe("rust-analyzer")
263
+ version: versionProbe("rust-analyzer"),
232
264
  }),
233
265
  rustup: spec("rustup", "optional", () => "install the Rust toolchain from https://rustup.rs", {
234
- version: versionProbe("rustup")
266
+ version: versionProbe("rustup"),
235
267
  }),
236
268
  "apt-get": spec("apt-get", "optional", () => "install apt via your Linux distribution"),
237
269
  dnf: spec("dnf", "optional", () => "install dnf via your Linux distribution"),
238
270
  pacman: spec("pacman", "optional", () => "install pacman via your Linux distribution"),
239
- zypper: spec("zypper", "optional", () => "install zypper via your Linux distribution")
240
- }
271
+ zypper: spec("zypper", "optional", () => "install zypper via your Linux distribution"),
272
+ };
241
273
 
242
274
  export function resolveDependency(
243
275
  specification: DependencySpec,
244
276
  exec: ProbeExecutor,
245
- platform: NodeJS.Platform = rawPlatform()
277
+ platform: NodeJS.Platform = rawPlatform(),
246
278
  ): ProbeResult {
247
- return (specification.resolve ?? pathProbe(specification.id))(exec, platform)
279
+ return (specification.resolve ?? pathProbe(specification.id))(exec, platform);
248
280
  }
249
281
 
250
- export async function resolveVersion(specification: DependencySpec, exec: ProbeExecutor): Promise<string> {
251
- if (resolveDependency(specification, exec).state !== "present") return ""
252
- return await (specification.version ?? versionProbe(specification.id, specification.versionArgs))(exec)
282
+ export async function resolveVersion(
283
+ specification: DependencySpec,
284
+ exec: ProbeExecutor,
285
+ ): Promise<string> {
286
+ if (resolveDependency(specification, exec).state !== "present") return "";
287
+ return await (specification.version ?? versionProbe(specification.id, specification.versionArgs))(
288
+ exec,
289
+ );
253
290
  }
254
291
 
255
292
  export async function resolvePath(
256
293
  specification: DependencySpec,
257
294
  exec: ProbeExecutor,
258
- platform: NodeJS.Platform = rawPlatform()
295
+ platform: NodeJS.Platform = rawPlatform(),
259
296
  ): Promise<string> {
260
- const result = resolveDependency(specification, exec, platform)
261
- return result.state === "present" ? (result.path ?? exec.which(specification.id)) : ""
297
+ const result = resolveDependency(specification, exec, platform);
298
+ return result.state === "present" ? (result.path ?? exec.which(specification.id)) : "";
262
299
  }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * EngineNative run context: the shared `Ctx` shape plus its construction from
3
+ * the environment. Split from index.ts, which re-exports the public surface
4
+ * so existing import paths keep working.
5
+ */
6
+ import { p } from "./exec";
7
+ import { engineHome } from "./harnesses";
8
+ import type { TerminalLease } from "./logger";
9
+ import type { BunRuntimeState } from "./bun";
10
+ import { kitHome } from "../kitHome";
11
+ import { ExitError, parseClaudePlugin, parseCompactWindow } from "./parseArgs";
12
+ import type { EngineServices } from "./services";
13
+ import type { SyncConcurrency } from "./syncConcurrency";
14
+
15
+ export type ModifierFlag =
16
+ | "--claude-model"
17
+ | "--claude-effort"
18
+ | "--claude-advisor"
19
+ | "--claude-compact-window"
20
+ | "--claude-permissive"
21
+ | "--claude-plugin"
22
+ | "--codex-model"
23
+ | "--codex-effort";
24
+
25
+ export interface Ctx {
26
+ readonly repoDir: string;
27
+ readonly home: string;
28
+ readonly agentsDir: string;
29
+ readonly interactive: boolean;
30
+ dryRun: boolean;
31
+ verbose: boolean;
32
+ skipBubblewrap: boolean;
33
+ skipPluginRefresh?: boolean;
34
+ reconcile: boolean;
35
+ prune: boolean;
36
+ claudeCompactWindow: string;
37
+ claudePermissive: boolean;
38
+ claudePlugins: Array<string>;
39
+ claudeModel: string;
40
+ claudeEffort: string;
41
+ claudeAdvisor: string;
42
+ codexModel: string;
43
+ codexEffort: string;
44
+ /** Distinguishes an explicitly empty modifier from an option that was not supplied. */
45
+ modifierFlags?: Set<ModifierFlag>;
46
+ /** Injected capability seam (logger/deps/platform) — see services.ts. */
47
+ readonly services: EngineServices;
48
+ syncConcurrency: SyncConcurrency;
49
+ terminalLease?: TerminalLease;
50
+ bunRuntime?: Promise<BunRuntimeState>;
51
+ targetFilterSet: boolean;
52
+ syncClaude: boolean;
53
+ syncCodex: boolean;
54
+ syncAgents: boolean;
55
+ syncOmp: boolean;
56
+ /** Per-run next-step triggers (Output Policy): advice prints only when its trigger changed or --verbose. */
57
+ readonly nextStepTriggers: {
58
+ claudePlugins: boolean;
59
+ claudeRestart: boolean;
60
+ codexRestart: boolean;
61
+ skillsRestart: boolean;
62
+ ompRestart: boolean;
63
+ };
64
+ /** Harness-CLI operations that failed this run; a non-empty list fails the sync. */
65
+ readonly failures: Array<string>;
66
+ }
67
+
68
+ /** Globals default from env using the historical ${VAR:-default} contract. */
69
+ export function makeCtx(services: EngineServices): Ctx {
70
+ const env = process.env;
71
+ const home = engineHome(env);
72
+ const compactWindowSource = env["CLAUDE_COMPACT_WINDOW"] ?? "";
73
+ const claudeCompactWindow =
74
+ compactWindowSource === "" ? "" : parseCompactWindow(compactWindowSource);
75
+ if (claudeCompactWindow === undefined) {
76
+ services.logger.err("CLAUDE_COMPACT_WINDOW expects a token count (e.g. 680000 or 680k)");
77
+ throw new ExitError(2);
78
+ }
79
+ const claudePlugins = (env["CLAUDE_PLUGINS"] ?? "")
80
+ .split(" ")
81
+ .filter((plugin) => plugin !== "")
82
+ .map((plugin) => parseClaudePlugin(plugin, services.logger.err));
83
+ return {
84
+ repoDir: kitHome(),
85
+ home,
86
+ agentsDir:
87
+ env["AGENTS_DIR"] !== undefined && env["AGENTS_DIR"] !== ""
88
+ ? env["AGENTS_DIR"]
89
+ : p(home, ".agents"),
90
+ interactive:
91
+ env["DOCKS_KIT_INTERACTIVE"] === "1" ||
92
+ (env["DOCKS_KIT_INTERACTIVE"] !== "0" &&
93
+ process.stdout.isTTY === true &&
94
+ process.stdin.isTTY === true),
95
+ dryRun: env["DRY_RUN"] === "1",
96
+ verbose: env["DOCKS_KIT_VERBOSE"] === "1",
97
+ skipBubblewrap: env["SKIP_BUBBLEWRAP"] === "1",
98
+ skipPluginRefresh: false,
99
+ reconcile: env["RECONCILE"] === "1",
100
+ prune: env["PRUNE"] === "1",
101
+ claudeCompactWindow,
102
+ claudePermissive: env["CLAUDE_PERMISSIVE"] === "1",
103
+ claudePlugins,
104
+ claudeModel: env["CLAUDE_MODEL"] ?? "",
105
+ claudeEffort: "",
106
+ claudeAdvisor: "",
107
+ codexModel: env["CODEX_MODEL"] ?? "",
108
+ codexEffort: "",
109
+ modifierFlags: new Set(),
110
+ syncConcurrency: 3,
111
+ services,
112
+ targetFilterSet: false,
113
+ syncClaude: false,
114
+ syncCodex: false,
115
+ syncAgents: false,
116
+ syncOmp: false,
117
+ nextStepTriggers: {
118
+ claudePlugins: false,
119
+ claudeRestart: false,
120
+ codexRestart: false,
121
+ skillsRestart: false,
122
+ ompRestart: false,
123
+ },
124
+ failures: [],
125
+ };
126
+ }