argsbarg 3.4.1 → 3.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 (66) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/biome.json +29 -6
  3. package/bun.lock +22 -0
  4. package/docs/install.md +1 -1
  5. package/docs/mcp.md +63 -3
  6. package/index.d.ts +51 -51
  7. package/justfile +27 -6
  8. package/package.json +4 -2
  9. package/scripts/release.ts +26 -9
  10. package/src/builtins/builtins.test.ts +9 -4
  11. package/src/builtins/completion-bash.ts +74 -50
  12. package/src/builtins/completion-fish.ts +3 -8
  13. package/src/builtins/completion-group.ts +1 -1
  14. package/src/builtins/completion-zsh.ts +80 -42
  15. package/src/builtins/dispatch.ts +20 -16
  16. package/src/builtins/export.ts +19 -10
  17. package/src/builtins/index.ts +9 -4
  18. package/src/builtins/install.ts +10 -10
  19. package/src/builtins/mcp.ts +3 -3
  20. package/src/builtins/presentation.ts +8 -8
  21. package/src/builtins/scopes.ts +1 -1
  22. package/src/builtins/version.ts +1 -1
  23. package/src/completion.ts +4 -4
  24. package/src/docs/api-guide.test.ts +2 -2
  25. package/src/docs/api-guide.ts +2 -2
  26. package/src/docs/builtin.ts +27 -8
  27. package/src/docs/docs.test.ts +23 -12
  28. package/src/docs/mcp-guide.ts +112 -11
  29. package/src/docs/resolve.ts +10 -3
  30. package/src/docs/save.ts +11 -3
  31. package/src/headless.test.ts +8 -16
  32. package/src/help.ts +73 -43
  33. package/src/hidden-mcpb.test.ts +8 -10
  34. package/src/hidden.ts +2 -2
  35. package/src/index.test.ts +113 -89
  36. package/src/index.ts +24 -24
  37. package/src/install/binary.ts +12 -5
  38. package/src/install/completions.ts +7 -3
  39. package/src/install/detect-installed.ts +35 -4
  40. package/src/install/gh-release-update.ts +31 -23
  41. package/src/install/index.ts +69 -19
  42. package/src/install/install.test.ts +57 -8
  43. package/src/install/mcp-codex.test.ts +57 -0
  44. package/src/install/mcp-codex.ts +125 -0
  45. package/src/install/mcp-config.ts +12 -5
  46. package/src/install/mcp-opencode.test.ts +98 -0
  47. package/src/install/mcp-opencode.ts +149 -0
  48. package/src/install/paths.ts +51 -3
  49. package/src/install/plan.ts +96 -7
  50. package/src/install/shell.ts +1 -4
  51. package/src/install/status.ts +15 -7
  52. package/src/install/uninstall.ts +49 -5
  53. package/src/install/update.test.ts +2 -2
  54. package/src/install/update.ts +3 -1
  55. package/src/invoke.ts +12 -9
  56. package/src/mcp/bundle.ts +38 -14
  57. package/src/mcp/env.ts +7 -13
  58. package/src/mcp/server.ts +12 -6
  59. package/src/mcp/tools.ts +20 -4
  60. package/src/mcp.ts +3 -3
  61. package/src/parse.ts +96 -24
  62. package/src/runtime.ts +22 -12
  63. package/src/schema.ts +11 -5
  64. package/src/skill/generate.ts +4 -4
  65. package/src/skill/install.ts +6 -2
  66. package/src/validate.ts +21 -16
package/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.5.0] - 2026-06-22
11
+
12
+ ### Added
13
+
14
+ - **`install --mcp`** — OpenCode: merges local MCP entry into `~/.config/opencode` config (`mcp` key, OpenCode `type: "local"` format).
15
+ - **`install --mcp`** — Codex: runs `codex mcp add` when `codex` is on PATH.
16
+ - **`install --mcp`** — ChatGPT desktop: merges into `chatgpt_mcp_config.json` when ChatGPT app data exists.
17
+
18
+ ### Changed
19
+
20
+ - **`docs mcp`** — Codex/ChatGPT guidance: Connectors for web (remote MCP); gated desktop JSON auto-install.
21
+
22
+ ## [3.4.2] - 2026-06-22
23
+
24
+ ### Added
25
+
26
+ - **`install --mcp`** — also merges into Claude Desktop `claude_desktop_config.json` when Claude Desktop app data is present (macOS, Windows, Linux paths).
27
+
28
+ ### Changed
29
+
30
+ - **`docs mcp`** — generated guide documents Cursor, Claude Code, and Claude Desktop install targets and platform config paths.
31
+ - **`mcp bundle`** — no longer macOS-only; packs `.mcpb` on any platform when the compiled binary exists.
32
+
10
33
  ## [3.4.1] - 2026-06-22
11
34
 
12
35
 
@@ -348,7 +371,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
348
371
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
349
372
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
350
373
 
351
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.4.1...HEAD
374
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.5.0...HEAD
375
+ [3.5.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.5.0
376
+ [3.4.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.2
352
377
  [3.4.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.1
353
378
  [3.4.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.0
354
379
  [3.3.14]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.14
package/biome.json CHANGED
@@ -1,17 +1,40 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
3
- "organizeImports": {
4
- "enabled": true
5
- },
2
+ "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
3
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
6
4
  "linter": {
7
5
  "enabled": true,
8
6
  "rules": {
9
- "recommended": true
7
+ "preset": "recommended"
10
8
  }
11
9
  },
12
10
  "formatter": {
13
11
  "enabled": true,
14
12
  "indentStyle": "space",
15
13
  "lineWidth": 100
16
- }
14
+ },
15
+ "overrides": [
16
+ {
17
+ "includes": ["**/completion-bash.ts", "**/completion-zsh.ts"],
18
+ "linter": {
19
+ "rules": {
20
+ "suspicious": {
21
+ "noTemplateCurlyInString": "off"
22
+ }
23
+ }
24
+ }
25
+ },
26
+ {
27
+ "includes": ["**/*.test.ts"],
28
+ "linter": {
29
+ "rules": {
30
+ "style": {
31
+ "noNonNullAssertion": "off"
32
+ },
33
+ "suspicious": {
34
+ "noTemplateCurlyInString": "off"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ]
17
40
  }
package/bun.lock CHANGED
@@ -5,17 +5,39 @@
5
5
  "": {
6
6
  "name": "argsbarg",
7
7
  "devDependencies": {
8
+ "@biomejs/biome": "^2.5.0",
8
9
  "@types/bun": "^1.3.12",
10
+ "typescript": "^5.9.3",
9
11
  },
10
12
  },
11
13
  },
12
14
  "packages": {
15
+ "@biomejs/biome": ["@biomejs/biome@2.5.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.0", "@biomejs/cli-darwin-x64": "2.5.0", "@biomejs/cli-linux-arm64": "2.5.0", "@biomejs/cli-linux-arm64-musl": "2.5.0", "@biomejs/cli-linux-x64": "2.5.0", "@biomejs/cli-linux-x64-musl": "2.5.0", "@biomejs/cli-win32-arm64": "2.5.0", "@biomejs/cli-win32-x64": "2.5.0" }, "bin": { "biome": "bin/biome" } }, "sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw=="],
16
+
17
+ "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Mn3Fwi3SA5fgmfCPqmzpWF2DLZnms3BVAhM088nTnGrTZmHS3wwIjcoZPqpXeNgd3DrrLH6xp8vTLIBuJoZiXw=="],
18
+
19
+ "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-rg3VPL5P8mYro6pqlXYXuJWph21slVp3SZtAqWSrkZs40d2gTzYmHF8E/X1iTID25btmNKltNDJ926sqVBp7DQ=="],
20
+
21
+ "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-tl+LW8fdD96/xdeWtWwc82LIOc5CoY7N2AsogLTp5R4ECErYt+8Jl/N68ezN9vzSiqPTxw6vjcihoLPYKZHrlw=="],
22
+
23
+ "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-vQdM4oSGaf7ZNeGO9w5+Y8SBtyser9M6znxYbm7Ec8wInxJu1WiKxFYZW5Auj2d80bcVvefuGGRxoFOE0eee8g=="],
24
+
25
+ "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-zpEGf4RQbFEh8Vt7OmavLyyOzRbtcE9osCqrS1kfvt8jDvxwhKXLSf7n0ebr/ov0RJ9ssP+lhs6C8a9WwFvrQA=="],
26
+
27
+ "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+9hIcMngJ+yGUahXqZuZ8CoWKJE9SAZsFsM3QDvXpNsLbXZ9lqVzgBhOk/jTSYkOA0GLP9eu3teukqpLUojHMg=="],
28
+
29
+ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-jB0wAvTLI4itx5VidqVUejPQFhRUxiZ9l9FvZ26D5fl6t3qme+ZB4PD3bTSeL1vZ8NI2Rx/zj6H9zcESuGHKGw=="],
30
+
31
+ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.0", "", { "os": "win32", "cpu": "x64" }, "sha512-VT/lF+GId+67j8aDfLkxdxNoVApsPSTbyAtB3jJq0IWTrY77WXfbPfpngxq0bA6JCEv/7k8C9qWjDRKRznDlyw=="],
32
+
13
33
  "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
14
34
 
15
35
  "@types/node": ["@types/node@26.0.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA=="],
16
36
 
17
37
  "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
18
38
 
39
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
40
+
19
41
  "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
20
42
  }
21
43
  }
package/docs/install.md CHANGED
@@ -31,7 +31,7 @@ myapp install --uninstall --all --yes
31
31
  | Fish completion | `--completions` | `~/.config/fish/completions/<key>.fish` |
32
32
  | Cursor skill | `--skill` | `~/.cursor/skills/<dir>/` when `~/.cursor` exists |
33
33
  | Claude skill | `--skill` | `~/.claude/skills/<dir>/` when `~/.claude` exists |
34
- | MCP config | `--mcp` | `~/.cursor/mcp.json` and `~/.claude.json` when MCP is enabled |
34
+ | MCP config | `--mcp` | Cursor, Claude Code/Desktop, OpenCode (`~/.config/opencode`), Codex (`codex` on PATH), ChatGPT desktop (when app data exists). ChatGPT web uses Connectors — see `docs mcp` |
35
35
 
36
36
  `--all` expands to `--bin`, `--completions`, `--skill`, and `--mcp` (when `mcpServer.enabled` is `true`) for both install and uninstall. Missing targets are skipped silently (no error if nothing is on disk or a shell/agent directory does not exist).
37
37
 
package/docs/mcp.md CHANGED
@@ -57,7 +57,67 @@ Add a server entry under `mcpServers` in your Cursor MCP config:
57
57
  }
58
58
  ```
59
59
 
60
- Use your real binary or script path. For a compiled CLI, `command` can be the installed binary and `args` can be `["ai", "mcp"]`.
60
+ Use your real binary or script path. For a compiled CLI, `command` can be the installed binary and `args` can be `["mcp"]`.
61
+
62
+ ### Claude Code
63
+
64
+ `install --mcp` merges into `~/.claude.json` under `mcpServers`.
65
+
66
+ ### Claude Desktop
67
+
68
+ `install --mcp` also merges into Claude Desktop config when app data is present:
69
+
70
+ | Platform | Path |
71
+ | --- | --- |
72
+ | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
73
+ | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
74
+ | Linux | `~/.config/Claude/claude_desktop_config.json` |
75
+
76
+ Restart Claude Desktop after config changes. You can also install a **`.mcpb`** bundle via **`mcp bundle`** (see [MCP Bundle](#mcp-bundle-mcp-bundle)).
77
+
78
+ ### OpenCode
79
+
80
+ When `~/.config/opencode` exists, **`install --mcp`** merges a local server under the top-level **`mcp`** key (not `mcpServers`):
81
+
82
+ ```json
83
+ {
84
+ "$schema": "https://opencode.ai/config.json",
85
+ "mcp": {
86
+ "myapp": {
87
+ "type": "local",
88
+ "command": ["myapp", "mcp"],
89
+ "enabled": true
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ OpenCode reads `opencode.jsonc`, `opencode.json`, or `config.json` in that directory. Argsbarg updates the first existing file, or creates `config.json`. JSON-with-comments (`.jsonc`) is not auto-edited — add the block manually or use a `.json` config file.
96
+
97
+ ### OpenAI Codex
98
+
99
+ When **`codex`** is on PATH, **`install --mcp`** runs `codex mcp add <server> -- <binary> mcp`, which writes **`~/.codex/config.toml`**. Otherwise add manually:
100
+
101
+ ```toml
102
+ [mcp_servers.myapp]
103
+ command = "myapp"
104
+ args = ["mcp"]
105
+ ```
106
+
107
+ Use **`codex mcp`** to list/add/remove servers, or **Settings → MCP → Open config.toml** in the Codex app. CLI and IDE extension share the same file.
108
+
109
+ ### ChatGPT
110
+
111
+ **Web / Connectors (OpenAI’s documented path)** — **Settings → Connectors → Developer mode** with a **remote HTTPS MCP URL**. ChatGPT does not spawn local stdio binaries; bridge and tunnel local servers when needed.
112
+
113
+ **Desktop JSON (gated auto-install)** — when ChatGPT app data exists, **`install --mcp`** also merges `mcpServers` into:
114
+
115
+ | Platform | Path |
116
+ | --- | --- |
117
+ | macOS | `~/Library/Application Support/ChatGPT/chatgpt_mcp_config.json` |
118
+ | Windows | `%APPDATA%\OpenAI\ChatGPT\chatgpt_mcp_config.json` |
119
+
120
+ Local JSON support varies by desktop build. Prefer **Connectors** for ChatGPT web or when tools do not appear after install.
61
121
 
62
122
  ### Other MCP hosts
63
123
 
@@ -295,7 +355,7 @@ You should get one JSON line on stdout with `result.capabilities` and `result.se
295
355
 
296
356
  ## MCP Bundle (`mcp bundle`)
297
357
 
298
- When `mcpServer.enabled` is true, **`mcp bundle`** packs a Claude Desktop **`.mcpb`** bundle (macOS-only v1):
358
+ When `mcpServer.enabled` is true, **`mcp bundle`** packs a Claude Desktop **`.mcpb`** bundle:
299
359
 
300
360
  ```bash
301
361
  just build
@@ -305,7 +365,7 @@ just build
305
365
 
306
366
  Expects the compiled binary at **`dist/<program.key>`** and writes **`dist/<program.key>.mcpb`**. Manifest metadata is generated from your schema (`mcpServerId`, tools, `requiresEnv`). Optional pack-time fields live under **`mcpServer.bundle`** (`author`, `icon`, `longDescription`).
307
367
 
308
- Bare **`myapp mcp`** still runs the stdio MCP server (unchanged for `install --mcp` and MCP hosts). Use **`install --mcp`** for Cursor / Claude Code JSON config.
368
+ Bare **`myapp mcp`** still runs the stdio MCP server (unchanged for `install --mcp` and MCP hosts). Use **`install --mcp`** for Cursor, Claude Code, Claude Desktop, and OpenCode JSON config.
309
369
 
310
370
  ## Hidden commands and options
311
371
 
package/index.d.ts CHANGED
@@ -1,33 +1,5 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- /**
4
- * Values passed to a leaf command handler after parsing: app name, routed path, args, and merged options.
5
- */
6
- export declare class CliContext {
7
- readonly appName: string;
8
- readonly commandPath: string[];
9
- readonly args: string[];
10
- readonly program: CliProgram;
11
- readonly opts: Record<string, string>;
12
- readonly invocation: CliInvocation;
13
- /** Captures the program root, routed path, positional words, and option map for a leaf handler. */
14
- constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>, program: CliProgram, invocation?: CliInvocation);
15
- /** Returns whether a presence flag was set (including implicit "1" for boolean options). */
16
- hasFlag(name: string): boolean;
17
- /** Returns the string value for a string-valued option, if present. */
18
- stringOpt(name: string): string | undefined;
19
- /** Parses a stored string as a number; returns null if missing or not a strict double string. */
20
- numberOpt(name: string): number | null;
21
- /**
22
- * Generic typed accessor: parses a stored string using the provided parse function.
23
- * This is the TypeScript-native advantage over the Swift version.
24
- */
25
- typedOpt<T>(name: string, parse: (s: string) => T): T | null;
26
- /** Returns the value(s) for a named positional slot. Varargs slots return string[]; single slots return string | undefined. */
27
- positional(name: string): string | string[] | undefined;
28
- private _posMap;
29
- private _positionalMap;
30
- }
31
3
  /**
32
4
  * How a leaf handler was dispatched.
33
5
  */
@@ -308,30 +280,34 @@ export declare class CliSchemaValidationError extends Error {
308
280
  /** Creates a schema validation error with a human-readable rule violation. */
309
281
  constructor(message: string);
310
282
  }
311
- /** Outcome of a non-exiting CLI invocation. */
312
- export type CliInvokeKind = "ok" | "help" | "error";
313
- /** Result of cliInvoke: captured output and exit metadata without process.exit. */
314
- export interface CliInvokeResult {
315
- /** Invocation outcome. */
316
- kind: CliInvokeKind;
317
- /** Simulated exit code. */
318
- exitCode: number;
319
- /** Captured stdout during handler execution. */
320
- stdout: string;
321
- /** Captured stderr during handler execution. */
322
- stderr: string;
323
- /** Set when kind === "error" (parse/validation message). */
324
- errorMsg?: string;
325
- }
326
283
  /**
327
- * Parses argv against the user root, runs the leaf handler, and returns captured output.
328
- * Never calls process.exit.
284
+ * Values passed to a leaf command handler after parsing: app name, routed path, args, and merged options.
329
285
  */
330
- export declare function cliInvoke(root: CliProgram, argv: string[]): Promise<CliInvokeResult>;
331
- export declare function cliRun(program: CliProgram, argv?: string[]): Promise<never>;
332
- export declare function cliErrWithHelp(ctx: CliContext, msg: string): never;
333
- /** True when stdin is a TTY. */
334
- export declare const isInteractiveTty: boolean;
286
+ export declare class CliContext {
287
+ readonly appName: string;
288
+ readonly commandPath: string[];
289
+ readonly args: string[];
290
+ readonly program: CliProgram;
291
+ readonly opts: Record<string, string>;
292
+ readonly invocation: CliInvocation;
293
+ /** Captures the program root, routed path, positional words, and option map for a leaf handler. */
294
+ constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>, program: CliProgram, invocation?: CliInvocation);
295
+ /** Returns whether a presence flag was set (including implicit "1" for boolean options). */
296
+ hasFlag(name: string): boolean;
297
+ /** Returns the string value for a string-valued option, if present. */
298
+ stringOpt(name: string): string | undefined;
299
+ /** Parses a stored string as a number; returns null if missing or not a strict double string. */
300
+ numberOpt(name: string): number | null;
301
+ /**
302
+ * Generic typed accessor: parses a stored string using the provided parse function.
303
+ * This is the TypeScript-native advantage over the Swift version.
304
+ */
305
+ typedOpt<T>(name: string, parse: (s: string) => T): T | null;
306
+ /** Returns the value(s) for a named positional slot. Varargs slots return string[]; single slots return string | undefined. */
307
+ positional(name: string): string | string[] | undefined;
308
+ private _posMap;
309
+ private _positionalMap;
310
+ }
335
311
  /** Minimal context for headless routing helpers. */
336
312
  export type HeadlessContext = Pick<CliContext, "invocation">;
337
313
  /** True when `--json` was passed or the handler was invoked via MCP. */
@@ -405,6 +381,26 @@ export declare function createGhVersionCheck(config: GhVersionCheckConfig): {
405
381
  };
406
382
  /** Shared `gh release view` fetcher for hooks and version-check refresh. */
407
383
  export declare function createGhFetchLatest(config: Pick<GhReleaseUpdateConfig, "repo" | "repoEnvHint">): () => Promise<string>;
384
+ /** Outcome of a non-exiting CLI invocation. */
385
+ export type CliInvokeKind = "ok" | "help" | "error";
386
+ /** Result of cliInvoke: captured output and exit metadata without process.exit. */
387
+ export interface CliInvokeResult {
388
+ /** Invocation outcome. */
389
+ kind: CliInvokeKind;
390
+ /** Simulated exit code. */
391
+ exitCode: number;
392
+ /** Captured stdout during handler execution. */
393
+ stdout: string;
394
+ /** Captured stderr during handler execution. */
395
+ stderr: string;
396
+ /** Set when kind === "error" (parse/validation message). */
397
+ errorMsg?: string;
398
+ }
399
+ /**
400
+ * Parses argv against the user root, runs the leaf handler, and returns captured output.
401
+ * Never calls process.exit.
402
+ */
403
+ export declare function cliInvoke(root: CliProgram, argv: string[]): Promise<CliInvokeResult>;
408
404
  /** Resolved paths for `mcp bundle`. */
409
405
  export interface McpBundlePaths {
410
406
  binaryPath: string;
@@ -422,8 +418,12 @@ export interface PackMcpBundleOpts {
422
418
  }
423
419
  /**
424
420
  * Stages manifest + binary (+ optional icon) and writes a `.mcpb` ZIP.
425
- * macOS-only v1; requires the compiled binary to exist.
421
+ * Requires the compiled binary to exist.
426
422
  */
427
423
  export declare function packMcpBundle(program: CliProgram, opts?: PackMcpBundleOpts): string;
424
+ export declare function cliRun(program: CliProgram, argv?: string[]): Promise<never>;
425
+ export declare function cliErrWithHelp(ctx: CliContext, msg: string): never;
426
+ /** True when stdin is a TTY. */
427
+ export declare const isInteractiveTty: boolean;
428
428
 
429
429
  export {};
package/justfile CHANGED
@@ -1,11 +1,27 @@
1
1
  # https://github.com/casey/just — run `just` to list recipes.
2
2
 
3
+ set shell := ["bash", "-eu", "-o", "pipefail", "-c"]
4
+
3
5
  _:
4
6
  @just --list
5
7
 
6
- # typecheck the codebase
7
- check-types:
8
- bun x tsc
8
+ # check the codebase
9
+ check: typecheck format
10
+
11
+ # Update local consumer apps: npm i argsbarg@latest, build, docgen
12
+ consumers-sync *apps:
13
+ #!/usr/bin/env bash
14
+ root="$(cd "{{justfile_directory()}}" && pwd)"
15
+ ss="$root/../../ss"
16
+ apps=({{apps}})
17
+ if [[ ${#apps[@]} -eq 0 ]]; then
18
+ apps=(idp-trees sqsp-qa-tools sqsp-i18n-tools)
19
+ fi
20
+ for app in "${apps[@]}"; do
21
+ dir="$(cd "$ss/$app" && pwd)"
22
+ echo "==> $app ($dir)"
23
+ (cd "$dir" && npm i argsbarg@latest --no-package-lock && just build && just docgen)
24
+ done
9
25
 
10
26
  # run the minimal example
11
27
  example *ARGS:
@@ -17,16 +33,20 @@ example-watch *ARGS:
17
33
 
18
34
  # format the codebase
19
35
  format:
20
- bun x biome format ./src ./scripts --write
36
+ bun run biome check ./src ./scripts --write
21
37
 
22
38
  # lint the codebase
23
39
  lint:
24
- bun x biome check ./src ./scripts
40
+ bun run biome check ./src ./scripts
25
41
 
26
42
  # Typecheck, lint, then run the test suite.
27
- test: check-types format lint
43
+ test: check
28
44
  bun test
29
45
 
46
+ # typecheck the codebase
47
+ typecheck:
48
+ bun run tsc --noEmit
49
+
30
50
  # generate type declarations for the package
31
51
  typegen:
32
52
  bunx dts-bundle-generator --out-file index.d.ts src/index.ts
@@ -34,3 +54,4 @@ typegen:
34
54
  # publish to github and npm
35
55
  release bump: test typegen
36
56
  bun scripts/release.ts {{bump}}
57
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "3.4.1",
3
+ "version": "3.5.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -21,6 +21,8 @@
21
21
  }
22
22
  },
23
23
  "devDependencies": {
24
- "@types/bun": "^1.3.12"
24
+ "@biomejs/biome": "^2.5.0",
25
+ "@types/bun": "^1.3.12",
26
+ "typescript": "^5.9.3"
25
27
  }
26
28
  }
@@ -12,7 +12,7 @@
12
12
  * repo changes (`git add -A`), not only the version and CHANGELOG edits from this script.
13
13
  */
14
14
 
15
- import { unlink } from "fs/promises";
15
+ import { unlink } from "node:fs/promises";
16
16
 
17
17
  /** Returns the parent directory of an absolute file path. */
18
18
  function parentDir(absolute: string): string {
@@ -96,9 +96,13 @@ function githubRepoBaseFromOrigin(origin: string): string | null {
96
96
  function releasedSemverVersionsFromChangelog(md: string): string[] {
97
97
  const re = /^## \[(\d+\.\d+\.\d+)\] /gm;
98
98
  const out: string[] = [];
99
- let m: RegExpExecArray | null;
100
- while ((m = re.exec(md)) !== null) {
101
- out.push(m[1]!);
99
+ let m = re.exec(md);
100
+ while (m !== null) {
101
+ const version = m[1];
102
+ if (version !== undefined) {
103
+ out.push(version);
104
+ }
105
+ m = re.exec(md);
102
106
  }
103
107
  return out;
104
108
  }
@@ -108,14 +112,18 @@ function releasedSemverVersionsFromChangelog(md: string): string[] {
108
112
  * (`[...]: http...`) at the end of the file.
109
113
  */
110
114
  function stripChangelogLinkDefinitions(md: string): string {
111
- let s = md.replace(/\r?\n## Links\r?\n/, "\n");
115
+ const s = md.replace(/\r?\n## Links\r?\n/, "\n");
112
116
  const lines = s.split(/\r?\n/);
113
117
  let i = lines.length;
114
118
  while (i > 0 && lines[i - 1] === "") {
115
119
  i -= 1;
116
120
  }
117
121
  const refLine = /^\[[^\]]+\]: .+$/;
118
- while (i > 0 && refLine.test(lines[i - 1]!)) {
122
+ while (i > 0) {
123
+ const line = lines[i - 1];
124
+ if (line === undefined || !refLine.test(line)) {
125
+ break;
126
+ }
119
127
  i -= 1;
120
128
  }
121
129
  while (i > 0 && lines[i - 1] === "") {
@@ -143,7 +151,10 @@ function appendChangelogLinkDefinitions(md: string, repoBase: string): string {
143
151
  if (versions.length === 0) {
144
152
  return `${body}\n`;
145
153
  }
146
- const newest = versions[0]!;
154
+ const newest = versions[0];
155
+ if (newest === undefined) {
156
+ return `${body}\n`;
157
+ }
147
158
  const lines = [
148
159
  "",
149
160
  "",
@@ -178,7 +189,9 @@ function promoteChangelog(content: string, version: string, date: string): strin
178
189
  const bodyStart = lineEnd + 1;
179
190
  const nextIdx = content.indexOf("\n## [", bodyStart);
180
191
  const body =
181
- nextIdx === -1 ? content.slice(bodyStart).trimEnd() : content.slice(bodyStart, nextIdx).trimEnd();
192
+ nextIdx === -1
193
+ ? content.slice(bodyStart).trimEnd()
194
+ : content.slice(bodyStart, nextIdx).trimEnd();
182
195
  const tail = nextIdx === -1 ? "" : content.slice(nextIdx + 1);
183
196
  const before = content.slice(0, idx);
184
197
  const newBlock = `${header}\n\n## [${version}] - ${date}\n${body}\n\n`;
@@ -240,7 +253,11 @@ try {
240
253
  run("git tag", ["git", "tag", "-a", tag, "-m", msg], repoRoot);
241
254
  run("git push", ["git", "push"], repoRoot);
242
255
  run("git push tags", ["git", "push", "--tags"], repoRoot);
243
- run("gh release", ["gh", "release", "create", tag, "--title", tag, "--notes-file", notesPath], repoRoot);
256
+ run(
257
+ "gh release",
258
+ ["gh", "release", "create", tag, "--title", tag, "--notes-file", notesPath],
259
+ repoRoot,
260
+ );
244
261
  run("npm publish", ["npm", "publish"], repoRoot);
245
262
  } finally {
246
263
  await unlink(notesPath).catch(() => {});
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import type { CliProgram } from "../types.ts";
3
+ import { exportPresentationBuiltins } from "./export.ts";
4
+ import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
2
5
  import { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
3
6
  import { cliBuiltinMcpCommand } from "./mcp.ts";
4
7
  import { cliPresentationRoot } from "./presentation.ts";
5
- import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
6
- import { exportPresentationBuiltins } from "./export.ts";
7
- import { CliProgram } from "../types.ts";
8
8
 
9
9
  const fixture: CliProgram = {
10
10
  key: "myapp",
@@ -114,7 +114,12 @@ describe("completion emitters", () => {
114
114
  });
115
115
 
116
116
  test("zsh script registers compdef", () => {
117
- const schema = cliPresentationRoot({ key: "zapp", version: "0.0.0", description: "z", handler: () => {} });
117
+ const schema = cliPresentationRoot({
118
+ key: "zapp",
119
+ version: "0.0.0",
120
+ description: "z",
121
+ handler: () => {},
122
+ });
118
123
  const zsh = completionZshScript(schema);
119
124
  expect(zsh).toContain("#compdef zapp");
120
125
  expect(zsh).toContain("compdef _zapp zapp");