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
@@ -0,0 +1,123 @@
1
+ import { CliCommand } from "../types.ts";
2
+ import { completionBashScript } from "./completion-bash.ts";
3
+ import { completionFishScript } from "./completion-fish.ts";
4
+ import { completionZshScript } from "./completion-zsh.ts";
5
+ import { cliBuiltinInstallCommand } from "./install.ts";
6
+ import { cliBuiltinMcpCommand } from "./mcp.ts";
7
+ import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
8
+ import { cliMcpServeStdio } from "../mcp.ts";
9
+ import { cliInstall } from "../install/index.ts";
10
+ import { isCompiledExecutable } from "../install/compiled.ts";
11
+ import type { ParseResult } from "../parse.ts";
12
+ import { ParseKind } from "../parse.ts";
13
+
14
+ export interface DispatchBuiltinOpts {
15
+ isLeafCompletionIntercept: boolean;
16
+ parseRoot: CliCommand;
17
+ }
18
+
19
+ /**
20
+ * Handles built-in commands after parse.
21
+ */
22
+ export async function dispatchBuiltin(
23
+ root: CliCommand,
24
+ pr: ParseResult,
25
+ opts: DispatchBuiltinOpts,
26
+ ): Promise<void> {
27
+ if (pr.kind !== ParseKind.Ok) {
28
+ return;
29
+ }
30
+ if (pr.path[0] === "completion") {
31
+ const schemaForCompletion = opts.isLeafCompletionIntercept ? root : opts.parseRoot;
32
+ if (pr.path[1] === "bash") {
33
+ process.stdout.write(completionBashScript(schemaForCompletion));
34
+ process.exit(0);
35
+ }
36
+ if (pr.path[1] === "zsh") {
37
+ process.stdout.write(completionZshScript(schemaForCompletion));
38
+ process.exit(0);
39
+ }
40
+ if (pr.path[1] === "fish") {
41
+ process.stdout.write(completionFishScript(schemaForCompletion));
42
+ process.exit(0);
43
+ }
44
+ return;
45
+ }
46
+
47
+ if (pr.path[0] === "mcp") {
48
+ if (!root.mcpServer) {
49
+ process.stderr.write("MCP is not enabled. Set mcpServer on the program root.\n");
50
+ process.exit(1);
51
+ }
52
+ if (pr.path.length !== 1) {
53
+ process.stderr.write("Unknown subcommand: mcp " + pr.path.slice(1).join(" ") + "\n");
54
+ process.exit(1);
55
+ }
56
+ await cliMcpServeStdio(root);
57
+ process.exit(0);
58
+ }
59
+
60
+ if (pr.path[0] === "install") {
61
+ if (!isCompiledExecutable()) {
62
+ process.stderr.write(
63
+ "install is only available in compiled binaries (bun build --compile).\n",
64
+ );
65
+ process.exit(1);
66
+ }
67
+ if (root.install?.enabled === false) {
68
+ process.stderr.write("install is disabled. Remove install.enabled: false from the program root.\n");
69
+ process.exit(1);
70
+ }
71
+ if (pr.path.length !== 1) {
72
+ process.stderr.write("Unknown subcommand: install " + pr.path.slice(1).join(" ") + "\n");
73
+ process.exit(1);
74
+ }
75
+ await cliInstall(root, pr.opts);
76
+ }
77
+ }
78
+
79
+ /** Built-in intercept roots for leaf programs. */
80
+ export function builtinInterceptRoot(
81
+ root: CliCommand,
82
+ argv: string[],
83
+ ): { parseRoot: CliCommand; isLeafCompletionIntercept: boolean } {
84
+ if (!root.handler || argv.length < 1) {
85
+ return { parseRoot: root, isLeafCompletionIntercept: false };
86
+ }
87
+
88
+ const first = argv[0];
89
+ if (first === "completion") {
90
+ return {
91
+ parseRoot: {
92
+ key: root.key,
93
+ description: root.description,
94
+ commands: [completionGroup(root.key)],
95
+ } as CliCommand,
96
+ isLeafCompletionIntercept: true,
97
+ };
98
+ }
99
+
100
+ if (first === "install" && isCompiledExecutable() && root.install?.enabled !== false) {
101
+ return {
102
+ parseRoot: {
103
+ key: root.key,
104
+ description: root.description,
105
+ commands: [cliBuiltinInstallCommand(root)],
106
+ } as CliCommand,
107
+ isLeafCompletionIntercept: false,
108
+ };
109
+ }
110
+
111
+ if (first === "mcp" && root.mcpServer !== undefined) {
112
+ return {
113
+ parseRoot: {
114
+ key: root.key,
115
+ description: root.description,
116
+ commands: [cliBuiltinMcpCommand()],
117
+ } as CliCommand,
118
+ isLeafCompletionIntercept: false,
119
+ };
120
+ }
121
+
122
+ return { parseRoot: root, isLeafCompletionIntercept: false };
123
+ }
@@ -0,0 +1,46 @@
1
+ import { CliCommand, CliFallbackMode, CliOption, CliPositional } from "../types.ts";
2
+ import { cliBuiltinCompletionGroup } from "./completion-group.ts";
3
+ import { cliBuiltinInstallCommand } from "./install.ts";
4
+ import { cliBuiltinMcpCommand } from "./mcp.ts";
5
+ import { isCompiledExecutable } from "../install/compiled.ts";
6
+
7
+ /** JSON-safe command node (no handlers). */
8
+ export interface CliSchemaExport {
9
+ key: string;
10
+ description: string;
11
+ notes?: string;
12
+ options?: CliOption[];
13
+ fallbackCommand?: string;
14
+ fallbackMode?: CliFallbackMode;
15
+ commands?: CliSchemaExport[];
16
+ positionals?: CliPositional[];
17
+ }
18
+
19
+ function exportBuiltinNode(cmd: CliCommand): CliSchemaExport {
20
+ const out: CliSchemaExport = {
21
+ key: cmd.key,
22
+ description: cmd.description,
23
+ };
24
+ if ((cmd.notes ?? "").length > 0) {
25
+ out.notes = cmd.notes;
26
+ }
27
+ if ((cmd.options ?? []).length > 0) {
28
+ out.options = cmd.options;
29
+ }
30
+ if ((cmd.commands ?? []).length > 0) {
31
+ out.commands = (cmd.commands ?? []).map((ch) => exportBuiltinNode(ch));
32
+ }
33
+ return out;
34
+ }
35
+
36
+ /** Built-in subtrees matching help visibility for `--schema` export. */
37
+ export function exportPresentationBuiltins(root: CliCommand): CliSchemaExport[] {
38
+ const builtins: CliSchemaExport[] = [exportBuiltinNode(cliBuiltinCompletionGroup(root.key))];
39
+ if (isCompiledExecutable() && root.install?.enabled !== false) {
40
+ builtins.push(exportBuiltinNode(cliBuiltinInstallCommand(root)));
41
+ }
42
+ if (root.mcpServer !== undefined) {
43
+ builtins.push(exportBuiltinNode(cliBuiltinMcpCommand()));
44
+ }
45
+ return builtins;
46
+ }
@@ -0,0 +1,10 @@
1
+ export { completionBashScript } from "./completion-bash.ts";
2
+ export { completionZshScript } from "./completion-zsh.ts";
3
+ export { completionFishScript } from "./completion-fish.ts";
4
+ export { cliBuiltinCompletionGroup } from "./completion-group.ts";
5
+ export { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
6
+ export { cliBuiltinMcpCommand } from "./mcp.ts";
7
+ export { cliPresentationRoot, presentationBuiltins } from "./presentation.ts";
8
+ export { exportPresentationBuiltins, type CliSchemaExport } from "./export.ts";
9
+ export { dispatchBuiltin, builtinInterceptRoot } from "./dispatch.ts";
10
+ export { collectScopes, type ScopeRec } from "./scopes.ts";
@@ -0,0 +1,99 @@
1
+ import { CliCommand, CliOption, CliOptionKind } from "../types.ts";
2
+
3
+ /** Install command options (dynamic: `--mcp` only when MCP is enabled). */
4
+ export function installBuiltinOptions(root: CliCommand): CliOption[] {
5
+ const opts: CliOption[] = [
6
+ {
7
+ name: "all",
8
+ description: "Install binary, completions, skills, and MCP config (when enabled).",
9
+ kind: CliOptionKind.Presence,
10
+ },
11
+ {
12
+ name: "bin",
13
+ description: "Copy this binary to your install directory (default ~/.local/bin).",
14
+ kind: CliOptionKind.Presence,
15
+ },
16
+ {
17
+ name: "completions",
18
+ description:
19
+ "Install bash, zsh, and fish tab-completion scripts (each skipped silently if that shell is not on PATH).",
20
+ kind: CliOptionKind.Presence,
21
+ },
22
+ {
23
+ name: "skill",
24
+ description: "Install Cursor and Claude Code skills when ~/.cursor or ~/.claude exists.",
25
+ kind: CliOptionKind.Presence,
26
+ },
27
+ {
28
+ name: "update",
29
+ description: "Update only artifacts already installed (always includes the binary).",
30
+ kind: CliOptionKind.Presence,
31
+ },
32
+ {
33
+ name: "status",
34
+ description: "Print what is currently installed (read-only).",
35
+ kind: CliOptionKind.Presence,
36
+ },
37
+ {
38
+ name: "uninstall",
39
+ description: "Remove installed artifacts (all detected, or limit with other flags).",
40
+ kind: CliOptionKind.Presence,
41
+ },
42
+ {
43
+ name: "prefix",
44
+ description: "Install directory for the binary (default ~/.local/bin; overrides INSTALL_PREFIX).",
45
+ kind: CliOptionKind.String,
46
+ },
47
+ {
48
+ name: "yes",
49
+ description: "Skip the confirmation prompt.",
50
+ kind: CliOptionKind.Presence,
51
+ },
52
+ {
53
+ name: "dry",
54
+ description: "Show what would change without writing files.",
55
+ kind: CliOptionKind.Presence,
56
+ },
57
+ {
58
+ name: "json",
59
+ description: "Print changed paths (install/update/uninstall) or status JSON on stdout.",
60
+ kind: CliOptionKind.Presence,
61
+ },
62
+ {
63
+ name: "quiet",
64
+ description: "Suppress informational output (requires --yes).",
65
+ kind: CliOptionKind.Presence,
66
+ },
67
+ ];
68
+
69
+ if (root.mcpServer !== undefined) {
70
+ opts.splice(4, 0, {
71
+ name: "mcp",
72
+ description: "Add or update MCP server entries in Cursor and Claude config files.",
73
+ kind: CliOptionKind.Presence,
74
+ });
75
+ }
76
+
77
+ return opts;
78
+ }
79
+
80
+ /** Builds the `install` built-in command (compiled binaries only). */
81
+ export function cliBuiltinInstallCommand(root: CliCommand): CliCommand {
82
+ return {
83
+ key: "install",
84
+ description: "Install the binary, shell completions, agent skills, and MCP config to your user environment.",
85
+ notes:
86
+ "Requires a compiled binary (bun build --compile).\n\n" +
87
+ "First-time setup:\n" +
88
+ ` {app} install --all --yes\n\n` +
89
+ "Refresh after upgrading:\n" +
90
+ ` {app} install --update\n\n` +
91
+ "See what is installed:\n" +
92
+ ` {app} install --status\n\n` +
93
+ "Remove everything:\n" +
94
+ ` {app} install --uninstall --yes\n\n` +
95
+ "Use --dry to preview changes, --json for machine-readable output.",
96
+ options: installBuiltinOptions(root),
97
+ handler: () => {},
98
+ };
99
+ }
@@ -0,0 +1,13 @@
1
+ import { CliCommand, CliOption, CliOptionKind } from "../types.ts";
2
+
3
+ /** Presence options for the top-level `mcp` built-in (leaf). */
4
+ export function cliBuiltinMcpCommand(): CliCommand {
5
+ return {
6
+ key: "mcp",
7
+ description: "Run as an MCP server over stdio for AI agents.",
8
+ notes:
9
+ "Configure MCP clients with `command` set to this program name and `args` set to `[\"mcp\"]`.\n\n" +
10
+ "See docs/mcp.md for setup details.",
11
+ handler: () => {},
12
+ };
13
+ }
@@ -0,0 +1,39 @@
1
+ import { CliCommand } from "../types.ts";
2
+ import { isCompiledExecutable } from "../install/compiled.ts";
3
+ import { cliBuiltinCompletionGroup } from "./completion-group.ts";
4
+ import { cliBuiltinInstallCommand } from "./install.ts";
5
+ import { cliBuiltinMcpCommand } from "./mcp.ts";
6
+
7
+ /** Built-in commands shown in help and merged for routing CLIs. */
8
+ export function presentationBuiltins(root: CliCommand): CliCommand[] {
9
+ const builtins: CliCommand[] = [cliBuiltinCompletionGroup(root.key)];
10
+ if (isCompiledExecutable() && root.install?.enabled !== false) {
11
+ builtins.push(cliBuiltinInstallCommand(root));
12
+ }
13
+ if (root.mcpServer !== undefined) {
14
+ builtins.push(cliBuiltinMcpCommand());
15
+ }
16
+ return builtins;
17
+ }
18
+
19
+ /**
20
+ * Returns a schema suitable for help display, including reserved built-in subtrees.
21
+ * Routing roots get builtins merged; leaf roots are wrapped as a tiny router.
22
+ */
23
+ export function cliPresentationRoot(root: CliCommand): CliCommand {
24
+ if ((root.commands ?? []).some((c) => c.key === "completion")) {
25
+ return root;
26
+ }
27
+ if ("handler" in root && root.handler) {
28
+ return {
29
+ key: root.key,
30
+ description: root.description,
31
+ options: root.options,
32
+ commands: presentationBuiltins(root),
33
+ } as CliCommand;
34
+ }
35
+ return {
36
+ ...root,
37
+ commands: [...(root.commands ?? []), ...presentationBuiltins(root)],
38
+ } as CliCommand;
39
+ }
@@ -0,0 +1,45 @@
1
+ /*
2
+ Shared completion scope walk used by bash, zsh, and fish emitters.
3
+ */
4
+
5
+ import { CliCommand } from "../types.ts";
6
+
7
+ /** One tab-completion scope: child commands, options, and path key for the schema walk. */
8
+ export interface ScopeRec {
9
+ kids: CliCommand[];
10
+ opts: import("../types.ts").CliOption[];
11
+ path: string;
12
+ wantsFiles: boolean;
13
+ }
14
+
15
+ function hasPositionalArguments(cmd: CliCommand): boolean {
16
+ return (cmd.positionals ?? []).length > 0;
17
+ }
18
+
19
+ function walkScopes(cmdPath: string, cmd: CliCommand, acc: ScopeRec[]): void {
20
+ acc.push({
21
+ kids: cmd.commands ?? [],
22
+ opts: cmd.options ?? [],
23
+ path: cmdPath,
24
+ wantsFiles: hasPositionalArguments(cmd),
25
+ });
26
+ for (const ch of cmd.commands ?? []) {
27
+ const nextPath = cmdPath === "" ? ch.key : cmdPath + "/" + ch.key;
28
+ walkScopes(nextPath, ch, acc);
29
+ }
30
+ }
31
+
32
+ /** Flattens the schema into a list of completion scopes (root + every command path). */
33
+ export function collectScopes(schema: CliCommand): ScopeRec[] {
34
+ const acc: ScopeRec[] = [];
35
+ acc.push({
36
+ kids: schema.commands ?? [],
37
+ opts: schema.options ?? [],
38
+ path: "",
39
+ wantsFiles: false,
40
+ });
41
+ for (const c of schema.commands ?? []) {
42
+ walkScopes(c.key, c, acc);
43
+ }
44
+ return acc;
45
+ }
@@ -0,0 +1,24 @@
1
+ /** Produces a shell-safe identifier from the app or command name (alnum → `_`). */
2
+ export function identToken(s: string): string {
3
+ return s.replace(/[^a-zA-Z0-9]/g, "_");
4
+ }
5
+
6
+ /** Escapes a string for use inside single quotes in generated shell scripts. */
7
+ export function escShellSingleQuoted(s: string): string {
8
+ return s.replace(/'/g, "'\\''");
9
+ }
10
+
11
+ /** Escapes a string for use inside fish single-quoted strings. */
12
+ export function escFishSingleQuoted(s: string): string {
13
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
14
+ }
15
+
16
+ /** Sanitizes the app key for generated shell function names (non-alnum removed). */
17
+ export function mainName(schemaName: string): string {
18
+ return schemaName.replace(/[^a-zA-Z0-9]/g, "_");
19
+ }
20
+
21
+ export const kHelpLong = "--help";
22
+ export const kHelpShort = "-h";
23
+ export const kSchemaLong = "--schema";
24
+ export const kSchemaDesc = "Print the full command tree as JSON.";