704cacfd8d
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
278 lines
7.4 KiB
JavaScript
278 lines
7.4 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
|
|
import { repoRoot } from "./core.mjs";
|
|
import {
|
|
defaultDbPath,
|
|
defaultWorkspaceRoot,
|
|
readPlanFile,
|
|
resolveOrchBinary,
|
|
writeTaskBodyFile
|
|
} from "./orch-support.mjs";
|
|
|
|
function printHelp() {
|
|
process.stdout.write(`Cadence UI orchestration wrapper
|
|
|
|
Usage:
|
|
pnpm harness:orch -- <orch command> [flags]
|
|
pnpm harness:orch -- doctor [--plan-file docs/exec-plans/foo.md]
|
|
pnpm harness:orch -- plan inspect --plan-file docs/exec-plans/foo.md
|
|
pnpm harness:orch -- plan body --run cadence_ui_demo --task T1 --plan-file docs/exec-plans/foo.md
|
|
|
|
Examples:
|
|
pnpm harness:orch -- run init --run cadence_ui_demo --goal "Refine release UX" --summary "Break work into isolated tasks"
|
|
pnpm harness:orch -- task add --run cadence_ui_demo --task T1 --title "Stabilize smoke tests" --summary "Fix Storybook smoke drift"
|
|
pnpm harness:orch -- dispatch --run cadence_ui_demo --task T1 --to default-worker --plan-file docs/exec-plans/task-plan.md
|
|
pnpm harness:orch -- doctor --plan-file docs/exec-plans/task-plan.md
|
|
|
|
Defaults applied by this wrapper:
|
|
--db ${path.relative(repoRoot, defaultDbPath)}
|
|
dispatch --repo-path ${repoRoot}
|
|
dispatch --workspace-root ${path.relative(repoRoot, defaultWorkspaceRoot)}
|
|
dispatch --strict-worktree
|
|
dispatch --base-ref <current branch>
|
|
|
|
Plan-linked helpers:
|
|
doctor Resolve the orch binary, show defaults, and inspect an execution plan.
|
|
plan inspect Print orchestration tasks parsed from a plan's task sketch.
|
|
plan body Generate a task body file from a plan and task id.
|
|
dispatch --plan-file Generate --body-file automatically from the referenced execution plan.
|
|
`);
|
|
}
|
|
|
|
function fail(message) {
|
|
process.stderr.write(`[harness:orch] ${message}\n`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function hasFlag(args, flag) {
|
|
return args.includes(flag);
|
|
}
|
|
|
|
function getFlagValue(args, flag) {
|
|
const index = args.indexOf(flag);
|
|
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
|
|
return args[index + 1] ?? null;
|
|
}
|
|
|
|
function consumeFlagValue(args, flag) {
|
|
const index = args.indexOf(flag);
|
|
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
|
|
const [value] = args.splice(index, 2).slice(1);
|
|
return value ?? null;
|
|
}
|
|
|
|
function getCurrentBranch() {
|
|
try {
|
|
return execFileSync("git", ["branch", "--show-current"], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8"
|
|
}).trim();
|
|
} catch {
|
|
return "main";
|
|
}
|
|
}
|
|
|
|
function printPlanTasks(plan) {
|
|
process.stdout.write(`[harness:orch] Plan: ${plan.relativePath}\n`);
|
|
process.stdout.write(`[harness:orch] Title: ${plan.planTitle}\n`);
|
|
|
|
if (plan.tasks.length === 0) {
|
|
process.stdout.write("[harness:orch] No orchestration task sketch entries found.\n");
|
|
return;
|
|
}
|
|
|
|
process.stdout.write("[harness:orch] Parsed tasks:\n");
|
|
|
|
for (const task of plan.tasks) {
|
|
const dependencySuffix = task.dependsOn.length > 0 ? ` (depends on ${task.dependsOn.join(", ")})` : "";
|
|
process.stdout.write(`- ${task.id}: ${task.title}${dependencySuffix}\n`);
|
|
}
|
|
}
|
|
|
|
function findPlanTask(plan, taskId) {
|
|
return plan.tasks.find((task) => task.id === taskId) ?? null;
|
|
}
|
|
|
|
function maybeHandleDoctor(rawArgs) {
|
|
if (rawArgs[0] !== "doctor") {
|
|
return false;
|
|
}
|
|
|
|
const planFile = getFlagValue(rawArgs, "--plan-file");
|
|
const resolution = resolveOrchBinary();
|
|
|
|
process.stdout.write("[harness:orch] Doctor\n");
|
|
process.stdout.write(`- repo root: ${repoRoot}\n`);
|
|
process.stdout.write(`- default db: ${path.relative(repoRoot, defaultDbPath)}\n`);
|
|
process.stdout.write(`- default workspace root: ${path.relative(repoRoot, defaultWorkspaceRoot)}\n`);
|
|
process.stdout.write(`- current branch: ${getCurrentBranch()}\n`);
|
|
process.stdout.write(
|
|
`- orch binary: ${resolution.binaryPath ? `${resolution.binaryPath} (${resolution.source})` : "not found"}\n`
|
|
);
|
|
|
|
if (!resolution.binaryPath) {
|
|
process.stdout.write("- checked locations:\n");
|
|
|
|
for (const checkedPath of resolution.checkedPaths) {
|
|
process.stdout.write(` - ${checkedPath}\n`);
|
|
}
|
|
|
|
process.stdout.write(
|
|
"[harness:orch] Set CADENCE_UI_ORCH_BIN to an installed orch binary or add `orch` to PATH.\n"
|
|
);
|
|
}
|
|
|
|
if (planFile) {
|
|
const plan = readPlanFile(planFile);
|
|
printPlanTasks(plan);
|
|
}
|
|
|
|
process.exit(resolution.binaryPath ? 0 : 1);
|
|
}
|
|
|
|
function maybeHandlePlanCommand(rawArgs) {
|
|
if (rawArgs[0] !== "plan") {
|
|
return false;
|
|
}
|
|
|
|
const subcommand = rawArgs[1];
|
|
const planFile = getFlagValue(rawArgs, "--plan-file");
|
|
|
|
if (!planFile) {
|
|
fail("Expected --plan-file for plan commands.");
|
|
}
|
|
|
|
const plan = readPlanFile(planFile);
|
|
|
|
if (subcommand === "inspect") {
|
|
printPlanTasks(plan);
|
|
process.exit(0);
|
|
}
|
|
|
|
if (subcommand === "body") {
|
|
const taskId = getFlagValue(rawArgs, "--task");
|
|
const runId = getFlagValue(rawArgs, "--run") ?? "adhoc";
|
|
|
|
if (!taskId) {
|
|
fail("Expected --task when generating a plan-backed task body.");
|
|
}
|
|
|
|
const task = findPlanTask(plan, taskId);
|
|
|
|
if (!task) {
|
|
fail(`Task ${taskId} was not found in ${plan.relativePath}.`);
|
|
}
|
|
|
|
const outputPath = writeTaskBodyFile({
|
|
plan,
|
|
runId,
|
|
task
|
|
});
|
|
|
|
process.stdout.write(
|
|
`[harness:orch] Wrote ${path.relative(repoRoot, outputPath)} from ${plan.relativePath}\n`
|
|
);
|
|
process.exit(0);
|
|
}
|
|
|
|
fail(`Unknown plan subcommand: ${subcommand ?? "<missing>"}`);
|
|
}
|
|
|
|
const rawArgs = process.argv.slice(2).filter((value) => value !== "--");
|
|
|
|
if (
|
|
rawArgs.length === 0 ||
|
|
rawArgs[0] === "help" ||
|
|
rawArgs[0] === "--help" ||
|
|
rawArgs[0] === "-h"
|
|
) {
|
|
printHelp();
|
|
process.exit(0);
|
|
}
|
|
|
|
maybeHandleDoctor(rawArgs);
|
|
maybeHandlePlanCommand(rawArgs);
|
|
|
|
fs.mkdirSync(path.dirname(defaultDbPath), { recursive: true });
|
|
fs.mkdirSync(defaultWorkspaceRoot, { recursive: true });
|
|
|
|
const command = rawArgs[0];
|
|
const orchArgs = [...rawArgs];
|
|
|
|
if (!hasFlag(orchArgs, "--db")) {
|
|
orchArgs.unshift(defaultDbPath);
|
|
orchArgs.unshift("--db");
|
|
}
|
|
|
|
if (command === "dispatch") {
|
|
const planFile = consumeFlagValue(orchArgs, "--plan-file");
|
|
|
|
if (planFile && !hasFlag(orchArgs, "--body-file")) {
|
|
const taskId = getFlagValue(orchArgs, "--task");
|
|
const runId = getFlagValue(orchArgs, "--run") ?? "adhoc";
|
|
|
|
if (!taskId) {
|
|
fail("Expected --task when using dispatch --plan-file.");
|
|
}
|
|
|
|
const plan = readPlanFile(planFile);
|
|
const task = findPlanTask(plan, taskId);
|
|
|
|
if (!task) {
|
|
fail(`Task ${taskId} was not found in ${plan.relativePath}.`);
|
|
}
|
|
|
|
const outputPath = writeTaskBodyFile({
|
|
plan,
|
|
runId,
|
|
task
|
|
});
|
|
|
|
process.stdout.write(
|
|
`[harness:orch] Generated ${path.relative(repoRoot, outputPath)} from ${plan.relativePath}\n`
|
|
);
|
|
orchArgs.push("--body-file", outputPath);
|
|
}
|
|
|
|
if (!hasFlag(orchArgs, "--repo-path")) {
|
|
orchArgs.push("--repo-path", repoRoot);
|
|
}
|
|
|
|
if (!hasFlag(orchArgs, "--workspace-root")) {
|
|
orchArgs.push("--workspace-root", defaultWorkspaceRoot);
|
|
}
|
|
|
|
if (!hasFlag(orchArgs, "--strict-worktree")) {
|
|
orchArgs.push("--strict-worktree");
|
|
}
|
|
|
|
if (!hasFlag(orchArgs, "--base-ref")) {
|
|
orchArgs.push("--base-ref", getCurrentBranch());
|
|
}
|
|
}
|
|
|
|
const resolution = resolveOrchBinary();
|
|
|
|
if (!resolution.binaryPath) {
|
|
fail(
|
|
`Unable to locate orch. Checked ${resolution.checkedPaths.join(", ")}. Set CADENCE_UI_ORCH_BIN or add orch to PATH.`
|
|
);
|
|
}
|
|
|
|
const result = spawnSync(resolution.binaryPath, orchArgs, {
|
|
cwd: repoRoot,
|
|
stdio: "inherit"
|
|
});
|
|
|
|
process.exit(result.status ?? 1);
|