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
@@ -5,102 +5,101 @@
5
5
  * golden contract. The key is used as a raw regex fragment, preserving the
6
6
  * historical record-oriented behavior.
7
7
  */
8
- import { p } from "./exec"
9
- import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs"
8
+ import { p } from "./exec";
9
+ import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
10
10
 
11
- import { resolveEffort } from "../efforts"
12
- import type { Ctx } from "./index"
11
+ import { resolveEffort } from "../efforts";
12
+ import type { Ctx } from "./index";
13
+ import type { SettingEdit } from "./sharedTypes";
13
14
 
14
15
  export function replaceTopLevelSetting(content: string, key: string, replacement: string): string {
15
- const lines = content.split("\n")
16
- if (lines[lines.length - 1] === "") lines.pop() // awk records exclude a trailing empty split artifact
17
- const keyRe = new RegExp(`^${key}[ \\t]*=`)
18
- const out: Array<string> = []
19
- let inTable = false
20
- let replaced = false
16
+ const lines = content.split("\n");
17
+ if (lines[lines.length - 1] === "") lines.pop(); // awk records exclude a trailing empty split artifact
18
+ const keyRe = new RegExp(`^${key}[ \\t]*=`);
19
+ const out: Array<string> = [];
20
+ let inTable = false;
21
+ let replaced = false;
21
22
  for (const line of lines) {
22
23
  if (line.startsWith("[")) {
23
24
  if (!replaced) {
24
- out.push(replacement)
25
- replaced = true
25
+ out.push(replacement);
26
+ replaced = true;
26
27
  }
27
- inTable = true
28
- out.push(line)
29
- continue
28
+ inTable = true;
29
+ out.push(line);
30
+ continue;
30
31
  }
31
32
  if (!inTable && keyRe.test(line)) {
32
33
  if (!replaced) {
33
- out.push(replacement)
34
- replaced = true
34
+ out.push(replacement);
35
+ replaced = true;
35
36
  }
36
- continue
37
+ continue;
37
38
  }
38
- out.push(line)
39
+ out.push(line);
39
40
  }
40
- if (!replaced) out.push(replacement)
41
- return `${out.join("\n")}\n`
41
+ if (!replaced) out.push(replacement);
42
+ return `${out.join("\n")}\n`;
42
43
  }
43
44
 
44
- export function replaceTopLevelSettingInFile(file: string, key: string, replacement: string): boolean {
45
- const before = readFileSync(file, "utf8")
46
- const next = replaceTopLevelSetting(before, key, replacement)
47
- if (next === before) return false
48
- writeFileSync(`${file}.tmp`, next)
49
- renameSync(`${file}.tmp`, file)
50
- return true
45
+ export function replaceTopLevelSettingInFile(
46
+ file: string,
47
+ key: string,
48
+ replacement: string,
49
+ ): boolean {
50
+ const before = readFileSync(file, "utf8");
51
+ const next = replaceTopLevelSetting(before, key, replacement);
52
+ if (next === before) return false;
53
+ writeFileSync(`${file}.tmp`, next);
54
+ renameSync(`${file}.tmp`, file);
55
+ return true;
51
56
  }
52
57
 
53
- interface CodexSettingEdit {
54
- readonly tag: string
55
- readonly key: "model" | "model_reasoning_effort"
56
- readonly value: string
57
- readonly changed: string
58
- readonly unchanged: string
59
- }
58
+ type CodexSettingEdit = SettingEdit<"model" | "model_reasoning_effort">;
60
59
 
61
60
  function syncCodexSetting(ctx: Ctx, edit: CodexSettingEdit): void {
62
- const { change, echo, verbose, warn } = ctx.services.logger
63
- const userCodexSettings = p(ctx.home, ".codex", "config.toml")
61
+ const { change, echo, verbose, warn } = ctx.services.logger;
62
+ const userCodexSettings = p(ctx.home, ".codex", "config.toml");
64
63
 
65
64
  if (!existsSync(userCodexSettings)) {
66
65
  // A dry-run sync already previewed installing the file, so the edit is not skipped.
67
66
  // The `docks-kit model codex <m>` command creates no file, so the skip stands.
68
67
  if (ctx.dryRun && ctx.syncCodex) {
69
- echo(`[dry-run] (${edit.tag}) set ${edit.key} = "${edit.value}" in ${userCodexSettings}`)
70
- return
68
+ echo(`[dry-run] (${edit.tag}) set ${edit.key} = "${edit.value}" in ${userCodexSettings}`);
69
+ return;
71
70
  }
72
- warn(`(${edit.tag}) ${userCodexSettings} missing — skipped`)
73
- return
71
+ warn(`(${edit.tag}) ${userCodexSettings} missing — skipped`);
72
+ return;
74
73
  }
75
74
 
76
75
  if (ctx.dryRun) {
77
- echo(`[dry-run] (${edit.tag}) set ${edit.key} = "${edit.value}" in ${userCodexSettings}`)
78
- return
76
+ echo(`[dry-run] (${edit.tag}) set ${edit.key} = "${edit.value}" in ${userCodexSettings}`);
77
+ return;
79
78
  }
80
79
 
81
80
  if (replaceTopLevelSettingInFile(userCodexSettings, edit.key, `${edit.key} = "${edit.value}"`)) {
82
- change(edit.changed)
83
- ctx.nextStepTriggers.codexRestart = true
81
+ change(edit.changed);
82
+ ctx.nextStepTriggers.codexRestart = true;
84
83
  } else {
85
- verbose(edit.unchanged)
84
+ verbose(edit.unchanged);
86
85
  }
87
86
  }
88
87
 
89
88
  export function syncCodexModel(ctx: Ctx, model: string): void {
90
- if (model === "") return
89
+ if (model === "") return;
91
90
  syncCodexSetting(ctx, {
92
91
  tag: "--codex-model",
93
92
  key: "model",
94
93
  value: model,
95
94
  changed: `Model: deployed Codex model set to ${model} (SoT unchanged; flag-less sync reverts)`,
96
- unchanged: `Model: deployed Codex model already ${model}`
97
- })
95
+ unchanged: `Model: deployed Codex model already ${model}`,
96
+ });
98
97
  }
99
98
 
100
99
  export function syncCodexEffort(ctx: Ctx, effort: string): void {
101
- if (effort === "") return
102
- const resolved = resolveEffort("codex", effort)
103
- const useDefault = effort === "default"
100
+ if (effort === "") return;
101
+ const resolved = resolveEffort("codex", effort);
102
+ const useDefault = effort === "default";
104
103
  syncCodexSetting(ctx, {
105
104
  tag: "--codex-effort",
106
105
  key: "model_reasoning_effort",
@@ -108,22 +107,22 @@ export function syncCodexEffort(ctx: Ctx, effort: string): void {
108
107
  changed: useDefault
109
108
  ? `Effort: deployed Codex model_reasoning_effort set to ${resolved} (SoT default)`
110
109
  : `Effort: deployed Codex model_reasoning_effort set to ${resolved} (SoT unchanged; flag-less sync reverts)`,
111
- unchanged: `Effort: deployed Codex model_reasoning_effort already ${resolved}`
112
- })
110
+ unchanged: `Effort: deployed Codex model_reasoning_effort already ${resolved}`,
111
+ });
113
112
  }
114
113
 
115
114
  export function mergeTopLevelSettings(sotConfigText: string, userConfig: string): void {
116
115
  for (const line of sotConfigText.split("\n")) {
117
- if (line.startsWith("[")) break
118
- if (/^[ \t]*($|#)/.test(line)) continue
119
- if (!/^[A-Za-z0-9_.-]+[ \t]*=/.test(line)) continue
120
- const key = line.slice(0, line.indexOf("=")).replace(/[ \t]+$/, "")
121
- replaceTopLevelSettingInFile(userConfig, key, line)
116
+ if (line.startsWith("[")) break;
117
+ if (/^[ \t]*($|#)/.test(line)) continue;
118
+ if (!/^[A-Za-z0-9_.-]+[ \t]*=/.test(line)) continue;
119
+ const key = line.slice(0, line.indexOf("=")).replace(/[ \t]+$/, "");
120
+ replaceTopLevelSettingInFile(userConfig, key, line);
122
121
  }
123
122
  }
124
123
 
125
124
  interface TomlTableHeader {
126
- readonly path: string
125
+ readonly path: string;
127
126
  }
128
127
 
129
128
  const TOML_BASIC_ESCAPES: Readonly<Record<string, string>> = {
@@ -133,121 +132,124 @@ const TOML_BASIC_ESCAPES: Readonly<Record<string, string>> = {
133
132
  f: "\f",
134
133
  r: "\r",
135
134
  '"': '"',
136
- "\\": "\\"
137
- }
138
-
139
- function tomlBasicEscape(line: string, offset: number): { readonly next: number; readonly value: string } | undefined {
140
- const escaped = line[offset]
141
- const simple = escaped === undefined ? undefined : TOML_BASIC_ESCAPES[escaped]
142
- if (simple !== undefined) return { next: offset + 1, value: simple }
143
- const digits = escaped === "u" ? 4 : escaped === "U" ? 8 : 0
144
- if (digits === 0) return undefined
145
- const hex = line.slice(offset + 1, offset + 1 + digits)
146
- if (hex.length !== digits || !/^[0-9A-Fa-f]+$/.test(hex)) return undefined
147
- const codePoint = Number.parseInt(hex, 16)
148
- if (codePoint > 0x10ffff || (codePoint >= 0xd800 && codePoint <= 0xdfff)) return undefined
149
- return { next: offset + 1 + digits, value: String.fromCodePoint(codePoint) }
135
+ "\\": "\\",
136
+ };
137
+
138
+ function tomlBasicEscape(
139
+ line: string,
140
+ offset: number,
141
+ ): { readonly next: number; readonly value: string } | undefined {
142
+ const escaped = line[offset];
143
+ const simple = escaped === undefined ? undefined : TOML_BASIC_ESCAPES[escaped];
144
+ if (simple !== undefined) return { next: offset + 1, value: simple };
145
+ const digits = escaped === "u" ? 4 : escaped === "U" ? 8 : 0;
146
+ if (digits === 0) return undefined;
147
+ const hex = line.slice(offset + 1, offset + 1 + digits);
148
+ if (hex.length !== digits || !/^[0-9A-Fa-f]+$/.test(hex)) return undefined;
149
+ const codePoint = Number.parseInt(hex, 16);
150
+ if (codePoint > 0x10ffff || (codePoint >= 0xd800 && codePoint <= 0xdfff)) return undefined;
151
+ return { next: offset + 1 + digits, value: String.fromCodePoint(codePoint) };
150
152
  }
151
153
 
152
154
  /** Decode a table header to the TOML path that determines managed ownership. */
153
155
  function tomlTableHeader(line: string): TomlTableHeader | undefined {
154
- let offset = 0
156
+ let offset = 0;
155
157
  const skipWhitespace = (): void => {
156
- while (line[offset] === " " || line[offset] === "\t") offset++
157
- }
158
+ while (line[offset] === " " || line[offset] === "\t") offset++;
159
+ };
158
160
 
159
- skipWhitespace()
160
- if (line[offset] !== "[") return undefined
161
- offset++
162
- const array = line[offset] === "["
163
- if (array) offset++
161
+ skipWhitespace();
162
+ if (line[offset] !== "[") return undefined;
163
+ offset++;
164
+ const array = line[offset] === "[";
165
+ if (array) offset++;
164
166
 
165
- const keys: Array<string> = []
167
+ const keys: Array<string> = [];
166
168
  while (true) {
167
- skipWhitespace()
168
- const quote = line[offset]
169
- let key = ""
169
+ skipWhitespace();
170
+ const quote = line[offset];
171
+ let key = "";
170
172
  if (quote === '"' || quote === "'") {
171
- offset++
172
- let closed = false
173
+ offset++;
174
+ let closed = false;
173
175
  while (offset < line.length) {
174
- const char = line[offset]!
176
+ const char = line[offset]!;
175
177
  if (char === quote) {
176
- offset++
177
- closed = true
178
- break
178
+ offset++;
179
+ closed = true;
180
+ break;
179
181
  }
180
182
  if (quote === '"' && char === "\\") {
181
- const escape = tomlBasicEscape(line, offset + 1)
182
- if (escape === undefined) return undefined
183
- key += escape.value
184
- offset = escape.next
185
- continue
183
+ const escape = tomlBasicEscape(line, offset + 1);
184
+ if (escape === undefined) return undefined;
185
+ key += escape.value;
186
+ offset = escape.next;
187
+ continue;
186
188
  }
187
- if (char === "\n" || char === "\r") return undefined
188
- key += char
189
- offset++
189
+ if (char === "\n" || char === "\r") return undefined;
190
+ key += char;
191
+ offset++;
190
192
  }
191
- if (!closed) return undefined
193
+ if (!closed) return undefined;
192
194
  } else {
193
- const start = offset
194
- while (offset < line.length && /[A-Za-z0-9_-]/.test(line[offset]!)) offset++
195
- if (offset === start) return undefined
196
- key = line.slice(start, offset)
195
+ const start = offset;
196
+ while (offset < line.length && /[A-Za-z0-9_-]/.test(line[offset]!)) offset++;
197
+ if (offset === start) return undefined;
198
+ key = line.slice(start, offset);
197
199
  }
198
- keys.push(key)
200
+ keys.push(key);
199
201
 
200
- skipWhitespace()
202
+ skipWhitespace();
201
203
  if (line[offset] === ".") {
202
- offset++
203
- continue
204
+ offset++;
205
+ continue;
204
206
  }
205
- if (line[offset] !== "]") return undefined
206
- offset++
207
+ if (line[offset] !== "]") return undefined;
208
+ offset++;
207
209
  if (array) {
208
- if (line[offset] !== "]") return undefined
209
- offset++
210
+ if (line[offset] !== "]") return undefined;
211
+ offset++;
210
212
  }
211
- skipWhitespace()
212
- if (offset < line.length && line[offset] !== "#") return undefined
213
- return { path: JSON.stringify(keys) }
213
+ skipWhitespace();
214
+ if (offset < line.length && line[offset] !== "#") return undefined;
215
+ return { path: JSON.stringify(keys) };
214
216
  }
215
217
  }
216
218
 
217
219
  function mergeTableSettingsText(sotConfigText: string, userConfigText: string): string {
218
- const sotLines = sotConfigText.split("\n")
219
- let merged = userConfigText
220
+ const sotLines = sotConfigText.split("\n");
221
+ let merged = userConfigText;
220
222
  for (let tableOffset = 0; tableOffset < sotLines.length; tableOffset++) {
221
- const managedHeader = tomlTableHeader(sotLines[tableOffset]!)
222
- if (managedHeader === undefined) continue
223
+ const managedHeader = tomlTableHeader(sotLines[tableOffset]!);
224
+ if (managedHeader === undefined) continue;
223
225
 
224
- const block: Array<string> = []
226
+ const block: Array<string> = [];
225
227
  for (let blockOffset = tableOffset; blockOffset < sotLines.length; blockOffset++) {
226
- const line = sotLines[blockOffset]!
227
- if (blockOffset !== tableOffset && tomlTableHeader(line) !== undefined) break
228
- block.push(line)
228
+ const line = sotLines[blockOffset]!;
229
+ if (blockOffset !== tableOffset && tomlTableHeader(line) !== undefined) break;
230
+ block.push(line);
229
231
  }
230
- const tableBlock = block.join("\n").replace(/\n+$/, "")
232
+ const tableBlock = block.join("\n").replace(/\n+$/, "");
231
233
 
232
- const userLines = merged.split("\n")
233
- if (userLines[userLines.length - 1] === "") userLines.pop()
234
- let skip = false
235
- const kept: Array<string> = []
234
+ const userLines = merged.split("\n");
235
+ if (userLines[userLines.length - 1] === "") userLines.pop();
236
+ let skip = false;
237
+ const kept: Array<string> = [];
236
238
  for (const line of userLines) {
237
- const header = tomlTableHeader(line)
239
+ const header = tomlTableHeader(line);
238
240
  if (header !== undefined) {
239
- skip = header.path === managedHeader.path
240
- if (skip) continue
241
+ skip = header.path === managedHeader.path;
242
+ if (skip) continue;
241
243
  }
242
- if (!skip) kept.push(line)
244
+ if (!skip) kept.push(line);
243
245
  }
244
- merged = `${kept.join("\n")}\n\n${tableBlock}\n`
246
+ merged = `${kept.join("\n")}\n\n${tableBlock}\n`;
245
247
  }
246
- return merged
248
+ return merged;
247
249
  }
248
250
 
249
251
  export function mergeTableSettings(sotConfigText: string, userConfig: string): void {
250
- const next = mergeTableSettingsText(sotConfigText, readFileSync(userConfig, "utf8"))
251
- writeFileSync(`${userConfig}.tmp`, next)
252
- renameSync(`${userConfig}.tmp`, userConfig)
252
+ const next = mergeTableSettingsText(sotConfigText, readFileSync(userConfig, "utf8"));
253
+ writeFileSync(`${userConfig}.tmp`, next);
254
+ renameSync(`${userConfig}.tmp`, userConfig);
253
255
  }