argsbarg 0.1.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.
@@ -0,0 +1,9 @@
1
+ ---
2
+ description: Code quality and style rules for TypeScript files
3
+ globs: **/*.ts
4
+ ---
5
+
6
+ # TypeScript Coding Standards
7
+
8
+ - All imports must be ordered alphabetically by their source module path (the `from` clause).
9
+ - Explicit exports using the `export { ... } from "..."` or `export type { ... } from "..."` syntax must be ordered alphabetically by their source module path and placed at the top of the file, immediately below the imports.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ ![Logo](logo.png)
2
+ <!-- Big money NE - https://patorjk.com/software/taag/#p=testall&f=Bulbhead&t=shebangsy&x=none&v=4&h=4&w=80&we=false> -->
3
+
4
+ [![GitHub](https://img.shields.io/badge/GitHub-bdombro%2Fbun--argsbarg-181717?logo=github)](https://github.com/bdombro/bun-argsbarg)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![npm version](https://img.shields.io/npm/v/bun-argsbarg.svg)](https://www.npmjs.com/package/argsbarg)
7
+ [![Bun](https://img.shields.io/badge/Bun-%23000000.svg?logo=bun&logoColor=white)](https://bun.sh)
8
+
9
+ Build beautiful, well-behaved CLI apps with Bun — **no third-party runtime dependencies**.
10
+
11
+ Why another CLI parser?
12
+
13
+ *Schema-first* -- define your entire CLI’s structure, commands, options, and help in a single, explicit data model, making the command-line interface centralized, clear, and self-describing upfront.
14
+
15
+ *Bun-optimized* -- built from the ground up for Bun and TypeScript, leveraging Bun’s performance and modern JavaScript features without any extra dependencies.
16
+
17
+ Also checkout ArgsBarg for [cpp](https://github.com/bdombro/cpp-argsbarg), [nim](https://github.com/bdombro/nim-argsbarg), and [swift](https://github.com/bdombro/swift-argsbarg)!
18
+
19
+ Halps! -->
20
+ ![help-preview.png](docs/help-preview.png)
21
+
22
+ Sub-level Halps! -->
23
+ ![help-l2-preview.png](docs/help-l2-preview.png)
24
+
25
+ Shell completions! -->
26
+ ![completions-preview.png](docs/completions-preview.png)
27
+
28
+
29
+ ## Usage
30
+
31
+ ```typescript
32
+ import { cliRun, CliCommand, createOption, CliOptionKind, CliFallbackMode } from "argsbarg";
33
+
34
+ const cli: CliCommand = {
35
+ key: "helloapp",
36
+ description: "Tiny demo.",
37
+ children: [
38
+ {
39
+ key: "hello",
40
+ description: "Say hello.",
41
+ options: [
42
+ createOption("name", "Who to greet.", {
43
+ kind: CliOptionKind.String,
44
+ shortName: "n",
45
+ }),
46
+ createOption("verbose", "Enable extra logging.", {
47
+ shortName: "v",
48
+ }),
49
+ ],
50
+ handler: async (ctx) => {
51
+ const name = ctx.stringOpt("name") ?? "world";
52
+ if (ctx.flag("verbose")) {
53
+ console.log("verbose mode");
54
+ }
55
+ console.log(`hello ${name}`);
56
+ },
57
+ },
58
+ ],
59
+ fallbackCommand: "hello",
60
+ fallbackMode: CliFallbackMode.MissingOrUnknown,
61
+ };
62
+
63
+ await cliRun(cli);
64
+ ```
65
+
66
+ `cliRun` parses `process.argv`, prints help or errors, dispatches the leaf handler, and **exits the process**.
67
+
68
+
69
+
70
+ ## What is it?
71
+
72
+ Everything you need for a first-class CLI:
73
+
74
+ - **Nested subcommands** (`CliCommand` with `children` for groups, `handler` for leaves)
75
+ - **POSIX-style options** (`-x`, `--long`, `--long=value`)
76
+ - **Bundled presence flags** (`-abc`)
77
+ - **Positional arguments and varargs tails** (`CliOptionDef` with `positional: true`)
78
+ - **Scoped help** at any routing depth (`-h` / `--help`)
79
+ - **Default-command fallback** (`CliFallbackMode`)
80
+ - **Option separator** (`--` to stop option parsing)
81
+ - **Rich help**: rounded UTF-8 boxes, tables, terminal width detection (`process.stdout.columns`), colors when stdout/stderr is a TTY
82
+ - **TypeScript-native**: Typed option accessors (`ctx.typedOpt<T>`) and `async/await` handler support.
83
+
84
+
85
+
86
+ ## Built-ins
87
+
88
+ Every app gets:
89
+
90
+ - `-h` / `--help` at any routing depth (scoped help).
91
+ - **`completion bash` / `completion zsh`** — print shell completion scripts to stdout (injected by `cliRun`).
92
+
93
+ Do not declare a top-level command named **`completion`** — it is reserved for this built-in.
94
+
95
+
96
+ ### Shell completions
97
+
98
+ ```bash
99
+ myapp completion bash > ~/.bash_completion.d/myapp
100
+ # or: source <(myapp completion bash)
101
+
102
+ myapp completion zsh > ~/.zsh/completions/_myapp
103
+ # then: fpath+=(~/.zsh/completions); autoload -Uz compinit && compinit
104
+ # or, for a one-off test in the current shell: eval "$(myapp completion zsh)"
105
+ ```
106
+
107
+
108
+
109
+ ## Install
110
+
111
+ ```bash
112
+ bun add bun-argsbarg
113
+ ```
114
+
115
+
116
+ ## How it works
117
+
118
+ 1. Build a **program root** `CliCommand` using pure TypeScript objects: `key` is the app/binary name, `children` are top-level subcommands, `options` are global flags. The root must not set `handler` or declare `positionals` (validated at startup). Use `fallbackCommand` / `fallbackMode` on the root only for default top-level routing.
119
+ 2. Call `await cliRun(root)` with that root — validates, parses argv, renders help or errors, invokes the leaf handler, and `process.exit`s with status **0** on success, **1** on implicit help or error (explicit `--help` → **0**).
120
+ 3. From a handler, `cliErrWithHelp(ctx, "message")` prints a red error line plus contextual help on stderr and exits **1**.
121
+
122
+ ### Fallback modes (`CliFallbackMode`)
123
+
124
+ | Mode | Empty argv | Unknown first token |
125
+ | --- | --- | --- |
126
+ | `MissingOnly` | Default command | Error |
127
+ | `MissingOrUnknown` | Default command | Default command (token becomes argv for the default) |
128
+ | `UnknownOnly` | Root help (exit 1) | Default command |
129
+
130
+ With `MissingOrUnknown` / `UnknownOnly`, unrecognized **root** flags stop root-flag consumption and the remainder is passed to the default command.
131
+
132
+ ### Positionals (help labels)
133
+
134
+ Use `createOption` with `positional: true`. With `argMax: 0`, the tail accepts at least `argMin` tokens and has no upper bound unless you set `argMax` > 0.
135
+
136
+ | Fields | Label |
137
+ | --- | --- |
138
+ | `positional: true`, default `argMin`/`argMax` | `<n>` |
139
+ | `positional: true`, `argMin: 0`, `argMax: 1` | `[n]` |
140
+ | `positional: true`, `argMin: 0`, `argMax: 0` | `[n...]` |
141
+ | `positional: true`, `argMin: 1`, `argMax: 0` | `<n...>` |
142
+
143
+ ### Reading values (`CliContext`)
144
+
145
+ - `ctx.flag("verbose")` — presence options (`boolean`).
146
+ - `ctx.stringOpt("name")` / `ctx.numberOpt("count")` — `string | undefined` / `number | null`.
147
+ - `ctx.typedOpt<T>("custom", parseFn)` — pass a custom parsing function for type-safe option resolution.
148
+ - `ctx.args` — positional words in order as `string[]`.
149
+ - `ctx.schema` — merged program root (`CliCommand`) for contextual help.
150
+
151
+
152
+
153
+ ## Examples
154
+
155
+ Check the `examples/` directory for full working scripts:
156
+
157
+ | Example | File | Shows |
158
+ | --- | --- | --- |
159
+ | `ArgsBargMinimal` | `examples/minimal.ts` | String + presence flags, `MissingOrUnknown` fallback. |
160
+ | `ArgsBargNested` | `examples/nested.ts` | Nested `CliCommand` tree, positional tails, async handlers. |
161
+
162
+ ```bash
163
+ bun examples/minimal.ts --help
164
+ bun examples/minimal.ts hello --name world
165
+ bun examples/nested.ts stat owner lookup -u alice ./README.md
166
+ bun examples/nested.ts read ./README.md
167
+ ```
168
+
169
+
170
+
171
+ ## Public API overview
172
+
173
+ | Symbol | Role |
174
+ | --- | --- |
175
+ | `CliCommand`, `CliOptionDef`, `CliOptionKind`, `CliFallbackMode` | Schema types. |
176
+ | `createOption()` | Factory helper for options with sensible defaults. |
177
+ | `CliContext`, `CliHandler` | Handler context and async-compatible closure type. |
178
+ | `cliRun(root, [argv])` | Parse argv, dispatch, exit. |
179
+ | `cliErrWithHelp(ctx, msg)` | Error + scoped help, exit 1. |
180
+ | `cliHelpRender(schema, helpPath, useStderr)` | Render help (`schema` is the program root `CliCommand`). |
181
+
182
+ Reserved identifier (validated at startup): root command **`completion`**.
183
+
184
+ ---
185
+
186
+ ## License
187
+
188
+ MIT
package/biome.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
3
+ "organizeImports": {
4
+ "enabled": true
5
+ },
6
+ "linter": {
7
+ "enabled": true,
8
+ "rules": {
9
+ "recommended": true
10
+ }
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "space",
15
+ "lineWidth": 100
16
+ }
17
+ }
package/bun.lock ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "bun-argsbarg",
7
+ "devDependencies": {
8
+ "@types/bun": "^1.3.12",
9
+ },
10
+ },
11
+ },
12
+ "packages": {
13
+ "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
14
+
15
+ "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
16
+
17
+ "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
18
+
19
+ "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
20
+ }
21
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bun
2
+ /*
3
+ This example shows the smallest end-to-end CLI setup.
4
+ It includes one command, a couple of options, and a direct call to the runtime so
5
+ readers can copy the pattern into their own scripts quickly.
6
+
7
+ It demonstrates the minimal Bun integration path.
8
+ */
9
+
10
+ import { cliRun, CliCommand, createOption, CliOptionKind, CliFallbackMode } from "../src/index.ts";
11
+
12
+ const cli: CliCommand = {
13
+ key: "minimal.ts",
14
+ description: "Tiny demo.",
15
+ children: [
16
+ {
17
+ key: "hello",
18
+ description: "Say hello.",
19
+ options: [
20
+ createOption("name", "Who to greet.", {
21
+ kind: CliOptionKind.String,
22
+ shortName: "n",
23
+ }),
24
+ createOption("verbose", "Enable extra logging.", {
25
+ shortName: "v",
26
+ }),
27
+ ],
28
+ handler: (ctx) => {
29
+ const name = ctx.stringOpt("name") ?? "world";
30
+ if (ctx.flag("verbose")) {
31
+ console.log("verbose mode");
32
+ }
33
+ console.log(`hello ${name}`);
34
+ },
35
+ },
36
+ ],
37
+ fallbackCommand: "hello",
38
+ fallbackMode: CliFallbackMode.MissingOrUnknown,
39
+ };
40
+
41
+ await cliRun(cli);
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bun
2
+ /*
3
+ This example shows nested routing with groups and fallback behavior.
4
+ It adds a deeper command tree so readers can see how grouped routing, leaf handlers,
5
+ and fallback commands fit together in one schema.
6
+
7
+ It demonstrates how the schema scales beyond one command.
8
+ */
9
+
10
+ import { cliRun, CliCommand, createOption, CliOptionKind, CliFallbackMode } from "../src/index.ts";
11
+
12
+ const cli: CliCommand = {
13
+ key: "nested.ts",
14
+ description: "Nested groups demo.",
15
+ children: [
16
+ {
17
+ key: "stat",
18
+ description: "File metadata.",
19
+ children: [
20
+ {
21
+ key: "owner",
22
+ description: "Ownership helpers.",
23
+ children: [
24
+ {
25
+ key: "lookup",
26
+ description: "Resolve owner info.",
27
+ options: [
28
+ createOption("user-name", "User to look up.", {
29
+ kind: CliOptionKind.String,
30
+ shortName: "u",
31
+ }),
32
+ ],
33
+ positionals: [
34
+ createOption("path", "File or directory.", {
35
+ kind: CliOptionKind.String,
36
+ positional: true,
37
+ }),
38
+ ],
39
+ handler: (ctx) => {
40
+ const user = ctx.stringOpt("user-name") ?? "?";
41
+ const path = ctx.args[0];
42
+ if (!path) {
43
+ console.error("Missing path.");
44
+ process.exit(1);
45
+ }
46
+ console.log(`lookup user=${user} path=${path}`);
47
+ },
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ },
53
+ {
54
+ key: "read",
55
+ description: "Print the first line of each file.",
56
+ notes: "Pass one or more file paths. {app} prints the first line of each.",
57
+ positionals: [
58
+ createOption("files", "Paths to read.", {
59
+ kind: CliOptionKind.String,
60
+ positional: true,
61
+ argMin: 1,
62
+ argMax: 0,
63
+ }),
64
+ ],
65
+ handler: async (ctx) => {
66
+ if (ctx.args.length === 0) {
67
+ console.error("Missing file path.");
68
+ process.exit(1);
69
+ }
70
+ for (const path of ctx.args) {
71
+ try {
72
+ const file = Bun.file(path);
73
+ const text = await file.text();
74
+ const firstLine = text.split("\n")[0];
75
+ console.log(`${path}: ${firstLine}`);
76
+ } catch (err) {
77
+ console.error(`Cannot open: ${path}`);
78
+ }
79
+ }
80
+ },
81
+ },
82
+ ],
83
+ fallbackCommand: "read",
84
+ fallbackMode: CliFallbackMode.MissingOrUnknown,
85
+ };
86
+
87
+ await cliRun(cli);
package/logo.png ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "argsbarg",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "module": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "bin": {
9
+ "argsbarg": "./src/index.ts"
10
+ },
11
+ "exports": {
12
+ ".": "./src/index.ts"
13
+ },
14
+ "scripts": {
15
+ "test": "bun check-types && bun lint && bun test",
16
+ "dev": "bun --watch ./examples/minimal.ts",
17
+ "lint": "bun x biome check ./src",
18
+ "format": "bun x biome format ./src --write",
19
+ "check-types": "bun x tsc",
20
+ "release": "bun x npm publish"
21
+ },
22
+ "devDependencies": {
23
+ "@types/bun": "^1.3.12"
24
+ }
25
+ }
package/plan.md ADDED
@@ -0,0 +1,194 @@
1
+ # bun-argsbarg Plan
2
+
3
+ ## Plan: bun-argsbarg — Bun CLI Argument Parser
4
+
5
+ **TL;DR**: Convert the Swift `argsbarg` declarative CLI framework into a TypeScript/Bun library with the same CLI features (schema-driven parsing, help rendering, shell completion, subcommand routing, fallback commands) while leveraging TypeScript's type system for compile-time schema safety.
6
+
7
+ ## Current Status
8
+
9
+ **Overall**: Phases 1–5 (95%) complete; Phases 6–8 in progress.
10
+
11
+ ### ✅ Completed
12
+
13
+ - **Phase 1**: Core Schema Types (`src/types.ts`)
14
+ - `CliOptionKind` enum (Presence, String, Number)
15
+ - `CliFallbackMode` enum (MissingOnly, MissingOrUnknown, UnknownOnly)
16
+ - `CliCommand`, `CliOptionDef`, `CliHandler`, `CliContext` interfaces
17
+ - `CliSchemaValidationError` class
18
+ - Helper factories: `createOption()`, `createCommand()`
19
+
20
+ - **Phase 2**: Argument Parser (`src/parse.ts`)
21
+ - `parse(root, argv)` with long/short options, bundling, equals syntax
22
+ - Option consumption logic with strict number validation
23
+ - Subcommand routing and positional argument collection
24
+ - Help token detection (`-h`, `--help`)
25
+ - `postParseValidate()` for strict option validation
26
+ - `cliValidateRoot()` for schema validation (root rules, child uniqueness, reserved names, positional ordering)
27
+ - Support for all fallback modes
28
+
29
+ - **Phase 3**: Runtime Context (`src/context.ts`)
30
+ - `CliContext` class with typed accessors
31
+ - `flag(name)` — boolean presence check
32
+ - `stringOpt(name)` — string value or undefined
33
+ - `numberOpt(name)` — strict double parsing or null
34
+ - `typedOpt<T>(name, parse)` — generic typed accessor (TypeScript advantage)
35
+
36
+ - **Phase 4**: Help Rendering (`src/help.ts`)
37
+ - `cliHelpRender()` for formatted help output
38
+ - TTY-aware ANSI colors (red, aqua, gray, bold)
39
+ - Unicode box drawing (╭╮├┤╰╯)
40
+ - Terminal width detection (`process.stdout.columns`)
41
+ - Text wrapping with visible width calculation (strips ANSI)
42
+ - Help sections: usage box, options table, positionals table, subcommands table, notes box
43
+ - `cliOptionLabel()` for formatted option display
44
+
45
+ - **Phase 5**: Shell Completion (`src/completion.ts`)
46
+ - `completionBashScript(schema)` — bash tab-completion generator
47
+ - `completionZshScript(schema)` — zsh tab-completion generator
48
+ - Scope walking for command tree traversal
49
+ - Option/command/positional matching logic for both shells
50
+
51
+ ### 🚧 In Progress
52
+
53
+ - **Phase 6**: Main Entry Point (`src/index.ts`)
54
+ - **Status**: Placeholder `hello()` function remains
55
+ - **Needs**: Implement `cliRun(root: CliCommand): Promise<void>`
56
+ - Validate schema via `cliValidateRoot()`
57
+ - Auto-merge built-in `completion` command with `bash`/`zsh` subcommands
58
+ - Call `parse()` on `process.argv.slice(2)`
59
+ - Call `postParseValidate()`
60
+ - Handle `ParseKind.Help` → render via `cliHelpRender()` → exit(0)
61
+ - Handle `ParseKind.Error` → render red error + contextual help → exit(1)
62
+ - Route to handler function with `CliContext`
63
+ - Support async handlers with `await`
64
+
65
+ - **Phase 7**: Examples & Tests (`src/index.test.ts`, `examples/`)
66
+ - **Status**: Only placeholder tests exist
67
+ - **Needs**:
68
+ - Replace `src/index.test.ts` with comprehensive test suite covering:
69
+ - Option parsing (long, short, bundled, equals syntax)
70
+ - Help detection and rendering
71
+ - Subcommand routing and fallback modes
72
+ - Positional argument collection and arity validation
73
+ - Unknown options/commands
74
+ - Schema validation errors
75
+ - Async handler support
76
+ - Typed option accessors
77
+ - Create `examples/minimal.ts` — hello with `--name` and `--verbose`
78
+ - Create `examples/nested.ts` — nested subcommands with positionals (mirroring Swift examples)
79
+
80
+ ### ❌ Not Started
81
+
82
+ - **Phase 8**: Project Polish
83
+ - Add `biome.json` for linting/formatting
84
+ - Add CLI binary entry point (`bin/argsbarg`)
85
+ - Create `README.md` with API docs and usage examples
86
+ - Update `package.json` scripts and bin entry
87
+ - Verify all verification steps pass
88
+
89
+ ## Next Immediate Steps
90
+
91
+ 1. Implement `cliRun()` in `src/index.ts`
92
+ 2. Rewrite `src/index.test.ts` with actual test coverage
93
+ 3. Create working examples in `examples/`
94
+ 4. Run `bun test` and verify all tests pass
95
+ 5. Run examples and verify output matches expectations
96
+
97
+ **Steps**
98
+
99
+ ### Phase 1: Core Schema Types (types.ts)
100
+ - Define TypeScript equivalents of Swift's `CliOptionKind`, `CliOption`, `CliCommand`, `CliFallbackMode`
101
+ - Leverage TypeScript generics/union types for compile-time safety (e.g., typed option values instead of string-only)
102
+ - Add `CliHandler` type as `(ctx: CliContext) => void | Promise<void>` (support async handlers)
103
+ - Add `CliSchemaValidationError` enum/class
104
+ - **Parallel with Phase 2**
105
+
106
+ ### Phase 2: Argument Parser (parse.ts)
107
+ - Implement `parse(root: CliCommand, argv: string[]): ParseResult` — same logic as Swift
108
+ - Long options (`--name`, `--name=value`)
109
+ - Short options (`-n`, bundled `-abc`)
110
+ - Subcommand routing through nested `CliCommand` tree
111
+ - Positional argument collection with arity validation (argMin/argMax)
112
+ - Help token detection (`-h`/`--help`)
113
+ - Root-level `fallbackCommand`/`fallbackMode` support
114
+ - Implement `postParseValidate()` — strict number validation, option key verification
115
+ - Implement `cliValidateRoot()` — schema validation (no handler on routing nodes, no positionals on root, unique short names, reserved `-h`, etc.)
116
+ - **Depends on Phase 1**
117
+
118
+ ### Phase 3: Context & Runtime (context.ts)
119
+ - Implement `CliContext` class with typed accessors:
120
+ - `flag(name)` — boolean presence check
121
+ - `stringOpt(name)` — string value
122
+ - `numberOpt(name)` — strict double parse
123
+ - **TypeScript enhancement**: `typedOpt<T>(name, parseFn)` — generic typed accessor
124
+ - Implement `cliErrWithHelp(ctx, msg)` — red error + contextual help on stderr
125
+ - **Depends on Phase 1**
126
+
127
+ ### Phase 4: Help Rendering (help.ts)
128
+ - Terminal-aware help with rounded box drawing (same Unicode box chars as Swift)
129
+ - TTY detection (Node's `process.stdout.isTTY` instead of Swift's `isatty`)
130
+ - Terminal width detection (`TIOCGWINSZ` via `ioctl` or fallback to `process.stdout.columns`)
131
+ - ANSI color support (TTY-aware, same palette: red/green/aqua/gray/bold)
132
+ - Help sections: Usage box, Options table, Arguments table, Subcommands table, Notes box
133
+ - `cliHelpRender(schema, helpPath, useStderr)` — full help for root or nested command
134
+ - **Depends on Phase 1**
135
+
136
+ ### Phase 5: Shell Completion (completion.ts)
137
+ - `completionBashScript(schema)` — generate bash tab-completion script
138
+ - `completionZshScript(schema)` — generate zsh tab-completion script
139
+ - Same scope-walking algorithm as Swift (depth-first, per-node arrays)
140
+ - **Depends on Phase 1**
141
+
142
+ ### Phase 6: Main Entry Point (index.ts)
143
+ - `cliRun(root: CliCommand)` — orchestrates: validate → merge builtins → parse → validate → dispatch
144
+ - Auto-merge `completion`/`bash`/`zsh` reserved commands
145
+ - Exit code handling (0 for help/success, 1 for errors)
146
+ - **Depends on Phases 2-5**
147
+
148
+ ### Phase 7: Examples & Tests
149
+ - `examples/minimal.ts` — hello world with options (like Swift's Minimal example)
150
+ - `examples/nested.ts` — deeply nested subcommands with positionals (like Swift's Nested example)
151
+ - `src/index.test.ts` — comprehensive tests mirroring Swift's ParseTests
152
+ - Bundled short flags, long option equals, fallback modes
153
+ - Unknown command, implicit help, invalid number validation
154
+ - Completion script generation verification
155
+ - Schema validation (root handler, root positionals, nested fallback, reserved names)
156
+ - **TypeScript-specific**: async handler support, typed option accessors
157
+
158
+ ### Phase 8: Project Polish
159
+ - Add `biome.json` config (lint/format)
160
+ - Add `bin/` entry in `package.json` for CLI executable
161
+ - Add `README.md` with API docs and usage examples
162
+ - Update `package.json` scripts
163
+ - **Parallel with Phase 7**
164
+
165
+ **Relevant files**
166
+ - `/Users/briandombrowski/dev/bdombro/bun-argsbarg/src/index.ts` — replace placeholder `hello()` with `cliRun` + schema types
167
+ - `/Users/briandombrowski/dev/bdombro/bun-argsbarg/src/index.test.ts` — replace with comprehensive test suite
168
+ - `/Users/briandombrowski/dev/bdombro/bun-argsbarg/package.json` — add bin entry, biome config, README
169
+ - `/Users/briandombrowski/dev/bdombro/bun-argsbarg/tsconfig.json` — keep as-is (already correct for Bun)
170
+ - `/Users/briandombrowski/dev/bdombro/bun-argsbarg/examples/local-check.ts` — replace with minimal/nested examples
171
+
172
+ **Verification**
173
+ 1. `bun test` — all tests pass
174
+ 2. `bun ./examples/minimal.ts hello --name World --verbose` — outputs "hello World" with verbose mode
175
+ 3. `bun ./examples/minimal.ts hello --help` — shows formatted help with options table
176
+ 4. `bun ./examples/nested.ts stat owner lookup --user-name bob /etc/passwd` — outputs "lookup user=bob path=/etc/passwd"
177
+ 5. `bun ./examples/minimal.ts completion bash` — outputs valid bash completion script
178
+ 6. `bun ./examples/minimal.ts completion zsh` — outputs valid zsh completion script
179
+ 7. `bun lint` and `bun check-types` — no errors
180
+ 8. `bun ./examples/minimal.ts unknown-cmd` — shows fallback command help with red error
181
+
182
+ **Decisions**
183
+ - **Single-file vs multi-file**: Multi-file (types/parse/context/help/completion) to match Swift's modular structure and keep files manageable
184
+ - **Typed options**: Add `typedOpt<T>(name, parse: (s: string) => T)` to leverage TypeScript's type system — this is the key TypeScript advantage over Swift's string-only approach
185
+ - **Async handlers**: Support `async (ctx) => Promise<void>` handlers — Bun/Node advantage over Swift's synchronous-only
186
+ - **TTY detection**: Use `process.stdout.isTTY` and `process.stdout.columns` (Node built-in) instead of `ioctl`/`isatty` FFI
187
+ - **No runtime dependencies**: Keep zero runtime deps, only `@types/bun` as dev dep
188
+ - **CLI binary**: Add `bin/argsbarg` entry point so the library can also be used as a CLI tool
189
+ - **Schema validation**: Same rules as Swift — root can't have handler/positionals, no duplicate shorts, `-h` reserved, etc.
190
+
191
+ **Further Considerations**
192
+ 1. Should we add a `@argsbarg()` decorator pattern for declarative schema definition (like Python's argparse decorators)? This would be a TypeScript-native enhancement.
193
+ 2. Should the CLI binary (`bin/argsbarg`) support loading schema from a config file (JSON/YAML) for dynamic CLIs?
194
+ 3. Error exit codes: Swift uses exit(1) for help (implicit) and exit(0) for explicit help. Should we match this or use more conventional exit codes?