@workflow-manager/runner 0.3.0 → 0.4.1
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 +18 -10
- package/dist/engine.js +5 -1
- package/dist/index.js +150 -50
- package/dist/manPage.d.ts +1 -1
- package/dist/manPage.js +10 -4
- package/dist/runtimePreflight.d.ts +16 -1
- package/dist/runtimePreflight.js +31 -0
- package/man/wfm.1 +10 -4
- package/package.json +1 -1
- package/skills/README.md +15 -1
- package/skills/workflow-manager-cli/SKILL.md +143 -78
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ The core ideas:
|
|
|
9
9
|
- **The envelope protocol.** Every step execution returns a structured result: an execution status (`SUCCESS`, `FAILED`, `QA_REJECTED`, `YIELD_EXTERNAL`) plus a QA routing action (`PROCEED`, `RETRY_CURRENT`, `ROLLBACK_PREVIOUS`, `RESTART_ALL`). That's what lets the engine retry a step, roll back to the previous one, or restart the whole run based on what the agent reported.
|
|
10
10
|
- **Human-in-the-loop control.** While a run is active, wfm starts a local HTTP attach API (token-protected, with SSE event streaming), so a waiting step can be resolved either in the terminal prompt or from another shell with `wfm approve` / `wfm resume` / `wfm cancel`.
|
|
11
11
|
- **A registry.** `wfm publish` / `pull` / `search` / `auth` talk to a Supabase-backed remote registry (the `apps/remote-registry` web app in this repo) for sharing workflows, with skills bundled and SHA-256 verified. Runs also emit opt-in telemetry there.
|
|
12
|
-
- **Supporting commands.** `wfm scaffold` writes a starter workflow, `wfm validate` checks the schema and dependency cycles, `wfm doctor` verifies the host has the needed CLIs and API keys before a run, `wfm
|
|
12
|
+
- **Supporting commands.** `wfm scaffold` writes a starter workflow, `wfm validate` checks the schema and dependency cycles, `wfm doctor` verifies the host has the needed CLIs and API keys before a run, `wfm skill install` installs the bundled agent skills into Claude Code or opencode skill directories, and `wfm man` shows the man page.
|
|
13
13
|
|
|
14
14
|
Install the latest prebuilt CLI with:
|
|
15
15
|
|
|
@@ -31,7 +31,7 @@ If `wfm` is not available immediately in the same terminal, run the shell reload
|
|
|
31
31
|
|
|
32
32
|
## Architecture
|
|
33
33
|
|
|
34
|
-
- `src/index.ts`: CLI commands (`doctor`, `
|
|
34
|
+
- `src/index.ts`: CLI commands (`doctor`, `skill`, `scaffold`, `validate`, `run`)
|
|
35
35
|
- `src/parser.ts`: parsing + validation
|
|
36
36
|
- `src/engine.ts`: execution loop, confirmations, retries, rollback/restart
|
|
37
37
|
- `src/piAgentExecutor.ts`: default executor driving the `pi` coding agent CLI
|
|
@@ -51,7 +51,7 @@ bun run build
|
|
|
51
51
|
bun link
|
|
52
52
|
|
|
53
53
|
wfm doctor
|
|
54
|
-
wfm
|
|
54
|
+
wfm skill install
|
|
55
55
|
wfm scaffold ./example-workflow.md
|
|
56
56
|
wfm validate ./example-workflow.md
|
|
57
57
|
wfm doctor ./example-workflow.md
|
|
@@ -214,10 +214,11 @@ Manual help:
|
|
|
214
214
|
wfm man
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
Agent
|
|
217
|
+
Agent skills:
|
|
218
218
|
|
|
219
219
|
```bash
|
|
220
|
-
wfm
|
|
220
|
+
wfm skill list
|
|
221
|
+
wfm skill install
|
|
221
222
|
```
|
|
222
223
|
|
|
223
224
|
Remote registry commands:
|
|
@@ -230,13 +231,20 @@ wfm remote info alice/remote-bunny
|
|
|
230
231
|
|
|
231
232
|
## Agent skills
|
|
232
233
|
|
|
233
|
-
The published `@workflow-manager/runner` npm package
|
|
234
|
+
The published `@workflow-manager/runner` npm package ships the CLI runner and the bundled agent skills together. The primary skill is `skills/workflow-manager-cli/SKILL.md`, which teaches an agent how to configure, author, run, and publish workflows with `wfm`.
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
- discovery keyword: `tanstack-intent`
|
|
237
|
-
- install flow: install `@workflow-manager/runner`, then run `npx @tanstack/intent@latest list` and `npx @tanstack/intent@latest install`
|
|
236
|
+
Install skills with the CLI:
|
|
238
237
|
|
|
239
|
-
|
|
238
|
+
```bash
|
|
239
|
+
wfm skill list # show bundled skills
|
|
240
|
+
wfm skill install # workflow-manager-cli -> ./.claude/skills/
|
|
241
|
+
wfm skill install --global # -> ~/.claude/skills/
|
|
242
|
+
wfm skill install --agent opencode # -> ./.opencode/skill/
|
|
243
|
+
wfm skill install --all # install every bundled skill
|
|
244
|
+
wfm skill install doc-sync --dir ./my/skills
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
TanStack Intent discovery is also supported (package keyword `tanstack-intent`):
|
|
240
248
|
|
|
241
249
|
```bash
|
|
242
250
|
npm install @workflow-manager/runner
|
package/dist/engine.js
CHANGED
|
@@ -6,7 +6,7 @@ import { executeClaudeCodeStep, shouldUseRealClaudeCode } from "./claudeCodeExec
|
|
|
6
6
|
import { executeMockStep } from "./mockExecutor.js";
|
|
7
7
|
import { executeOpencodeStep, shouldUseRealOpencode } from "./opencodeExecutor.js";
|
|
8
8
|
import { executePiAgentStep } from "./piAgentExecutor.js";
|
|
9
|
-
import { validateRuntimeRequirements } from "./runtimePreflight.js";
|
|
9
|
+
import { adapterMockFallbackReason, validateRuntimeRequirements } from "./runtimePreflight.js";
|
|
10
10
|
function nodeType(step) {
|
|
11
11
|
if (step.kind === "approval")
|
|
12
12
|
return "HUMAN";
|
|
@@ -302,6 +302,10 @@ async function executeStep(step, input, attempt, workflow, workflowFilePath, hoo
|
|
|
302
302
|
if (adapterKey === "claude-code" && shouldUseRealClaudeCode(step)) {
|
|
303
303
|
return executeClaudeCodeStep(step, input, attempt, workflow, workflowFilePath, hooks);
|
|
304
304
|
}
|
|
305
|
+
const fallbackReason = adapterMockFallbackReason(step);
|
|
306
|
+
if (fallbackReason) {
|
|
307
|
+
hooks?.onStderr?.(`[wfm] ${step.key}: ${fallbackReason}\n`);
|
|
308
|
+
}
|
|
305
309
|
return executeMockStep(step, input, attempt, hooks);
|
|
306
310
|
}
|
|
307
311
|
export async function runWorkflow(definition, options) {
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import matter from "gray-matter";
|
|
8
9
|
import { CliRunRenderer } from "./cliRunRenderer.js";
|
|
9
10
|
import { startRunnerApiServer } from "./runnerApi.js";
|
|
10
11
|
import { RunnerSessionStore } from "./runnerSession.js";
|
|
@@ -13,7 +14,7 @@ import { MAN_PAGE_SOURCE } from "./manPage.js";
|
|
|
13
14
|
import { promptForApprovalDecision, runWorkflow } from "./engine.js";
|
|
14
15
|
import { cmdAuth, cmdPublish, cmdPull, cmdRemoteInfo, cmdSearch } from "./remote/commands.js";
|
|
15
16
|
import { emitRunTelemetryBestEffort } from "./remote/telemetry.js";
|
|
16
|
-
import { adapterImplementationStatuses, runtimeDoctorChecks, validateRuntimeRequirements, } from "./runtimePreflight.js";
|
|
17
|
+
import { adapterImplementationStatuses, adapterMockFallbackWarnings, runtimeDoctorChecks, validateRuntimeRequirements, } from "./runtimePreflight.js";
|
|
17
18
|
function cliDisplayName() {
|
|
18
19
|
const invokedAs = path.basename(process.argv[1] ?? "");
|
|
19
20
|
if (invokedAs === "workflow-manager" || invokedAs === "wfm") {
|
|
@@ -25,7 +26,8 @@ function usage() {
|
|
|
25
26
|
const cli = cliDisplayName();
|
|
26
27
|
console.log(`${cli} commands:
|
|
27
28
|
doctor [workflow.md|workflow.json] [--json]
|
|
28
|
-
|
|
29
|
+
skill list
|
|
30
|
+
skill install [name ...] [--agent claude|opencode] [--global] [--dir path] [--all] [--force]
|
|
29
31
|
scaffold [path] [--format markdown|json]
|
|
30
32
|
validate <workflow.md|workflow.json>
|
|
31
33
|
run <workflow.md|workflow.json> [--input input.json] [--objective "string"] [--confirm stepA,stepB:human] [--auto-confirm-all] [--port 43121] [--verbose] [--json]
|
|
@@ -248,36 +250,6 @@ steps:
|
|
|
248
250
|
|
|
249
251
|
Edit frontmatter to configure orchestration behavior.
|
|
250
252
|
`;
|
|
251
|
-
const AGENT_RULES_TEMPLATE = `# WFM Agent Rules
|
|
252
|
-
|
|
253
|
-
Use these rules when creating, validating, running, or publishing workflow-manager workflows with the \`wfm\` CLI.
|
|
254
|
-
|
|
255
|
-
## Core Flow
|
|
256
|
-
|
|
257
|
-
1. Start with \`wfm doctor\` to inspect local adapter and API key setup.
|
|
258
|
-
2. Create a starter workflow with \`wfm scaffold ./workflow.md\` or \`wfm scaffold ./workflow.json --format json\`.
|
|
259
|
-
3. Edit stable workflow keys, step objectives, dependencies, validation modes, and adapter initialization.
|
|
260
|
-
4. Run \`wfm validate <workflow>\` before every run or publish.
|
|
261
|
-
5. Run \`wfm doctor <workflow>\` before using real host-backed adapters.
|
|
262
|
-
6. Run with \`wfm run <workflow>\`; use \`--verbose\` when agent logs are needed.
|
|
263
|
-
7. Publish only after validation succeeds with \`wfm publish <workflow>\`.
|
|
264
|
-
|
|
265
|
-
## Workflow Authoring
|
|
266
|
-
|
|
267
|
-
- Keep workflow, step, and adapter keys stable; external tools and tests may reference them.
|
|
268
|
-
- Omit \`taskSpec.adapterKey\` for the default \`pi-agent\` adapter.
|
|
269
|
-
- Use \`adapterKey: mock\` for deterministic tests and examples.
|
|
270
|
-
- Put skills, MCP endpoints, system prompts, model, and context under \`taskSpec.init\`.
|
|
271
|
-
- Model-backed steps should declare required environment variables through provider-specific model names or \`taskSpec.payload.requiredEnv\`.
|
|
272
|
-
- Human or external validation should be explicit in the workflow file.
|
|
273
|
-
|
|
274
|
-
## Running Safely
|
|
275
|
-
|
|
276
|
-
- Do not use \`--auto-confirm-all\` unless the workflow is intentionally non-interactive.
|
|
277
|
-
- Prefer \`wfm doctor <workflow>\` before runs that use \`pi-agent\`, real \`opencode\`, or real \`claude-code\`.
|
|
278
|
-
- Use the attach API output from \`wfm run\` for approval, resume, and cancel commands.
|
|
279
|
-
- Preserve Markdown workflows when humans will review notes; use JSON for generated or machine-edited workflows.
|
|
280
|
-
`;
|
|
281
253
|
function resolveScaffoldFormat(targetPath, explicitFormat) {
|
|
282
254
|
if (explicitFormat === "markdown" || explicitFormat === "json") {
|
|
283
255
|
return explicitFormat;
|
|
@@ -300,29 +272,138 @@ function parseScaffoldArgs(args) {
|
|
|
300
272
|
}
|
|
301
273
|
return { targetPath, format };
|
|
302
274
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
275
|
+
const DEFAULT_INSTALL_SKILL = "workflow-manager-cli";
|
|
276
|
+
const SKILL_INSTALL_TARGETS = {
|
|
277
|
+
claude: {
|
|
278
|
+
projectDir: path.join(".claude", "skills"),
|
|
279
|
+
globalDir: path.join(os.homedir(), ".claude", "skills"),
|
|
280
|
+
},
|
|
281
|
+
opencode: {
|
|
282
|
+
projectDir: path.join(".opencode", "skill"),
|
|
283
|
+
globalDir: path.join(os.homedir(), ".config", "opencode", "skill"),
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
function packagedSkillsDir() {
|
|
287
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "skills");
|
|
288
|
+
}
|
|
289
|
+
function listPackagedSkills() {
|
|
290
|
+
const root = packagedSkillsDir();
|
|
291
|
+
if (!fs.existsSync(root)) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
const skills = [];
|
|
295
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
296
|
+
if (!entry.isDirectory())
|
|
297
|
+
continue;
|
|
298
|
+
const dir = path.join(root, entry.name);
|
|
299
|
+
const skillFile = path.join(dir, "SKILL.md");
|
|
300
|
+
if (!fs.existsSync(skillFile))
|
|
301
|
+
continue;
|
|
302
|
+
let description = "";
|
|
303
|
+
try {
|
|
304
|
+
const parsed = matter(fs.readFileSync(skillFile, "utf-8"));
|
|
305
|
+
if (typeof parsed.data.description === "string") {
|
|
306
|
+
description = parsed.data.description.replace(/\s+/g, " ").trim();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// skills without parseable frontmatter are still installable
|
|
311
|
+
}
|
|
312
|
+
skills.push({ name: entry.name, dir, description });
|
|
313
|
+
}
|
|
314
|
+
return skills;
|
|
315
|
+
}
|
|
316
|
+
function cmdSkillList() {
|
|
317
|
+
const skills = listPackagedSkills();
|
|
318
|
+
if (skills.length === 0) {
|
|
319
|
+
console.error(`No bundled skills found at ${packagedSkillsDir()}.`);
|
|
320
|
+
return 1;
|
|
321
|
+
}
|
|
322
|
+
console.log("Bundled skills:\n");
|
|
323
|
+
for (const skill of skills) {
|
|
324
|
+
console.log(` ${skill.name}`);
|
|
325
|
+
if (skill.description) {
|
|
326
|
+
console.log(` ${skill.description}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
console.log(`\nInstall with: ${cliDisplayName()} skill install <name> [--agent claude|opencode] [--global]`);
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
function parseSkillInstallArgs(args) {
|
|
333
|
+
const parsed = { names: [], agent: "claude", global: false, force: false, all: false };
|
|
334
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
335
|
+
const arg = args[i];
|
|
336
|
+
if (arg === "--agent") {
|
|
337
|
+
parsed.agent = args[i + 1] ?? "";
|
|
338
|
+
i += 1;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (arg === "--dir") {
|
|
342
|
+
parsed.dir = args[i + 1];
|
|
343
|
+
i += 1;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (arg === "--global" || arg === "-g") {
|
|
347
|
+
parsed.global = true;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
307
350
|
if (arg === "--force" || arg === "-f") {
|
|
308
|
-
force = true;
|
|
351
|
+
parsed.force = true;
|
|
309
352
|
continue;
|
|
310
353
|
}
|
|
311
|
-
if (
|
|
312
|
-
|
|
354
|
+
if (arg === "--all") {
|
|
355
|
+
parsed.all = true;
|
|
356
|
+
continue;
|
|
313
357
|
}
|
|
358
|
+
if (!arg.startsWith("-")) {
|
|
359
|
+
parsed.names.push(arg);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return parsed;
|
|
363
|
+
}
|
|
364
|
+
function resolveSkillInstallRoot(args) {
|
|
365
|
+
if (args.dir) {
|
|
366
|
+
return path.resolve(args.dir);
|
|
314
367
|
}
|
|
315
|
-
|
|
368
|
+
const target = SKILL_INSTALL_TARGETS[args.agent];
|
|
369
|
+
if (!target) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
return args.global ? target.globalDir : path.resolve(target.projectDir);
|
|
316
373
|
}
|
|
317
|
-
function
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
374
|
+
function cmdSkillInstall(args) {
|
|
375
|
+
const parsed = parseSkillInstallArgs(args);
|
|
376
|
+
const targetRoot = resolveSkillInstallRoot(parsed);
|
|
377
|
+
if (!targetRoot) {
|
|
378
|
+
console.error(`Unknown --agent value: ${parsed.agent}. Supported agents: ${Object.keys(SKILL_INSTALL_TARGETS).join(", ")}. Use --dir for any other destination.`);
|
|
321
379
|
return 1;
|
|
322
380
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
381
|
+
const available = listPackagedSkills();
|
|
382
|
+
const names = parsed.all
|
|
383
|
+
? available.map((skill) => skill.name)
|
|
384
|
+
: parsed.names.length > 0
|
|
385
|
+
? parsed.names
|
|
386
|
+
: [DEFAULT_INSTALL_SKILL];
|
|
387
|
+
for (const name of names) {
|
|
388
|
+
const skill = available.find((candidate) => candidate.name === name);
|
|
389
|
+
if (!skill) {
|
|
390
|
+
console.error(`Unknown skill: ${name}. Run \`${cliDisplayName()} skill list\` to see bundled skills.`);
|
|
391
|
+
return 1;
|
|
392
|
+
}
|
|
393
|
+
const destDir = path.join(targetRoot, name);
|
|
394
|
+
const destFile = path.join(destDir, "SKILL.md");
|
|
395
|
+
if (fs.existsSync(destFile) && !parsed.force) {
|
|
396
|
+
console.error(`Skill already installed at ${destFile}. Pass --force to overwrite.`);
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
400
|
+
for (const entry of fs.readdirSync(skill.dir, { withFileTypes: true })) {
|
|
401
|
+
if (!entry.isFile() || entry.name === "README.md")
|
|
402
|
+
continue;
|
|
403
|
+
fs.copyFileSync(path.join(skill.dir, entry.name), path.join(destDir, entry.name));
|
|
404
|
+
}
|
|
405
|
+
console.log(`Installed skill ${name} -> ${destDir}`);
|
|
406
|
+
}
|
|
326
407
|
return 0;
|
|
327
408
|
}
|
|
328
409
|
function cmdScaffold(targetPath, format) {
|
|
@@ -405,12 +486,14 @@ function cmdDoctor(args) {
|
|
|
405
486
|
let workflow = null;
|
|
406
487
|
let workflowErrors = [];
|
|
407
488
|
let runtimeErrors = [];
|
|
489
|
+
let adapterWarnings = [];
|
|
408
490
|
if (workflowPath) {
|
|
409
491
|
try {
|
|
410
492
|
workflow = parseWorkflowFile(path.resolve(workflowPath));
|
|
411
493
|
workflowErrors = validateWorkflow(workflow);
|
|
412
494
|
if (workflowErrors.length === 0) {
|
|
413
495
|
runtimeErrors = validateRuntimeRequirements(workflow);
|
|
496
|
+
adapterWarnings = adapterMockFallbackWarnings(workflow);
|
|
414
497
|
}
|
|
415
498
|
}
|
|
416
499
|
catch (err) {
|
|
@@ -434,6 +517,7 @@ function cmdDoctor(args) {
|
|
|
434
517
|
title: workflow.title,
|
|
435
518
|
errors: workflowErrors,
|
|
436
519
|
runtimeErrors,
|
|
520
|
+
adapterWarnings,
|
|
437
521
|
}
|
|
438
522
|
: null,
|
|
439
523
|
baselineErrors,
|
|
@@ -467,6 +551,12 @@ function cmdDoctor(args) {
|
|
|
467
551
|
else {
|
|
468
552
|
console.log("- OK workflow schema and runtime requirements");
|
|
469
553
|
}
|
|
554
|
+
if (adapterWarnings.length > 0) {
|
|
555
|
+
console.log("\nAdapter warnings:");
|
|
556
|
+
for (const warning of adapterWarnings) {
|
|
557
|
+
console.log(`- ${warning.stepKey}: ${warning.message}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
470
560
|
}
|
|
471
561
|
return exitCode;
|
|
472
562
|
}
|
|
@@ -548,6 +638,9 @@ async function cmdRun(filePath) {
|
|
|
548
638
|
});
|
|
549
639
|
return 1;
|
|
550
640
|
}
|
|
641
|
+
for (const warning of adapterMockFallbackWarnings(workflow)) {
|
|
642
|
+
process.stderr.write(`⚠ ${warning.stepKey}: ${warning.message}\n`);
|
|
643
|
+
}
|
|
551
644
|
const objective = getFlag("--objective");
|
|
552
645
|
const inputRaw = getFlag("--input");
|
|
553
646
|
let input = {};
|
|
@@ -677,9 +770,16 @@ async function main() {
|
|
|
677
770
|
if (cmd === "doctor") {
|
|
678
771
|
process.exit(cmdDoctor(process.argv.slice(3)));
|
|
679
772
|
}
|
|
680
|
-
if (cmd === "
|
|
681
|
-
const
|
|
682
|
-
|
|
773
|
+
if (cmd === "skill") {
|
|
774
|
+
const sub = process.argv[3];
|
|
775
|
+
if (sub === "list") {
|
|
776
|
+
process.exit(cmdSkillList());
|
|
777
|
+
}
|
|
778
|
+
if (sub === "install") {
|
|
779
|
+
process.exit(cmdSkillInstall(process.argv.slice(4)));
|
|
780
|
+
}
|
|
781
|
+
usage();
|
|
782
|
+
process.exit(1);
|
|
683
783
|
}
|
|
684
784
|
if (cmd === "scaffold") {
|
|
685
785
|
const { targetPath, format } = parseScaffoldArgs(process.argv.slice(3));
|
package/dist/manPage.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MAN_PAGE_SOURCE = ".TH WFM 1 \"April 2026\" \"@workflow-manager/runner\" \"User Commands\"\n.SH NAME\nwfm \\- run markdown or json workflows from the CLI\n.SH SYNOPSIS\n.B wfm\n.I command\n[options]\n.SH DESCRIPTION\nwfm parses a workflow definition file, validates it, and executes\nit with deterministic in-memory orchestration.\n\nWorkflow files can be Markdown with YAML frontmatter or JSON.\n.SH COMMANDS\n.TP\n.B doctor [workflow.md|workflow.json] [--json]\nInspect host adapter setup, LLM access keys, and current adapter implementation status. When a workflow path is provided, also validate schema and runtime requirements without executing steps.\n.TP\n.B agent [path] [--force]\
|
|
1
|
+
export declare const MAN_PAGE_SOURCE = ".TH WFM 1 \"April 2026\" \"@workflow-manager/runner\" \"User Commands\"\n.SH NAME\nwfm \\- run markdown or json workflows from the CLI\n.SH SYNOPSIS\n.B wfm\n.I command\n[options]\n.SH DESCRIPTION\nwfm parses a workflow definition file, validates it, and executes\nit with deterministic in-memory orchestration.\n\nWorkflow files can be Markdown with YAML frontmatter or JSON.\n.SH COMMANDS\n.TP\n.B doctor [workflow.md|workflow.json] [--json]\nInspect host adapter setup, LLM access keys, and current adapter implementation status. When a workflow path is provided, also validate schema and runtime requirements without executing steps.\n.TP\n.B skill list\nList the agent skills bundled with the npm package.\n.TP\n.B skill install [name ...] [--agent claude|opencode] [--global] [--dir path] [--all] [--force]\nInstall bundled agent skills into an agent skill directory. Defaults to the workflow-manager-cli skill and the project-level Claude Code directory (./.claude/skills). Existing skills are not overwritten unless --force is passed.\n.TP\n.B scaffold [path] [--format markdown|json]\nCreate a starter workflow file. Format defaults to markdown unless the output\npath ends in .json.\n.TP\n.B validate <workflow.md|workflow.json>\nValidate workflow structure and report schema errors.\n.TP\n.B run <workflow.md|workflow.json> [--input input.json] [--objective text] [--confirm list] [--auto-confirm-all] [--port number] [--verbose] [--json]\nRun the workflow with live CLI progress and optional JSON output.\n.TP\n.B approve [--url value] [--token value] [--run-id value] [--step value] [--actor value] [--note text]\nApprove the current waiting runner step through the local attach API.\n.TP\n.B resume [--url value] [--token value] [--run-id value] [--step value] [--actor value] [--note text]\nAlias for approve, intended for external resume flows.\n.TP\n.B cancel [--url value] [--token value] [--run-id value] [--step value] [--actor value] [--note text]\nCancel the current waiting runner step through the local attach API.\n.TP\n.B auth <login|whoami|logout> [--token value]\nManage remote registry authentication for CLI publish and pull flows.\n.TP\n.B publish <workflow.md|workflow.json> [--slug slug] [--title text] [--description text] [--visibility public|private] [--version label] [--tag a,b] [--draft]\nPublish a validated local workflow to the remote registry.\n.TP\n.B pull <owner/slug> [--version label] [--output path]\nDownload a remote workflow and write it to a local file.\n.TP\n.B search [query]\nSearch public workflows from the remote registry.\n.TP\n.B remote info <owner/slug>\nShow metadata and source information for a remote workflow.\n.TP\n.B man\nOpen this man page.\n.SH RUN OPTIONS\n.TP\n.B --input <path>\nJSON file merged into global workflow input state.\n.TP\n.B --objective <text>\nOverride the default run objective.\n.TP\n.B --confirm <stepA,stepB:human,...>\nProvide explicit confirmations for steps that require validation.\n.TP\n.B --auto-confirm-all\nBypass confirmation gating for all steps.\n.TP\n.B --port <number>\nBind the local attach API to a specific port. If omitted, the OS assigns a free port on 127.0.0.1.\n.TP\n.B --verbose\nStream per-step agent output and execution updates to stderr while the workflow runs.\n.TP\n.B --json\nPrint the final run result as JSON on stdout while keeping live progress on stderr.\n.TP\nHuman approval steps in an interactive terminal show an inline review prompt so they can be approved or cancelled without a separate HTTP client.\n.TP\n.B --url <value>\nRunner attach API base URL for approve, resume, or cancel commands.\n.TP\n.B --token <value>\nRunner attach API bearer token for approve, resume, or cancel commands.\n.TP\n.B --run-id <value>\nRunner id to control. If omitted, the CLI reads it from /session.\n.TP\n.B --step <value>\nOptional step key when controlling a specific waiting step.\n.TP\n.B --actor <value>\nActor name recorded in approval audit events.\n.TP\n.B --note <text>\nOptional approval or cancellation note recorded in the event payload.\n.SH EXAMPLES\n.TP\nValidate markdown workflow:\n.B wfm validate ./example-workflow.md\n.TP\nValidate json workflow:\n.B wfm validate ./example-workflow.json\n.TP\nScaffold json workflow file:\n.B wfm scaffold ./new-workflow.json --format json\n.TP\nAuthenticate with a CLI token:\n.B wfm auth login --token wm_exampletoken\n.TP\nPublish a workflow:\n.B wfm publish ./example-workflow.json --visibility public --tag example,automation\n.TP\nPull a remote workflow:\n.B wfm pull alice/remote-bunny --output ./remote-bunny.json\n.TP\nSearch the remote registry:\n.B wfm search bunny\n.TP\nRun with explicit confirmations:\n.B wfm run ./example-workflow.json --confirm discover:human,qa_gate:human\n.TP\nInspect host setup:\n.B wfm doctor\n.TP\nCheck a workflow before running it:\n.B wfm doctor ./example-workflow.json\n.TP\nInstall the bundled CLI skill for Claude Code:\n.B wfm skill install\n.TP\nInstall every bundled skill globally:\n.B wfm skill install --all --global\n.SH FILES\n.TP\n.B man/wfm.1\nThe manual page source shipped with this repository.\n.SH EXIT STATUS\n.TP\n.B 0\nSuccessful command execution.\n.TP\n.B 1\nValidation or runtime error.\n.TP\n.B 2\nRun completed in non-success terminal status.\n";
|
package/dist/manPage.js
CHANGED
|
@@ -15,8 +15,11 @@ Workflow files can be Markdown with YAML frontmatter or JSON.
|
|
|
15
15
|
.B doctor [workflow.md|workflow.json] [--json]
|
|
16
16
|
Inspect host adapter setup, LLM access keys, and current adapter implementation status. When a workflow path is provided, also validate schema and runtime requirements without executing steps.
|
|
17
17
|
.TP
|
|
18
|
-
.B
|
|
19
|
-
|
|
18
|
+
.B skill list
|
|
19
|
+
List the agent skills bundled with the npm package.
|
|
20
|
+
.TP
|
|
21
|
+
.B skill install [name ...] [--agent claude|opencode] [--global] [--dir path] [--all] [--force]
|
|
22
|
+
Install bundled agent skills into an agent skill directory. Defaults to the workflow-manager-cli skill and the project-level Claude Code directory (./.claude/skills). Existing skills are not overwritten unless --force is passed.
|
|
20
23
|
.TP
|
|
21
24
|
.B scaffold [path] [--format markdown|json]
|
|
22
25
|
Create a starter workflow file. Format defaults to markdown unless the output
|
|
@@ -128,8 +131,11 @@ Inspect host setup:
|
|
|
128
131
|
Check a workflow before running it:
|
|
129
132
|
.B wfm doctor ./example-workflow.json
|
|
130
133
|
.TP
|
|
131
|
-
|
|
132
|
-
.B wfm
|
|
134
|
+
Install the bundled CLI skill for Claude Code:
|
|
135
|
+
.B wfm skill install
|
|
136
|
+
.TP
|
|
137
|
+
Install every bundled skill globally:
|
|
138
|
+
.B wfm skill install --all --global
|
|
133
139
|
.SH FILES
|
|
134
140
|
.TP
|
|
135
141
|
.B man/wfm.1
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdapterKey, WorkflowDefinition } from "./types.js";
|
|
1
|
+
import type { AdapterKey, StepDefinition, WorkflowDefinition } from "./types.js";
|
|
2
2
|
export interface RuntimeDoctorCheck {
|
|
3
3
|
key: string;
|
|
4
4
|
label: string;
|
|
@@ -13,4 +13,19 @@ export interface AdapterImplementationStatus {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function validateRuntimeRequirements(definition: WorkflowDefinition, env?: NodeJS.ProcessEnv): string[];
|
|
15
15
|
export declare function runtimeDoctorChecks(env?: NodeJS.ProcessEnv): RuntimeDoctorCheck[];
|
|
16
|
+
export interface AdapterMockFallbackWarning {
|
|
17
|
+
stepKey: string;
|
|
18
|
+
adapter: AdapterKey;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detects a step that explicitly selects a host-backed adapter whose real
|
|
23
|
+
* execution path is gated behind opt-in payload flags that are not set. Without
|
|
24
|
+
* those flags the engine routes the step to the mock executor; returning a
|
|
25
|
+
* message here lets callers warn instead of silently mocking. Returns null when
|
|
26
|
+
* the step will run as the user expects (default pi-agent, an enabled real
|
|
27
|
+
* adapter, or an intentional mock/codex selection).
|
|
28
|
+
*/
|
|
29
|
+
export declare function adapterMockFallbackReason(step: StepDefinition): string | null;
|
|
30
|
+
export declare function adapterMockFallbackWarnings(definition: WorkflowDefinition): AdapterMockFallbackWarning[];
|
|
16
31
|
export declare function adapterImplementationStatuses(): AdapterImplementationStatus[];
|
package/dist/runtimePreflight.js
CHANGED
|
@@ -158,6 +158,37 @@ export function runtimeDoctorChecks(env = process.env) {
|
|
|
158
158
|
envCheck("anthropic-key", "Anthropic API key", "ANTHROPIC_API_KEY", env),
|
|
159
159
|
];
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Detects a step that explicitly selects a host-backed adapter whose real
|
|
163
|
+
* execution path is gated behind opt-in payload flags that are not set. Without
|
|
164
|
+
* those flags the engine routes the step to the mock executor; returning a
|
|
165
|
+
* message here lets callers warn instead of silently mocking. Returns null when
|
|
166
|
+
* the step will run as the user expects (default pi-agent, an enabled real
|
|
167
|
+
* adapter, or an intentional mock/codex selection).
|
|
168
|
+
*/
|
|
169
|
+
export function adapterMockFallbackReason(step) {
|
|
170
|
+
if (step.kind !== "task" || !step.taskSpec?.adapterKey) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const adapter = resolveTaskAdapter(step.taskSpec.adapterKey);
|
|
174
|
+
if (adapter === "claude-code" && !shouldUseRealClaudeCode(step)) {
|
|
175
|
+
return "adapterKey 'claude-code' is set, but the real Claude Code CLI path is off, so the step runs as a mock. Set taskSpec.payload.useRealAdapter: true to drive the real `claude` CLI.";
|
|
176
|
+
}
|
|
177
|
+
if (adapter === "opencode" && !shouldUseRealOpencode(step)) {
|
|
178
|
+
return "adapterKey 'opencode' is set, but the real OpenCode CLI path is off, so the step runs as a mock. Set taskSpec.payload.useRealAdapter: true and taskSpec.payload.opencodeSmokeTest: true to drive the real `opencode` CLI.";
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
export function adapterMockFallbackWarnings(definition) {
|
|
183
|
+
const warnings = [];
|
|
184
|
+
for (const step of definition.steps) {
|
|
185
|
+
const message = adapterMockFallbackReason(step);
|
|
186
|
+
if (message) {
|
|
187
|
+
warnings.push({ stepKey: step.key, adapter: resolveTaskAdapter(step.taskSpec?.adapterKey), message });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return warnings;
|
|
191
|
+
}
|
|
161
192
|
export function adapterImplementationStatuses() {
|
|
162
193
|
return [
|
|
163
194
|
{
|
package/man/wfm.1
CHANGED
|
@@ -15,8 +15,11 @@ Workflow files can be Markdown with YAML frontmatter or JSON.
|
|
|
15
15
|
.B doctor [workflow.md|workflow.json] [--json]
|
|
16
16
|
Inspect host adapter setup, LLM access keys, and current adapter implementation status. When a workflow path is provided, also validate schema and runtime requirements without executing steps.
|
|
17
17
|
.TP
|
|
18
|
-
.B
|
|
19
|
-
|
|
18
|
+
.B skill list
|
|
19
|
+
List the agent skills bundled with the npm package.
|
|
20
|
+
.TP
|
|
21
|
+
.B skill install [name ...] [--agent claude|opencode] [--global] [--dir path] [--all] [--force]
|
|
22
|
+
Install bundled agent skills into an agent skill directory. Defaults to the workflow-manager-cli skill and the project-level Claude Code directory (./.claude/skills). Existing skills are not overwritten unless --force is passed.
|
|
20
23
|
.TP
|
|
21
24
|
.B scaffold [path] [--format markdown|json]
|
|
22
25
|
Create a starter workflow file. Format defaults to markdown unless the output
|
|
@@ -128,8 +131,11 @@ Inspect host setup:
|
|
|
128
131
|
Check a workflow before running it:
|
|
129
132
|
.B wfm doctor ./example-workflow.json
|
|
130
133
|
.TP
|
|
131
|
-
|
|
132
|
-
.B wfm
|
|
134
|
+
Install the bundled CLI skill for Claude Code:
|
|
135
|
+
.B wfm skill install
|
|
136
|
+
.TP
|
|
137
|
+
Install every bundled skill globally:
|
|
138
|
+
.B wfm skill install --all --global
|
|
133
139
|
.SH FILES
|
|
134
140
|
.TP
|
|
135
141
|
.B man/wfm.1
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -18,7 +18,21 @@ Install the main package in the project where you want the CLI and skill availab
|
|
|
18
18
|
npm install @workflow-manager/runner
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Then
|
|
21
|
+
Then install bundled skills into an agent's skill directory with the CLI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
wfm skill list # list bundled skills
|
|
25
|
+
wfm skill install # workflow-manager-cli -> ./.claude/skills/
|
|
26
|
+
wfm skill install --global # -> ~/.claude/skills/
|
|
27
|
+
wfm skill install --agent opencode # -> ./.opencode/skill/
|
|
28
|
+
wfm skill install --all # install every bundled skill
|
|
29
|
+
wfm skill install <name> --dir path # install into any directory
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`wfm skill install` copies the skill directory (`SKILL.md` plus any assets) into the
|
|
33
|
+
target so the agent loads it on demand; pass `--force` to overwrite an existing install.
|
|
34
|
+
|
|
35
|
+
Alternatively, use TanStack Intent to discover and map the shipped skills into your agent configuration:
|
|
22
36
|
|
|
23
37
|
```bash
|
|
24
38
|
npx @tanstack/intent@latest list
|
|
@@ -1,116 +1,181 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: workflow-manager-cli
|
|
3
3
|
description: >
|
|
4
|
-
Load this skill when working with the wfm CLI from @workflow-manager/runner
|
|
5
|
-
validating workflow definitions, configuring
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
Load this skill when working with the wfm CLI from @workflow-manager/runner:
|
|
5
|
+
authoring, validating, or running workflow definitions, configuring adapters
|
|
6
|
+
and step skills, controlling runs through the attach API, or publishing
|
|
7
|
+
workflows to the remote registry. Covers doctor, skill, scaffold, validate,
|
|
8
|
+
run, approve, resume, cancel, auth, publish, pull, search, and remote info.
|
|
8
9
|
type: core
|
|
9
|
-
library: @workflow-manager/runner
|
|
10
|
-
library_version: "0.1.0"
|
|
10
|
+
library: "@workflow-manager/runner"
|
|
11
11
|
sources:
|
|
12
12
|
- "navio/workflow-manager:README.md"
|
|
13
13
|
- "navio/workflow-manager:src/index.ts"
|
|
14
14
|
- "navio/workflow-manager:src/parser.ts"
|
|
15
15
|
- "navio/workflow-manager:src/engine.ts"
|
|
16
|
+
- "navio/workflow-manager:src/runtimePreflight.ts"
|
|
17
|
+
- "navio/workflow-manager:src/skillResolver.ts"
|
|
16
18
|
- "navio/workflow-manager:src/remote/commands.ts"
|
|
19
|
+
- "navio/workflow-manager:doc/guide/runner-api.md"
|
|
17
20
|
---
|
|
18
21
|
|
|
19
22
|
# wfm CLI
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
`wfm` (also installed as `workflow-manager`) parses a workflow definition file (Markdown frontmatter or JSON), validates it, and executes its steps through pluggable agent adapters with deterministic dependency ordering, approvals, retries, and rollback routing.
|
|
22
25
|
|
|
23
|
-
##
|
|
26
|
+
## Core flow
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
Follow this sequence unless the user asks for a narrower task:
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
1. `wfm doctor` — inspect host adapter binaries and provider API keys.
|
|
31
|
+
2. `wfm scaffold ./workflow.md` (or `--format json`) — generate a starter definition.
|
|
32
|
+
3. Edit step keys, objectives, `dependsOn`, validation modes, and `taskSpec.init`.
|
|
33
|
+
4. `wfm validate ./workflow.md` — always validate before running or publishing.
|
|
34
|
+
5. `wfm doctor ./workflow.md` — preflight the specific workflow before real adapter runs.
|
|
35
|
+
6. `wfm run ./workflow.md` — execute with live progress; add `--verbose` for agent output.
|
|
36
|
+
7. `wfm publish ./workflow.md` — only after validation succeeds.
|
|
34
37
|
|
|
35
|
-
##
|
|
38
|
+
## Command reference
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
```bash
|
|
41
|
+
wfm doctor [workflow] [--json] # host + per-workflow preflight checks
|
|
42
|
+
wfm skill list # list skills bundled with the npm package
|
|
43
|
+
wfm skill install [name ...] # install bundled skills for an agent (see below)
|
|
44
|
+
wfm scaffold [path] [--format markdown|json]
|
|
45
|
+
wfm validate <workflow>
|
|
46
|
+
wfm run <workflow> [--input input.json] [--objective "text"] [--confirm stepA,stepB:human] [--auto-confirm-all] [--port 43121] [--verbose] [--json]
|
|
47
|
+
wfm approve|resume|cancel [--url ...] [--token ...] [--run-id ...] [--step ...] [--actor ...] [--note ...]
|
|
48
|
+
wfm auth <login|whoami|logout> [--token <token>]
|
|
49
|
+
wfm publish <workflow> [--slug s] [--title t] [--description d] [--visibility public|private] [--version v] [--tag a,b] [--draft]
|
|
50
|
+
wfm pull <owner/slug> [--version v] [--output path]
|
|
51
|
+
wfm search [query]
|
|
52
|
+
wfm remote info <owner/slug>
|
|
53
|
+
wfm man
|
|
54
|
+
```
|
|
38
55
|
|
|
39
|
-
1
|
|
40
|
-
2. Create project agent rules with `wfm agent` when local agent guidance is useful
|
|
41
|
-
3. Scaffold a starter file with `wfm scaffold`
|
|
42
|
-
4. Edit the workflow definition
|
|
43
|
-
5. Validate the file with `wfm validate`
|
|
44
|
-
6. Execute it with `wfm run`
|
|
45
|
-
7. If needed, authenticate and publish with the remote registry commands
|
|
56
|
+
Exit codes: `0` success, `1` validation or runtime error, `2` run finished in a non-success terminal status.
|
|
46
57
|
|
|
47
|
-
##
|
|
58
|
+
## Host configuration
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
wfm doctor
|
|
51
|
-
wfm agent ./AGENTS.md
|
|
60
|
+
Adapters (set per step via `taskSpec.adapterKey`; omit for the default):
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
| Adapter | Kind | Notes |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| `pi-agent` | real (default) | Drives the host `pi` coding agent CLI in print mode. `pi` must be on `PATH`; its auth lives in `~/.pi`, not env vars. |
|
|
65
|
+
| `opencode` | real (opt-in) | Requires the `opencode` CLI on `PATH`. |
|
|
66
|
+
| `claude-code` | real (opt-in) | Requires the `claude` CLI on `PATH`. |
|
|
67
|
+
| `codex` | mock-routed | Deterministic simulation. |
|
|
68
|
+
| `mock` | mock | Deterministic simulation for tests and examples. |
|
|
55
69
|
|
|
56
|
-
wfm
|
|
57
|
-
wfm validate ./example-workflow.json
|
|
70
|
+
Provider API keys are inferred from `taskSpec.init.model` and checked by `wfm doctor` / run preflight:
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
- `openrouter/...` → `OPENROUTER_API_KEY`
|
|
73
|
+
- `openai/...`, `gpt-...` → `OPENAI_API_KEY`
|
|
74
|
+
- `anthropic/...`, `claude-...` → `ANTHROPIC_API_KEY`
|
|
62
75
|
|
|
63
|
-
|
|
76
|
+
Steps can also declare extra required env vars in `taskSpec.payload.requiredEnv`.
|
|
64
77
|
|
|
65
|
-
|
|
66
|
-
wfm auth login --token <token>
|
|
67
|
-
wfm auth whoami
|
|
68
|
-
wfm auth logout
|
|
78
|
+
For custom `pi-agent` commands, the run directory exposes `input.json` / `output.json` envelopes through the `WFM_PI_INPUT_FILE` and `WFM_PI_OUTPUT_FILE` env vars.
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
## Workflow anatomy
|
|
81
|
+
|
|
82
|
+
Both formats describe the same schema; Markdown holds it in YAML frontmatter (body text is free-form notes), JSON holds it directly. Required top-level fields: `key`, `title`, `steps`. Optional: `description`, `objectives`, `inputSchema`, `outputSchema`, `defaultRetryPolicy`, `skills`.
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
---
|
|
86
|
+
key: my-workflow # stable external identifier — change cautiously
|
|
87
|
+
title: My Workflow
|
|
88
|
+
objectives: [deliver a working implementation]
|
|
89
|
+
defaultRetryPolicy:
|
|
90
|
+
maxAttempts: 2
|
|
91
|
+
steps:
|
|
92
|
+
- key: discover # stable step key
|
|
93
|
+
kind: task # task | approval
|
|
94
|
+
objective: Understand requirements and constraints
|
|
95
|
+
dependsOn: []
|
|
96
|
+
validation: # mode: none | human | external
|
|
97
|
+
mode: human
|
|
98
|
+
required: true
|
|
99
|
+
autoConfirm: false
|
|
100
|
+
taskSpec:
|
|
101
|
+
# adapterKey omitted -> pi-agent
|
|
102
|
+
init:
|
|
103
|
+
context: { repo: example/repo }
|
|
104
|
+
skills: [architecture, planning] # resolved step skills, see below
|
|
105
|
+
mcps: [mcp://github]
|
|
106
|
+
systemPrompts: [Focus on architecture trade-offs]
|
|
107
|
+
model: openrouter/anthropic/claude-sonnet-4
|
|
108
|
+
payload: {}
|
|
109
|
+
- key: qa_gate
|
|
110
|
+
kind: approval # human checkpoint, no adapter
|
|
111
|
+
dependsOn: [discover]
|
|
112
|
+
approvalSpec:
|
|
113
|
+
autoApprove: false
|
|
114
|
+
validation: { mode: human, required: true, autoConfirm: false }
|
|
115
|
+
---
|
|
74
116
|
```
|
|
75
117
|
|
|
76
|
-
|
|
118
|
+
Authoring rules:
|
|
119
|
+
|
|
120
|
+
- Workflow keys, step keys, and status strings are stable external identifiers; prefer additive, backward-compatible changes.
|
|
121
|
+
- Make `dependsOn` explicit; the engine resolves dependencies deterministically and rejects cycles.
|
|
122
|
+
- Keep validation modes explicit per step. `human` pauses for approval (inline terminal prompt or attach API); `external` expects an outside system to resume the run.
|
|
123
|
+
- Use `adapterKey: mock` for deterministic examples and tests; use Markdown when humans will review notes, JSON for machine-generated definitions.
|
|
124
|
+
|
|
125
|
+
Steps communicate via ATEP-like envelopes: an `InputEnvelope` (global/step context plus priming config) goes in, an `OutputEnvelope` comes out carrying execution status and a QA routing action — `PROCEED`, `RETRY_CURRENT`, `ROLLBACK_PREVIOUS`, or `RESTART_ALL` — which the engine uses to advance, retry, roll back, or restart the run.
|
|
77
126
|
|
|
78
|
-
|
|
79
|
-
- Keep `runWorkflow(...)` behavior registry-agnostic; registry operations belong in the CLI remote commands
|
|
80
|
-
- Use Markdown workflows when the user wants editable frontmatter plus notes
|
|
81
|
-
- Use JSON workflows when the user wants machine-generated or strongly structured files
|
|
82
|
-
- Use `--auto-confirm-all` only when the workflow is intentionally non-interactive
|
|
83
|
-
- When publishing, preserve the source format the user authored
|
|
127
|
+
## Step skills
|
|
84
128
|
|
|
85
|
-
|
|
129
|
+
`taskSpec.init.skills` names are resolved per step in this order:
|
|
86
130
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
- Use stable step keys because tests and docs may refer to them
|
|
131
|
+
1. Embedded `skills.<name>.content` in the workflow file (how published workflows ship skills).
|
|
132
|
+
2. `skills.<name>.source` — a relative path that must match `skills/**/SKILL.md` under the workflow directory.
|
|
133
|
+
3. `<workflow-dir>/skills/<name>/SKILL.md` (project-local).
|
|
134
|
+
4. `~/.workflow-manager/skills/<name>/SKILL.md` (user-global).
|
|
135
|
+
5. The npm-packaged skills under `node_modules`.
|
|
93
136
|
|
|
94
|
-
|
|
137
|
+
`wfm publish` inlines resolved skill markdown into `skills[*].content` with a `contentSha256` integrity hash; pulled workflows are rejected when declared skill content is missing or tampered.
|
|
95
138
|
|
|
96
|
-
|
|
97
|
-
- Omit `taskSpec.adapterKey` to run a task with the `pi` coding agent CLI
|
|
98
|
-
- Put skills, MCP endpoints, system prompts, model, and context under `taskSpec.init`
|
|
99
|
-
- Human approvals should stay explicit in workflow definitions
|
|
100
|
-
- External validation should be deterministic where possible
|
|
101
|
-
- Use the mock adapter for fast tests and scaffolding flows
|
|
139
|
+
## Running and controlling runs
|
|
102
140
|
|
|
103
|
-
|
|
141
|
+
`wfm run` shows live progress on stderr and starts a local attach API on `127.0.0.1` for the lifetime of the run (OS-assigned port, or `--port`). The base URL and a per-run bearer token are printed to stderr before execution starts; every endpoint except `/health` requires `Authorization: Bearer <token>`. The API serves the session (`/session`), run snapshots, per-step detail, logs, an SSE event stream, and approve/resume/cancel endpoints (contract: `doc/guide/runner-api.md`).
|
|
104
142
|
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
107
|
-
- `
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
143
|
+
- Interactive human approvals show an inline terminal prompt; non-interactive waits are resolved via `wfm approve` / `wfm resume` / `wfm cancel` using the printed URL and token.
|
|
144
|
+
- `--confirm stepA,stepB:human` pre-supplies confirmations for specific steps.
|
|
145
|
+
- Never use `--auto-confirm-all` unless the workflow is intentionally non-interactive — it bypasses every approval gate.
|
|
146
|
+
- `--json` prints the final result (including a `session` object) on stdout while progress stays on stderr.
|
|
147
|
+
- `--input input.json` merges a JSON file into global input state; `--objective` overrides the run objective.
|
|
148
|
+
|
|
149
|
+
## Remote registry
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
wfm auth login --token <token> # token created in the registry web app
|
|
153
|
+
wfm auth whoami
|
|
154
|
+
wfm search <query>
|
|
155
|
+
wfm remote info <owner/slug>
|
|
156
|
+
wfm publish ./workflow.md --visibility public --tag automation
|
|
157
|
+
wfm pull <owner/slug> --output ./pulled-workflow.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
If `publish` fails, check login state with `wfm auth whoami`. Preserve the author's source format when publishing.
|
|
161
|
+
|
|
162
|
+
## Installing these skills for agents
|
|
163
|
+
|
|
164
|
+
`wfm skill install` copies bundled skills into an agent's skill directory so the agent loads this guidance on demand:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
wfm skill list # see what ships with the package
|
|
168
|
+
wfm skill install # workflow-manager-cli -> ./.claude/skills/
|
|
169
|
+
wfm skill install --global # -> ~/.claude/skills/
|
|
170
|
+
wfm skill install --agent opencode # -> ./.opencode/skill/
|
|
171
|
+
wfm skill install --all --force # every bundled skill, overwrite existing
|
|
172
|
+
wfm skill install doc-sync --dir ./my/skills # any other destination
|
|
173
|
+
```
|
|
110
174
|
|
|
111
|
-
##
|
|
175
|
+
## Troubleshooting
|
|
112
176
|
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
177
|
+
- Validation failure: fix the reported schema or dependency-cycle error before re-running; ordinary input errors are reported as messages, not stack traces.
|
|
178
|
+
- `doctor` failure for a real adapter: install the missing CLI (`pi`, `opencode`, `claude`) or export the missing provider API key.
|
|
179
|
+
- Run stuck `waiting_for_approval`: approve inline in the terminal, or use `wfm approve` with the attach URL/token printed at run start.
|
|
180
|
+
- Non-zero exit `2`: the run completed but not successfully — re-run with `--json` or `--verbose` to inspect step output.
|
|
181
|
+
- Publish/pull failures: verify `wfm auth whoami`; for skill integrity errors, confirm the declared skill files exist and match their `contentSha256`.
|