@united-workforce/cli 0.4.0 → 0.5.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 +30 -3
- package/dist/.build-fingerprint +1 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js +16 -6
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/concurrency.test.d.ts +2 -0
- package/dist/__tests__/concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/concurrency.test.js +196 -0
- package/dist/__tests__/concurrency.test.js.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.js +23 -7
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/format-text-default.test.d.ts +2 -0
- package/dist/__tests__/format-text-default.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-default.test.js +43 -0
- package/dist/__tests__/format-text-default.test.js.map +1 -0
- package/dist/__tests__/format-text-registry.test.d.ts +2 -0
- package/dist/__tests__/format-text-registry.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-registry.test.js +158 -0
- package/dist/__tests__/format-text-registry.test.js.map +1 -0
- package/dist/__tests__/log-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/log-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/log-text-renderer.test.js +265 -0
- package/dist/__tests__/log-text-renderer.test.js.map +1 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js +102 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js +22 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +9 -7
- package/dist/__tests__/pid-recycling.test.js.map +1 -1
- package/dist/__tests__/prompt.test.js +46 -4
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +8 -0
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/solve-issue-tea-worktree.test.js +3 -1
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.js +9 -1
- package/dist/__tests__/step-ask.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +19 -17
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +19 -13
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js +110 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-list-filters.test.js +10 -8
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts +2 -0
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js +102 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts +2 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js +157 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js.map +1 -0
- package/dist/__tests__/thread-poke.test.js +11 -1
- package/dist/__tests__/thread-poke.test.js.map +1 -1
- package/dist/__tests__/thread-read-xml-tags.test.js +10 -9
- package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -1
- package/dist/__tests__/thread-resume.test.js +11 -1
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-start-cwd-cli.test.js +15 -3
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js +148 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.js +5 -2
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-test-helpers.d.ts +7 -0
- package/dist/__tests__/thread-test-helpers.d.ts.map +1 -1
- package/dist/__tests__/thread-test-helpers.js +13 -0
- package/dist/__tests__/thread-test-helpers.js.map +1 -1
- package/dist/__tests__/thread.test.js +11 -9
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +56 -2
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.js +10 -7
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -1
- package/dist/__tests__/workflow-resolution.test.js +10 -7
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.js +10 -7
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-validate.test.js +75 -55
- package/dist/__tests__/workflow-validate.test.js.map +1 -1
- package/dist/__tests__/write-envelope.test.d.ts +2 -0
- package/dist/__tests__/write-envelope.test.d.ts.map +1 -0
- package/dist/__tests__/write-envelope.test.js +201 -0
- package/dist/__tests__/write-envelope.test.js.map +1 -0
- package/dist/cli.js +58 -35
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +12 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +42 -29
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +9 -4
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +51 -7
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +44 -2
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +2 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/concurrency/concurrency.d.ts +34 -0
- package/dist/concurrency/concurrency.d.ts.map +1 -0
- package/dist/concurrency/concurrency.js +216 -0
- package/dist/concurrency/concurrency.js.map +1 -0
- package/dist/concurrency/index.d.ts +3 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +2 -0
- package/dist/concurrency/index.js.map +1 -0
- package/dist/concurrency/types.d.ts +19 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +2 -0
- package/dist/concurrency/types.js.map +1 -0
- package/dist/format.d.ts +69 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +198 -1
- package/dist/format.js.map +1 -1
- package/dist/output-mappers.d.ts +122 -0
- package/dist/output-mappers.d.ts.map +1 -0
- package/dist/output-mappers.js +134 -0
- package/dist/output-mappers.js.map +1 -0
- package/dist/schemas.d.ts +4 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +31 -4
- package/dist/schemas.js.map +1 -1
- package/dist/text-renderers.d.ts +30 -0
- package/dist/text-renderers.d.ts.map +1 -0
- package/dist/text-renderers.js +251 -0
- package/dist/text-renderers.js.map +1 -0
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +28 -11
- package/dist/validate-semantic.js.map +1 -1
- package/examples/brainstorm.yaml +130 -0
- package/examples/debate.yaml +169 -0
- package/examples/socratic-questioning.yaml +112 -0
- package/package.json +5 -4
- package/src/__tests__/adapter-json-roundtrip.test.ts +15 -6
- package/src/__tests__/concurrency.test.ts +266 -0
- package/src/__tests__/e2e-mock-agent.test.ts +45 -7
- package/src/__tests__/format-text-default.test.ts +49 -0
- package/src/__tests__/format-text-registry.test.ts +173 -0
- package/src/__tests__/log-text-renderer.test.ts +294 -0
- package/src/__tests__/output-mapper-thread-list-startedat.test.ts +124 -0
- package/src/__tests__/output-mapper-workflow-add.test.ts +24 -0
- package/src/__tests__/pid-recycling.test.ts +9 -8
- package/src/__tests__/prompt.test.ts +48 -4
- package/src/__tests__/resolve-head-hash.test.ts +7 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +3 -1
- package/src/__tests__/step-ask.test.ts +8 -1
- package/src/__tests__/store-unified-threads.test.ts +21 -18
- package/src/__tests__/thread-cancel-status.test.ts +21 -14
- package/src/__tests__/thread-cancel-text-renderer.test.ts +125 -0
- package/src/__tests__/thread-list-filters.test.ts +9 -9
- package/src/__tests__/thread-list-template-ms-date.test.ts +110 -0
- package/src/__tests__/thread-list-workflow-corrupt.test.ts +198 -0
- package/src/__tests__/thread-poke.test.ts +10 -1
- package/src/__tests__/thread-read-xml-tags.test.ts +9 -11
- package/src/__tests__/thread-resume.test.ts +10 -1
- package/src/__tests__/thread-start-cwd-cli.test.ts +15 -3
- package/src/__tests__/thread-stop-text-renderer.test.ts +168 -0
- package/src/__tests__/thread-suspend-step.test.ts +5 -2
- package/src/__tests__/thread-test-helpers.ts +15 -1
- package/src/__tests__/thread.test.ts +10 -10
- package/src/__tests__/validate-semantic.test.ts +59 -2
- package/src/__tests__/workflow-list-recursive.test.ts +9 -9
- package/src/__tests__/workflow-resolution.test.ts +9 -8
- package/src/__tests__/workflow-show-resolution.test.ts +9 -8
- package/src/__tests__/workflow-validate.test.ts +78 -56
- package/src/__tests__/write-envelope.test.ts +257 -0
- package/src/cli.ts +92 -35
- package/src/commands/config.ts +11 -0
- package/src/commands/prompt.ts +42 -29
- package/src/commands/setup.ts +57 -7
- package/src/commands/thread.ts +48 -2
- package/src/commands/workflow.ts +3 -7
- package/src/concurrency/concurrency.ts +245 -0
- package/src/concurrency/index.ts +10 -0
- package/src/concurrency/types.ts +19 -0
- package/src/format.ts +282 -2
- package/src/output-mappers.ts +254 -0
- package/src/schemas.ts +39 -3
- package/src/text-renderers.ts +355 -0
- package/src/validate-semantic.ts +33 -12
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --disable-warning=ExperimentalWarning
|
|
2
2
|
|
|
3
|
-
import type { CasRef, ThreadId, ThreadStatus } from "@united-workforce/protocol";
|
|
3
|
+
import type { CasRef, OutputSchemaName, ThreadId, ThreadStatus } from "@united-workforce/protocol";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { cmdConfigGet, cmdConfigList, cmdConfigSet } from "./commands/config.js";
|
|
6
6
|
import { cmdLogClean, cmdLogList, cmdLogShow } from "./commands/log.js";
|
|
@@ -32,12 +32,61 @@ import {
|
|
|
32
32
|
cmdWorkflowShow,
|
|
33
33
|
cmdWorkflowValidate,
|
|
34
34
|
} from "./commands/workflow.js";
|
|
35
|
-
import {
|
|
36
|
-
|
|
35
|
+
import {
|
|
36
|
+
formatOutput,
|
|
37
|
+
isOutputFormat,
|
|
38
|
+
type OutputFormat,
|
|
39
|
+
SUPPORTED_FORMATS,
|
|
40
|
+
writeEnvelope,
|
|
41
|
+
} from "./format.js";
|
|
42
|
+
import {
|
|
43
|
+
toStepDetailPayload,
|
|
44
|
+
toStepListPayload,
|
|
45
|
+
toThreadExecPayload,
|
|
46
|
+
toThreadListPayload,
|
|
47
|
+
toThreadStartPayload,
|
|
48
|
+
toThreadStatusPayload,
|
|
49
|
+
toValidateResultPayload,
|
|
50
|
+
toWorkflowAddPayload,
|
|
51
|
+
toWorkflowDetailPayload,
|
|
52
|
+
toWorkflowListPayload,
|
|
53
|
+
} from "./output-mappers.js";
|
|
54
|
+
import { createUwfStore, resolveStorageRoot } from "./store.js";
|
|
55
|
+
|
|
56
|
+
function getFormat(): OutputFormat {
|
|
57
|
+
const raw = program.opts().format as string;
|
|
58
|
+
if (!isOutputFormat(raw)) {
|
|
59
|
+
process.stderr.write(
|
|
60
|
+
`Invalid --format: ${raw}. Must be one of: ${SUPPORTED_FORMATS.join(", ")}\n`,
|
|
61
|
+
);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return raw;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function writeOutput(
|
|
68
|
+
payload: unknown,
|
|
69
|
+
schemaName: OutputSchemaName,
|
|
70
|
+
storageRoot: string,
|
|
71
|
+
): Promise<void> {
|
|
72
|
+
const fmt = getFormat();
|
|
73
|
+
const uwf = await createUwfStore(storageRoot);
|
|
74
|
+
await writeEnvelope(payload, schemaName, {
|
|
75
|
+
format: fmt,
|
|
76
|
+
store: uwf.store,
|
|
77
|
+
schemas: uwf.schemas,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
37
80
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Legacy raw output for commands without an output schema (log/config/setup).
|
|
83
|
+
* Always emits text/JSON/YAML based on the active --format. For `text`
|
|
84
|
+
* (the default) it renders via the per-command registry when available
|
|
85
|
+
* and falls back to JSON.
|
|
86
|
+
*/
|
|
87
|
+
function writeRawOutput(data: unknown, commandPath?: string): void {
|
|
88
|
+
const fmt = getFormat();
|
|
89
|
+
process.stdout.write(`${formatOutput(data, fmt, commandPath)}\n`);
|
|
41
90
|
}
|
|
42
91
|
|
|
43
92
|
function runAction(action: () => Promise<void>): void {
|
|
@@ -60,7 +109,11 @@ program
|
|
|
60
109
|
" workflow → thread → step → turn",
|
|
61
110
|
)
|
|
62
111
|
.version(pkg.default.version, "-V, --version");
|
|
63
|
-
program.option(
|
|
112
|
+
program.option(
|
|
113
|
+
"--format <fmt>",
|
|
114
|
+
"Output format: text (default), json, yaml, raw-json, raw-yaml",
|
|
115
|
+
"text",
|
|
116
|
+
);
|
|
64
117
|
|
|
65
118
|
const workflow = program
|
|
66
119
|
.command("workflow")
|
|
@@ -74,7 +127,7 @@ workflow
|
|
|
74
127
|
const storageRoot = resolveStorageRoot();
|
|
75
128
|
runAction(async () => {
|
|
76
129
|
const result = await cmdWorkflowAdd(storageRoot, file);
|
|
77
|
-
writeOutput(result);
|
|
130
|
+
await writeOutput(toWorkflowAddPayload(result), "workflow-add", storageRoot);
|
|
78
131
|
});
|
|
79
132
|
});
|
|
80
133
|
|
|
@@ -83,9 +136,13 @@ workflow
|
|
|
83
136
|
.description("Validate a workflow YAML without registering it (CI-friendly)")
|
|
84
137
|
.argument("<file>", "Workflow YAML file")
|
|
85
138
|
.action((file: string) => {
|
|
139
|
+
const storageRoot = resolveStorageRoot();
|
|
86
140
|
runAction(async () => {
|
|
87
|
-
await cmdWorkflowValidate(file);
|
|
88
|
-
|
|
141
|
+
const errors = await cmdWorkflowValidate(file);
|
|
142
|
+
await writeOutput(toValidateResultPayload(errors), "validate-result", storageRoot);
|
|
143
|
+
if (errors.length > 0) {
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
89
146
|
});
|
|
90
147
|
});
|
|
91
148
|
|
|
@@ -97,7 +154,7 @@ workflow
|
|
|
97
154
|
const storageRoot = resolveStorageRoot();
|
|
98
155
|
runAction(async () => {
|
|
99
156
|
const result = await cmdWorkflowShow(storageRoot, id, process.cwd());
|
|
100
|
-
writeOutput(result);
|
|
157
|
+
await writeOutput(toWorkflowDetailPayload(result), "workflow-detail", storageRoot);
|
|
101
158
|
});
|
|
102
159
|
});
|
|
103
160
|
|
|
@@ -108,7 +165,7 @@ workflow
|
|
|
108
165
|
const storageRoot = resolveStorageRoot();
|
|
109
166
|
runAction(async () => {
|
|
110
167
|
const result = await cmdWorkflowList(storageRoot, process.cwd());
|
|
111
|
-
writeOutput(result);
|
|
168
|
+
await writeOutput(toWorkflowListPayload(result), "workflow-list", storageRoot);
|
|
112
169
|
});
|
|
113
170
|
});
|
|
114
171
|
|
|
@@ -130,7 +187,7 @@ thread
|
|
|
130
187
|
process.cwd(),
|
|
131
188
|
opts.cwd ?? process.cwd(),
|
|
132
189
|
);
|
|
133
|
-
writeOutput(result);
|
|
190
|
+
await writeOutput(toThreadStartPayload(result), "thread-start", storageRoot);
|
|
134
191
|
});
|
|
135
192
|
});
|
|
136
193
|
|
|
@@ -167,11 +224,7 @@ thread
|
|
|
167
224
|
background,
|
|
168
225
|
backgroundWorker,
|
|
169
226
|
);
|
|
170
|
-
|
|
171
|
-
writeOutput(results[0]);
|
|
172
|
-
} else {
|
|
173
|
-
writeOutput(results);
|
|
174
|
-
}
|
|
227
|
+
await writeOutput(toThreadExecPayload(results), "thread-exec", storageRoot);
|
|
175
228
|
});
|
|
176
229
|
},
|
|
177
230
|
);
|
|
@@ -184,7 +237,7 @@ thread
|
|
|
184
237
|
const storageRoot = resolveStorageRoot();
|
|
185
238
|
runAction(async () => {
|
|
186
239
|
const result = await cmdThreadShow(storageRoot, threadId);
|
|
187
|
-
writeOutput(result);
|
|
240
|
+
await writeOutput(toThreadStatusPayload(result), "thread-status", storageRoot);
|
|
188
241
|
});
|
|
189
242
|
});
|
|
190
243
|
|
|
@@ -292,7 +345,7 @@ thread
|
|
|
292
345
|
take,
|
|
293
346
|
showAll,
|
|
294
347
|
);
|
|
295
|
-
writeOutput(result);
|
|
348
|
+
await writeOutput(toThreadListPayload(result), "thread-list", storageRoot);
|
|
296
349
|
});
|
|
297
350
|
},
|
|
298
351
|
);
|
|
@@ -314,7 +367,7 @@ thread
|
|
|
314
367
|
supplement,
|
|
315
368
|
agentOverride,
|
|
316
369
|
);
|
|
317
|
-
writeOutput(result);
|
|
370
|
+
await writeOutput(toThreadStatusPayload(result), "thread-status", storageRoot);
|
|
318
371
|
});
|
|
319
372
|
});
|
|
320
373
|
|
|
@@ -334,7 +387,7 @@ thread
|
|
|
334
387
|
opts.prompt,
|
|
335
388
|
agentOverride,
|
|
336
389
|
);
|
|
337
|
-
writeOutput(result);
|
|
390
|
+
await writeOutput(toThreadStatusPayload(result), "thread-status", storageRoot);
|
|
338
391
|
});
|
|
339
392
|
});
|
|
340
393
|
|
|
@@ -346,7 +399,7 @@ thread
|
|
|
346
399
|
const storageRoot = resolveStorageRoot();
|
|
347
400
|
runAction(async () => {
|
|
348
401
|
const result = await cmdThreadStop(storageRoot, threadId);
|
|
349
|
-
|
|
402
|
+
writeRawOutput(result, "thread stop");
|
|
350
403
|
});
|
|
351
404
|
});
|
|
352
405
|
|
|
@@ -358,7 +411,7 @@ thread
|
|
|
358
411
|
const storageRoot = resolveStorageRoot();
|
|
359
412
|
runAction(async () => {
|
|
360
413
|
const result = await cmdThreadCancel(storageRoot, threadId);
|
|
361
|
-
|
|
414
|
+
writeRawOutput(result, "thread cancel");
|
|
362
415
|
});
|
|
363
416
|
});
|
|
364
417
|
|
|
@@ -401,7 +454,7 @@ step
|
|
|
401
454
|
const storageRoot = resolveStorageRoot();
|
|
402
455
|
runAction(async () => {
|
|
403
456
|
const result = await cmdStepList(storageRoot, threadId);
|
|
404
|
-
writeOutput(result);
|
|
457
|
+
await writeOutput(toStepListPayload(result), "step-list", storageRoot);
|
|
405
458
|
});
|
|
406
459
|
});
|
|
407
460
|
|
|
@@ -413,7 +466,11 @@ step
|
|
|
413
466
|
const storageRoot = resolveStorageRoot();
|
|
414
467
|
runAction(async () => {
|
|
415
468
|
const detail = await cmdStepShow(storageRoot, stepHash as CasRef);
|
|
416
|
-
writeOutput(
|
|
469
|
+
await writeOutput(
|
|
470
|
+
toStepDetailPayload(stepHash as CasRef, detail),
|
|
471
|
+
"step-detail",
|
|
472
|
+
storageRoot,
|
|
473
|
+
);
|
|
417
474
|
});
|
|
418
475
|
});
|
|
419
476
|
|
|
@@ -475,7 +532,7 @@ step
|
|
|
475
532
|
const storageRoot = resolveStorageRoot();
|
|
476
533
|
runAction(async () => {
|
|
477
534
|
const result = await cmdStepFork(storageRoot, stepHash as CasRef);
|
|
478
|
-
|
|
535
|
+
writeRawOutput(result);
|
|
479
536
|
});
|
|
480
537
|
});
|
|
481
538
|
|
|
@@ -618,7 +675,7 @@ program
|
|
|
618
675
|
.command("setup")
|
|
619
676
|
.description(
|
|
620
677
|
"Configure the default agent. Run without --agent for interactive wizard.\n" +
|
|
621
|
-
"LLM
|
|
678
|
+
"Each adapter owns its own LLM configuration — the engine config is LLM-free.",
|
|
622
679
|
)
|
|
623
680
|
.option("--agent <name>", "Default agent adapter (e.g. hermes → uwf-hermes)")
|
|
624
681
|
.action((opts: { agent?: string }) => {
|
|
@@ -626,7 +683,7 @@ program
|
|
|
626
683
|
runAction(async () => {
|
|
627
684
|
if (opts.agent !== undefined && opts.agent !== "") {
|
|
628
685
|
const result = await cmdSetup({ agent: opts.agent, storageRoot });
|
|
629
|
-
|
|
686
|
+
writeRawOutput(result);
|
|
630
687
|
} else {
|
|
631
688
|
await cmdSetupInteractive(storageRoot);
|
|
632
689
|
}
|
|
@@ -642,7 +699,7 @@ log
|
|
|
642
699
|
const storageRoot = resolveStorageRoot();
|
|
643
700
|
runAction(async () => {
|
|
644
701
|
const result = await cmdLogList(storageRoot);
|
|
645
|
-
|
|
702
|
+
writeRawOutput(result, "log list");
|
|
646
703
|
});
|
|
647
704
|
});
|
|
648
705
|
|
|
@@ -665,7 +722,7 @@ log
|
|
|
665
722
|
process: opts.process ?? null,
|
|
666
723
|
date: opts.date ?? null,
|
|
667
724
|
});
|
|
668
|
-
|
|
725
|
+
writeRawOutput(result, "log show");
|
|
669
726
|
});
|
|
670
727
|
},
|
|
671
728
|
);
|
|
@@ -678,7 +735,7 @@ log
|
|
|
678
735
|
const storageRoot = resolveStorageRoot();
|
|
679
736
|
runAction(async () => {
|
|
680
737
|
const result = await cmdLogClean(storageRoot, opts.before);
|
|
681
|
-
|
|
738
|
+
writeRawOutput(result);
|
|
682
739
|
});
|
|
683
740
|
});
|
|
684
741
|
|
|
@@ -691,7 +748,7 @@ config
|
|
|
691
748
|
const storageRoot = resolveStorageRoot();
|
|
692
749
|
runAction(async () => {
|
|
693
750
|
const result = await cmdConfigList(storageRoot);
|
|
694
|
-
|
|
751
|
+
writeRawOutput(result, "config list");
|
|
695
752
|
});
|
|
696
753
|
});
|
|
697
754
|
|
|
@@ -706,7 +763,7 @@ config
|
|
|
706
763
|
const storageRoot = resolveStorageRoot();
|
|
707
764
|
runAction(async () => {
|
|
708
765
|
const result = await cmdConfigGet(storageRoot, key);
|
|
709
|
-
|
|
766
|
+
writeRawOutput({ value: result }, "config get");
|
|
710
767
|
});
|
|
711
768
|
});
|
|
712
769
|
|
|
@@ -719,7 +776,7 @@ config
|
|
|
719
776
|
const storageRoot = resolveStorageRoot();
|
|
720
777
|
runAction(async () => {
|
|
721
778
|
const result = await cmdConfigSet(storageRoot, key, value);
|
|
722
|
-
|
|
779
|
+
writeRawOutput(result, "config set");
|
|
723
780
|
});
|
|
724
781
|
});
|
|
725
782
|
|
package/src/commands/config.ts
CHANGED
|
@@ -21,6 +21,11 @@ const VALID_CONFIG_KEYS: Record<
|
|
|
21
21
|
// No knownFields — workflow/role names are user-defined
|
|
22
22
|
},
|
|
23
23
|
defaultAgent: { nested: false },
|
|
24
|
+
concurrency: {
|
|
25
|
+
nested: true,
|
|
26
|
+
knownFields: ["maxRunning"],
|
|
27
|
+
minDepth: 2,
|
|
28
|
+
},
|
|
24
29
|
};
|
|
25
30
|
|
|
26
31
|
/**
|
|
@@ -264,6 +269,12 @@ export async function cmdConfigSet(
|
|
|
264
269
|
let parsedValue: unknown = value;
|
|
265
270
|
if (lastSegment === "args") {
|
|
266
271
|
parsedValue = parseArgsValue(value);
|
|
272
|
+
} else if (lastSegment === "maxRunning") {
|
|
273
|
+
const num = Number(value);
|
|
274
|
+
if (!Number.isInteger(num) || num < 1) {
|
|
275
|
+
throw new Error("Value for 'maxRunning' must be a positive integer");
|
|
276
|
+
}
|
|
277
|
+
parsedValue = num;
|
|
267
278
|
}
|
|
268
279
|
|
|
269
280
|
// Validate we're not setting a property on a non-object
|
package/src/commands/prompt.ts
CHANGED
|
@@ -148,44 +148,36 @@ pipx install 'hermes-agent[acp]'
|
|
|
148
148
|
pip install -e '.[acp]'
|
|
149
149
|
\`\`\`
|
|
150
150
|
|
|
151
|
-
### Step 2 — Configure
|
|
151
|
+
### Step 2 — Configure default agent
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
Run the interactive wizard:
|
|
154
154
|
|
|
155
155
|
\`\`\`bash
|
|
156
|
-
uwf setup
|
|
156
|
+
uwf setup
|
|
157
157
|
\`\`\`
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
**Preset providers** — when using a preset name, \`--base-url\` is auto-filled and can be omitted:
|
|
162
|
-
|
|
163
|
-
| Provider | Name | Default base URL |
|
|
164
|
-
|----------|------|-----------------|
|
|
165
|
-
| OpenAI | \`openai\` | https://api.openai.com/v1 |
|
|
166
|
-
| xAI | \`xai\` | https://api.x.ai/v1 |
|
|
167
|
-
| OpenRouter | \`openrouter\` | https://openrouter.ai/api/v1 |
|
|
168
|
-
| Venice | \`venice\` | https://api.venice.ai/api/v1 |
|
|
169
|
-
| Dashscope | \`dashscope\` | https://dashscope.aliyuncs.com/compatible-mode/v1 |
|
|
170
|
-
| DeepSeek | \`deepseek\` | https://api.deepseek.com/v1 |
|
|
171
|
-
| SiliconFlow | \`siliconflow\` | https://api.siliconflow.cn/v1 |
|
|
172
|
-
| VolcEngine | \`volcengine\` | https://ark.cn-beijing.volces.com/api/v3 |
|
|
173
|
-
| Kimi (Moonshot) | \`kimi\` | https://api.moonshot.cn/v1 |
|
|
174
|
-
| GLM (Zhipu AI) | \`glm\` | https://open.bigmodel.cn/api/paas/v4 |
|
|
175
|
-
| StepFun | \`stepfun\` | https://api.stepfun.com/v1 |
|
|
176
|
-
| MiniMax | \`minimax\` | https://api.minimax.io/v1 |
|
|
177
|
-
| Ollama (local) | \`ollama\` | http://localhost:11434/v1 |
|
|
178
|
-
|
|
179
|
-
For **non-preset providers**, you must specify \`--base-url\` manually.
|
|
180
|
-
|
|
181
|
-
Example:
|
|
159
|
+
Or configure non-interactively:
|
|
160
|
+
|
|
182
161
|
\`\`\`bash
|
|
183
|
-
uwf setup --
|
|
162
|
+
uwf setup --agent <adapter-command>
|
|
163
|
+
\`\`\`
|
|
164
|
+
|
|
165
|
+
**Note:** \`--agent\` takes the adapter **command name** (e.g. \`uwf-hermes\`, \`uwf-claude-code\`), not the npm package name.
|
|
166
|
+
|
|
167
|
+
Config is saved to \`~/.uwf/config.yaml\`:
|
|
168
|
+
|
|
169
|
+
\`\`\`yaml
|
|
170
|
+
agents:
|
|
171
|
+
hermes:
|
|
172
|
+
command: uwf-hermes
|
|
173
|
+
args: []
|
|
174
|
+
defaultAgent: hermes
|
|
175
|
+
agentOverrides: {}
|
|
184
176
|
\`\`\`
|
|
185
177
|
|
|
186
|
-
|
|
178
|
+
**LLM configuration** is per-adapter — each adapter manages its own provider, model, and API key settings independently (typically via environment variables like \`CLAUDE_MODEL\`, \`ANTHROPIC_API_KEY\`, etc.). The engine config (\`~/.uwf/config.yaml\`) is LLM-free.
|
|
187
179
|
|
|
188
|
-
|
|
180
|
+
Verify with \`cat ~/.uwf/config.yaml\`.
|
|
189
181
|
|
|
190
182
|
### Step 3 — Install skills
|
|
191
183
|
|
|
@@ -339,5 +331,26 @@ uwf prompt workflow-authoring # workflow YAML design guide
|
|
|
339
331
|
uwf prompt adapter-developing # building agent adapters
|
|
340
332
|
uwf prompt bootstrap # this guide
|
|
341
333
|
\`\`\`
|
|
334
|
+
|
|
335
|
+
## What's next — introduce uwf to the user
|
|
336
|
+
|
|
337
|
+
After setup is complete, give the user a brief introduction to what uwf can do
|
|
338
|
+
and invite them to try it. Cover these three things:
|
|
339
|
+
|
|
340
|
+
1. **Run workflows** — execute pre-built workflows to automate multi-step tasks.
|
|
341
|
+
Setup auto-registers example workflows, ready to use immediately.
|
|
342
|
+
2. **Create workflows** — design custom YAML workflows for their own recurring tasks
|
|
343
|
+
(code review, issue triage, release pipelines, etc.). Use \`uwf prompt workflow-authoring\`
|
|
344
|
+
for the authoring guide.
|
|
345
|
+
3. **Debug & improve workflows** — iterate on workflow definitions, inspect thread state
|
|
346
|
+
with \`uwf thread show\`, replay failed steps with \`uwf thread poke\`, and refine
|
|
347
|
+
role procedures based on real execution results.
|
|
348
|
+
|
|
349
|
+
**Discover & try built-in examples:**
|
|
350
|
+
|
|
351
|
+
Run \`uwf workflow list\` to see which workflows are registered (setup auto-registers
|
|
352
|
+
several built-in examples). Show the user what's available, then invite them to try
|
|
353
|
+
one — for instance, suggest a fun debate topic like "AI 是否会抢了人类的工作?" and
|
|
354
|
+
offer to kick it off for them.
|
|
342
355
|
`;
|
|
343
356
|
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
import { stdin as input, stdout as output } from "node:process";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { parse, stringify } from "yaml";
|
|
8
|
+
import { cmdWorkflowAdd } from "./workflow.js";
|
|
7
9
|
|
|
8
10
|
export type SetupArgs = {
|
|
9
11
|
agent: string;
|
|
@@ -261,10 +263,56 @@ export function _checkAdapterAvailability(agentName: string): string[] {
|
|
|
261
263
|
return warnings;
|
|
262
264
|
}
|
|
263
265
|
|
|
266
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
267
|
+
// Bundled example workflows
|
|
268
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
/** Resolve the examples/ directory bundled with the CLI package. */
|
|
271
|
+
function _findExamplesDir(): string | null {
|
|
272
|
+
// Walk up from this file (src/commands/ or dist/commands/) to the package root
|
|
273
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
274
|
+
for (let i = 0; i < 5; i++) {
|
|
275
|
+
const candidate = join(dir, "examples");
|
|
276
|
+
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
277
|
+
return candidate;
|
|
278
|
+
}
|
|
279
|
+
dir = dirname(dir);
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Register bundled example workflows. Non-destructive — silently skips
|
|
286
|
+
* any that fail (e.g. already registered with same hash).
|
|
287
|
+
* Returns list of successfully registered workflow names.
|
|
288
|
+
*/
|
|
289
|
+
export async function _registerBundledExamples(storageRoot: string): Promise<string[]> {
|
|
290
|
+
const examplesDir = _findExamplesDir();
|
|
291
|
+
if (examplesDir === null) return [];
|
|
292
|
+
|
|
293
|
+
const registered: string[] = [];
|
|
294
|
+
const files = readdirSync(examplesDir)
|
|
295
|
+
.filter((f) => f.endsWith(".yaml"))
|
|
296
|
+
.sort();
|
|
297
|
+
|
|
298
|
+
for (const file of files) {
|
|
299
|
+
try {
|
|
300
|
+
const result = await cmdWorkflowAdd(storageRoot, join(examplesDir, file));
|
|
301
|
+
registered.push(result.name);
|
|
302
|
+
console.error(` ✓ ${result.name}`);
|
|
303
|
+
} catch {
|
|
304
|
+
// Skip silently — workflow may already exist or be invalid
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return registered;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
312
|
+
|
|
264
313
|
/**
|
|
265
314
|
* Non-interactive setup. Engine config is LLM-free — only writes
|
|
266
|
-
* agents + defaultAgent.
|
|
267
|
-
* config.yaml under `providers` and `models`.
|
|
315
|
+
* agents + defaultAgent. Each adapter owns its own LLM configuration.
|
|
268
316
|
*/
|
|
269
317
|
export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>> {
|
|
270
318
|
const { storageRoot } = args;
|
|
@@ -287,16 +335,20 @@ export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>
|
|
|
287
335
|
console.error(`⚠ ${w}`);
|
|
288
336
|
}
|
|
289
337
|
|
|
338
|
+
// Auto-register bundled example workflows
|
|
339
|
+
const registeredExamples = await _registerBundledExamples(storageRoot);
|
|
340
|
+
|
|
290
341
|
return {
|
|
291
342
|
configPath,
|
|
292
343
|
defaultAgent: merged.defaultAgent,
|
|
293
344
|
adapterWarnings,
|
|
345
|
+
registeredExamples,
|
|
294
346
|
};
|
|
295
347
|
}
|
|
296
348
|
|
|
297
349
|
/**
|
|
298
|
-
* Interactive setup — prompts the user only for the default agent.
|
|
299
|
-
*
|
|
350
|
+
* Interactive setup — prompts the user only for the default agent.
|
|
351
|
+
* Each adapter owns its own LLM configuration.
|
|
300
352
|
*/
|
|
301
353
|
export async function cmdSetupInteractive(storageRoot: string): Promise<Record<string, unknown>> {
|
|
302
354
|
const rl = createInterface({ input, output });
|
|
@@ -313,8 +365,6 @@ export async function cmdSetupInteractive(storageRoot: string): Promise<Record<s
|
|
|
313
365
|
console.log(' uwf thread start <name> -p "..." Start a thread');
|
|
314
366
|
console.log(" uwf thread exec <thread-id> Execute next step");
|
|
315
367
|
console.log("");
|
|
316
|
-
console.log("LLM config: edit ~/.uwf/config.yaml (providers + models sections).");
|
|
317
|
-
console.log("");
|
|
318
368
|
|
|
319
369
|
return null as unknown as Record<string, unknown>;
|
|
320
370
|
} finally {
|
package/src/commands/thread.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
isThreadRunning,
|
|
44
44
|
readMarker,
|
|
45
45
|
} from "../background/index.js";
|
|
46
|
+
import { acquireSlot, DEFAULT_MAX_RUNNING, installSlotCleanup } from "../concurrency/index.js";
|
|
46
47
|
import { createIncludeTag } from "../include.js";
|
|
47
48
|
import { evaluate } from "../moderator/index.js";
|
|
48
49
|
import {
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
} from "../store.js";
|
|
61
62
|
import { checkWorkflowFilenameConsistency, isCasRef, parseWorkflowPayload } from "../validate.js";
|
|
62
63
|
import { validateWorkflow } from "../validate-semantic.js";
|
|
64
|
+
import { getConfigPath, getNestedValue, loadConfig, parseDotPath } from "./config.js";
|
|
63
65
|
import {
|
|
64
66
|
type ChainState,
|
|
65
67
|
collectOrderedSteps,
|
|
@@ -992,6 +994,15 @@ type EvaluateLastOutput = Record<string, unknown>;
|
|
|
992
994
|
|
|
993
995
|
const STATUS_KEY = "$status";
|
|
994
996
|
|
|
997
|
+
/**
|
|
998
|
+
* Strip YAML frontmatter (---...---) from a raw markdown string,
|
|
999
|
+
* returning only the body portion.
|
|
1000
|
+
*/
|
|
1001
|
+
function stripFrontmatter(raw: string): string {
|
|
1002
|
+
const match = raw.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
1003
|
+
return match ? raw.slice(match[0].length).trim() : raw.trim();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
995
1006
|
function resolveEvaluateArgs(
|
|
996
1007
|
uwf: UwfStore,
|
|
997
1008
|
chain: ChainState,
|
|
@@ -1011,6 +1022,13 @@ function resolveEvaluateArgs(
|
|
|
1011
1022
|
? (raw as Record<string, unknown>)
|
|
1012
1023
|
: {};
|
|
1013
1024
|
|
|
1025
|
+
// Inject _body — the markdown body (after frontmatter) from the last step's
|
|
1026
|
+
// assistant output. Workflow edge prompts can reference it via {{ _body }}.
|
|
1027
|
+
const content = extractLastAssistantContent(uwf, lastStep.detail);
|
|
1028
|
+
if (content !== null) {
|
|
1029
|
+
base._body = stripFrontmatter(content);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1014
1032
|
return {
|
|
1015
1033
|
lastRole: lastStep.role,
|
|
1016
1034
|
lastOutput: base,
|
|
@@ -1020,10 +1038,10 @@ function resolveEvaluateArgs(
|
|
|
1020
1038
|
function loadWorkflowPayload(uwf: UwfStore, workflowRef: CasRef): WorkflowPayload {
|
|
1021
1039
|
const node = uwf.store.cas.get(workflowRef);
|
|
1022
1040
|
if (node === null) {
|
|
1023
|
-
|
|
1041
|
+
throw new Error(`workflow CAS node not found: ${workflowRef}`);
|
|
1024
1042
|
}
|
|
1025
1043
|
if (node.type !== uwf.schemas.workflow) {
|
|
1026
|
-
|
|
1044
|
+
throw new Error(`node ${workflowRef} is not a Workflow`);
|
|
1027
1045
|
}
|
|
1028
1046
|
return node.payload as WorkflowPayload;
|
|
1029
1047
|
}
|
|
@@ -1406,6 +1424,25 @@ export function validateCount(count: number): void {
|
|
|
1406
1424
|
}
|
|
1407
1425
|
}
|
|
1408
1426
|
|
|
1427
|
+
/**
|
|
1428
|
+
* Resolve the effective maxRunning limit.
|
|
1429
|
+
* Priority: config file > DEFAULT_MAX_RUNNING (2).
|
|
1430
|
+
*/
|
|
1431
|
+
async function resolveMaxRunning(storageRoot: string): Promise<number> {
|
|
1432
|
+
try {
|
|
1433
|
+
const configPath = getConfigPath(storageRoot);
|
|
1434
|
+
const config = loadConfig(configPath);
|
|
1435
|
+
const path = parseDotPath("concurrency.maxRunning");
|
|
1436
|
+
const value = getNestedValue(config, path);
|
|
1437
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) {
|
|
1438
|
+
return value;
|
|
1439
|
+
}
|
|
1440
|
+
} catch {
|
|
1441
|
+
// Config file missing or invalid — fall through to default
|
|
1442
|
+
}
|
|
1443
|
+
return DEFAULT_MAX_RUNNING;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1409
1446
|
export async function cmdThreadExec(
|
|
1410
1447
|
storageRoot: string,
|
|
1411
1448
|
threadId: ThreadId,
|
|
@@ -1446,6 +1483,13 @@ export async function cmdThreadExec(
|
|
|
1446
1483
|
processStartTime: getProcessStartTime(process.pid),
|
|
1447
1484
|
});
|
|
1448
1485
|
|
|
1486
|
+
// Resolve concurrency limit: config > default
|
|
1487
|
+
const effectiveMaxRunning = await resolveMaxRunning(storageRoot);
|
|
1488
|
+
|
|
1489
|
+
// Acquire concurrency slot (blocks if at capacity)
|
|
1490
|
+
const slotHandle = await acquireSlot(storageRoot, effectiveMaxRunning);
|
|
1491
|
+
const uninstallCleanup = installSlotCleanup(slotHandle);
|
|
1492
|
+
|
|
1449
1493
|
try {
|
|
1450
1494
|
const results: StepOutput[] = [];
|
|
1451
1495
|
for (let i = 0; i < count; i++) {
|
|
@@ -1457,6 +1501,8 @@ export async function cmdThreadExec(
|
|
|
1457
1501
|
}
|
|
1458
1502
|
return results;
|
|
1459
1503
|
} finally {
|
|
1504
|
+
uninstallCleanup();
|
|
1505
|
+
await slotHandle.release();
|
|
1460
1506
|
await deleteMarker(storageRoot, threadId);
|
|
1461
1507
|
}
|
|
1462
1508
|
}
|
package/src/commands/workflow.ts
CHANGED
|
@@ -126,7 +126,7 @@ export async function materializeWorkflowPayload(
|
|
|
126
126
|
* returns silently (no stdout/stderr) and exits 0. On any error, writes a
|
|
127
127
|
* single message to stderr and exits 1.
|
|
128
128
|
*/
|
|
129
|
-
export async function cmdWorkflowValidate(filePath: string): Promise<
|
|
129
|
+
export async function cmdWorkflowValidate(filePath: string): Promise<string[]> {
|
|
130
130
|
let text: string;
|
|
131
131
|
try {
|
|
132
132
|
text = await readFile(filePath, "utf8");
|
|
@@ -150,14 +150,10 @@ export async function cmdWorkflowValidate(filePath: string): Promise<void> {
|
|
|
150
150
|
|
|
151
151
|
const filenameError = checkWorkflowFilenameConsistency(filePath, payload);
|
|
152
152
|
if (filenameError !== null) {
|
|
153
|
-
|
|
153
|
+
return [filenameError];
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
if (semanticErrors.length > 0) {
|
|
158
|
-
fail(`workflow validation failed:\n${semanticErrors.map((e) => ` - ${e}`).join("\n")}`);
|
|
159
|
-
}
|
|
160
|
-
// success: silent return
|
|
156
|
+
return validateWorkflow(payload);
|
|
161
157
|
}
|
|
162
158
|
|
|
163
159
|
export async function cmdWorkflowAdd(
|