argsbarg 3.4.2 → 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 +14 -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 +45 -1
  6. package/index.d.ts +50 -50
  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 +1 -1
  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 +18 -11
  28. package/src/docs/mcp-guide.ts +105 -24
  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 +7 -6
  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 +29 -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 +31 -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 +29 -3
  49. package/src/install/plan.ts +73 -6
  50. package/src/install/shell.ts +1 -4
  51. package/src/install/status.ts +12 -6
  52. package/src/install/uninstall.ts +38 -4
  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 +36 -8
  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,18 @@ 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
+
10
22
  ## [3.4.2] - 2026-06-22
11
23
 
12
24
  ### Added
@@ -359,7 +371,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
359
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`).
360
372
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
361
373
 
362
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.4.2...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
363
376
  [3.4.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.2
364
377
  [3.4.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.1
365
378
  [3.4.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.4.0
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` (when `~/.cursor` exists), `~/.claude.json` (Claude Code), and Claude Desktop `claude_desktop_config.json` when Claude Desktop app data exists |
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
@@ -75,6 +75,50 @@ Use your real binary or script path. For a compiled CLI, `command` can be the in
75
75
 
76
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
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.
121
+
78
122
  ### Other MCP hosts
79
123
 
80
124
  Any host that spawns a subprocess and wires stdin/stdout works the same way: the **command** is your app, and **`mcp`** starts the server.
@@ -321,7 +365,7 @@ just build
321
365
 
322
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`).
323
367
 
324
- Bare **`myapp mcp`** still runs the stdio MCP server (unchanged for `install --mcp` and MCP hosts). Use **`install --mcp`** for Cursor, Claude Code, and Claude Desktop 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.
325
369
 
326
370
  ## Hidden commands and options
327
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;
@@ -425,5 +421,9 @@ export interface PackMcpBundleOpts {
425
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.2",
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");