paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
188 lines (184 loc) • 5.84 kB
JavaScript
import { createRequire as __paqadCreateRequire } from 'module'; const require = __paqadCreateRequire(import.meta.url);
import {
STAGE_EVIDENCE_DOC_TYPE
} from "./chunk-7TBVBCDH.js";
import {
sessionLedgerDir
} from "./chunk-Q353EDKY.js";
import {
init_esm_shims
} from "./chunk-IDFRWBMN.js";
// src/pipeline/routed-workflow.ts
init_esm_shims();
var ROUTED_WORKFLOWS = [
"feature-development",
"project-question",
"documentation-update",
"module-documentation",
"pentest",
"design-test",
"codebase-health",
"site-map",
"rules-analyze",
"root-cause-analysis",
"discovery",
"rules-discovery",
"no-workflow"
];
function isFeatureDevelopmentRoute(routed) {
return routed === "feature-development";
}
var OUTCOME_BY_WORKFLOW = {
"feature-development": "feature-development",
"bug-fix": "feature-development",
refactor: "feature-development",
migration: "feature-development",
cleanup: "feature-development",
"architecture-change": "feature-development",
"test-improvement": "feature-development",
"schema-change": "feature-development",
"query-optimization": "feature-development",
"project-question": "project-question",
investigation: "project-question",
"ticket-refinement": "project-question",
"documentation-update": "documentation-update",
"module-documentation": "module-documentation",
pentest: "pentest",
"pentest-retest": "pentest",
"codebase-health": "codebase-health",
"health-retest": "codebase-health",
"site-map": "site-map",
"site-map-retest": "site-map",
"design-test": "design-test",
"design-retest": "design-test",
"rules-analyze": "rules-analyze",
"rules-generate": "rules-analyze",
"root-cause-analysis": "root-cause-analysis",
discovery: "discovery",
"rules-discovery": "rules-discovery",
writing: "no-workflow",
editing: "no-workflow",
planning: "no-workflow",
research: "no-workflow",
"content-update": "no-workflow",
custom: "no-workflow"
};
function resolveRoutedWorkflow(workflow) {
if (!workflow) {
return "no-workflow";
}
return OUTCOME_BY_WORKFLOW[workflow];
}
function changeKindForWorkflow(workflow) {
return workflow === "bug-fix" ? "fix" : "other";
}
// src/pipeline/workflow-state.ts
init_esm_shims();
import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
var WORKFLOW_STATE_FILE = ".workflow-state.json";
var EMPTY_STATE = { active: null, paused: [] };
function workflowStatePath(projectRoot, sessionId) {
return join(
projectRoot,
sessionLedgerDir(STAGE_EVIDENCE_DOC_TYPE, sessionId),
WORKFLOW_STATE_FILE
);
}
var VALID_WORKFLOWS = new Set(ROUTED_WORKFLOWS);
function isRoutedWorkflow(value) {
return typeof value === "string" && VALID_WORKFLOWS.has(value);
}
function toEntry(value) {
if (typeof value !== "object" || value === null) {
return null;
}
const record = value;
if (!isRoutedWorkflow(record.workflow)) {
return null;
}
const entry = { workflow: record.workflow };
if (typeof record.changeKey === "string") {
entry.changeKey = record.changeKey;
}
if (record.lane === "fast" || record.lane === "graduated" || record.lane === "full") {
entry.lane = record.lane;
}
if (typeof record.specId === "string") {
entry.specId = record.specId;
}
if (typeof record.discoveryRunId === "string") {
entry.discoveryRunId = record.discoveryRunId;
}
if (typeof record.reason === "string") {
entry.reason = record.reason;
}
return entry;
}
function readWorkflowState(projectRoot, sessionId) {
try {
const parsed = JSON.parse(readFileSync(workflowStatePath(projectRoot, sessionId), "utf8"));
const active = toEntry(parsed.active);
const paused = Array.isArray(parsed.paused) ? parsed.paused.map(toEntry).filter((entry) => entry !== null) : [];
const state = { active, paused };
if (typeof parsed.turn_started_at === "string") {
state.turn_started_at = parsed.turn_started_at;
}
return state;
} catch {
return { ...EMPTY_STATE, paused: [] };
}
}
function writeWorkflowState(projectRoot, sessionId, state) {
const path = workflowStatePath(projectRoot, sessionId);
mkdirSync(join(projectRoot, sessionLedgerDir(STAGE_EVIDENCE_DOC_TYPE, sessionId)), {
recursive: true
});
writeFileSync(path, JSON.stringify(state), "utf8");
}
function routeWorkflow(state, next, anchors = {}) {
if (state.active && state.active.workflow === next) {
const merged = { ...state.active, ...stripUndefined(anchors) };
return { state: { active: merged, paused: state.paused }, resumed: null, switched: false };
}
const pausedIndex = state.paused.findIndex((entry) => entry.workflow === next);
const carriedForward = state.active ? [...state.paused.filter((_, index) => index !== pausedIndex), state.active] : state.paused.filter((_, index) => index !== pausedIndex);
if (pausedIndex >= 0) {
const resumed = state.paused[pausedIndex];
return { state: { active: resumed, paused: carriedForward }, resumed, switched: true };
}
const active = { workflow: next, ...stripUndefined(anchors) };
return {
state: { active, paused: carriedForward },
resumed: null,
switched: Boolean(state.active)
};
}
function stripUndefined(anchors) {
const out = {};
if (anchors.changeKey !== void 0) {
out.changeKey = anchors.changeKey;
}
if (anchors.lane !== void 0) {
out.lane = anchors.lane;
}
if (anchors.specId !== void 0) {
out.specId = anchors.specId;
}
if (anchors.discoveryRunId !== void 0) {
out.discoveryRunId = anchors.discoveryRunId;
}
if (anchors.reason !== void 0) {
out.reason = anchors.reason;
}
return out;
}
export {
ROUTED_WORKFLOWS,
isFeatureDevelopmentRoute,
resolveRoutedWorkflow,
changeKindForWorkflow,
readWorkflowState,
writeWorkflowState,
routeWorkflow
};