incur 0.2.2 → 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/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +50 -6
- package/dist/Cli.js.map +1 -1
- package/dist/Help.js +1 -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 +1 -1
- package/src/Cli.test.ts +90 -27
- package/src/Cli.ts +51 -5
- package/src/Help.test.ts +7 -7
- package/src/Help.ts +1 -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 +277 -35
package/src/Help.test.ts
CHANGED
|
@@ -28,7 +28,7 @@ describe('formatCommand', () => {
|
|
|
28
28
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
29
29
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
30
30
|
--help Show help
|
|
31
|
-
--llms
|
|
31
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
32
32
|
--schema Show JSON Schema for a command
|
|
33
33
|
--token-count Print token count of output (instead of output)
|
|
34
34
|
--token-limit <n> Limit output to n tokens
|
|
@@ -50,7 +50,7 @@ describe('formatCommand', () => {
|
|
|
50
50
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
51
51
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
52
52
|
--help Show help
|
|
53
|
-
--llms
|
|
53
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
54
54
|
--schema Show JSON Schema for a command
|
|
55
55
|
--token-count Print token count of output (instead of output)
|
|
56
56
|
--token-limit <n> Limit output to n tokens
|
|
@@ -79,7 +79,7 @@ describe('formatCommand', () => {
|
|
|
79
79
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
80
80
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
81
81
|
--help Show help
|
|
82
|
-
--llms
|
|
82
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
83
83
|
--schema Show JSON Schema for a command
|
|
84
84
|
--token-count Print token count of output (instead of output)
|
|
85
85
|
--token-limit <n> Limit output to n tokens
|
|
@@ -125,7 +125,7 @@ describe('formatRoot', () => {
|
|
|
125
125
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
126
126
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
127
127
|
--help Show help
|
|
128
|
-
--llms
|
|
128
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
129
129
|
--schema Show JSON Schema for a command
|
|
130
130
|
--token-count Print token count of output (instead of output)
|
|
131
131
|
--token-limit <n> Limit output to n tokens
|
|
@@ -150,7 +150,7 @@ describe('formatRoot', () => {
|
|
|
150
150
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
151
151
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
152
152
|
--help Show help
|
|
153
|
-
--llms
|
|
153
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
154
154
|
--schema Show JSON Schema for a command
|
|
155
155
|
--token-count Print token count of output (instead of output)
|
|
156
156
|
--token-limit <n> Limit output to n tokens
|
|
@@ -179,7 +179,7 @@ describe('formatRoot', () => {
|
|
|
179
179
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
180
180
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
181
181
|
--help Show help
|
|
182
|
-
--llms
|
|
182
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
183
183
|
--schema Show JSON Schema for a command
|
|
184
184
|
--token-count Print token count of output (instead of output)
|
|
185
185
|
--token-limit <n> Limit output to n tokens
|
|
@@ -208,7 +208,7 @@ describe('formatRoot', () => {
|
|
|
208
208
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
209
209
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
210
210
|
--help Show help
|
|
211
|
-
--llms
|
|
211
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
212
212
|
--schema Show JSON Schema for a command
|
|
213
213
|
--token-count Print token count of output (instead of output)
|
|
214
214
|
--token-limit <n> Limit output to n tokens
|
package/src/Help.ts
CHANGED
|
@@ -329,7 +329,7 @@ function globalOptionsLines(root = false): string[] {
|
|
|
329
329
|
{ flag: '--filter-output <keys>', desc: 'Filter output by key paths (e.g. foo,bar.baz,a[0,3])' },
|
|
330
330
|
{ flag: '--format <toon|json|yaml|md|jsonl>', desc: 'Output format' },
|
|
331
331
|
{ flag: '--help', desc: 'Show help' },
|
|
332
|
-
{ flag: '--llms', desc: 'Print LLM-readable manifest' },
|
|
332
|
+
{ flag: '--llms, --llms-full', desc: 'Print LLM-readable manifest' },
|
|
333
333
|
...(root ? [{ flag: '--mcp', desc: 'Start as MCP stdio server' }] : []),
|
|
334
334
|
{ flag: '--schema', desc: 'Show JSON Schema for a command' },
|
|
335
335
|
{ flag: '--token-count', desc: 'Print token count of output (instead of output)' },
|
package/src/Skill.test.ts
CHANGED
|
@@ -145,6 +145,41 @@ test('concatenates multiple commands', () => {
|
|
|
145
145
|
`)
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
+
describe('index', () => {
|
|
149
|
+
test('generates compact command index', () => {
|
|
150
|
+
const result = Skill.index('test', [
|
|
151
|
+
{ name: 'ping', description: 'Health check' },
|
|
152
|
+
{ name: 'greet', description: 'Greet someone', args: z.object({ name: z.string() }) },
|
|
153
|
+
])
|
|
154
|
+
expect(result).toMatchInlineSnapshot(`
|
|
155
|
+
"# test
|
|
156
|
+
|
|
157
|
+
| Command | Description |
|
|
158
|
+
|---------|-------------|
|
|
159
|
+
| \`test ping\` | Health check |
|
|
160
|
+
| \`test greet <name>\` | Greet someone |
|
|
161
|
+
|
|
162
|
+
Run \`test --llms-full\` for full manifest. Run \`test <command> --schema\` for argument details."
|
|
163
|
+
`)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('uses brackets for optional args', () => {
|
|
167
|
+
const result = Skill.index('test', [
|
|
168
|
+
{
|
|
169
|
+
name: 'install',
|
|
170
|
+
description: 'Install a package',
|
|
171
|
+
args: z.object({ package: z.string().optional() }),
|
|
172
|
+
},
|
|
173
|
+
])
|
|
174
|
+
expect(result).toContain('`test install [package]`')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test('handles commands without descriptions', () => {
|
|
178
|
+
const result = Skill.index('test', [{ name: 'ping' }])
|
|
179
|
+
expect(result).toContain('| `test ping` | |')
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
148
183
|
describe('hash', () => {
|
|
149
184
|
test('returns consistent hash for same commands', () => {
|
|
150
185
|
const commands: Skill.CommandInfo[] = [
|
|
@@ -218,6 +253,7 @@ describe('split', () => {
|
|
|
218
253
|
"---
|
|
219
254
|
name: gh-auth
|
|
220
255
|
description: Authenticate with GitHub. Log in, Check status. Run \`gh auth --help\` for usage details.
|
|
256
|
+
requires_bin: gh
|
|
221
257
|
command: gh auth
|
|
222
258
|
---
|
|
223
259
|
|
|
@@ -235,6 +271,7 @@ describe('split', () => {
|
|
|
235
271
|
"---
|
|
236
272
|
name: gh-pr
|
|
237
273
|
description: Manage pull requests. List PRs, Create PR. Run \`gh pr --help\` for usage details.
|
|
274
|
+
requires_bin: gh
|
|
238
275
|
command: gh pr
|
|
239
276
|
---
|
|
240
277
|
|
|
@@ -294,9 +331,16 @@ describe('split', () => {
|
|
|
294
331
|
expect(files[0]!.content).toContain('Run `gh auth login --help` for usage details.')
|
|
295
332
|
})
|
|
296
333
|
|
|
297
|
-
test('
|
|
334
|
+
test('emits fallback description when no explicit descriptions exist', () => {
|
|
298
335
|
const files = Skill.split('test', [{ name: 'ping' }], 1)
|
|
299
|
-
expect(files[0]!.content).
|
|
336
|
+
expect(files[0]!.content).toContain(
|
|
337
|
+
'description: Run `test ping --help` for usage details.',
|
|
338
|
+
)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
test('includes requires_bin in frontmatter', () => {
|
|
342
|
+
const files = Skill.split('gh', [{ name: 'auth login', description: 'Log in' }], 1)
|
|
343
|
+
expect(files[0]!.content).toContain('requires_bin: gh')
|
|
300
344
|
})
|
|
301
345
|
|
|
302
346
|
test('no per-command frontmatter in split files', () => {
|
package/src/Skill.ts
CHANGED
|
@@ -23,6 +23,33 @@ export type File = {
|
|
|
23
23
|
content: string
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** Generates a compact Markdown command index for `--llms`. */
|
|
27
|
+
export function index(name: string, commands: CommandInfo[], description?: string | undefined): string {
|
|
28
|
+
const lines: string[] = [`# ${name}`]
|
|
29
|
+
if (description) lines.push('', description)
|
|
30
|
+
lines.push('')
|
|
31
|
+
lines.push('| Command | Description |')
|
|
32
|
+
lines.push('|---------|-------------|')
|
|
33
|
+
for (const cmd of commands) {
|
|
34
|
+
const signature = buildSignature(name, cmd)
|
|
35
|
+
const desc = cmd.description ?? ''
|
|
36
|
+
lines.push(`| \`${signature}\` | ${desc} |`)
|
|
37
|
+
}
|
|
38
|
+
lines.push('', `Run \`${name} --llms-full\` for full manifest. Run \`${name} <command> --schema\` for argument details.`)
|
|
39
|
+
return lines.join('\n')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @internal Builds a command signature with arg placeholders. */
|
|
43
|
+
function buildSignature(cli: string, cmd: CommandInfo): string {
|
|
44
|
+
const base = `${cli} ${cmd.name}`
|
|
45
|
+
if (!cmd.args) return base
|
|
46
|
+
const shape = cmd.args.shape as Record<string, z.ZodType>
|
|
47
|
+
const json = Schema.toJsonSchema(cmd.args)
|
|
48
|
+
const required = new Set((json.required as string[] | undefined) ?? [])
|
|
49
|
+
const argNames = Object.keys(shape).map((k) => (required.has(k) ? `<${k}>` : `[${k}]`))
|
|
50
|
+
return `${base} ${argNames.join(' ')}`
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
/** Generates a Markdown skill file from a CLI name and collected command data. */
|
|
27
54
|
export function generate(
|
|
28
55
|
name: string,
|
|
@@ -88,12 +115,15 @@ function renderGroup(
|
|
|
88
115
|
const descParts: string[] = []
|
|
89
116
|
if (groupDesc) descParts.push(groupDesc.replace(/\.$/, ''))
|
|
90
117
|
if (childDescs.length > 0) descParts.push(childDescs.join(', '))
|
|
91
|
-
const description =
|
|
118
|
+
const description =
|
|
119
|
+
descParts.length > 0
|
|
120
|
+
? `${descParts.join('. ')}. Run \`${title} --help\` for usage details.`
|
|
121
|
+
: `Run \`${title} --help\` for usage details.`
|
|
92
122
|
|
|
93
123
|
const slug = title.replace(/\s+/g, '-')
|
|
94
124
|
const fm = ['---', `name: ${slug}`]
|
|
95
|
-
|
|
96
|
-
|
|
125
|
+
fm.push(`description: ${description}`)
|
|
126
|
+
fm.push(`requires_bin: ${cli}`)
|
|
97
127
|
fm.push(`command: ${title}`, '---')
|
|
98
128
|
|
|
99
129
|
const body = cmds.map((cmd) => renderCommandBody(cli, cmd)).join('\n\n---\n\n')
|
package/src/SyncSkills.ts
CHANGED
|
@@ -13,8 +13,8 @@ export async function sync(
|
|
|
13
13
|
commands: Map<string, any>,
|
|
14
14
|
options: sync.Options = {},
|
|
15
15
|
): Promise<sync.Result> {
|
|
16
|
-
const cwd = options.cwd ?? resolvePackageRoot()
|
|
17
16
|
const { depth = 1, description, global = true } = options
|
|
17
|
+
const cwd = options.cwd ?? (global ? resolvePackageRoot() : process.cwd())
|
|
18
18
|
|
|
19
19
|
const groups = new Map<string, string>()
|
|
20
20
|
if (description) groups.set(name, description)
|
package/src/e2e.test.ts
CHANGED
|
@@ -939,7 +939,7 @@ describe('help', () => {
|
|
|
939
939
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
940
940
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
941
941
|
--help Show help
|
|
942
|
-
--llms
|
|
942
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
943
943
|
--mcp Start as MCP stdio server
|
|
944
944
|
--schema Show JSON Schema for a command
|
|
945
945
|
--token-count Print token count of output (instead of output)
|
|
@@ -973,7 +973,7 @@ describe('help', () => {
|
|
|
973
973
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
974
974
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
975
975
|
--help Show help
|
|
976
|
-
--llms
|
|
976
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
977
977
|
--schema Show JSON Schema for a command
|
|
978
978
|
--token-count Print token count of output (instead of output)
|
|
979
979
|
--token-limit <n> Limit output to n tokens
|
|
@@ -1000,7 +1000,7 @@ describe('help', () => {
|
|
|
1000
1000
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
1001
1001
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1002
1002
|
--help Show help
|
|
1003
|
-
--llms
|
|
1003
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
1004
1004
|
--schema Show JSON Schema for a command
|
|
1005
1005
|
--token-count Print token count of output (instead of output)
|
|
1006
1006
|
--token-limit <n> Limit output to n tokens
|
|
@@ -1026,7 +1026,7 @@ describe('help', () => {
|
|
|
1026
1026
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
1027
1027
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1028
1028
|
--help Show help
|
|
1029
|
-
--llms
|
|
1029
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
1030
1030
|
--schema Show JSON Schema for a command
|
|
1031
1031
|
--token-count Print token count of output (instead of output)
|
|
1032
1032
|
--token-limit <n> Limit output to n tokens
|
|
@@ -1058,7 +1058,7 @@ describe('help', () => {
|
|
|
1058
1058
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
1059
1059
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1060
1060
|
--help Show help
|
|
1061
|
-
--llms
|
|
1061
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
1062
1062
|
--schema Show JSON Schema for a command
|
|
1063
1063
|
--token-count Print token count of output (instead of output)
|
|
1064
1064
|
--token-limit <n> Limit output to n tokens
|
|
@@ -1090,9 +1090,9 @@ describe('help', () => {
|
|
|
1090
1090
|
})
|
|
1091
1091
|
})
|
|
1092
1092
|
|
|
1093
|
-
describe('--llms', () => {
|
|
1093
|
+
describe('--llms-full', () => {
|
|
1094
1094
|
test('json manifest lists all leaf commands sorted', async () => {
|
|
1095
|
-
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1095
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'json'])
|
|
1096
1096
|
const manifest = json(output)
|
|
1097
1097
|
expect(manifest.version).toBe('incur.v1')
|
|
1098
1098
|
const names = manifest.commands.map((c: any) => c.name)
|
|
@@ -1127,7 +1127,7 @@ describe('--llms', () => {
|
|
|
1127
1127
|
})
|
|
1128
1128
|
|
|
1129
1129
|
test('manifest includes schema.args and schema.options separately', async () => {
|
|
1130
|
-
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1130
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'json'])
|
|
1131
1131
|
const projectList = json(output).commands.find((c: any) => c.name === 'project list')
|
|
1132
1132
|
expect(projectList.schema.options.properties).toMatchInlineSnapshot(`
|
|
1133
1133
|
{
|
|
@@ -1157,7 +1157,7 @@ describe('--llms', () => {
|
|
|
1157
1157
|
})
|
|
1158
1158
|
|
|
1159
1159
|
test('manifest includes schema.output', async () => {
|
|
1160
|
-
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1160
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'json'])
|
|
1161
1161
|
const projectGet = json(output).commands.find((c: any) => c.name === 'project get')
|
|
1162
1162
|
expect(projectGet.schema.output).toMatchInlineSnapshot(`
|
|
1163
1163
|
{
|
|
@@ -1204,13 +1204,13 @@ describe('--llms', () => {
|
|
|
1204
1204
|
})
|
|
1205
1205
|
|
|
1206
1206
|
test('manifest omits schema when no schemas defined', async () => {
|
|
1207
|
-
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1207
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'json'])
|
|
1208
1208
|
const ping = json(output).commands.find((c: any) => c.name === 'ping')
|
|
1209
1209
|
expect(ping.schema).toBeUndefined()
|
|
1210
1210
|
})
|
|
1211
1211
|
|
|
1212
|
-
test('scoped --llms to group', async () => {
|
|
1213
|
-
const { output } = await serve(createApp(), ['auth', '--llms', '--format', 'json'])
|
|
1212
|
+
test('scoped --llms-full to group', async () => {
|
|
1213
|
+
const { output } = await serve(createApp(), ['auth', '--llms-full', '--format', 'json'])
|
|
1214
1214
|
const names = json(output).commands.map((c: any) => c.name)
|
|
1215
1215
|
expect(names).toMatchInlineSnapshot(`
|
|
1216
1216
|
[
|
|
@@ -1221,8 +1221,8 @@ describe('--llms', () => {
|
|
|
1221
1221
|
`)
|
|
1222
1222
|
})
|
|
1223
1223
|
|
|
1224
|
-
test('scoped --llms to nested group', async () => {
|
|
1225
|
-
const { output } = await serve(createApp(), ['project', 'deploy', '--llms', '--format', 'json'])
|
|
1224
|
+
test('scoped --llms-full to nested group', async () => {
|
|
1225
|
+
const { output } = await serve(createApp(), ['project', 'deploy', '--llms-full', '--format', 'json'])
|
|
1226
1226
|
const names = json(output).commands.map((c: any) => c.name)
|
|
1227
1227
|
expect(names).toMatchInlineSnapshot(`
|
|
1228
1228
|
[
|
|
@@ -1233,27 +1233,27 @@ describe('--llms', () => {
|
|
|
1233
1233
|
`)
|
|
1234
1234
|
})
|
|
1235
1235
|
|
|
1236
|
-
test('default --llms outputs markdown', async () => {
|
|
1237
|
-
const { output } = await serve(createApp(), ['--llms'])
|
|
1236
|
+
test('default --llms-full outputs markdown', async () => {
|
|
1237
|
+
const { output } = await serve(createApp(), ['--llms-full'])
|
|
1238
1238
|
expect(output).toContain('# app')
|
|
1239
1239
|
expect(output).toContain('auth login')
|
|
1240
1240
|
expect(output).toContain('project list')
|
|
1241
1241
|
})
|
|
1242
1242
|
|
|
1243
|
-
test('--llms markdown includes argument tables', async () => {
|
|
1244
|
-
const { output } = await serve(createApp(), ['project', '--llms'])
|
|
1243
|
+
test('--llms-full markdown includes argument tables', async () => {
|
|
1244
|
+
const { output } = await serve(createApp(), ['project', '--llms-full'])
|
|
1245
1245
|
expect(output).toContain('Arguments')
|
|
1246
1246
|
expect(output).toContain('`id`')
|
|
1247
1247
|
})
|
|
1248
1248
|
|
|
1249
|
-
test('--llms markdown includes options tables', async () => {
|
|
1250
|
-
const { output } = await serve(createApp(), ['project', '--llms'])
|
|
1249
|
+
test('--llms-full markdown includes options tables', async () => {
|
|
1250
|
+
const { output } = await serve(createApp(), ['project', '--llms-full'])
|
|
1251
1251
|
expect(output).toContain('Options')
|
|
1252
1252
|
expect(output).toContain('`--limit`')
|
|
1253
1253
|
})
|
|
1254
1254
|
|
|
1255
|
-
test('--llms json includes examples on commands', async () => {
|
|
1256
|
-
const { output } = await serve(createApp(), ['project', 'deploy', '--llms', '--format', 'json'])
|
|
1255
|
+
test('--llms-full json includes examples on commands', async () => {
|
|
1256
|
+
const { output } = await serve(createApp(), ['project', 'deploy', '--llms-full', '--format', 'json'])
|
|
1257
1257
|
const deployCreate = json(output).commands.find((c: any) => c.name === 'project deploy create')
|
|
1258
1258
|
expect(deployCreate.examples).toMatchInlineSnapshot(`
|
|
1259
1259
|
[
|
|
@@ -1269,27 +1269,268 @@ describe('--llms', () => {
|
|
|
1269
1269
|
`)
|
|
1270
1270
|
})
|
|
1271
1271
|
|
|
1272
|
-
test('--llms json omits examples when not defined', async () => {
|
|
1273
|
-
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1272
|
+
test('--llms-full json omits examples when not defined', async () => {
|
|
1273
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'json'])
|
|
1274
1274
|
const ping = json(output).commands.find((c: any) => c.name === 'ping')
|
|
1275
1275
|
expect(ping.examples).toBeUndefined()
|
|
1276
1276
|
})
|
|
1277
1277
|
|
|
1278
|
-
test('--llms markdown includes examples section', async () => {
|
|
1279
|
-
const { output } = await serve(createApp(), ['--llms'])
|
|
1278
|
+
test('--llms-full markdown includes examples section', async () => {
|
|
1279
|
+
const { output } = await serve(createApp(), ['--llms-full'])
|
|
1280
1280
|
expect(output).toContain('Examples')
|
|
1281
1281
|
expect(output).toContain('Deploy staging from main')
|
|
1282
1282
|
expect(output).toContain('app project deploy create staging')
|
|
1283
1283
|
})
|
|
1284
1284
|
|
|
1285
|
-
test('--llms markdown includes output tables', async () => {
|
|
1286
|
-
const { output } = await serve(createApp(), ['project', '--llms'])
|
|
1285
|
+
test('--llms-full markdown includes output tables', async () => {
|
|
1286
|
+
const { output } = await serve(createApp(), ['project', '--llms-full'])
|
|
1287
1287
|
expect(output).toContain('Output')
|
|
1288
1288
|
})
|
|
1289
1289
|
|
|
1290
|
+
test('--llms-full --format yaml', async () => {
|
|
1291
|
+
const { output } = await serve(createApp(), ['--llms-full', '--format', 'yaml'])
|
|
1292
|
+
expect(output).toContain('version: incur.v1')
|
|
1293
|
+
})
|
|
1294
|
+
})
|
|
1295
|
+
|
|
1296
|
+
describe('--llms', () => {
|
|
1297
|
+
test('outputs compact markdown table with all commands', async () => {
|
|
1298
|
+
const { output } = await serve(createApp(), ['--llms'])
|
|
1299
|
+
expect(output).toMatchInlineSnapshot(`
|
|
1300
|
+
"# app
|
|
1301
|
+
|
|
1302
|
+
A comprehensive CLI application for testing.
|
|
1303
|
+
|
|
1304
|
+
| Command | Description |
|
|
1305
|
+
|---------|-------------|
|
|
1306
|
+
| \`app api\` | Proxy to HTTP API |
|
|
1307
|
+
| \`app auth login\` | Log in to the service |
|
|
1308
|
+
| \`app auth logout\` | Log out of the service |
|
|
1309
|
+
| \`app auth status\` | Show authentication status |
|
|
1310
|
+
| \`app config [key]\` | Show current configuration |
|
|
1311
|
+
| \`app echo <message> [repeat]\` | Echo back arguments |
|
|
1312
|
+
| \`app explode\` | Always fails |
|
|
1313
|
+
| \`app explode-clac\` | Fails with IncurError |
|
|
1314
|
+
| \`app noop\` | Returns nothing |
|
|
1315
|
+
| \`app ping\` | Health check |
|
|
1316
|
+
| \`app project create <name>\` | Create a new project |
|
|
1317
|
+
| \`app project delete <id>\` | Delete a project |
|
|
1318
|
+
| \`app project deploy create <env>\` | Create a deployment |
|
|
1319
|
+
| \`app project deploy rollback <deployId>\` | Rollback a deployment |
|
|
1320
|
+
| \`app project deploy status <deployId>\` | Check deployment status |
|
|
1321
|
+
| \`app project get <id>\` | Get a project by ID |
|
|
1322
|
+
| \`app project list\` | List projects |
|
|
1323
|
+
| \`app slow\` | Async command |
|
|
1324
|
+
| \`app stream\` | Stream chunks |
|
|
1325
|
+
| \`app stream-error\` | Stream with mid-stream error |
|
|
1326
|
+
| \`app stream-ok\` | Stream with ok() return |
|
|
1327
|
+
| \`app stream-text\` | Stream plain text |
|
|
1328
|
+
| \`app stream-throw\` | Stream that throws |
|
|
1329
|
+
| \`app validate-fail <email> <age>\` | Fails validation |
|
|
1330
|
+
|
|
1331
|
+
Run \`app --llms-full\` for full manifest. Run \`app <command> --schema\` for argument details.
|
|
1332
|
+
"
|
|
1333
|
+
`)
|
|
1334
|
+
})
|
|
1335
|
+
|
|
1336
|
+
test('json manifest has name + description only, no schema/examples', async () => {
|
|
1337
|
+
const { output } = await serve(createApp(), ['--llms', '--format', 'json'])
|
|
1338
|
+
expect(json(output)).toMatchInlineSnapshot(`
|
|
1339
|
+
{
|
|
1340
|
+
"commands": [
|
|
1341
|
+
{
|
|
1342
|
+
"description": "Proxy to HTTP API",
|
|
1343
|
+
"name": "api",
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
"description": "Log in to the service",
|
|
1347
|
+
"name": "auth login",
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
"description": "Log out of the service",
|
|
1351
|
+
"name": "auth logout",
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
"description": "Show authentication status",
|
|
1355
|
+
"name": "auth status",
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
"description": "Show current configuration",
|
|
1359
|
+
"name": "config",
|
|
1360
|
+
},
|
|
1361
|
+
{
|
|
1362
|
+
"description": "Echo back arguments",
|
|
1363
|
+
"name": "echo",
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
"description": "Always fails",
|
|
1367
|
+
"name": "explode",
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
"description": "Fails with IncurError",
|
|
1371
|
+
"name": "explode-clac",
|
|
1372
|
+
},
|
|
1373
|
+
{
|
|
1374
|
+
"description": "Returns nothing",
|
|
1375
|
+
"name": "noop",
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"description": "Health check",
|
|
1379
|
+
"name": "ping",
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
"description": "Create a new project",
|
|
1383
|
+
"name": "project create",
|
|
1384
|
+
},
|
|
1385
|
+
{
|
|
1386
|
+
"description": "Delete a project",
|
|
1387
|
+
"name": "project delete",
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"description": "Create a deployment",
|
|
1391
|
+
"name": "project deploy create",
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
"description": "Rollback a deployment",
|
|
1395
|
+
"name": "project deploy rollback",
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
"description": "Check deployment status",
|
|
1399
|
+
"name": "project deploy status",
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
"description": "Get a project by ID",
|
|
1403
|
+
"name": "project get",
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
"description": "List projects",
|
|
1407
|
+
"name": "project list",
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
"description": "Async command",
|
|
1411
|
+
"name": "slow",
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
"description": "Stream chunks",
|
|
1415
|
+
"name": "stream",
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"description": "Stream with mid-stream error",
|
|
1419
|
+
"name": "stream-error",
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
"description": "Stream with ok() return",
|
|
1423
|
+
"name": "stream-ok",
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
"description": "Stream plain text",
|
|
1427
|
+
"name": "stream-text",
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
"description": "Stream that throws",
|
|
1431
|
+
"name": "stream-throw",
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
"description": "Fails validation",
|
|
1435
|
+
"name": "validate-fail",
|
|
1436
|
+
},
|
|
1437
|
+
],
|
|
1438
|
+
"version": "incur.v1",
|
|
1439
|
+
}
|
|
1440
|
+
`)
|
|
1441
|
+
})
|
|
1442
|
+
|
|
1443
|
+
test('scoped to group', async () => {
|
|
1444
|
+
const { output } = await serve(createApp(), ['auth', '--llms'])
|
|
1445
|
+
expect(output).toMatchInlineSnapshot(`
|
|
1446
|
+
"# app auth
|
|
1447
|
+
|
|
1448
|
+
Authentication commands
|
|
1449
|
+
|
|
1450
|
+
| Command | Description |
|
|
1451
|
+
|---------|-------------|
|
|
1452
|
+
| \`app auth auth login\` | Log in to the service |
|
|
1453
|
+
| \`app auth auth logout\` | Log out of the service |
|
|
1454
|
+
| \`app auth auth status\` | Show authentication status |
|
|
1455
|
+
|
|
1456
|
+
Run \`app auth --llms-full\` for full manifest. Run \`app auth <command> --schema\` for argument details.
|
|
1457
|
+
"
|
|
1458
|
+
`)
|
|
1459
|
+
})
|
|
1460
|
+
|
|
1461
|
+
test('scoped to nested group', async () => {
|
|
1462
|
+
const { output } = await serve(createApp(), ['project', 'deploy', '--llms'])
|
|
1463
|
+
expect(output).toMatchInlineSnapshot(`
|
|
1464
|
+
"# app project deploy
|
|
1465
|
+
|
|
1466
|
+
Deployment commands
|
|
1467
|
+
|
|
1468
|
+
| Command | Description |
|
|
1469
|
+
|---------|-------------|
|
|
1470
|
+
| \`app project deploy project deploy create <env>\` | Create a deployment |
|
|
1471
|
+
| \`app project deploy project deploy rollback <deployId>\` | Rollback a deployment |
|
|
1472
|
+
| \`app project deploy project deploy status <deployId>\` | Check deployment status |
|
|
1473
|
+
|
|
1474
|
+
Run \`app project deploy --llms-full\` for full manifest. Run \`app project deploy <command> --schema\` for argument details.
|
|
1475
|
+
"
|
|
1476
|
+
`)
|
|
1477
|
+
})
|
|
1478
|
+
|
|
1290
1479
|
test('--llms --format yaml', async () => {
|
|
1291
1480
|
const { output } = await serve(createApp(), ['--llms', '--format', 'yaml'])
|
|
1292
|
-
expect(output).
|
|
1481
|
+
expect(output).toMatchInlineSnapshot(`
|
|
1482
|
+
"version: incur.v1
|
|
1483
|
+
commands:
|
|
1484
|
+
- name: api
|
|
1485
|
+
description: Proxy to HTTP API
|
|
1486
|
+
- name: auth login
|
|
1487
|
+
description: Log in to the service
|
|
1488
|
+
- name: auth logout
|
|
1489
|
+
description: Log out of the service
|
|
1490
|
+
- name: auth status
|
|
1491
|
+
description: Show authentication status
|
|
1492
|
+
- name: config
|
|
1493
|
+
description: Show current configuration
|
|
1494
|
+
- name: echo
|
|
1495
|
+
description: Echo back arguments
|
|
1496
|
+
- name: explode
|
|
1497
|
+
description: Always fails
|
|
1498
|
+
- name: explode-clac
|
|
1499
|
+
description: Fails with IncurError
|
|
1500
|
+
- name: noop
|
|
1501
|
+
description: Returns nothing
|
|
1502
|
+
- name: ping
|
|
1503
|
+
description: Health check
|
|
1504
|
+
- name: project create
|
|
1505
|
+
description: Create a new project
|
|
1506
|
+
- name: project delete
|
|
1507
|
+
description: Delete a project
|
|
1508
|
+
- name: project deploy create
|
|
1509
|
+
description: Create a deployment
|
|
1510
|
+
- name: project deploy rollback
|
|
1511
|
+
description: Rollback a deployment
|
|
1512
|
+
- name: project deploy status
|
|
1513
|
+
description: Check deployment status
|
|
1514
|
+
- name: project get
|
|
1515
|
+
description: Get a project by ID
|
|
1516
|
+
- name: project list
|
|
1517
|
+
description: List projects
|
|
1518
|
+
- name: slow
|
|
1519
|
+
description: Async command
|
|
1520
|
+
- name: stream
|
|
1521
|
+
description: Stream chunks
|
|
1522
|
+
- name: stream-error
|
|
1523
|
+
description: Stream with mid-stream error
|
|
1524
|
+
- name: stream-ok
|
|
1525
|
+
description: Stream with ok() return
|
|
1526
|
+
- name: stream-text
|
|
1527
|
+
description: Stream plain text
|
|
1528
|
+
- name: stream-throw
|
|
1529
|
+
description: Stream that throws
|
|
1530
|
+
- name: validate-fail
|
|
1531
|
+
description: Fails validation
|
|
1532
|
+
"
|
|
1533
|
+
`)
|
|
1293
1534
|
})
|
|
1294
1535
|
})
|
|
1295
1536
|
|
|
@@ -1461,7 +1702,7 @@ describe('root command with subcommands', () => {
|
|
|
1461
1702
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
1462
1703
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1463
1704
|
--help Show help
|
|
1464
|
-
--llms
|
|
1705
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
1465
1706
|
--mcp Start as MCP stdio server
|
|
1466
1707
|
--schema Show JSON Schema for a command
|
|
1467
1708
|
--token-count Print token count of output (instead of output)
|
|
@@ -1638,7 +1879,7 @@ describe('env', () => {
|
|
|
1638
1879
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
1639
1880
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1640
1881
|
--help Show help
|
|
1641
|
-
--llms
|
|
1882
|
+
--llms, --llms-full Print LLM-readable manifest
|
|
1642
1883
|
--schema Show JSON Schema for a command
|
|
1643
1884
|
--token-count Print token count of output (instead of output)
|
|
1644
1885
|
--token-limit <n> Limit output to n tokens
|
|
@@ -1652,8 +1893,8 @@ describe('env', () => {
|
|
|
1652
1893
|
`)
|
|
1653
1894
|
})
|
|
1654
1895
|
|
|
1655
|
-
test('--llms json includes schema.env', async () => {
|
|
1656
|
-
const { output } = await serve(createApp(), ['auth', '--llms', '--format', 'json'])
|
|
1896
|
+
test('--llms-full json includes schema.env', async () => {
|
|
1897
|
+
const { output } = await serve(createApp(), ['auth', '--llms-full', '--format', 'json'])
|
|
1657
1898
|
const login = json(output).commands.find((c: any) => c.name === 'auth login')
|
|
1658
1899
|
expect(login.schema.env.properties).toMatchInlineSnapshot(`
|
|
1659
1900
|
{
|
|
@@ -1670,8 +1911,8 @@ describe('env', () => {
|
|
|
1670
1911
|
`)
|
|
1671
1912
|
})
|
|
1672
1913
|
|
|
1673
|
-
test('--llms markdown includes env vars table', async () => {
|
|
1674
|
-
const { output } = await serve(createApp(), ['auth', '--llms'])
|
|
1914
|
+
test('--llms-full markdown includes env vars table', async () => {
|
|
1915
|
+
const { output } = await serve(createApp(), ['auth', '--llms-full'])
|
|
1675
1916
|
expect(output).toContain('Environment Variables')
|
|
1676
1917
|
expect(output).toContain('`AUTH_TOKEN`')
|
|
1677
1918
|
expect(output).toContain('`AUTH_HOST`')
|
|
@@ -2631,6 +2872,7 @@ describe('.well-known/skills', () => {
|
|
|
2631
2872
|
"---
|
|
2632
2873
|
name: app-ping
|
|
2633
2874
|
description: Health check. Run \`app ping --help\` for usage details.
|
|
2875
|
+
requires_bin: app
|
|
2634
2876
|
command: app ping
|
|
2635
2877
|
---
|
|
2636
2878
|
|