argsbarg 1.4.3 → 1.5.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.
Files changed (44) hide show
  1. package/.private/scratch.md +1 -1
  2. package/CHANGELOG.md +16 -1
  3. package/README.md +17 -13
  4. package/docs/ai-skills.md +24 -52
  5. package/docs/install.md +84 -0
  6. package/docs/mcp.md +5 -5
  7. package/index.d.ts +9 -18
  8. package/package.json +1 -1
  9. package/src/builtins/builtins.test.ts +101 -0
  10. package/src/builtins/completion-bash.ts +240 -0
  11. package/src/builtins/completion-fish.ts +73 -0
  12. package/src/builtins/completion-group.ts +50 -0
  13. package/src/builtins/completion-zsh.ts +244 -0
  14. package/src/builtins/dispatch.ts +123 -0
  15. package/src/builtins/export.ts +46 -0
  16. package/src/builtins/index.ts +10 -0
  17. package/src/builtins/install.ts +99 -0
  18. package/src/builtins/mcp.ts +13 -0
  19. package/src/builtins/presentation.ts +39 -0
  20. package/src/builtins/scopes.ts +45 -0
  21. package/src/builtins/shell-helpers.ts +24 -0
  22. package/src/completion.ts +10 -693
  23. package/src/index.test.ts +44 -55
  24. package/src/index.ts +1 -0
  25. package/src/install/binary.ts +82 -0
  26. package/src/install/compiled.ts +15 -0
  27. package/src/install/completions.ts +52 -0
  28. package/src/install/detect-installed.ts +67 -0
  29. package/src/install/index.ts +196 -0
  30. package/src/install/install.test.ts +124 -0
  31. package/src/install/mcp-config.ts +70 -0
  32. package/src/install/paths.ts +69 -0
  33. package/src/install/plan.ts +183 -0
  34. package/src/install/shell.ts +56 -0
  35. package/src/install/status.ts +63 -0
  36. package/src/install/uninstall.ts +111 -0
  37. package/src/mcp/tools.ts +1 -1
  38. package/src/runtime.ts +21 -83
  39. package/src/schema.ts +7 -49
  40. package/src/skill/generate.ts +4 -4
  41. package/src/skill/install.ts +20 -18
  42. package/src/types.ts +9 -9
  43. package/src/validate.ts +10 -22
  44. package/src/ai.ts +0 -7
package/src/runtime.ts CHANGED
@@ -1,25 +1,17 @@
1
1
  /*
2
2
  This module runs parsed commands, help, errors, completion, and leaf handlers.
3
- It owns the top-level control flow after parsing, including validation failures,
4
- shell completion dispatch, and leaf handler invocation.
5
-
6
- It keeps execution flow out of the public barrel so the exported API stays small and
7
- the runtime responsibilities remain easy to reason about.
8
3
  */
9
4
 
10
- import { cliBuiltinAiGroup, cliBuiltinCompletionGroup, cliPresentationRoot, completionBashScript, completionZshScript } from "./completion.ts";
5
+ import { builtinInterceptRoot, dispatchBuiltin } from "./builtins/dispatch.ts";
6
+ import { cliPresentationRoot } from "./builtins/presentation.ts";
7
+ import { isCompiledExecutable } from "./install/compiled.ts";
11
8
  import { CliContext } from "./context.ts";
12
9
  import { cliHelpRender } from "./help.ts";
13
- import { cliSkillInstall } from "./skill/install.ts";
14
- import { cliMcpServeStdio } from "./mcp.ts";
15
10
  import { parse, postParseValidate, ParseKind } from "./parse.ts";
16
11
  import { cliSchemaJson } from "./schema.ts";
17
12
  import { CliCommand } from "./types.ts";
18
13
  import { cliValidateRoot } from "./validate.ts";
19
14
 
20
- /**
21
- * Merges the caller's program root with the reserved `completion` subtree.
22
- */
23
15
  function cliRootMergedWithBuiltins(root: CliCommand): CliCommand {
24
16
  if (root.handler) {
25
17
  return root;
@@ -27,12 +19,6 @@ function cliRootMergedWithBuiltins(root: CliCommand): CliCommand {
27
19
  return cliPresentationRoot(root);
28
20
  }
29
21
 
30
- /**
31
- * Validates the schema, parses argv, prints help or errors, runs completion or the leaf handler, then exits.
32
- *
33
- * @param root The root CliCommand.
34
- * @param argv Override the default argv (process.argv.slice(2)).
35
- */
36
22
  export async function cliRun(root: CliCommand, argv: string[] = process.argv.slice(2)): Promise<never> {
37
23
  try {
38
24
  cliValidateRoot(root);
@@ -45,27 +31,27 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
45
31
  process.exit(1);
46
32
  }
47
33
 
48
- if (argv.length >= 2 && argv[0] === "ai" && argv[1] === "mcp" && !root.mcpServer) {
34
+ if (argv.length >= 1 && argv[0] === "mcp" && !root.mcpServer) {
49
35
  process.stderr.write("MCP is not enabled. Set mcpServer on the program root.\n");
50
36
  process.exit(1);
51
37
  }
52
38
 
53
- let parseRoot = root;
39
+ if (argv.length >= 1 && argv[0] === "install" && !isCompiledExecutable()) {
40
+ process.stderr.write("install is only available in compiled binaries (bun build --compile).\n");
41
+ process.exit(1);
42
+ }
43
+
44
+ let parseRoot: CliCommand;
54
45
  let isLeafCompletionIntercept = false;
55
46
 
56
- if (root.handler && argv.length >= 1 && argv[0] === "ai") {
57
- parseRoot = {
58
- key: root.key,
59
- description: root.description,
60
- commands: [cliBuiltinAiGroup(root)],
61
- } as CliCommand;
62
- } else if (root.handler && argv.length >= 1 && argv[0] === "completion") {
63
- isLeafCompletionIntercept = true;
64
- parseRoot = {
65
- key: root.key,
66
- description: root.description,
67
- commands: [cliBuiltinCompletionGroup(root.key)],
68
- } as any;
47
+ if (root.handler) {
48
+ const intercept = builtinInterceptRoot(root, argv);
49
+ if (intercept.isLeafCompletionIntercept || intercept.parseRoot !== root) {
50
+ parseRoot = intercept.parseRoot;
51
+ isLeafCompletionIntercept = intercept.isLeafCompletionIntercept;
52
+ } else {
53
+ parseRoot = root;
54
+ }
69
55
  } else {
70
56
  parseRoot = cliRootMergedWithBuiltins(root);
71
57
  }
@@ -91,53 +77,8 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
91
77
  process.exit(1);
92
78
  }
93
79
 
94
- // Leaf roots have an empty path; that's normal.
95
-
96
- if (pr.path[0] === "completion") {
97
- // If we intercepted a leaf, we MUST pass the original `root` to generate completions
98
- // because `parseRoot` is just a dummy router!
99
- const schemaForCompletion = isLeafCompletionIntercept ? root : parseRoot;
100
-
101
- if (pr.path[1] === "bash") {
102
- process.stdout.write(completionBashScript(schemaForCompletion));
103
- process.exit(0);
104
- }
105
- if (pr.path[1] === "zsh") {
106
- process.stdout.write(completionZshScript(schemaForCompletion));
107
- process.exit(0);
108
- }
109
- }
110
-
111
- if (pr.path[0] === "ai") {
112
- if (pr.path[1] === "mcp") {
113
- if (!root.mcpServer) {
114
- process.stderr.write("MCP is not enabled. Set mcpServer on the program root.\n");
115
- process.exit(1);
116
- }
117
- if (pr.path.length !== 2) {
118
- process.stderr.write("Unknown subcommand: ai " + pr.path.slice(1).join(" ") + "\n");
119
- process.exit(1);
120
- }
121
- await cliMcpServeStdio(root);
122
- } else if (pr.path[1] === "skill" && (pr.path[2] === "cursor" || pr.path[2] === "claude")) {
123
- if (root.aiSkill?.enabled === false) {
124
- process.stderr.write("AI skills are disabled. Remove aiSkill.enabled: false from the program root.\n");
125
- process.exit(1);
126
- }
127
- if (pr.path.length !== 3) {
128
- process.stderr.write("Unknown subcommand: ai " + pr.path.slice(1).join(" ") + "\n");
129
- process.exit(1);
130
- }
131
- const msg = cliSkillInstall(root, pr.path[2], {
132
- global: pr.opts.global === "1",
133
- force: pr.opts.force === "1",
134
- });
135
- process.stderr.write(msg + "\n");
136
- process.exit(0);
137
- } else {
138
- process.stderr.write("Unknown subcommand: ai " + pr.path.slice(1).join(" ") + "\n");
139
- process.exit(1);
140
- }
80
+ if (pr.kind === ParseKind.Ok) {
81
+ await dispatchBuiltin(root, pr, { isLeafCompletionIntercept, parseRoot });
141
82
  }
142
83
 
143
84
  let current = parseRoot;
@@ -167,13 +108,10 @@ export async function cliRun(root: CliCommand, argv: string[] = process.argv.sli
167
108
  }
168
109
  }
169
110
 
170
- /**
171
- * Prints a red error line and contextual help on stderr, then exits with status 1.
172
- */
173
111
  export function cliErrWithHelp(ctx: CliContext, msg: string): never {
174
112
  const color = process.stderr.isTTY;
175
113
  const line = color ? `\u001B[31m${msg}\u001B[0m` : msg;
176
114
  process.stderr.write(line + "\n");
177
115
  process.stderr.write(cliHelpRender(cliPresentationRoot(ctx.schema), ctx.commandPath, true));
178
116
  process.exit(1);
179
- }
117
+ }
package/src/schema.ts CHANGED
@@ -1,55 +1,12 @@
1
1
  /*
2
2
  This module serializes the CLI schema tree to JSON for machine-readable introspection.
3
- It strips handlers and runtime-only nodes so agents can discover commands, options,
4
- and positionals in one shot.
5
-
6
- It keeps schema export aligned with the declarative CliCommand model that drives help
7
- and completion.
8
3
  */
9
4
 
10
- import {
11
- CliCommand,
12
- CliFallbackMode,
13
- CliOption,
14
- CliPositional,
15
- } from "./types.ts";
16
- import { cliBuiltinCompletionGroup } from "./completion.ts";
17
-
18
- /** JSON-safe command node (no handlers). */
19
- export interface CliSchemaExport {
20
- /** Program or command key. */
21
- key: string;
22
- /** Short description shown in help. */
23
- description: string;
24
- /** Additional notes shown in help (supports {app} placeholder). */
25
- notes?: string;
26
- /** Global or command-level flags/options. */
27
- options?: CliOption[];
28
- /** Default top-level subcommand (program root only). */
29
- fallbackCommand?: string;
30
- /** How fallbackCommand is applied (program root only). */
31
- fallbackMode?: CliFallbackMode;
32
- /** Nested subcommands (routing nodes only). */
33
- commands?: CliSchemaExport[];
34
- /** Positional argument definitions (leaf nodes only). */
35
- positionals?: CliPositional[];
36
- }
5
+ import { CliCommand } from "./types.ts";
6
+ import { exportPresentationBuiltins, type CliSchemaExport } from "./builtins/export.ts";
37
7
 
38
- /** JSON-safe export of the reserved `completion` subtree (no handler recursion). */
39
- function exportBuiltinCompletionGroup(appName: string): CliSchemaExport {
40
- const group = cliBuiltinCompletionGroup(appName);
41
- return {
42
- key: group.key,
43
- description: group.description,
44
- commands: (group.commands ?? []).map((ch) => ({
45
- key: ch.key,
46
- description: ch.description,
47
- ...((ch.notes ?? "").length > 0 ? { notes: ch.notes } : {}),
48
- })),
49
- };
50
- }
8
+ const RESERVED = new Set(["completion", "install", "mcp"]);
51
9
 
52
- /** Converts one `CliCommand` node into a JSON-safe export (handlers omitted). */
53
10
  function exportCommand(cmd: CliCommand): CliSchemaExport {
54
11
  const out: CliSchemaExport = {
55
12
  key: cmd.key,
@@ -68,7 +25,7 @@ function exportCommand(cmd: CliCommand): CliSchemaExport {
68
25
  if ((cmd.positionals ?? []).length > 0) {
69
26
  out.positionals = cmd.positionals;
70
27
  }
71
- out.commands = [exportBuiltinCompletionGroup(cmd.key)];
28
+ out.commands = exportPresentationBuiltins(cmd);
72
29
  return out;
73
30
  }
74
31
 
@@ -79,7 +36,7 @@ function exportCommand(cmd: CliCommand): CliSchemaExport {
79
36
  out.fallbackMode = cmd.fallbackMode;
80
37
  }
81
38
 
82
- const children = (cmd.commands ?? []).filter((ch) => ch.key !== "completion");
39
+ const children = (cmd.commands ?? []).filter((ch) => !RESERVED.has(ch.key));
83
40
  if (children.length > 0) {
84
41
  out.commands = children.map(exportCommand);
85
42
  }
@@ -87,7 +44,8 @@ function exportCommand(cmd: CliCommand): CliSchemaExport {
87
44
  return out;
88
45
  }
89
46
 
90
- /** Returns pretty-printed JSON for the full program schema (trailing newline). */
91
47
  export function cliSchemaJson(root: CliCommand): string {
92
48
  return JSON.stringify(exportCommand(root), null, 2) + "\n";
93
49
  }
50
+
51
+ export type { CliSchemaExport };
@@ -53,7 +53,7 @@ function formatCommandEntry(root: CliCommand, tool: ReturnType<typeof collectMcp
53
53
 
54
54
  /** Builds SKILL.md body for the given target. */
55
55
  function buildSkillMd(root: CliCommand, target: SkillTarget, dirName: string): string {
56
- const name = root.aiSkill?.name ?? sanitizeToolSegment(root.key);
56
+ const name = sanitizeToolSegment(root.key);
57
57
  const description = skillDescription(root);
58
58
  const tools = collectMcpTools(root);
59
59
 
@@ -80,7 +80,7 @@ function buildSkillMd(root: CliCommand, target: SkillTarget, dirName: string): s
80
80
  "**Prefer MCP** when a host has the server connected:",
81
81
  "",
82
82
  "```bash",
83
- `${root.key} ai mcp`,
83
+ `${root.key} mcp`,
84
84
  "```",
85
85
  "",
86
86
  "Example Cursor `mcp.json` entry:",
@@ -91,7 +91,7 @@ function buildSkillMd(root: CliCommand, target: SkillTarget, dirName: string): s
91
91
  mcpServers: {
92
92
  [root.mcpServer.name ?? root.key]: {
93
93
  command: root.key,
94
- args: ["ai", "mcp"],
94
+ args: ["mcp"],
95
95
  },
96
96
  },
97
97
  },
@@ -174,7 +174,7 @@ function buildReferenceMd(root: CliCommand): string {
174
174
 
175
175
  /** Generates SKILL.md and reference.md for Cursor or Claude Code. */
176
176
  export function generateSkillBundle(root: CliCommand, target: SkillTarget): SkillBundle {
177
- const dirName = root.aiSkill?.name ?? sanitizeToolSegment(root.key);
177
+ const dirName = sanitizeToolSegment(root.key);
178
178
  return {
179
179
  dirName,
180
180
  skillMd: buildSkillMd(root, target, dirName),
@@ -1,8 +1,4 @@
1
- /*
2
- This module installs generated Agent Skills to Cursor or Claude Code skill directories.
3
- */
4
-
5
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
6
2
  import { homedir } from "node:os";
7
3
  import { join } from "node:path";
8
4
  import { CliCommand } from "../types.ts";
@@ -10,15 +6,16 @@ import { generateSkillBundle, type SkillTarget } from "./generate.ts";
10
6
 
11
7
  export interface SkillInstallOpts {
12
8
  global?: boolean;
13
- force?: boolean;
9
+ /** When true, remove an existing skill directory before writing. */
10
+ rimraf?: boolean;
11
+ /** When true, skip writes but return paths that would change. */
12
+ dry?: boolean;
14
13
  }
15
14
 
16
- /** Resolves the user home directory (`$HOME` when set). */
17
15
  function userHome(): string {
18
16
  return process.env.HOME ?? homedir();
19
17
  }
20
18
 
21
- /** Resolves the install directory for a skill target. */
22
19
  function resolveSkillDir(target: SkillTarget, dirName: string, global: boolean): string {
23
20
  const base = global
24
21
  ? join(userHome(), target === "cursor" ? ".cursor" : ".claude", "skills")
@@ -26,20 +23,25 @@ function resolveSkillDir(target: SkillTarget, dirName: string, global: boolean):
26
23
  return join(base, dirName);
27
24
  }
28
25
 
29
- /** Writes SKILL.md and reference.md to the target skills directory. */
30
- export function cliSkillInstall(root: CliCommand, target: SkillTarget, opts: SkillInstallOpts): string {
26
+ /** Writes SKILL.md and reference.md; returns changed file paths. */
27
+ export function cliSkillInstall(root: CliCommand, target: SkillTarget, opts: SkillInstallOpts): string[] {
31
28
  const bundle = generateSkillBundle(root, target);
32
29
  const dir = resolveSkillDir(target, bundle.dirName, opts.global ?? false);
30
+ const changed: string[] = [];
33
31
 
34
- if (existsSync(dir) && !opts.force) {
35
- process.stderr.write(`Skill directory already exists: ${dir}\nUse --force to overwrite.\n`);
36
- process.exit(1);
32
+ if (opts.rimraf && existsSync(dir) && !opts.dry) {
33
+ rmSync(dir, { recursive: true, force: true });
37
34
  }
38
35
 
39
- mkdirSync(dir, { recursive: true });
40
- writeFileSync(join(dir, "SKILL.md"), bundle.skillMd, "utf8");
41
- writeFileSync(join(dir, "reference.md"), bundle.referenceMd, "utf8");
36
+ const skillPath = join(dir, "SKILL.md");
37
+ const refPath = join(dir, "reference.md");
38
+
39
+ if (!opts.dry) {
40
+ mkdirSync(dir, { recursive: true });
41
+ writeFileSync(skillPath, bundle.skillMd, "utf8");
42
+ writeFileSync(refPath, bundle.referenceMd, "utf8");
43
+ }
42
44
 
43
- const label = target === "cursor" ? "Cursor" : "Claude Code";
44
- return `Installed ${label} skill to ${dir}/`;
45
+ changed.push(skillPath, refPath);
46
+ return changed;
45
47
  }
package/src/types.ts CHANGED
@@ -90,7 +90,7 @@ export interface CliPositional {
90
90
  }
91
91
 
92
92
  /**
93
- * Root-only. Enables `myapp ai mcp` and MCP stdio server metadata.
93
+ * Root-only. Enables `myapp mcp` and MCP stdio server metadata.
94
94
  */
95
95
  export interface CliMcpServerConfig {
96
96
  /** `initialize` serverInfo.name (default: root `key`). */
@@ -154,13 +154,13 @@ export interface CliMcpToolConfig {
154
154
  }
155
155
 
156
156
  /**
157
- * Root-only. Opt out of `ai skill` install commands with `{ enabled: false }`.
157
+ * Root-only. Opt-out and defaults for the `install` built-in (compiled binaries only).
158
158
  */
159
- export interface CliAiSkillConfig {
160
- /** When `false`, disable `ai skill *` install commands (default: enabled). */
159
+ export interface CliInstallConfig {
160
+ /** When `false`, hide/disable `install` (default: enabled). */
161
161
  enabled?: boolean;
162
- /** Skill directory name (default: sanitized root `key`). */
163
- name?: string;
162
+ /** Default bin directory (default: `~/.local/bin`). Overridden by `INSTALL_PREFIX` env and `--prefix`. */
163
+ prefix?: string;
164
164
  }
165
165
 
166
166
  /**
@@ -175,10 +175,10 @@ export interface CliCommandBase {
175
175
  notes?: string;
176
176
  /** Global or command-level flags/options. */
177
177
  options?: CliOption[];
178
- /** Root-only. When set, enables the `ai mcp` built-in subcommand. */
178
+ /** Root-only. When set, enables the `mcp` built-in subcommand. */
179
179
  mcpServer?: CliMcpServerConfig;
180
- /** Root-only. Opt out of `ai skill` install with `{ enabled: false }`. */
181
- aiSkill?: CliAiSkillConfig;
180
+ /** Root-only. Opt-out and defaults for `install` (compiled binaries only). */
181
+ install?: CliInstallConfig;
182
182
  /** Leaf-only. Per-tool MCP exposure and metadata. */
183
183
  mcpTool?: CliMcpToolConfig;
184
184
  }
package/src/validate.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  /*
2
2
  This module validates CLI schemas before execution.
3
- It checks reserved command names, handler placement, fallback rules, duplicate names,
4
- and positional ordering before the runtime starts.
5
-
6
- It fails early on structural problems so invalid trees never reach parsing or dispatch.
7
3
  */
8
4
 
9
5
  import {
@@ -14,26 +10,24 @@ import {
14
10
  } from "./types.ts";
15
11
  import { MCP_SCHEMA_URI_DEFAULT } from "./mcp/tools.ts";
16
12
 
17
- const reservedCommandNames = ["completion", "ai"];
13
+ function reservedCommandNames(root: CliCommand): string[] {
14
+ const names = ["completion", "install"];
15
+ if (root.mcpServer !== undefined) {
16
+ names.push("mcp");
17
+ }
18
+ return names;
19
+ }
18
20
 
19
- /**
20
- * Validates the static CliCommand tree against ArgBarg rules.
21
- * Throws CliSchemaValidationError if rules are violated.
22
- */
23
21
  export function cliValidateRoot(root: CliCommand): void {
24
-
25
- // Check for reserved command names at root
26
22
  for (const child of root.commands ?? []) {
27
- if (reservedCommandNames.includes(child.key)) {
23
+ if (reservedCommandNames(root).includes(child.key)) {
28
24
  throw new CliSchemaValidationError(`Reserved command name: ${child.key}`);
29
25
  }
30
26
  }
31
27
 
32
- // Recursively validate
33
28
  walkCommand(root, true);
34
29
  }
35
30
 
36
- /** Recursively validates a command node: handlers vs children, options, and positionals. */
37
31
  function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
38
32
  if (!isRoot && cmd.mcpServer !== undefined) {
39
33
  throw new CliSchemaValidationError(
@@ -41,9 +35,9 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
41
35
  );
42
36
  }
43
37
 
44
- if (!isRoot && cmd.aiSkill !== undefined) {
38
+ if (!isRoot && cmd.install !== undefined) {
45
39
  throw new CliSchemaValidationError(
46
- "aiSkill is only supported on the program root (not on " + cmd.key + ")",
40
+ "install is only supported on the program root (not on " + cmd.key + ")",
47
41
  );
48
42
  }
49
43
 
@@ -70,7 +64,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
70
64
  }
71
65
  }
72
66
 
73
- // Check for duplicate child names
74
67
  const seenNames = new Set<string>();
75
68
  for (const child of cmd.commands ?? []) {
76
69
  if (seenNames.has(child.key)) {
@@ -95,7 +88,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
95
88
  }
96
89
  }
97
90
 
98
- // Validate options (short name uniqueness, reserved -h, required presence)
99
91
  const seenShorts = new Set<string>();
100
92
  for (const opt of cmd.options ?? []) {
101
93
  if (opt.required && opt.kind === CliOptionKind.Presence) {
@@ -149,7 +141,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
149
141
  }
150
142
  }
151
143
 
152
- // Validate positionals
153
144
  const positionals = cmd.positionals ?? [];
154
145
  for (const p of positionals) {
155
146
  if (p.argMin !== undefined && p.argMin < 0) {
@@ -168,7 +159,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
168
159
  }
169
160
  }
170
161
 
171
- // Check positional ordering: required before optional
172
162
  let sawOptional = false;
173
163
  for (const p of positionals) {
174
164
  const { argMin = 1 } = p;
@@ -179,7 +169,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
179
169
  }
180
170
  }
181
171
 
182
- // Check unlimited positional must be last
183
172
  for (let idx = 0; idx < positionals.length; idx++) {
184
173
  const { argMax = 1 } = positionals[idx]!;
185
174
  if (argMax === 0 && idx + 1 < positionals.length) {
@@ -189,7 +178,6 @@ function walkCommand(cmd: CliCommand, isRoot: boolean = false): void {
189
178
  }
190
179
  }
191
180
 
192
- // Recurse into nested commands
193
181
  for (const child of cmd.commands ?? []) {
194
182
  walkCommand(child, false);
195
183
  }
package/src/ai.ts DELETED
@@ -1,7 +0,0 @@
1
- /*
2
- This module re-exports AI agent integration entry points (skill install).
3
- MCP stdio serving remains in mcp.ts.
4
- */
5
-
6
- export { cliSkillInstall, type SkillInstallOpts } from "./skill/install.ts";
7
- export { generateSkillBundle, type SkillBundle, type SkillTarget } from "./skill/generate.ts";