@kisev/skills-opencode 2.4.1 → 3.0.0

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/dist/cli.js CHANGED
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { AgentProfileError, applyAgentProfileChange, availableModels, availableModelVariants, FIXED_AGENT_ROLES, listAgentProfiles, previewAgentProfileChange, validateAgentName, validateModel, validateVariant, } from "./agent-profiles.js";
3
- import { renderDoctor, renderInventory, renderPlan, renderReconcile, shellCommand, terminalSafe, } from "./cli-output.js";
3
+ import { renderDoctor, renderInventory, renderPlan, renderReconcile, scopeArguments, shellCommand, terminalSafe, } from "./cli-output.js";
4
4
  import { collectDoctorFacts, doctorExitCode } from "./doctor.js";
5
5
  import { CATALOG } from "./catalog.js";
6
- import { apply, defaultSelection, InstallerError, normalizeSelection, PACKAGE_COMMANDS, preview, SELECTABLE_PLUGINS, SKILL_COMMANDS, } from "./installer.js";
6
+ import { apply, defaultSelection, InstallerError, normalizeSelection, preview, SELECTABLE_PLUGINS, SKILL_COMMANDS, } from "./installer.js";
7
7
  import { LifecycleError } from "./lifecycle.js";
8
8
  import { skillsInstallerSpec } from "./package-metadata.js";
9
9
  import { applyReconcile, previewReconcile } from "./reconcile.js";
10
10
  import { promptText, selectOption, selectOptions } from "./terminal-wizard.js";
11
- function parseOptions(values, requireScope = true) {
12
- const options = { dryRun: false, json: false, selectionFlag: false };
11
+ function parseOptions(values) {
12
+ const options = {
13
+ scope: "project",
14
+ global: false,
15
+ dryRun: false,
16
+ json: false,
17
+ selectionFlag: false,
18
+ };
13
19
  for (let index = 0; index < values.length; index += 1) {
14
20
  const value = values[index];
15
21
  if (!value.startsWith("--")) {
@@ -17,13 +23,11 @@ function parseOptions(values, requireScope = true) {
17
23
  throw new InstallerError("invalid_input", `Unexpected argument: ${value}`);
18
24
  options.name = value;
19
25
  }
20
- else if (value === "--scope") {
21
- const scope = values[++index];
22
- if (scope !== "global" && scope !== "project")
23
- throw new InstallerError("invalid_input", "--scope must be global or project");
24
- if (options.scope)
25
- throw new InstallerError("invalid_input", "--scope may be supplied once");
26
- options.scope = scope;
26
+ else if (value === "--global") {
27
+ if (options.global)
28
+ throw new InstallerError("invalid_input", "--global may be supplied once");
29
+ options.global = true;
30
+ options.scope = "global";
27
31
  }
28
32
  else if (value === "--dry-run") {
29
33
  if (options.dryRun)
@@ -62,7 +66,7 @@ function parseOptions(values, requireScope = true) {
62
66
  throw new InstallerError("invalid_input", "--json may be supplied once");
63
67
  options.json = true;
64
68
  }
65
- else if (["--commands", "--skill-commands", "--package-commands", "--agents", "--plugins"].includes(value)) {
69
+ else if (["--commands", "--skill-commands", "--agents", "--plugins"].includes(value)) {
66
70
  const raw = values[++index];
67
71
  if (!raw)
68
72
  throw new InstallerError("invalid_input", `${value} requires a comma-separated value`);
@@ -74,10 +78,7 @@ function parseOptions(values, requireScope = true) {
74
78
  .map((item) => item.trim())
75
79
  .filter(Boolean);
76
80
  options.selectionFlag = true;
77
- if (value === "--package-commands") {
78
- options.commands = [...(options.commands ?? []), ...names];
79
- }
80
- else if (value === "--skill-commands") {
81
+ if (value === "--skill-commands") {
81
82
  options.commands = [...(options.commands ?? []), ...names];
82
83
  }
83
84
  else {
@@ -88,8 +89,6 @@ function parseOptions(values, requireScope = true) {
88
89
  throw new InstallerError("invalid_input", `Unknown argument: ${value}`);
89
90
  }
90
91
  }
91
- if (requireScope && !options.scope)
92
- throw new InstallerError("invalid_input", "--scope is required");
93
92
  return options;
94
93
  }
95
94
  function helpRows(rows) {
@@ -109,7 +108,7 @@ function rootHelp() {
109
108
  "",
110
109
  "Commands:",
111
110
  ...helpRows([
112
- ["install", "Select and deploy package-owned commands, agents, and plugin wrappers."],
111
+ ["install", "Select and deploy skill commands, agents, and plugin wrappers."],
113
112
  [
114
113
  "uninstall",
115
114
  "Archive and remove exact-owned assets while preserving conflicts and user files.",
@@ -127,7 +126,7 @@ function rootHelp() {
127
126
  "",
128
127
  "Common options:",
129
128
  ...helpRows([
130
- ["--scope <project|global>", "Required by every command except capabilities."],
129
+ ["--global", "Use global scope; project scope is the default."],
131
130
  ["--dry-run", "Preview a mutation and issue a one-time confirmation digest."],
132
131
  ["--confirm <digest>", "Apply the exact unexpired preview after final revalidation."],
133
132
  ["--json", "Emit stable machine-readable output when supported."],
@@ -137,9 +136,8 @@ function rootHelp() {
137
136
  "",
138
137
  "Install selection:",
139
138
  ...helpRows([
140
- ["--commands <list|none>", "Select command adapters from both command groups."],
139
+ ["--commands <list|none>", "Select adapters for installed portable skills."],
141
140
  ["--skill-commands <list|none>", "Select adapters for installed portable skills."],
142
- ["--package-commands <list|none>", "Select adapters for package tools."],
143
141
  ["--agents <list|none>", "Select fixed agents."],
144
142
  ["--plugins <list|none>", "Select optional plugin wrappers."],
145
143
  ]),
@@ -163,20 +161,19 @@ function rootHelp() {
163
161
  "",
164
162
  "Scope behavior:",
165
163
  ...helpRows([
166
- ["project", "Targets .opencode under the current directory; run from the project root."],
167
- ["global", "Targets ~/.config/opencode and can run from any directory."],
164
+ ["default", "Targets .opencode under the current directory; run from the project root."],
165
+ ["--global", "Targets ~/.config/opencode and can run from any directory."],
168
166
  ]),
169
167
  "",
170
168
  "Examples:",
171
169
  ` ${shellCommand(["capabilities", "--json"])}`,
172
- ` ${shellCommand(["install", "--scope", "global", "--dry-run"])}`,
173
- ` ${shellCommand(["doctor", "--scope", "global"])}`,
170
+ ` ${shellCommand(["install", "--global", "--dry-run"])}`,
171
+ ` ${shellCommand(["doctor", "--global"])}`,
174
172
  ` ${shellCommand([
175
173
  "agent",
176
174
  "model-set",
177
175
  "worker",
178
- "--scope",
179
- "global",
176
+ "--global",
180
177
  "--model",
181
178
  "openai/gpt-5",
182
179
  "--variant",
@@ -233,15 +230,14 @@ function contextualHelp(arguments_) {
233
230
  const [domain, operation] = arguments_.filter((value) => value !== "--help");
234
231
  if (domain === "install")
235
232
  return commandHelp("install", "Select and deploy package-owned OpenCode integration assets.", [
236
- "skills-opencode install --scope <project|global> --dry-run [selection options]",
237
- "skills-opencode install --scope <project|global> --confirm <digest> [selection options]",
233
+ "skills-opencode install [--global] --dry-run [selection options]",
234
+ "skills-opencode install [--global] --confirm <digest> [selection options]",
238
235
  ], [
239
- ["--scope <project|global>", "Choose the deployment scope."],
236
+ ["--global", "Use global scope; project scope is the default."],
240
237
  ["--dry-run", "Preview operations and issue a one-time confirmation digest."],
241
238
  ["--confirm <digest>", "Apply the exact unexpired preview."],
242
- ["--commands <list|none>", "Select adapters from both command groups."],
239
+ ["--commands <list|none>", "Select installed-skill adapters."],
243
240
  ["--skill-commands <list|none>", "Select installed-skill adapters."],
244
- ["--package-commands <list|none>", "Select package-tool adapters."],
245
241
  ["--agents <list|none>", "Select fixed agents."],
246
242
  ["--plugins <list|none>", "Select optional plugin wrappers."],
247
243
  ["--json", "Emit stable machine-readable output."],
@@ -253,11 +249,9 @@ function contextualHelp(arguments_) {
253
249
  "Writes occur only after confirmation; the installer never edits opencode.json.",
254
250
  "Portable skills are installed separately; restart OpenCode after asset changes.",
255
251
  ], [
256
- shellCommand(["install", "--scope", "global", "--dry-run"]),
252
+ shellCommand(["install", "--global", "--dry-run"]),
257
253
  shellCommand([
258
254
  "install",
259
- "--scope",
260
- "project",
261
255
  "--commands",
262
256
  "doctor,reconcile,agent-profiles",
263
257
  "--agents",
@@ -269,10 +263,10 @@ function contextualHelp(arguments_) {
269
263
  ]);
270
264
  if (domain === "uninstall")
271
265
  return commandHelp("uninstall", "Remove package-owned integration assets safely.", [
272
- "skills-opencode uninstall --scope <project|global> --dry-run",
273
- "skills-opencode uninstall --scope <project|global> --confirm <digest>",
266
+ "skills-opencode uninstall [--global] --dry-run",
267
+ "skills-opencode uninstall [--global] --confirm <digest>",
274
268
  ], [
275
- ["--scope <project|global>", "Choose the deployment scope."],
269
+ ["--global", "Use global scope; project scope is the default."],
276
270
  ["--dry-run", "Preview removals, archives, and conflicts."],
277
271
  ["--confirm <digest>", "Apply the exact unexpired preview."],
278
272
  ["--json", "Emit stable machine-readable output."],
@@ -282,22 +276,19 @@ function contextualHelp(arguments_) {
282
276
  "Preserves modified and user-owned files, worktrees, runtime state, and portable skills.",
283
277
  "Remove the plugin config entry and npm dependency only after managed assets.",
284
278
  ], [
285
- shellCommand(["uninstall", "--scope", "global", "--dry-run"]),
286
- shellCommand(["uninstall", "--scope", "project", "--dry-run"]),
279
+ shellCommand(["uninstall", "--global", "--dry-run"]),
280
+ shellCommand(["uninstall", "--dry-run"]),
287
281
  ]);
288
282
  if (domain === "doctor")
289
- return commandHelp("doctor", "Inspect integration health without changing state.", ["skills-opencode doctor --scope <project|global> [--json]"], [
290
- ["--scope <project|global>", "Choose the scope to inspect."],
283
+ return commandHelp("doctor", "Inspect integration health without changing state.", ["skills-opencode doctor [--global] [--json]"], [
284
+ ["--global", "Inspect global scope; project scope is the default."],
291
285
  ["--json", "Emit the complete stable machine-readable report."],
292
286
  ["--help", "Show this command help and exit."],
293
287
  ], [
294
288
  "Creates no receipts and starts no recovery, plugin factories, or LSP servers.",
295
289
  "Reports versions, ownership, drift, collisions, config, archives, tools, and LSP facts.",
296
290
  "Exit status: 0 clean, 1 findings, 2 invalid input or incomplete probing.",
297
- ], [
298
- shellCommand(["doctor", "--scope", "global"]),
299
- shellCommand(["doctor", "--scope", "global", "--json"]),
300
- ]);
291
+ ], [shellCommand(["doctor"]), shellCommand(["doctor", "--global", "--json"])]);
301
292
  if (domain === "capabilities")
302
293
  return commandHelp("capabilities", "Print the versioned package capability catalog.", ["skills-opencode capabilities [--json]"], [
303
294
  ["--json", "Explicitly request stable JSON output; JSON is the default."],
@@ -308,38 +299,35 @@ function contextualHelp(arguments_) {
308
299
  ], [shellCommand(["capabilities", "--json"])]);
309
300
  if (domain === "reconcile")
310
301
  return commandHelp("reconcile", "Classify current and historical assets and retire exact-owned entries.", [
311
- "skills-opencode reconcile --scope <project|global> --dry-run",
312
- "skills-opencode reconcile --scope <project|global> --confirm <digest>",
302
+ "skills-opencode reconcile [--global] --dry-run",
303
+ "skills-opencode reconcile [--global] --confirm <digest>",
313
304
  ], [
314
- ["--scope <project|global>", "Choose the scope to reconcile."],
305
+ ["--global", "Reconcile global scope; project scope is the default."],
315
306
  ["--dry-run", "Preview classifications, archives, and blockers."],
316
307
  ["--confirm <digest>", "Apply the exact unexpired preview."],
317
308
  ["--json", "Emit stable machine-readable output."],
318
309
  ["--help", "Show this command help and exit."],
319
310
  ], [
320
311
  "Preview is read-only and blocks Apply on modified managed files or ownership conflicts.",
321
- "Confirmed reconcile archives only exact-owned retired assets.",
322
- "Never installs, updates, or removes portable skills.",
312
+ "Confirmed reconcile archives exact-owned retired package assets.",
313
+ "Marked retired portable skills are removed by the pinned skills CLI.",
323
314
  ], [
324
- shellCommand(["reconcile", "--scope", "global", "--dry-run"]),
325
- shellCommand(["reconcile", "--scope", "project", "--dry-run", "--json"]),
315
+ shellCommand(["reconcile", "--global", "--dry-run"]),
316
+ shellCommand(["reconcile", "--dry-run", "--json"]),
326
317
  ]);
327
318
  if (domain === "agent" && operation === "list")
328
- return commandHelp("agent list", "List agent profiles, models, ownership, collisions, and drift.", ["skills-opencode agent list --scope <project|global> [--json]"], [
329
- ["--scope <project|global>", "Choose the profile scope."],
319
+ return commandHelp("agent list", "List agent profiles, models, ownership, collisions, and drift.", ["skills-opencode agent list [--global] [--json]"], [
320
+ ["--global", "Inspect global scope; project scope is the default."],
330
321
  ["--json", "Emit stable machine-readable inventory."],
331
322
  ["--help", "Show this command help and exit."],
332
- ], ["Read-only inventory; creates no receipts and changes no files or configuration."], [
333
- shellCommand(["agent", "list", "--scope", "global"]),
334
- shellCommand(["agent", "list", "--scope", "project", "--json"]),
335
- ]);
323
+ ], ["Read-only inventory; creates no receipts and changes no files or configuration."], [shellCommand(["agent", "list", "--global"]), shellCommand(["agent", "list", "--json"])]);
336
324
  if (domain === "agent" && operation === "configure")
337
325
  return commandHelp("agent configure", "Choose an agent model interactively or with explicit options.", [
338
- "skills-opencode agent configure [name] --scope <project|global> --dry-run",
339
- "skills-opencode agent configure <name> --scope <project|global> --model <provider/model> [--variant <id>] --dry-run",
340
- "skills-opencode agent configure <name> --scope <project|global> --model <provider/model> [--variant <id>] --confirm <digest>",
326
+ "skills-opencode agent configure [name] [--global] --dry-run",
327
+ "skills-opencode agent configure <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
328
+ "skills-opencode agent configure <name> [--global] --model <provider/model> [--variant <id>] --confirm <digest>",
341
329
  ], [
342
- ["--scope <project|global>", "Choose the profile scope."],
330
+ ["--global", "Use global scope; project scope is the default."],
343
331
  ["--provider <id>", "Provider when --model is not provider/model."],
344
332
  ["--model <id>", "Exact provider/model or model paired with --provider."],
345
333
  ["--variant <id>", "Set an optional model variant."],
@@ -353,13 +341,12 @@ function contextualHelp(arguments_) {
353
341
  "Uses the cached OpenCode model catalog and makes no LLM call.",
354
342
  "The preview normally prints an agent model-set Apply command.",
355
343
  ], [
356
- shellCommand(["agent", "configure", "manager", "--scope", "global", "--dry-run"]),
344
+ shellCommand(["agent", "configure", "manager", "--global", "--dry-run"]),
357
345
  shellCommand([
358
346
  "agent",
359
347
  "configure",
360
348
  "worker",
361
- "--scope",
362
- "global",
349
+ "--global",
363
350
  "--model",
364
351
  "openai/gpt-5",
365
352
  "--variant",
@@ -369,10 +356,10 @@ function contextualHelp(arguments_) {
369
356
  ]);
370
357
  if (domain === "agent" && operation === "model-set")
371
358
  return commandHelp("agent model-set", "Set one agent model and optional variant directly.", [
372
- "skills-opencode agent model-set <name> --scope <project|global> --model <provider/model> [--variant <id>] --dry-run",
373
- "skills-opencode agent model-set <name> --scope <project|global> --model <provider/model> [--variant <id>] --confirm <digest>",
359
+ "skills-opencode agent model-set <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
360
+ "skills-opencode agent model-set <name> [--global] --model <provider/model> [--variant <id>] --confirm <digest>",
374
361
  ], [
375
- ["--scope <project|global>", "Choose the profile scope."],
362
+ ["--global", "Use global scope; project scope is the default."],
376
363
  ["--provider <id>", "Provider when --model is not provider/model."],
377
364
  ["--model <id>", "Exact provider/model or model paired with --provider."],
378
365
  ["--variant <id>", "Set an optional model variant."],
@@ -390,8 +377,7 @@ function contextualHelp(arguments_) {
390
377
  "agent",
391
378
  "model-set",
392
379
  "worker",
393
- "--scope",
394
- "global",
380
+ "--global",
395
381
  "--model",
396
382
  "openai/gpt-5",
397
383
  "--variant",
@@ -402,8 +388,7 @@ function contextualHelp(arguments_) {
402
388
  "agent",
403
389
  "model-set",
404
390
  "manager",
405
- "--scope",
406
- "global",
391
+ "--global",
407
392
  "--model",
408
393
  "openai/gpt-5",
409
394
  "--clear-variant",
@@ -412,10 +397,10 @@ function contextualHelp(arguments_) {
412
397
  ]);
413
398
  if (domain === "agent" && operation === "reconcile")
414
399
  return commandHelp("agent reconcile", "Re-render managed agent files from saved profile configuration.", [
415
- "skills-opencode agent reconcile --scope <project|global> --dry-run",
416
- "skills-opencode agent reconcile --scope <project|global> --confirm <digest>",
400
+ "skills-opencode agent reconcile [--global] --dry-run",
401
+ "skills-opencode agent reconcile [--global] --confirm <digest>",
417
402
  ], [
418
- ["--scope <project|global>", "Choose the profile scope."],
403
+ ["--global", "Use global scope; project scope is the default."],
419
404
  ["--dry-run", "Preview rendered agent changes and conflicts."],
420
405
  ["--confirm <digest>", "Apply the exact unexpired preview."],
421
406
  ["--json", "Emit stable machine-readable output."],
@@ -424,8 +409,8 @@ function contextualHelp(arguments_) {
424
409
  "Uses saved models and variants while preserving package prompts and permissions.",
425
410
  "Preserves user-owned files and collisions; writes only after confirmation.",
426
411
  ], [
427
- shellCommand(["agent", "reconcile", "--scope", "global", "--dry-run"]),
428
- shellCommand(["agent", "reconcile", "--scope", "project", "--dry-run", "--json"]),
412
+ shellCommand(["agent", "reconcile", "--global", "--dry-run"]),
413
+ shellCommand(["agent", "reconcile", "--dry-run", "--json"]),
429
414
  ]);
430
415
  if (domain === "agent")
431
416
  return groupHelp("agent", "Inspect and manage fixed agents and additional critics.", "skills-opencode agent <command> [options]", [
@@ -437,17 +422,14 @@ function contextualHelp(arguments_) {
437
422
  "Prompts and permissions remain package-owned.",
438
423
  "Profile configuration changes models, variants, and additional critics.",
439
424
  "Mutations use preview and exact confirmation.",
440
- ], [
441
- shellCommand(["agent", "list", "--scope", "global"]),
442
- shellCommand(["agent", "model-set", "--help"]),
443
- ]);
425
+ ], [shellCommand(["agent", "list", "--global"]), shellCommand(["agent", "model-set", "--help"])]);
444
426
  if (domain === "critic" && operation === "add")
445
427
  return commandHelp("critic add", "Add an additional named critic with a selected model.", [
446
- "skills-opencode critic add [name] --scope <project|global> --dry-run",
447
- "skills-opencode critic add <name> --scope <project|global> --model <provider/model> [--variant <id>] --dry-run",
448
- "skills-opencode critic add <name> --scope <project|global> --model <provider/model> [--variant <id>] --confirm <digest>",
428
+ "skills-opencode critic add [name] [--global] --dry-run",
429
+ "skills-opencode critic add <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
430
+ "skills-opencode critic add <name> [--global] --model <provider/model> [--variant <id>] --confirm <digest>",
449
431
  ], [
450
- ["--scope <project|global>", "Choose the profile scope."],
432
+ ["--global", "Use global scope; project scope is the default."],
451
433
  ["--provider <id>", "Provider when --model is not provider/model."],
452
434
  ["--model <id>", "Exact provider/model or model paired with --provider."],
453
435
  ["--variant <id>", "Set an optional model variant."],
@@ -464,20 +446,19 @@ function contextualHelp(arguments_) {
464
446
  "critic",
465
447
  "add",
466
448
  "security",
467
- "--scope",
468
- "global",
449
+ "--global",
469
450
  "--model",
470
451
  "anthropic/claude-sonnet-4-6",
471
452
  "--dry-run",
472
453
  ]),
473
- shellCommand(["critic", "add", "performance", "--scope", "global", "--dry-run"]),
454
+ shellCommand(["critic", "add", "performance", "--global", "--dry-run"]),
474
455
  ]);
475
456
  if (domain === "critic" && operation === "remove")
476
457
  return commandHelp("critic remove", "Remove a package-managed additional critic.", [
477
- "skills-opencode critic remove <name> --scope <project|global> --dry-run",
478
- "skills-opencode critic remove <name> --scope <project|global> --confirm <digest>",
458
+ "skills-opencode critic remove <name> [--global] --dry-run",
459
+ "skills-opencode critic remove <name> [--global] --confirm <digest>",
479
460
  ], [
480
- ["--scope <project|global>", "Choose the profile scope."],
461
+ ["--global", "Use global scope; project scope is the default."],
481
462
  ["--dry-run", "Preview critic removal and pool changes."],
482
463
  ["--confirm <digest>", "Apply the exact unexpired preview."],
483
464
  ["--json", "Emit stable machine-readable output."],
@@ -487,8 +468,8 @@ function contextualHelp(arguments_) {
487
468
  "Preserves the fixed critic and user-owned files.",
488
469
  "Updates manager and review critic pools after confirmation.",
489
470
  ], [
490
- shellCommand(["critic", "remove", "security", "--scope", "global", "--dry-run"]),
491
- shellCommand(["critic", "remove", "security", "--scope", "project", "--dry-run"]),
471
+ shellCommand(["critic", "remove", "security", "--global", "--dry-run"]),
472
+ shellCommand(["critic", "remove", "security", "--dry-run"]),
492
473
  ]);
493
474
  if (domain === "critic")
494
475
  return groupHelp("critic", "Manage additional package-owned critic profiles.", "skills-opencode critic <command> [options]", [
@@ -504,9 +485,6 @@ function contextualHelp(arguments_) {
504
485
  ]);
505
486
  return undefined;
506
487
  }
507
- function help(arguments_ = []) {
508
- return contextualHelp(arguments_) ?? rootHelp();
509
- }
510
488
  async function interactiveInstallerSelection() {
511
489
  if (!process.stdin.isTTY || !process.stderr.isTTY)
512
490
  throw new InstallerError("terminal_required", "install requires explicit selection flags outside a terminal");
@@ -514,7 +492,6 @@ async function interactiveInstallerSelection() {
514
492
  "Portable skills are installed separately through npx skills.",
515
493
  "This installer does not install, update, or remove portable skills.",
516
494
  "Skill command adapters are OpenCode slash commands that load an already-installed skill with the same name.",
517
- "Package command adapters invoke package tools.",
518
495
  "Selecting a command adapter does not select or install its skill.",
519
496
  "\n",
520
497
  ].join("\n"));
@@ -525,10 +502,7 @@ async function interactiveInstallerSelection() {
525
502
  return selected;
526
503
  };
527
504
  const defaults = defaultSelection();
528
- const commands = [
529
- ...(await group("Skill command adapters", SKILL_COMMANDS, SKILL_COMMANDS)),
530
- ...(await group("Package command adapters", PACKAGE_COMMANDS, PACKAGE_COMMANDS)),
531
- ];
505
+ const commands = await group("Skill command adapters", SKILL_COMMANDS, SKILL_COMMANDS);
532
506
  const agents = await group("Fixed agents", defaults.agents, defaults.agents);
533
507
  const plugins = await group("Selectable plugins", SELECTABLE_PLUGINS, []);
534
508
  return normalizeSelection({
@@ -717,13 +691,13 @@ async function runProfile(request, options) {
717
691
  }
718
692
  function profileConfirmationArguments(request, scope, digest) {
719
693
  if (request.action === "reconcile")
720
- return ["agent", "reconcile", "--scope", scope, "--confirm", digest];
694
+ return ["agent", "reconcile", ...scopeArguments(scope), "--confirm", digest];
721
695
  if (request.action === "critic-remove")
722
- return ["critic", "remove", request.name, "--scope", scope, "--confirm", digest];
696
+ return ["critic", "remove", request.name, ...scopeArguments(scope), "--confirm", digest];
723
697
  const command = request.action === "critic-add"
724
698
  ? ["critic", "add", request.name]
725
699
  : ["agent", "model-set", request.name];
726
- command.push("--scope", scope, "--model", request.model);
700
+ command.push(...scopeArguments(scope), "--model", request.model);
727
701
  if (request.variant)
728
702
  command.push("--variant", request.variant);
729
703
  else if (request.action === "model-set")
@@ -732,11 +706,36 @@ function profileConfirmationArguments(request, scope, digest) {
732
706
  return command;
733
707
  }
734
708
  async function run(arguments_) {
735
- if (arguments_.includes("--help") || arguments_.length === 0) {
736
- process.stdout.write(help(arguments_));
709
+ const removedScope = arguments_.find((value) => value === "--scope" || value.startsWith("--scope="));
710
+ if (removedScope)
711
+ throw new InstallerError("invalid_input", "--scope has been removed; omit it for project scope or use --global");
712
+ if (arguments_.filter((value) => value === "--global").length > 1)
713
+ throw new InstallerError("invalid_input", "--global may be supplied once");
714
+ if (arguments_[0] === "capabilities" && arguments_.includes("--global"))
715
+ throw new InstallerError("invalid_input", "capabilities accepts only --json");
716
+ if (arguments_.includes("--help")) {
717
+ const [domain, operation] = arguments_.filter((value) => !value.startsWith("--"));
718
+ if ((domain === "agent" &&
719
+ operation &&
720
+ !["list", "configure", "model-set", "reconcile"].includes(operation)) ||
721
+ (domain === "critic" && operation && !["add", "remove"].includes(operation)))
722
+ throw new InstallerError("invalid_input", `Unknown command: ${domain} ${operation}`);
723
+ const page = contextualHelp(arguments_);
724
+ if (!page && arguments_[0] !== "--help")
725
+ throw new InstallerError("invalid_input", `Unknown command: ${arguments_[0]}`);
726
+ for (const value of arguments_.filter((item) => item.startsWith("--") && item !== "--help"))
727
+ if (!(page ?? rootHelp()).includes(value))
728
+ throw new InstallerError("invalid_input", `Unknown argument: ${value}`);
729
+ process.stdout.write(page ?? rootHelp());
730
+ return;
731
+ }
732
+ if (arguments_.length === 0) {
733
+ process.stdout.write(rootHelp());
737
734
  return;
738
735
  }
739
736
  if (arguments_.includes("--version")) {
737
+ if (arguments_.some((value) => value !== "--version" && value !== "--json"))
738
+ throw new InstallerError("invalid_input", "--version accepts only --json");
740
739
  process.stdout.write(arguments_.includes("--json")
741
740
  ? `${JSON.stringify({ status: "ok", version: CATALOG.version })}\n`
742
741
  : `${CATALOG.version}\n`);
@@ -752,8 +751,9 @@ async function run(arguments_) {
752
751
  options.name ||
753
752
  options.provider ||
754
753
  options.model ||
755
- options.variant !== undefined)
756
- throw new InstallerError("invalid_input", "doctor accepts only --scope and --json");
754
+ options.variant !== undefined ||
755
+ options.selectionFlag)
756
+ throw new InstallerError("invalid_input", "doctor accepts only --global and --json");
757
757
  const report = await collectDoctorFacts(options.scope);
758
758
  if (options.json)
759
759
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
@@ -763,15 +763,18 @@ async function run(arguments_) {
763
763
  return;
764
764
  }
765
765
  if (domain === "capabilities") {
766
- const options = parseOptions(rest, false);
766
+ if (operation)
767
+ rest.unshift(operation);
768
+ const options = parseOptions(rest);
767
769
  if (options.dryRun ||
768
770
  options.confirm ||
769
771
  options.name ||
770
772
  options.provider ||
771
773
  options.model ||
772
774
  options.variant !== undefined ||
775
+ options.global ||
773
776
  options.selectionFlag)
774
- throw new InstallerError("invalid_input", "capabilities accepts only --scope and --json");
777
+ throw new InstallerError("invalid_input", "capabilities accepts only --json");
775
778
  process.stdout.write(`${JSON.stringify({ schema_version: 1, status: "ok", ...CATALOG }, null, 2)}\n`);
776
779
  return;
777
780
  }
@@ -779,8 +782,12 @@ async function run(arguments_) {
779
782
  if (operation)
780
783
  rest.unshift(operation);
781
784
  const options = parseOptions(rest);
782
- if (options.name || options.provider || options.model || options.variant !== undefined)
783
- throw new InstallerError("invalid_input", "reconcile accepts only scope and confirmation options");
785
+ if (options.name ||
786
+ options.provider ||
787
+ options.model ||
788
+ options.variant !== undefined ||
789
+ options.selectionFlag)
790
+ throw new InstallerError("invalid_input", "reconcile accepts only --global, --dry-run, --confirm, and --json");
784
791
  requireConfirmationMode(options);
785
792
  if (options.dryRun) {
786
793
  const plan = await previewReconcile(options.scope);
@@ -792,8 +799,7 @@ async function run(arguments_) {
792
799
  confirmationCommand: plan.confirmable
793
800
  ? shellCommand([
794
801
  "reconcile",
795
- "--scope",
796
- options.scope,
802
+ ...scopeArguments(options.scope),
797
803
  "--confirm",
798
804
  plan.confirmation_digest ?? plan.digest,
799
805
  ])
@@ -815,9 +821,15 @@ async function run(arguments_) {
815
821
  else
816
822
  throw new InstallerError("invalid_input", `Unexpected argument: ${operation}`);
817
823
  const options = parseOptions(rest);
818
- if (options.name || options.provider || options.model || options.variant !== undefined)
819
- throw new InstallerError("invalid_input", "Installer accepts only scope and confirmation options");
820
824
  const action = domain;
825
+ if (options.name ||
826
+ options.provider ||
827
+ options.model ||
828
+ options.variant !== undefined ||
829
+ (action === "uninstall" && options.selectionFlag))
830
+ throw new InstallerError("invalid_input", action === "install"
831
+ ? "install does not accept model options or positional arguments"
832
+ : "uninstall accepts only --global, --dry-run, --confirm, and --json");
821
833
  if (action === "install" &&
822
834
  options.selectionFlag &&
823
835
  (options.commands === undefined ||
@@ -839,8 +851,7 @@ async function run(arguments_) {
839
851
  applied: false,
840
852
  confirmationCommand: shellCommand([
841
853
  action,
842
- "--scope",
843
- options.scope,
854
+ ...scopeArguments(options.scope),
844
855
  ...selectionArguments(plan.selection),
845
856
  "--confirm",
846
857
  plan.digest,
@@ -863,8 +874,9 @@ async function run(arguments_) {
863
874
  options.name ||
864
875
  options.provider ||
865
876
  options.model ||
866
- options.variant !== undefined)
867
- throw new InstallerError("invalid_input", "agent list accepts only --scope");
877
+ options.variant !== undefined ||
878
+ options.selectionFlag)
879
+ throw new InstallerError("invalid_input", "agent list accepts only --global and --json");
868
880
  const inventory = await listAgentProfiles(options.scope);
869
881
  if (options.json)
870
882
  process.stdout.write(`${JSON.stringify({ status: "ok", inventory }, null, 2)}\n`);
@@ -874,6 +886,8 @@ async function run(arguments_) {
874
886
  }
875
887
  if (domain === "agent" && operation === "configure") {
876
888
  const options = parseOptions(rest);
889
+ if (options.selectionFlag)
890
+ throw new InstallerError("invalid_input", "agent configure does not accept selection options");
877
891
  requireConfirmationMode(options);
878
892
  const selected = options.provider && options.model && options.name
879
893
  ? {
@@ -889,6 +903,8 @@ async function run(arguments_) {
889
903
  }
890
904
  if (domain === "agent" && operation === "model-set") {
891
905
  const options = parseOptions(rest);
906
+ if (options.selectionFlag)
907
+ throw new InstallerError("invalid_input", "agent model-set does not accept selection options");
892
908
  const name = validateAgentName(options.name ?? "");
893
909
  const model = exactModel(options);
894
910
  if (!model)
@@ -898,17 +914,25 @@ async function run(arguments_) {
898
914
  }
899
915
  if (domain === "agent" && operation === "reconcile") {
900
916
  const options = parseOptions(rest);
901
- if (options.name || options.provider || options.model || options.variant !== undefined)
902
- throw new InstallerError("invalid_input", "agent reconcile accepts only scope and confirmation options");
917
+ if (options.name ||
918
+ options.provider ||
919
+ options.model ||
920
+ options.variant !== undefined ||
921
+ options.selectionFlag)
922
+ throw new InstallerError("invalid_input", "agent reconcile accepts only --global, --dry-run, --confirm, and --json");
903
923
  await runProfile({ action: "reconcile" }, options);
904
924
  return;
905
925
  }
906
926
  if (domain === "critic" && (operation === "add" || operation === "remove")) {
907
927
  const options = parseOptions(rest);
928
+ if (options.selectionFlag)
929
+ throw new InstallerError("invalid_input", `critic ${operation} does not accept selection options`);
908
930
  const name = options.name === "critic" || options.name?.startsWith("critic-")
909
931
  ? options.name
910
932
  : `critic-${options.name ?? ""}`;
911
933
  if (operation === "add") {
934
+ if (options.variant === null)
935
+ throw new InstallerError("invalid_input", "critic add does not accept --clear-variant");
912
936
  const model = exactModel(options);
913
937
  if (!model) {
914
938
  const selected = await interactiveSelection(options, "critic-add");
@@ -924,13 +948,16 @@ async function run(arguments_) {
924
948
  }
925
949
  }
926
950
  else {
927
- if (options.provider || options.model || options.variant !== undefined)
951
+ if (options.provider ||
952
+ options.model ||
953
+ options.variant !== undefined ||
954
+ options.selectionFlag)
928
955
  throw new InstallerError("invalid_input", "critic remove does not accept model options");
929
956
  await runProfile({ action: "critic-remove", name }, options);
930
957
  }
931
958
  return;
932
959
  }
933
- throw new InstallerError("invalid_input", "Use install, uninstall, agent list|configure|model-set|reconcile, or critic add|remove");
960
+ throw new InstallerError("invalid_input", "Use install, uninstall, doctor, capabilities, reconcile, agent list|configure|model-set|reconcile, or critic add|remove");
934
961
  }
935
962
  async function main() {
936
963
  try {