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/README.md +40 -0
- package/SKILL.md +18 -0
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +116 -12
- package/dist/Cli.js.map +1 -1
- package/dist/Help.js +4 -1
- package/dist/Help.js.map +1 -1
- package/dist/Skill.d.ts +2 -0
- package/dist/Skill.d.ts.map +1 -1
- package/dist/Skill.js +32 -3
- package/dist/Skill.js.map +1 -1
- package/dist/SyncSkills.js +1 -1
- package/dist/SyncSkills.js.map +1 -1
- package/package.json +2 -1
- package/src/Cli.test.ts +117 -27
- package/src/Cli.ts +120 -13
- package/src/Help.test.ts +28 -7
- package/src/Help.ts +4 -1
- package/src/Skill.test.ts +46 -2
- package/src/Skill.ts +33 -3
- package/src/SyncSkills.ts +1 -1
- package/src/e2e.test.ts +389 -35
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
523
|
+
writeln(Skill.index(scopedName, cmds, scopedDescription))
|
|
505
524
|
return
|
|
506
525
|
}
|
|
507
|
-
writeln(Formatter.format(
|
|
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
|
-
|
|
919
|
-
|
|
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)
|
|
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
|
|
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
|
|