incur 0.3.3 → 0.3.5
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 +11 -11
- package/dist/Cli.d.ts +2 -7
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +234 -359
- package/dist/Cli.js.map +1 -1
- package/dist/Completions.d.ts +1 -2
- package/dist/Completions.d.ts.map +1 -1
- package/dist/Completions.js.map +1 -1
- package/dist/Help.d.ts +2 -0
- package/dist/Help.d.ts.map +1 -1
- package/dist/Help.js +20 -10
- package/dist/Help.js.map +1 -1
- package/dist/Mcp.d.ts +25 -5
- package/dist/Mcp.d.ts.map +1 -1
- package/dist/Mcp.js +61 -69
- package/dist/Mcp.js.map +1 -1
- package/dist/Skill.d.ts.map +1 -1
- package/dist/Skill.js +5 -1
- package/dist/Skill.js.map +1 -1
- package/dist/SyncSkills.d.ts.map +1 -1
- package/dist/SyncSkills.js +10 -1
- package/dist/SyncSkills.js.map +1 -1
- package/dist/internal/command.d.ts +116 -0
- package/dist/internal/command.d.ts.map +1 -0
- package/dist/internal/command.js +275 -0
- package/dist/internal/command.js.map +1 -0
- package/package.json +1 -1
- package/src/Cli.test.ts +165 -18
- package/src/Cli.ts +288 -439
- package/src/Completions.test.ts +35 -9
- package/src/Completions.ts +1 -2
- package/src/Help.test.ts +18 -7
- package/src/Help.ts +21 -10
- package/src/Mcp.test.ts +143 -0
- package/src/Mcp.ts +92 -84
- package/src/Skill.ts +5 -1
- package/src/SyncSkills.ts +11 -1
- package/src/e2e.test.ts +40 -27
- package/src/internal/command.ts +425 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IncurError, ValidationError } from '../Errors.js';
|
|
3
|
+
import * as Parser from '../Parser.js';
|
|
4
|
+
/** @internal Sentinel symbol for `ok()` and `error()` return values. */
|
|
5
|
+
const sentinel = Symbol.for('incur.sentinel');
|
|
6
|
+
/** @internal Unified command execution used by CLI, HTTP, and MCP transports. */
|
|
7
|
+
export async function execute(command, options) {
|
|
8
|
+
const { argv, inputOptions, agent, format, formatExplicit, name, path, version, envSource = process.env, env: envSchema, vars: varsSchema, middlewares = [], } = options;
|
|
9
|
+
const parseMode = options.parseMode ?? 'argv';
|
|
10
|
+
const varsMap = varsSchema ? varsSchema.parse({}) : {};
|
|
11
|
+
let result;
|
|
12
|
+
// For streaming with middleware: runCommand suspends on streamConsumed so middleware "after"
|
|
13
|
+
// runs after the stream is consumed. The wrapped generator resolves it in its finally block.
|
|
14
|
+
// resultReady signals that result has been set (for streams, before the chain finishes).
|
|
15
|
+
let streamConsumed;
|
|
16
|
+
let resolveStreamConsumed;
|
|
17
|
+
let resolveResultReady;
|
|
18
|
+
const resultReady = new Promise((r) => {
|
|
19
|
+
resolveResultReady = r;
|
|
20
|
+
});
|
|
21
|
+
const runCommand = async () => {
|
|
22
|
+
// Parse args and options
|
|
23
|
+
let args;
|
|
24
|
+
let parsedOptions;
|
|
25
|
+
if (parseMode === 'argv') {
|
|
26
|
+
// CLI mode: parse both args and options from argv tokens
|
|
27
|
+
const parsed = Parser.parse(argv, {
|
|
28
|
+
alias: command.alias,
|
|
29
|
+
args: command.args,
|
|
30
|
+
options: command.options,
|
|
31
|
+
});
|
|
32
|
+
args = parsed.args;
|
|
33
|
+
parsedOptions = parsed.options;
|
|
34
|
+
}
|
|
35
|
+
else if (parseMode === 'split') {
|
|
36
|
+
// HTTP mode: positional args from URL path segments, options from body/query
|
|
37
|
+
const parsed = Parser.parse(argv, { args: command.args });
|
|
38
|
+
args = parsed.args;
|
|
39
|
+
parsedOptions = command.options ? command.options.parse(inputOptions) : {};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// MCP mode: all params come from inputOptions, split into args vs options
|
|
43
|
+
const split = splitParams(inputOptions, command);
|
|
44
|
+
args = command.args ? command.args.parse(split.args) : {};
|
|
45
|
+
parsedOptions = command.options ? command.options.parse(split.options) : {};
|
|
46
|
+
}
|
|
47
|
+
// Parse env
|
|
48
|
+
const commandEnv = command.env ? Parser.parseEnv(command.env, envSource) : {};
|
|
49
|
+
// Build sentinel helpers
|
|
50
|
+
const okFn = (data, meta = {}) => ({ [sentinel]: 'ok', data, cta: meta.cta });
|
|
51
|
+
const errorFn = (opts) => ({ [sentinel]: 'error', ...opts });
|
|
52
|
+
const raw = command.run({
|
|
53
|
+
agent,
|
|
54
|
+
args,
|
|
55
|
+
env: commandEnv,
|
|
56
|
+
error: errorFn,
|
|
57
|
+
format,
|
|
58
|
+
formatExplicit,
|
|
59
|
+
name,
|
|
60
|
+
ok: okFn,
|
|
61
|
+
options: parsedOptions,
|
|
62
|
+
var: varsMap,
|
|
63
|
+
version,
|
|
64
|
+
});
|
|
65
|
+
// Streaming: wrap the generator so middleware "after" runs after consumption.
|
|
66
|
+
// When middleware is active, runCommand suspends until the stream is fully consumed,
|
|
67
|
+
// keeping the middleware chain alive around the stream's lifetime.
|
|
68
|
+
if (isAsyncGenerator(raw)) {
|
|
69
|
+
if (middlewares.length > 0) {
|
|
70
|
+
streamConsumed = new Promise((r) => {
|
|
71
|
+
resolveStreamConsumed = r;
|
|
72
|
+
});
|
|
73
|
+
async function* wrapped() {
|
|
74
|
+
try {
|
|
75
|
+
yield* raw;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
resolveStreamConsumed();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
result = { stream: wrapped() };
|
|
82
|
+
resolveResultReady();
|
|
83
|
+
await streamConsumed;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
result = { stream: raw };
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const awaited = await raw;
|
|
91
|
+
if (isSentinel(awaited)) {
|
|
92
|
+
if (awaited[sentinel] === 'ok') {
|
|
93
|
+
const ok = awaited;
|
|
94
|
+
result = { ok: true, data: ok.data, ...(ok.cta ? { cta: ok.cta } : undefined) };
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const err = awaited;
|
|
98
|
+
result = {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: {
|
|
101
|
+
code: err.code,
|
|
102
|
+
message: err.message,
|
|
103
|
+
...(err.retryable !== undefined ? { retryable: err.retryable } : undefined),
|
|
104
|
+
},
|
|
105
|
+
...(err.cta ? { cta: err.cta } : undefined),
|
|
106
|
+
...(err.exitCode !== undefined ? { exitCode: err.exitCode } : undefined),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
result = { ok: true, data: awaited };
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
// Parse CLI-level env
|
|
115
|
+
const cliEnv = envSchema ? Parser.parseEnv(envSchema, envSource) : {};
|
|
116
|
+
if (middlewares.length > 0) {
|
|
117
|
+
const errorFn = (opts) => {
|
|
118
|
+
// Side-effect: set result directly (handles both `return c.error()` and bare `c.error()`)
|
|
119
|
+
result = {
|
|
120
|
+
ok: false,
|
|
121
|
+
error: {
|
|
122
|
+
code: opts.code,
|
|
123
|
+
message: opts.message,
|
|
124
|
+
...(opts.retryable !== undefined ? { retryable: opts.retryable } : undefined),
|
|
125
|
+
},
|
|
126
|
+
...(opts.cta ? { cta: opts.cta } : undefined),
|
|
127
|
+
...(opts.exitCode !== undefined ? { exitCode: opts.exitCode } : undefined),
|
|
128
|
+
};
|
|
129
|
+
return undefined;
|
|
130
|
+
};
|
|
131
|
+
const mwCtx = {
|
|
132
|
+
agent,
|
|
133
|
+
command: path,
|
|
134
|
+
env: cliEnv,
|
|
135
|
+
error: errorFn,
|
|
136
|
+
format: format,
|
|
137
|
+
formatExplicit,
|
|
138
|
+
name,
|
|
139
|
+
set(key, value) {
|
|
140
|
+
varsMap[key] = value;
|
|
141
|
+
},
|
|
142
|
+
var: varsMap,
|
|
143
|
+
version,
|
|
144
|
+
};
|
|
145
|
+
const composed = middlewares.reduceRight((next, mw) => async () => {
|
|
146
|
+
await mw(mwCtx, next);
|
|
147
|
+
}, runCommand);
|
|
148
|
+
// Start the chain and race against resultReady. For streams with middleware,
|
|
149
|
+
// runCommand suspends on streamConsumed (keeping middleware "after" deferred)
|
|
150
|
+
// but signals resultReady so we can return the stream immediately. The transport
|
|
151
|
+
// consumes the stream, which resolves streamConsumed, letting middleware "after" run.
|
|
152
|
+
const chainPromise = composed();
|
|
153
|
+
await Promise.race([chainPromise, resultReady]);
|
|
154
|
+
if (streamConsumed)
|
|
155
|
+
return result;
|
|
156
|
+
await chainPromise;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await runCommand();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof ValidationError)
|
|
164
|
+
return {
|
|
165
|
+
ok: false,
|
|
166
|
+
error: {
|
|
167
|
+
code: 'VALIDATION_ERROR',
|
|
168
|
+
message: error.message,
|
|
169
|
+
fieldErrors: error.fieldErrors,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
error: {
|
|
175
|
+
code: error instanceof IncurError ? error.code : 'UNKNOWN',
|
|
176
|
+
message: error instanceof Error ? error.message : String(error),
|
|
177
|
+
...(error instanceof IncurError ? { retryable: error.retryable } : undefined),
|
|
178
|
+
},
|
|
179
|
+
...(error instanceof IncurError && error.exitCode !== undefined
|
|
180
|
+
? { exitCode: error.exitCode }
|
|
181
|
+
: undefined),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return result ?? { ok: true, data: undefined };
|
|
185
|
+
}
|
|
186
|
+
/** @internal Splits flat params into args vs options using schema shapes. */
|
|
187
|
+
function splitParams(params, command) {
|
|
188
|
+
const argKeys = new Set(command.args ? Object.keys(command.args.shape) : []);
|
|
189
|
+
const a = {};
|
|
190
|
+
const o = {};
|
|
191
|
+
for (const [key, value] of Object.entries(params))
|
|
192
|
+
if (argKeys.has(key))
|
|
193
|
+
a[key] = value;
|
|
194
|
+
else
|
|
195
|
+
o[key] = value;
|
|
196
|
+
return { args: a, options: o };
|
|
197
|
+
}
|
|
198
|
+
/** @internal Type guard for sentinel results. */
|
|
199
|
+
function isSentinel(value) {
|
|
200
|
+
return typeof value === 'object' && value !== null && sentinel in value;
|
|
201
|
+
}
|
|
202
|
+
/** @internal Type guard for async generators. */
|
|
203
|
+
function isAsyncGenerator(value) {
|
|
204
|
+
return (typeof value === 'object' &&
|
|
205
|
+
value !== null &&
|
|
206
|
+
Symbol.asyncIterator in value &&
|
|
207
|
+
typeof value.next === 'function');
|
|
208
|
+
}
|
|
209
|
+
/** @internal Creates a builtin subcommand with typesafe alias inference. */
|
|
210
|
+
function subcommand(def) {
|
|
211
|
+
return def;
|
|
212
|
+
}
|
|
213
|
+
/** Supported shell names for completions. */
|
|
214
|
+
export const shells = ['bash', 'fish', 'nushell', 'zsh'];
|
|
215
|
+
/** Built-in command metadata shared by help, completions, and handler logic. */
|
|
216
|
+
export const builtinCommands = [
|
|
217
|
+
{
|
|
218
|
+
name: 'completions',
|
|
219
|
+
description: 'Generate shell completion script',
|
|
220
|
+
args: z.object({
|
|
221
|
+
shell: z.enum(shells).describe('Shell to generate completions for'),
|
|
222
|
+
}),
|
|
223
|
+
hint(name) {
|
|
224
|
+
const rows = [
|
|
225
|
+
['bash', `eval "$(${name} completions bash)"`, '# add to ~/.bashrc'],
|
|
226
|
+
['fish', `${name} completions fish | source`, '# add to ~/.config/fish/config.fish'],
|
|
227
|
+
['nushell', `see \`${name} completions nushell\``, '# add to config.nu'],
|
|
228
|
+
['zsh', `eval "$(${name} completions zsh)"`, '# add to ~/.zshrc'],
|
|
229
|
+
];
|
|
230
|
+
const shellW = Math.max(...rows.map((r) => r[0].length));
|
|
231
|
+
const cmdW = Math.max(...rows.map((r) => r[1].length));
|
|
232
|
+
return ('Setup:\n' +
|
|
233
|
+
rows
|
|
234
|
+
.map(([s, cmd, comment]) => ` ${s.padEnd(shellW)} ${cmd.padEnd(cmdW)} ${comment}`)
|
|
235
|
+
.join('\n'));
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'mcp',
|
|
240
|
+
description: 'Register as MCP server',
|
|
241
|
+
subcommands: [
|
|
242
|
+
subcommand({
|
|
243
|
+
name: 'add',
|
|
244
|
+
description: 'Register as MCP server',
|
|
245
|
+
alias: { command: 'c' },
|
|
246
|
+
options: z.object({
|
|
247
|
+
agent: z
|
|
248
|
+
.string()
|
|
249
|
+
.optional()
|
|
250
|
+
.describe('Target a specific agent (e.g. claude-code, cursor)'),
|
|
251
|
+
command: z
|
|
252
|
+
.string()
|
|
253
|
+
.optional()
|
|
254
|
+
.describe('Override the command agents will run (e.g. "pnpm my-cli --mcp")'),
|
|
255
|
+
noGlobal: z.boolean().optional().describe('Install to project instead of globally'),
|
|
256
|
+
}),
|
|
257
|
+
}),
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'skills',
|
|
262
|
+
description: 'Sync skill files to agents',
|
|
263
|
+
subcommands: [
|
|
264
|
+
subcommand({
|
|
265
|
+
name: 'add',
|
|
266
|
+
description: 'Sync skill files to agents',
|
|
267
|
+
options: z.object({
|
|
268
|
+
depth: z.number().optional().describe('Grouping depth for skill files (default: 1)'),
|
|
269
|
+
noGlobal: z.boolean().optional().describe('Install to project instead of globally'),
|
|
270
|
+
}),
|
|
271
|
+
}),
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/internal/command.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/Cli.test.ts
CHANGED
|
@@ -921,7 +921,7 @@ describe('subcommands', () => {
|
|
|
921
921
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
922
922
|
--help Show help
|
|
923
923
|
--llms, --llms-full Print LLM-readable manifest
|
|
924
|
-
--schema Show JSON Schema for
|
|
924
|
+
--schema Show JSON Schema for command
|
|
925
925
|
--token-count Print token count of output (instead of output)
|
|
926
926
|
--token-limit <n> Limit output to n tokens
|
|
927
927
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1350,10 +1350,10 @@ describe('help', () => {
|
|
|
1350
1350
|
Commands:
|
|
1351
1351
|
ping Health check
|
|
1352
1352
|
|
|
1353
|
-
|
|
1353
|
+
Integrations:
|
|
1354
1354
|
completions Generate shell completion script
|
|
1355
|
-
mcp add Register as
|
|
1356
|
-
skills add Sync skill files to
|
|
1355
|
+
mcp add Register as MCP server
|
|
1356
|
+
skills add Sync skill files to agents
|
|
1357
1357
|
|
|
1358
1358
|
Global Options:
|
|
1359
1359
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
@@ -1361,7 +1361,7 @@ describe('help', () => {
|
|
|
1361
1361
|
--help Show help
|
|
1362
1362
|
--llms, --llms-full Print LLM-readable manifest
|
|
1363
1363
|
--mcp Start as MCP stdio server
|
|
1364
|
-
--schema Show JSON Schema for
|
|
1364
|
+
--schema Show JSON Schema for command
|
|
1365
1365
|
--token-count Print token count of output (instead of output)
|
|
1366
1366
|
--token-limit <n> Limit output to n tokens
|
|
1367
1367
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1388,10 +1388,10 @@ describe('help', () => {
|
|
|
1388
1388
|
Commands:
|
|
1389
1389
|
ping Health check
|
|
1390
1390
|
|
|
1391
|
-
|
|
1391
|
+
Integrations:
|
|
1392
1392
|
completions Generate shell completion script
|
|
1393
|
-
mcp add Register as
|
|
1394
|
-
skills add Sync skill files to
|
|
1393
|
+
mcp add Register as MCP server
|
|
1394
|
+
skills add Sync skill files to agents
|
|
1395
1395
|
|
|
1396
1396
|
Global Options:
|
|
1397
1397
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
@@ -1399,7 +1399,7 @@ describe('help', () => {
|
|
|
1399
1399
|
--help Show help
|
|
1400
1400
|
--llms, --llms-full Print LLM-readable manifest
|
|
1401
1401
|
--mcp Start as MCP stdio server
|
|
1402
|
-
--schema Show JSON Schema for
|
|
1402
|
+
--schema Show JSON Schema for command
|
|
1403
1403
|
--token-count Print token count of output (instead of output)
|
|
1404
1404
|
--token-limit <n> Limit output to n tokens
|
|
1405
1405
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1432,7 +1432,7 @@ describe('help', () => {
|
|
|
1432
1432
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1433
1433
|
--help Show help
|
|
1434
1434
|
--llms, --llms-full Print LLM-readable manifest
|
|
1435
|
-
--schema Show JSON Schema for
|
|
1435
|
+
--schema Show JSON Schema for command
|
|
1436
1436
|
--token-count Print token count of output (instead of output)
|
|
1437
1437
|
--token-limit <n> Limit output to n tokens
|
|
1438
1438
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1466,7 +1466,7 @@ describe('help', () => {
|
|
|
1466
1466
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1467
1467
|
--help Show help
|
|
1468
1468
|
--llms, --llms-full Print LLM-readable manifest
|
|
1469
|
-
--schema Show JSON Schema for
|
|
1469
|
+
--schema Show JSON Schema for command
|
|
1470
1470
|
--token-count Print token count of output (instead of output)
|
|
1471
1471
|
--token-limit <n> Limit output to n tokens
|
|
1472
1472
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1551,10 +1551,10 @@ describe('help', () => {
|
|
|
1551
1551
|
Commands:
|
|
1552
1552
|
ping Ping
|
|
1553
1553
|
|
|
1554
|
-
|
|
1554
|
+
Integrations:
|
|
1555
1555
|
completions Generate shell completion script
|
|
1556
|
-
mcp add Register as
|
|
1557
|
-
skills add Sync skill files to
|
|
1556
|
+
mcp add Register as MCP server
|
|
1557
|
+
skills add Sync skill files to agents
|
|
1558
1558
|
|
|
1559
1559
|
Global Options:
|
|
1560
1560
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
@@ -1562,7 +1562,7 @@ describe('help', () => {
|
|
|
1562
1562
|
--help Show help
|
|
1563
1563
|
--llms, --llms-full Print LLM-readable manifest
|
|
1564
1564
|
--mcp Start as MCP stdio server
|
|
1565
|
-
--schema Show JSON Schema for
|
|
1565
|
+
--schema Show JSON Schema for command
|
|
1566
1566
|
--token-count Print token count of output (instead of output)
|
|
1567
1567
|
--token-limit <n> Limit output to n tokens
|
|
1568
1568
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1593,7 +1593,7 @@ describe('help', () => {
|
|
|
1593
1593
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1594
1594
|
--help Show help
|
|
1595
1595
|
--llms, --llms-full Print LLM-readable manifest
|
|
1596
|
-
--schema Show JSON Schema for
|
|
1596
|
+
--schema Show JSON Schema for command
|
|
1597
1597
|
--token-count Print token count of output (instead of output)
|
|
1598
1598
|
--token-limit <n> Limit output to n tokens
|
|
1599
1599
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1688,7 +1688,7 @@ describe('env', () => {
|
|
|
1688
1688
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1689
1689
|
--help Show help
|
|
1690
1690
|
--llms, --llms-full Print LLM-readable manifest
|
|
1691
|
-
--schema Show JSON Schema for
|
|
1691
|
+
--schema Show JSON Schema for command
|
|
1692
1692
|
--token-count Print token count of output (instead of output)
|
|
1693
1693
|
--token-limit <n> Limit output to n tokens
|
|
1694
1694
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1726,7 +1726,7 @@ describe('env', () => {
|
|
|
1726
1726
|
--format <toon|json|yaml|md|jsonl> Output format
|
|
1727
1727
|
--help Show help
|
|
1728
1728
|
--llms, --llms-full Print LLM-readable manifest
|
|
1729
|
-
--schema Show JSON Schema for
|
|
1729
|
+
--schema Show JSON Schema for command
|
|
1730
1730
|
--token-count Print token count of output (instead of output)
|
|
1731
1731
|
--token-limit <n> Limit output to n tokens
|
|
1732
1732
|
--token-offset <n> Skip first n tokens of output
|
|
@@ -1838,6 +1838,76 @@ describe('env', () => {
|
|
|
1838
1838
|
})
|
|
1839
1839
|
})
|
|
1840
1840
|
|
|
1841
|
+
describe('built-in commands', () => {
|
|
1842
|
+
test('bare completions shows help', async () => {
|
|
1843
|
+
const cli = Cli.create('test')
|
|
1844
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1845
|
+
const { output } = await serve(cli, ['completions'])
|
|
1846
|
+
expect(output).toContain('Generate shell completion script')
|
|
1847
|
+
})
|
|
1848
|
+
|
|
1849
|
+
test('completions --help shows help', async () => {
|
|
1850
|
+
const cli = Cli.create('test')
|
|
1851
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1852
|
+
const { output } = await serve(cli, ['completions', '--help'])
|
|
1853
|
+
expect(output).toContain('test completions')
|
|
1854
|
+
expect(output).toContain('Generate shell completion script')
|
|
1855
|
+
})
|
|
1856
|
+
|
|
1857
|
+
test('bare mcp shows help with subcommands', async () => {
|
|
1858
|
+
const cli = Cli.create('test')
|
|
1859
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1860
|
+
const { output } = await serve(cli, ['mcp'])
|
|
1861
|
+
expect(output).toContain('test mcp')
|
|
1862
|
+
expect(output).toContain('Register as MCP server')
|
|
1863
|
+
expect(output).toContain('add')
|
|
1864
|
+
})
|
|
1865
|
+
|
|
1866
|
+
test('mcp --help shows help with subcommands', async () => {
|
|
1867
|
+
const cli = Cli.create('test')
|
|
1868
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1869
|
+
const { output } = await serve(cli, ['mcp', '--help'])
|
|
1870
|
+
expect(output).toContain('test mcp')
|
|
1871
|
+
expect(output).toContain('add')
|
|
1872
|
+
})
|
|
1873
|
+
|
|
1874
|
+
test('mcp add --help shows options', async () => {
|
|
1875
|
+
const cli = Cli.create('test')
|
|
1876
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1877
|
+
const { output } = await serve(cli, ['mcp', 'add', '--help'])
|
|
1878
|
+
expect(output).toContain('test mcp add')
|
|
1879
|
+
expect(output).toContain('--command')
|
|
1880
|
+
expect(output).toContain('--no-global')
|
|
1881
|
+
expect(output).toContain('--agent')
|
|
1882
|
+
})
|
|
1883
|
+
|
|
1884
|
+
test('bare skills shows help with subcommands', async () => {
|
|
1885
|
+
const cli = Cli.create('test')
|
|
1886
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1887
|
+
const { output } = await serve(cli, ['skills'])
|
|
1888
|
+
expect(output).toContain('test skills')
|
|
1889
|
+
expect(output).toContain('Sync skill files to agents')
|
|
1890
|
+
expect(output).toContain('add')
|
|
1891
|
+
})
|
|
1892
|
+
|
|
1893
|
+
test('skills --help shows help with subcommands', async () => {
|
|
1894
|
+
const cli = Cli.create('test')
|
|
1895
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1896
|
+
const { output } = await serve(cli, ['skills', '--help'])
|
|
1897
|
+
expect(output).toContain('test skills')
|
|
1898
|
+
expect(output).toContain('add')
|
|
1899
|
+
})
|
|
1900
|
+
|
|
1901
|
+
test('skills add --help shows options', async () => {
|
|
1902
|
+
const cli = Cli.create('test')
|
|
1903
|
+
cli.command('ping', { run: () => ({ pong: true }) })
|
|
1904
|
+
const { output } = await serve(cli, ['skills', 'add', '--help'])
|
|
1905
|
+
expect(output).toContain('test skills add')
|
|
1906
|
+
expect(output).toContain('--depth')
|
|
1907
|
+
expect(output).toContain('--no-global')
|
|
1908
|
+
})
|
|
1909
|
+
})
|
|
1910
|
+
|
|
1841
1911
|
describe('skills staleness', () => {
|
|
1842
1912
|
let stderrSpy: ReturnType<typeof vi.spyOn>
|
|
1843
1913
|
|
|
@@ -3375,6 +3445,83 @@ describe('fetch', () => {
|
|
|
3375
3445
|
`)
|
|
3376
3446
|
})
|
|
3377
3447
|
|
|
3448
|
+
test('group middleware runs for nested commands', async () => {
|
|
3449
|
+
const sub = Cli.create('admin', {
|
|
3450
|
+
vars: z.object({ role: z.string().default('none') }),
|
|
3451
|
+
})
|
|
3452
|
+
sub.use(async (c, next) => {
|
|
3453
|
+
c.set('role', 'admin')
|
|
3454
|
+
await next()
|
|
3455
|
+
})
|
|
3456
|
+
sub.command('status', {
|
|
3457
|
+
run: (c) => ({ role: c.var.role }),
|
|
3458
|
+
})
|
|
3459
|
+
const cli = Cli.create('test', {
|
|
3460
|
+
vars: z.object({ role: z.string().default('none') }),
|
|
3461
|
+
})
|
|
3462
|
+
cli.command(sub)
|
|
3463
|
+
expect(await fetchJson(cli, new Request('http://localhost/admin/status')))
|
|
3464
|
+
.toMatchInlineSnapshot(`
|
|
3465
|
+
{
|
|
3466
|
+
"body": {
|
|
3467
|
+
"data": {
|
|
3468
|
+
"role": "admin",
|
|
3469
|
+
},
|
|
3470
|
+
"meta": {
|
|
3471
|
+
"command": "admin status",
|
|
3472
|
+
"duration": "<stripped>",
|
|
3473
|
+
},
|
|
3474
|
+
"ok": true,
|
|
3475
|
+
},
|
|
3476
|
+
"status": 200,
|
|
3477
|
+
}
|
|
3478
|
+
`)
|
|
3479
|
+
})
|
|
3480
|
+
|
|
3481
|
+
test('cli-level env schema is parsed', async () => {
|
|
3482
|
+
const cli = Cli.create('test', {
|
|
3483
|
+
env: z.object({ APP_TOKEN: z.string().default('fallback') }),
|
|
3484
|
+
})
|
|
3485
|
+
cli.use(async (c, next) => {
|
|
3486
|
+
// env should be parsed from envSchema
|
|
3487
|
+
;(globalThis as any).__testEnv = c.env
|
|
3488
|
+
await next()
|
|
3489
|
+
})
|
|
3490
|
+
cli.command('check', { run: () => ({ ok: true }) })
|
|
3491
|
+
await cli.fetch(new Request('http://localhost/check'))
|
|
3492
|
+
expect((globalThis as any).__testEnv).toEqual({ APP_TOKEN: 'fallback' })
|
|
3493
|
+
delete (globalThis as any).__testEnv
|
|
3494
|
+
})
|
|
3495
|
+
|
|
3496
|
+
test('retryable error is propagated', async () => {
|
|
3497
|
+
const cli = Cli.create('test')
|
|
3498
|
+
cli.command('rate-limit', {
|
|
3499
|
+
run: (c) => c.error({ code: 'RATE_LIMITED', message: 'slow down', retryable: true }),
|
|
3500
|
+
})
|
|
3501
|
+
const { body } = await fetchJson(cli, new Request('http://localhost/rate-limit'))
|
|
3502
|
+
expect(body.ok).toBe(false)
|
|
3503
|
+
expect(body.error.retryable).toBe(true)
|
|
3504
|
+
})
|
|
3505
|
+
|
|
3506
|
+
test('cta block is propagated', async () => {
|
|
3507
|
+
const cli = Cli.create('test')
|
|
3508
|
+
cli.command('done', {
|
|
3509
|
+
run: (c) => c.ok({ id: 1 }, { cta: { commands: ['list'], description: 'Next steps:' } }),
|
|
3510
|
+
})
|
|
3511
|
+
const { body } = await fetchJson(cli, new Request('http://localhost/done'))
|
|
3512
|
+
expect(body.ok).toBe(true)
|
|
3513
|
+
expect(body.meta.cta).toMatchInlineSnapshot(`
|
|
3514
|
+
{
|
|
3515
|
+
"commands": [
|
|
3516
|
+
{
|
|
3517
|
+
"command": "test list",
|
|
3518
|
+
},
|
|
3519
|
+
],
|
|
3520
|
+
"description": "Next steps:",
|
|
3521
|
+
}
|
|
3522
|
+
`)
|
|
3523
|
+
})
|
|
3524
|
+
|
|
3378
3525
|
describe('mcp over http', () => {
|
|
3379
3526
|
function mcpCli() {
|
|
3380
3527
|
const cli = Cli.create('test', { version: '1.0.0' })
|