toolcraft 0.0.107 → 0.0.108
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/composition.json +2 -2
- package/dist/cli.d.ts +14 -2
- package/dist/cli.js +83 -27
- package/dist/composition.json +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/renderer.d.ts +1 -1
- package/dist/testing/harness.d.ts +11 -1
- package/dist/testing/harness.js +30 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/render-capture.d.ts +1 -1
- package/dist/testing/render-capture.js +3 -2
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"name": "toolcraft",
|
|
56
|
-
"version": "0.0.
|
|
56
|
+
"version": "0.0.108",
|
|
57
57
|
"license": "MIT"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
"name": "toolcraft-schema",
|
|
66
|
-
"version": "0.0.
|
|
66
|
+
"version": "0.0.108",
|
|
67
67
|
"license": "MIT"
|
|
68
68
|
}
|
|
69
69
|
]
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./node-require-shim.js";
|
|
2
2
|
import { configureTheme } from "toolcraft-design";
|
|
3
|
-
import type { Group, HandlerFs, LogLevel, RuntimeLoggerInput } from "./index.js";
|
|
3
|
+
import type { Command, Group, HandlerFs, LogLevel, RenderPrimitives, RuntimeLoggerInput } from "./index.js";
|
|
4
4
|
import { type ErrorReportsOption } from "./error-report.js";
|
|
5
5
|
import type { HumanInLoopRuntimeOptions } from "./human-in-loop/types.js";
|
|
6
6
|
export { renderErrorReport } from "./error-report.js";
|
|
@@ -10,10 +10,21 @@ type Casing = "kebab" | "snake";
|
|
|
10
10
|
export interface CLIControls {
|
|
11
11
|
debug?: boolean;
|
|
12
12
|
logLevel?: boolean;
|
|
13
|
-
output?: boolean;
|
|
13
|
+
output?: boolean | CLIOutputControl;
|
|
14
14
|
verbose?: boolean;
|
|
15
15
|
yes?: boolean;
|
|
16
16
|
}
|
|
17
|
+
export interface CLIOutputFormatContext {
|
|
18
|
+
command: Command<any, any, any, any>;
|
|
19
|
+
commandPath: string;
|
|
20
|
+
primitives: RenderPrimitives;
|
|
21
|
+
result: unknown;
|
|
22
|
+
}
|
|
23
|
+
export type CLIOutputFormatRenderer = (context: CLIOutputFormatContext) => string | undefined;
|
|
24
|
+
export type CLIOutputFormats = Readonly<Record<string, CLIOutputFormatRenderer>>;
|
|
25
|
+
export interface CLIOutputControl {
|
|
26
|
+
formats?: CLIOutputFormats;
|
|
27
|
+
}
|
|
17
28
|
export interface RunCLIOptions<TServices extends object = Record<string, unknown>> {
|
|
18
29
|
apiVersion?: string;
|
|
19
30
|
approvals?: boolean;
|
|
@@ -48,6 +59,7 @@ export interface CLICommandTreeSnapshotOption {
|
|
|
48
59
|
positional?: boolean;
|
|
49
60
|
global?: boolean;
|
|
50
61
|
dynamic?: boolean;
|
|
62
|
+
choices?: string[];
|
|
51
63
|
}
|
|
52
64
|
export interface CLICommandTreeSnapshotCommand {
|
|
53
65
|
kind: "command";
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { Command as CommanderCommand, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
5
5
|
import { validate as validateSchema } from "toolcraft-schema";
|
|
6
6
|
import { cancel, configureTheme, confirm, createLogger, formatCommandList, formatOptionList, getTheme, helpFormatterPlain, isCancel, note, promptText, renderTable, resetOutputFormatCache, select, text } from "toolcraft-design";
|
|
7
|
-
import { ApprovalDeclinedError, UserError, assertCommandRequirements, getCommandSourcePath, hasMcpProxyConfig, resolveCommandSecrets } from "./index.js";
|
|
7
|
+
import { ApprovalDeclinedError, ToolcraftBugError, UserError, assertCommandRequirements, getCommandSourcePath, hasMcpProxyConfig, resolveCommandSecrets } from "./index.js";
|
|
8
8
|
import { hasOwnErrorCode } from "./error-codes.js";
|
|
9
9
|
import { writeErrorReport } from "./error-report.js";
|
|
10
10
|
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
@@ -662,14 +662,35 @@ function createOption(field, globalLongOptionFlags) {
|
|
|
662
662
|
return [option];
|
|
663
663
|
}
|
|
664
664
|
function resolveCLIControls(controls) {
|
|
665
|
+
const outputFormats = typeof controls?.output === "object" ? (controls.output.formats ?? {}) : {};
|
|
666
|
+
validateOutputFormats(outputFormats);
|
|
665
667
|
return {
|
|
666
668
|
debug: controls?.debug === true,
|
|
667
669
|
logLevel: controls?.logLevel === true,
|
|
668
|
-
output: controls?.output === true,
|
|
670
|
+
output: controls?.output === true || typeof controls?.output === "object",
|
|
671
|
+
outputFormats,
|
|
669
672
|
verbose: controls?.verbose === true,
|
|
670
673
|
yes: controls?.yes === true
|
|
671
674
|
};
|
|
672
675
|
}
|
|
676
|
+
const BUILT_IN_OUTPUT_FORMATS = ["rich", "md", "markdown", "json"];
|
|
677
|
+
function outputFormatNames(controls) {
|
|
678
|
+
return [...BUILT_IN_OUTPUT_FORMATS, ...Object.keys(controls.outputFormats)];
|
|
679
|
+
}
|
|
680
|
+
function validateOutputFormats(formats) {
|
|
681
|
+
for (const [name, renderer] of Object.entries(formats)) {
|
|
682
|
+
const hasWhitespace = name.split("").some((character) => character.trim() === "");
|
|
683
|
+
if (name.length === 0 || name.trim() !== name || hasWhitespace) {
|
|
684
|
+
throw new ToolcraftBugError(`Custom output format names must be non-empty and contain no whitespace: ${JSON.stringify(name)}.`);
|
|
685
|
+
}
|
|
686
|
+
if (BUILT_IN_OUTPUT_FORMATS.includes(name)) {
|
|
687
|
+
throw new ToolcraftBugError(`Custom output format "${name}" conflicts with a built-in format.`);
|
|
688
|
+
}
|
|
689
|
+
if (typeof renderer !== "function") {
|
|
690
|
+
throw new ToolcraftBugError(`Custom output format "${name}" must define a renderer function.`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
673
694
|
function getGlobalLongOptionFlags(presetsEnabled, versionEnabled, controls) {
|
|
674
695
|
const flags = [];
|
|
675
696
|
if (presetsEnabled) {
|
|
@@ -1129,7 +1150,7 @@ function formatGlobalOptionsLine(ctx) {
|
|
|
1129
1150
|
flags.push("--yes");
|
|
1130
1151
|
}
|
|
1131
1152
|
if (ctx.controls.output) {
|
|
1132
|
-
flags.push(
|
|
1153
|
+
flags.push(`--output <${outputFormatNames(ctx.controls).join("|")}>`);
|
|
1133
1154
|
}
|
|
1134
1155
|
if (ctx.controls.verbose) {
|
|
1135
1156
|
flags.push("-v, --verbose");
|
|
@@ -1535,13 +1556,15 @@ function createGlobalSnapshotOptions(presetsEnabled, versionEnabled, controls) {
|
|
|
1535
1556
|
});
|
|
1536
1557
|
}
|
|
1537
1558
|
if (controls.output) {
|
|
1559
|
+
const choices = outputFormatNames(controls);
|
|
1538
1560
|
options.push({
|
|
1539
1561
|
name: "output",
|
|
1540
1562
|
flags: ["--output"],
|
|
1541
1563
|
type: "enum",
|
|
1542
1564
|
required: false,
|
|
1543
1565
|
hidden: true,
|
|
1544
|
-
description: "Output format."
|
|
1566
|
+
description: "Output format.",
|
|
1567
|
+
choices
|
|
1545
1568
|
});
|
|
1546
1569
|
}
|
|
1547
1570
|
if (controls.debug) {
|
|
@@ -1656,15 +1679,19 @@ function addGlobalOptions(command, presetsEnabled, controls) {
|
|
|
1656
1679
|
options.push(new Option("--yes", "Accept defaults and skip prompts."));
|
|
1657
1680
|
}
|
|
1658
1681
|
if (controls.output) {
|
|
1659
|
-
|
|
1682
|
+
const choices = outputFormatNames(controls);
|
|
1683
|
+
options.push(new Option("--output <format>", "Output format.").choices(choices).argParser((value) => {
|
|
1660
1684
|
if (value === "rich" || value === "md" || value === "json") {
|
|
1661
1685
|
return value;
|
|
1662
1686
|
}
|
|
1663
1687
|
if (value === "markdown") {
|
|
1664
1688
|
return "md";
|
|
1665
1689
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1690
|
+
if (Object.hasOwn(controls.outputFormats, value)) {
|
|
1691
|
+
return value;
|
|
1692
|
+
}
|
|
1693
|
+
throw new InvalidArgumentError(formatInvalidEnumMessage("--output", value, choices, {
|
|
1694
|
+
candidates: ["rich", "markdown", "json", ...Object.keys(controls.outputFormats)],
|
|
1668
1695
|
threshold: 3
|
|
1669
1696
|
}));
|
|
1670
1697
|
}));
|
|
@@ -1834,11 +1861,11 @@ function resolveOutput(resolvedFlags) {
|
|
|
1834
1861
|
return "json";
|
|
1835
1862
|
}
|
|
1836
1863
|
if (resolvedFlags.output !== undefined) {
|
|
1837
|
-
return resolvedFlags.output;
|
|
1864
|
+
return resolvedFlags.output === "markdown" ? "md" : resolvedFlags.output;
|
|
1838
1865
|
}
|
|
1839
1866
|
return "rich";
|
|
1840
1867
|
}
|
|
1841
|
-
function resolveOutputFromArgv(argv) {
|
|
1868
|
+
function resolveOutputFromArgv(argv, formats = {}) {
|
|
1842
1869
|
for (let index = 0; index < argv.length; index += 1) {
|
|
1843
1870
|
const token = argv[index] ?? "";
|
|
1844
1871
|
if (token === "--json") {
|
|
@@ -1855,6 +1882,9 @@ function resolveOutputFromArgv(argv) {
|
|
|
1855
1882
|
if (value === "markdown") {
|
|
1856
1883
|
return "md";
|
|
1857
1884
|
}
|
|
1885
|
+
if (value !== undefined && Object.hasOwn(formats, value)) {
|
|
1886
|
+
return value;
|
|
1887
|
+
}
|
|
1858
1888
|
continue;
|
|
1859
1889
|
}
|
|
1860
1890
|
if (token.startsWith("--output=")) {
|
|
@@ -1865,17 +1895,21 @@ function resolveOutputFromArgv(argv) {
|
|
|
1865
1895
|
if (value === "markdown") {
|
|
1866
1896
|
return "md";
|
|
1867
1897
|
}
|
|
1898
|
+
if (Object.hasOwn(formats, value)) {
|
|
1899
|
+
return value;
|
|
1900
|
+
}
|
|
1868
1901
|
}
|
|
1869
1902
|
}
|
|
1870
1903
|
return "rich";
|
|
1871
1904
|
}
|
|
1872
|
-
const DESIGN_SYSTEM_OUTPUT_BY_MODE = {
|
|
1873
|
-
rich: "terminal",
|
|
1874
|
-
md: "markdown",
|
|
1875
|
-
json: "json"
|
|
1876
|
-
};
|
|
1877
1905
|
function toDesignSystemOutput(output) {
|
|
1878
|
-
|
|
1906
|
+
if (output === "md") {
|
|
1907
|
+
return "markdown";
|
|
1908
|
+
}
|
|
1909
|
+
if (output === "json") {
|
|
1910
|
+
return "json";
|
|
1911
|
+
}
|
|
1912
|
+
return "terminal";
|
|
1879
1913
|
}
|
|
1880
1914
|
async function withOutputFormat(output, fn) {
|
|
1881
1915
|
const previous = process.env.OUTPUT_FORMAT;
|
|
@@ -2994,17 +3028,18 @@ function getResolvedFlags(command) {
|
|
|
2994
3028
|
const flags = command.optsWithGlobals();
|
|
2995
3029
|
return flags;
|
|
2996
3030
|
}
|
|
2997
|
-
async function executeCommand(state, services, requirementOptions, runtimeFetch, runtimeOptions, runtimeEnv, runtimeFs, outputEmitter, promptStreams, diagnosticsOptions, onErrorReportContext) {
|
|
3031
|
+
async function executeCommand(state, services, requirementOptions, runtimeFetch, runtimeOptions, runtimeEnv, runtimeFs, outputEmitter, outputFormats, promptStreams, diagnosticsOptions, onErrorReportContext) {
|
|
2998
3032
|
const logger = createLogger(outputEmitter);
|
|
3033
|
+
const optionValues = state.actionCommand.optsWithGlobals();
|
|
3034
|
+
const resolvedFlags = optionValues;
|
|
3035
|
+
const output = resolveOutput(resolvedFlags);
|
|
2999
3036
|
const primitives = {
|
|
3000
3037
|
logger,
|
|
3001
3038
|
renderTable,
|
|
3002
3039
|
getTheme,
|
|
3003
|
-
note
|
|
3040
|
+
note,
|
|
3041
|
+
outputFormat: output
|
|
3004
3042
|
};
|
|
3005
|
-
const optionValues = state.actionCommand.optsWithGlobals();
|
|
3006
|
-
const resolvedFlags = optionValues;
|
|
3007
|
-
const output = resolveOutput(resolvedFlags);
|
|
3008
3043
|
const diagnostics = createRuntimeLogger({
|
|
3009
3044
|
level: resolvedFlags.logLevel ??
|
|
3010
3045
|
(diagnosticsOptions.verboseControlEnabled && resolvedFlags.verbose
|
|
@@ -3021,7 +3056,7 @@ async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
|
3021
3056
|
? {
|
|
3022
3057
|
commandPath: state.commandPath,
|
|
3023
3058
|
params: {},
|
|
3024
|
-
output,
|
|
3059
|
+
output: "rich",
|
|
3025
3060
|
stdinTTY,
|
|
3026
3061
|
stdoutTTY
|
|
3027
3062
|
}
|
|
@@ -3091,9 +3126,9 @@ async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
|
3091
3126
|
}
|
|
3092
3127
|
}
|
|
3093
3128
|
else {
|
|
3094
|
-
|
|
3129
|
+
renderCLIResult(state.command, state.commandPath, event, output, primitives, outputFormats, outputEmitter === undefined
|
|
3095
3130
|
? undefined
|
|
3096
|
-
: (chunk) => outputEmitter(chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk));
|
|
3131
|
+
: (chunk) => outputEmitter(chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk), outputEmitter);
|
|
3097
3132
|
}
|
|
3098
3133
|
}
|
|
3099
3134
|
}
|
|
@@ -3137,9 +3172,9 @@ async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
|
3137
3172
|
renderHumanInLoopPending(result);
|
|
3138
3173
|
return;
|
|
3139
3174
|
}
|
|
3140
|
-
const renderStatus =
|
|
3175
|
+
const renderStatus = renderCLIResult(state.command, state.commandPath, result, output, primitives, outputFormats, outputEmitter === undefined
|
|
3141
3176
|
? undefined
|
|
3142
|
-
: (chunk) => outputEmitter(chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk));
|
|
3177
|
+
: (chunk) => outputEmitter(chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk), outputEmitter);
|
|
3143
3178
|
if (renderStatus.mcpError) {
|
|
3144
3179
|
process.exitCode = 1;
|
|
3145
3180
|
}
|
|
@@ -3155,6 +3190,25 @@ async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
|
3155
3190
|
throw error;
|
|
3156
3191
|
}
|
|
3157
3192
|
}
|
|
3193
|
+
function renderCLIResult(command, commandPath, result, output, primitives, outputFormats, write, emitExact) {
|
|
3194
|
+
const customRenderer = outputFormats[output];
|
|
3195
|
+
if (customRenderer === undefined) {
|
|
3196
|
+
return renderResult(command, result, output, primitives, write);
|
|
3197
|
+
}
|
|
3198
|
+
const payload = customRenderer({ command, commandPath, primitives, result });
|
|
3199
|
+
if (payload !== undefined && payload.length > 0) {
|
|
3200
|
+
if (emitExact !== undefined) {
|
|
3201
|
+
emitExact(payload);
|
|
3202
|
+
}
|
|
3203
|
+
else if (write === undefined) {
|
|
3204
|
+
process.stdout.write(payload);
|
|
3205
|
+
}
|
|
3206
|
+
else {
|
|
3207
|
+
write(payload);
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
return { mcpError: false };
|
|
3211
|
+
}
|
|
3158
3212
|
function isStringRecord(value) {
|
|
3159
3213
|
return isPlainObject(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
3160
3214
|
}
|
|
@@ -3723,7 +3777,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
3723
3777
|
const execute = async (state) => {
|
|
3724
3778
|
lastActionCommand = state.actionCommand;
|
|
3725
3779
|
resolvedCommandPath = formatCliCommandPath(state.commandPath);
|
|
3726
|
-
await executeCommand(state, servicesWithBuiltIns, requirementOptions, runtimeFetch, runtimeOptions, options.env, options.fs, options.outputEmitter, {
|
|
3780
|
+
await executeCommand(state, servicesWithBuiltIns, requirementOptions, runtimeFetch, runtimeOptions, options.env, options.fs, options.outputEmitter, controls.outputFormats, {
|
|
3727
3781
|
input: options.promptInput,
|
|
3728
3782
|
output: options.promptOutput
|
|
3729
3783
|
}, {
|
|
@@ -3785,7 +3839,9 @@ export async function runCLI(roots, options = {}) {
|
|
|
3785
3839
|
debugStackMode: resolvedFlags !== undefined
|
|
3786
3840
|
? resolveDebugStackMode(resolvedFlags.debug)
|
|
3787
3841
|
: getDebugStackModeFromArgv(argv),
|
|
3788
|
-
output: resolvedFlags !== undefined
|
|
3842
|
+
output: resolvedFlags !== undefined
|
|
3843
|
+
? resolveOutput(resolvedFlags)
|
|
3844
|
+
: resolveOutputFromArgv(argv, controls.outputFormats),
|
|
3789
3845
|
verbose: resolvedFlags ? Boolean(resolvedFlags.verbose) : argv.includes("--verbose"),
|
|
3790
3846
|
program,
|
|
3791
3847
|
argv,
|
package/dist/composition.json
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"name": "toolcraft",
|
|
56
|
-
"version": "0.0.
|
|
56
|
+
"version": "0.0.108",
|
|
57
57
|
"license": "MIT"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
"name": "toolcraft-schema",
|
|
66
|
-
"version": "0.0.
|
|
66
|
+
"version": "0.0.108",
|
|
67
67
|
"license": "MIT"
|
|
68
68
|
}
|
|
69
69
|
]
|
package/dist/index.d.ts
CHANGED
package/dist/renderer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command, RenderPrimitives } from "./index.js";
|
|
2
|
-
export type OutputMode = "rich" | "md" | "json";
|
|
2
|
+
export type OutputMode = "rich" | "md" | "json" | (string & {});
|
|
3
3
|
type WriteStream = "stdout" | "stderr";
|
|
4
4
|
type WriteFn = (chunk: string, stream?: WriteStream) => void;
|
|
5
5
|
export interface RenderResultStatus {
|
|
@@ -4,6 +4,7 @@ import { type FetchRoute } from "./fakes.js";
|
|
|
4
4
|
import { type FsChange, type MemoryFs } from "./memory-fs.js";
|
|
5
5
|
import { type ParityResult } from "./parity.js";
|
|
6
6
|
import { type StreamStatusEvent } from "../stream.js";
|
|
7
|
+
import { type CLIOutputFormats } from "../cli.js";
|
|
7
8
|
export type PipelineStage = "resolve" | "secrets" | "requirements" | "params" | "confirm" | "handler" | "render";
|
|
8
9
|
export type EffectEvent = {
|
|
9
10
|
seq: number;
|
|
@@ -67,6 +68,7 @@ export interface RunResult<T> {
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
export interface CommandTestHarness {
|
|
71
|
+
cli(args: string[], options?: HarnessCLIOptions): Promise<HarnessCLIResult>;
|
|
70
72
|
run<T>(path: string[], params?: Record<string, unknown>): Promise<RunResult<T>>;
|
|
71
73
|
stream<T>(path: string[], params?: Record<string, unknown>, options?: {
|
|
72
74
|
limit?: number;
|
|
@@ -76,6 +78,14 @@ export interface CommandTestHarness {
|
|
|
76
78
|
fs: MemoryFs;
|
|
77
79
|
timeline: EffectEvent[];
|
|
78
80
|
}
|
|
81
|
+
export interface HarnessCLIOptions {
|
|
82
|
+
formats?: CLIOutputFormats;
|
|
83
|
+
}
|
|
84
|
+
export interface HarnessCLIResult {
|
|
85
|
+
exitCode: number;
|
|
86
|
+
stdout: string;
|
|
87
|
+
stderr: string;
|
|
88
|
+
}
|
|
79
89
|
export interface StreamRunResult<T> {
|
|
80
90
|
ok: boolean;
|
|
81
91
|
events: T[];
|
|
@@ -83,5 +93,5 @@ export interface StreamRunResult<T> {
|
|
|
83
93
|
error?: unknown;
|
|
84
94
|
}
|
|
85
95
|
type EmptyHarnessServices = Record<string, never>;
|
|
86
|
-
export declare function createCommandTestHarness<TServices extends object = EmptyHarnessServices>(root: Group
|
|
96
|
+
export declare function createCommandTestHarness<TServices extends object = EmptyHarnessServices>(root: Group<TServices>, options?: HarnessOptions<TServices>): CommandTestHarness;
|
|
87
97
|
export {};
|
package/dist/testing/harness.js
CHANGED
|
@@ -12,6 +12,7 @@ import { createMemoryFs } from "./memory-fs.js";
|
|
|
12
12
|
import { runParity } from "./parity.js";
|
|
13
13
|
import { createRenderCapture } from "./render-capture.js";
|
|
14
14
|
import { createManagedStream } from "../stream.js";
|
|
15
|
+
import { runCLI } from "../cli.js";
|
|
15
16
|
function isHandlerFs(value) {
|
|
16
17
|
return typeof value.readFile === "function";
|
|
17
18
|
}
|
|
@@ -186,6 +187,33 @@ export function createCommandTestHarness(root, options = {}) {
|
|
|
186
187
|
return {
|
|
187
188
|
fs: memoryFs,
|
|
188
189
|
timeline: cumulativeTimeline,
|
|
190
|
+
async cli(args, cliOptions = {}) {
|
|
191
|
+
let stdout = "";
|
|
192
|
+
const previousExitCode = process.exitCode;
|
|
193
|
+
process.exitCode = undefined;
|
|
194
|
+
try {
|
|
195
|
+
await runCLI(root, {
|
|
196
|
+
argv: ["node", root.name, ...args],
|
|
197
|
+
controls: { output: { formats: cliOptions.formats } },
|
|
198
|
+
services,
|
|
199
|
+
env: Object.fromEntries(Object.entries(options.env ?? {}).flatMap(([key, value]) => value === undefined ? [] : [[key, value]])),
|
|
200
|
+
fs: memoryFs,
|
|
201
|
+
fetch: runtimeFetch,
|
|
202
|
+
logLevel: options.logLevel,
|
|
203
|
+
outputEmitter(entry) {
|
|
204
|
+
stdout += entry;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
exitCode: process.exitCode ?? 0,
|
|
209
|
+
stdout,
|
|
210
|
+
stderr: ""
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
process.exitCode = previousExitCode;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
189
217
|
async stream(requestedPath, inputParams = {}, streamOptions = {}) {
|
|
190
218
|
const events = [];
|
|
191
219
|
const statuses = [];
|
|
@@ -444,11 +472,11 @@ export function createCommandTestHarness(root, options = {}) {
|
|
|
444
472
|
rendered.rich = capture.output();
|
|
445
473
|
}
|
|
446
474
|
if (command.render?.markdown) {
|
|
447
|
-
const capture = createRenderCapture();
|
|
475
|
+
const capture = createRenderCapture("md");
|
|
448
476
|
rendered.markdown = stripAnsi(command.render.markdown(invoked, capture.primitives));
|
|
449
477
|
}
|
|
450
478
|
if (command.render?.json) {
|
|
451
|
-
const capture = createRenderCapture();
|
|
479
|
+
const capture = createRenderCapture("json");
|
|
452
480
|
rendered.json = command.render.json(invoked, capture.primitives);
|
|
453
481
|
}
|
|
454
482
|
return result({ ok: true, failedAt: undefined });
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createCommandTestHarness, type CommandTestHarness, type ConfirmationRequest, type EffectEvent, type HarnessOptions, type PipelineStage, type RunResult, type StreamRunResult } from "./harness.js";
|
|
1
|
+
export { createCommandTestHarness, type CommandTestHarness, type ConfirmationRequest, type EffectEvent, type HarnessCLIOptions, type HarnessCLIResult, type HarnessOptions, type PipelineStage, type RunResult, type StreamRunResult } from "./harness.js";
|
|
2
2
|
export { fakeFetch, fakeService, type FetchRoute, type ServiceCall } from "./fakes.js";
|
|
3
3
|
export { createMemoryFs, type FsChange, type MemoryFs } from "./memory-fs.js";
|
|
4
4
|
export type { ParityResult, SurfaceOutcome } from "./parity.js";
|
|
@@ -32,7 +32,7 @@ function createCaptureTheme() {
|
|
|
32
32
|
intro: (text) => ` Poe - ${text} `
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
export function createRenderCapture() {
|
|
35
|
+
export function createRenderCapture(outputFormat = "rich") {
|
|
36
36
|
const output = [];
|
|
37
37
|
const captureTheme = createCaptureTheme();
|
|
38
38
|
const emit = (message) => {
|
|
@@ -47,7 +47,8 @@ export function createRenderCapture() {
|
|
|
47
47
|
maxWidth: captureWidth
|
|
48
48
|
}))),
|
|
49
49
|
getTheme: () => captureTheme,
|
|
50
|
-
note: (message, title) => emit(title === undefined ? message : `${title}\n${message}`)
|
|
50
|
+
note: (message, title) => emit(title === undefined ? message : `${title}\n${message}`),
|
|
51
|
+
outputFormat
|
|
51
52
|
},
|
|
52
53
|
output: () => output.join("\n")
|
|
53
54
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.108",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"auth-store"
|
|
151
151
|
],
|
|
152
152
|
"optionalDependencies": {
|
|
153
|
-
"toolcraft-schema": "0.0.
|
|
153
|
+
"toolcraft-schema": "0.0.108",
|
|
154
154
|
"toolcraft-design": "^0.0.2",
|
|
155
155
|
"@poe-code/frontmatter": "*",
|
|
156
156
|
"@poe-code/agent-mcp-config": "*",
|