incur 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -21,6 +21,7 @@
21
21
  - [**Inferred types**](#inferred-types): generic type flow from schemas to `run` callbacks with zero manual annotations
22
22
  - [**Global options**](#global-options): `--format`, `--json`, `--verbose`, `--help`, `--version` on every CLI for free
23
23
  - [**Light API surface**](#light-api-surface): `Cli.create()`, `.command()`, `.serve()` – that's it
24
+ - [**Middleware**](#middleware): composable before/after hooks with typed dependency injection via `cli.use()`
24
25
 
25
26
  ## Quickprompt
26
27
 
@@ -66,8 +67,8 @@ Cli.create('greet', {
66
67
  args: z.object({
67
68
  name: z.string().describe('Name to greet'),
68
69
  }),
69
- run({ args }) {
70
- return { message: `hello ${args.name}` }
70
+ run(c) {
71
+ return { message: `hello ${c.args.name}` }
71
72
  },
72
73
  }).serve()
73
74
  ```
@@ -124,7 +125,7 @@ Cli.create('my-cli', {
124
125
  saveDev: z.boolean().optional().describe('Save as dev dependency'),
125
126
  }),
126
127
  alias: { saveDev: 'D' },
127
- run({ args }) {
128
+ run(c) {
128
129
  return { added: 1, packages: 451 }
129
130
  },
130
131
  })
@@ -176,8 +177,8 @@ const pr = Cli.create('pr', { description: 'Pull request commands' }).command('l
176
177
  options: z.object({
177
178
  state: z.enum(['open', 'closed', 'all']).default('open'),
178
179
  }),
179
- run({ options }) {
180
- return { prs: [], state: options.state }
180
+ run(c) {
181
+ return { prs: [], state: c.options.state }
181
182
  },
182
183
  })
183
184
 
@@ -264,16 +265,19 @@ Return CTAs from `ok()` or `error()` to suggest next steps. `cta` parameters are
264
265
  ```ts
265
266
  cli.command('list', {
266
267
  args: z.object({ state: z.enum(['open', 'closed']).default('open') }),
267
- run({ args, ok }) {
268
+ run(c) {
268
269
  const items = [{ id: 1, title: 'Fix bug' }]
269
- return ok({ items }, {
270
- cta: {
271
- commands: [
272
- { command: 'get 1', description: 'View item' },
273
- { command: 'list', args: { state: 'closed' }, description: 'View closed' },
274
- ],
270
+ return c.ok(
271
+ { items },
272
+ {
273
+ cta: {
274
+ commands: [
275
+ { command: 'get 1', description: 'View item' },
276
+ { command: 'list', args: { state: 'closed' }, description: 'View closed' },
277
+ ],
278
+ },
275
279
  },
276
- })
280
+ )
277
281
  },
278
282
  })
279
283
  ```
@@ -296,17 +300,19 @@ A small API means agents can build entire CLIs in a single pass without needing
296
300
  import { Cli, z } from 'incur'
297
301
 
298
302
  // Define sub-command groups
299
- const db = Cli.create('db', { description: 'Database commands' })
300
- .command('migrate', { description: 'Run migrations', run: () => ({ migrated: true }) })
303
+ const db = Cli.create('db', { description: 'Database commands' }).command('migrate', {
304
+ description: 'Run migrations',
305
+ run: () => ({ migrated: true }),
306
+ })
301
307
 
302
308
  // Create the root CLI
303
309
  Cli.create('tool', { description: 'A tool' })
304
310
  // Register commands
305
311
  .command('run', { description: 'Run a task', run: () => ({ ok: true }) })
306
312
  // Mount sub-command groups
307
- .command(db)
313
+ .command(db)
308
314
  // Serve the CLI
309
- .serve()
315
+ .serve()
310
316
  ```
311
317
 
312
318
  ```sh
@@ -364,8 +370,8 @@ cli.command('deploy', {
364
370
  options: z.object({ force: z.boolean().optional() }),
365
371
  env: z.object({ DEPLOY_TOKEN: z.string() }),
366
372
  output: z.object({ url: z.string(), duration: z.number() }),
367
- run({ args, options, env }) {
368
- return { url: `https://${args.env}.example.com`, duration: 3.2 }
373
+ run(c) {
374
+ return { url: `https://${c.args.env}.example.com`, duration: 3.2 }
369
375
  },
370
376
  })
371
377
  ```
@@ -404,10 +410,10 @@ async *run() {
404
410
  Use `ok()` or `error()` as the return value to attach CTAs or signal failure:
405
411
 
406
412
  ```ts
407
- async *run({ ok }) {
413
+ async *run(c) {
408
414
  yield { step: 1 }
409
415
  yield { step: 2 }
410
- return ok(undefined, { cta: { commands: ['status'] } })
416
+ return c.ok(undefined, { cta: { commands: ['status'] } })
411
417
  }
412
418
  ```
413
419
 
@@ -420,20 +426,158 @@ cli.command('greet', {
420
426
  args: z.object({ name: z.string() }),
421
427
  options: z.object({ loud: z.boolean().default(false) }),
422
428
  output: z.object({ message: z.string() }),
423
- run({ args, options, ok }) {
424
- args.name
425
- // ^? (property) name: string
426
- options.loud
427
- // ^? (property) loud: boolean
428
- return ok({ message: `hello ${args.name}` }, {
429
- // ^? (property) message: string
430
- cta: { commands: ['greet world'] },
431
- // ^? 'greet' | 'other-cmd'
432
- })
429
+ run(c) {
430
+ c.args.name
431
+ // ^? (property) name: string
432
+ c.options.loud
433
+ // ^? (property) loud: boolean
434
+ return c.ok(
435
+ { message: `hello ${c.args.name}` },
436
+ {
437
+ // ^? (property) message: string
438
+ cta: { commands: ['greet world'] },
439
+ // ^? 'greet' | 'other-cmd'
440
+ },
441
+ )
442
+ },
443
+ })
444
+ ```
445
+
446
+ ### Output policy
447
+
448
+ Control whether output data is displayed to humans. By default, output goes to everyone (`'all'`). Set `outputPolicy: 'agent-only'` to suppress data in TTY mode while still returning it to agents via `--json`, `--format`, or `--verbose`.
449
+
450
+ ```ts
451
+ cli.command('deploy', {
452
+ outputPolicy: 'agent-only',
453
+ run() {
454
+ // Agents get the structured data; humans see nothing (or just CTAs/errors)
455
+ return { id: 'deploy-123', url: 'https://staging.example.com' }
456
+ },
457
+ })
458
+ ```
459
+
460
+ Set it on a group or root CLI to inherit across all children:
461
+
462
+ ```ts
463
+ const internal = Cli.create('internal', {
464
+ description: 'Internal commands',
465
+ outputPolicy: 'agent-only',
466
+ })
467
+ internal.command('sync', { run: () => ({ synced: true }) }) // inherits agent-only
468
+ internal.command('status', {
469
+ outputPolicy: 'all', // overrides to show output
470
+ run: () => ({ ok: true }),
471
+ })
472
+ ```
473
+
474
+ ### Agent detection
475
+
476
+ The `run` context includes an `agent` boolean — `true` when stdout is not a TTY (piped or consumed by an agent), `false` when running in a terminal. Use it to tailor behavior:
477
+
478
+ ```ts
479
+ cli.command('deploy', {
480
+ args: z.object({ env: z.enum(['staging', 'production']) }),
481
+ run(c) {
482
+ if (!c.agent) console.log(`Deploying to ${c.args.env}...`)
483
+ return { url: `https://${c.args.env}.example.com` }
484
+ },
485
+ })
486
+ ```
487
+
488
+ ### Middleware
489
+
490
+ Register composable before/after hooks with `cli.use()`. Middleware executes in registration order, onion-style – each calls `await next()` to proceed to the next middleware or the command handler.
491
+
492
+ ```ts
493
+ const cli = Cli.create('deploy-cli', { description: 'Deploy tools' })
494
+ .use(async (c, next) => {
495
+ const start = Date.now()
496
+ await next()
497
+ console.log(`took ${Date.now() - start}ms`)
498
+ })
499
+ .command('deploy', {
500
+ run() {
501
+ return { deployed: true }
502
+ },
503
+ })
504
+ ```
505
+
506
+ ```sh
507
+ $ deploy-cli deploy
508
+ # → deployed: true
509
+ # took 12ms
510
+ ```
511
+
512
+ Per-command middleware runs after root and group middleware, and only for that command:
513
+
514
+ ```ts
515
+ import { Cli, middleware, z } from 'incur'
516
+
517
+ const cli = Cli.create('my-cli', {
518
+ description: 'My CLI',
519
+ vars: z.object({ user: z.custom<User>() }),
520
+ })
521
+
522
+ const requireAuth = middleware<typeof cli.vars>((c, next) => {
523
+ if (!c.var.user) throw new Error('must be logged in')
524
+ return next()
525
+ })
526
+
527
+ cli.command('deploy', {
528
+ middleware: [requireAuth],
529
+ run() {
530
+ return { deployed: true }
531
+ },
532
+ })
533
+ ```
534
+
535
+ ```sh
536
+ $ my-cli deploy
537
+ # Error: must be logged in
538
+
539
+ $ my-cli other-cmd
540
+ # per-command middleware does not run
541
+ ```
542
+
543
+ ### Variables
544
+
545
+ Declare a `vars` schema on `create()` to enable typed variables. Middleware sets them with `c.set()`, and both middleware and command handlers read them via `c.var`. Use `.default()` for vars that don't need middleware:
546
+
547
+ ```ts
548
+ type User = { id: string; name: string }
549
+
550
+ const cli = Cli.create('my-cli', {
551
+ description: 'My CLI',
552
+ vars: z.object({
553
+ user: z.custom<User>(),
554
+ requestId: z.string(),
555
+ debug: z.boolean().default(true),
556
+ }),
557
+ })
558
+
559
+ cli.use(async (c, next) => {
560
+ c.set('user', await authenticate())
561
+ c.set('requestId', crypto.randomUUID())
562
+ await next()
563
+ })
564
+
565
+ cli.command('whoami', {
566
+ run(c) {
567
+ return { user: c.var.user, requestId: c.var.requestId, debug: c.var.debug }
433
568
  },
434
569
  })
435
570
  ```
436
571
 
572
+ ```sh
573
+ $ my-cli whoami
574
+ # → user:
575
+ # → id: u_123
576
+ # → name: Alice
577
+ # → requestId: 550e8400-e29b-41d4-a716-446655440000
578
+ # → debug: true
579
+ ```
580
+
437
581
  ### Global options
438
582
 
439
583
  Every incur CLI includes these flags automatically:
package/SKILL.md CHANGED
@@ -339,22 +339,35 @@ incur adapts output based on whether stdout is a TTY:
339
339
  | `--help` | Pretty help text | Same |
340
340
  | `--json` / `--format` | Overrides to structured | Same |
341
341
 
342
- ## Structured Errors
342
+ ## Run Context
343
343
 
344
- ### `ok()` and `error()` context helpers
344
+ ### `agent` boolean
345
+
346
+ The `run` context includes `agent` — `true` when stdout is not a TTY (piped or consumed by an agent), `false` when running in a terminal:
347
+
348
+ ```ts
349
+ cli.command('deploy', {
350
+ run(c) {
351
+ if (!c.agent) console.log('Deploying...')
352
+ return { status: 'ok' }
353
+ },
354
+ })
355
+ ```
356
+
357
+ ### `ok()` and `error()` helpers
345
358
 
346
359
  Use the context helpers for explicit result control:
347
360
 
348
361
  ```ts
349
- run({ args, ok, error }) {
350
- const item = await db.find(args.id)
362
+ run(c) {
363
+ const item = await db.find(c.args.id)
351
364
  if (!item)
352
365
  return error({
353
366
  code: 'NOT_FOUND',
354
- message: `Item ${args.id} not found`,
367
+ message: `Item ${c.args.id} not found`,
355
368
  retryable: false,
356
369
  })
357
- return ok(item)
370
+ return c.ok(item)
358
371
  }
359
372
  ```
360
373
 
@@ -363,9 +376,9 @@ run({ args, ok, error }) {
363
376
  Suggest next commands to guide agents on success:
364
377
 
365
378
  ```ts
366
- run({ args, ok }) {
367
- const result = { id: 42, name: args.name }
368
- return ok(result, {
379
+ run(c) {
380
+ const result = { id: 42, name: c.args.name }
381
+ return c.ok(result, {
369
382
  cta: {
370
383
  description: 'Suggested commands:',
371
384
  commands: [
@@ -380,10 +393,10 @@ run({ args, ok }) {
380
393
  Or on errors, to help agents self-correct:
381
394
 
382
395
  ```ts
383
- run({ args, error }) {
396
+ run(c) {
384
397
  const token = process.env.GH_TOKEN
385
398
  if (!token)
386
- return error({
399
+ return c.error({
387
400
  code: 'NOT_AUTHENTICATED',
388
401
  message: 'GitHub token not found',
389
402
  retryable: true,
@@ -554,6 +567,141 @@ cli.command('publish', {
554
567
 
555
568
  Hints are displayed after examples in help output and included in skill files.
556
569
 
570
+ ### Output policy
571
+
572
+ Control whether output data is displayed to humans. `'all'` (default) shows output to everyone. `'agent-only'` suppresses data in human/TTY mode while still returning it via `--json`, `--format`, or `--verbose`.
573
+
574
+ ```ts
575
+ cli.command('deploy', {
576
+ outputPolicy: 'agent-only',
577
+ run() {
578
+ return { id: 'deploy-123', url: 'https://staging.example.com' }
579
+ },
580
+ })
581
+ ```
582
+
583
+ Set on a group or root CLI to inherit across children. Children can override:
584
+
585
+ ```ts
586
+ const sub = Cli.create('internal', { outputPolicy: 'agent-only' })
587
+ sub.command('sync', { run: () => ({ synced: true }) }) // inherits agent-only
588
+ sub.command('status', { outputPolicy: 'all', run: () => ({}) }) // overrides
589
+ ```
590
+
591
+ ## Middleware
592
+
593
+ Register composable before/after hooks with `cli.use()`. Middleware executes in registration order, onion-style. Each calls `await next()` to proceed.
594
+
595
+ ```ts
596
+ const cli = Cli.create('deploy-cli', { description: 'Deploy tools' })
597
+ .use(async (c, next) => {
598
+ const start = Date.now()
599
+ await next()
600
+ console.log(`took ${Date.now() - start}ms`)
601
+ })
602
+ .command('deploy', {
603
+ run() {
604
+ return { deployed: true }
605
+ },
606
+ })
607
+ ```
608
+
609
+ ```sh
610
+ $ deploy-cli deploy
611
+ # → deployed: true
612
+ # took 12ms
613
+ ```
614
+
615
+ Middleware on a sub-CLI only applies to its commands:
616
+
617
+ ```ts
618
+ const admin = Cli.create('admin', { description: 'Admin commands' })
619
+ .use(async (c, next) => {
620
+ if (!isAdmin()) throw new Error('forbidden')
621
+ await next()
622
+ })
623
+ .command('reset', { run: () => ({ reset: true }) })
624
+
625
+ cli.command(admin) // middleware only runs for `my-cli admin reset`
626
+ ```
627
+
628
+ ```sh
629
+ $ my-cli admin reset
630
+ # → reset: true
631
+
632
+ $ my-cli other-cmd
633
+ # middleware does not run
634
+ ```
635
+
636
+ Per-command middleware runs after root and group middleware, and only for that command:
637
+
638
+ ```ts
639
+ import { Cli, middleware, z } from 'incur'
640
+
641
+ const cli = Cli.create('my-cli', {
642
+ description: 'My CLI',
643
+ vars: z.object({ user: z.custom<{ id: string }>() }),
644
+ })
645
+
646
+ const requireAuth = middleware<typeof cli.vars>((c, next) => {
647
+ if (!c.var.user) throw new Error('must be logged in')
648
+ return next()
649
+ })
650
+
651
+ cli.command('deploy', {
652
+ middleware: [requireAuth],
653
+ run() {
654
+ return { deployed: true }
655
+ },
656
+ })
657
+ ```
658
+
659
+ ```sh
660
+ $ my-cli deploy
661
+ # Error: must be logged in
662
+
663
+ $ my-cli other-cmd
664
+ # per-command middleware does not run
665
+ ```
666
+
667
+ ### Vars — typed dependency injection
668
+
669
+ Declare a `vars` schema on `create()` to inject typed variables. Middleware sets them with `c.set()`, handlers read them via `c.var`. Use `.default()` for vars that don't need middleware:
670
+
671
+ ```ts
672
+ const cli = Cli.create('my-cli', {
673
+ description: 'My CLI',
674
+ vars: z.object({
675
+ user: z.custom<{ id: string; name: string }>(),
676
+ requestId: z.string(),
677
+ debug: z.boolean().default(false),
678
+ }),
679
+ })
680
+
681
+ cli.use(async (c, next) => {
682
+ c.set('user', await authenticate())
683
+ c.set('requestId', crypto.randomUUID())
684
+ await next()
685
+ })
686
+
687
+ cli.command('whoami', {
688
+ run(c) {
689
+ return { user: c.var.user, requestId: c.var.requestId, debug: c.var.debug }
690
+ },
691
+ })
692
+ ```
693
+
694
+ ```sh
695
+ $ my-cli whoami
696
+ # → user:
697
+ # → id: u_123
698
+ # → name: Alice
699
+ # → requestId: 550e8400-e29b-41d4-a716-446655440000
700
+ # → debug: false
701
+ ```
702
+
703
+ Middleware does **not** run for built-in commands (`--help`, `--llms`, `--mcp`, `mcp add`, `skills add`).
704
+
557
705
  ## Serving
558
706
 
559
707
  Call `.serve()` to parse `process.argv` and run:
package/dist/Cli.d.ts CHANGED
@@ -1,23 +1,25 @@
1
1
  import type { z } from 'zod';
2
2
  import * as Formatter from './Formatter.js';
3
+ import type { Handler as MiddlewareHandler } from './middleware.js';
4
+ export type { MiddlewareHandler };
3
5
  import type { Register } from './Register.js';
4
6
  /** A CLI application instance. Also used as a command group when mounted on a parent CLI. */
5
- export type Cli<commands extends CommandsMap = {}> = {
7
+ export type Cli<commands extends CommandsMap = {}, vars extends z.ZodObject<any> | undefined = undefined> = {
6
8
  /** Registers a root command or mounts a sub-CLI as a command group. */
7
9
  command: {
8
10
  /** Registers a command. Returns the CLI instance for chaining. */
9
- <const name extends string, const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const options extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(name: name, definition: CommandDefinition<args, env, options, output>): Cli<commands & {
11
+ <const name extends string, const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const options extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(name: name, definition: CommandDefinition<args, env, options, output, vars>): Cli<commands & {
10
12
  [key in name]: {
11
13
  args: InferOutput<args>;
12
14
  options: InferOutput<options>;
13
15
  };
14
- }>;
16
+ }, vars>;
15
17
  /** Mounts a sub-CLI as a command group. */
16
- <const name extends string, const sub extends CommandsMap>(cli: Cli<sub> & {
18
+ <const name extends string, const sub extends CommandsMap>(cli: Cli<sub, any> & {
17
19
  name: name;
18
20
  }): Cli<commands & {
19
21
  [key in keyof sub & string as `${name} ${key}`]: sub[key];
20
- }>;
22
+ }, vars>;
21
23
  /** Mounts a root CLI as a single command. */
22
24
  <const name extends string, const args extends z.ZodObject<any> | undefined, const opts extends z.ZodObject<any> | undefined>(cli: Root<args, opts> & {
23
25
  name: name;
@@ -26,7 +28,7 @@ export type Cli<commands extends CommandsMap = {}> = {
26
28
  args: InferOutput<args>;
27
29
  options: InferOutput<opts>;
28
30
  };
29
- }>;
31
+ }, vars>;
30
32
  };
31
33
  /** A short description of the CLI. */
32
34
  description?: string | undefined;
@@ -34,6 +36,10 @@ export type Cli<commands extends CommandsMap = {}> = {
34
36
  name: string;
35
37
  /** Parses argv, runs the matched command, and writes the output envelope to stdout. */
36
38
  serve(argv?: string[], options?: serve.Options): Promise<void>;
39
+ /** Registers middleware that runs around every command. */
40
+ use(handler: MiddlewareHandler<vars>): Cli<commands, vars>;
41
+ /** The vars schema, if declared. Use `typeof cli.vars` with `Middleware.create` for typed middleware. */
42
+ vars: vars;
37
43
  };
38
44
  /** Root CLI — a single command with no subcommands. Carries phantom generics for mounting inference. */
39
45
  export type Root<_args extends z.ZodObject<any> | undefined = undefined, _options extends z.ZodObject<any> | undefined = undefined> = Omit<Cli, 'command'>;
@@ -73,18 +79,18 @@ export type Cta<commands extends CommandsMap = Commands> = ([keyof commands] ext
73
79
  description?: string | undefined;
74
80
  });
75
81
  /** Creates a CLI with a root handler. Can still register subcommands which take precedence. */
76
- export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(name: string, definition: create.Options<args, env, opts, output> & {
82
+ export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined, const vars extends z.ZodObject<any> | undefined = undefined>(name: string, definition: create.Options<args, env, opts, output, vars> & {
77
83
  run: Function;
78
84
  }): Cli<{
79
85
  [key in typeof name]: {
80
86
  args: InferOutput<args>;
81
87
  options: InferOutput<opts>;
82
88
  };
83
- }>;
89
+ }, vars>;
84
90
  /** Creates a router CLI that registers subcommands. */
85
- export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(name: string, definition?: create.Options<args, env, opts, output>): Cli;
91
+ export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined, const vars extends z.ZodObject<any> | undefined = undefined>(name: string, definition?: create.Options<args, env, opts, output, vars>): Cli<{}, vars>;
86
92
  /** Creates a CLI with a root handler from a single options object. Can still register subcommands. */
87
- export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(definition: create.Options<args, env, opts, output> & {
93
+ export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined, const vars extends z.ZodObject<any> | undefined = undefined>(definition: create.Options<args, env, opts, output, vars> & {
88
94
  name: string;
89
95
  run: Function;
90
96
  }): Cli<{
@@ -92,14 +98,14 @@ export declare function create<const args extends z.ZodObject<any> | undefined =
92
98
  args: InferOutput<args>;
93
99
  options: InferOutput<opts>;
94
100
  };
95
- }>;
101
+ }, vars>;
96
102
  /** Creates a router CLI from a single options object (e.g. package.json). */
97
- export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined>(definition: create.Options<args, env, opts, output> & {
103
+ export declare function create<const args extends z.ZodObject<any> | undefined = undefined, const env extends z.ZodObject<any> | undefined = undefined, const opts extends z.ZodObject<any> | undefined = undefined, const output extends z.ZodType | undefined = undefined, const vars extends z.ZodObject<any> | undefined = undefined>(definition: create.Options<args, env, opts, output, vars> & {
98
104
  name: string;
99
- }): Cli;
105
+ }): Cli<{}, vars>;
100
106
  export declare namespace create {
101
107
  /** Options for creating a CLI. Provide `run` for a leaf CLI, omit it for a router. */
102
- type Options<args extends z.ZodObject<any> | undefined = undefined, env extends z.ZodObject<any> | undefined = undefined, options extends z.ZodObject<any> | undefined = undefined, output extends z.ZodType | undefined = undefined> = {
108
+ type Options<args extends z.ZodObject<any> | undefined = undefined, env extends z.ZodObject<any> | undefined = undefined, options extends z.ZodObject<any> | undefined = undefined, output extends z.ZodType | undefined = undefined, vars extends z.ZodObject<any> | undefined = undefined> = {
103
109
  /** Map of option names to single-char aliases. */
104
110
  alias?: options extends z.ZodObject<any> ? Partial<Record<keyof z.output<options>, string>> : Record<string, string> | undefined;
105
111
  /** Zod schema for positional arguments. */
@@ -116,10 +122,24 @@ export declare namespace create {
116
122
  options?: options | undefined;
117
123
  /** Zod schema for the return value. */
118
124
  output?: output | undefined;
125
+ /**
126
+ * Controls when output data is displayed. Inherited by child commands when set on a group or root CLI.
127
+ *
128
+ * - `'all'` — displays to both humans and agents.
129
+ * - `'agent-only'` — suppresses data output in human/TTY mode while still returning it to agents.
130
+ *
131
+ * @default 'all'
132
+ */
133
+ outputPolicy?: OutputPolicy | undefined;
119
134
  /** Alternative usage patterns shown in help output. */
120
135
  usage?: Usage<args, options>[] | undefined;
136
+ /** Zod schema for middleware variables. Keys define variable names, schemas define types and defaults. */
137
+ vars?: vars | undefined;
121
138
  /** The root command handler. When provided, creates a leaf CLI with no subcommands. */
122
139
  run?: ((context: {
140
+ /** Whether the consumer is an agent (stdout is not a TTY). */
141
+ agent: boolean;
142
+ /** Positional arguments. */
123
143
  args: InferOutput<args>;
124
144
  /** Parsed environment variables. */
125
145
  env: InferOutput<env>;
@@ -135,6 +155,8 @@ export declare namespace create {
135
155
  cta?: CtaBlock | undefined;
136
156
  }) => never;
137
157
  options: InferOutput<options>;
158
+ /** Variables set by middleware. */
159
+ var: InferVars<vars>;
138
160
  }) => InferReturn<output> | Promise<InferReturn<output>> | AsyncGenerator<InferReturn<output>, unknown, unknown>) | undefined;
139
161
  /** Options for the built-in `mcp add` command. */
140
162
  mcp?: {
@@ -176,14 +198,18 @@ export type CommandsMap = Record<string, {
176
198
  }>;
177
199
  /** @internal Entry stored in a command map — either a leaf definition or a group. */
178
200
  type CommandEntry = CommandDefinition<any, any, any> | InternalGroup;
201
+ /** Controls when output data is displayed. `'all'` displays to both humans and agents. `'agent-only'` suppresses data output in human/TTY mode. */
202
+ export type OutputPolicy = 'agent-only' | 'all';
179
203
  /** @internal A command group's internal storage. */
180
204
  type InternalGroup = {
181
205
  _group: true;
182
206
  description?: string | undefined;
207
+ middlewares?: MiddlewareHandler[] | undefined;
208
+ outputPolicy?: OutputPolicy | undefined;
183
209
  commands: Map<string, CommandEntry>;
184
210
  };
185
211
  /** @internal Maps CLI instances to their command maps. */
186
- export declare const toCommands: WeakMap<Cli<{}>, Map<string, CommandEntry>>;
212
+ export declare const toCommands: WeakMap<Cli<{}, undefined>, Map<string, CommandEntry>>;
187
213
  /** @internal A CTA block with a description and list of suggested commands. */
188
214
  type CtaBlock<commands extends CommandsMap = Commands> = {
189
215
  /** Commands to suggest. */
@@ -220,8 +246,10 @@ type Usage<args extends z.ZodObject<any> | undefined, options extends z.ZodObjec
220
246
  type InferOutput<schema extends z.ZodObject<any> | undefined> = schema extends z.ZodObject<any> ? z.output<schema> : {};
221
247
  /** @internal Inferred return type for a command handler. */
222
248
  type InferReturn<output extends z.ZodType | undefined> = output extends z.ZodType ? z.output<output> : unknown;
249
+ /** @internal Inferred vars type from a Zod schema, or `{}` when no schema is provided. */
250
+ type InferVars<vars extends z.ZodObject<any> | undefined> = vars extends z.ZodObject<any> ? z.output<vars> : {};
223
251
  /** @internal Defines a command's schema, handler, and metadata. */
224
- type CommandDefinition<args extends z.ZodObject<any> | undefined = undefined, env extends z.ZodObject<any> | undefined = undefined, options extends z.ZodObject<any> | undefined = undefined, output extends z.ZodType | undefined = undefined> = {
252
+ type CommandDefinition<args extends z.ZodObject<any> | undefined = undefined, env extends z.ZodObject<any> | undefined = undefined, options extends z.ZodObject<any> | undefined = undefined, output extends z.ZodType | undefined = undefined, vars extends z.ZodObject<any> | undefined = undefined> = {
225
253
  /** Map of option names to single-char aliases. */
226
254
  alias?: options extends z.ZodObject<any> ? Partial<Record<keyof z.output<options>, string>> : Record<string, string> | undefined;
227
255
  /** Zod schema for positional arguments. */
@@ -240,10 +268,24 @@ type CommandDefinition<args extends z.ZodObject<any> | undefined = undefined, en
240
268
  options?: options | undefined;
241
269
  /** Zod schema for the command's return value. */
242
270
  output?: output | undefined;
271
+ /**
272
+ * Controls when output data is displayed. Inherited by child commands when set on a group.
273
+ *
274
+ * - `'all'` — displays to both humans and agents.
275
+ * - `'agent-only'` — suppresses data output in human/TTY mode while still returning it to agents.
276
+ *
277
+ * @default 'all'
278
+ */
279
+ outputPolicy?: OutputPolicy | undefined;
280
+ /** Middleware that runs only for this command, after root and group middleware. */
281
+ middleware?: MiddlewareHandler<vars>[] | undefined;
243
282
  /** Alternative usage patterns shown in help output. */
244
283
  usage?: Usage<args, options>[] | undefined;
245
284
  /** The command handler. Return a value for single-return, or use `async *run` to stream chunks. */
246
285
  run(context: {
286
+ /** Whether the consumer is an agent (stdout is not a TTY). */
287
+ agent: boolean;
288
+ /** Positional arguments. */
247
289
  args: InferOutput<args>;
248
290
  /** Parsed environment variables. */
249
291
  env: InferOutput<env>;
@@ -259,7 +301,8 @@ type CommandDefinition<args extends z.ZodObject<any> | undefined = undefined, en
259
301
  cta?: CtaBlock | undefined;
260
302
  }) => never;
261
303
  options: InferOutput<options>;
304
+ /** Variables set by middleware. */
305
+ var: InferVars<vars>;
262
306
  }): InferReturn<output> | Promise<InferReturn<output>> | AsyncGenerator<InferReturn<output>, unknown, unknown>;
263
307
  };
264
- export {};
265
308
  //# sourceMappingURL=Cli.d.ts.map