@politty/zod 0.0.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/LICENSE +21 -0
- package/README.md +561 -0
- package/bin/cli.mjs +3 -0
- package/dist/arg-registry-YaWTVu_x.d.ts +1025 -0
- package/dist/augment.d.ts +15 -0
- package/dist/augment.js +1 -0
- package/dist/cli-main-Dn88vIyn.js +84 -0
- package/dist/cli-run-eibUcgys.js +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +16 -0
- package/dist/command-k-4yAz4J.js +42 -0
- package/dist/compile-cache-Ct41pWGL.js +100 -0
- package/dist/compile-cache.d.ts +78 -0
- package/dist/compile-cache.js +3 -0
- package/dist/completion-gtWX3mwP.js +5608 -0
- package/dist/completion.d.ts +242 -0
- package/dist/completion.js +4 -0
- package/dist/docs.d.ts +770 -0
- package/dist/docs.js +3044 -0
- package/dist/field-meta-DMy5BcRr.js +146 -0
- package/dist/index-CvhsecfS.d.ts +455 -0
- package/dist/index.d.ts +799 -0
- package/dist/index.js +17 -0
- package/dist/log-collector-CoUkLVJB.js +114 -0
- package/dist/logger-i_bb-Jhc.js +133 -0
- package/dist/prompt-CEIZ-7H1.js +171 -0
- package/dist/prompt-clack.d.ts +16 -0
- package/dist/prompt-clack.js +32 -0
- package/dist/prompt-inquirer.d.ts +16 -0
- package/dist/prompt-inquirer.js +47 -0
- package/dist/prompt.d.ts +106 -0
- package/dist/prompt.js +4 -0
- package/dist/register-Bk0K83W2.js +439 -0
- package/dist/runner-D72I7wvK.js +2956 -0
- package/dist/runner-FvUwOHyE.js +3 -0
- package/dist/schema-extractor-DMSozq40.js +250 -0
- package/dist/skill.d.ts +608 -0
- package/dist/skill.js +1832 -0
- package/dist/src-KzC0g5CS.js +191 -0
- package/dist/subcommand-router-Cskpofdk.js +134 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 toiroakr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# politty
|
|
2
|
+
|
|
3
|
+
**politty** is a lightweight, type-safe CLI framework for Node.js built on **Zod v4**.
|
|
4
|
+
|
|
5
|
+
From simple scripts to complex CLI tools with subcommands, validation, and auto-generated help, you can build them all with a developer-friendly API.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Zod Native**: Use Zod schemas directly for argument definition and validation
|
|
10
|
+
- **Type Safety**: Full TypeScript support with automatic type inference for parsed arguments
|
|
11
|
+
- **Flexible Argument Definition**: Support for positional arguments, flags, aliases, arrays, and environment variable fallbacks
|
|
12
|
+
- **Subcommands**: Build Git-style nested subcommands (with lazy loading and alias support)
|
|
13
|
+
- **Lifecycle Management**: Guaranteed `setup` → `run` → `cleanup` execution order
|
|
14
|
+
- **Signal Handling**: Proper SIGINT/SIGTERM handling with guaranteed cleanup execution
|
|
15
|
+
- **Auto Help Generation**: Automatically generate help text from definitions
|
|
16
|
+
- **Interactive Prompts**: Prompt for missing arguments with pluggable adapters (clack, inquirer)
|
|
17
|
+
- **Discriminated Union**: Support for mutually exclusive argument sets
|
|
18
|
+
- **Skill Management**: Manage agent skills (SKILL.md) with file-based install/uninstall
|
|
19
|
+
- **Fast Startup**: Automatic Node.js compile cache (V8 code cache) integration, with a `generate-shim` command to cache the whole CLI graph
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Node.js >= 20.12.0 (excluding 21.0.0 - 21.6.x)
|
|
24
|
+
- Zod >= 4.2.1
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install politty zod
|
|
30
|
+
# or
|
|
31
|
+
pnpm add politty zod
|
|
32
|
+
# or
|
|
33
|
+
yarn add politty zod
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Using valibot instead of zod
|
|
37
|
+
|
|
38
|
+
politty is also available as [`@politty/valibot`](https://www.npmjs.com/package/@politty/valibot), built on [valibot](https://valibot.dev/) schemas — same API (`defineCommand`, `arg`, `runMain`, and all subpath modules), and its published build never loads zod:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @politty/valibot valibot
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import * as v from "valibot";
|
|
46
|
+
import { defineCommand, runMain, arg } from "@politty/valibot";
|
|
47
|
+
|
|
48
|
+
const command = defineCommand({
|
|
49
|
+
name: "greet",
|
|
50
|
+
args: v.object({
|
|
51
|
+
name: arg(v.string(), { description: "Name to greet", positional: true }),
|
|
52
|
+
loud: arg(v.optional(v.boolean(), false), { alias: "l" }),
|
|
53
|
+
}),
|
|
54
|
+
run: (args) => {
|
|
55
|
+
const greeting = `Hello, ${args.name}!`;
|
|
56
|
+
console.log(args.loud ? greeting.toUpperCase() : greeting);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
runMain(command);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Field descriptions can also come from valibot's own metadata actions. They are actions, not schemas, so they belong inside `v.pipe(...)`: `v.pipe(v.string(), v.description("..."))` or `v.pipe(v.string(), v.metadata({ description: "..." }))`. The one zod-only feature without a valibot counterpart is the `politty/augment` module (zod `GlobalMeta` interface augmentation) — use `arg()` or `v.metadata()` instead. The `politty` package itself remains the zod flavor (an alias of `@politty/zod`).
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { z } from "zod";
|
|
69
|
+
import { defineCommand, runMain, arg } from "politty";
|
|
70
|
+
|
|
71
|
+
const command = defineCommand({
|
|
72
|
+
name: "greet",
|
|
73
|
+
description: "A CLI tool that displays greetings",
|
|
74
|
+
args: z.object({
|
|
75
|
+
name: arg(z.string(), {
|
|
76
|
+
positional: true,
|
|
77
|
+
description: "Name of the person to greet",
|
|
78
|
+
}),
|
|
79
|
+
greeting: arg(z.string().default("Hello"), {
|
|
80
|
+
alias: "g",
|
|
81
|
+
description: "Greeting phrase",
|
|
82
|
+
}),
|
|
83
|
+
loud: arg(z.boolean().default(false), {
|
|
84
|
+
alias: "l",
|
|
85
|
+
description: "Output in uppercase",
|
|
86
|
+
}),
|
|
87
|
+
}),
|
|
88
|
+
run: (args) => {
|
|
89
|
+
let message = `${args.greeting}, ${args.name}!`;
|
|
90
|
+
if (args.loud) {
|
|
91
|
+
message = message.toUpperCase();
|
|
92
|
+
}
|
|
93
|
+
console.log(message);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
runMain(command);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Example usage:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
$ my-cli World
|
|
104
|
+
Hello, World!
|
|
105
|
+
|
|
106
|
+
$ my-cli World -g "Hi" -l
|
|
107
|
+
HI, WORLD!
|
|
108
|
+
|
|
109
|
+
$ my-cli --help
|
|
110
|
+
Usage: greet <name> [options]
|
|
111
|
+
|
|
112
|
+
A CLI tool that displays greetings
|
|
113
|
+
|
|
114
|
+
Arguments:
|
|
115
|
+
name Name of the person to greet
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
-g, --greeting <value> Greeting phrase (default: "Hello")
|
|
119
|
+
-l, --loud Output in uppercase
|
|
120
|
+
-h, --help Show help
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Basic Usage
|
|
124
|
+
|
|
125
|
+
### Defining Arguments
|
|
126
|
+
|
|
127
|
+
Use the `arg()` function to define argument metadata:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { z } from "zod";
|
|
131
|
+
import { arg, defineCommand } from "politty";
|
|
132
|
+
|
|
133
|
+
const command = defineCommand({
|
|
134
|
+
name: "example",
|
|
135
|
+
args: z.object({
|
|
136
|
+
// Positional argument (required)
|
|
137
|
+
input: arg(z.string(), {
|
|
138
|
+
positional: true,
|
|
139
|
+
description: "Input file",
|
|
140
|
+
}),
|
|
141
|
+
|
|
142
|
+
// Optional positional argument
|
|
143
|
+
output: arg(z.string().optional(), {
|
|
144
|
+
positional: true,
|
|
145
|
+
description: "Output file",
|
|
146
|
+
}),
|
|
147
|
+
|
|
148
|
+
// Flag (with alias)
|
|
149
|
+
verbose: arg(z.boolean().default(false), {
|
|
150
|
+
alias: "v",
|
|
151
|
+
description: "Verbose output",
|
|
152
|
+
}),
|
|
153
|
+
|
|
154
|
+
// Environment variable fallback
|
|
155
|
+
apiKey: arg(z.string().optional(), {
|
|
156
|
+
env: "API_KEY",
|
|
157
|
+
description: "API key",
|
|
158
|
+
}),
|
|
159
|
+
|
|
160
|
+
// Array argument (--file a.txt --file b.txt)
|
|
161
|
+
files: arg(z.array(z.string()).default([]), {
|
|
162
|
+
alias: "f",
|
|
163
|
+
description: "Files to process",
|
|
164
|
+
}),
|
|
165
|
+
}),
|
|
166
|
+
run: (args) => {
|
|
167
|
+
console.log(args);
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Use `args.$source(name)` to check whether a value came from an explicit CLI token, an `env` fallback, or neither (e.g. a schema default). It's typed as optional since directly-constructed args objects (e.g. in unit tests) won't have it:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
run: (args) => {
|
|
176
|
+
args.$source?.("apiKey"); // "cli" | "env" | "default"
|
|
177
|
+
},
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Subcommands
|
|
181
|
+
|
|
182
|
+
Define Git-style subcommands:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { z } from "zod";
|
|
186
|
+
import { arg, defineCommand, runMain } from "politty";
|
|
187
|
+
|
|
188
|
+
const initCommand = defineCommand({
|
|
189
|
+
name: "init",
|
|
190
|
+
description: "Initialize a project",
|
|
191
|
+
aliases: ["i"],
|
|
192
|
+
args: z.object({
|
|
193
|
+
template: arg(z.string().default("default"), {
|
|
194
|
+
alias: "t",
|
|
195
|
+
description: "Template name",
|
|
196
|
+
}),
|
|
197
|
+
}),
|
|
198
|
+
run: (args) => {
|
|
199
|
+
console.log(`Initializing with template: ${args.template}`);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const buildCommand = defineCommand({
|
|
204
|
+
name: "build",
|
|
205
|
+
description: "Build the project",
|
|
206
|
+
aliases: ["b"],
|
|
207
|
+
args: z.object({
|
|
208
|
+
output: arg(z.string().default("dist"), {
|
|
209
|
+
alias: "o",
|
|
210
|
+
description: "Output directory",
|
|
211
|
+
}),
|
|
212
|
+
minify: arg(z.boolean().default(false), {
|
|
213
|
+
alias: "m",
|
|
214
|
+
description: "Minify output",
|
|
215
|
+
}),
|
|
216
|
+
}),
|
|
217
|
+
run: (args) => {
|
|
218
|
+
console.log(`Building to: ${args.output}`);
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const cli = defineCommand({
|
|
223
|
+
name: "my-cli",
|
|
224
|
+
description: "Example CLI with subcommands",
|
|
225
|
+
subCommands: {
|
|
226
|
+
init: initCommand,
|
|
227
|
+
build: buildCommand,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
runMain(cli, { version: "1.0.0" });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Example usage:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
$ my-cli init -t react
|
|
238
|
+
$ my-cli i -t react # alias for init
|
|
239
|
+
$ my-cli build -o out -m
|
|
240
|
+
$ my-cli b -o out -m # alias for build
|
|
241
|
+
$ my-cli --help
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Lifecycle Hooks
|
|
245
|
+
|
|
246
|
+
Execute hooks in `setup` → `run` → `cleanup` order. The `cleanup` hook is always executed, even if an error occurs:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const command = defineCommand({
|
|
250
|
+
name: "db-query",
|
|
251
|
+
description: "Execute database queries",
|
|
252
|
+
args: z.object({
|
|
253
|
+
database: arg(z.string(), {
|
|
254
|
+
alias: "d",
|
|
255
|
+
description: "Database connection string",
|
|
256
|
+
}),
|
|
257
|
+
query: arg(z.string(), {
|
|
258
|
+
alias: "q",
|
|
259
|
+
description: "SQL query",
|
|
260
|
+
}),
|
|
261
|
+
}),
|
|
262
|
+
setup: async ({ args }) => {
|
|
263
|
+
console.log("[setup] Connecting to database...");
|
|
264
|
+
// Establish DB connection
|
|
265
|
+
},
|
|
266
|
+
run: async (args) => {
|
|
267
|
+
console.log("[run] Executing query...");
|
|
268
|
+
// Execute query
|
|
269
|
+
return { rowCount: 42 };
|
|
270
|
+
},
|
|
271
|
+
cleanup: async ({ args, error }) => {
|
|
272
|
+
console.log("[cleanup] Closing connection...");
|
|
273
|
+
if (error) {
|
|
274
|
+
console.error(`Error occurred: ${error.message}`);
|
|
275
|
+
}
|
|
276
|
+
// Close connection
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## API
|
|
282
|
+
|
|
283
|
+
### `defineCommand(options)`
|
|
284
|
+
|
|
285
|
+
Define a command.
|
|
286
|
+
|
|
287
|
+
| Option | Type | Description |
|
|
288
|
+
| ------------- | ----------------------------- | ------------------- |
|
|
289
|
+
| `name` | `string` | Command name |
|
|
290
|
+
| `description` | `string?` | Command description |
|
|
291
|
+
| `args` | `ZodSchema` | Argument schema |
|
|
292
|
+
| `aliases` | `string[]?` | Command aliases |
|
|
293
|
+
| `subCommands` | `Record<string, Command>?` | Subcommands |
|
|
294
|
+
| `setup` | `(context) => Promise<void>?` | Setup hook |
|
|
295
|
+
| `run` | `(args) => T?` | Run function |
|
|
296
|
+
| `cleanup` | `(context) => Promise<void>?` | Cleanup hook |
|
|
297
|
+
|
|
298
|
+
### `runMain(command, options?)`
|
|
299
|
+
|
|
300
|
+
CLI entry point. Handles signals and calls `process.exit()`.
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
runMain(command, {
|
|
304
|
+
version: "1.0.0", // Displayed with --version flag
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### `runCommand(command, argv, options?)`
|
|
309
|
+
|
|
310
|
+
Programmatic/testing entry point. Does not call `process.exit()` and returns a result object.
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const result = await runCommand(command, ["arg1", "--flag"]);
|
|
314
|
+
if (result.success) {
|
|
315
|
+
console.log(result.result);
|
|
316
|
+
} else {
|
|
317
|
+
console.error(result.error);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### `arg(schema, meta)`
|
|
322
|
+
|
|
323
|
+
Attach metadata to an argument.
|
|
324
|
+
|
|
325
|
+
| Metadata | Type | Description |
|
|
326
|
+
| ------------- | ------------- | ------------------------------------------------------------------------ |
|
|
327
|
+
| `positional` | `boolean?` | Treat as positional argument |
|
|
328
|
+
| `alias` | `string?` | Short alias (e.g., `-v`) |
|
|
329
|
+
| `description` | `string?` | Argument description |
|
|
330
|
+
| `placeholder` | `string?` | Placeholder shown in help |
|
|
331
|
+
| `env` | `string?` | Environment variable name (fallback) |
|
|
332
|
+
| `completion` | `object?` | Shell completion configuration |
|
|
333
|
+
| `prompt` | `PromptMeta?` | Interactive prompt configuration ([docs](./docs/interactive-prompts.md)) |
|
|
334
|
+
|
|
335
|
+
## Shell Completion
|
|
336
|
+
|
|
337
|
+
politty provides automatic shell completion generation for bash, zsh, and fish.
|
|
338
|
+
The default generated script is a small runtime dispatcher: when the user
|
|
339
|
+
presses TAB, it resolves the executable currently visible on `PATH` and uses a
|
|
340
|
+
bundled static worker from that executable's package when one is available. If
|
|
341
|
+
no package-relative bundled worker is found, it falls back to a per-binary
|
|
342
|
+
static worker cache and regenerates that cache from the hidden
|
|
343
|
+
`__refresh-completion` command when needed. Sourced workers are memoized for the
|
|
344
|
+
shell session, so warm completions call the worker function directly.
|
|
345
|
+
Completion fields that require runtime JavaScript still delegate to the
|
|
346
|
+
binary's hidden `__complete` command.
|
|
347
|
+
When `NODE_COMPILE_CACHE` is unset, the dispatcher sets it to a
|
|
348
|
+
program-specific cache directory before invoking `__complete`, letting Node.js
|
|
349
|
+
22+ reuse V8 module compile cache across repeated completion requests.
|
|
350
|
+
|
|
351
|
+
### Quick Setup
|
|
352
|
+
|
|
353
|
+
Use `withCompletionCommand` to add completion support to your CLI:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import { defineCommand, runMain, withCompletionCommand } from "politty";
|
|
357
|
+
|
|
358
|
+
const mainCommand = withCompletionCommand(
|
|
359
|
+
defineCommand({
|
|
360
|
+
name: "mycli",
|
|
361
|
+
subCommands: {
|
|
362
|
+
build: buildCommand,
|
|
363
|
+
test: testCommand,
|
|
364
|
+
},
|
|
365
|
+
}),
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
runMain(mainCommand);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Then users can enable completions:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# Bash
|
|
375
|
+
eval "$(mycli completion bash)"
|
|
376
|
+
|
|
377
|
+
# Zsh
|
|
378
|
+
eval "$(mycli completion zsh)"
|
|
379
|
+
|
|
380
|
+
# Fish
|
|
381
|
+
mycli completion fish | source
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
For project-local CLIs, put the local binary directory in `PATH` with your
|
|
385
|
+
environment manager, for example:
|
|
386
|
+
|
|
387
|
+
```sh
|
|
388
|
+
# .envrc
|
|
389
|
+
PATH_add node_modules/.bin
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Completion then follows the same executable the shell would run.
|
|
393
|
+
|
|
394
|
+
Published CLIs can ship a fast worker artifact:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
politty generate-worker --bin dist/cli/index.mjs --program mycli --shell zsh --verify
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
or call `generateBundledCompletionWorker()` from `politty/completion` in your
|
|
401
|
+
own build script. The default output path is
|
|
402
|
+
`dist/completion/<shell>-worker.<ext>`.
|
|
403
|
+
|
|
404
|
+
The dispatcher looks for common package-relative worker paths such as
|
|
405
|
+
`dist/completion/zsh-worker.zsh` from the visible binary. Use
|
|
406
|
+
`withCompletionCommand({ bundledWorker: { relativePaths: { zsh: [...] } } })`
|
|
407
|
+
to customize the package-relative lookup. For package layouts that cannot be
|
|
408
|
+
expressed with relative paths, `bundledWorker.queryCommand: true` lets the
|
|
409
|
+
dispatcher ask the binary's hidden `__completion-worker-path` command on the
|
|
410
|
+
miss path.
|
|
411
|
+
|
|
412
|
+
To generate the older static script with command metadata baked in, use:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
eval "$(mycli completion bash --static)"
|
|
416
|
+
eval "$(mycli completion zsh --static)"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Value Completion
|
|
420
|
+
|
|
421
|
+
Define completion hints for arguments:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
const command = defineCommand({
|
|
425
|
+
name: "build",
|
|
426
|
+
args: z.object({
|
|
427
|
+
// Auto-detected from z.enum()
|
|
428
|
+
format: arg(z.enum(["json", "yaml", "xml"]), {
|
|
429
|
+
alias: "f",
|
|
430
|
+
description: "Output format",
|
|
431
|
+
}),
|
|
432
|
+
|
|
433
|
+
// File completion
|
|
434
|
+
config: arg(z.string(), {
|
|
435
|
+
completion: { type: "file", extensions: ["json", "yaml"] },
|
|
436
|
+
}),
|
|
437
|
+
|
|
438
|
+
// Directory completion
|
|
439
|
+
outputDir: arg(z.string(), {
|
|
440
|
+
completion: { type: "directory" },
|
|
441
|
+
}),
|
|
442
|
+
|
|
443
|
+
// Custom shell command
|
|
444
|
+
branch: arg(z.string().optional(), {
|
|
445
|
+
completion: {
|
|
446
|
+
custom: { shellCommand: "git branch --format='%(refname:short)'" },
|
|
447
|
+
},
|
|
448
|
+
}),
|
|
449
|
+
|
|
450
|
+
// Static choices
|
|
451
|
+
environment: arg(z.string(), {
|
|
452
|
+
completion: {
|
|
453
|
+
custom: { choices: ["development", "staging", "production"] },
|
|
454
|
+
},
|
|
455
|
+
}),
|
|
456
|
+
}),
|
|
457
|
+
run: (args) => {
|
|
458
|
+
/* ... */
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Faster Startup (Compile Cache)
|
|
464
|
+
|
|
465
|
+
politty integrates Node.js's on-disk compile cache (V8 code cache, Node >= 22.8.0) so warm starts skip recompilation; older runtimes are a silent no-op. `runMain` enables it automatically, which covers dynamically imported modules such as `lazy()` subcommands. To cache the whole CLI graph — politty, zod, and your command definitions — generate a bin shim as part of your build:
|
|
466
|
+
|
|
467
|
+
```jsonc
|
|
468
|
+
// package.json
|
|
469
|
+
{
|
|
470
|
+
"bin": { "my-cli": "./dist/bin.js" },
|
|
471
|
+
"scripts": {
|
|
472
|
+
"build": "tsdown", // builds src/cli.ts -> dist/cli.js
|
|
473
|
+
"postbuild": "politty generate-shim --entry ./cli.js",
|
|
474
|
+
},
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
`generate-shim` writes an executable shim that enables the cache and loads `--entry` (a specifier relative to the shim) with a dynamic import. The rest is derived from `package.json`: the output path from the `bin` path and the program name from the `bin` name; packages with multiple bins pass `--entry` once per bin, and `--entry` itself can be omitted when the entry sits next to the shim under a conventional name (`./cli.js`, `./index.js`, ...). See [Faster Startup (Compile Cache)](./docs/recipes.md#faster-startup-compile-cache) for the flags, the opt-out (`compileCache: false`), and the hand-written shim variant.
|
|
479
|
+
|
|
480
|
+
## Skill Management
|
|
481
|
+
|
|
482
|
+
politty manages SKILL.md-based agent skills distributed via npm packages.
|
|
483
|
+
|
|
484
|
+
### Quick Setup
|
|
485
|
+
|
|
486
|
+
Use `withSkillCommand` to add skill management to your CLI:
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
import { dirname, resolve } from "node:path";
|
|
490
|
+
import { fileURLToPath } from "node:url";
|
|
491
|
+
import { defineCommand, runMain } from "politty";
|
|
492
|
+
import { withSkillCommand } from "politty/skill";
|
|
493
|
+
|
|
494
|
+
// Resolves to ../skills from both src/ and dist/
|
|
495
|
+
const sourceDir = resolve(dirname(fileURLToPath(import.meta.url)), "../skills");
|
|
496
|
+
|
|
497
|
+
const cli = withSkillCommand(
|
|
498
|
+
defineCommand({
|
|
499
|
+
name: "my-agent",
|
|
500
|
+
subCommands: {/* ... */},
|
|
501
|
+
}),
|
|
502
|
+
{ sourceDir, package: "@my-agent/skills" },
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
runMain(cli);
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
`package` identifies who owns these skills. It is combined with the command name as `"{package}:{cliName}"` and must match the `metadata["politty-cli"]` stamp pre-declared in each source SKILL.md — `skills add`/`sync` refuse mismatches, and `remove`/`sync` refuse to delete skills belonging to another tool. The default install mode is `"symlink"` (`.agents/skills/<name>` -> source, `.claude/skills/<name>` -> canonical), so source updates propagate live; on filesystems without symlink support (e.g. Windows without Developer Mode) install throws with guidance to retry with `mode: "copy"`, which recursively copies instead (source updates then require re-running `sync`). See [Skill Management](./docs/skill-management.md) for details.
|
|
509
|
+
|
|
510
|
+
Skills are SKILL.md files with YAML frontmatter (spec-compliant: https://agentskills.io/specification). The `metadata["politty-cli"]` stamp is authored by the skill package:
|
|
511
|
+
|
|
512
|
+
```markdown
|
|
513
|
+
---
|
|
514
|
+
name: commit
|
|
515
|
+
description: Git commit message generation
|
|
516
|
+
license: MIT
|
|
517
|
+
metadata:
|
|
518
|
+
politty-cli: "@my-agent/skills:my-agent"
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
# Instructions for the agent...
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Then users can manage skills:
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
my-agent skills sync # Remove and reinstall all skills
|
|
528
|
+
my-agent skills add commit # Install a specific skill
|
|
529
|
+
my-agent skills remove commit # Remove a specific skill
|
|
530
|
+
my-agent skills list # List available skills
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Documentation
|
|
534
|
+
|
|
535
|
+
For detailed documentation, see the `docs/` directory:
|
|
536
|
+
|
|
537
|
+
- [Getting Started](./docs/getting-started.md) - Installation and creating your first command
|
|
538
|
+
- [Essentials](./docs/essentials.md) - Core concepts explained
|
|
539
|
+
- [Advanced Features](./docs/advanced-features.md) - Subcommands, Discriminated Union
|
|
540
|
+
- [Interactive Prompts](./docs/interactive-prompts.md) - Prompt for missing arguments interactively
|
|
541
|
+
- [Recipes](./docs/recipes.md) - Testing, configuration, error handling, faster startup (compile cache)
|
|
542
|
+
- [Skill Management](./docs/skill-management.md) - Agent skill management (SKILL.md-based)
|
|
543
|
+
- [API Reference](./docs/api-reference.md) - Detailed API reference
|
|
544
|
+
- [Doc Generation](./docs/doc-generation.md) - Automatic documentation generation
|
|
545
|
+
|
|
546
|
+
## Examples
|
|
547
|
+
|
|
548
|
+
The `playground/` directory contains many examples:
|
|
549
|
+
|
|
550
|
+
- `01-hello-world` - Minimal command configuration
|
|
551
|
+
- `02-greet` - Positional arguments and flags
|
|
552
|
+
- `03-array-args` - Array arguments
|
|
553
|
+
- `05-lifecycle-hooks` - Lifecycle hooks
|
|
554
|
+
- `10-subcommands` - Subcommands
|
|
555
|
+
- `12-discriminated-union` - Discriminated Union
|
|
556
|
+
- `21-lazy-subcommands` - Lazy loading
|
|
557
|
+
- `26-command-alias` - Command aliases
|
|
558
|
+
|
|
559
|
+
## License
|
|
560
|
+
|
|
561
|
+
MIT
|
package/bin/cli.mjs
ADDED