argsbarg 4.1.1 → 5.0.1

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 (62) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +5 -5
  3. package/docs/README.md +3 -3
  4. package/docs/ai-skills.md +9 -9
  5. package/docs/bundled-docs.md +4 -4
  6. package/docs/cli-program.md +8 -8
  7. package/docs/config-schema.md +2 -2
  8. package/docs/configure.md +177 -0
  9. package/docs/developing.md +1 -1
  10. package/docs/distribution-homebrew.md +10 -9
  11. package/docs/mcp.md +9 -9
  12. package/examples/full-example/README.md +3 -3
  13. package/examples/full-example/justfile +6 -6
  14. package/examples/full-example/scripts/formula-shared.ts +2 -2
  15. package/examples/full-example/src/program.ts +3 -3
  16. package/examples/nested.ts +1 -1
  17. package/index.d.ts +23 -16
  18. package/package.json +1 -1
  19. package/src/builtins/builtins.test.ts +82 -61
  20. package/src/builtins/completion-group.ts +4 -6
  21. package/src/builtins/configure-copy.ts +86 -0
  22. package/src/builtins/configure.ts +70 -0
  23. package/src/builtins/dispatch.ts +13 -33
  24. package/src/builtins/index.ts +1 -1
  25. package/src/builtins/mcp.ts +2 -2
  26. package/src/builtins/registry.ts +6 -6
  27. package/src/capabilities.ts +22 -13
  28. package/src/cli-tool/cli-smoke.test.ts +13 -3
  29. package/src/cli-tool/create.test.ts +23 -1
  30. package/src/cli-tool/create.ts +26 -4
  31. package/src/cli-tool/full-example-capabilities.test.ts +2 -2
  32. package/src/cli-tool/program.ts +20 -5
  33. package/src/cli-tool/run-create.ts +8 -19
  34. package/src/config/bootstrap.ts +11 -9
  35. package/src/config/file.test.ts +1 -1
  36. package/src/config/resolve.ts +2 -2
  37. package/src/configure/configure.test.ts +148 -0
  38. package/src/configure/index.ts +284 -0
  39. package/src/configure/prompt.ts +40 -0
  40. package/src/docs/builtin.ts +3 -5
  41. package/src/docs/docs.test.ts +5 -5
  42. package/src/docs/mcp-guide.ts +4 -4
  43. package/src/index.ts +2 -2
  44. package/src/install/install-validate.test.ts +5 -5
  45. package/src/install/opts.ts +17 -0
  46. package/src/install/target-effective.ts +8 -8
  47. package/src/install/target-scope.ts +11 -8
  48. package/src/install/targets/configure.ts +1 -1
  49. package/src/install/targets.test.ts +4 -4
  50. package/src/invoke.test.ts +1 -1
  51. package/src/mcp/tools.ts +1 -1
  52. package/src/mcp.integration.test.ts +4 -4
  53. package/src/parse.test.ts +11 -13
  54. package/src/schema.ts +1 -9
  55. package/src/skill/hint.ts +2 -2
  56. package/src/types.ts +22 -14
  57. package/src/validate.ts +17 -17
  58. package/docs/install.md +0 -206
  59. package/src/builtins/install.ts +0 -106
  60. package/src/builtins/uninstall.ts +0 -80
  61. package/src/install/index.ts +0 -409
  62. package/src/install/install.test.ts +0 -317
@@ -32,7 +32,7 @@ const configSchema = {
32
32
  export const program = {
33
33
  key: createIdentity.key,
34
34
  version: "1.0.0",
35
- description: "Argsbarg full example — all builtins, schemagen, ctx.appConfig.",
35
+ description: createIdentity.desc,
36
36
  appConfig: {
37
37
  jsonSchema: APP_CONFIG_JSON_SCHEMA,
38
38
  entries: configSchema,
@@ -50,8 +50,8 @@ export const program = {
50
50
  mcpd: true,
51
51
  claudePlugin: true,
52
52
  },
53
- install: {
54
- // Defaults: agentIntegration picks skill vs MCP for --all; configure is opt-in.
53
+ configure: {
54
+ // Defaults: agentIntegration picks skill vs MCP for sync; app config is opt-in in interactive configure.
55
55
  },
56
56
  commands: [echoCommand, statusCommand],
57
57
  } satisfies CliProgram;
@@ -21,7 +21,7 @@ const program = {
21
21
  readme: { text: "# nested.ts\n\nNested groups demo.\n" },
22
22
  },
23
23
  },
24
- install: {},
24
+ configure: {},
25
25
  commands: [
26
26
  {
27
27
  key: "stat",
package/index.d.ts CHANGED
@@ -307,18 +307,23 @@ export interface CliAppConfig {
307
307
  /** Per-key metadata; keys must match `jsonSchema.properties` when `jsonSchema` is set. */
308
308
  entries: Record<string, CliAppConfigEntry>;
309
309
  }
310
- export interface CliInstallConfig {
311
- /** When `false`, hide/disable `install` (default: enabled). */
310
+ /** Opt-out for the `completion` built-in (default: enabled). */
311
+ export interface CliCompletionConfig {
312
+ /** When `false`, hide/disable `completion` (default: enabled). */
313
+ enabled?: boolean;
314
+ }
315
+ export interface CliConfigureConfig {
316
+ /** When `false`, hide/disable `configure` (default: enabled). */
312
317
  enabled?: boolean;
313
318
  /**
314
- * Default agent integration for full install (`install --all`).
315
- * - `'mcp'` when `mcpServer.enabled` (default): MCP targets in `--all`; paired skills excluded.
316
- * - `'skill'` when MCP is off (default): skill targets in `--all`; paired MCP excluded.
317
- * - `'both'`: install MCP and skill for the same host when both are available.
319
+ * Default agent integration for sync (`configure --sync`).
320
+ * - `'mcp'` when `mcpServer.enabled` (default): MCP targets in sync; paired skills excluded.
321
+ * - `'skill'` when MCP is off (default): skill targets in sync; paired MCP excluded.
322
+ * - `'both'`: sync MCP and skill for the same host when both are available.
318
323
  */
319
324
  agentIntegration?: InstallAgentIntegration;
320
- /** Per-artifact gates for full install/uninstall. See {@link resolveEffectiveInstallTargets}. */
321
- targets?: CliInstallTargets;
325
+ /** Per-artifact gates for configure sync and interactive wizard. See {@link resolveEffectiveInstallTargets}. */
326
+ targets?: CliConfigureTargets;
322
327
  }
323
328
  /** Agent integration mode for install — MCP vs shell skill per host. */
324
329
  export type InstallAgentIntegration = "mcp" | "skill" | "both";
@@ -326,15 +331,15 @@ export type InstallAgentIntegration = "mcp" | "skill" | "both";
326
331
  export type InstallTargetSpec = boolean | {
327
332
  /** When false, artifact is never installed (even with scoped CLI flags). Default true. */
328
333
  enabled?: boolean;
329
- /** When true, included in bare `install` / `install --all`. Default varies by key. */
334
+ /** When true, included in `configure --sync`. Default varies by key. */
330
335
  includedInAll?: boolean;
331
336
  };
332
337
  export interface ResolvedInstallTarget {
333
338
  enabled: boolean;
334
339
  includedInAll: boolean;
335
340
  }
336
- /** Per-artifact gates for full install/uninstall. See {@link resolveEffectiveInstallTargets}. */
337
- export interface CliInstallTargets {
341
+ /** Per-artifact gates for configure. See {@link resolveEffectiveInstallTargets}. */
342
+ export interface CliConfigureTargets {
338
343
  /** App binary status only (Homebrew PATH); no self-install. */
339
344
  app?: InstallTargetSpec;
340
345
  /** ChatGPT desktop MCP. Default false. */
@@ -349,7 +354,7 @@ export interface CliInstallTargets {
349
354
  codexMcp?: InstallTargetSpec;
350
355
  /** Codex skill. Default false. */
351
356
  codexSkill?: InstallTargetSpec;
352
- /** App config: wizard via install --configure only. Default not in --all. */
357
+ /** App config: interactive wizard step in `configure`. Default not in sync. */
353
358
  configure?: InstallTargetSpec;
354
359
  /** Cursor MCP. Default false. */
355
360
  cursorMcp?: InstallTargetSpec;
@@ -447,8 +452,10 @@ export type CliProgram = CliNode & {
447
452
  appConfig?: CliAppConfig;
448
453
  /** When set with `enabled: true`, enables the `mcp` built-in subcommand. */
449
454
  mcpServer?: CliMcpServerConfig;
450
- /** Opt-out and defaults for `install`. */
451
- install?: CliInstallConfig;
455
+ /** Opt-out and defaults for `configure`. */
456
+ configure?: CliConfigureConfig;
457
+ /** Opt-out for shell completion generation (`completion bash|zsh|fish`). */
458
+ completion?: CliCompletionConfig;
452
459
  /** When set with `enabled: true`, enables the `docs` built-in command group. */
453
460
  docs?: CliDocsConfig;
454
461
  };
@@ -466,9 +473,9 @@ export declare class CliSchemaValidationError extends Error {
466
473
  }
467
474
  /** Platform builtins derived from program config and runtime. */
468
475
  export interface CliCapabilities {
469
- completion: true;
476
+ completion: boolean;
470
477
  mcp: boolean;
471
- install: boolean;
478
+ configure: boolean;
472
479
  docs: boolean;
473
480
  configCommands: boolean;
474
481
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "4.1.1",
3
+ "version": "5.0.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -1,12 +1,14 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { resolveCapabilities } from "../capabilities.ts";
3
+ import { cliBuiltinDocsGroup } from "../docs/builtin.ts";
2
4
  import { ParseKind, parse, postParseValidate } from "../parse.ts";
3
5
  import type { CliProgram } from "../types.ts";
6
+ import { cliBuiltinConfigureCommand, configureBuiltinOptions } from "./configure.ts";
7
+ import { configureCommandDescription, configureSyncOptionDescription } from "./configure-copy.ts";
4
8
  import { exportPresentationBuiltins } from "./export.ts";
5
9
  import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
6
- import { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
7
10
  import { cliBuiltinMcpCommand } from "./mcp.ts";
8
11
  import { cliParseRoot, cliPresentationRoot } from "./presentation.ts";
9
- import { cliBuiltinUninstallCommand, uninstallBuiltinOptions } from "./uninstall.ts";
10
12
 
11
13
  const fixture: CliProgram = {
12
14
  key: "myapp",
@@ -22,54 +24,63 @@ const fixture: CliProgram = {
22
24
  ],
23
25
  };
24
26
 
27
+ const noMcp: CliProgram = {
28
+ key: "skillonly",
29
+ version: "0.0.0",
30
+ description: "Skills only.",
31
+ commands: [{ key: "ping", description: "Ping.", handler: () => {} }],
32
+ };
33
+
25
34
  describe("builtins help copy", () => {
26
- test("install command includes Homebrew-oriented description", () => {
27
- const install = cliBuiltinInstallCommand(fixture);
28
- expect(install.description).toContain("agent skills");
29
- expect(install.notes).toContain("brew install");
30
- const names = installBuiltinOptions(fixture).map((o) => o.name);
31
- expect(names).toContain("all");
32
- expect(names).toContain("mcp");
33
- expect(names).not.toContain("app");
34
- expect(names).not.toContain("completions");
35
- expect(names).not.toContain("update");
36
- expect(names.indexOf("all")).toBeLessThan(names.indexOf("mcp"));
37
- expect(names.indexOf("mcp")).toBeLessThan(names.indexOf("status"));
38
- expect(names).not.toContain("uninstall");
39
- const yesOpt = installBuiltinOptions(fixture).find((o) => o.name === "yes");
35
+ test("configure command includes Homebrew-oriented description", () => {
36
+ const configure = cliBuiltinConfigureCommand(fixture);
37
+ expect(configure.description).toContain("agent skills");
38
+ expect(configure.description).toContain("MCP config");
39
+ expect(configure.notes).toContain("brew install");
40
+ const names = configureBuiltinOptions(fixture).map((o) => o.name);
41
+ expect(names).toContain("sync");
42
+ expect(names).toContain("remove-all");
43
+ expect(names).toContain("status");
44
+ const yesOpt = configureBuiltinOptions(fixture).find((o) => o.name === "yes");
40
45
  expect(yesOpt?.shortName).toBe("y");
41
46
  });
42
47
 
43
- test("uninstall command includes removal guidance", () => {
44
- const uninstall = cliBuiltinUninstallCommand(fixture);
45
- expect(uninstall.notes).toContain("brew uninstall");
46
- const names = uninstallBuiltinOptions(fixture).map((o) => o.name);
47
- expect(names).toContain("all");
48
- expect(names).not.toContain("status");
49
- expect(names).not.toContain("reinstall");
48
+ test("configure copy omits MCP when mcpServer unset", () => {
49
+ const caps = resolveCapabilities(noMcp);
50
+ expect(configureCommandDescription(noMcp, caps)).toBe(
51
+ "Set up agent skills for this app (binary via Homebrew).",
52
+ );
53
+ expect(configureCommandDescription(noMcp, caps)).not.toContain("MCP");
54
+ expect(configureSyncOptionDescription(noMcp, caps)).not.toContain("MCP");
55
+ const configure = cliBuiltinConfigureCommand(noMcp);
56
+ expect(configure.description).not.toContain("MCP");
50
57
  });
51
58
 
52
- test("install -y parses as --yes", () => {
59
+ test("configure notes mention brew upgrade and interactive configure", () => {
60
+ const configure = cliBuiltinConfigureCommand(fixture);
61
+ expect(configure.notes).toContain("brew upgrade");
62
+ expect(configure.notes).toContain("configure --sync --yes");
63
+ expect(configure.notes).toContain(`${fixture.key} configure`);
64
+
65
+ const withConfig: CliProgram = {
66
+ ...fixture,
67
+ appConfig: {
68
+ entries: { token: { description: "Token.", env: "TOKEN" } },
69
+ },
70
+ };
71
+ expect(configureBuiltinOptions(withConfig).map((o) => o.name)).toContain("remove-config");
72
+ });
73
+
74
+ test("configure -y parses as --yes", () => {
53
75
  const root = cliParseRoot(fixture);
54
- const pr = postParseValidate(root, parse(root, ["install", "-y"]));
76
+ const pr = postParseValidate(root, parse(root, ["configure", "-y", "--sync"]));
55
77
  expect(pr.kind).toBe(ParseKind.Ok);
56
78
  if (pr.kind === ParseKind.Ok) {
57
79
  expect(pr.opts.yes).toBe("1");
80
+ expect(pr.opts.sync).toBe("1");
58
81
  }
59
82
  });
60
83
 
61
- test("install omits --mcp option when mcpServer unset", () => {
62
- const noMcp: CliProgram = { key: "x", version: "0.0.0", description: "x", handler: () => {} };
63
- const names = installBuiltinOptions(noMcp).map((o) => o.name);
64
- expect(names).not.toContain("mcp");
65
- });
66
-
67
- test("install notes mention brew upgrade", () => {
68
- const install = cliBuiltinInstallCommand(fixture);
69
- expect(install.notes).toContain("brew upgrade");
70
- expect(install.notes).toContain("install --configure");
71
- });
72
-
73
84
  test("mcp builtin description is user-facing", () => {
74
85
  const withDocs: CliProgram = {
75
86
  ...fixture,
@@ -77,39 +88,31 @@ describe("builtins help copy", () => {
77
88
  };
78
89
  const mcp = cliBuiltinMcpCommand(withDocs);
79
90
  expect(mcp.description).toContain("MCP server");
80
- expect(mcp.notes).toContain("install --mcp --yes");
91
+ expect(mcp.notes).toContain("configure");
81
92
  expect(mcp.notes).toContain("docs mcp");
82
93
  });
83
94
  });
84
95
 
85
96
  describe("presentation root", () => {
86
- test("includes mcp and install when enabled", () => {
97
+ test("includes mcp and configure when enabled", () => {
87
98
  const root = cliPresentationRoot(fixture);
88
99
  const keys = root.commands?.map((c) => c.key) ?? [];
89
100
  expect(keys).toContain("mcp");
90
- expect(keys).toContain("install");
91
- expect(keys).toContain("uninstall");
101
+ expect(keys).toContain("configure");
102
+ expect(keys).not.toContain("completion");
103
+ expect(keys).not.toContain("install");
92
104
  });
93
105
 
94
- test("omits install when install.enabled is false", () => {
95
- const disabled: CliProgram = { ...fixture, install: { enabled: false } };
106
+ test("omits configure when configure.enabled is false", () => {
107
+ const disabled: CliProgram = { ...fixture, configure: { enabled: false } };
96
108
  const root = cliPresentationRoot(disabled);
97
- expect(root.commands?.map((c) => c.key)).not.toContain("install");
109
+ expect(root.commands?.map((c) => c.key)).not.toContain("configure");
98
110
  });
111
+
99
112
  test("includes version builtin", () => {
100
113
  const root = cliPresentationRoot(fixture);
101
114
  expect(root.commands?.map((c) => c.key)).toContain("version");
102
115
  });
103
-
104
- test("root notes include agent hint when docs enabled", () => {
105
- const withDocs: CliProgram = {
106
- ...fixture,
107
- docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
108
- };
109
- const root = cliPresentationRoot(withDocs);
110
- expect(root.notes).toContain("For AI agents: `myapp docs skill`.");
111
- expect(root.notes).not.toContain("install --skill");
112
- });
113
116
  });
114
117
 
115
118
  describe("completion emitters", () => {
@@ -118,14 +121,14 @@ describe("completion emitters", () => {
118
121
  const fish = completionFishScript(schema);
119
122
  expect(fish).toContain("complete -c myapp");
120
123
  expect(fish).toContain("hello");
121
- expect(fish).toContain("install");
124
+ expect(fish).toContain("configure");
122
125
  });
123
126
 
124
- test("bash script includes install flags", () => {
127
+ test("bash script includes configure flags", () => {
125
128
  const schema = cliPresentationRoot(fixture);
126
129
  const bash = completionBashScript(schema);
127
- expect(bash).toContain("--all");
128
- expect(bash).toContain("install");
130
+ expect(bash).toContain("hello");
131
+ expect(bash).toContain("--sync");
129
132
  });
130
133
 
131
134
  test("zsh script registers compdef", () => {
@@ -153,11 +156,29 @@ describe("schema export builtins", () => {
153
156
  };
154
157
  const builtins = exportPresentationBuiltins(withConfig);
155
158
  expect(builtins.map((b) => b.key)).toContain("config");
159
+ expect(builtins.map((b) => b.key)).toContain("configure");
156
160
  });
157
161
 
158
- test("exportPresentationBuiltins includes install options", () => {
162
+ test("exportPresentationBuiltins omits hidden completion", () => {
159
163
  const builtins = exportPresentationBuiltins(fixture);
160
- const install = builtins.find((b) => b.key === "install");
161
- expect(install?.options?.find((o) => o.name === "all")?.description).toContain("agent");
164
+ expect(builtins.map((b) => b.key)).not.toContain("completion");
165
+ });
166
+ });
167
+
168
+ describe("docs skill topic copy", () => {
169
+ test("mentions configure when configure is enabled", () => {
170
+ const withDocs: CliProgram = {
171
+ ...noMcp,
172
+ docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
173
+ };
174
+ const skill = cliBuiltinDocsGroup(withDocs).commands.find((c) => c.key === "skill");
175
+ expect(skill?.description).toContain("configure");
176
+
177
+ const configureOff: CliProgram = {
178
+ ...withDocs,
179
+ configure: { enabled: false },
180
+ };
181
+ const skillOff = cliBuiltinDocsGroup(configureOff).commands.find((c) => c.key === "skill");
182
+ expect(skillOff?.description).not.toContain("configure");
162
183
  });
163
184
  });
@@ -1,4 +1,3 @@
1
- import { resolveCapabilities } from "../capabilities.ts";
2
1
  import type { CliProgram } from "../types.ts";
3
2
 
4
3
  /**
@@ -8,6 +7,7 @@ export function cliBuiltinCompletionGroup(program: CliProgram): import("../types
8
7
  const appName = program.key;
9
8
  const router: import("../types.ts").CliRouter = {
10
9
  key: "completion",
10
+ hidden: true,
11
11
  description: "Generate the autocompletion script for shells.",
12
12
  commands: [
13
13
  {
@@ -44,10 +44,8 @@ export function cliBuiltinCompletionGroup(program: CliProgram): import("../types
44
44
  },
45
45
  ],
46
46
  };
47
- if (resolveCapabilities(program).install) {
48
- router.notes =
49
- "Completions are installed by Homebrew during formula install.\n\n" +
50
- "See: https://docs.brew.sh/Shell-Completion";
51
- }
47
+ router.notes =
48
+ "Completions are installed by Homebrew during formula install.\n\n" +
49
+ "See: https://docs.brew.sh/Shell-Completion";
52
50
  return router;
53
51
  }
@@ -0,0 +1,86 @@
1
+ /** Capability-aware labels for configure builtin and docs copy. */
2
+
3
+ import type { CliCapabilities } from "../capabilities.ts";
4
+ import type { CliProgram } from "../types.ts";
5
+
6
+ type Kind = "skills" | "mcp" | "config";
7
+
8
+ const LABEL: Record<Kind, { prose: string; short: string }> = {
9
+ skills: { prose: "agent skills", short: "skills" },
10
+ mcp: { prose: "MCP config", short: "MCP" },
11
+ config: { prose: "app config", short: "config" },
12
+ };
13
+
14
+ function enabledKinds(program: CliProgram, caps: CliCapabilities): Kind[] {
15
+ const kinds: Kind[] = ["skills"];
16
+ if (caps.mcp) kinds.push("mcp");
17
+ if (program.appConfig) kinds.push("config");
18
+ return kinds;
19
+ }
20
+
21
+ function joinEnglish(items: string[]): string {
22
+ if (items.length === 0) return "agent artifacts";
23
+ if (items.length === 1) return items[0]!;
24
+ if (items.length === 2) return `${items[0]} and ${items[1]}`;
25
+ return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
26
+ }
27
+
28
+ function prose(program: CliProgram, caps: CliCapabilities): string {
29
+ return joinEnglish(enabledKinds(program, caps).map((k) => LABEL[k].prose));
30
+ }
31
+
32
+ function short(program: CliProgram, caps: CliCapabilities): string {
33
+ return joinEnglish(enabledKinds(program, caps).map((k) => LABEL[k].short));
34
+ }
35
+
36
+ export function configureCommandDescription(program: CliProgram, caps: CliCapabilities): string {
37
+ return `Set up ${prose(program, caps)} for this app (binary via Homebrew).`;
38
+ }
39
+
40
+ export function configureSyncOptionDescription(program: CliProgram, caps: CliCapabilities): string {
41
+ return `Refresh installed ${short(program, caps)}. Used by Homebrew post_install.`;
42
+ }
43
+
44
+ export function docsSkillTopicDescription(program: CliProgram, caps: CliCapabilities): string {
45
+ if (caps.configure) {
46
+ return "Print a reference agent SKILL; run `configure` to install an optimized copy.";
47
+ }
48
+ return "Print a reference agent SKILL for AI agents.";
49
+ }
50
+
51
+ export function configureCommandNotes(program: CliProgram, caps: CliCapabilities): string {
52
+ const app = program.key;
53
+ const lines = [
54
+ "Install the binary via Homebrew (tap-from-repo), then set up agent artifacts:",
55
+ ` brew tap <org>/<repo>`,
56
+ ` brew install <tap>/${app}`,
57
+ "",
58
+ "Homebrew post_install runs:",
59
+ ` ${app} configure --sync --yes`,
60
+ "",
61
+ "Interactive setup (per target):",
62
+ ` ${app} configure`,
63
+ "",
64
+ "Upgrade:",
65
+ ` brew upgrade ${app}`,
66
+ "",
67
+ "Shell completions are installed by Homebrew during brew install.",
68
+ "See: https://docs.brew.sh/Shell-Completion",
69
+ "",
70
+ "See what is installed:",
71
+ ` ${app} configure --status`,
72
+ "",
73
+ "Remove agent artifacts before brew uninstall:",
74
+ ` ${app} configure --remove-all --yes`,
75
+ ` brew uninstall <tap>/${app}`,
76
+ "",
77
+ ];
78
+ if (program.appConfig) {
79
+ lines.push("Remove app config only:", ` ${app} configure --remove-config --yes`, "");
80
+ }
81
+ lines.push(
82
+ "Use --dry to preview changes without writing files.",
83
+ "Use --json for machine-readable output.",
84
+ );
85
+ return lines.join("\n");
86
+ }
@@ -0,0 +1,70 @@
1
+ import { resolveCapabilities } from "../capabilities.ts";
2
+ import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram } from "../types.ts";
3
+ import {
4
+ configureCommandDescription,
5
+ configureCommandNotes,
6
+ configureSyncOptionDescription,
7
+ } from "./configure-copy.ts";
8
+
9
+ /** Configure command options. */
10
+ export function configureBuiltinOptions(root: CliProgram): CliOption[] {
11
+ const caps = resolveCapabilities(root);
12
+ const opts: CliOption[] = [
13
+ {
14
+ name: "sync",
15
+ description: configureSyncOptionDescription(root, caps),
16
+ kind: CliOptionKind.Presence,
17
+ },
18
+ {
19
+ name: "remove-all",
20
+ description: "Remove all detected agent artifacts (skills and MCP).",
21
+ kind: CliOptionKind.Presence,
22
+ },
23
+ ];
24
+
25
+ if (root.appConfig) {
26
+ opts.push({
27
+ name: "remove-config",
28
+ description: "Remove the app config file only.",
29
+ kind: CliOptionKind.Presence,
30
+ });
31
+ }
32
+
33
+ opts.push(
34
+ {
35
+ name: "status",
36
+ description: "Print what is currently installed (read-only).",
37
+ kind: CliOptionKind.Presence,
38
+ },
39
+ {
40
+ name: "yes",
41
+ description: "Skip confirmation (required for --sync, --remove-all, --remove-config).",
42
+ kind: CliOptionKind.Presence,
43
+ shortName: "y",
44
+ },
45
+ {
46
+ name: "dry",
47
+ description: "Show what would change without writing files.",
48
+ kind: CliOptionKind.Presence,
49
+ },
50
+ {
51
+ name: "json",
52
+ description: "Print changed paths or status JSON on stdout.",
53
+ kind: CliOptionKind.Presence,
54
+ },
55
+ );
56
+
57
+ return opts;
58
+ }
59
+
60
+ /** Builds the `configure` built-in command. */
61
+ export function cliBuiltinConfigureCommand(root: CliProgram): CliLeaf {
62
+ const caps = resolveCapabilities(root);
63
+ return {
64
+ key: "configure",
65
+ description: configureCommandDescription(root, caps),
66
+ options: configureBuiltinOptions(root),
67
+ notes: configureCommandNotes(root, caps),
68
+ handler: () => {},
69
+ };
70
+ }
@@ -1,7 +1,7 @@
1
1
  import { capabilityDeniedMessage, resolveCapabilities } from "../capabilities.ts";
2
2
  import { Cli } from "../cli.ts";
3
+ import { cliConfigure } from "../configure/index.ts";
3
4
  import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
4
- import { cliInstall, cliUninstall } from "../install/index.ts";
5
5
  import { runMcpBundle } from "../mcp/bundle.ts";
6
6
  import type { ParseResult } from "../parse.ts";
7
7
  import { ParseKind } from "../parse.ts";
@@ -12,10 +12,9 @@ import { completionFishScript } from "./completion-fish.ts";
12
12
  import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
13
13
  import { completionZshScript } from "./completion-zsh.ts";
14
14
  import { cliBuiltinConfigGroupIfEnabled } from "./config.ts";
15
- import { cliBuiltinInstallCommand } from "./install.ts";
15
+ import { cliBuiltinConfigureCommand } from "./configure.ts";
16
16
  import { cliBuiltinMcpCommand } from "./mcp.ts";
17
17
  import { cliPresentationRoot } from "./presentation.ts";
18
- import { cliBuiltinUninstallCommand } from "./uninstall.ts";
19
18
  import { cliBuiltinVersionCommand } from "./version.ts";
20
19
 
21
20
  export interface DispatchBuiltinOpts {
@@ -45,6 +44,10 @@ export async function dispatchBuiltin(
45
44
  const caps = resolveCapabilities(program);
46
45
 
47
46
  if (pr.path[0] === "completion") {
47
+ if (!caps.completion) {
48
+ process.stderr.write(capabilityDeniedMessage("completion"));
49
+ process.exit(1);
50
+ }
48
51
  const schemaForCompletion = completionSchema(program, opts);
49
52
  if (pr.path[1] === "bash") {
50
53
  process.stdout.write(completionBashScript(schemaForCompletion));
@@ -93,28 +96,16 @@ export async function dispatchBuiltin(
93
96
  process.exit(1);
94
97
  }
95
98
 
96
- if (pr.path[0] === "install") {
97
- if (!caps.install) {
98
- process.stderr.write(capabilityDeniedMessage("install"));
99
- process.exit(1);
100
- }
101
- if (pr.path.length !== 1) {
102
- process.stderr.write(`Unknown subcommand: install ${pr.path.slice(1).join(" ")}\n`);
103
- process.exit(1);
104
- }
105
- await cliInstall(program, pr.opts);
106
- }
107
-
108
- if (pr.path[0] === "uninstall") {
109
- if (!caps.install) {
110
- process.stderr.write(capabilityDeniedMessage("install"));
99
+ if (pr.path[0] === "configure") {
100
+ if (!caps.configure) {
101
+ process.stderr.write(capabilityDeniedMessage("configure"));
111
102
  process.exit(1);
112
103
  }
113
104
  if (pr.path.length !== 1) {
114
- process.stderr.write(`Unknown subcommand: uninstall ${pr.path.slice(1).join(" ")}\n`);
105
+ process.stderr.write(`Unknown subcommand: configure ${pr.path.slice(1).join(" ")}\n`);
115
106
  process.exit(1);
116
107
  }
117
- await cliUninstall(program, pr.opts);
108
+ await cliConfigure(program, pr.opts);
118
109
  }
119
110
  }
120
111
 
@@ -141,23 +132,12 @@ export function builtinInterceptRoot(
141
132
  };
142
133
  }
143
134
 
144
- if (first === "install" && caps.install) {
145
- return {
146
- parseRoot: {
147
- key: program.key,
148
- description: program.description,
149
- commands: [cliBuiltinInstallCommand(program)],
150
- },
151
- isLeafCompletionIntercept: false,
152
- };
153
- }
154
-
155
- if (first === "uninstall" && caps.install) {
135
+ if (first === "configure" && caps.configure) {
156
136
  return {
157
137
  parseRoot: {
158
138
  key: program.key,
159
139
  description: program.description,
160
- commands: [cliBuiltinUninstallCommand(program)],
140
+ commands: [cliBuiltinConfigureCommand(program)],
161
141
  },
162
142
  isLeafCompletionIntercept: false,
163
143
  };
@@ -2,9 +2,9 @@ export { completionBashScript } from "./completion-bash.ts";
2
2
  export { completionFishScript } from "./completion-fish.ts";
3
3
  export { cliBuiltinCompletionGroup } from "./completion-group.ts";
4
4
  export { completionZshScript } from "./completion-zsh.ts";
5
+ export { cliBuiltinConfigureCommand, configureBuiltinOptions } from "./configure.ts";
5
6
  export { builtinInterceptRoot, dispatchBuiltin } from "./dispatch.ts";
6
7
  export { type CliSchemaExport, exportPresentationBuiltins } from "./export.ts";
7
- export { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
8
8
  export { cliBuiltinMcpCommand } from "./mcp.ts";
9
9
  export {
10
10
  cliParseRoot,
@@ -12,8 +12,8 @@ export function cliBuiltinMcpCommand(program: CliProgram): CliRouter {
12
12
  " args: mcp",
13
13
  "",
14
14
  ];
15
- if (caps.install) {
16
- lines.push("Or:", "", " {argsbarg:program} install --mcp --yes", "");
15
+ if (caps.configure) {
16
+ lines.push("Or:", "", " {argsbarg:program} configure", "");
17
17
  }
18
18
  if (docsEnabled(program)) {
19
19
  lines.push("Full setup guide: {argsbarg:program} docs mcp");