argsbarg 5.1.8 → 5.1.10

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 (94) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/docs/cli-program.md +3 -1
  3. package/docs/config-schema.md +5 -3
  4. package/docs/configure.md +13 -7
  5. package/docs/distribution-homebrew.md +17 -3
  6. package/examples/full-example/justfile +6 -5
  7. package/examples/full-example/scripts/formula-shared.test.ts +68 -0
  8. package/examples/full-example/scripts/formula-shared.ts +39 -1
  9. package/examples/full-example/scripts/release.ts +212 -0
  10. package/index.d.ts +10 -1
  11. package/package.json +2 -1
  12. package/src/builtins/builtins.test.ts +1 -3
  13. package/src/builtins/completion-bash.ts +6 -27
  14. package/src/builtins/completion-group.ts +1 -2
  15. package/src/builtins/completion-simulate-shared.ts +2 -10
  16. package/src/builtins/completion-zsh.ts +7 -33
  17. package/src/builtins/config.test.ts +28 -0
  18. package/src/builtins/config.ts +40 -10
  19. package/src/builtins/configure-copy.ts +3 -6
  20. package/src/builtins/dispatch.ts +2 -10
  21. package/src/builtins/export.ts +1 -3
  22. package/src/builtins/presentation.ts +2 -7
  23. package/src/builtins/registry.ts +1 -5
  24. package/src/cli-tool/create.test.ts +3 -6
  25. package/src/cli-tool/create.ts +5 -22
  26. package/src/cli-tool/post-create.ts +1 -3
  27. package/src/cli-tool/program.ts +1 -2
  28. package/src/cli-tool/run-create.ts +2 -8
  29. package/src/cli.ts +6 -27
  30. package/src/config/bindings.test.ts +72 -0
  31. package/src/config/bindings.ts +115 -0
  32. package/src/config/bootstrap.ts +80 -65
  33. package/src/config/context.test.ts +45 -0
  34. package/src/config/context.ts +74 -14
  35. package/src/config/entry.ts +1 -3
  36. package/src/config/file.test.ts +46 -3
  37. package/src/config/file.ts +59 -19
  38. package/src/config/resolve.ts +1 -4
  39. package/src/config/schema.ts +1 -3
  40. package/src/config/validate.test.ts +3 -11
  41. package/src/config/validate.ts +30 -30
  42. package/src/config.integration.test.ts +1 -5
  43. package/src/configure/configure.test.ts +19 -5
  44. package/src/configure/index.ts +17 -15
  45. package/src/docs/api-guide.ts +1 -6
  46. package/src/docs/builtin.ts +1 -5
  47. package/src/docs/docs.test.ts +2 -6
  48. package/src/docs/mcp-guide.ts +3 -13
  49. package/src/docs/mcp-resources.test.ts +1 -3
  50. package/src/docs/mcp-resources.ts +1 -6
  51. package/src/docs/save.ts +2 -10
  52. package/src/formats.test.ts +1 -7
  53. package/src/formats.ts +1 -5
  54. package/src/headless.test.ts +5 -17
  55. package/src/help.ts +9 -51
  56. package/src/hidden-mcpb.test.ts +3 -12
  57. package/src/hidden.ts +1 -3
  58. package/src/install/binary-placement.test.ts +2 -6
  59. package/src/install/binary-placement.ts +1 -4
  60. package/src/install/gh-release-update.ts +5 -22
  61. package/src/install/mcp-codex.ts +1 -2
  62. package/src/install/mcp-config.ts +2 -12
  63. package/src/install/mcp-opencode.test.ts +1 -3
  64. package/src/install/mcp-opencode.ts +3 -15
  65. package/src/install/plan.ts +2 -10
  66. package/src/install/status.ts +3 -5
  67. package/src/install/target-base.ts +2 -11
  68. package/src/install/target-detect.ts +1 -5
  69. package/src/install/target-effective.ts +3 -13
  70. package/src/install/target-mcp-cli.ts +4 -26
  71. package/src/install/target-mcp-json.ts +3 -14
  72. package/src/install/target-plan-build.ts +1 -5
  73. package/src/install/target-registry.ts +13 -21
  74. package/src/install/target-scope.ts +8 -25
  75. package/src/install/target-types.ts +1 -6
  76. package/src/install/targets/app.ts +2 -2
  77. package/src/install/targets/opencode-mcp.ts +1 -6
  78. package/src/install/targets.test.ts +3 -10
  79. package/src/install/uninstall.ts +2 -9
  80. package/src/invoke.test.ts +1 -3
  81. package/src/mcp/bundle.ts +4 -19
  82. package/src/mcp/claude.test.ts +3 -3
  83. package/src/mcp/claude.ts +4 -17
  84. package/src/mcp/env.ts +1 -3
  85. package/src/mcp/server.ts +3 -12
  86. package/src/mcp/tools.ts +2 -11
  87. package/src/mcp.integration.test.ts +4 -10
  88. package/src/parse.test.ts +9 -34
  89. package/src/parse.ts +10 -44
  90. package/src/schema.ts +2 -10
  91. package/src/skill/hint.ts +1 -5
  92. package/src/skill/install.ts +1 -5
  93. package/src/test-fixtures.ts +1 -3
  94. package/src/validate.ts +29 -90
@@ -7,28 +7,23 @@ export { INSTALL_TARGETS } from "./targets/index.ts";
7
7
 
8
8
  export const INSTALL_ARTIFACT_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.map((t) => t.key);
9
9
 
10
- export const SKILL_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter(
11
- (t) => t.category === "skill",
12
- ).map((t) => t.key);
10
+ export const SKILL_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter((t) => t.category === "skill").map(
11
+ (t) => t.key,
12
+ );
13
13
 
14
- export const MCP_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter(
15
- (t) => t.category === "mcp",
16
- ).map((t) => t.key);
14
+ export const MCP_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter((t) => t.category === "mcp").map((t) => t.key);
17
15
 
18
16
  /** Maps plan action kinds to install.targets artifact keys. */
19
- export const ACTION_KIND_TO_ARTIFACT: Record<InstallActionKind, CliInstallArtifactKey> =
20
- Object.fromEntries(INSTALL_TARGETS.map((t) => [t.actionKind, t.key])) as Record<
21
- InstallActionKind,
22
- CliInstallArtifactKey
23
- >;
17
+ export const ACTION_KIND_TO_ARTIFACT: Record<InstallActionKind, CliInstallArtifactKey> = Object.fromEntries(
18
+ INSTALL_TARGETS.map((t) => [t.actionKind, t.key]),
19
+ ) as Record<InstallActionKind, CliInstallArtifactKey>;
24
20
 
25
21
  /** Hosts with both MCP config and shell skill install targets. */
26
- export const AGENT_PAIRS: [CliInstallArtifactKey, CliInstallArtifactKey][] =
27
- INSTALL_TARGETS.flatMap((t) =>
28
- t.category === "mcp" && t.pairedKey !== undefined
29
- ? [[t.key, t.pairedKey] as [CliInstallArtifactKey, CliInstallArtifactKey]]
30
- : [],
31
- );
22
+ export const AGENT_PAIRS: [CliInstallArtifactKey, CliInstallArtifactKey][] = INSTALL_TARGETS.flatMap((t) =>
23
+ t.category === "mcp" && t.pairedKey !== undefined
24
+ ? [[t.key, t.pairedKey] as [CliInstallArtifactKey, CliInstallArtifactKey]]
25
+ : [],
26
+ );
32
27
 
33
28
  const targetByKey = new Map(INSTALL_TARGETS.map((t) => [t.key, t]));
34
29
 
@@ -47,10 +42,7 @@ export function isMcpArtifactKey(key: CliInstallArtifactKey): boolean {
47
42
  * Defaults: artifactDefaults in target-effective.ts.
48
43
  * Explicit spec rejection: validateConfigureConfig in validate.ts.
49
44
  */
50
- export function mcpServerRequiredForArtifact(
51
- key: CliInstallArtifactKey,
52
- mcpServerEnabled: boolean,
53
- ): boolean {
45
+ export function mcpServerRequiredForArtifact(key: CliInstallArtifactKey, mcpServerEnabled: boolean): boolean {
54
46
  return !isMcpArtifactKey(key) || mcpServerEnabled;
55
47
  }
56
48
 
@@ -1,5 +1,5 @@
1
1
  import { resolveCapabilities } from "../capabilities.ts";
2
- import { appConfigInstalled } from "../config/file.ts";
2
+ import { appConfigFileExists } from "../config/file.ts";
3
3
  import type { CliConfigureTargets, CliProgram } from "../types.ts";
4
4
  import type { InstallPaths } from "./paths.ts";
5
5
  import {
@@ -46,10 +46,7 @@ function emptyInstalledArtifacts(): InstalledArtifacts {
46
46
  }
47
47
 
48
48
  /** Detects which install artifacts are currently present. */
49
- export function detectInstalledArtifacts(
50
- paths: InstallPaths,
51
- root: CliProgram,
52
- ): InstalledArtifacts {
49
+ export function detectInstalledArtifacts(paths: InstallPaths, root: CliProgram): InstalledArtifacts {
53
50
  const out = emptyInstalledArtifacts();
54
51
  for (const target of INSTALL_TARGETS) {
55
52
  target.applyDetected(paths, root, out);
@@ -73,14 +70,11 @@ export function buildDetectedSnapshot(root: CliProgram, paths: InstallPaths): De
73
70
  const base = detectInstalledArtifacts(paths, root);
74
71
  return {
75
72
  ...base,
76
- appConfig: root.appConfig ? appConfigInstalled(root) : false,
73
+ appConfig: appConfigFileExists(root),
77
74
  };
78
75
  }
79
76
 
80
- function targetExplicitlyConfigured(
81
- user: CliConfigureTargets | undefined,
82
- key: CliInstallArtifactKey,
83
- ): boolean {
77
+ function targetExplicitlyConfigured(user: CliConfigureTargets | undefined, key: CliInstallArtifactKey): boolean {
84
78
  return user?.[key] !== undefined;
85
79
  }
86
80
 
@@ -220,10 +214,7 @@ export function buildTargetPlanContext(
220
214
  }
221
215
 
222
216
  /** Maps detected snapshot to artifact key (for tests and legacy callers). */
223
- export function detectedForArtifact(
224
- key: CliInstallArtifactKey,
225
- detected: DetectedSnapshot,
226
- ): boolean {
217
+ export function detectedForArtifact(key: CliInstallArtifactKey, detected: DetectedSnapshot): boolean {
227
218
  const target = installTargetForKey(key);
228
219
  if (key === "configure") {
229
220
  return detected.appConfig ?? false;
@@ -232,20 +223,12 @@ export function detectedForArtifact(
232
223
  }
233
224
 
234
225
  /** Resolved artifact keys for `--all`, `--mcp`, and `--skill` (availability-gated). */
235
- export function resolveInstallTargetPreview(
236
- program: CliProgram,
237
- paths: InstallPaths,
238
- ): InstallTargetPreview {
226
+ export function resolveInstallTargetPreview(program: CliProgram, paths: InstallPaths): InstallTargetPreview {
239
227
  const effective = resolveEffectiveInstallTargets(program.configure, program);
240
- const integration = resolveAgentIntegration(
241
- program.configure,
242
- program.mcpServer?.enabled === true,
243
- );
228
+ const integration = resolveAgentIntegration(program.configure, program.mcpServer?.enabled === true);
244
229
 
245
230
  const keysForScope = (scope: InstallScope, mode: InstallPlanMode): CliInstallArtifactKey[] =>
246
- INSTALL_ARTIFACT_KEYS.filter((key) =>
247
- shouldIncludeArtifact(key, program, paths, scope, mode, effective),
248
- );
231
+ INSTALL_ARTIFACT_KEYS.filter((key) => shouldIncludeArtifact(key, program, paths, scope, mode, effective));
249
232
 
250
233
  return {
251
234
  agentIntegration: integration,
@@ -3,12 +3,7 @@ import type { InstallPaths } from "./paths.ts";
3
3
 
4
4
  export type { InstallTargetSpec, ResolvedInstallTarget } from "../types.ts";
5
5
 
6
- export type InstallPlanMode =
7
- | "install-all"
8
- | "install-scoped"
9
- | "uninstall-all"
10
- | "uninstall-scoped"
11
- | "refresh";
6
+ export type InstallPlanMode = "install-all" | "install-scoped" | "uninstall-all" | "uninstall-scoped" | "refresh";
12
7
 
13
8
  export interface InstallScope {
14
9
  all?: boolean;
@@ -1,6 +1,6 @@
1
1
  import type { CliProgram } from "../../types.ts";
2
2
  import { isExternallyManagedBinary } from "../binary-placement.ts";
3
- import { displayInstallPath, type InstallPaths } from "../paths.ts";
3
+ import type { InstallPaths } from "../paths.ts";
4
4
  import { InstallTarget } from "../target-base.ts";
5
5
  import type {
6
6
  DetectedSnapshot,
@@ -25,7 +25,7 @@ class AppInstallTarget extends InstallTarget {
25
25
  return true;
26
26
  }
27
27
 
28
- isDetected(paths: InstallPaths, root: CliProgram): boolean {
28
+ isDetected(_paths: InstallPaths, root: CliProgram): boolean {
29
29
  return isExternallyManagedBinary(root.key) || false;
30
30
  }
31
31
 
@@ -57,12 +57,7 @@ class OpenCodeMcpInstallTarget extends InstallTarget {
57
57
 
58
58
  preflight(ctx: TargetPlanContext): string | null {
59
59
  const entry = expectedOpenCodeMcpEntry(ctx.root);
60
- return checkOpenCodeMcpConflict(
61
- ctx.paths.opencodeMcpPath,
62
- ctx.paths.mcpName,
63
- entry,
64
- !!ctx.opts.yes,
65
- );
60
+ return checkOpenCodeMcpConflict(ctx.paths.opencodeMcpPath, ctx.paths.mcpName, entry, !!ctx.opts.yes);
66
61
  }
67
62
 
68
63
  protected buildInstallActions(ctx: TargetPlanContext): InstallAction[] {
@@ -95,10 +95,7 @@ describe("resolveEffectiveInstallTargets", () => {
95
95
  isArtifactInScope(
96
96
  "cursorMcp",
97
97
  scope,
98
- resolveEffectiveInstallTargets(
99
- { targets: { cursorMcp: true }, agentIntegration: "both" },
100
- program,
101
- ),
98
+ resolveEffectiveInstallTargets({ targets: { cursorMcp: true }, agentIntegration: "both" }, program),
102
99
  "install-scoped",
103
100
  program,
104
101
  ),
@@ -117,13 +114,9 @@ describe("resolveEffectiveInstallTargets", () => {
117
114
  };
118
115
  const effective = resolveEffectiveInstallTargets(program.configure, program);
119
116
  const scope = { mcp: true, skill: true, configure: true };
120
- expect(isArtifactInScope("cursorSkill", scope, effective, "install-scoped", program)).toBe(
121
- true,
122
- );
117
+ expect(isArtifactInScope("cursorSkill", scope, effective, "install-scoped", program)).toBe(true);
123
118
  expect(isArtifactInScope("cursorMcp", scope, effective, "install-scoped", program)).toBe(true);
124
119
  expect(isArtifactInScope("configure", scope, effective, "install-scoped", program)).toBe(true);
125
- expect(isArtifactInScope("claudeSkill", scope, effective, "install-scoped", program)).toBe(
126
- true,
127
- );
120
+ expect(isArtifactInScope("claudeSkill", scope, effective, "install-scoped", program)).toBe(true);
128
121
  });
129
122
  });
@@ -8,11 +8,7 @@ import type { InstallOpts, UninstallAction } from "./target-types.ts";
8
8
  export type { InstallActionKind, InstallOpts, UninstallAction } from "./target-types.ts";
9
9
 
10
10
  /** Builds uninstall actions for scoped targets or --all (ignores install.targets on --all). */
11
- export function buildUninstallPlan(
12
- root: CliProgram,
13
- paths: InstallPaths,
14
- opts: InstallOpts,
15
- ): UninstallAction[] {
11
+ export function buildUninstallPlan(root: CliProgram, paths: InstallPaths, opts: InstallOpts): UninstallAction[] {
16
12
  return buildUninstallPlanFromTargets(root, paths, opts);
17
13
  }
18
14
 
@@ -24,10 +20,7 @@ export function uninstallSkillDir(dir: string, dry: boolean): string[] {
24
20
  }
25
21
 
26
22
  /** Skill directory for uninstall action summary prefix. */
27
- export function skillDirFromUninstallSummary(
28
- summary: string,
29
- paths: InstallPaths,
30
- ): string | undefined {
23
+ export function skillDirFromUninstallSummary(summary: string, paths: InstallPaths): string | undefined {
31
24
  const target = skillTargetForUninstallSummary(summary);
32
25
  return target?.skillDir(paths);
33
26
  }
@@ -188,9 +188,7 @@ test("ctx.positional returns undefined for absent optional slot", async () => {
188
188
  {
189
189
  key: "x",
190
190
  description: "",
191
- positionals: [
192
- { name: "opt", description: "", kind: CliOptionKind.String, argMin: 0, argMax: 1 },
193
- ],
191
+ positionals: [{ name: "opt", description: "", kind: CliOptionKind.String, argMin: 0, argMax: 1 }],
194
192
  handler: (ctx: CliContext) => {
195
193
  captured = ctx.positional("opt");
196
194
  },
package/src/mcp/bundle.ts CHANGED
@@ -3,15 +3,7 @@ Packs a CLI program into an MCP Bundle (`.mcpb`) for Claude Desktop.
3
3
  Expects `dist/<program.key>` as the compiled binary input.
4
4
  */
5
5
 
6
- import {
7
- cpSync,
8
- existsSync,
9
- mkdirSync,
10
- mkdtempSync,
11
- readFileSync,
12
- rmSync,
13
- writeFileSync,
14
- } from "node:fs";
6
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
15
7
  import { tmpdir } from "node:os";
16
8
  import { basename, join, resolve } from "node:path";
17
9
  import { buildProgramUserConfig } from "../config/manifest.ts";
@@ -56,10 +48,7 @@ function defaultAuthor(bundle?: CliMcpBundleConfig): {
56
48
  }
57
49
 
58
50
  /** Generates MCPB `manifest.json` object from program schema and MCP tools. */
59
- export function generateMcpManifest(
60
- program: CliProgram,
61
- binaryName: string,
62
- ): Record<string, unknown> {
51
+ export function generateMcpManifest(program: CliProgram, binaryName: string): Record<string, unknown> {
63
52
  const bundle = program.mcpServer?.bundle;
64
53
  const tools = collectMcpTools(program).map((t) => ({
65
54
  name: t.name,
@@ -123,9 +112,7 @@ export function packMcpBundle(program: CliProgram, opts: PackMcpBundleOpts = {})
123
112
  const binaryName = basename(binaryPath);
124
113
 
125
114
  if (!existsSync(binaryPath)) {
126
- throw new Error(
127
- `Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`,
128
- );
115
+ throw new Error(`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`);
129
116
  }
130
117
 
131
118
  const staging = mkdtempSync(join(tmpdir(), "mcpb-"));
@@ -164,9 +151,7 @@ export function packMcpBundle(program: CliProgram, opts: PackMcpBundleOpts = {})
164
151
  export function runMcpBundle(program: CliProgram): void {
165
152
  const mcp = program.mcpServer;
166
153
  if (!mcp?.mcpd && !mcp?.claudePlugin) {
167
- throw new Error(
168
- "mcp bundle: enable mcpServer.mcpd and/or mcpServer.claudePlugin on the program root.",
169
- );
154
+ throw new Error("mcp bundle: enable mcpServer.mcpd and/or mcpServer.claudePlugin on the program root.");
170
155
  }
171
156
 
172
157
  const lines: string[] = [];
@@ -87,9 +87,9 @@ describe("claude plugin", () => {
87
87
  execSync("unzip -o -q ../out.zip", { cwd: extract });
88
88
  const binMode = statSync(join(extract, "bin", "myapp")).mode & 0o777;
89
89
  expect(binMode & 0o111).not.toBe(0);
90
- const pluginJson = JSON.parse(
91
- readFileSync(join(extract, ".claude-plugin", "plugin.json"), "utf8"),
92
- ) as { mcpServers: string };
90
+ const pluginJson = JSON.parse(readFileSync(join(extract, ".claude-plugin", "plugin.json"), "utf8")) as {
91
+ mcpServers: string;
92
+ };
93
93
  expect(pluginJson.mcpServers).toBe(".mcp.json");
94
94
  } finally {
95
95
  rmSync(work, { recursive: true, force: true });
package/src/mcp/claude.ts CHANGED
@@ -58,10 +58,7 @@ export function defaultClaudePluginPaths(program: CliProgram, cwd = process.cwd(
58
58
  }
59
59
 
60
60
  /** Generates `.claude-plugin/plugin.json` object. */
61
- export function generatePluginManifest(
62
- program: CliProgram,
63
- _binaryName: string,
64
- ): Record<string, unknown> {
61
+ export function generatePluginManifest(program: CliProgram, _binaryName: string): Record<string, unknown> {
65
62
  const bundle = program.mcpServer?.bundle;
66
63
  const manifest: Record<string, unknown> = {
67
64
  name: pluginName(program),
@@ -78,10 +75,7 @@ export function generatePluginManifest(
78
75
  }
79
76
 
80
77
  /** Generates plugin `.mcp.json` stdio server config. */
81
- export function generatePluginMcpJson(
82
- program: CliProgram,
83
- binaryName: string,
84
- ): Record<string, unknown> {
78
+ export function generatePluginMcpJson(program: CliProgram, binaryName: string): Record<string, unknown> {
85
79
  const mcp: Record<string, unknown> = {
86
80
  command: `\${CLAUDE_PLUGIN_ROOT}/bin/${binaryName}`,
87
81
  args: ["mcp"],
@@ -117,12 +111,7 @@ function collectZipEntries(rootDir: string, dir = rootDir): ZipFileEntry[] {
117
111
  return entries;
118
112
  }
119
113
 
120
- function writePluginTree(
121
- pluginRoot: string,
122
- program: CliProgram,
123
- binaryPath: string,
124
- binaryName: string,
125
- ): void {
114
+ function writePluginTree(pluginRoot: string, program: CliProgram, binaryPath: string, binaryName: string): void {
126
115
  const bundle = generatePluginSkillBundle(program);
127
116
  const skillMd = applyPluginSkillHint(program, bundle.skillMd);
128
117
 
@@ -155,9 +144,7 @@ export function packClaudePlugin(program: CliProgram, opts: PackMcpBundleOpts =
155
144
  const binaryName = basename(binaryPath);
156
145
 
157
146
  if (!existsSync(binaryPath)) {
158
- throw new Error(
159
- `Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`,
160
- );
147
+ throw new Error(`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`);
161
148
  }
162
149
 
163
150
  const staging = mkdtempSync(join(tmpdir(), "claude-plugin-"));
package/src/mcp/env.ts CHANGED
@@ -51,9 +51,7 @@ export function bootstrapMcpEnv(config: { shellEnv?: boolean | string }): void {
51
51
  : (process.env.SHELL ?? (process.platform === "darwin" ? "/bin/zsh" : "/bin/bash"));
52
52
  const captured = captureShellEnv(shell);
53
53
  if (Object.keys(captured).length === 0) {
54
- process.stderr.write(
55
- `[argsbarg] shellEnv: failed to capture shell environment from ${shell}\n`,
56
- );
54
+ process.stderr.write(`[argsbarg] shellEnv: failed to capture shell environment from ${shell}\n`);
57
55
  } else {
58
56
  applyShellEnv(captured);
59
57
  }
package/src/mcp/server.ts CHANGED
@@ -7,12 +7,7 @@ import type { Cli } from "../cli.ts";
7
7
  import { bootstrapAppConfig } from "../config/bootstrap.ts";
8
8
  import { formatMcpMissingConfigMessage, missingRequiredConfig } from "../config/resolve.ts";
9
9
  import { buildToolCallSuccess } from "./result.ts";
10
- import {
11
- allMcpResources,
12
- collectMcpTools,
13
- mcpToolCallToArgv,
14
- resolveMcpServerInfo,
15
- } from "./tools.ts";
10
+ import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpServerInfo } from "./tools.ts";
16
11
 
17
12
  const MCP_PROTOCOL_VERSION = "2024-11-05";
18
13
 
@@ -110,10 +105,7 @@ async function handleRequestLine(cli: Cli, line: string): Promise<void> {
110
105
  return;
111
106
  }
112
107
  const rawArgs = params.arguments;
113
- if (
114
- rawArgs !== undefined &&
115
- (typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs))
116
- ) {
108
+ if (rawArgs !== undefined && (typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs))) {
117
109
  writeError(id, -32602, "Invalid params: arguments must be an object");
118
110
  return;
119
111
  }
@@ -162,8 +154,7 @@ async function handleRequestLine(cli: Cli, line: string): Promise<void> {
162
154
  });
163
155
  return;
164
156
  }
165
- const errText =
166
- invokeResult.stderr.trim() || invokeResult.errorMsg || `Exit code ${invokeResult.exitCode}`;
157
+ const errText = invokeResult.stderr.trim() || invokeResult.errorMsg || `Exit code ${invokeResult.exitCode}`;
167
158
  writeResponse({
168
159
  jsonrpc: "2.0",
169
160
  id,
package/src/mcp/tools.ts CHANGED
@@ -137,11 +137,7 @@ function positionalProperty(p: CliPositional): Record<string, unknown> {
137
137
  }
138
138
 
139
139
  /** Builds inputSchema for a leaf command. */
140
- function buildInputSchema(
141
- root: CliProgram,
142
- path: string[],
143
- leaf: CliLeaf,
144
- ): Record<string, unknown> {
140
+ function buildInputSchema(root: CliProgram, path: string[], leaf: CliLeaf): Record<string, unknown> {
145
141
  const properties: Record<string, unknown> = {};
146
142
  const required: string[] = [];
147
143
 
@@ -222,12 +218,7 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
222
218
  /** Walks the command tree and appends leaf tools. */
223
219
  function walk(cmd: CliNode, path: string[]): void {
224
220
  if (isCliLeaf(cmd)) {
225
- if (
226
- cmd.key === "completion" ||
227
- cmd.key === "configure" ||
228
- cmd.key === "mcp" ||
229
- cmd.key === "version"
230
- ) {
221
+ if (cmd.key === "completion" || cmd.key === "configure" || cmd.key === "mcp" || cmd.key === "version") {
231
222
  return;
232
223
  }
233
224
  if (cmd.hidden || cmd.mcpTool?.enabled === false) {
@@ -7,12 +7,7 @@ import { join } from "node:path";
7
7
  import { $ } from "bun";
8
8
  import type { CliProgram } from "./index.ts";
9
9
  import { buildToolCallSuccess } from "./mcp/result.ts";
10
- import {
11
- collectMcpTools,
12
- mcpToolCallToArgv,
13
- mcpToolDescription,
14
- sanitizeToolSegment,
15
- } from "./mcp/tools.ts";
10
+ import { collectMcpTools, mcpToolCallToArgv, mcpToolDescription, sanitizeToolSegment } from "./mcp/tools.ts";
16
11
  import { cliSchemaExport } from "./schema.ts";
17
12
  import { mcpRequest, nestedMcpFixture, testProgram } from "./test-fixtures.ts";
18
13
  import { cliValidateProgram } from "./validate.ts";
@@ -489,10 +484,9 @@ test("minimal.ts mcp without opt-in fails", async () => {
489
484
  });
490
485
 
491
486
  test("MCP resources/list includes custom resource", async () => {
492
- const responses = await mcpRequest(
493
- [{ jsonrpc: "2.0", id: 10, method: "resources/list", params: {} }],
494
- { script: "examples/mcp-test.ts" },
495
- );
487
+ const responses = await mcpRequest([{ jsonrpc: "2.0", id: 10, method: "resources/list", params: {} }], {
488
+ script: "examples/mcp-test.ts",
489
+ });
496
490
  const res = responses.get(10) as { result: { resources: { uri: string }[] } };
497
491
  const uris = res.result.resources.map((r) => r.uri);
498
492
  expect(uris).toContain("mcp_test://schema");
package/src/parse.test.ts CHANGED
@@ -12,12 +12,7 @@ import { cliPresentationRoot } from "./builtins/presentation.ts";
12
12
  import { cliHelpRender } from "./help.ts";
13
13
  import { Cli, CliFallbackMode, CliOptionKind, type CliProgram } from "./index.ts";
14
14
  import { applyShellEnv } from "./mcp/env.ts";
15
- import {
16
- allMcpResources,
17
- collectMcpTools,
18
- mcpToolCallToArgv,
19
- resolveMcpSchemaUri,
20
- } from "./mcp/tools.ts";
15
+ import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpSchemaUri } from "./mcp/tools.ts";
21
16
  import { ParseKind, parse, postParseValidate } from "./parse.ts";
22
17
  import { cliSchemaJson } from "./schema.ts";
23
18
  import { generatePluginSkillBundle, generateSkillBundle } from "./skill/generate.ts";
@@ -338,10 +333,7 @@ test("trailing options include parent-scoped flags", () => {
338
333
  ],
339
334
  });
340
335
  cliValidateProgram(root);
341
- const pr = postParseValidate(
342
- root,
343
- parse(root, ["group", "leaf", "-u", "alice", "./file", "--json"]),
344
- );
336
+ const pr = postParseValidate(root, parse(root, ["group", "leaf", "-u", "alice", "./file", "--json"]));
345
337
  expect(pr.kind).toBe(ParseKind.Ok);
346
338
  expect(pr.path).toEqual(["group", "leaf"]);
347
339
  expect(pr.args).toEqual(["./file"]);
@@ -415,10 +407,7 @@ test("stops parsing options at --", () => {
415
407
  ],
416
408
  });
417
409
  cliValidateProgram(root);
418
- const pr = postParseValidate(
419
- root,
420
- parse(root, ["x", "--name", "pat", "--", "--name", "bob", "-x"]),
421
- );
410
+ const pr = postParseValidate(root, parse(root, ["x", "--name", "pat", "--", "--name", "bob", "-x"]));
422
411
  expect(pr.kind).toBe(ParseKind.Ok);
423
412
  expect(pr.opts.name).toBe("pat");
424
413
  expect(pr.args).toEqual(["--name", "bob", "-x"]);
@@ -505,9 +494,7 @@ test("presence option cannot be required", () => {
505
494
  test("leaf completion help prints correctly", async () => {
506
495
  // Test the fix where `completion zsh -h` on a leaf root was incorrectly ignored.
507
496
  // We run this as a subprocess so we don't accidentally exit the test runner.
508
- const { stdout, stderr, exitCode } = await $`bun run examples/minimal.ts completion zsh -h`
509
- .nothrow()
510
- .quiet();
497
+ const { stdout, stderr, exitCode } = await $`bun run examples/minimal.ts completion zsh -h`.nothrow().quiet();
511
498
  const out = stdout.toString();
512
499
  expect(exitCode).toBe(0);
513
500
  expect(out).toContain("Show help for this command.");
@@ -517,9 +504,7 @@ test("leaf completion help prints correctly", async () => {
517
504
 
518
505
  /** Docs schema exports JSON for nested CLIs. */
519
506
  test("docs schema exports JSON for nested CLIs", async () => {
520
- const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema`
521
- .nothrow()
522
- .quiet();
507
+ const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema`.nothrow().quiet();
523
508
  expect(exitCode).toBe(0);
524
509
  expect(stderr.toString()).toBe("");
525
510
 
@@ -543,11 +528,7 @@ test("docs schema exports JSON for leaf roots", async () => {
543
528
  expect(schema.key).toBe("minimal.ts");
544
529
  expect(schema.positionals[0].name).toBe("name");
545
530
  expect(schema.options[0].name).toBe("verbose");
546
- expect(schema.commands.map((c: { key: string }) => c.key)).toEqual([
547
- "version",
548
- "configure",
549
- "docs",
550
- ]);
531
+ expect(schema.commands.map((c: { key: string }) => c.key)).toEqual(["version", "configure", "docs"]);
551
532
  });
552
533
 
553
534
  test("version builtin prints program version", async () => {
@@ -956,9 +937,7 @@ test("Enum completions list choices in bash script", () => {
956
937
  {
957
938
  key: "run",
958
939
  description: "",
959
- options: [
960
- { name: "mode", description: "m", kind: CliOptionKind.Enum, choices: ["dev", "prod"] },
961
- ],
940
+ options: [{ name: "mode", description: "m", kind: CliOptionKind.Enum, choices: ["dev", "prod"] }],
962
941
  handler: () => {},
963
942
  },
964
943
  ],
@@ -1047,9 +1026,7 @@ test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
1047
1026
  },
1048
1027
  ],
1049
1028
  });
1050
- expect(() => cliValidateProgram(root)).toThrow(
1051
- /fallbackCommand 'missing' is not a child of 'docs'/,
1052
- );
1029
+ expect(() => cliValidateProgram(root)).toThrow(/fallbackCommand 'missing' is not a child of 'docs'/);
1053
1030
  });
1054
1031
 
1055
1032
  test("cliValidateProgram accepts nested fallbackCommand when child exists", () => {
@@ -1279,9 +1256,7 @@ test("cliSkillInstall claude target uses .claude/skills", () => {
1279
1256
  try {
1280
1257
  const files = cliSkillInstall(nestedMcpFixture, "claude", { rimraf: true });
1281
1258
  expect(files.some((f) => f.includes(".claude/skills/nested_ts/"))).toBe(true);
1282
- expect(readFileSync(join(cwd, ".claude", "skills", "nested_ts", "SKILL.md"), "utf8")).toContain(
1283
- "Claude Code",
1284
- );
1259
+ expect(readFileSync(join(cwd, ".claude", "skills", "nested_ts", "SKILL.md"), "utf8")).toContain("Claude Code");
1285
1260
  } finally {
1286
1261
  process.chdir(prev);
1287
1262
  rmSync(cwd, { recursive: true, force: true });