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.
- package/.cursor/rules/code.mdc +9 -0
- package/README.md +188 -0
- package/biome.json +17 -0
- package/bun.lock +21 -0
- package/docs/completions-preview.png +0 -0
- package/docs/help-l2-preview.png +0 -0
- package/docs/help-preview.png +0 -0
- package/examples/minimal.ts +41 -0
- package/examples/nested.ts +87 -0
- package/logo.png +0 -0
- package/package.json +25 -0
- package/plan.md +194 -0
- package/src/completion.ts +523 -0
- package/src/context.ts +67 -0
- package/src/help.ts +429 -0
- package/src/index.test.ts +255 -0
- package/src/index.ts +24 -0
- package/src/parse.ts +487 -0
- package/src/runtime.ts +113 -0
- package/src/types.ts +114 -0
- package/src/utils.ts +24 -0
- package/src/validate.ts +136 -0
- package/tsconfig.json +12 -0
|
@@ -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
|
+

|
|
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
|
+
[](https://github.com/bdombro/bun-argsbarg)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/argsbarg)
|
|
7
|
+
[](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
|
+

|
|
21
|
+
|
|
22
|
+
Sub-level Halps! -->
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
Shell completions! -->
|
|
26
|
+

|
|
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?
|