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.
- package/AGENTS.md +37 -33
- package/README.md +1 -0
- package/cli/docs/omp-models.md +114 -58
- package/cli/src/argv.ts +38 -469
- package/cli/src/argvSurface.ts +371 -0
- package/cli/src/argvValidate.ts +122 -0
- package/cli/src/commands/docs.ts +57 -42
- package/cli/src/commands/harnesses.ts +38 -45
- package/cli/src/commands/model.ts +48 -53
- package/cli/src/commands/models.ts +24 -26
- package/cli/src/commands/omp.ts +178 -88
- package/cli/src/commands/plugins.ts +20 -20
- package/cli/src/commands/skills.ts +22 -20
- package/cli/src/commands/status.ts +87 -80
- package/cli/src/commands/sync.ts +113 -87
- package/cli/src/commands/toolchain.ts +21 -21
- package/cli/src/commands/update.ts +139 -117
- package/cli/src/efforts.ts +35 -34
- package/cli/src/engine-native/DESIGN.md +30 -30
- package/cli/src/engine-native/bun.ts +77 -55
- package/cli/src/engine-native/claudeLsp.ts +154 -0
- package/cli/src/engine-native/claudeOptionalPlugins.ts +113 -0
- package/cli/src/engine-native/claudePluginPasses.ts +348 -0
- package/cli/src/engine-native/claudeRemovals.ts +195 -0
- package/cli/src/engine-native/claudeRetired.ts +4 -4
- package/cli/src/engine-native/claudeRuntime.ts +94 -68
- package/cli/src/engine-native/claudeSettings.ts +174 -0
- package/cli/src/engine-native/claudeSettingsModifiers.ts +50 -54
- package/cli/src/engine-native/claudeSync.ts +176 -456
- package/cli/src/engine-native/codexConfig.ts +158 -0
- package/cli/src/engine-native/codexHooks.ts +91 -0
- package/cli/src/engine-native/codexPlugins.ts +290 -0
- package/cli/src/engine-native/codexStatus.ts +24 -0
- package/cli/src/engine-native/codexSync.ts +110 -568
- package/cli/src/engine-native/codexToml.ts +140 -138
- package/cli/src/engine-native/deps.ts +144 -107
- package/cli/src/engine-native/engineCtx.ts +126 -0
- package/cli/src/engine-native/exec.ts +98 -87
- package/cli/src/engine-native/failures.ts +3 -3
- package/cli/src/engine-native/harnesses.ts +62 -72
- package/cli/src/engine-native/index.ts +34 -315
- package/cli/src/engine-native/jq.ts +25 -24
- package/cli/src/engine-native/logger.ts +98 -100
- package/cli/src/engine-native/models.ts +50 -45
- package/cli/src/engine-native/modes.ts +83 -80
- package/cli/src/engine-native/ompFileDeploy.ts +111 -0
- package/cli/src/engine-native/ompMarketplace.ts +136 -0
- package/cli/src/engine-native/ompOverlay.ts +93 -53
- package/cli/src/engine-native/ompPaths.ts +49 -38
- package/cli/src/engine-native/ompPlugins.ts +170 -0
- package/cli/src/engine-native/ompSync.ts +64 -408
- package/cli/src/engine-native/ompYaml.ts +43 -41
- package/cli/src/engine-native/os/darwin.ts +5 -5
- package/cli/src/engine-native/os/index.ts +15 -15
- package/cli/src/engine-native/os/linux.ts +5 -5
- package/cli/src/engine-native/os/posix.ts +28 -24
- package/cli/src/engine-native/os/targets.ts +17 -17
- package/cli/src/engine-native/os/types.ts +31 -31
- package/cli/src/engine-native/os/windows.ts +61 -61
- package/cli/src/engine-native/parseArgs.ts +122 -372
- package/cli/src/engine-native/parseHelp.ts +66 -0
- package/cli/src/engine-native/parseModifiers.ts +222 -0
- package/cli/src/engine-native/services.ts +34 -34
- package/cli/src/engine-native/settings.ts +15 -15
- package/cli/src/engine-native/sharedTypes.d.ts +46 -0
- package/cli/src/engine-native/skillsInstall.ts +105 -0
- package/cli/src/engine-native/skillsLinks.ts +203 -0
- package/cli/src/engine-native/skillsManifest.ts +16 -0
- package/cli/src/engine-native/skillsPrune.ts +108 -0
- package/cli/src/engine-native/skillsSync.ts +28 -362
- package/cli/src/engine-native/syncConcurrency.ts +55 -0
- package/cli/src/engine-native/syncDispatch.ts +141 -0
- package/cli/src/engine-native/toolchain.ts +65 -62
- package/cli/src/engine.ts +65 -57
- package/cli/src/generated/sotPayload.ts +7 -7
- package/cli/src/kitHome.ts +44 -40
- package/cli/src/main.ts +63 -56
- package/cli/src/manifests.ts +53 -54
- package/cli/src/md.d.ts +2 -2
- package/cli/src/payload.ts +9 -9
- package/cli/src/services.ts +21 -13
- package/cli/tsconfig.json +4 -1
- package/package.json +9 -2
- 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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 =
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
+
};
|