704cacfd8d
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
187 lines
4.4 KiB
JavaScript
187 lines
4.4 KiB
JavaScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
import { repoRoot } from "./core.mjs";
|
|
|
|
const orchExecutableName = process.platform === "win32" ? "orch.exe" : "orch";
|
|
|
|
export const defaultDbPath = path.join(repoRoot, ".artifacts", "orch", "coord.db");
|
|
export const defaultWorkspaceRoot = path.join(repoRoot, ".artifacts", "orch", "worktrees");
|
|
export const defaultTaskBodyRoot = path.join(repoRoot, ".artifacts", "orch", "task-bodies");
|
|
export const legacyOrchBinPath = path.join(
|
|
os.homedir(),
|
|
".codex",
|
|
"skills",
|
|
"orch",
|
|
"assets",
|
|
orchExecutableName
|
|
);
|
|
|
|
function searchPathForExecutable(pathValue, existsSync = fs.existsSync) {
|
|
if (!pathValue) {
|
|
return null;
|
|
}
|
|
|
|
for (const entry of pathValue.split(path.delimiter)) {
|
|
if (!entry) {
|
|
continue;
|
|
}
|
|
|
|
const candidate = path.join(entry, orchExecutableName);
|
|
|
|
if (existsSync(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function resolveOrchBinary({ env = process.env, existsSync = fs.existsSync } = {}) {
|
|
const checkedPaths = [];
|
|
const explicitPath = env.CADENCE_UI_ORCH_BIN ? path.resolve(env.CADENCE_UI_ORCH_BIN) : null;
|
|
|
|
if (explicitPath) {
|
|
checkedPaths.push(explicitPath);
|
|
|
|
if (existsSync(explicitPath)) {
|
|
return {
|
|
binaryPath: explicitPath,
|
|
checkedPaths,
|
|
source: "CADENCE_UI_ORCH_BIN"
|
|
};
|
|
}
|
|
}
|
|
|
|
const pathMatch = searchPathForExecutable(env.PATH ?? "", existsSync);
|
|
|
|
if (pathMatch) {
|
|
checkedPaths.push(pathMatch);
|
|
return {
|
|
binaryPath: pathMatch,
|
|
checkedPaths,
|
|
source: "PATH"
|
|
};
|
|
}
|
|
|
|
checkedPaths.push(legacyOrchBinPath);
|
|
|
|
if (existsSync(legacyOrchBinPath)) {
|
|
return {
|
|
binaryPath: legacyOrchBinPath,
|
|
checkedPaths,
|
|
source: "legacy"
|
|
};
|
|
}
|
|
|
|
return {
|
|
binaryPath: null,
|
|
checkedPaths,
|
|
source: null
|
|
};
|
|
}
|
|
|
|
function parseTaskToken(token) {
|
|
const [taskIdPart, dependencyPart] = token.split("->").map((value) => value.trim());
|
|
|
|
return {
|
|
dependsOn: dependencyPart
|
|
? dependencyPart
|
|
.split(",")
|
|
.map((value) => value.trim())
|
|
.filter(Boolean)
|
|
: [],
|
|
id: taskIdPart
|
|
};
|
|
}
|
|
|
|
export function parsePlanTaskSketch(markdown) {
|
|
const lines = markdown.split(/\r?\n/);
|
|
const planTitle =
|
|
lines
|
|
.find((line) => line.startsWith("# "))
|
|
?.slice(2)
|
|
.trim() ?? "Execution Plan";
|
|
const tasks = [];
|
|
let inTaskSketch = false;
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith("## ")) {
|
|
if (inTaskSketch) {
|
|
break;
|
|
}
|
|
|
|
inTaskSketch = line.trim() === "## Orchestration Task Sketch";
|
|
continue;
|
|
}
|
|
|
|
if (!inTaskSketch) {
|
|
continue;
|
|
}
|
|
|
|
const match = line.match(/^\s*-\s+(?:`([^`]+)`|([^:]+)):\s+(.+)$/);
|
|
|
|
if (!match) {
|
|
continue;
|
|
}
|
|
|
|
const taskToken = (match[1] ?? match[2]).trim();
|
|
const taskTitle = match[3].trim();
|
|
const task = parseTaskToken(taskToken);
|
|
|
|
tasks.push({
|
|
...task,
|
|
title: taskTitle
|
|
});
|
|
}
|
|
|
|
return {
|
|
planTitle,
|
|
tasks
|
|
};
|
|
}
|
|
|
|
export function readPlanFile(planFile) {
|
|
const absolutePath = path.isAbsolute(planFile) ? planFile : path.join(repoRoot, planFile);
|
|
const markdown = fs.readFileSync(absolutePath, "utf8");
|
|
const parsed = parsePlanTaskSketch(markdown);
|
|
|
|
return {
|
|
...parsed,
|
|
absolutePath,
|
|
relativePath: path.relative(repoRoot, absolutePath)
|
|
};
|
|
}
|
|
|
|
export function renderTaskBody({ plan, task }) {
|
|
return `# ${task.id} — ${task.title}
|
|
|
|
- Source plan: \`${plan.relativePath}\`
|
|
- Plan title: ${plan.planTitle}
|
|
- Task id: ${task.id}
|
|
- Dependencies: ${task.dependsOn.length > 0 ? task.dependsOn.join(", ") : "none"}
|
|
|
|
## Execution Notes
|
|
|
|
- Read the source execution plan before editing.
|
|
- Keep this task scoped to the work implied by the task title and dependencies.
|
|
- Run the narrowest useful harness suites first, then report what broader validation remains.
|
|
- Return any shared integration follow-up instead of editing unrelated surfaces opportunistically.
|
|
`;
|
|
}
|
|
|
|
function sanitizeSegment(value) {
|
|
return value.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
}
|
|
|
|
export function writeTaskBodyFile({ bodyRoot = defaultTaskBodyRoot, plan, runId, task }) {
|
|
const runDir = path.join(bodyRoot, sanitizeSegment(runId));
|
|
const outputPath = path.join(runDir, `${sanitizeSegment(task.id)}.md`);
|
|
|
|
fs.mkdirSync(runDir, { recursive: true });
|
|
fs.writeFileSync(outputPath, `${renderTaskBody({ plan, task })}\n`, "utf8");
|
|
|
|
return outputPath;
|
|
}
|