docks-kit 0.17.0 → 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 (84) hide show
  1. package/AGENTS.md +37 -33
  2. package/README.md +1 -0
  3. package/cli/docs/omp-models.md +114 -58
  4. package/cli/src/argv.ts +38 -469
  5. package/cli/src/argvSurface.ts +371 -0
  6. package/cli/src/argvValidate.ts +122 -0
  7. package/cli/src/commands/docs.ts +57 -42
  8. package/cli/src/commands/harnesses.ts +38 -45
  9. package/cli/src/commands/model.ts +48 -53
  10. package/cli/src/commands/models.ts +24 -26
  11. package/cli/src/commands/omp.ts +178 -88
  12. package/cli/src/commands/plugins.ts +20 -20
  13. package/cli/src/commands/skills.ts +22 -20
  14. package/cli/src/commands/status.ts +87 -80
  15. package/cli/src/commands/sync.ts +113 -87
  16. package/cli/src/commands/toolchain.ts +21 -21
  17. package/cli/src/commands/update.ts +139 -117
  18. package/cli/src/efforts.ts +35 -34
  19. package/cli/src/engine-native/DESIGN.md +30 -30
  20. package/cli/src/engine-native/bun.ts +77 -55
  21. package/cli/src/engine-native/claudeLsp.ts +154 -0
  22. package/cli/src/engine-native/claudeOptionalPlugins.ts +113 -0
  23. package/cli/src/engine-native/claudePluginPasses.ts +348 -0
  24. package/cli/src/engine-native/claudeRemovals.ts +195 -0
  25. package/cli/src/engine-native/claudeRetired.ts +4 -4
  26. package/cli/src/engine-native/claudeRuntime.ts +94 -68
  27. package/cli/src/engine-native/claudeSettings.ts +174 -0
  28. package/cli/src/engine-native/claudeSettingsModifiers.ts +50 -54
  29. package/cli/src/engine-native/claudeSync.ts +176 -456
  30. package/cli/src/engine-native/codexConfig.ts +158 -0
  31. package/cli/src/engine-native/codexHooks.ts +91 -0
  32. package/cli/src/engine-native/codexPlugins.ts +290 -0
  33. package/cli/src/engine-native/codexStatus.ts +24 -0
  34. package/cli/src/engine-native/codexSync.ts +110 -568
  35. package/cli/src/engine-native/codexToml.ts +140 -138
  36. package/cli/src/engine-native/deps.ts +144 -107
  37. package/cli/src/engine-native/engineCtx.ts +126 -0
  38. package/cli/src/engine-native/exec.ts +98 -87
  39. package/cli/src/engine-native/failures.ts +3 -3
  40. package/cli/src/engine-native/harnesses.ts +62 -72
  41. package/cli/src/engine-native/index.ts +34 -315
  42. package/cli/src/engine-native/jq.ts +25 -24
  43. package/cli/src/engine-native/logger.ts +98 -100
  44. package/cli/src/engine-native/models.ts +50 -45
  45. package/cli/src/engine-native/modes.ts +83 -80
  46. package/cli/src/engine-native/ompFileDeploy.ts +111 -0
  47. package/cli/src/engine-native/ompMarketplace.ts +136 -0
  48. package/cli/src/engine-native/ompOverlay.ts +93 -53
  49. package/cli/src/engine-native/ompPaths.ts +49 -38
  50. package/cli/src/engine-native/ompPlugins.ts +170 -0
  51. package/cli/src/engine-native/ompSync.ts +64 -408
  52. package/cli/src/engine-native/ompYaml.ts +43 -41
  53. package/cli/src/engine-native/os/darwin.ts +5 -5
  54. package/cli/src/engine-native/os/index.ts +15 -15
  55. package/cli/src/engine-native/os/linux.ts +5 -5
  56. package/cli/src/engine-native/os/posix.ts +28 -24
  57. package/cli/src/engine-native/os/targets.ts +17 -17
  58. package/cli/src/engine-native/os/types.ts +31 -31
  59. package/cli/src/engine-native/os/windows.ts +61 -61
  60. package/cli/src/engine-native/parseArgs.ts +122 -372
  61. package/cli/src/engine-native/parseHelp.ts +66 -0
  62. package/cli/src/engine-native/parseModifiers.ts +222 -0
  63. package/cli/src/engine-native/services.ts +34 -34
  64. package/cli/src/engine-native/settings.ts +15 -15
  65. package/cli/src/engine-native/sharedTypes.d.ts +46 -0
  66. package/cli/src/engine-native/skillsInstall.ts +105 -0
  67. package/cli/src/engine-native/skillsLinks.ts +203 -0
  68. package/cli/src/engine-native/skillsManifest.ts +16 -0
  69. package/cli/src/engine-native/skillsPrune.ts +108 -0
  70. package/cli/src/engine-native/skillsSync.ts +28 -362
  71. package/cli/src/engine-native/syncConcurrency.ts +55 -0
  72. package/cli/src/engine-native/syncDispatch.ts +141 -0
  73. package/cli/src/engine-native/toolchain.ts +65 -62
  74. package/cli/src/engine.ts +65 -57
  75. package/cli/src/generated/sotPayload.ts +7 -7
  76. package/cli/src/kitHome.ts +44 -40
  77. package/cli/src/main.ts +63 -56
  78. package/cli/src/manifests.ts +53 -54
  79. package/cli/src/md.d.ts +2 -2
  80. package/cli/src/payload.ts +9 -9
  81. package/cli/src/services.ts +21 -13
  82. package/cli/tsconfig.json +4 -1
  83. package/package.json +9 -2
  84. package/cli/src/engine-native/claudePlugins.ts +0 -510
package/cli/src/argv.ts CHANGED
@@ -1,157 +1,28 @@
1
- import { GlobalFlag } from "effect/unstable/cli"
2
- import { docsCommand } from "./commands/docs"
3
- import { modelCommand } from "./commands/model"
4
- import { modelsCommand } from "./commands/models"
5
- import { pluginsCommand } from "./commands/plugins"
6
- import { skillsCommand } from "./commands/skills"
7
- import { statusCommand } from "./commands/status"
8
- import { syncCommand } from "./commands/sync"
9
- import { toolchainCommand } from "./commands/toolchain"
10
- import { updateCommand } from "./commands/update"
11
- import { ompCommand } from "./commands/omp"
1
+ import { GlobalFlag } from "effect/unstable/cli";
2
+ import { docsCommand } from "./commands/docs";
3
+ import { modelCommand } from "./commands/model";
4
+ import { modelsCommand } from "./commands/models";
5
+ import { pluginsCommand } from "./commands/plugins";
6
+ import { skillsCommand } from "./commands/skills";
7
+ import { statusCommand } from "./commands/status";
8
+ import { syncCommand } from "./commands/sync";
9
+ import { toolchainCommand } from "./commands/toolchain";
10
+ import { updateCommand } from "./commands/update";
11
+ import { ompCommand } from "./commands/omp";
12
12
  import {
13
- advisorCatalog,
14
- advisorFlagGrammar,
15
- effortCatalog,
16
- effortFlagGrammar
17
- } from "./efforts"
18
- import { KNOWN_CLAUDE_OPTIN_PLUGINS } from "./engine-native/parseArgs"
19
- import { modelCatalog } from "./engine-native/models"
20
- import type { Tool } from "./manifests"
13
+ buildSurfaces,
14
+ normalizeArgv,
15
+ scanArgv,
16
+ spliceOmpBoundary,
17
+ subcommandName as scanSubcommandName,
18
+ type ArgvSurfaces,
19
+ type CommandValue,
20
+ } from "./argvSurface";
21
+ import { validateScannedArgv } from "./argvValidate";
21
22
 
22
23
  export type ArgvOutcome =
23
24
  | { readonly kind: "reject"; readonly message: string; readonly exitCode: number }
24
- | { readonly kind: "accept"; readonly args: ReadonlyArray<string> }
25
-
26
- interface FlagSurface {
27
- readonly longFlags: ReadonlyArray<string>
28
- readonly aliases: ReadonlyMap<string, string>
29
- readonly valueFlags: ReadonlyArray<string>
30
- readonly repeatableFlags: ReadonlyArray<string>
31
- }
32
- interface GlobalFlagSurface extends FlagSurface {
33
- readonly actionFlags: ReadonlySet<string>
34
- }
35
-
36
- interface CommandValue {
37
- readonly name: string
38
- }
39
-
40
-
41
- const flagMetadata = (
42
- param: unknown
43
- ): {
44
- readonly name: string
45
- readonly aliases: ReadonlyArray<string>
46
- readonly takesValue: boolean
47
- readonly repeatable: boolean
48
- } => {
49
- let current = param
50
- let repeatable = false
51
-
52
- while (typeof current === "object" && current !== null) {
53
- if (!("_tag" in current)) break
54
- if (current._tag === "Variadic") repeatable = true
55
- if (current._tag !== "Single") {
56
- if (!("param" in current)) break
57
- current = current.param
58
- continue
59
- }
60
-
61
- if (!("name" in current) || typeof current.name !== "string") {
62
- throw new Error("Effect CLI exposed a flag without a string name")
63
- }
64
- if (
65
- !("aliases" in current) ||
66
- !Array.isArray(current.aliases) ||
67
- !current.aliases.every((alias) => typeof alias === "string")
68
- ) {
69
- throw new Error(`Effect CLI exposed invalid aliases for --${current.name}`)
70
- }
71
- if (
72
- !("primitiveType" in current) ||
73
- typeof current.primitiveType !== "object" ||
74
- current.primitiveType === null ||
75
- !("_tag" in current.primitiveType) ||
76
- typeof current.primitiveType._tag !== "string"
77
- ) {
78
- throw new Error(`Effect CLI exposed no primitive type for --${current.name}`)
79
- }
80
- const aliases: ReadonlyArray<string> = current.aliases
81
- return {
82
- name: current.name,
83
- aliases,
84
- takesValue: current.primitiveType._tag !== "Boolean",
85
- repeatable
86
- }
87
- }
88
-
89
- throw new Error("Effect CLI exposed an unsupported flag parameter")
90
- }
91
-
92
- const flagSurface = (params: ReadonlyArray<unknown>): FlagSurface => {
93
- const longFlags: Array<string> = []
94
- const aliases = new Map<string, string>()
95
- const valueFlags: Array<string> = []
96
- const repeatableFlags: Array<string> = []
97
-
98
- for (const param of params) {
99
- const metadata = flagMetadata(param)
100
- const longName = `--${metadata.name}`
101
- longFlags.push(longName)
102
- if (metadata.takesValue) valueFlags.push(longName)
103
- if (metadata.repeatable) repeatableFlags.push(longName)
104
- for (const alias of metadata.aliases) aliases.set(`-${alias}`, longName)
105
- }
106
-
107
- return { longFlags, aliases, valueFlags, repeatableFlags }
108
- }
109
-
110
- const commandSurface = (command: CommandValue): FlagSurface => {
111
- if (!("config" in command)) {
112
- throw new Error(`Effect CLI did not expose flags for '${command.name}'`)
113
- }
114
- const config = command.config
115
- if (
116
- typeof config !== "object" ||
117
- config === null ||
118
- !("flags" in config) ||
119
- !Array.isArray(config.flags)
120
- ) {
121
- throw new Error(`Effect CLI did not expose flags for '${command.name}'`)
122
- }
123
-
124
- return flagSurface(config.flags)
125
- }
126
-
127
- const globalSurface = (): GlobalFlagSurface => {
128
- const builtIns: unknown = GlobalFlag.BuiltIns
129
- if (!Array.isArray(builtIns)) {
130
- throw new Error("Effect CLI did not expose its built-in global flags")
131
- }
132
-
133
- const actionFlags = new Set<string>()
134
- const flags = builtIns.map((builtIn) => {
135
- if (
136
- (typeof builtIn !== "object" && typeof builtIn !== "function") ||
137
- builtIn === null ||
138
- !("flag" in builtIn) ||
139
- !("_tag" in builtIn) ||
140
- typeof builtIn._tag !== "string"
141
- ) {
142
- throw new Error("Effect CLI exposed an unsupported built-in global flag")
143
- }
144
- if (builtIn._tag === "Action") {
145
- // Only a presence-based action is idempotent. `--completions` is an action
146
- // that takes a value, and Effect 4 would silently keep the first one, which
147
- // is exactly what the duplicate rule exists to refuse.
148
- const metadata = flagMetadata(builtIn.flag)
149
- if (!metadata.takesValue) actionFlags.add(`--${metadata.name}`)
150
- }
151
- return builtIn.flag
152
- })
153
- return { ...flagSurface(flags), actionFlags }
154
- }
25
+ | { readonly kind: "accept"; readonly args: ReadonlyArray<string> };
155
26
 
156
27
  const COMMANDS: ReadonlyArray<CommandValue> = [
157
28
  syncCommand,
@@ -163,331 +34,29 @@ const COMMANDS: ReadonlyArray<CommandValue> = [
163
34
  pluginsCommand,
164
35
  skillsCommand,
165
36
  docsCommand,
166
- ompCommand
167
- ]
168
-
169
- // A Map, not a plain object: an object literal answers `toString` and friends from
170
- // `Object.prototype`, so an unknown subcommand with such a name would slip past the
171
- // unknown-command guard and dereference a surface that was never built.
172
- const COMMAND_SURFACES: ReadonlyMap<string, FlagSurface> = new Map(
173
- COMMANDS.map((command) => [command.name, commandSurface(command)])
174
- )
175
-
176
- const GLOBAL_SURFACE: GlobalFlagSurface = globalSurface()
177
-
178
- const LEGACY_HINTS: Readonly<Record<string, string>> = {
179
- "--force": "--force was renamed to --reconcile",
180
- "--remove-plugins":
181
- "--remove-plugins was renamed to --prune (it also removes marketplaces + kit-managed skills)",
182
- "--680k": "--680k was renamed to --claude-compact-window=680k",
183
- "--permissive": "--permissive was renamed to --claude-permissive",
184
- "--supabase": "--supabase was renamed to --claude-plugin=supabase",
185
- "--n8n": "--n8n was renamed to --claude-plugin=n8n",
186
- "--skip-rtk": "--skip-rtk was renamed to --skip-bubblewrap",
187
- "--claude": "--claude was renamed: pass the target as a word, e.g. 'sync claude'",
188
- "--codex": "--codex was renamed: pass the target as a word, e.g. 'sync codex'",
189
- "--agents": "--agents was renamed: pass the target as a word, e.g. 'sync agents'"
190
- }
37
+ ompCommand,
38
+ ];
191
39
 
192
- const flagNameOf = (token: string): string => {
193
- const equals = token.indexOf("=")
194
- return equals === -1 ? token : token.slice(0, equals)
195
- }
196
-
197
- const canonicalFlagName = (
198
- name: string,
199
- surface: FlagSurface | undefined
200
- ): string | undefined => {
201
- if (surface === undefined) return undefined
202
- const aliased = surface.aliases.get(name)
203
- if (aliased !== undefined) return aliased
204
- return surface.longFlags.includes(name) ? name : undefined
205
- }
206
-
207
- const declaredFlagName = (
208
- name: string,
209
- commandSurface: FlagSurface | undefined
210
- ): string | undefined =>
211
- canonicalFlagName(name, commandSurface) ?? canonicalFlagName(name, GLOBAL_SURFACE)
212
-
213
- const declaredInAnySurface = (name: string): boolean => {
214
- if (canonicalFlagName(name, GLOBAL_SURFACE) !== undefined) return true
215
- return COMMANDS.some(
216
- (command) => canonicalFlagName(name, COMMAND_SURFACES.get(command.name)) !== undefined
217
- )
218
- }
219
-
220
- const takesValueInAnySurface = (name: string): boolean => {
221
- const globalName = canonicalFlagName(name, GLOBAL_SURFACE)
222
- if (globalName !== undefined && GLOBAL_SURFACE.valueFlags.includes(globalName)) return true
223
- return COMMANDS.some((command) => {
224
- const surface = COMMAND_SURFACES.get(command.name)
225
- if (surface === undefined) return false
226
- const canonicalName = canonicalFlagName(name, surface)
227
- return canonicalName !== undefined && surface.valueFlags.includes(canonicalName)
228
- })
229
- }
230
-
231
- /** The resolved subcommand word, or undefined at the root. */
232
- export const subcommandName = (args: ReadonlyArray<string>): string | undefined => {
233
- for (let index = 0; index < args.length; index++) {
234
- const token = args[index]
235
- if (token === "--") return undefined
236
- const name = flagNameOf(token)
237
- if (token === name && takesValueInAnySurface(name)) {
238
- const next = args[index + 1]
239
- if (next === "--") return undefined
240
- if (
241
- next !== undefined &&
242
- !(next.startsWith("-") && declaredInAnySurface(flagNameOf(next)))
243
- ) {
244
- index++
245
- continue
246
- }
247
- }
248
- if (!token.startsWith("-")) return token
249
- }
250
- return undefined
251
- }
252
-
253
- interface ScannedFlag {
254
- readonly token: string
255
- readonly name: string
256
- readonly canonicalName: string | undefined
257
- readonly hasValue: boolean
258
- readonly takesValue: boolean
259
- }
260
-
261
- interface ArgvNormalization {
262
- readonly flagIndex: number
263
- readonly valueIndex: number
264
- readonly token: string
265
- }
266
-
267
- interface ScannedArgv {
268
- readonly flags: ReadonlyArray<ScannedFlag>
269
- readonly normalizations: ReadonlyArray<ArgvNormalization>
270
- }
271
-
272
- const scanArgv = (
273
- args: ReadonlyArray<string>,
274
- commandSurface: FlagSurface | undefined
275
- ): ScannedArgv => {
276
- const flags: Array<ScannedFlag> = []
277
- const normalizations: Array<ArgvNormalization> = []
278
-
279
- for (let index = 0; index < args.length; index++) {
280
- const token = args[index]
281
- if (token === "--") break
282
- if (!token.startsWith("-")) continue
283
-
284
- const name = flagNameOf(token)
285
- const canonicalName = declaredFlagName(name, commandSurface)
286
- let hasValue = token.includes("=")
287
- const takesValue =
288
- canonicalName !== undefined &&
289
- (commandSurface?.valueFlags.includes(canonicalName) === true ||
290
- GLOBAL_SURFACE.valueFlags.includes(canonicalName))
291
-
292
- if (!hasValue && takesValue) {
293
- const next = args[index + 1]
294
- const nextIsRecognizedFlag =
295
- next !== undefined &&
296
- next.startsWith("-") &&
297
- declaredFlagName(flagNameOf(next), commandSurface) !== undefined
298
- // A legitimate value may begin with `-`; only a recognized flag proves it is missing.
299
- if (next !== undefined && next !== "--" && !nextIsRecognizedFlag) {
300
- hasValue = true
301
- if (next.startsWith("-")) {
302
- // Effect 4's lexer treats any `-`-leading token as an option, so a legitimate
303
- // dash-leading value only survives in the inline form.
304
- normalizations.push({
305
- flagIndex: index,
306
- valueIndex: index + 1,
307
- token: `${token}=${next}`
308
- })
309
- }
310
- index++
311
- }
312
- }
313
-
314
- flags.push({ token, name, canonicalName, hasValue, takesValue })
315
- }
316
-
317
- return { flags, normalizations }
318
- }
319
-
320
- const normalizeArgv = (
321
- args: ReadonlyArray<string>,
322
- normalizations: ReadonlyArray<ArgvNormalization>
323
- ): ReadonlyArray<string> => {
324
- if (normalizations.length === 0) return args
325
-
326
- const normalized: Array<string> = []
327
- let normalizationIndex = 0
328
- for (let index = 0; index < args.length; index++) {
329
- const normalization = normalizations[normalizationIndex]
330
- if (normalization !== undefined && normalization.flagIndex === index) {
331
- normalized.push(normalization.token)
332
- index = normalization.valueIndex
333
- normalizationIndex++
334
- continue
335
- }
336
- normalized.push(args[index])
337
- }
338
- return normalized
339
- }
40
+ const SURFACES: ArgvSurfaces = buildSurfaces(COMMANDS, GlobalFlag.BuiltIns);
340
41
 
341
42
  const reject = (message: string): ArgvOutcome => ({
342
43
  kind: "reject",
343
44
  message,
344
- exitCode: 2
345
- })
346
-
347
- const modelCatalogHint = (tool: Tool): string => {
348
- const catalog = modelCatalog(tool)
349
- const list = catalog.models
350
- .map((model) => ` ${model.id}${model.note !== undefined ? ` — ${model.note}` : ""}`)
351
- .join("\n")
352
- return `Available ${tool} models (kit-verified ${catalog.verified} — SoT/models.json):\n${list}`
353
- }
354
-
355
- const missingModifierValue = (flag: string): string | undefined => {
356
- switch (flag) {
357
- case "--claude-model":
358
- case "--codex-model": {
359
- const tool: Tool = flag === "--claude-model" ? "claude" : "codex"
360
- return `${modelCatalogHint(tool)}\n${flag} requires a value: ${flag}=<model>`
361
- }
362
- case "--claude-effort":
363
- case "--codex-effort": {
364
- const tool: Tool = flag === "--claude-effort" ? "claude" : "codex"
365
- return `${effortCatalog(tool)}\n${flag} requires a value: ${effortFlagGrammar(tool)}`
366
- }
367
- case "--claude-advisor":
368
- return `${advisorCatalog()}\n${flag} requires a value: ${advisorFlagGrammar()}`
369
- case "--claude-compact-window":
370
- return `${flag} requires a value: ${flag}=<tokens> (e.g. 680k)`
371
- case "--claude-plugin":
372
- return `${flag} requires a value: ${flag}=<${KNOWN_CLAUDE_OPTIN_PLUGINS.join("|")}>`
373
- default:
374
- return undefined
375
- }
376
- }
45
+ exitCode: 2,
46
+ });
377
47
 
378
- // Without the injected `--`, Effect 4 rejects a forwarded flag such as `-p` or
379
- // `--mode` as unrecognized. The omp launcher therefore owns a passthrough
380
- // boundary: the first token after the `omp` word that is neither a flag
381
- // declared on the omp or global surface nor a value consumed by such a flag
382
- // starts the verbatim tail forwarded to omp. An undeclared long flag joins the
383
- // tail too, because most omp flags are long (`--mode`, `--continue`,
384
- // `--models`), and omp itself reports an unrecognized one accurately. Use
385
- // `docks-kit omp -- --model x` to reach omp's own same-named flag.
386
- const spliceOmpBoundary = (args: ReadonlyArray<string>): ReadonlyArray<string> => {
387
- const surface = COMMAND_SURFACES.get("omp")
388
- let ompIndex = -1
389
- for (let index = 0; index < args.length; index++) {
390
- const token = args[index] as string
391
- if (token === "--") return args
392
- const name = flagNameOf(token)
393
- if (token === name && takesValueInAnySurface(name)) {
394
- const next = args[index + 1]
395
- if (next === "--") return args
396
- if (next !== undefined && !(next.startsWith("-") && declaredInAnySurface(flagNameOf(next)))) {
397
- index++
398
- continue
399
- }
400
- }
401
- if (!token.startsWith("-")) {
402
- ompIndex = index
403
- break
404
- }
405
- }
406
- if (ompIndex === -1 || args[ompIndex] !== "omp") return args
407
- for (let index = ompIndex + 1; index < args.length; index++) {
408
- const token = args[index] as string
409
- // An explicit delimiter already marks the tail; a second one would be
410
- // forwarded to omp as a literal argument.
411
- if (token === "--") return args
412
- if (!token.startsWith("-")) {
413
- return [...args.slice(0, index), "--", ...args.slice(index)]
414
- }
415
- const name = flagNameOf(token)
416
- const canonical = declaredFlagName(name, surface)
417
- if (canonical === undefined) return [...args.slice(0, index), "--", ...args.slice(index)]
418
- const takesValue =
419
- surface?.valueFlags.includes(canonical) === true ||
420
- GLOBAL_SURFACE.valueFlags.includes(canonical)
421
- if (takesValue && !token.includes("=")) {
422
- const next = args[index + 1]
423
- if (
424
- next !== undefined &&
425
- !(next.startsWith("-") && declaredFlagName(flagNameOf(next), surface) !== undefined)
426
- ) {
427
- index++
428
- }
429
- }
430
- }
431
- return args
432
- }
48
+ /** The resolved subcommand word, or undefined at the root. */
49
+ export const subcommandName = (args: ReadonlyArray<string>): string | undefined =>
50
+ scanSubcommandName(args, SURFACES);
433
51
 
434
52
  /** Validate the argument list, then hand back the arguments Effect 4 should parse. */
435
53
  export const prepareArgv = (args: ReadonlyArray<string>): ArgvOutcome => {
436
- const boundaryArgs = subcommandName(args) === "omp" ? spliceOmpBoundary(args) : args
437
- const subcommand = subcommandName(boundaryArgs)
438
- const commandSurface = subcommand === undefined ? undefined : COMMAND_SURFACES.get(subcommand)
439
- const { flags, normalizations } = scanArgv(boundaryArgs, commandSurface)
440
- const unknownCommandWouldMisdiagnoseFlag =
441
- subcommand !== undefined &&
442
- commandSurface === undefined &&
443
- flags.some((flag) => flag.canonicalName === undefined)
444
- if (unknownCommandWouldMisdiagnoseFlag) {
445
- return reject(`unknown command '${subcommand}'`)
446
- }
447
-
448
- if (subcommand === "sync") {
449
- for (const flag of flags) {
450
- const hint = LEGACY_HINTS[flag.name]
451
- if (hint !== undefined) return reject(hint)
452
- }
453
- }
454
-
455
- // Effect 4 expands clustered shorts, but the kit declares no clusterable alias
456
- // pair, so refusing a cluster as one unknown flag is the conservative direction.
457
- for (const flag of flags) {
458
- if (flag.canonicalName !== undefined) continue
459
- const scope = subcommand === undefined ? "" : ` for '${subcommand}'`
460
- return reject(`unknown flag ${flag.token}${scope}`)
461
- }
462
-
463
- for (const flag of flags) {
464
- if (!flag.token.includes("=") || flag.takesValue) continue
465
- // These booleans are presence-based; Effect 4 would otherwise let
466
- // `--dry-run=false` read as a dry run while performing a real mutating sync.
467
- return reject(`flag ${flag.name} does not take a value`)
468
- }
469
-
470
- const seen = new Set<string>()
471
- for (const flag of flags) {
472
- const name = flag.canonicalName
473
- if (name === undefined) continue
474
- const duplicateAllowed =
475
- commandSurface?.repeatableFlags.includes(name) === true ||
476
- GLOBAL_SURFACE.repeatableFlags.includes(name) ||
477
- GLOBAL_SURFACE.actionFlags.has(name)
478
- if (!duplicateAllowed && seen.has(name)) {
479
- return reject(`flag ${name} was given more than once`)
480
- }
481
- seen.add(name)
482
- }
483
-
484
- if (subcommand === "sync") {
485
- for (const flag of flags) {
486
- if (flag.hasValue) continue
487
- const message = missingModifierValue(flag.name)
488
- if (message !== undefined) return reject(message)
489
- }
490
- }
491
-
492
- return { kind: "accept", args: normalizeArgv(boundaryArgs, normalizations) }
493
- }
54
+ const boundaryArgs = subcommandName(args) === "omp" ? spliceOmpBoundary(args, SURFACES) : args;
55
+ const subcommand = subcommandName(boundaryArgs);
56
+ const commandSurface =
57
+ subcommand === undefined ? undefined : SURFACES.commandSurfaces.get(subcommand);
58
+ const { flags, normalizations } = scanArgv(boundaryArgs, commandSurface, SURFACES);
59
+ const message = validateScannedArgv(subcommand, commandSurface, SURFACES, flags);
60
+ if (message !== undefined) return reject(message);
61
+ return { kind: "accept", args: normalizeArgv(boundaryArgs, normalizations) };
62
+ };