argsbarg 2.1.0 → 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/src/index.ts CHANGED
File without changes
@@ -1,4 +1,5 @@
1
1
  import { readSync } from "node:fs";
2
+ import { resolveCapabilities } from "../capabilities.ts";
2
3
  import { CliProgram } from "../types.ts";
3
4
  import { cliSkillInstall } from "../skill/install.ts";
4
5
  import { checkMcpConflict, expectedMcpEntry } from "./mcp-config.ts";
@@ -130,7 +131,7 @@ export async function cliInstall(root: CliProgram, rawOpts: Record<string, strin
130
131
  }
131
132
 
132
133
  // MCP conflict checks before planning
133
- if (!opts.uninstall && root.mcpServer && (opts.all || opts.mcp)) {
134
+ if (!opts.uninstall && resolveCapabilities(root).mcp && (opts.all || opts.mcp)) {
134
135
  const entry = expectedMcpEntry(root);
135
136
  const yes = !!opts.yes;
136
137
  for (const p of [paths.cursorMcpPath, paths.claudeMcpPath]) {
@@ -11,8 +11,9 @@ import { parseInstallOpts } from "./index.ts";
11
11
 
12
12
  const fixture: CliProgram = {
13
13
  key: "testapp",
14
+ version: "0.0.0",
14
15
  description: "Test",
15
- mcpServer: { name: "testapp" },
16
+ mcpServer: { enabled: true },
16
17
  handler: () => {},
17
18
  };
18
19
 
@@ -1,7 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join } from "node:path";
3
3
  import { CliProgram } from "../types.ts";
4
- import { sanitizeToolSegment } from "../mcp/tools.ts";
4
+ import { sanitizeToolSegment, mcpServerId } from "../mcp/tools.ts";
5
5
 
6
6
  export interface InstallPaths {
7
7
  bindir: string;
@@ -63,7 +63,7 @@ export function resolveInstallPaths(root: CliProgram, opts: { prefix?: string })
63
63
  claudeMcpPath: join(home, ".claude.json"),
64
64
  bashRc: join(home, ".bashrc"),
65
65
  zshRc: join(home, ".zshrc"),
66
- mcpName: root.mcpServer?.name ?? root.key,
66
+ mcpName: mcpServerId(root),
67
67
  skillDirName,
68
68
  };
69
69
  }
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { resolveCapabilities } from "../capabilities.ts";
3
4
  import { CliProgram } from "../types.ts";
4
5
  import { installBinary } from "./binary.ts";
5
6
  import { installCompletions } from "./completions.ts";
@@ -46,7 +47,7 @@ function wantsSkill(opts: InstallOpts): boolean {
46
47
  }
47
48
 
48
49
  function wantsMcp(opts: InstallOpts, root: CliProgram): boolean {
49
- return !!(opts.all || opts.mcp) && root.mcpServer !== undefined;
50
+ return !!(opts.mcp || opts.all) && resolveCapabilities(root).mcp;
50
51
  }
51
52
 
52
53
  /** Builds install actions for normal mode (--all / scoped targets). */
@@ -154,7 +155,7 @@ export function buildUpdatePlan(root: CliProgram, paths: InstallPaths, opts: Ins
154
155
  bin: true,
155
156
  completions: detected.bashCompletion || detected.zshCompletion || detected.fishCompletion,
156
157
  skill: detected.cursorSkill || detected.claudeSkill,
157
- mcp: (detected.cursorMcp || detected.claudeMcp) && root.mcpServer !== undefined,
158
+ mcp: (detected.cursorMcp || detected.claudeMcp) && resolveCapabilities(root).mcp,
158
159
  dry: opts.dry,
159
160
  };
160
161
  const plan = buildInstallPlan(root, paths, scoped);
@@ -1,5 +1,6 @@
1
1
  import { existsSync, rmSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { resolveCapabilities } from "../capabilities.ts";
3
4
  import { CliProgram } from "../types.ts";
4
5
  import { uninstallBinary } from "./binary.ts";
5
6
  import { uninstallCompletions } from "./completions.ts";
@@ -77,7 +78,7 @@ export function buildUninstallPlan(
77
78
  });
78
79
  }
79
80
 
80
- if ((all || opts.mcp) && root.mcpServer !== undefined) {
81
+ if ((all || opts.mcp) && resolveCapabilities(root).mcp) {
81
82
  if (detected.cursorMcp) {
82
83
  actions.push({
83
84
  summary: `cursor mcp: ${paths.cursorMcpPath}`,
package/src/invoke.ts CHANGED
@@ -82,7 +82,7 @@ export async function cliInvoke(root: CliProgram, argv: string[]): Promise<CliIn
82
82
  };
83
83
  }
84
84
 
85
- let current: CliProgram = root;
85
+ let current: CliNode = root;
86
86
  for (const seg of pr.path) {
87
87
  if (!isCliRouter(current)) {
88
88
  return {
package/src/mcp/tools.ts CHANGED
@@ -3,14 +3,24 @@ This module maps CliProgram leaf nodes to MCP tool definitions and converts
3
3
  flat JSON tool arguments into argv for cliInvoke.
4
4
  */
5
5
 
6
- import { readFileSync } from "node:fs";
7
- import { join } from "node:path";
8
6
  import { collectOptionDefs } from "../parse.ts";
9
7
  import { cliSchemaJson } from "../schema.ts";
10
8
  import { CliProgram, CliLeaf, CliNode, CliOption, CliOptionKind, CliPositional, isCliLeaf, isCliRouter } from "../types.ts";
11
9
 
12
- /** Default URI for the CLI schema MCP resource. */
13
- export const MCP_SCHEMA_URI_DEFAULT = "argsbarg://schema";
10
+ /** Default URI pattern for the CLI schema MCP resource (`<mcpId>://schema`). */
11
+ export function defaultMcpSchemaUri(mcpId: string): string {
12
+ return `${mcpId}://schema`;
13
+ }
14
+
15
+ /** Sanitizes a command key segment for MCP tool names and server identity. */
16
+ export function sanitizeToolSegment(key: string): string {
17
+ return key.replace(/[^a-zA-Z0-9]/g, "_");
18
+ }
19
+
20
+ /** MCP server id derived from the program root key (sanitized). */
21
+ export function mcpServerId(root: CliProgram): string {
22
+ return sanitizeToolSegment(root.key);
23
+ }
14
24
 
15
25
  /** One MCP tool derived from a leaf CLI command. */
16
26
  export interface McpToolDef {
@@ -32,11 +42,6 @@ export function mcpToolDescription(path: string[], rootKey: string, description:
32
42
  return `${prefix} — ${description}`;
33
43
  }
34
44
 
35
- /** Sanitizes a command key segment for MCP tool names. */
36
- export function sanitizeToolSegment(key: string): string {
37
- return key.replace(/[^a-zA-Z0-9]/g, "_");
38
- }
39
-
40
45
  /** Builds the MCP tool name for a leaf at the given path. */
41
46
  export function mcpToolName(root: CliProgram, path: string[]): string {
42
47
  if (path.length === 0) {
@@ -150,7 +155,7 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
150
155
  /** Walks the command tree and appends leaf tools. */
151
156
  function walk(cmd: CliNode, path: string[]): void {
152
157
  if (isCliLeaf(cmd)) {
153
- if (cmd.key === "completion" || cmd.key === "install" || cmd.key === "mcp") {
158
+ if (cmd.key === "completion" || cmd.key === "install" || cmd.key === "mcp" || cmd.key === "version") {
154
159
  return;
155
160
  }
156
161
  if (cmd.mcpTool?.enabled === false) {
@@ -181,28 +186,20 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
181
186
  return out;
182
187
  }
183
188
 
184
- /** Reads package.json version from cwd synchronously. */
185
- function resolveMcpVersionFromPackageJson(): string | undefined {
186
- try {
187
- const text = readFileSync(join(process.cwd(), "package.json"), "utf8");
188
- const version = (JSON.parse(text) as { version?: string }).version;
189
- return typeof version === "string" ? version : undefined;
190
- } catch {
191
- return undefined;
192
- }
193
- }
194
-
195
189
  /** Resolves MCP server name and version for initialize. */
196
190
  export function resolveMcpServerInfo(root: CliProgram): { name: string; version: string } {
197
191
  return {
198
- name: root.mcpServer?.name ?? root.key,
199
- version: root.mcpServer?.version ?? resolveMcpVersionFromPackageJson() ?? "0.0.0",
192
+ name: mcpServerId(root),
193
+ version: root.version,
200
194
  };
201
195
  }
202
196
 
203
197
  /** Resolves the schema resource URI for this app. */
204
198
  export function resolveMcpSchemaUri(root: CliProgram): string {
205
- return root.mcpServer?.schemaResourceUri ?? MCP_SCHEMA_URI_DEFAULT;
199
+ if (root.mcpServer?.schemaResourceUri) {
200
+ return root.mcpServer.schemaResourceUri;
201
+ }
202
+ return defaultMcpSchemaUri(mcpServerId(root));
206
203
  }
207
204
 
208
205
  /** Converts flat MCP tool arguments to argv for cliInvoke. */
package/src/runtime.ts CHANGED
@@ -7,7 +7,6 @@ import { builtinInterceptRoot, dispatchBuiltin } from "./builtins/dispatch.ts";
7
7
  import { cliPresentationRoot } from "./builtins/presentation.ts";
8
8
  import type { CliRouter } from "./types.ts";
9
9
  import { type CliNode, type CliProgram, isCliLeaf, isCliRouter } from "./types.ts";
10
- import { isCompiledExecutable } from "./install/compiled.ts";
11
10
  import { CliContext } from "./context.ts";
12
11
  import { cliHelpRender } from "./help.ts";
13
12
  import { parse, postParseValidate, ParseKind } from "./parse.ts";
@@ -33,12 +32,12 @@ export async function cliRun(program: CliProgram, argv: string[] = process.argv.
33
32
  const caps = resolveCapabilities(program);
34
33
 
35
34
  if (argv.length >= 1 && argv[0] === "mcp" && !caps.mcp) {
36
- process.stderr.write("MCP is not enabled. Set mcpServer on the program root.\n");
35
+ process.stderr.write("MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n");
37
36
  process.exit(1);
38
37
  }
39
38
 
40
- if (argv.length >= 1 && argv[0] === "install" && !isCompiledExecutable()) {
41
- process.stderr.write("install is only available in compiled binaries (bun build --compile).\n");
39
+ if (argv.length >= 1 && argv[0] === "install" && !caps.install) {
40
+ process.stderr.write("install is disabled. Remove install.enabled: false from the program root.\n");
42
41
  process.exit(1);
43
42
  }
44
43
 
@@ -4,7 +4,7 @@ This module generates Agent Skills content (SKILL.md + reference.md) from a CLI
4
4
 
5
5
  import { collectOptionDefs } from "../parse.ts";
6
6
  import { cliSchemaJson } from "../schema.ts";
7
- import { collectMcpTools, sanitizeToolSegment } from "../mcp/tools.ts";
7
+ import { collectMcpTools, mcpServerId, sanitizeToolSegment } from "../mcp/tools.ts";
8
8
  import { CliProgram, CliOptionKind } from "../types.ts";
9
9
 
10
10
  export type SkillTarget = "cursor" | "claude";
@@ -75,7 +75,7 @@ function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): s
75
75
  "",
76
76
  ];
77
77
 
78
- if (root.mcpServer !== undefined) {
78
+ if (root.mcpServer?.enabled === true) {
79
79
  lines.push(
80
80
  "**Prefer MCP** when a host has the server connected:",
81
81
  "",
@@ -89,7 +89,7 @@ function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): s
89
89
  JSON.stringify(
90
90
  {
91
91
  mcpServers: {
92
- [root.mcpServer.name ?? root.key]: {
92
+ [mcpServerId(root)]: {
93
93
  command: root.key,
94
94
  args: ["mcp"],
95
95
  },
package/src/types.test.ts CHANGED
@@ -18,8 +18,9 @@ const _leafOnly: CliLeaf = {
18
18
 
19
19
  const _program: CliProgram = {
20
20
  key: "app",
21
+ version: "0.0.0",
21
22
  description: "",
22
- mcpServer: {},
23
+ mcpServer: { enabled: true },
23
24
  commands: [],
24
25
  };
25
26
 
@@ -27,7 +28,7 @@ const _badMcpOnNode = {
27
28
  key: "x",
28
29
  description: "",
29
30
  // @ts-expect-error mcpServer is program-root only
30
- mcpServer: {},
31
+ mcpServer: { enabled: true },
31
32
  commands: [],
32
33
  } satisfies CliNode;
33
34
 
package/src/types.ts CHANGED
@@ -89,13 +89,12 @@ export interface CliPositional {
89
89
 
90
90
  /**
91
91
  * Enables `myapp mcp` and MCP stdio server metadata (program root only).
92
+ * Must include `enabled: true`; omit `mcpServer` entirely to disable MCP.
92
93
  */
93
94
  export interface CliMcpServerConfig {
94
- /** `initialize` serverInfo.name (default: root `key`). */
95
- name?: string;
96
- /** `initialize` serverInfo.version (default: see resolveMcpVersion). */
97
- version?: string;
98
- /** Resource URI for schema export (default: `"argsbarg://schema"`). */
95
+ /** When `true`, enables the `mcp` built-in and MCP stdio server. */
96
+ enabled: boolean;
97
+ /** Resource URI for schema export (default: `<sanitized root key>://schema`). */
99
98
  schemaResourceUri?: string;
100
99
  /**
101
100
  * Capture the user's login shell environment at MCP server start and merge it
@@ -110,7 +109,7 @@ export interface CliMcpServerConfig {
110
109
  */
111
110
  envFile?: string;
112
111
  /**
113
- * Custom MCP resources exposed alongside the built-in argsbarg://schema resource.
112
+ * Custom MCP resources exposed alongside the built-in schema resource.
114
113
  * URIs must be unique and must not equal schemaResourceUri.
115
114
  */
116
115
  resources?: CliMcpResource[];
@@ -152,7 +151,7 @@ export interface CliMcpToolConfig {
152
151
  }
153
152
 
154
153
  /**
155
- * Opt-out and defaults for the `install` built-in (compiled binaries only; program root only).
154
+ * Opt-out and defaults for the `install` built-in (program root only).
156
155
  */
157
156
  export interface CliInstallConfig {
158
157
  /** When `false`, hide/disable `install` (default: enabled). */
@@ -209,9 +208,11 @@ export type CliNode = CliLeaf | CliRouter;
209
208
  * May be a leaf or router, plus optional program-level MCP and install config.
210
209
  */
211
210
  export type CliProgram = CliNode & {
212
- /** When set, enables the `mcp` built-in subcommand. */
211
+ /** Program version (printed by the `version` built-in and MCP serverInfo). */
212
+ version: string;
213
+ /** When set with `enabled: true`, enables the `mcp` built-in subcommand. */
213
214
  mcpServer?: CliMcpServerConfig;
214
- /** Opt-out and defaults for `install` (compiled binaries only). */
215
+ /** Opt-out and defaults for `install`. */
215
216
  install?: CliInstallConfig;
216
217
  };
217
218
 
package/src/validate.ts CHANGED
@@ -12,10 +12,20 @@ import {
12
12
  isCliLeaf,
13
13
  isCliRouter,
14
14
  } from "./types.ts";
15
- import { MCP_SCHEMA_URI_DEFAULT } from "./mcp/tools.ts";
15
+ import { resolveMcpSchemaUri } from "./mcp/tools.ts";
16
16
 
17
17
  /** Validates a program schema. */
18
18
  export function cliValidateProgram(program: CliProgram): void {
19
+ if (!program.version || program.version.trim().length === 0) {
20
+ throw new CliSchemaValidationError("CliProgram.version is required");
21
+ }
22
+
23
+ if (program.mcpServer !== undefined && program.mcpServer.enabled !== true) {
24
+ throw new CliSchemaValidationError(
25
+ "mcpServer requires enabled: true; omit mcpServer to disable MCP",
26
+ );
27
+ }
28
+
19
29
  const caps = resolveCapabilities(program);
20
30
  const reserved = reservedCommandNames(caps);
21
31
 
@@ -58,8 +68,8 @@ function walkNode(node: CliNode, program: CliProgram, isRoot: boolean): void {
58
68
  }
59
69
  }
60
70
 
61
- if (isRoot && program.mcpServer?.resources) {
62
- const schemaUri = program.mcpServer.schemaResourceUri ?? MCP_SCHEMA_URI_DEFAULT;
71
+ if (isRoot && program.mcpServer?.enabled === true && program.mcpServer.resources) {
72
+ const schemaUri = resolveMcpSchemaUri(program);
63
73
  const uris = program.mcpServer.resources.map((r) => r.uri);
64
74
  if (uris.includes(schemaUri)) {
65
75
  throw new CliSchemaValidationError(
@@ -1,15 +0,0 @@
1
- /** Test override: when set, `isCompiledExecutable()` returns this value instead of checking Bun.embeddedFiles. */
2
- let compiledOverride: boolean | null = null;
3
-
4
- /** @internal For tests only. */
5
- export function setCompiledExecutableOverride(value: boolean | null): void {
6
- compiledOverride = value;
7
- }
8
-
9
- /** True when running as a `bun build --compile` binary (embedded files present). */
10
- export function isCompiledExecutable(): boolean {
11
- if (compiledOverride !== null) {
12
- return compiledOverride;
13
- }
14
- return (Bun.embeddedFiles?.length ?? 0) > 0;
15
- }