incur 0.2.1 → 0.3.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/src/Cli.test.ts CHANGED
@@ -378,12 +378,12 @@ describe('serve', () => {
378
378
  })
379
379
  })
380
380
 
381
- describe('--llms', () => {
381
+ describe('--llms-full', () => {
382
382
  test('outputs manifest with version and commands', async () => {
383
383
  const cli = Cli.create('test')
384
384
  cli.command('ping', { description: 'Health check', run: () => ({ pong: true }) })
385
385
 
386
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
386
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
387
387
  const manifest = JSON.parse(output)
388
388
  expect(manifest.version).toBe('incur.v1')
389
389
  expect(manifest.commands).toHaveLength(1)
@@ -399,7 +399,7 @@ describe('--llms', () => {
399
399
  run: ({ args }) => ({ message: `hello ${args.name}` }),
400
400
  })
401
401
 
402
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
402
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
403
403
  const manifest = JSON.parse(output)
404
404
  expect(manifest.commands[0].schema.args).toEqual({
405
405
  type: 'object',
@@ -423,7 +423,7 @@ describe('--llms', () => {
423
423
  run: ({ args }) => ({ message: `hello ${args.name}` }),
424
424
  })
425
425
 
426
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
426
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
427
427
  const manifest = JSON.parse(output)
428
428
  expect(manifest.commands[0].schema.output).toEqual({
429
429
  type: 'object',
@@ -437,7 +437,7 @@ describe('--llms', () => {
437
437
  const cli = Cli.create('test')
438
438
  cli.command('ping', { run: () => ({ pong: true }) })
439
439
 
440
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
440
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
441
441
  const manifest = JSON.parse(output)
442
442
  expect(manifest.commands[0].schema).toBeUndefined()
443
443
  })
@@ -457,7 +457,7 @@ describe('--llms', () => {
457
457
  })
458
458
  cli.command(pr)
459
459
 
460
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
460
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
461
461
  const manifest = JSON.parse(output)
462
462
  expect(manifest.commands).toHaveLength(2)
463
463
  expect(manifest.commands[0].name).toBe('pr create')
@@ -474,7 +474,7 @@ describe('--llms', () => {
474
474
  pr.command(review)
475
475
  cli.command(pr)
476
476
 
477
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
477
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
478
478
  const manifest = JSON.parse(output)
479
479
  expect(manifest.commands[0].name).toBe('pr review approve')
480
480
  expect(manifest.commands[0].description).toBe('Approve a review')
@@ -484,7 +484,7 @@ describe('--llms', () => {
484
484
  const cli = Cli.create('test')
485
485
  cli.command('ping', { description: 'Health check', run: () => ({ pong: true }) })
486
486
 
487
- const { output } = await serve(cli, ['--llms'])
487
+ const { output } = await serve(cli, ['--llms-full'])
488
488
  expect(output).toContain('# test ping')
489
489
  expect(output).toContain('Health check')
490
490
  })
@@ -493,7 +493,7 @@ describe('--llms', () => {
493
493
  const cli = Cli.create('test')
494
494
  cli.command('ping', { description: 'Health check', run: () => ({ pong: true }) })
495
495
 
496
- const { output } = await serve(cli, ['--llms', '--format', 'yaml'])
496
+ const { output } = await serve(cli, ['--llms-full', '--format', 'yaml'])
497
497
  expect(output).toContain('version: incur.v1')
498
498
  expect(output).toContain('name: ping')
499
499
  })
@@ -508,7 +508,7 @@ describe('--llms', () => {
508
508
  run: ({ args }) => ({ message: `hello ${args.name}` }),
509
509
  })
510
510
 
511
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
511
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
512
512
  expect(JSON.parse(output)).toMatchInlineSnapshot(`
513
513
  {
514
514
  "commands": [
@@ -572,7 +572,7 @@ describe('--llms', () => {
572
572
  run: ({ args }) => ({ message: `hello ${args.name}` }),
573
573
  })
574
574
 
575
- const { output } = await serve(cli, ['--llms', '--format', 'md'])
575
+ const { output } = await serve(cli, ['--llms-full', '--format', 'md'])
576
576
  expect(output).toContain('# test greet')
577
577
  expect(output).toContain('## Arguments')
578
578
  expect(output).toContain('## Output')
@@ -580,6 +580,69 @@ describe('--llms', () => {
580
580
  })
581
581
  })
582
582
 
583
+ describe('--llms', () => {
584
+ test('outputs compact command index', async () => {
585
+ const cli = Cli.create('test')
586
+ cli.command('ping', { description: 'Health check', run: () => ({}) })
587
+ cli.command('greet', {
588
+ description: 'Greet someone',
589
+ args: z.object({ name: z.string() }),
590
+ run: () => ({}),
591
+ })
592
+
593
+ const { output } = await serve(cli, ['--llms-full'])
594
+ expect(output).toMatchInlineSnapshot(`
595
+ "# test greet
596
+
597
+ Greet someone
598
+
599
+ ## Arguments
600
+
601
+ | Name | Type | Required | Description |
602
+ |------|------|----------|-------------|
603
+ | \`name\` | \`string\` | yes | |
604
+
605
+ # test ping
606
+
607
+ Health check
608
+ "
609
+ `)
610
+ })
611
+
612
+ test('--llms --json strips schemas', async () => {
613
+ const cli = Cli.create('test')
614
+ cli.command('greet', {
615
+ description: 'Greet someone',
616
+ args: z.object({ name: z.string() }),
617
+ output: z.object({ message: z.string() }),
618
+ run: () => ({ message: 'hi' }),
619
+ })
620
+
621
+ const { output } = await serve(cli, ['--llms', '--json'])
622
+ const manifest = JSON.parse(output)
623
+ expect(manifest.version).toBe('incur.v1')
624
+ expect(manifest.commands).toHaveLength(1)
625
+ expect(manifest.commands[0].name).toBe('greet')
626
+ expect(manifest.commands[0].description).toBe('Greet someone')
627
+ expect(manifest.commands[0].schema).toBeUndefined()
628
+ expect(manifest.commands[0].examples).toBeUndefined()
629
+ })
630
+
631
+ test('scopes to subtree', async () => {
632
+ const cli = Cli.create('test')
633
+ const group = Cli.create('auth', { description: 'Authentication' })
634
+ group.command('login', { description: 'Log in', run: () => ({}) })
635
+ group.command('logout', { description: 'Log out', run: () => ({}) })
636
+ cli.command(group)
637
+ cli.command('ping', { description: 'Health check', run: () => ({}) })
638
+
639
+ const { output } = await serve(cli, ['auth', '--llms'])
640
+ expect(output).toContain('test auth auth login')
641
+ expect(output).toContain('test auth auth logout')
642
+ expect(output).not.toContain('ping')
643
+ })
644
+ })
645
+
583
646
  describe('--schema', () => {
584
647
  test('returns command schema in toon format', async () => {
585
648
  const cli = Cli.create('test')
@@ -857,8 +920,11 @@ describe('subcommands', () => {
857
920
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
858
921
  --format <toon|json|yaml|md|jsonl> Output format
859
922
  --help Show help
860
- --llms Print LLM-readable manifest
923
+ --llms, --llms-full Print LLM-readable manifest
861
924
  --schema Show JSON Schema for a command
925
+ --token-count Print token count of output (instead of output)
926
+ --token-limit <n> Limit output to n tokens
927
+ --token-offset <n> Skip first n tokens of output
862
928
  --verbose Show full output envelope
863
929
  "
864
930
  `)
@@ -1258,7 +1324,7 @@ describe('leaf cli', () => {
1258
1324
  const cli = Cli.create('app')
1259
1325
  cli.command(ping)
1260
1326
 
1261
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
1327
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
1262
1328
  const manifest = JSON.parse(output)
1263
1329
  expect(manifest.commands).toHaveLength(1)
1264
1330
  expect(manifest.commands[0].name).toBe('ping')
@@ -1293,9 +1359,12 @@ describe('help', () => {
1293
1359
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1294
1360
  --format <toon|json|yaml|md|jsonl> Output format
1295
1361
  --help Show help
1296
- --llms Print LLM-readable manifest
1362
+ --llms, --llms-full Print LLM-readable manifest
1297
1363
  --mcp Start as MCP stdio server
1298
1364
  --schema Show JSON Schema for a command
1365
+ --token-count Print token count of output (instead of output)
1366
+ --token-limit <n> Limit output to n tokens
1367
+ --token-offset <n> Skip first n tokens of output
1299
1368
  --verbose Show full output envelope
1300
1369
  --version Show version
1301
1370
  "
@@ -1328,9 +1397,12 @@ describe('help', () => {
1328
1397
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1329
1398
  --format <toon|json|yaml|md|jsonl> Output format
1330
1399
  --help Show help
1331
- --llms Print LLM-readable manifest
1400
+ --llms, --llms-full Print LLM-readable manifest
1332
1401
  --mcp Start as MCP stdio server
1333
1402
  --schema Show JSON Schema for a command
1403
+ --token-count Print token count of output (instead of output)
1404
+ --token-limit <n> Limit output to n tokens
1405
+ --token-offset <n> Skip first n tokens of output
1334
1406
  --verbose Show full output envelope
1335
1407
  --version Show version
1336
1408
  "
@@ -1359,8 +1431,11 @@ describe('help', () => {
1359
1431
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1360
1432
  --format <toon|json|yaml|md|jsonl> Output format
1361
1433
  --help Show help
1362
- --llms Print LLM-readable manifest
1434
+ --llms, --llms-full Print LLM-readable manifest
1363
1435
  --schema Show JSON Schema for a command
1436
+ --token-count Print token count of output (instead of output)
1437
+ --token-limit <n> Limit output to n tokens
1438
+ --token-offset <n> Skip first n tokens of output
1364
1439
  --verbose Show full output envelope
1365
1440
  "
1366
1441
  `)
@@ -1390,8 +1465,11 @@ describe('help', () => {
1390
1465
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1391
1466
  --format <toon|json|yaml|md|jsonl> Output format
1392
1467
  --help Show help
1393
- --llms Print LLM-readable manifest
1468
+ --llms, --llms-full Print LLM-readable manifest
1394
1469
  --schema Show JSON Schema for a command
1470
+ --token-count Print token count of output (instead of output)
1471
+ --token-limit <n> Limit output to n tokens
1472
+ --token-offset <n> Skip first n tokens of output
1395
1473
  --verbose Show full output envelope
1396
1474
  "
1397
1475
  `)
@@ -1482,9 +1560,12 @@ describe('help', () => {
1482
1560
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1483
1561
  --format <toon|json|yaml|md|jsonl> Output format
1484
1562
  --help Show help
1485
- --llms Print LLM-readable manifest
1563
+ --llms, --llms-full Print LLM-readable manifest
1486
1564
  --mcp Start as MCP stdio server
1487
1565
  --schema Show JSON Schema for a command
1566
+ --token-count Print token count of output (instead of output)
1567
+ --token-limit <n> Limit output to n tokens
1568
+ --token-offset <n> Skip first n tokens of output
1488
1569
  --verbose Show full output envelope
1489
1570
  --version Show version
1490
1571
  "
@@ -1511,8 +1592,11 @@ describe('help', () => {
1511
1592
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1512
1593
  --format <toon|json|yaml|md|jsonl> Output format
1513
1594
  --help Show help
1514
- --llms Print LLM-readable manifest
1595
+ --llms, --llms-full Print LLM-readable manifest
1515
1596
  --schema Show JSON Schema for a command
1597
+ --token-count Print token count of output (instead of output)
1598
+ --token-limit <n> Limit output to n tokens
1599
+ --token-offset <n> Skip first n tokens of output
1516
1600
  --verbose Show full output envelope
1517
1601
  "
1518
1602
  `)
@@ -1603,8 +1687,11 @@ describe('env', () => {
1603
1687
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1604
1688
  --format <toon|json|yaml|md|jsonl> Output format
1605
1689
  --help Show help
1606
- --llms Print LLM-readable manifest
1690
+ --llms, --llms-full Print LLM-readable manifest
1607
1691
  --schema Show JSON Schema for a command
1692
+ --token-count Print token count of output (instead of output)
1693
+ --token-limit <n> Limit output to n tokens
1694
+ --token-offset <n> Skip first n tokens of output
1608
1695
  --verbose Show full output envelope
1609
1696
 
1610
1697
  Environment Variables:
@@ -1638,8 +1725,11 @@ describe('env', () => {
1638
1725
  --filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
1639
1726
  --format <toon|json|yaml|md|jsonl> Output format
1640
1727
  --help Show help
1641
- --llms Print LLM-readable manifest
1728
+ --llms, --llms-full Print LLM-readable manifest
1642
1729
  --schema Show JSON Schema for a command
1730
+ --token-count Print token count of output (instead of output)
1731
+ --token-limit <n> Limit output to n tokens
1732
+ --token-offset <n> Skip first n tokens of output
1643
1733
  --verbose Show full output envelope
1644
1734
 
1645
1735
  Environment Variables:
@@ -1694,7 +1784,7 @@ describe('env', () => {
1694
1784
  },
1695
1785
  })
1696
1786
 
1697
- const { output } = await serve(cli, ['--llms', '--format', 'json'])
1787
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json'])
1698
1788
  const cmd = JSON.parse(output).commands.find((c: any) => c.name === 'deploy')
1699
1789
  expect(cmd.schema.env).toMatchInlineSnapshot(`
1700
1790
  {
@@ -1724,7 +1814,7 @@ describe('env', () => {
1724
1814
  },
1725
1815
  })
1726
1816
 
1727
- const { output } = await serve(cli, ['--llms'])
1817
+ const { output } = await serve(cli, ['--llms-full'])
1728
1818
  expect(output).toContain('Environment Variables')
1729
1819
  expect(output).toContain('`API_TOKEN`')
1730
1820
  })
@@ -2468,7 +2558,7 @@ test('--llms scoped to leaf command', async () => {
2468
2558
  cli.command('ping', { description: 'Health check', run: () => ({ pong: true }) })
2469
2559
  cli.command('greet', { description: 'Greet someone', run: () => ({}) })
2470
2560
 
2471
- const { output } = await serve(cli, ['--llms', '--format', 'json', 'ping'])
2561
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json', 'ping'])
2472
2562
  const manifest = JSON.parse(output)
2473
2563
  expect(manifest.commands).toHaveLength(1)
2474
2564
  expect(manifest.commands[0].name).toBe('ping')
@@ -2482,7 +2572,7 @@ test('--llms scoped to group', async () => {
2482
2572
  cli.command(pr)
2483
2573
  cli.command('ping', { description: 'Health check', run: () => ({}) })
2484
2574
 
2485
- const { output } = await serve(cli, ['--llms', '--format', 'json', 'pr'])
2575
+ const { output } = await serve(cli, ['--llms-full', '--format', 'json', 'pr'])
2486
2576
  const manifest = JSON.parse(output)
2487
2577
  expect(manifest.commands).toHaveLength(2)
2488
2578
  expect(manifest.commands.every((c: any) => c.name.startsWith('pr '))).toBe(true)
@@ -2684,7 +2774,7 @@ test('--llms includes hint in skill output', async () => {
2684
2774
  run: () => ({}),
2685
2775
  })
2686
2776
 
2687
- const { output } = await serve(cli, ['--llms'])
2777
+ const { output } = await serve(cli, ['--llms-full'])
2688
2778
  expect(output).toContain('Always confirm before deploying to production')
2689
2779
  })
2690
2780
 
@@ -2877,7 +2967,7 @@ describe('fetch', async () => {
2877
2967
  description: 'Proxy to API',
2878
2968
  fetch: app.fetch,
2879
2969
  })
2880
- const { output } = await serve(cli, ['--llms'])
2970
+ const { output } = await serve(cli, ['--llms-full'])
2881
2971
  expect(output).toContain('api')
2882
2972
  expect(output).toContain('Proxy to API')
2883
2973
  })
package/src/Cli.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { z } from 'zod'
2
2
 
3
+ import { estimateTokenCount, sliceByTokens } from 'tokenx'
3
4
  import * as Completions from './Completions.js'
4
5
  import * as Filter from './Filter.js'
5
6
  import type { FieldError } from './Errors.js'
@@ -419,7 +420,11 @@ async function serveImpl(
419
420
  format: formatFlag,
420
421
  formatExplicit,
421
422
  filterOutput,
423
+ tokenLimit,
424
+ tokenOffset,
425
+ tokenCount,
422
426
  llms,
427
+ llmsFull,
423
428
  mcp: mcpFlag,
424
429
  help,
425
430
  version,
@@ -460,7 +465,7 @@ async function serveImpl(
460
465
  }
461
466
 
462
467
  // Skills staleness check (skip for built-in commands)
463
- if (!llms && !schema && !help && !version) {
468
+ if (!llms && !llmsFull && !schema && !help && !version) {
464
469
  const isSkillsAdd =
465
470
  filtered[0] === 'skills' || (filtered[0] === name && filtered[1] === 'skills')
466
471
  const isMcpAdd = filtered[0] === 'mcp' || (filtered[0] === name && filtered[1] === 'mcp')
@@ -480,15 +485,17 @@ async function serveImpl(
480
485
  }
481
486
  }
482
487
 
483
- if (llms) {
488
+ if (llms || llmsFull) {
484
489
  // Scope to a subtree if command tokens are provided
485
490
  let scopedCommands = commands
486
491
  const prefix: string[] = []
492
+ let scopedDescription: string | undefined = options.description
487
493
  for (const token of filtered) {
488
494
  const entry = scopedCommands.get(token)
489
495
  if (!entry) break
490
496
  if (isGroup(entry)) {
491
497
  scopedCommands = entry.commands
498
+ scopedDescription = entry.description
492
499
  prefix.push(token)
493
500
  } else {
494
501
  // Leaf command — scope to just this command
@@ -497,14 +504,26 @@ async function serveImpl(
497
504
  }
498
505
  }
499
506
 
507
+ if (llmsFull) {
508
+ if (!formatExplicit || formatFlag === 'md') {
509
+ const groups = new Map<string, string>()
510
+ const cmds = collectSkillCommands(scopedCommands, prefix, groups)
511
+ const scopedName = prefix.length > 0 ? `${name} ${prefix.join(' ')}` : name
512
+ writeln(Skill.generate(scopedName, cmds, groups))
513
+ return
514
+ }
515
+ writeln(Formatter.format(buildManifest(scopedCommands, prefix), formatFlag))
516
+ return
517
+ }
518
+
500
519
  if (!formatExplicit || formatFlag === 'md') {
501
520
  const groups = new Map<string, string>()
502
521
  const cmds = collectSkillCommands(scopedCommands, prefix, groups)
503
522
  const scopedName = prefix.length > 0 ? `${name} ${prefix.join(' ')}` : name
504
- writeln(Skill.generate(scopedName, cmds, groups))
523
+ writeln(Skill.index(scopedName, cmds, scopedDescription))
505
524
  return
506
525
  }
507
- writeln(Formatter.format(buildManifest(scopedCommands, prefix), formatFlag))
526
+ writeln(Formatter.format(buildIndexManifest(scopedCommands, prefix), formatFlag))
508
527
  return
509
528
  }
510
529
 
@@ -909,27 +928,69 @@ async function serveImpl(
909
928
 
910
929
  const filterPaths = filterOutput ? Filter.parse(filterOutput) : undefined
911
930
 
931
+ function truncate(s: string): { text: string; truncated: boolean; nextOffset?: number | undefined } {
932
+ if (tokenLimit == null && tokenOffset == null) return { text: s, truncated: false }
933
+ const total = estimateTokenCount(s)
934
+ const offset = tokenOffset ?? 0
935
+ const end = tokenLimit != null ? offset + tokenLimit : total
936
+ if (offset === 0 && end >= total) return { text: s, truncated: false }
937
+ const sliced = sliceByTokens(s, offset, end)
938
+ const actualEnd = Math.min(end, total)
939
+ const nextOffset = actualEnd < total ? actualEnd : undefined
940
+ return {
941
+ text: `${sliced}\n[truncated: showing tokens ${offset}–${actualEnd} of ${total}]`,
942
+ truncated: true,
943
+ nextOffset,
944
+ }
945
+ }
946
+
912
947
  function write(output: Output) {
913
948
  if (filterPaths && output.ok && output.data != null)
914
949
  output = { ...output, data: Filter.apply(output.data, filterPaths) }
950
+ if (tokenCount) {
951
+ const base = output.ok ? output.data : output.error
952
+ const formatted = base != null ? Formatter.format(base, format) : ''
953
+ return writeln(String(estimateTokenCount(formatted)))
954
+ }
915
955
  const cta = output.meta.cta
916
956
  if (human && !verbose) {
917
- if (output.ok && output.data != null && renderOutput)
918
- writeln(Formatter.format(output.data, format))
919
- else if (!output.ok) writeln(formatHumanError(output.error))
957
+ if (output.ok && output.data != null && renderOutput) {
958
+ const t = truncate(Formatter.format(output.data, format))
959
+ writeln(t.text)
960
+ } else if (!output.ok) writeln(formatHumanError(output.error))
920
961
  if (cta) writeln(formatHumanCta(cta))
921
962
  return
922
963
  }
923
- if (verbose) return writeln(Formatter.format(output, format))
964
+ if (verbose) {
965
+ if (tokenLimit != null || tokenOffset != null) {
966
+ // Truncate data separately so meta (including nextOffset) is always visible
967
+ const dataFormatted = output.ok && output.data != null
968
+ ? Formatter.format(output.data, format)
969
+ : !output.ok
970
+ ? Formatter.format(output.error, format)
971
+ : ''
972
+ const t = truncate(dataFormatted)
973
+ if (t.truncated) {
974
+ const envelope: Record<string, unknown> = output.ok
975
+ ? { ok: true, data: t.text }
976
+ : { ok: false, error: t.text }
977
+ const meta: Record<string, unknown> = { ...output.meta }
978
+ if (t.nextOffset != null) meta.nextOffset = t.nextOffset
979
+ envelope.meta = meta
980
+ return writeln(Formatter.format(envelope, format))
981
+ }
982
+ }
983
+ return writeln(Formatter.format(output, format))
984
+ }
924
985
  const base = output.ok ? output.data : output.error
925
986
  const formatted = Formatter.format(base, format)
926
987
  if (!cta) {
927
- if (formatted) writeln(formatted)
988
+ if (formatted) writeln(truncate(formatted).text)
928
989
  return
929
990
  }
930
991
  const payload =
931
992
  typeof base === 'object' && base !== null ? { ...base, cta } : { data: base, cta }
932
- writeln(Formatter.format(payload, format))
993
+ writeln(truncate(Formatter.format(payload, format)).text)
933
994
  }
934
995
 
935
996
  if ('error' in effective) {
@@ -985,6 +1046,7 @@ async function serveImpl(
985
1046
  human,
986
1047
  renderOutput,
987
1048
  verbose,
1049
+ truncate,
988
1050
  write,
989
1051
  writeln,
990
1052
  exit,
@@ -1141,6 +1203,7 @@ async function serveImpl(
1141
1203
  human,
1142
1204
  renderOutput,
1143
1205
  verbose,
1206
+ truncate,
1144
1207
  write,
1145
1208
  writeln,
1146
1209
  exit,
@@ -1752,6 +1815,7 @@ declare namespace serveImpl {
1752
1815
  function extractBuiltinFlags(argv: string[]) {
1753
1816
  let verbose = false
1754
1817
  let llms = false
1818
+ let llmsFull = false
1755
1819
  let mcp = false
1756
1820
  let help = false
1757
1821
  let version = false
@@ -1759,12 +1823,16 @@ function extractBuiltinFlags(argv: string[]) {
1759
1823
  let format: Formatter.Format = 'toon'
1760
1824
  let formatExplicit = false
1761
1825
  let filterOutput: string | undefined
1826
+ let tokenLimit: number | undefined
1827
+ let tokenOffset: number | undefined
1828
+ let tokenCount = false
1762
1829
  const rest: string[] = []
1763
1830
 
1764
1831
  for (let i = 0; i < argv.length; i++) {
1765
1832
  const token = argv[i]!
1766
1833
  if (token === '--verbose') verbose = true
1767
1834
  else if (token === '--llms') llms = true
1835
+ else if (token === '--llms-full') llmsFull = true
1768
1836
  else if (token === '--mcp') mcp = true
1769
1837
  else if (token === '--help' || token === '-h') help = true
1770
1838
  else if (token === '--version') version = true
@@ -1779,10 +1847,17 @@ function extractBuiltinFlags(argv: string[]) {
1779
1847
  } else if (token === '--filter-output' && argv[i + 1]) {
1780
1848
  filterOutput = argv[i + 1]!
1781
1849
  i++
1782
- } else rest.push(token)
1850
+ } else if (token === '--token-limit' && argv[i + 1]) {
1851
+ tokenLimit = Number(argv[i + 1])
1852
+ i++
1853
+ } else if (token === '--token-offset' && argv[i + 1]) {
1854
+ tokenOffset = Number(argv[i + 1])
1855
+ i++
1856
+ } else if (token === '--token-count') tokenCount = true
1857
+ else rest.push(token)
1783
1858
  }
1784
1859
 
1785
- return { verbose, format, formatExplicit, filterOutput, llms, mcp, help, version, schema, rest }
1860
+ return { verbose, format, formatExplicit, filterOutput, tokenLimit, tokenOffset, tokenCount, llms, llmsFull, mcp, help, version, schema, rest }
1786
1861
  }
1787
1862
 
1788
1863
  /** @internal Collects immediate child commands/groups for help output. */
@@ -1954,6 +2029,7 @@ async function handleStreaming(
1954
2029
  human: boolean
1955
2030
  renderOutput: boolean
1956
2031
  verbose: boolean
2032
+ truncate: (s: string) => { text: string; truncated: boolean; nextOffset?: number | undefined }
1957
2033
  write: (output: Output) => void
1958
2034
  writeln: (s: string) => void
1959
2035
  exit: (code: number) => void
@@ -1997,7 +2073,7 @@ async function handleStreaming(
1997
2073
  }
1998
2074
  }
1999
2075
  if (useJsonl) ctx.writeln(JSON.stringify({ type: 'chunk', data: value }))
2000
- else if (ctx.renderOutput) ctx.writeln(Formatter.format(value, ctx.format))
2076
+ else if (ctx.renderOutput) ctx.writeln(ctx.truncate(Formatter.format(value, ctx.format)).text)
2001
2077
  }
2002
2078
 
2003
2079
  // Handle return value — error() or ok() sentinel
@@ -2164,6 +2240,35 @@ function formatCta(name: string, cta: Cta): FormattedCta {
2164
2240
  return { command: cmd, ...(cta.description ? { description: cta.description } : undefined) }
2165
2241
  }
2166
2242
 
2243
+ /** @internal Builds the `--llms` index manifest (name + description only) from the command tree. */
2244
+ function buildIndexManifest(commands: Map<string, CommandEntry>, prefix: string[] = []) {
2245
+ return {
2246
+ version: 'incur.v1',
2247
+ commands: collectIndexCommands(commands, prefix).sort((a, b) => a.name.localeCompare(b.name)),
2248
+ }
2249
+ }
2250
+
2251
+ /** @internal Recursively collects leaf commands with name + description only. */
2252
+ function collectIndexCommands(
2253
+ commands: Map<string, CommandEntry>,
2254
+ prefix: string[],
2255
+ ): { name: string; description?: string | undefined }[] {
2256
+ const result: { name: string; description?: string | undefined }[] = []
2257
+ for (const [name, entry] of commands) {
2258
+ const path = [...prefix, name]
2259
+ if (isGroup(entry)) {
2260
+ result.push(...collectIndexCommands(entry.commands, path))
2261
+ } else {
2262
+ const cmd: (typeof result)[number] = { name: path.join(' ') }
2263
+ if (isFetchGateway(entry)) {
2264
+ if (entry.description) cmd.description = entry.description
2265
+ } else if (entry.description) cmd.description = entry.description
2266
+ result.push(cmd)
2267
+ }
2268
+ }
2269
+ return result
2270
+ }
2271
+
2167
2272
  /** @internal Builds the `--llms` manifest from the command tree. */
2168
2273
  function buildManifest(commands: Map<string, CommandEntry>, prefix: string[] = []) {
2169
2274
  return {
@@ -2382,6 +2487,8 @@ declare namespace Output {
2382
2487
  cta?: FormattedCtaBlock | undefined
2383
2488
  /** Wall-clock duration of the command. */
2384
2489
  duration: string
2490
+ /** Offset to pass as `--token-offset` to fetch the next page of truncated output. */
2491
+ nextOffset?: number | undefined
2385
2492
  }
2386
2493
  }
2387
2494