New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

krow-cli

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

krow-cli - npm Package Compare versions

Comparing version
0.2.0
to
0.2.1
+1043
dist/chunk-6Y7VKUWA.js
// src/validators.ts
function isObject(v) {
return typeof v === "object" && v !== null && !Array.isArray(v);
}
function isString(v) {
return typeof v === "string";
}
function isArray(v) {
return Array.isArray(v);
}
function fail(issues) {
return { ok: false, issues };
}
function pass(value) {
return { ok: true, issues: [], value };
}
function validateWorkItems(input) {
if (!isArray(input)) return fail(["work items must be an array"]);
if (input.length === 0) return fail(["work items must not be empty"]);
const issues = [];
for (let i = 0; i < input.length; i++) {
const item = input[i];
if (!isObject(item)) {
issues.push(`items[${i}] must be an object`);
continue;
}
if (!isString(item.id)) issues.push(`items[${i}].id must be a string`);
if (!isString(item.role)) issues.push(`items[${i}].role must be a string`);
if (!isString(item.task)) issues.push(`items[${i}].task must be a string`);
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function allItemsHaveResults(items) {
return items.every((item) => item.result !== void 0);
}
function validateClarifyOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["clarify output must be an object"]);
if (typeof input.ready !== "boolean") issues.push("ready must be a boolean");
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (!isArray(input.assumptions)) issues.push("assumptions must be an array");
if (!isArray(input.decisions)) issues.push("decisions must be an array");
if (!input.ready && isArray(input.decisions) && input.decisions.length === 0) {
issues.push("ready=false requires at least one decision");
}
if (isArray(input.decisions)) {
for (let i = 0; i < input.decisions.length; i++) {
const d = input.decisions[i];
if (!isObject(d)) {
issues.push(`decisions[${i}] must be an object`);
continue;
}
if (!isString(d.id)) issues.push(`decisions[${i}].id must be a string`);
if (!isString(d.question)) issues.push(`decisions[${i}].question must be a string`);
if (!isArray(d.options) || d.options.length === 0) issues.push(`decisions[${i}].options must be a non-empty array`);
}
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validatePlanOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["plan output must be an object"]);
if (!isArray(input.tasks) || input.tasks.length === 0) {
issues.push("tasks must be a non-empty array");
} else {
for (let i = 0; i < input.tasks.length; i++) {
const t = input.tasks[i];
if (!isObject(t)) {
issues.push(`tasks[${i}] must be an object`);
continue;
}
if (!isString(t.id)) issues.push(`tasks[${i}].id must be a string`);
if (!isString(t.title)) issues.push(`tasks[${i}].title must be a string`);
if (!isArray(t.scope) && !isString(t.scope)) issues.push(`tasks[${i}].scope must be an array or string`);
}
}
if (!isString(input.approach) || input.approach.length === 0) issues.push("approach must be a non-empty string");
if (!isArray(input.risks)) issues.push("risks must be an array");
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateExecuteOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["execute output must be an object"]);
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (!isArray(input.changedFiles)) issues.push("changedFiles must be an array");
if (!isArray(input.notes)) issues.push("notes must be an array");
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateVerifyOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["verify output must be an object"]);
if (typeof input.passed !== "boolean") issues.push("passed must be a boolean");
if (!isArray(input.issues)) issues.push("issues must be an array");
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (isArray(input.issues)) {
for (let i = 0; i < input.issues.length; i++) {
const issue = input.issues[i];
if (!isObject(issue)) {
issues.push(`issues[${i}] must be an object`);
continue;
}
if (!isString(issue.severity) || !["error", "warning"].includes(issue.severity)) {
issues.push(`issues[${i}].severity must be "error" or "warning"`);
}
if (!isString(issue.description)) issues.push(`issues[${i}].description must be a string`);
}
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateDecisionAnswers(input) {
if (!isArray(input)) return fail(["decision answers must be an array"]);
const issues = [];
for (let i = 0; i < input.length; i++) {
const a = input[i];
if (!isObject(a)) {
issues.push(`answers[${i}] must be an object`);
continue;
}
if (!isString(a.decisionId)) issues.push(`answers[${i}].decisionId must be a string`);
if (!isString(a.selectedOptionId)) issues.push(`answers[${i}].selectedOptionId must be a string`);
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateWorkflowState(state) {
const issues = [];
if (!isObject(state)) return fail(["state must be an object"]);
if (!isString(state.schemaVersion)) issues.push("schemaVersion required");
if (!isString(state.workflowId)) issues.push("workflowId required");
if (!isString(state.description)) issues.push("description required");
if (!isString(state.status)) issues.push("status required");
if (!isString(state.phase)) issues.push("phase required");
if (!isArray(state.pendingDecisions)) issues.push("pendingDecisions must be an array");
if (!isArray(state.decisionHistory)) issues.push("decisionHistory must be an array");
if (!isArray(state.tasks)) issues.push("tasks must be an array");
if (typeof state.escalateAfter !== "number") issues.push("escalateAfter must be a number");
if (!isString(state.createdAt)) issues.push("createdAt required");
if (!isString(state.updatedAt)) issues.push("updatedAt required");
if (issues.length > 0) return fail(issues);
return pass(state);
}
// src/prompts.ts
function workItemsOutputFormat(phaseOutputExample) {
return `
## Output Format
Always return a JSON array of work items:
\`\`\`json
[
{ "id": "string", "role": "string", "task": "string", "result": <phase output or omit> }
]
\`\`\`
**Simple case** \u2014 you do it yourself, return 1 item with result:
\`\`\`json
[{ "id": "main", "role": "main", "task": "did everything", "result": ${phaseOutputExample} }]
\`\`\`
**Complex case** \u2014 too big for one agent, return multiple items WITHOUT result:
\`\`\`json
[
{ "id": "s1", "role": "structure-analyst", "task": "analyze project structure and entry points" },
{ "id": "s2", "role": "dependency-analyst", "task": "analyze internal and external dependencies" }
]
\`\`\`
These will be executed in parallel by separate agents. Results will be synthesized afterward.
Choose based on complexity. Most tasks need just 1 item.`;
}
function buildClarifyPrompt(state) {
const parts = [];
parts.push(`# Clarify Phase`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
parts.push(`## Your task`);
parts.push(`Make this request concrete enough to plan and execute safely.`);
parts.push(``);
if (state.decisionHistory.length > 0) {
parts.push(`## Previous decisions`);
for (const d of state.decisionHistory) {
parts.push(`- ${d.decisionId}: ${d.selectedOptionId}${d.customInput ? ` (${d.customInput})` : ""}`);
}
parts.push(``);
}
parts.push(`## Step 1: Understand`);
parts.push(`Restate what the user wants in your own words. Identify the core intent.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Explore the codebase. Find relevant files, patterns, constraints, and dependencies.`);
parts.push(``);
parts.push(`## Step 3: Tighten`);
parts.push(`Identify assumptions you're making. Surface any gaps or ambiguities.`);
parts.push(`If information is missing and you cannot proceed safely, define decisions for the user.`);
parts.push(`If everything is clear enough, mark as ready.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{ready, summary, assumptions, verifyFocus, decisions}`));
return parts.join("\n");
}
function buildPlanPrompt(state) {
const parts = [];
parts.push(`# Plan Phase`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
if (state.clarifyOutput) {
parts.push(`## Clarify summary`);
parts.push(state.clarifyOutput.summary);
parts.push(``);
if (state.clarifyOutput.assumptions.length > 0) {
parts.push(`## Assumptions`);
for (const a of state.clarifyOutput.assumptions) {
parts.push(`- ${a}`);
}
parts.push(``);
}
}
const adjustments = state.decisionHistory.filter((d) => d.decisionId === "plan-adjustment");
if (adjustments.length > 0) {
parts.push(`## Plan adjustment feedback`);
for (const a of adjustments) {
parts.push(`- ${a.customInput}`);
}
parts.push(``);
}
parts.push(`## Your task`);
parts.push(`Design an approach and break it into concrete, ordered tasks.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review the clarify output and assumptions. Understand what needs to happen.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Identify files, modules, and dependencies involved. Check feasibility.`);
parts.push(``);
parts.push(`## Step 3: Design`);
parts.push(`Break the work into bounded tasks. Each task should:`);
parts.push(`- Have a clear, narrow scope (specific files/modules)`);
parts.push(`- Be independently verifiable`);
parts.push(`- List what context it needs from earlier tasks`);
parts.push(`- Declare dependencies on other tasks`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{approach, risks, tasks: [{id, title, scope, context, dependsOn}]}`));
return parts.join("\n");
}
function buildExecutePrompt(state, task) {
const parts = [];
parts.push(`# Execute Phase \u2014 Task: ${task.title}`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
parts.push(`## Task scope`);
parts.push(`Files/modules to modify: ${task.scope.join(", ") || "as needed"}`);
parts.push(``);
parts.push(`## Task context`);
const ctx = Array.isArray(task.context) ? task.context : task.context ? [task.context] : [];
parts.push(`Files/data to read: ${ctx.join(", ") || "as needed"}`);
parts.push(``);
if (state.clarifyOutput) {
parts.push(`## Clarify summary`);
parts.push(state.clarifyOutput.summary);
parts.push(``);
}
if (state.planOutput) {
parts.push(`## Approach`);
parts.push(state.planOutput.approach);
parts.push(``);
}
if (task.verifyOutput && !task.verifyOutput.passed) {
parts.push(`## Previous verification feedback`);
parts.push(task.verifyOutput.summary);
if (task.verifyOutput.retryHint) {
parts.push(`Retry hint: ${task.verifyOutput.retryHint}`);
}
for (const issue of task.verifyOutput.issues) {
parts.push(`- [${issue.severity}] ${issue.description}${issue.suggestion ? ` \u2192 ${issue.suggestion}` : ""}`);
}
parts.push(``);
}
parts.push(`## Your task`);
parts.push(`Implement the changes described in this task.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review what this task must produce. Check the scope and constraints.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Read the target files. Understand the current state before making changes.`);
parts.push(``);
parts.push(`## Step 3: Implement`);
parts.push(`Make the changes. Stay within scope.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{summary, changedFiles, notes}`));
return parts.join("\n");
}
function buildVerifyPrompt(state, task) {
const parts = [];
parts.push(`# Verify Phase \u2014 Task: ${task.title}`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`Determine if the task was completed correctly.`);
parts.push(``);
if (task.executeOutput) {
parts.push(`## What was done`);
parts.push(task.executeOutput.summary);
parts.push(``);
parts.push(`## Changed files`);
for (const f of task.executeOutput.changedFiles) {
parts.push(`- ${f}`);
}
parts.push(``);
if (task.executeOutput.notes.length > 0) {
parts.push(`## Notes from executor`);
for (const n of task.executeOutput.notes) {
parts.push(`- ${n}`);
}
parts.push(``);
}
}
if (state.clarifyOutput?.verifyFocus && state.clarifyOutput.verifyFocus.length > 0) {
parts.push(`## Verify focus`);
for (const f of state.clarifyOutput.verifyFocus) {
parts.push(`- ${f}`);
}
parts.push(``);
}
parts.push(`## Attempt ${task.verifyAttempts}`);
parts.push(``);
parts.push(`## Your task`);
parts.push(`Try to disprove the claimed result. Report what you find.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review what was claimed to be done.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Read the changed files. Run relevant checks or tests if applicable.`);
parts.push(``);
parts.push(`## Step 3: Validate`);
parts.push(`Check each claim against evidence. Look for missing pieces, bugs, or regressions.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{passed, issues: [{severity, description, suggestion?}], summary, retryHint?}`));
return parts.join("\n");
}
// src/instructions.ts
var _prefix;
function cliPrefix() {
if (_prefix) return _prefix;
const arg1 = process.argv[1] ?? "";
if (arg1.includes("cli.ts") || arg1.includes("cli.js")) {
_prefix = `node ${arg1}`;
} else {
_prefix = "krow";
}
return _prefix;
}
function submitWorkItemsCommand(workflowId, phase, taskId) {
const taskArg = taskId ? ` --task ${taskId}` : "";
return `${cliPrefix()} submit ${workflowId} ${phase}${taskArg} '<JSON>'`;
}
function submitWorkItemResultCommand(workflowId, itemId) {
return `${cliPrefix()} submit-item ${workflowId} ${itemId} '<RESULT>'`;
}
function submitSynthesizedCommand(workflowId) {
return `${cliPrefix()} submit-synthesized ${workflowId} '<JSON>'`;
}
function decideCommand(workflowId) {
return `${cliPrefix()} decide ${workflowId} '<JSON>'`;
}
function approvePlanCommand(workflowId) {
return `${cliPrefix()} approve-plan ${workflowId}`;
}
function adjustPlanCommand(workflowId) {
return `${cliPrefix()} adjust-plan ${workflowId} '<feedback>'`;
}
function resolveVerifyCommand(workflowId, taskId, action) {
return `${cliPrefix()} resolve-verify ${workflowId} ${taskId} ${action}`;
}
function phaseInstructions(phase, onComplete) {
return [
`Execute the prompt for the "${phase}" phase.`,
``,
`CRITICAL OUTPUT REQUIREMENT: Your response MUST be a JSON array of work items. No prose, no markdown outside JSON, no explanation.`,
`Schema: [{ "id": string, "role": string, "task": string, "result"?: object }]`,
``,
`Simple case (you do it yourself) \u2014 return 1 item with result:`,
`[{ "id": "main", "role": "main", "task": "did everything", "result": { ... } }]`,
``,
`Complex case (needs parallel agents) \u2014 return multiple items WITHOUT result:`,
`[{ "id": "s1", "role": "analyst", "task": "..." }, { "id": "s2", "role": "reviewer", "task": "..." }]`,
``,
`Then run: ${onComplete}`
].join("\n");
}
function gateClarifyInstructions(onComplete) {
return [
`Present each decision to the user and collect their answers.`,
`Then run: ${onComplete}`,
`Replace '<JSON>' with an array of { decisionId, selectedOptionId, customInput? }.`
].join("\n");
}
function gatePlanInstructions(approveCmd, adjustCmd) {
return [
`Present the plan to the user for review.`,
`If approved, run: ${approveCmd}`,
`If adjustments needed, run: ${adjustCmd}`
].join("\n");
}
function gateVerifyInstructions(taskTitle, attempts, continueCmd, stopCmd) {
return [
`Task "${taskTitle}" has failed verification ${attempts} time(s).`,
`Present the issues to the user.`,
`Keep trying: ${continueCmd}`,
`Stop this task: ${stopCmd}`
].join("\n");
}
function batchInstructions(count) {
return [
`${count} items to execute in parallel.`,
`Execute each independently. Submit each result separately using its onComplete command.`
].join("\n");
}
function synthesizeInstructions(phase, onComplete) {
return [
`All work items completed. Synthesize the results into a single ${phase} output.`,
`Then run: ${onComplete}`
].join("\n");
}
function doneInstructions() {
return "The workflow has reached a terminal state. Report the result to the user.";
}
function faultInstructions(recoverable) {
if (recoverable) return "A recoverable error occurred. Review the issues and retry.";
return "An unrecoverable error occurred. Report the error to the user.";
}
// src/responses.ts
var PHASE_CAPABILITY = {
clarify: "read",
plan: "read",
execute: "write",
verify: "read"
};
function clarifyContext() {
return { isolation: "isolated", inputs: [] };
}
function planContext(state) {
const inputs = [];
if (state.clarifyOutput) inputs.push({ type: "data", ref: "clarifyOutput" });
return { isolation: "isolated", inputs };
}
function executeContext(state, task) {
const inputs = [];
if (state.clarifyOutput) inputs.push({ type: "data", ref: "clarifyOutput" });
if (state.planOutput) inputs.push({ type: "data", ref: "planOutput.approach" });
const ctx = Array.isArray(task.context) ? task.context : task.context ? [task.context] : [];
for (const c of ctx) inputs.push({ type: "file", ref: c });
return { isolation: "isolated", inputs };
}
function verifyContext(state, task) {
const inputs = [];
if (task.executeOutput) {
inputs.push({ type: "data", ref: `tasks.${task.id}.executeOutput` });
for (const f of task.executeOutput.changedFiles) inputs.push({ type: "file", ref: f });
}
if (state.clarifyOutput?.verifyFocus) inputs.push({ type: "data", ref: "clarifyOutput.verifyFocus" });
return { isolation: "isolated", inputs };
}
function outputSpec(phase) {
return { schema: `${phase}-output`, format: phase === "execute" ? "files" : "json" };
}
function buildPromptForTask(state, phase, task) {
switch (phase) {
case "execute":
return buildExecutePrompt(state, task);
case "verify":
return buildVerifyPrompt(state, task);
default:
return "";
}
}
function contextForTask(state, phase, task) {
switch (phase) {
case "execute":
return executeContext(state, task);
case "verify":
return verifyContext(state, task);
default:
return { isolation: "isolated", inputs: [] };
}
}
function buildPhaseResponse(state, phase, taskId) {
if (phase === "clarify" || phase === "plan") {
const onComplete2 = submitWorkItemsCommand(state.workflowId, phase);
return {
type: "phase",
phase,
workflowId: state.workflowId,
prompt: phase === "clarify" ? buildClarifyPrompt(state) : buildPlanPrompt(state),
capability: PHASE_CAPABILITY[phase],
context: phase === "clarify" ? clarifyContext() : planContext(state),
output: outputSpec(phase),
onComplete: onComplete2,
instructions: phaseInstructions(phase, onComplete2)
};
}
const task = taskId ? state.tasks.find((t) => t.id === taskId) : void 0;
const onComplete = submitWorkItemsCommand(state.workflowId, phase, taskId);
if (!task) {
return {
type: "phase",
phase,
workflowId: state.workflowId,
prompt: `Error: no task found for ${phase} phase.`,
capability: PHASE_CAPABILITY[phase],
context: { isolation: "isolated", inputs: [] },
output: outputSpec(phase),
onComplete,
instructions: phaseInstructions(phase, onComplete)
};
}
return {
type: "phase",
phase,
workflowId: state.workflowId,
taskId: task.id,
prompt: buildPromptForTask(state, phase, task),
capability: PHASE_CAPABILITY[phase],
context: contextForTask(state, phase, task),
output: outputSpec(phase),
onComplete,
instructions: phaseInstructions(phase, onComplete)
};
}
function buildBatchResponse(state, items) {
const responses = items.map((item) => buildPhaseResponse(state, item.phase, item.taskId));
return {
type: "batch",
workflowId: state.workflowId,
responses,
instructions: batchInstructions(responses.length)
};
}
function buildWorkItemBatchResponse(state, work) {
const running = work.items.filter((i) => i.status === "running" || i.status === "pending");
const responses = running.map((item) => {
const onComplete = submitWorkItemResultCommand(state.workflowId, item.id);
return {
type: "phase",
phase: work.phase,
workflowId: state.workflowId,
taskId: work.taskId,
prompt: `# Work Item: ${item.role}
${item.task}`,
capability: PHASE_CAPABILITY[work.phase],
context: { isolation: "isolated", inputs: [] },
output: { schema: "work-item-result", format: "json" },
onComplete,
instructions: `Execute this work item. Return your result as JSON (no prose, no markdown). Then run: ${onComplete}`
};
});
return {
type: "batch",
workflowId: state.workflowId,
responses,
instructions: batchInstructions(responses.length)
};
}
function buildSynthesizeResponse(state, work) {
const results = work.items.map((i) => `### ${i.role}
${i.result ?? "(no result)"}`).join("\n\n");
const onComplete = submitSynthesizedCommand(state.workflowId);
return {
type: "phase",
phase: work.phase,
workflowId: state.workflowId,
taskId: work.taskId,
prompt: `# Synthesize Results
${work.synthesize}
## Work Item Results
${results}`,
capability: PHASE_CAPABILITY[work.phase],
context: { isolation: "shared", inputs: [] },
output: outputSpec(work.phase),
onComplete,
instructions: synthesizeInstructions(work.phase, onComplete)
};
}
function buildGateResponse(state, gate, taskId) {
if (gate === "clarify") {
const onComplete = decideCommand(state.workflowId);
return { type: "gate", gate: "clarify", workflowId: state.workflowId, decisions: state.pendingDecisions, onComplete, instructions: gateClarifyInstructions(onComplete) };
}
if (gate === "plan") {
const approveCmd = approvePlanCommand(state.workflowId);
const adjustCmd = adjustPlanCommand(state.workflowId);
return { type: "gate", gate: "plan", workflowId: state.workflowId, tasks: state.tasks, approach: state.planOutput?.approach, risks: state.planOutput?.risks, onComplete: approveCmd, instructions: gatePlanInstructions(approveCmd, adjustCmd) };
}
const task = taskId ? state.tasks.find((t) => t.id === taskId) : void 0;
const continueCmd = resolveVerifyCommand(state.workflowId, taskId ?? "", "continue");
const stopCmd = resolveVerifyCommand(state.workflowId, taskId ?? "", "stop");
return { type: "gate", gate: "verify", workflowId: state.workflowId, taskId, verifyAttempts: task?.verifyAttempts, lastIssues: task?.verifyOutput?.issues, onComplete: continueCmd, instructions: gateVerifyInstructions(task?.title ?? taskId ?? "", task?.verifyAttempts ?? 0, continueCmd, stopCmd) };
}
function buildDoneResponse(state) {
let message;
if (state.status === "completed") message = "Workflow completed successfully.";
else if (state.status === "blocked") message = `Workflow blocked: ${state.blockedReason || "unknown reason"}`;
else message = `Workflow stopped: ${state.blockedReason || "stopped by user"}`;
return { type: "done", workflowId: state.workflowId, status: state.status, message, instructions: doneInstructions() };
}
function buildFaultResponse(state, error, issues, recoverable) {
return { type: "fault", workflowId: state.workflowId, error, issues, recoverable, instructions: faultInstructions(recoverable) };
}
// src/orchestrator.ts
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function now() {
return (/* @__PURE__ */ new Date()).toISOString();
}
function makeId() {
return `wf-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
function setPhase(state, status, phase) {
state.status = status;
state.phase = phase;
state.updatedAt = now();
}
function findTask(state, taskId) {
return state.tasks.find((t) => t.id === taskId);
}
function readyTasks(state) {
const doneIds = new Set(state.tasks.filter((t) => t.status === "done").map((t) => t.id));
return state.tasks.filter(
(t) => t.status === "pending" && t.dependsOn.every((dep) => doneIds.has(dep))
);
}
function allTasksDone(state) {
return state.tasks.length > 0 && state.tasks.every((t) => t.status === "done");
}
function createWorkflow(description) {
const timestamp = now();
const state = {
schemaVersion: "1.0.0",
workflowId: makeId(),
description,
status: "phase_clarify",
phase: "clarify",
pendingDecisions: [],
decisionHistory: [],
tasks: [],
escalateAfter: 3,
createdAt: timestamp,
updatedAt: timestamp
};
const v = validateWorkflowState(state);
if (!v.ok) {
return { state, response: buildFaultResponse(state, "invalid initial state", v.issues, false) };
}
return { state, response: nextResponse(state) };
}
function nextResponse(state) {
switch (state.status) {
case "phase_clarify":
return buildPhaseResponse(state, "clarify");
case "gate_clarify":
return buildGateResponse(state, "clarify");
case "phase_plan":
return buildPhaseResponse(state, "plan");
case "gate_plan":
return buildGateResponse(state, "plan");
case "phase_execute":
case "phase_verify":
return buildNextTaskResponses(state);
case "phase_synthesize":
return buildSynthesizeFromState(state);
case "gate_verify":
return buildVerifyGate(state);
case "completed":
case "blocked":
case "stopped":
return buildDoneResponse(state);
default:
return buildFaultResponse(state, `unknown status: ${state.status}`, [], false);
}
}
function buildNextTaskResponses(state) {
const actionable = [];
for (const task of state.tasks) {
if (task.status === "executing") actionable.push({ task, phase: "execute" });
else if (task.status === "verifying") actionable.push({ task, phase: "verify" });
}
const ready = readyTasks(state);
for (const task of ready) {
task.status = "executing";
actionable.push({ task, phase: "execute" });
}
if (actionable.length === 0) {
if (allTasksDone(state)) {
state.status = "completed";
state.updatedAt = now();
return buildDoneResponse(state);
}
return buildFaultResponse(state, "no actionable tasks", [], false);
}
if (actionable.length === 1) {
return buildPhaseResponse(state, actionable[0].phase, actionable[0].task.id);
}
return buildBatchResponse(state, actionable.map((a) => ({ phase: a.phase, taskId: a.task.id })));
}
function buildVerifyGate(state) {
const stuck = state.tasks.find((t) => t.status === "verifying" && t.verifyAttempts >= state.escalateAfter);
if (!stuck) return buildFaultResponse(state, "gate_verify but no stuck task found", [], false);
return buildGateResponse(state, "verify", stuck.id);
}
function buildSynthesizeFromState(state) {
if (!state.activeWork) return buildFaultResponse(state, "phase_synthesize but no active work", [], false);
return buildSynthesizeResponse(state, state.activeWork);
}
function submitWorkItems(state, phase, input, taskId) {
const v = validateWorkItems(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid work items", v.issues, true) };
}
const items = v.value;
if (items.length === 1 && allItemsHaveResults(items)) {
return submitPhaseOutput(state, phase, items[0].result, taskId);
}
const next = clone(state);
next.activeWork = {
phase,
taskId,
synthesize: `Combine the results from ${items.length} work items into a single ${phase} output.`,
items: items.map((item) => ({
id: item.id,
role: item.role,
task: item.task,
status: item.result !== void 0 ? "done" : "pending",
result: item.result !== void 0 ? JSON.stringify(item.result) : void 0
}))
};
if (next.activeWork.items.every((i) => i.status === "done")) {
next.status = "phase_synthesize";
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
for (const item of next.activeWork.items) {
if (item.status === "pending") item.status = "running";
}
next.status = "phase_synthesize";
next.updatedAt = now();
return { state: next, response: buildWorkItemBatchResponse(next, next.activeWork) };
}
function submitWorkItemResult(state, itemId, result) {
if (!state.activeWork) {
return { response: buildFaultResponse(state, "no active work items", [], true) };
}
const next = clone(state);
const item = next.activeWork.items.find((i) => i.id === itemId);
if (!item) {
return { response: buildFaultResponse(next, `work item ${itemId} not found`, [], true) };
}
item.status = "done";
item.result = result;
next.updatedAt = now();
if (next.activeWork.items.every((i) => i.status === "done")) {
next.status = "phase_synthesize";
return { state: next, response: nextResponse(next) };
}
return { state: next, response: buildWorkItemBatchResponse(next, next.activeWork) };
}
function submitSynthesizedResult(state, input) {
if (!state.activeWork) {
return { response: buildFaultResponse(state, "no active work to synthesize", [], true) };
}
const work = state.activeWork;
const next = clone(state);
next.activeWork = void 0;
const phaseStatus = `phase_${work.phase}`;
next.status = phaseStatus;
next.phase = work.phase;
return submitPhaseOutput(next, work.phase, input, work.taskId);
}
function submitPhaseOutput(state, phase, input, taskId) {
switch (phase) {
case "clarify":
return applyClarify(state, input);
case "plan":
return applyPlan(state, input);
case "execute":
return applyExecute(state, input, taskId);
case "verify":
return applyVerify(state, input, taskId);
default:
return { response: buildFaultResponse(state, `unsupported phase: ${phase}`, [], false) };
}
}
function submitDecisions(state, input) {
if (state.status !== "gate_clarify") {
return { response: buildFaultResponse(state, "decisions only valid during gate_clarify", [], true) };
}
const v = validateDecisionAnswers(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid decision answers", v.issues, true) };
}
const next = clone(state);
next.decisionHistory.push(...v.value);
next.pendingDecisions = [];
setPhase(next, "phase_clarify", "clarify");
return { state: next, response: nextResponse(next) };
}
function approvePlan(state) {
if (state.status !== "gate_plan") {
return { response: buildFaultResponse(state, "approve-plan only valid during gate_plan", [], true) };
}
const next = clone(state);
if (next.tasks.length === 0) {
return { response: buildFaultResponse(next, "no tasks to execute", [], true) };
}
const ready = readyTasks(next);
for (const task of ready) task.status = "executing";
setPhase(next, "phase_execute", "execute");
return { state: next, response: nextResponse(next) };
}
function adjustPlan(state, feedback) {
if (state.status !== "gate_plan") {
return { response: buildFaultResponse(state, "adjust-plan only valid during gate_plan", [], true) };
}
const next = clone(state);
next.planOutput = void 0;
next.tasks = [];
next.decisionHistory.push({
decisionId: "plan-adjustment",
selectedOptionId: "feedback",
customInput: feedback
});
setPhase(next, "phase_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function resolveVerifyGate(state, taskId, action) {
if (state.status !== "gate_verify") {
return { response: buildFaultResponse(state, "resolve-verify only valid during gate_verify", [], true) };
}
const next = clone(state);
const task = findTask(next, taskId);
if (!task) {
return { response: buildFaultResponse(next, `task ${taskId} not found`, [], true) };
}
if (action === "stop") {
task.status = "failed";
next.status = "blocked";
next.blockedReason = `User stopped task ${taskId} after ${task.verifyAttempts} verify attempts`;
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
task.status = "executing";
setPhase(next, "phase_execute", "execute");
return { state: next, response: nextResponse(next) };
}
function stopWorkflow(state, reason = "stopped by user") {
const next = clone(state);
next.status = "stopped";
next.blockedReason = reason;
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
function applyClarify(state, input) {
const v = validateClarifyOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid clarify output", v.issues, true) };
}
const next = clone(state);
next.clarifyOutput = v.value;
if (!v.value.ready) {
next.pendingDecisions = v.value.decisions;
setPhase(next, "gate_clarify", "clarify");
return { state: next, response: nextResponse(next) };
}
next.pendingDecisions = [];
setPhase(next, "phase_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function applyPlan(state, input) {
const v = validatePlanOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid plan output", v.issues, true) };
}
const next = clone(state);
next.planOutput = v.value;
next.tasks = v.value.tasks.map((td) => ({
id: td.id,
title: td.title,
scope: Array.isArray(td.scope) ? td.scope : td.scope ? [td.scope] : [],
context: Array.isArray(td.context) ? td.context : td.context ? [String(td.context)] : [],
dependsOn: Array.isArray(td.dependsOn) ? td.dependsOn : td.dependsOn ? [td.dependsOn] : [],
status: "pending",
verifyAttempts: 0
}));
setPhase(next, "gate_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function applyExecute(state, input, taskId) {
const v = validateExecuteOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid execute output", v.issues, true) };
}
const next = clone(state);
const resolvedId = taskId ?? next.tasks.find((t) => t.status === "executing")?.id;
const task = resolvedId ? findTask(next, resolvedId) : void 0;
if (!task) {
return { response: buildFaultResponse(next, "no executing task", [], true) };
}
task.executeOutput = v.value;
task.status = "verifying";
task.verifyAttempts += 1;
updateWorkflowPhase(next);
return { state: next, response: nextResponse(next) };
}
function applyVerify(state, input, taskId) {
const v = validateVerifyOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid verify output", v.issues, true) };
}
const next = clone(state);
const resolvedId = taskId ?? next.tasks.find((t) => t.status === "verifying")?.id;
const task = resolvedId ? findTask(next, resolvedId) : void 0;
if (!task) {
return { response: buildFaultResponse(next, "no verifying task", [], true) };
}
task.verifyOutput = v.value;
if (v.value.passed) {
task.status = "done";
} else if (task.verifyAttempts >= next.escalateAfter) {
next.status = "gate_verify";
next.phase = "verify";
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
} else {
task.status = "executing";
}
updateWorkflowPhase(next);
return { state: next, response: nextResponse(next) };
}
function updateWorkflowPhase(state) {
if (allTasksDone(state)) {
state.status = "completed";
state.updatedAt = now();
return;
}
const hasExecuting = state.tasks.some((t) => t.status === "executing");
const hasVerifying = state.tasks.some((t) => t.status === "verifying");
const hasReady = readyTasks(state).length > 0;
if (hasExecuting || hasReady) {
setPhase(state, "phase_execute", "execute");
} else if (hasVerifying) {
setPhase(state, "phase_verify", "verify");
}
}
// src/state-store.ts
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
import { join, dirname } from "path";
var STATE_DIR = ".krow/state";
function statePath(workflowId, rootDir = process.cwd()) {
return join(rootDir, STATE_DIR, `${workflowId}.json`);
}
function saveState(state, rootDir = process.cwd()) {
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
const p = statePath(state.workflowId, rootDir);
const dir = dirname(p);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
writeFileSync(p, JSON.stringify(state, null, 2));
}
function loadState(workflowId, rootDir = process.cwd()) {
const p = statePath(workflowId, rootDir);
if (!existsSync(p)) return null;
return JSON.parse(readFileSync(p, "utf-8"));
}
function listStates(rootDir = process.cwd()) {
const dir = join(rootDir, STATE_DIR);
if (!existsSync(dir)) return [];
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
try {
return JSON.parse(readFileSync(join(dir, f), "utf-8"));
} catch {
return null;
}
}).filter((s) => s !== null).filter((s) => s.status !== "completed" && s.status !== "stopped").sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
}
export {
validateWorkItems,
allItemsHaveResults,
validateClarifyOutput,
validatePlanOutput,
validateExecuteOutput,
validateVerifyOutput,
validateDecisionAnswers,
validateWorkflowState,
buildClarifyPrompt,
buildPlanPrompt,
buildExecutePrompt,
buildVerifyPrompt,
buildPhaseResponse,
buildBatchResponse,
buildWorkItemBatchResponse,
buildSynthesizeResponse,
buildGateResponse,
buildDoneResponse,
buildFaultResponse,
createWorkflow,
nextResponse,
submitWorkItems,
submitWorkItemResult,
submitSynthesizedResult,
submitPhaseOutput,
submitDecisions,
approvePlan,
adjustPlan,
resolveVerifyGate,
stopWorkflow,
statePath,
saveState,
loadState,
listStates
};
+1
-1

@@ -16,3 +16,3 @@ #!/usr/bin/env node

submitWorkItems
} from "./chunk-MDLCULSL.js";
} from "./chunk-6Y7VKUWA.js";

@@ -19,0 +19,0 @@ // src/cli.ts

@@ -36,3 +36,3 @@ import {

validateWorkflowState
} from "./chunk-MDLCULSL.js";
} from "./chunk-6Y7VKUWA.js";
export {

@@ -39,0 +39,0 @@ adjustPlan,

{
"name": "krow-cli",
"version": "0.2.0",
"version": "0.2.1",
"description": "A host-agnostic agent harness for coding work",

@@ -5,0 +5,0 @@ "type": "module",

// src/validators.ts
function isObject(v) {
return typeof v === "object" && v !== null && !Array.isArray(v);
}
function isString(v) {
return typeof v === "string";
}
function isArray(v) {
return Array.isArray(v);
}
function fail(issues) {
return { ok: false, issues };
}
function pass(value) {
return { ok: true, issues: [], value };
}
function validateWorkItems(input) {
if (!isArray(input)) return fail(["work items must be an array"]);
if (input.length === 0) return fail(["work items must not be empty"]);
const issues = [];
for (let i = 0; i < input.length; i++) {
const item = input[i];
if (!isObject(item)) {
issues.push(`items[${i}] must be an object`);
continue;
}
if (!isString(item.id)) issues.push(`items[${i}].id must be a string`);
if (!isString(item.role)) issues.push(`items[${i}].role must be a string`);
if (!isString(item.task)) issues.push(`items[${i}].task must be a string`);
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function allItemsHaveResults(items) {
return items.every((item) => item.result !== void 0);
}
function validateClarifyOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["clarify output must be an object"]);
if (typeof input.ready !== "boolean") issues.push("ready must be a boolean");
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (!isArray(input.assumptions)) issues.push("assumptions must be an array");
if (!isArray(input.decisions)) issues.push("decisions must be an array");
if (!input.ready && isArray(input.decisions) && input.decisions.length === 0) {
issues.push("ready=false requires at least one decision");
}
if (isArray(input.decisions)) {
for (let i = 0; i < input.decisions.length; i++) {
const d = input.decisions[i];
if (!isObject(d)) {
issues.push(`decisions[${i}] must be an object`);
continue;
}
if (!isString(d.id)) issues.push(`decisions[${i}].id must be a string`);
if (!isString(d.question)) issues.push(`decisions[${i}].question must be a string`);
if (!isArray(d.options) || d.options.length === 0) issues.push(`decisions[${i}].options must be a non-empty array`);
}
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validatePlanOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["plan output must be an object"]);
if (!isArray(input.tasks) || input.tasks.length === 0) {
issues.push("tasks must be a non-empty array");
} else {
for (let i = 0; i < input.tasks.length; i++) {
const t = input.tasks[i];
if (!isObject(t)) {
issues.push(`tasks[${i}] must be an object`);
continue;
}
if (!isString(t.id)) issues.push(`tasks[${i}].id must be a string`);
if (!isString(t.title)) issues.push(`tasks[${i}].title must be a string`);
if (!isArray(t.scope) && !isString(t.scope)) issues.push(`tasks[${i}].scope must be an array or string`);
}
}
if (!isString(input.approach) || input.approach.length === 0) issues.push("approach must be a non-empty string");
if (!isArray(input.risks)) issues.push("risks must be an array");
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateExecuteOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["execute output must be an object"]);
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (!isArray(input.changedFiles)) issues.push("changedFiles must be an array");
if (!isArray(input.notes)) issues.push("notes must be an array");
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateVerifyOutput(input) {
const issues = [];
if (!isObject(input)) return fail(["verify output must be an object"]);
if (typeof input.passed !== "boolean") issues.push("passed must be a boolean");
if (!isArray(input.issues)) issues.push("issues must be an array");
if (!isString(input.summary) || input.summary.length === 0) issues.push("summary must be a non-empty string");
if (isArray(input.issues)) {
for (let i = 0; i < input.issues.length; i++) {
const issue = input.issues[i];
if (!isObject(issue)) {
issues.push(`issues[${i}] must be an object`);
continue;
}
if (!isString(issue.severity) || !["error", "warning"].includes(issue.severity)) {
issues.push(`issues[${i}].severity must be "error" or "warning"`);
}
if (!isString(issue.description)) issues.push(`issues[${i}].description must be a string`);
}
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateDecisionAnswers(input) {
if (!isArray(input)) return fail(["decision answers must be an array"]);
const issues = [];
for (let i = 0; i < input.length; i++) {
const a = input[i];
if (!isObject(a)) {
issues.push(`answers[${i}] must be an object`);
continue;
}
if (!isString(a.decisionId)) issues.push(`answers[${i}].decisionId must be a string`);
if (!isString(a.selectedOptionId)) issues.push(`answers[${i}].selectedOptionId must be a string`);
}
if (issues.length > 0) return fail(issues);
return pass(input);
}
function validateWorkflowState(state) {
const issues = [];
if (!isObject(state)) return fail(["state must be an object"]);
if (!isString(state.schemaVersion)) issues.push("schemaVersion required");
if (!isString(state.workflowId)) issues.push("workflowId required");
if (!isString(state.description)) issues.push("description required");
if (!isString(state.status)) issues.push("status required");
if (!isString(state.phase)) issues.push("phase required");
if (!isArray(state.pendingDecisions)) issues.push("pendingDecisions must be an array");
if (!isArray(state.decisionHistory)) issues.push("decisionHistory must be an array");
if (!isArray(state.tasks)) issues.push("tasks must be an array");
if (typeof state.escalateAfter !== "number") issues.push("escalateAfter must be a number");
if (!isString(state.createdAt)) issues.push("createdAt required");
if (!isString(state.updatedAt)) issues.push("updatedAt required");
if (issues.length > 0) return fail(issues);
return pass(state);
}
// src/prompts.ts
function workItemsOutputFormat(phaseOutputExample) {
return `
## Output Format
Always return a JSON array of work items:
\`\`\`json
[
{ "id": "string", "role": "string", "task": "string", "result": <phase output or omit> }
]
\`\`\`
**Simple case** \u2014 you do it yourself, return 1 item with result:
\`\`\`json
[{ "id": "main", "role": "main", "task": "did everything", "result": ${phaseOutputExample} }]
\`\`\`
**Complex case** \u2014 too big for one agent, return multiple items WITHOUT result:
\`\`\`json
[
{ "id": "s1", "role": "structure-analyst", "task": "analyze project structure and entry points" },
{ "id": "s2", "role": "dependency-analyst", "task": "analyze internal and external dependencies" }
]
\`\`\`
These will be executed in parallel by separate agents. Results will be synthesized afterward.
Choose based on complexity. Most tasks need just 1 item.`;
}
function buildClarifyPrompt(state) {
const parts = [];
parts.push(`# Clarify Phase`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
parts.push(`## Your task`);
parts.push(`Make this request concrete enough to plan and execute safely.`);
parts.push(``);
if (state.decisionHistory.length > 0) {
parts.push(`## Previous decisions`);
for (const d of state.decisionHistory) {
parts.push(`- ${d.decisionId}: ${d.selectedOptionId}${d.customInput ? ` (${d.customInput})` : ""}`);
}
parts.push(``);
}
parts.push(`## Step 1: Understand`);
parts.push(`Restate what the user wants in your own words. Identify the core intent.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Explore the codebase. Find relevant files, patterns, constraints, and dependencies.`);
parts.push(``);
parts.push(`## Step 3: Tighten`);
parts.push(`Identify assumptions you're making. Surface any gaps or ambiguities.`);
parts.push(`If information is missing and you cannot proceed safely, define decisions for the user.`);
parts.push(`If everything is clear enough, mark as ready.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{ready, summary, assumptions, verifyFocus, decisions}`));
return parts.join("\n");
}
function buildPlanPrompt(state) {
const parts = [];
parts.push(`# Plan Phase`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
if (state.clarifyOutput) {
parts.push(`## Clarify summary`);
parts.push(state.clarifyOutput.summary);
parts.push(``);
if (state.clarifyOutput.assumptions.length > 0) {
parts.push(`## Assumptions`);
for (const a of state.clarifyOutput.assumptions) {
parts.push(`- ${a}`);
}
parts.push(``);
}
}
const adjustments = state.decisionHistory.filter((d) => d.decisionId === "plan-adjustment");
if (adjustments.length > 0) {
parts.push(`## Plan adjustment feedback`);
for (const a of adjustments) {
parts.push(`- ${a.customInput}`);
}
parts.push(``);
}
parts.push(`## Your task`);
parts.push(`Design an approach and break it into concrete, ordered tasks.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review the clarify output and assumptions. Understand what needs to happen.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Identify files, modules, and dependencies involved. Check feasibility.`);
parts.push(``);
parts.push(`## Step 3: Design`);
parts.push(`Break the work into bounded tasks. Each task should:`);
parts.push(`- Have a clear, narrow scope (specific files/modules)`);
parts.push(`- Be independently verifiable`);
parts.push(`- List what context it needs from earlier tasks`);
parts.push(`- Declare dependencies on other tasks`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{approach, risks, tasks: [{id, title, scope, context, dependsOn}]}`));
return parts.join("\n");
}
function buildExecutePrompt(state, task) {
const parts = [];
parts.push(`# Execute Phase \u2014 Task: ${task.title}`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`User request: "${state.description}"`);
parts.push(``);
parts.push(`## Task scope`);
parts.push(`Files/modules to modify: ${task.scope.join(", ") || "as needed"}`);
parts.push(``);
parts.push(`## Task context`);
const ctx = Array.isArray(task.context) ? task.context : task.context ? [task.context] : [];
parts.push(`Files/data to read: ${ctx.join(", ") || "as needed"}`);
parts.push(``);
if (state.clarifyOutput) {
parts.push(`## Clarify summary`);
parts.push(state.clarifyOutput.summary);
parts.push(``);
}
if (state.planOutput) {
parts.push(`## Approach`);
parts.push(state.planOutput.approach);
parts.push(``);
}
if (task.verifyOutput && !task.verifyOutput.passed) {
parts.push(`## Previous verification feedback`);
parts.push(task.verifyOutput.summary);
if (task.verifyOutput.retryHint) {
parts.push(`Retry hint: ${task.verifyOutput.retryHint}`);
}
for (const issue of task.verifyOutput.issues) {
parts.push(`- [${issue.severity}] ${issue.description}${issue.suggestion ? ` \u2192 ${issue.suggestion}` : ""}`);
}
parts.push(``);
}
parts.push(`## Your task`);
parts.push(`Implement the changes described in this task.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review what this task must produce. Check the scope and constraints.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Read the target files. Understand the current state before making changes.`);
parts.push(``);
parts.push(`## Step 3: Implement`);
parts.push(`Make the changes. Stay within scope.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{summary, changedFiles, notes}`));
return parts.join("\n");
}
function buildVerifyPrompt(state, task) {
const parts = [];
parts.push(`# Verify Phase \u2014 Task: ${task.title}`);
parts.push(``);
parts.push(`## Goal`);
parts.push(`Determine if the task was completed correctly.`);
parts.push(``);
if (task.executeOutput) {
parts.push(`## What was done`);
parts.push(task.executeOutput.summary);
parts.push(``);
parts.push(`## Changed files`);
for (const f of task.executeOutput.changedFiles) {
parts.push(`- ${f}`);
}
parts.push(``);
if (task.executeOutput.notes.length > 0) {
parts.push(`## Notes from executor`);
for (const n of task.executeOutput.notes) {
parts.push(`- ${n}`);
}
parts.push(``);
}
}
if (state.clarifyOutput?.verifyFocus && state.clarifyOutput.verifyFocus.length > 0) {
parts.push(`## Verify focus`);
for (const f of state.clarifyOutput.verifyFocus) {
parts.push(`- ${f}`);
}
parts.push(``);
}
parts.push(`## Attempt ${task.verifyAttempts}`);
parts.push(``);
parts.push(`## Your task`);
parts.push(`Try to disprove the claimed result. Report what you find.`);
parts.push(``);
parts.push(`## Step 1: Understand`);
parts.push(`Review what was claimed to be done.`);
parts.push(``);
parts.push(`## Step 2: Assess`);
parts.push(`Read the changed files. Run relevant checks or tests if applicable.`);
parts.push(``);
parts.push(`## Step 3: Validate`);
parts.push(`Check each claim against evidence. Look for missing pieces, bugs, or regressions.`);
parts.push(``);
parts.push(`## Step 4: Report`);
parts.push(workItemsOutputFormat(`{passed, issues: [{severity, description, suggestion?}], summary, retryHint?}`));
return parts.join("\n");
}
// src/instructions.ts
var _prefix;
function cliPrefix() {
if (_prefix) return _prefix;
const arg1 = process.argv[1] ?? "";
if (arg1.includes("cli.ts") || arg1.includes("cli.js")) {
_prefix = `node ${arg1}`;
} else {
_prefix = "krow";
}
return _prefix;
}
function submitWorkItemsCommand(workflowId, phase, taskId) {
const taskArg = taskId ? ` --task ${taskId}` : "";
return `${cliPrefix()} submit ${workflowId} ${phase}${taskArg} '<JSON>'`;
}
function submitWorkItemResultCommand(workflowId, itemId) {
return `${cliPrefix()} submit-item ${workflowId} ${itemId} '<RESULT>'`;
}
function submitSynthesizedCommand(workflowId) {
return `${cliPrefix()} submit-synthesized ${workflowId} '<JSON>'`;
}
function decideCommand(workflowId) {
return `${cliPrefix()} decide ${workflowId} '<JSON>'`;
}
function approvePlanCommand(workflowId) {
return `${cliPrefix()} approve-plan ${workflowId}`;
}
function adjustPlanCommand(workflowId) {
return `${cliPrefix()} adjust-plan ${workflowId} '<feedback>'`;
}
function resolveVerifyCommand(workflowId, taskId, action) {
return `${cliPrefix()} resolve-verify ${workflowId} ${taskId} ${action}`;
}
function phaseInstructions(phase, onComplete) {
return [
`Execute the prompt for the "${phase}" phase.`,
`Return your output as a JSON array of work items: [{id, role, task, result?}, ...]`,
`If you can do it all yourself, return 1 item with result included.`,
`If complex, return multiple items (without result) \u2014 they'll be executed in parallel.`,
`Then run: ${onComplete}`
].join("\n");
}
function gateClarifyInstructions(onComplete) {
return [
`Present each decision to the user and collect their answers.`,
`Then run: ${onComplete}`,
`Replace '<JSON>' with an array of { decisionId, selectedOptionId, customInput? }.`
].join("\n");
}
function gatePlanInstructions(approveCmd, adjustCmd) {
return [
`Present the plan to the user for review.`,
`If approved, run: ${approveCmd}`,
`If adjustments needed, run: ${adjustCmd}`
].join("\n");
}
function gateVerifyInstructions(taskTitle, attempts, continueCmd, stopCmd) {
return [
`Task "${taskTitle}" has failed verification ${attempts} time(s).`,
`Present the issues to the user.`,
`Keep trying: ${continueCmd}`,
`Stop this task: ${stopCmd}`
].join("\n");
}
function batchInstructions(count) {
return [
`${count} items to execute in parallel.`,
`Execute each independently. Submit each result separately using its onComplete command.`
].join("\n");
}
function synthesizeInstructions(phase, onComplete) {
return [
`All work items completed. Synthesize the results into a single ${phase} output.`,
`Then run: ${onComplete}`
].join("\n");
}
function doneInstructions() {
return "The workflow has reached a terminal state. Report the result to the user.";
}
function faultInstructions(recoverable) {
if (recoverable) return "A recoverable error occurred. Review the issues and retry.";
return "An unrecoverable error occurred. Report the error to the user.";
}
// src/responses.ts
var PHASE_CAPABILITY = {
clarify: "read",
plan: "read",
execute: "write",
verify: "read"
};
function clarifyContext() {
return { isolation: "isolated", inputs: [] };
}
function planContext(state) {
const inputs = [];
if (state.clarifyOutput) inputs.push({ type: "data", ref: "clarifyOutput" });
return { isolation: "isolated", inputs };
}
function executeContext(state, task) {
const inputs = [];
if (state.clarifyOutput) inputs.push({ type: "data", ref: "clarifyOutput" });
if (state.planOutput) inputs.push({ type: "data", ref: "planOutput.approach" });
const ctx = Array.isArray(task.context) ? task.context : task.context ? [task.context] : [];
for (const c of ctx) inputs.push({ type: "file", ref: c });
return { isolation: "isolated", inputs };
}
function verifyContext(state, task) {
const inputs = [];
if (task.executeOutput) {
inputs.push({ type: "data", ref: `tasks.${task.id}.executeOutput` });
for (const f of task.executeOutput.changedFiles) inputs.push({ type: "file", ref: f });
}
if (state.clarifyOutput?.verifyFocus) inputs.push({ type: "data", ref: "clarifyOutput.verifyFocus" });
return { isolation: "isolated", inputs };
}
function outputSpec(phase) {
return { schema: `${phase}-output`, format: phase === "execute" ? "files" : "json" };
}
function buildPromptForTask(state, phase, task) {
switch (phase) {
case "execute":
return buildExecutePrompt(state, task);
case "verify":
return buildVerifyPrompt(state, task);
default:
return "";
}
}
function contextForTask(state, phase, task) {
switch (phase) {
case "execute":
return executeContext(state, task);
case "verify":
return verifyContext(state, task);
default:
return { isolation: "isolated", inputs: [] };
}
}
function buildPhaseResponse(state, phase, taskId) {
if (phase === "clarify" || phase === "plan") {
const onComplete2 = submitWorkItemsCommand(state.workflowId, phase);
return {
type: "phase",
phase,
workflowId: state.workflowId,
prompt: phase === "clarify" ? buildClarifyPrompt(state) : buildPlanPrompt(state),
capability: PHASE_CAPABILITY[phase],
context: phase === "clarify" ? clarifyContext() : planContext(state),
output: outputSpec(phase),
onComplete: onComplete2,
instructions: phaseInstructions(phase, onComplete2)
};
}
const task = taskId ? state.tasks.find((t) => t.id === taskId) : void 0;
const onComplete = submitWorkItemsCommand(state.workflowId, phase, taskId);
if (!task) {
return {
type: "phase",
phase,
workflowId: state.workflowId,
prompt: `Error: no task found for ${phase} phase.`,
capability: PHASE_CAPABILITY[phase],
context: { isolation: "isolated", inputs: [] },
output: outputSpec(phase),
onComplete,
instructions: phaseInstructions(phase, onComplete)
};
}
return {
type: "phase",
phase,
workflowId: state.workflowId,
taskId: task.id,
prompt: buildPromptForTask(state, phase, task),
capability: PHASE_CAPABILITY[phase],
context: contextForTask(state, phase, task),
output: outputSpec(phase),
onComplete,
instructions: phaseInstructions(phase, onComplete)
};
}
function buildBatchResponse(state, items) {
const responses = items.map((item) => buildPhaseResponse(state, item.phase, item.taskId));
return {
type: "batch",
workflowId: state.workflowId,
responses,
instructions: batchInstructions(responses.length)
};
}
function buildWorkItemBatchResponse(state, work) {
const running = work.items.filter((i) => i.status === "running" || i.status === "pending");
const responses = running.map((item) => {
const onComplete = submitWorkItemResultCommand(state.workflowId, item.id);
return {
type: "phase",
phase: work.phase,
workflowId: state.workflowId,
taskId: work.taskId,
prompt: `# Work Item: ${item.role}
${item.task}`,
capability: PHASE_CAPABILITY[work.phase],
context: { isolation: "isolated", inputs: [] },
output: { schema: "work-item-result", format: "json" },
onComplete,
instructions: `Execute this work item. Return your result. Then run: ${onComplete}`
};
});
return {
type: "batch",
workflowId: state.workflowId,
responses,
instructions: batchInstructions(responses.length)
};
}
function buildSynthesizeResponse(state, work) {
const results = work.items.map((i) => `### ${i.role}
${i.result ?? "(no result)"}`).join("\n\n");
const onComplete = submitSynthesizedCommand(state.workflowId);
return {
type: "phase",
phase: work.phase,
workflowId: state.workflowId,
taskId: work.taskId,
prompt: `# Synthesize Results
${work.synthesize}
## Work Item Results
${results}`,
capability: PHASE_CAPABILITY[work.phase],
context: { isolation: "shared", inputs: [] },
output: outputSpec(work.phase),
onComplete,
instructions: synthesizeInstructions(work.phase, onComplete)
};
}
function buildGateResponse(state, gate, taskId) {
if (gate === "clarify") {
const onComplete = decideCommand(state.workflowId);
return { type: "gate", gate: "clarify", workflowId: state.workflowId, decisions: state.pendingDecisions, onComplete, instructions: gateClarifyInstructions(onComplete) };
}
if (gate === "plan") {
const approveCmd = approvePlanCommand(state.workflowId);
const adjustCmd = adjustPlanCommand(state.workflowId);
return { type: "gate", gate: "plan", workflowId: state.workflowId, tasks: state.tasks, approach: state.planOutput?.approach, risks: state.planOutput?.risks, onComplete: approveCmd, instructions: gatePlanInstructions(approveCmd, adjustCmd) };
}
const task = taskId ? state.tasks.find((t) => t.id === taskId) : void 0;
const continueCmd = resolveVerifyCommand(state.workflowId, taskId ?? "", "continue");
const stopCmd = resolveVerifyCommand(state.workflowId, taskId ?? "", "stop");
return { type: "gate", gate: "verify", workflowId: state.workflowId, taskId, verifyAttempts: task?.verifyAttempts, lastIssues: task?.verifyOutput?.issues, onComplete: continueCmd, instructions: gateVerifyInstructions(task?.title ?? taskId ?? "", task?.verifyAttempts ?? 0, continueCmd, stopCmd) };
}
function buildDoneResponse(state) {
let message;
if (state.status === "completed") message = "Workflow completed successfully.";
else if (state.status === "blocked") message = `Workflow blocked: ${state.blockedReason || "unknown reason"}`;
else message = `Workflow stopped: ${state.blockedReason || "stopped by user"}`;
return { type: "done", workflowId: state.workflowId, status: state.status, message, instructions: doneInstructions() };
}
function buildFaultResponse(state, error, issues, recoverable) {
return { type: "fault", workflowId: state.workflowId, error, issues, recoverable, instructions: faultInstructions(recoverable) };
}
// src/orchestrator.ts
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function now() {
return (/* @__PURE__ */ new Date()).toISOString();
}
function makeId() {
return `wf-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
function setPhase(state, status, phase) {
state.status = status;
state.phase = phase;
state.updatedAt = now();
}
function findTask(state, taskId) {
return state.tasks.find((t) => t.id === taskId);
}
function readyTasks(state) {
const doneIds = new Set(state.tasks.filter((t) => t.status === "done").map((t) => t.id));
return state.tasks.filter(
(t) => t.status === "pending" && t.dependsOn.every((dep) => doneIds.has(dep))
);
}
function allTasksDone(state) {
return state.tasks.length > 0 && state.tasks.every((t) => t.status === "done");
}
function createWorkflow(description) {
const timestamp = now();
const state = {
schemaVersion: "1.0.0",
workflowId: makeId(),
description,
status: "phase_clarify",
phase: "clarify",
pendingDecisions: [],
decisionHistory: [],
tasks: [],
escalateAfter: 3,
createdAt: timestamp,
updatedAt: timestamp
};
const v = validateWorkflowState(state);
if (!v.ok) {
return { state, response: buildFaultResponse(state, "invalid initial state", v.issues, false) };
}
return { state, response: nextResponse(state) };
}
function nextResponse(state) {
switch (state.status) {
case "phase_clarify":
return buildPhaseResponse(state, "clarify");
case "gate_clarify":
return buildGateResponse(state, "clarify");
case "phase_plan":
return buildPhaseResponse(state, "plan");
case "gate_plan":
return buildGateResponse(state, "plan");
case "phase_execute":
case "phase_verify":
return buildNextTaskResponses(state);
case "phase_synthesize":
return buildSynthesizeFromState(state);
case "gate_verify":
return buildVerifyGate(state);
case "completed":
case "blocked":
case "stopped":
return buildDoneResponse(state);
default:
return buildFaultResponse(state, `unknown status: ${state.status}`, [], false);
}
}
function buildNextTaskResponses(state) {
const actionable = [];
for (const task of state.tasks) {
if (task.status === "executing") actionable.push({ task, phase: "execute" });
else if (task.status === "verifying") actionable.push({ task, phase: "verify" });
}
const ready = readyTasks(state);
for (const task of ready) {
task.status = "executing";
actionable.push({ task, phase: "execute" });
}
if (actionable.length === 0) {
if (allTasksDone(state)) {
state.status = "completed";
state.updatedAt = now();
return buildDoneResponse(state);
}
return buildFaultResponse(state, "no actionable tasks", [], false);
}
if (actionable.length === 1) {
return buildPhaseResponse(state, actionable[0].phase, actionable[0].task.id);
}
return buildBatchResponse(state, actionable.map((a) => ({ phase: a.phase, taskId: a.task.id })));
}
function buildVerifyGate(state) {
const stuck = state.tasks.find((t) => t.status === "verifying" && t.verifyAttempts >= state.escalateAfter);
if (!stuck) return buildFaultResponse(state, "gate_verify but no stuck task found", [], false);
return buildGateResponse(state, "verify", stuck.id);
}
function buildSynthesizeFromState(state) {
if (!state.activeWork) return buildFaultResponse(state, "phase_synthesize but no active work", [], false);
return buildSynthesizeResponse(state, state.activeWork);
}
function submitWorkItems(state, phase, input, taskId) {
const v = validateWorkItems(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid work items", v.issues, true) };
}
const items = v.value;
if (items.length === 1 && allItemsHaveResults(items)) {
return submitPhaseOutput(state, phase, items[0].result, taskId);
}
const next = clone(state);
next.activeWork = {
phase,
taskId,
synthesize: `Combine the results from ${items.length} work items into a single ${phase} output.`,
items: items.map((item) => ({
id: item.id,
role: item.role,
task: item.task,
status: item.result !== void 0 ? "done" : "pending",
result: item.result !== void 0 ? JSON.stringify(item.result) : void 0
}))
};
if (next.activeWork.items.every((i) => i.status === "done")) {
next.status = "phase_synthesize";
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
for (const item of next.activeWork.items) {
if (item.status === "pending") item.status = "running";
}
next.status = "phase_synthesize";
next.updatedAt = now();
return { state: next, response: buildWorkItemBatchResponse(next, next.activeWork) };
}
function submitWorkItemResult(state, itemId, result) {
if (!state.activeWork) {
return { response: buildFaultResponse(state, "no active work items", [], true) };
}
const next = clone(state);
const item = next.activeWork.items.find((i) => i.id === itemId);
if (!item) {
return { response: buildFaultResponse(next, `work item ${itemId} not found`, [], true) };
}
item.status = "done";
item.result = result;
next.updatedAt = now();
if (next.activeWork.items.every((i) => i.status === "done")) {
next.status = "phase_synthesize";
return { state: next, response: nextResponse(next) };
}
return { state: next, response: buildWorkItemBatchResponse(next, next.activeWork) };
}
function submitSynthesizedResult(state, input) {
if (!state.activeWork) {
return { response: buildFaultResponse(state, "no active work to synthesize", [], true) };
}
const work = state.activeWork;
const next = clone(state);
next.activeWork = void 0;
const phaseStatus = `phase_${work.phase}`;
next.status = phaseStatus;
next.phase = work.phase;
return submitPhaseOutput(next, work.phase, input, work.taskId);
}
function submitPhaseOutput(state, phase, input, taskId) {
switch (phase) {
case "clarify":
return applyClarify(state, input);
case "plan":
return applyPlan(state, input);
case "execute":
return applyExecute(state, input, taskId);
case "verify":
return applyVerify(state, input, taskId);
default:
return { response: buildFaultResponse(state, `unsupported phase: ${phase}`, [], false) };
}
}
function submitDecisions(state, input) {
if (state.status !== "gate_clarify") {
return { response: buildFaultResponse(state, "decisions only valid during gate_clarify", [], true) };
}
const v = validateDecisionAnswers(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid decision answers", v.issues, true) };
}
const next = clone(state);
next.decisionHistory.push(...v.value);
next.pendingDecisions = [];
setPhase(next, "phase_clarify", "clarify");
return { state: next, response: nextResponse(next) };
}
function approvePlan(state) {
if (state.status !== "gate_plan") {
return { response: buildFaultResponse(state, "approve-plan only valid during gate_plan", [], true) };
}
const next = clone(state);
if (next.tasks.length === 0) {
return { response: buildFaultResponse(next, "no tasks to execute", [], true) };
}
const ready = readyTasks(next);
for (const task of ready) task.status = "executing";
setPhase(next, "phase_execute", "execute");
return { state: next, response: nextResponse(next) };
}
function adjustPlan(state, feedback) {
if (state.status !== "gate_plan") {
return { response: buildFaultResponse(state, "adjust-plan only valid during gate_plan", [], true) };
}
const next = clone(state);
next.planOutput = void 0;
next.tasks = [];
next.decisionHistory.push({
decisionId: "plan-adjustment",
selectedOptionId: "feedback",
customInput: feedback
});
setPhase(next, "phase_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function resolveVerifyGate(state, taskId, action) {
if (state.status !== "gate_verify") {
return { response: buildFaultResponse(state, "resolve-verify only valid during gate_verify", [], true) };
}
const next = clone(state);
const task = findTask(next, taskId);
if (!task) {
return { response: buildFaultResponse(next, `task ${taskId} not found`, [], true) };
}
if (action === "stop") {
task.status = "failed";
next.status = "blocked";
next.blockedReason = `User stopped task ${taskId} after ${task.verifyAttempts} verify attempts`;
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
task.status = "executing";
setPhase(next, "phase_execute", "execute");
return { state: next, response: nextResponse(next) };
}
function stopWorkflow(state, reason = "stopped by user") {
const next = clone(state);
next.status = "stopped";
next.blockedReason = reason;
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
}
function applyClarify(state, input) {
const v = validateClarifyOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid clarify output", v.issues, true) };
}
const next = clone(state);
next.clarifyOutput = v.value;
if (!v.value.ready) {
next.pendingDecisions = v.value.decisions;
setPhase(next, "gate_clarify", "clarify");
return { state: next, response: nextResponse(next) };
}
next.pendingDecisions = [];
setPhase(next, "phase_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function applyPlan(state, input) {
const v = validatePlanOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid plan output", v.issues, true) };
}
const next = clone(state);
next.planOutput = v.value;
next.tasks = v.value.tasks.map((td) => ({
id: td.id,
title: td.title,
scope: Array.isArray(td.scope) ? td.scope : td.scope ? [td.scope] : [],
context: Array.isArray(td.context) ? td.context : td.context ? [String(td.context)] : [],
dependsOn: Array.isArray(td.dependsOn) ? td.dependsOn : td.dependsOn ? [td.dependsOn] : [],
status: "pending",
verifyAttempts: 0
}));
setPhase(next, "gate_plan", "plan");
return { state: next, response: nextResponse(next) };
}
function applyExecute(state, input, taskId) {
const v = validateExecuteOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid execute output", v.issues, true) };
}
const next = clone(state);
const resolvedId = taskId ?? next.tasks.find((t) => t.status === "executing")?.id;
const task = resolvedId ? findTask(next, resolvedId) : void 0;
if (!task) {
return { response: buildFaultResponse(next, "no executing task", [], true) };
}
task.executeOutput = v.value;
task.status = "verifying";
task.verifyAttempts += 1;
updateWorkflowPhase(next);
return { state: next, response: nextResponse(next) };
}
function applyVerify(state, input, taskId) {
const v = validateVerifyOutput(input);
if (!v.ok || !v.value) {
return { response: buildFaultResponse(state, "invalid verify output", v.issues, true) };
}
const next = clone(state);
const resolvedId = taskId ?? next.tasks.find((t) => t.status === "verifying")?.id;
const task = resolvedId ? findTask(next, resolvedId) : void 0;
if (!task) {
return { response: buildFaultResponse(next, "no verifying task", [], true) };
}
task.verifyOutput = v.value;
if (v.value.passed) {
task.status = "done";
} else if (task.verifyAttempts >= next.escalateAfter) {
next.status = "gate_verify";
next.phase = "verify";
next.updatedAt = now();
return { state: next, response: nextResponse(next) };
} else {
task.status = "executing";
}
updateWorkflowPhase(next);
return { state: next, response: nextResponse(next) };
}
function updateWorkflowPhase(state) {
if (allTasksDone(state)) {
state.status = "completed";
state.updatedAt = now();
return;
}
const hasExecuting = state.tasks.some((t) => t.status === "executing");
const hasVerifying = state.tasks.some((t) => t.status === "verifying");
const hasReady = readyTasks(state).length > 0;
if (hasExecuting || hasReady) {
setPhase(state, "phase_execute", "execute");
} else if (hasVerifying) {
setPhase(state, "phase_verify", "verify");
}
}
// src/state-store.ts
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
import { join, dirname } from "path";
var STATE_DIR = ".krow/state";
function statePath(workflowId, rootDir = process.cwd()) {
return join(rootDir, STATE_DIR, `${workflowId}.json`);
}
function saveState(state, rootDir = process.cwd()) {
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
const p = statePath(state.workflowId, rootDir);
const dir = dirname(p);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
writeFileSync(p, JSON.stringify(state, null, 2));
}
function loadState(workflowId, rootDir = process.cwd()) {
const p = statePath(workflowId, rootDir);
if (!existsSync(p)) return null;
return JSON.parse(readFileSync(p, "utf-8"));
}
function listStates(rootDir = process.cwd()) {
const dir = join(rootDir, STATE_DIR);
if (!existsSync(dir)) return [];
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
try {
return JSON.parse(readFileSync(join(dir, f), "utf-8"));
} catch {
return null;
}
}).filter((s) => s !== null).filter((s) => s.status !== "completed" && s.status !== "stopped").sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
}
export {
validateWorkItems,
allItemsHaveResults,
validateClarifyOutput,
validatePlanOutput,
validateExecuteOutput,
validateVerifyOutput,
validateDecisionAnswers,
validateWorkflowState,
buildClarifyPrompt,
buildPlanPrompt,
buildExecutePrompt,
buildVerifyPrompt,
buildPhaseResponse,
buildBatchResponse,
buildWorkItemBatchResponse,
buildSynthesizeResponse,
buildGateResponse,
buildDoneResponse,
buildFaultResponse,
createWorkflow,
nextResponse,
submitWorkItems,
submitWorkItemResult,
submitSynthesizedResult,
submitPhaseOutput,
submitDecisions,
approvePlan,
adjustPlan,
resolveVerifyGate,
stopWorkflow,
statePath,
saveState,
loadState,
listStates
};