@delete101020/aidlc 4.0.10 → 4.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bundle.js +1013 -9
  2. package/package.json +1 -1
package/dist/bundle.js CHANGED
@@ -20068,6 +20068,24 @@ var require_WorkspaceSchema = __commonJS({
20068
20068
  }
20069
20069
  }
20070
20070
  }
20071
+ const pipelineIds = new Set(config.pipelines.map((p) => p.id));
20072
+ for (const cmd of config.slash_commands ?? []) {
20073
+ const target = cmd;
20074
+ if (typeof target.agent === "string" && !agentIds.has(target.agent)) {
20075
+ issues.push({
20076
+ code: "unknown-command-agent",
20077
+ message: `Slash command "${target.name}" targets agent "${target.agent}" which is not defined in agents:`,
20078
+ path: `slash_commands.${target.name}.agent`
20079
+ });
20080
+ }
20081
+ if (typeof target.pipeline === "string" && !pipelineIds.has(target.pipeline)) {
20082
+ issues.push({
20083
+ code: "unknown-command-pipeline",
20084
+ message: `Slash command "${target.name}" targets pipeline "${target.pipeline}" which is not defined in pipelines:`,
20085
+ path: `slash_commands.${target.name}.pipeline`
20086
+ });
20087
+ }
20088
+ }
20071
20089
  const pipelinesById = new Map(config.pipelines.map((p) => [p.id, p]));
20072
20090
  for (const recipe of config.recipes) {
20073
20091
  const source = recipe.from ? pipelinesById.get(recipe.from) : config.pipelines[0];
@@ -21000,6 +21018,8 @@ var require_PipelineRunner = __commonJS({
21000
21018
  exports2.startRun = startRun2;
21001
21019
  exports2.canStartStep = canStartStep;
21002
21020
  exports2.markStepDone = markStepDone3;
21021
+ exports2.canUndoStepDone = canUndoStepDone;
21022
+ exports2.undoStepDone = undoStepDone;
21003
21023
  exports2.submitAutoReviewVerdict = submitAutoReviewVerdict2;
21004
21024
  exports2.retryAutoReview = retryAutoReview;
21005
21025
  exports2.approveStep = approveStep3;
@@ -21152,6 +21172,101 @@ var require_PipelineRunner = __commonJS({
21152
21172
  }
21153
21173
  return advance(next, idx, pipeline);
21154
21174
  }
21175
+ var UNDOABLE_STATUSES = ["awaiting_auto_review", "awaiting_review", "approved"];
21176
+ function followersOf(pipeline, idx) {
21177
+ const normalized = pipeline.steps.map(WorkspaceSchema_1.normalizeStep);
21178
+ const usesDag = normalized.some((s) => s.depends_on.length > 0);
21179
+ if (!usesDag) {
21180
+ return idx + 1 < normalized.length ? [idx + 1] : [];
21181
+ }
21182
+ const dagId = normalized[idx] ? normalized[idx].name ?? normalized[idx].agent : void 0;
21183
+ if (dagId === void 0) {
21184
+ return [];
21185
+ }
21186
+ const out2 = [];
21187
+ normalized.forEach((s, i) => {
21188
+ if (s.depends_on.includes(dagId)) {
21189
+ out2.push(i);
21190
+ }
21191
+ });
21192
+ return out2;
21193
+ }
21194
+ function canUndoStepDone(args) {
21195
+ const { state, pipeline } = args;
21196
+ const idx = args.stepIdx ?? state.currentStepIdx;
21197
+ const step = state.steps[idx];
21198
+ if (!step) {
21199
+ return { ok: false, reason: `No step at index ${idx}` };
21200
+ }
21201
+ if (!(0, reconcileRun_1.reconcileRunSteps)(state, pipeline).aligned) {
21202
+ return {
21203
+ ok: false,
21204
+ reason: `Run "${state.runId}" no longer matches pipeline "${pipeline.id}" \u2014 reconcile it before undoing anything.`
21205
+ };
21206
+ }
21207
+ if (!UNDOABLE_STATUSES.includes(step.status)) {
21208
+ return {
21209
+ ok: false,
21210
+ reason: `Step "${(0, RunState_1.stepIdentity)(step)}" is "${step.status}" \u2014 there is no "mark done" to undo.`
21211
+ };
21212
+ }
21213
+ if (step.status === "approved") {
21214
+ for (const i of followersOf(pipeline, idx)) {
21215
+ const follower = state.steps[i];
21216
+ if (!follower || follower.status === "pending") {
21217
+ continue;
21218
+ }
21219
+ const untouched = follower.status === "awaiting_work" && follower.artifactsProduced.length === 0;
21220
+ if (!untouched) {
21221
+ return {
21222
+ ok: false,
21223
+ reason: `Step ${i + 1} ("${(0, RunState_1.stepIdentity)(follower)}") has already moved on (${follower.status}) \u2014 undo would silently discard it. Use "Request update" instead.`
21224
+ };
21225
+ }
21226
+ }
21227
+ }
21228
+ return { ok: true };
21229
+ }
21230
+ function undoStepDone(args) {
21231
+ const { pipeline } = args;
21232
+ const state = alignedOrThrow(args.state, pipeline);
21233
+ const idx = args.stepIdx ?? state.currentStepIdx;
21234
+ const gate = canUndoStepDone({ state, pipeline, stepIdx: idx });
21235
+ if (!gate.ok) {
21236
+ throw new PipelineRunError3(gate.reason);
21237
+ }
21238
+ const step = state.steps[idx];
21239
+ const now = (/* @__PURE__ */ new Date()).toISOString();
21240
+ const next = clone(state);
21241
+ if (step.status === "approved") {
21242
+ for (const i of followersOf(pipeline, idx)) {
21243
+ const follower = next.steps[i];
21244
+ if (!follower || follower.status !== "awaiting_work") {
21245
+ continue;
21246
+ }
21247
+ next.steps[i] = { ...follower, status: "pending", startedAt: void 0 };
21248
+ }
21249
+ }
21250
+ next.steps[idx] = {
21251
+ ...next.steps[idx],
21252
+ status: "awaiting_work",
21253
+ finishedAt: void 0,
21254
+ artifactsProduced: [],
21255
+ autoReviewVerdict: void 0,
21256
+ // `startedAt` stays: this is the same attempt at the same step, and the
21257
+ // panel dates the artifact against it to tell "written for this step" from
21258
+ // "inherited from an earlier one".
21259
+ history: pushHistory(step.history, {
21260
+ kind: "undo",
21261
+ at: now,
21262
+ revision: step.revision,
21263
+ from: step.status
21264
+ })
21265
+ };
21266
+ next.currentStepIdx = idx;
21267
+ next.status = "running";
21268
+ return next;
21269
+ }
21155
21270
  function submitAutoReviewVerdict2(args) {
21156
21271
  const { pipeline, verdict } = args;
21157
21272
  const state = alignedOrThrow(args.state, pipeline);
@@ -25198,6 +25313,7 @@ var require_EpicScaffold = __commonJS({
25198
25313
  exports2.epicsRoot = epicsRoot3;
25199
25314
  exports2.mapStepStatusToEpic = mapStepStatusToEpic;
25200
25315
  exports2.mirrorRunStateToEpic = mirrorRunStateToEpic2;
25316
+ exports2.lockedEpicDirError = lockedEpicDirError;
25201
25317
  exports2.scaffoldEpic = scaffoldEpic2;
25202
25318
  var fs14 = __importStar(require("fs"));
25203
25319
  var path17 = __importStar(require("path"));
@@ -25281,6 +25397,15 @@ var require_EpicScaffold = __commonJS({
25281
25397
  };
25282
25398
  fs14.writeFileSync(stateFile, JSON.stringify(next, null, 2) + "\n", "utf8");
25283
25399
  }
25400
+ function lockedEpicDirError(workspaceRoot, epicDir, err, action) {
25401
+ const rel = path17.relative(workspaceRoot, epicDir) || epicDir;
25402
+ const code = err?.code;
25403
+ const detail = err instanceof Error ? err.message : String(err);
25404
+ if (code === "EPERM" || code === "EACCES" || code === "EBUSY") {
25405
+ return new EpicScaffoldError3(`Epic dir ${rel} exists but cannot be ${action === "read" ? "read" : "created"} (${code}). It was most likely deleted while another process still held it open \u2014 on Windows the name survives until the last handle closes. Close whatever points at it (editor window, a terminal sitting in that folder, file explorer) or reboot, then start the epic again. Deleting it again will not help. Underlying error: ${detail}`);
25406
+ }
25407
+ return new EpicScaffoldError3(`Cannot ${action} epic dir ${rel}: ${detail}`);
25408
+ }
25284
25409
  function scaffoldEpic2(args) {
25285
25410
  const { workspaceRoot, doc, epicId, title, description, target, agents, inputs, extraProjects, pipeline, seedArtifacts, enableAutopilot = false, strictMode = true, tags } = args;
25286
25411
  if (!epicId.trim()) {
@@ -25291,12 +25416,21 @@ var require_EpicScaffold = __commonJS({
25291
25416
  }
25292
25417
  const epicDir = path17.join(epicsRoot3(workspaceRoot, doc), epicId);
25293
25418
  if (fs14.existsSync(epicDir)) {
25294
- const entries = fs14.readdirSync(epicDir);
25419
+ let entries;
25420
+ try {
25421
+ entries = fs14.readdirSync(epicDir);
25422
+ } catch (err) {
25423
+ throw lockedEpicDirError(workspaceRoot, epicDir, err, "read");
25424
+ }
25295
25425
  if (entries.length !== 1 || entries[0] !== EpicPipelineStore_1.EPIC_PIPELINE_FILENAME) {
25296
25426
  throw new EpicScaffoldError3(`Epic dir already exists at ${path17.relative(workspaceRoot, epicDir) || epicDir}. Delete it first.`);
25297
25427
  }
25298
25428
  }
25299
- fs14.mkdirSync(epicDir, { recursive: true });
25429
+ try {
25430
+ fs14.mkdirSync(epicDir, { recursive: true });
25431
+ } catch (err) {
25432
+ throw lockedEpicDirError(workspaceRoot, epicDir, err, "create");
25433
+ }
25300
25434
  const artifactsDir = path17.join(epicDir, "artifacts");
25301
25435
  fs14.mkdirSync(artifactsDir, { recursive: true });
25302
25436
  for (const [fileName, content] of Object.entries(seedArtifacts ?? {})) {
@@ -25547,6 +25681,400 @@ var require_EpicArtifactCommit = __commonJS({
25547
25681
  }
25548
25682
  });
25549
25683
 
25684
+ // ../core/dist/runs/EpicDescription.js
25685
+ var require_EpicDescription = __commonJS({
25686
+ "../core/dist/runs/EpicDescription.js"(exports2) {
25687
+ "use strict";
25688
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
25689
+ if (k2 === void 0) k2 = k;
25690
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25691
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25692
+ desc = { enumerable: true, get: function() {
25693
+ return m[k];
25694
+ } };
25695
+ }
25696
+ Object.defineProperty(o, k2, desc);
25697
+ } : function(o, m, k, k2) {
25698
+ if (k2 === void 0) k2 = k;
25699
+ o[k2] = m[k];
25700
+ });
25701
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
25702
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25703
+ } : function(o, v) {
25704
+ o["default"] = v;
25705
+ });
25706
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
25707
+ var ownKeys = function(o) {
25708
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
25709
+ var ar = [];
25710
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
25711
+ return ar;
25712
+ };
25713
+ return ownKeys(o);
25714
+ };
25715
+ return function(mod) {
25716
+ if (mod && mod.__esModule) return mod;
25717
+ var result = {};
25718
+ if (mod != null) {
25719
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
25720
+ }
25721
+ __setModuleDefault(result, mod);
25722
+ return result;
25723
+ };
25724
+ }();
25725
+ Object.defineProperty(exports2, "__esModule", { value: true });
25726
+ exports2.EpicDescriptionError = void 0;
25727
+ exports2.setEpicDescription = setEpicDescription2;
25728
+ var fs14 = __importStar(require("fs"));
25729
+ var path17 = __importStar(require("path"));
25730
+ var EpicDescriptionError2 = class extends Error {
25731
+ constructor(message) {
25732
+ super(message);
25733
+ this.name = "EpicDescriptionError";
25734
+ }
25735
+ };
25736
+ exports2.EpicDescriptionError = EpicDescriptionError2;
25737
+ function readState(stateFile, epicId) {
25738
+ let raw;
25739
+ try {
25740
+ raw = fs14.readFileSync(stateFile, "utf8");
25741
+ } catch (err) {
25742
+ throw new EpicDescriptionError2(`Cannot read ${path17.basename(stateFile)} for ${epicId}: ${err.message}`);
25743
+ }
25744
+ let parsed;
25745
+ try {
25746
+ parsed = JSON.parse(raw);
25747
+ } catch (err) {
25748
+ throw new EpicDescriptionError2(`${stateFile} is not valid JSON \u2014 fix it by hand before editing the description. ${err.message}`);
25749
+ }
25750
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
25751
+ throw new EpicDescriptionError2(`${stateFile} does not hold an epic state object.`);
25752
+ }
25753
+ return parsed;
25754
+ }
25755
+ function splitDoc(text) {
25756
+ const lines = text.split(/\r?\n/);
25757
+ const h1 = lines.findIndex((l) => /^#\s/.test(l));
25758
+ if (h1 === -1) {
25759
+ return null;
25760
+ }
25761
+ let end = lines.length;
25762
+ for (let i = h1 + 1; i < lines.length; i++) {
25763
+ if (/^#{1,6}\s/.test(lines[i])) {
25764
+ end = i;
25765
+ break;
25766
+ }
25767
+ }
25768
+ return {
25769
+ head: lines.slice(0, h1 + 1).join("\n"),
25770
+ lead: lines.slice(h1 + 1, end).join("\n"),
25771
+ rest: lines.slice(end).join("\n")
25772
+ };
25773
+ }
25774
+ function setEpicDescription2(epicDir, epicId, description) {
25775
+ const next = description.trim();
25776
+ const stateFile = path17.join(epicDir, "state.json");
25777
+ const docFile = path17.join(epicDir, `${epicId}.md`);
25778
+ const state = readState(stateFile, epicId);
25779
+ const previous = typeof state.description === "string" ? state.description : "";
25780
+ state.description = next;
25781
+ try {
25782
+ fs14.writeFileSync(stateFile, JSON.stringify(state, null, 2) + "\n", "utf8");
25783
+ } catch (err) {
25784
+ throw new EpicDescriptionError2(`Cannot write ${stateFile}: ${err.message}`);
25785
+ }
25786
+ const title = typeof state.title === "string" ? state.title.trim() : "";
25787
+ const doc = writeDoc(docFile, epicId, title, previous, next);
25788
+ return { epicId, description: next, previous, doc, docFile };
25789
+ }
25790
+ function writeDoc(docFile, epicId, title, previous, next) {
25791
+ let text = null;
25792
+ try {
25793
+ text = fs14.readFileSync(docFile, "utf8");
25794
+ } catch {
25795
+ text = null;
25796
+ }
25797
+ if (text === null) {
25798
+ const body = `# ${epicId}${title ? ` \u2014 ${title}` : ""}
25799
+ ` + (next ? `
25800
+ ${next}
25801
+ ` : "");
25802
+ try {
25803
+ fs14.mkdirSync(path17.dirname(docFile), { recursive: true });
25804
+ fs14.writeFileSync(docFile, body, "utf8");
25805
+ } catch (err) {
25806
+ throw new EpicDescriptionError2(`Cannot write ${docFile}: ${err.message}`);
25807
+ }
25808
+ return "created";
25809
+ }
25810
+ const parts = splitDoc(text);
25811
+ if (!parts) {
25812
+ return "kept";
25813
+ }
25814
+ const leadText = parts.lead.trim();
25815
+ if (leadText !== previous.trim()) {
25816
+ return "kept";
25817
+ }
25818
+ if (leadText === next) {
25819
+ return "unchanged";
25820
+ }
25821
+ const rebuilt = parts.head + "\n" + (next ? `
25822
+ ${next}
25823
+ ` : "") + (parts.rest ? `
25824
+ ${parts.rest}` : "");
25825
+ try {
25826
+ fs14.writeFileSync(docFile, rebuilt, "utf8");
25827
+ } catch (err) {
25828
+ throw new EpicDescriptionError2(`Cannot write ${docFile}: ${err.message}`);
25829
+ }
25830
+ return "updated";
25831
+ }
25832
+ }
25833
+ });
25834
+
25835
+ // ../core/dist/runs/EpicWorkflowSwitch.js
25836
+ var require_EpicWorkflowSwitch = __commonJS({
25837
+ "../core/dist/runs/EpicWorkflowSwitch.js"(exports2) {
25838
+ "use strict";
25839
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
25840
+ if (k2 === void 0) k2 = k;
25841
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25842
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25843
+ desc = { enumerable: true, get: function() {
25844
+ return m[k];
25845
+ } };
25846
+ }
25847
+ Object.defineProperty(o, k2, desc);
25848
+ } : function(o, m, k, k2) {
25849
+ if (k2 === void 0) k2 = k;
25850
+ o[k2] = m[k];
25851
+ });
25852
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
25853
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25854
+ } : function(o, v) {
25855
+ o["default"] = v;
25856
+ });
25857
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
25858
+ var ownKeys = function(o) {
25859
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
25860
+ var ar = [];
25861
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
25862
+ return ar;
25863
+ };
25864
+ return ownKeys(o);
25865
+ };
25866
+ return function(mod) {
25867
+ if (mod && mod.__esModule) return mod;
25868
+ var result = {};
25869
+ if (mod != null) {
25870
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
25871
+ }
25872
+ __setModuleDefault(result, mod);
25873
+ return result;
25874
+ };
25875
+ }();
25876
+ Object.defineProperty(exports2, "__esModule", { value: true });
25877
+ exports2.EpicWorkflowSwitchError = void 0;
25878
+ exports2.epicWorkflowLock = epicWorkflowLock2;
25879
+ exports2.canSwitchEpicWorkflow = canSwitchEpicWorkflow;
25880
+ exports2.planEpicWorkflowSwitch = planEpicWorkflowSwitch2;
25881
+ exports2.stageEpicWorkflowSwitch = stageEpicWorkflowSwitch2;
25882
+ exports2.applyEpicWorkflowSwitch = applyEpicWorkflowSwitch2;
25883
+ var fs14 = __importStar(require("fs"));
25884
+ var path17 = __importStar(require("path"));
25885
+ var WorkspaceSchema_1 = require_WorkspaceSchema();
25886
+ var PipelineRunner_1 = require_PipelineRunner();
25887
+ var RunStateStore_1 = require_RunStateStore();
25888
+ var PipelineAssembler_1 = require_PipelineAssembler();
25889
+ var EpicScaffold_1 = require_EpicScaffold();
25890
+ var EpicPipelineStore_1 = require_EpicPipelineStore();
25891
+ var EpicWorkflowSwitchError2 = class extends Error {
25892
+ constructor(message) {
25893
+ super(message);
25894
+ this.name = "EpicWorkflowSwitchError";
25895
+ }
25896
+ };
25897
+ exports2.EpicWorkflowSwitchError = EpicWorkflowSwitchError2;
25898
+ function epicWorkflowLock2(run) {
25899
+ if (!run) {
25900
+ return null;
25901
+ }
25902
+ if (run.status !== "running") {
25903
+ return `the run is already ${run.status}`;
25904
+ }
25905
+ for (const step of run.steps) {
25906
+ const id = step.name ?? step.agent;
25907
+ if (step.status !== "pending" && step.status !== "awaiting_work") {
25908
+ return `step "${id}" is ${step.status}`;
25909
+ }
25910
+ if (step.revision > 1) {
25911
+ return `step "${id}" has already been rerun`;
25912
+ }
25913
+ if ((step.history?.length ?? 0) > 0) {
25914
+ return `step "${id}" already has history`;
25915
+ }
25916
+ if (step.artifactsProduced.length > 0) {
25917
+ return `step "${id}" has produced artifacts`;
25918
+ }
25919
+ }
25920
+ return null;
25921
+ }
25922
+ function canSwitchEpicWorkflow(workspaceRoot, epicId) {
25923
+ return epicWorkflowLock2(RunStateStore_1.RunStateStore.load(workspaceRoot, epicId)) === null;
25924
+ }
25925
+ function readEpicState(epicDir, epicId) {
25926
+ const file = path17.join(epicDir, "state.json");
25927
+ let raw;
25928
+ try {
25929
+ raw = fs14.readFileSync(file, "utf8");
25930
+ } catch {
25931
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" has no state.json at ${file}.`);
25932
+ }
25933
+ let parsed;
25934
+ try {
25935
+ parsed = JSON.parse(raw);
25936
+ } catch (err) {
25937
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" has a state.json that is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
25938
+ }
25939
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
25940
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" has a state.json that is not an object.`);
25941
+ }
25942
+ return parsed;
25943
+ }
25944
+ function stepAgents(pipeline) {
25945
+ return pipeline.steps.map((s) => (0, WorkspaceSchema_1.normalizeStep)(s).agent);
25946
+ }
25947
+ function planEpicWorkflowSwitch2(args) {
25948
+ const { workspaceRoot, doc, config, epicId, target } = args;
25949
+ const epicDir = path17.join((0, EpicScaffold_1.epicsRoot)(workspaceRoot, doc), epicId);
25950
+ const state = readEpicState(epicDir, epicId);
25951
+ const lock = epicWorkflowLock2(RunStateStore_1.RunStateStore.load(workspaceRoot, epicId));
25952
+ if (lock) {
25953
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" has already started, so its workflow can't be changed: ${lock}. Add or remove individual steps instead (\`aidlc epic step add\` / \`remove\`), or start a new epic.`);
25954
+ }
25955
+ const currentPipelineId = typeof state.pipeline === "string" && state.pipeline ? state.pipeline : null;
25956
+ const currentAgentId = typeof state.agent === "string" && state.agent ? state.agent : null;
25957
+ const from = currentPipelineId ? { kind: "pipeline", id: currentPipelineId } : currentAgentId ? { kind: "agent", id: currentAgentId } : { kind: "none", id: null };
25958
+ const ownFile = (0, EpicPipelineStore_1.epicPipelinePath)(workspaceRoot, doc, epicId);
25959
+ const previousOwnedPipelineId = fs14.existsSync(ownFile) && currentPipelineId ? currentPipelineId : null;
25960
+ let pipeline;
25961
+ let owned;
25962
+ if (target.kind === "pipeline") {
25963
+ const found = config.pipelines.find((p) => p.id === target.id);
25964
+ if (!found) {
25965
+ const available = config.pipelines.map((p) => p.id).join(", ") || "(none defined)";
25966
+ throw new EpicWorkflowSwitchError2(`Pipeline "${target.id}" is not defined. Available: ${available}`);
25967
+ }
25968
+ const otherOwner = (0, EpicPipelineStore_1.epicOwningPipeline)(doc, target.id);
25969
+ if (otherOwner && otherOwner !== epicId) {
25970
+ throw new EpicWorkflowSwitchError2(`Pipeline "${target.id}" belongs to epic "${otherOwner}" \u2014 it lives in that epic's folder and editing it there would reshape that epic too. Pick a recipe instead, and this epic gets its own copy.`);
25971
+ }
25972
+ if (target.id === currentPipelineId) {
25973
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" already runs pipeline "${target.id}".`);
25974
+ }
25975
+ if (found.steps.length === 0) {
25976
+ throw new EpicWorkflowSwitchError2(`Pipeline "${target.id}" has no steps to run.`);
25977
+ }
25978
+ pipeline = found;
25979
+ owned = otherOwner === epicId;
25980
+ } else {
25981
+ const taken = new Set(config.pipelines.map((p) => p.id).filter((id) => id !== previousOwnedPipelineId));
25982
+ const pipelineId = (0, PipelineAssembler_1.recipePipelineId)({ recipeId: target.id, epicId, taken });
25983
+ pipeline = (0, PipelineAssembler_1.assemblePipeline)(config, { recipeId: target.id, pipelineId });
25984
+ owned = true;
25985
+ }
25986
+ return {
25987
+ epicId,
25988
+ from,
25989
+ to: target,
25990
+ pipeline,
25991
+ owned,
25992
+ previousOwnedPipelineId,
25993
+ agents: stepAgents(pipeline)
25994
+ };
25995
+ }
25996
+ function stageEpicWorkflowSwitch2(doc, plan) {
25997
+ const { previousOwnedPipelineId, pipeline, owned, epicId } = plan;
25998
+ const next = pipeline;
25999
+ const at = previousOwnedPipelineId ? doc.pipelines.findIndex((p) => p.id === previousOwnedPipelineId) : -1;
26000
+ if (previousOwnedPipelineId) {
26001
+ (0, EpicPipelineStore_1.unstageEpicPipeline)(doc, previousOwnedPipelineId);
26002
+ }
26003
+ if (owned) {
26004
+ const existing = doc.pipelines.findIndex((p) => p.id === pipeline.id);
26005
+ if (existing >= 0) {
26006
+ doc.pipelines.splice(existing, 1, next);
26007
+ } else if (at >= 0) {
26008
+ doc.pipelines.splice(at, 1, next);
26009
+ } else {
26010
+ doc.pipelines.push(next);
26011
+ }
26012
+ if (previousOwnedPipelineId && previousOwnedPipelineId !== pipeline.id) {
26013
+ doc.pipelines = doc.pipelines.filter((p) => p.id !== previousOwnedPipelineId);
26014
+ }
26015
+ (0, EpicPipelineStore_1.stageEpicPipeline)(doc, pipeline.id, epicId);
26016
+ } else if (at >= 0) {
26017
+ doc.pipelines.splice(at, 1);
26018
+ }
26019
+ }
26020
+ function applyEpicWorkflowSwitch2(workspaceRoot, doc, plan) {
26021
+ const { epicId } = plan;
26022
+ const epicDir = path17.join((0, EpicScaffold_1.epicsRoot)(workspaceRoot, doc), epicId);
26023
+ const stateFile = path17.join(epicDir, "state.json");
26024
+ const state = readEpicState(epicDir, epicId);
26025
+ const previous = RunStateStore_1.RunStateStore.load(workspaceRoot, epicId);
26026
+ const lock = epicWorkflowLock2(previous);
26027
+ if (lock) {
26028
+ throw new EpicWorkflowSwitchError2(`Epic "${epicId}" has already started, so its workflow can't be changed: ${lock}.`);
26029
+ }
26030
+ let removedPipelineFile = null;
26031
+ if (!plan.owned && plan.previousOwnedPipelineId) {
26032
+ const file = (0, EpicPipelineStore_1.epicPipelinePath)(workspaceRoot, doc, epicId);
26033
+ if (fs14.existsSync(file)) {
26034
+ fs14.rmSync(file);
26035
+ removedPipelineFile = file;
26036
+ }
26037
+ }
26038
+ state.pipeline = plan.pipeline.id;
26039
+ state.agent = null;
26040
+ fs14.writeFileSync(stateFile, JSON.stringify(state, null, 2) + "\n", "utf8");
26041
+ const context = previous?.context ?? readInputs2(epicDir);
26042
+ const runState = (0, PipelineRunner_1.startRun)({
26043
+ runId: epicId,
26044
+ pipeline: plan.pipeline,
26045
+ context: { ...context, epic: epicId }
26046
+ });
26047
+ RunStateStore_1.RunStateStore.save(workspaceRoot, runState);
26048
+ (0, EpicScaffold_1.mirrorRunStateToEpic)(workspaceRoot, runState, doc);
26049
+ return {
26050
+ epicId,
26051
+ pipelineId: plan.pipeline.id,
26052
+ agents: plan.agents,
26053
+ previousPipelineId: plan.from.kind === "pipeline" ? plan.from.id : null,
26054
+ removedPipelineFile,
26055
+ runState
26056
+ };
26057
+ }
26058
+ function readInputs2(epicDir) {
26059
+ try {
26060
+ const parsed = JSON.parse(fs14.readFileSync(path17.join(epicDir, "inputs.json"), "utf8"));
26061
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
26062
+ return {};
26063
+ }
26064
+ const out2 = {};
26065
+ for (const [k, v] of Object.entries(parsed)) {
26066
+ if (typeof v === "string") {
26067
+ out2[k] = v;
26068
+ }
26069
+ }
26070
+ return out2;
26071
+ } catch {
26072
+ return {};
26073
+ }
26074
+ }
26075
+ }
26076
+ });
26077
+
25550
26078
  // ../core/dist/runs/EpicStepEdit.js
25551
26079
  var require_EpicStepEdit = __commonJS({
25552
26080
  "../core/dist/runs/EpicStepEdit.js"(exports2) {
@@ -28532,6 +29060,304 @@ var require_WorkspaceLoader = __commonJS({
28532
29060
  }
28533
29061
  });
28534
29062
 
29063
+ // ../core/dist/loader/recipeRefs.js
29064
+ var require_recipeRefs = __commonJS({
29065
+ "../core/dist/loader/recipeRefs.js"(exports2) {
29066
+ "use strict";
29067
+ Object.defineProperty(exports2, "__esModule", { value: true });
29068
+ exports2.recipesDrawingFrom = recipesDrawingFrom;
29069
+ exports2.dropRecipeStep = dropRecipeStep;
29070
+ exports2.dropRecipesForPipeline = dropRecipesForPipeline;
29071
+ function recipeList(doc) {
29072
+ return Array.isArray(doc.recipes) ? doc.recipes : [];
29073
+ }
29074
+ function stepIds(recipe) {
29075
+ return Array.isArray(recipe.steps) ? recipe.steps.map(String) : [];
29076
+ }
29077
+ function recipesDrawingFrom(doc, pipelineId) {
29078
+ const firstPipelineId = doc.pipelines.length > 0 ? String(doc.pipelines[0].id ?? "") : "";
29079
+ return recipeList(doc).filter((r) => {
29080
+ const from = typeof r.from === "string" && r.from ? r.from : "";
29081
+ return from ? from === pipelineId : firstPipelineId === pipelineId;
29082
+ });
29083
+ }
29084
+ function emptyEdit(recipe) {
29085
+ return { recipeId: String(recipe.id ?? ""), steps: [], gates: [], removed: false };
29086
+ }
29087
+ function dropGate(recipe, stepId) {
29088
+ const gates = recipe.gates;
29089
+ if (!gates || typeof gates !== "object" || Array.isArray(gates)) {
29090
+ return false;
29091
+ }
29092
+ const map2 = gates;
29093
+ if (!(stepId in map2)) {
29094
+ return false;
29095
+ }
29096
+ delete map2[stepId];
29097
+ if (Object.keys(map2).length === 0) {
29098
+ delete recipe.gates;
29099
+ }
29100
+ return true;
29101
+ }
29102
+ function dropRecipeStep(doc, pipelineId, stepId) {
29103
+ const edits = [];
29104
+ const emptied = [];
29105
+ for (const recipe of recipesDrawingFrom(doc, pipelineId)) {
29106
+ const edit = emptyEdit(recipe);
29107
+ const steps = stepIds(recipe);
29108
+ if (steps.includes(stepId)) {
29109
+ const kept = steps.filter((s) => s !== stepId);
29110
+ edit.steps.push(stepId);
29111
+ if (kept.length === 0) {
29112
+ edit.removed = true;
29113
+ emptied.push(recipe);
29114
+ } else {
29115
+ recipe.steps = kept;
29116
+ }
29117
+ }
29118
+ if (dropGate(recipe, stepId)) {
29119
+ edit.gates.push(stepId);
29120
+ }
29121
+ if (edit.steps.length > 0 || edit.gates.length > 0) {
29122
+ edits.push(edit);
29123
+ }
29124
+ }
29125
+ if (emptied.length > 0) {
29126
+ doc.recipes = recipeList(doc).filter((r) => !emptied.includes(r));
29127
+ }
29128
+ return edits;
29129
+ }
29130
+ function dropRecipesForPipeline(doc, pipelineId) {
29131
+ const doomed = recipeList(doc).filter((r) => r.from === pipelineId);
29132
+ if (doomed.length === 0) {
29133
+ return [];
29134
+ }
29135
+ doc.recipes = recipeList(doc).filter((r) => !doomed.includes(r));
29136
+ return doomed.map((r) => String(r.id ?? ""));
29137
+ }
29138
+ }
29139
+ });
29140
+
29141
+ // ../core/dist/loader/renameRefs.js
29142
+ var require_renameRefs = __commonJS({
29143
+ "../core/dist/loader/renameRefs.js"(exports2) {
29144
+ "use strict";
29145
+ Object.defineProperty(exports2, "__esModule", { value: true });
29146
+ exports2.renameAgentRefs = renameAgentRefs;
29147
+ exports2.renameSkillRefs = renameSkillRefs;
29148
+ exports2.agentReferences = agentReferences;
29149
+ exports2.skillReferences = skillReferences;
29150
+ exports2.unusedAfterStepRemoval = unusedAfterStepRemoval;
29151
+ var recipeRefs_1 = require_recipeRefs();
29152
+ function list(value) {
29153
+ return Array.isArray(value) ? value : [];
29154
+ }
29155
+ function strings(value) {
29156
+ return Array.isArray(value) ? value.map(String) : null;
29157
+ }
29158
+ function dagIdOf(step) {
29159
+ return typeof step.name === "string" && step.name.trim() ? step.name : String(step.agent ?? "");
29160
+ }
29161
+ function renameSkillOn(obj, oldId, newId) {
29162
+ let moved = false;
29163
+ const arr = strings(obj.skills);
29164
+ if (arr && arr.includes(oldId)) {
29165
+ obj.skills = arr.map((s) => s === oldId ? newId : s);
29166
+ moved = true;
29167
+ }
29168
+ if (typeof obj.skill === "string" && obj.skill === oldId) {
29169
+ obj.skill = newId;
29170
+ moved = true;
29171
+ }
29172
+ return moved;
29173
+ }
29174
+ function declaredSkills(obj) {
29175
+ return strings(obj.skills) ?? (typeof obj.skill === "string" ? [obj.skill] : []);
29176
+ }
29177
+ function renameAgentRefs(doc, oldId, newId) {
29178
+ const edit = { paths: [], renamedStepIds: [] };
29179
+ if (!oldId || !newId || oldId === newId) {
29180
+ return edit;
29181
+ }
29182
+ for (const pipeline of list(doc.pipelines)) {
29183
+ const pipelineId = String(pipeline.id ?? "");
29184
+ const steps = pipeline.steps;
29185
+ if (!Array.isArray(steps)) {
29186
+ continue;
29187
+ }
29188
+ let dagIdMoved = false;
29189
+ for (let i = 0; i < steps.length; i++) {
29190
+ const step = steps[i];
29191
+ if (typeof step === "string") {
29192
+ if (step === oldId) {
29193
+ steps[i] = newId;
29194
+ dagIdMoved = true;
29195
+ edit.paths.push(`pipelines.${pipelineId}.steps.${oldId}`);
29196
+ }
29197
+ continue;
29198
+ }
29199
+ if (!step || typeof step !== "object") {
29200
+ continue;
29201
+ }
29202
+ const obj = step;
29203
+ if (obj.agent !== oldId) {
29204
+ continue;
29205
+ }
29206
+ obj.agent = newId;
29207
+ const named = typeof obj.name === "string" && obj.name.trim() !== "";
29208
+ edit.paths.push(`pipelines.${pipelineId}.steps.${named ? String(obj.name) : oldId}.agent`);
29209
+ if (!named) {
29210
+ dagIdMoved = true;
29211
+ }
29212
+ }
29213
+ if (!dagIdMoved) {
29214
+ continue;
29215
+ }
29216
+ edit.renamedStepIds.push(oldId);
29217
+ for (const step of steps) {
29218
+ if (!step || typeof step !== "object") {
29219
+ continue;
29220
+ }
29221
+ const obj = step;
29222
+ const deps = strings(obj.depends_on);
29223
+ if (!deps || !deps.includes(oldId)) {
29224
+ continue;
29225
+ }
29226
+ obj.depends_on = deps.map((d) => d === oldId ? newId : d);
29227
+ edit.paths.push(`pipelines.${pipelineId}.steps.${dagIdOf(obj)}.depends_on`);
29228
+ }
29229
+ for (const recipe of (0, recipeRefs_1.recipesDrawingFrom)(doc, pipelineId)) {
29230
+ const recipeId = String(recipe.id ?? "");
29231
+ const recipeSteps = strings(recipe.steps);
29232
+ if (recipeSteps && recipeSteps.includes(oldId)) {
29233
+ recipe.steps = recipeSteps.map((s) => s === oldId ? newId : s);
29234
+ edit.paths.push(`recipes.${recipeId}.steps`);
29235
+ }
29236
+ const gates = recipe.gates;
29237
+ if (gates && typeof gates === "object" && !Array.isArray(gates)) {
29238
+ const map2 = gates;
29239
+ if (oldId in map2) {
29240
+ map2[newId] = map2[oldId];
29241
+ delete map2[oldId];
29242
+ edit.paths.push(`recipes.${recipeId}.gates.${oldId}`);
29243
+ }
29244
+ }
29245
+ }
29246
+ }
29247
+ for (const cmd of list(doc.slash_commands)) {
29248
+ if (cmd.agent === oldId) {
29249
+ cmd.agent = newId;
29250
+ edit.paths.push(`slash_commands.${String(cmd.name ?? "")}.agent`);
29251
+ }
29252
+ }
29253
+ return edit;
29254
+ }
29255
+ function renameSkillRefs(doc, oldId, newId) {
29256
+ const edit = { paths: [], renamedStepIds: [] };
29257
+ if (!oldId || !newId || oldId === newId) {
29258
+ return edit;
29259
+ }
29260
+ for (const agent of list(doc.agents)) {
29261
+ if (renameSkillOn(agent, oldId, newId)) {
29262
+ edit.paths.push(`agents.${String(agent.id ?? "")}.skills`);
29263
+ }
29264
+ }
29265
+ for (const pipeline of list(doc.pipelines)) {
29266
+ const pipelineId = String(pipeline.id ?? "");
29267
+ for (const step of Array.isArray(pipeline.steps) ? pipeline.steps : []) {
29268
+ if (!step || typeof step !== "object") {
29269
+ continue;
29270
+ }
29271
+ const obj = step;
29272
+ if (!renameSkillOn(obj, oldId, newId)) {
29273
+ continue;
29274
+ }
29275
+ edit.paths.push(`pipelines.${pipelineId}.steps.${dagIdOf(obj)}.skills`);
29276
+ }
29277
+ }
29278
+ return edit;
29279
+ }
29280
+ function agentReferences(doc, id) {
29281
+ const out2 = { paths: [], messages: [] };
29282
+ if (!id) {
29283
+ return out2;
29284
+ }
29285
+ for (const pipeline of list(doc.pipelines)) {
29286
+ const pipelineId = String(pipeline.id ?? "");
29287
+ for (const step of Array.isArray(pipeline.steps) ? pipeline.steps : []) {
29288
+ const isObj = Boolean(step) && typeof step === "object";
29289
+ const agent = typeof step === "string" ? step : step?.agent;
29290
+ if (agent !== id) {
29291
+ continue;
29292
+ }
29293
+ const name = isObj ? dagIdOf(step) : id;
29294
+ out2.paths.push(`pipelines.${pipelineId}.steps.${name}`);
29295
+ out2.messages.push(`Pipeline "${pipelineId}" step "${name}" still runs agent "${id}".`);
29296
+ }
29297
+ }
29298
+ for (const cmd of list(doc.slash_commands)) {
29299
+ if (cmd.agent !== id) {
29300
+ continue;
29301
+ }
29302
+ const name = String(cmd.name ?? "");
29303
+ out2.paths.push(`slash_commands.${name}.agent`);
29304
+ out2.messages.push(`Slash command "${name}" still targets agent "${id}".`);
29305
+ }
29306
+ return out2;
29307
+ }
29308
+ function skillReferences(doc, id) {
29309
+ const out2 = { paths: [], messages: [] };
29310
+ if (!id) {
29311
+ return out2;
29312
+ }
29313
+ for (const agent of list(doc.agents)) {
29314
+ const declared = declaredSkills(agent);
29315
+ if (!declared.includes(id)) {
29316
+ continue;
29317
+ }
29318
+ const agentId = String(agent.id ?? "");
29319
+ out2.paths.push(`agents.${agentId}.skills`);
29320
+ out2.messages.push(declared.length === 1 ? `Agent "${agentId}" declares "${id}" as its only skill \u2014 it needs another before this one goes.` : `Agent "${agentId}" declares skill "${id}".`);
29321
+ }
29322
+ for (const pipeline of list(doc.pipelines)) {
29323
+ const pipelineId = String(pipeline.id ?? "");
29324
+ for (const step of Array.isArray(pipeline.steps) ? pipeline.steps : []) {
29325
+ if (!step || typeof step !== "object") {
29326
+ continue;
29327
+ }
29328
+ const obj = step;
29329
+ if (!declaredSkills(obj).includes(id)) {
29330
+ continue;
29331
+ }
29332
+ out2.paths.push(`pipelines.${pipelineId}.steps.${dagIdOf(obj)}.skills`);
29333
+ out2.messages.push(`Pipeline "${pipelineId}" step "${dagIdOf(obj)}" narrows to skill "${id}".`);
29334
+ }
29335
+ }
29336
+ return out2;
29337
+ }
29338
+ function unusedAfterStepRemoval(doc, agentId) {
29339
+ const none = { agent: null, slashCommands: [], skills: [] };
29340
+ if (!agentId) {
29341
+ return none;
29342
+ }
29343
+ for (const pipeline of list(doc.pipelines)) {
29344
+ for (const step of Array.isArray(pipeline.steps) ? pipeline.steps : []) {
29345
+ const agent2 = typeof step === "string" ? step : step?.agent;
29346
+ if (agent2 === agentId) {
29347
+ return none;
29348
+ }
29349
+ }
29350
+ }
29351
+ const agent = list(doc.agents).find((a) => a.id === agentId);
29352
+ return {
29353
+ agent: agentId,
29354
+ slashCommands: list(doc.slash_commands).filter((c) => c.agent === agentId).map((c) => String(c.name ?? "")),
29355
+ skills: agent ? declaredSkills(agent) : []
29356
+ };
29357
+ }
29358
+ }
29359
+ });
29360
+
28535
29361
  // ../core/dist/loader/projectInstructions.js
28536
29362
  var require_projectInstructions = __commonJS({
28537
29363
  "../core/dist/loader/projectInstructions.js"(exports2) {
@@ -30053,6 +30879,8 @@ var require_runReport = __commonJS({
30053
30879
  return ` - \u{1F916} auto-review ${h.decision} (rev ${h.revision}): ${h.reason}`;
30054
30880
  case "approve":
30055
30881
  return ` - \u2714 approved (rev ${h.revision})`;
30882
+ case "undo":
30883
+ return ` - \u21B6 mark-done undone (rev ${h.revision}, was ${h.from})`;
30056
30884
  default:
30057
30885
  return " - (unknown event)";
30058
30886
  }
@@ -32219,12 +33047,12 @@ var require_dist = __commonJS({
32219
33047
  "../core/dist/index.js"(exports2) {
32220
33048
  "use strict";
32221
33049
  Object.defineProperty(exports2, "__esModule", { value: true });
32222
- exports2.parseFollowUps = exports2.FollowUpsParseError = exports2.FollowUpManifestSchema = exports2.FollowUpItemSchema = exports2.SIGNAL_FILE = exports2.FOLLOW_UP_ARTIFACT = exports2.readEpicSignal = exports2.existingEpicIds = exports2.followUpIdFor = exports2.followUpEpicId = exports2.renderIntentMarkdown = exports2.openFollowUpEpic = exports2.openIncidentEpic = exports2.SignalParseError = exports2.isSignal = exports2.parseSignal = exports2.SignalSchema = exports2.EpicStepEditError = exports2.resolveStepRef = exports2.describeGateEffect = exports2.commitEpicStepEdit = exports2.planSetEpicStepGates = exports2.planRemoveEpicStep = exports2.planAddEpicStep = exports2.EpicScaffoldError = exports2.epicsRoot = exports2.mapStepStatusToEpic = exports2.mirrorRunStateToEpic = exports2.scaffoldEpic = exports2.resolveArtifactCommitConfig = exports2.commitApprovedArtifacts = exports2.PipelineAdaptError = exports2.applyAdaptation = exports2.parseAdaptationVerdict = exports2.buildAdaptationPrompt = exports2.buildPhaseCatalog = exports2.slugEpicId = exports2.parseClassificationVerdict = exports2.buildClassificationPrompt = exports2.heuristicClassify = exports2.PipelineAssembleError = exports2.recipePipelineId = exports2.assemblePipeline = exports2.collectWorkspaceRefIssues = exports2.stepDagId = exports2.stepAgentId = exports2.normalizeStep = exports2.WorkspaceValidationError = exports2.validateWorkspace = exports2.WorkspaceSchema = void 0;
32223
- exports2.composeAgentPrompt = exports2.PROJECT_INSTRUCTION_FILES = exports2.findProjectInstructions = exports2.stripPersonaMetadata = exports2.PersonaLoader = exports2.SkillNotFoundError = exports2.SkillLoader = exports2.EnvVarMissingError = exports2.EnvResolver = exports2.epicPipelineReport = exports2.planEpicPipelineExtraction = exports2.epicOwningPipeline = exports2.unstageEpicPipeline = exports2.stageEpicPipeline = exports2.writeEpicPipelines = exports2.splitEpicPipelines = exports2.mergeEpicPipelines = exports2.epicPipelinePath = exports2.EPIC_PIPELINE_FILENAME = exports2.WORKSPACE_DIR = exports2.WORKSPACE_FILENAME = exports2.WorkspaceParseError = exports2.WorkspaceNotFoundError = exports2.WorkspaceLoader = exports2.renderPlanMarkdown = exports2.generatePlan = exports2.collectContext = exports2.followUpHookFailures = exports2.pendingFollowUpDone = exports2.applyFollowUpHookRun = exports2.trackFollowUpChildren = exports2.writeFollowUpHookLedger = exports2.readFollowUpHookLedger = exports2.followUpHookLedgerPath = exports2.runFollowUpHook = exports2.fillFollowUpHookCommand = exports2.resolveFollowUpHooks = exports2.epicPipelineId = exports2.FOLLOW_UP_HOOK_TIMEOUT_MS = exports2.FOLLOW_UP_HOOKS_LEDGER = exports2.stepProducesFollowUps = exports2.collectFollowUpHookIssues = exports2.FOLLOW_UP_HOOK_PLACEHOLDERS = exports2.FOLLOW_UP_HOOK_KEYS = exports2.FOLLOW_UP_INTENT = exports2.FOLLOW_UPS_FILE = exports2.openManifestFollowUp = exports2.followUpChildId = exports2.followUpsOf = exports2.readEpicFollowUps = void 0;
32224
- exports2.buildClaudeSpawnEnv = exports2.hasClaudeLogin = exports2.isInsideClaudeCodeSession = exports2.createJsonSink = exports2.createLineSink = exports2.isCodexMcpConfigured = exports2.codexConfigPath = exports2.readProjectMcpServer = exports2.mcpRegistrarFor = exports2.codexMcpRegistrar = exports2.claudeMcpRegistrar = exports2.CodexRunner = exports2.DefaultRunner = exports2.RunnerRegistry = exports2.targetPath = exports2.scopePaths = exports2.discoverAssets = exports2.commandBodyIsStale = exports2.readEpicTags = exports2.normalizeTags = exports2.normalizeTag = exports2.epicMatchesTags = exports2.applyTagEdit = exports2.MAX_TAG_LENGTH = exports2.EPIC_TAGS_KEY = exports2.strictModeSection = exports2.resolveEpicStrictMode = exports2.epicStrictMode = exports2.commandBodyPredatesStrictMode = exports2.STRICT_MODE_KEY = exports2.STRICT_MODE_HEADING = exports2.writeUserEpicIdPrefix = exports2.userConfigPath = exports2.readUserConfig = exports2.readGitUserName = exports2.ensureUserConfigIgnored = exports2.USER_CONFIG_RELPATH = exports2.USER_CONFIG_IGNORE_LINE = exports2.suggestEpicId = exports2.resolveEpicIdPrefixChain = exports2.resolveEpicIdPrefix = exports2.epicIdDateStamp = exports2.deriveEpicIdPrefix = exports2.EPIC_ID_PREFIX_PATTERN = exports2.EPIC_ID_PREFIX_KEY = exports2.resolveArtifactLanguage = exports2.commandBodyPredatesArtifactLanguage = exports2.artifactLanguageSection = exports2.ARTIFACT_LANGUAGE_HEADING = exports2.stripPersonaDirectives = void 0;
32225
- exports2.getBuiltinWorkflow = exports2.workflowSlug = exports2.builtinTemplatesRoot = exports2.pipelineCommandId = exports2.PHASES = exports2.BUILTIN_WORKFLOWS = exports2.withBackfilledStepNames = exports2.describeDrift = exports2.reconcileRunSteps = exports2.RUN_STATE_SCHEMA_VERSION = exports2.migrateRunState = exports2.stepIdentity = exports2.resolvePath = exports2.AutoReviewerError = exports2.runAutoReview = exports2.renderRunReport = exports2.verifyRun = exports2.runExecLoop = exports2.estimateCostUsd = exports2.providerAliases = exports2.ratesFromConfig = exports2.mergeRates = exports2.BUILTIN_RATES = exports2.checkBudget = exports2.isActiveStatus = exports2.isStepOptional = exports2.isRunComplete = exports2.deriveRunProgress = exports2.PipelineRunError = exports2.retryAutoReview = exports2.submitAutoReviewVerdict = exports2.requestStepUpdate = exports2.rerunStep = exports2.rejectStep = exports2.approveStep = exports2.markStepDone = exports2.canStartStep = exports2.startRun = exports2.activateBackendFromWorkspace = exports2.activateRunStateBackend = exports2.resolveRunStateBackend = exports2.GitRunStateStore = exports2.RUN_ID_PATTERN = exports2.FileRunStateStore = exports2.RunStateStore = exports2.harnessCapabilities = exports2.NO_HARNESS_CAPABILITIES = exports2.RunnerValidationError = exports2.validateRunnerExport = exports2.CustomRunnerLoader = void 0;
32226
- exports2.isEpicMemoryHookEnabled = exports2.installAnnotationTools = exports2.relativeEpicRoot = exports2.provisionDeclaredWorkflows = exports2.provisionWorkflowFiles = exports2.writeWorkflowArtifactTemplates = exports2.writeWorkflowCommands = exports2.provisionShortcutDocs = exports2.writeTwoLayerCommands = exports2.shortcutCommandDoc = exports2.backboneCommandDoc = exports2.unprovisionedPhases = exports2.nextEligiblePhase = exports2.resolveComposition = exports2.shortcutCommandId = exports2.getCanonicalPhase = exports2.isCanonicalPhase = exports2.BACKBONE_COMMAND_ID = exports2.CANONICAL_PHASE_IDS = exports2.CANONICAL_PHASES = exports2.renderTemplate = exports2.DEFAULT_GLOBAL_WORKFLOW_IDS = exports2.detectGlobalBuiltinSource = exports2.uninstallWorkflowGlobalsByIds = exports2.isWorkflowGloballyInstalled = exports2.installWorkflowGlobalsByIds = exports2.installGlobalDefaults = exports2.resolveProviderModel = exports2.isClaudeTierAlias = exports2.CLAUDE_TIER_ALIASES = exports2.FAST_MODEL = exports2.CODING_MODEL = exports2.PLANNING_MODEL = exports2.isBuiltinPreset = exports2.BUILTIN_PRESET_IDS = exports2.writeBuiltinAutoReviewValidators = exports2.resolvePrimaryStack = exports2.getSdlcArtifactTemplates = exports2.getBuiltinArtifactTemplates = exports2.phaseArtifactFileName = exports2.sdlcClaudeCommand = exports2.builtinClaudeCommand = exports2.loadAllBuiltinPresets = exports2.loadBuiltinPreset = exports2.planRecipeMigration = exports2.getBuiltinRecipeSummaries = exports2.getAllBuiltinPipelineSummaries = exports2.getSdlcBuiltinPipelineSummary = exports2.getBuiltinPipelineSummary = exports2.getBuiltinWorkflowByPipelineId = void 0;
32227
- exports2.AIDLC_CORE_VERSION = exports2.AIDLC_CLI_GUIDE_TEXT = exports2.AIDLC_KNOWLEDGE = exports2.clearResolveCommandCache = exports2.resolveCommand = exports2.remapClaudePath = exports2.claudeConfigEnv = exports2.claudeJsonPath = exports2.isDefaultClaudeConfigDir = exports2.claudeConfigDir = exports2.resolveClaudeConfigDir = exports2.defaultClaudeConfigDir = exports2.getClaudeConfigDirOverride = exports2.setClaudeConfigDir = exports2.resolveDeclaredPath = exports2.expandHome = exports2.ProfileLoadError = exports2.UnknownStandardError = exports2.builtinProfiles = exports2.loadActiveProfile = exports2.loadProfile = exports2.listProfileManifests = exports2.resolveStandard = exports2.workspaceStandard = exports2.isBuiltinProfileId = exports2.ProfileSchema = exports2.TRACE_RULES = exports2.PROFILES_DIR = exports2.DEFAULT_PROFILE_ID = exports2.BUILTIN_PROFILE_IDS = exports2.setEpicMemoryHook = void 0;
33050
+ exports2.followUpEpicId = exports2.renderIntentMarkdown = exports2.openFollowUpEpic = exports2.openIncidentEpic = exports2.SignalParseError = exports2.isSignal = exports2.parseSignal = exports2.SignalSchema = exports2.EpicStepEditError = exports2.resolveStepRef = exports2.describeGateEffect = exports2.commitEpicStepEdit = exports2.planSetEpicStepGates = exports2.planRemoveEpicStep = exports2.planAddEpicStep = exports2.EpicWorkflowSwitchError = exports2.canSwitchEpicWorkflow = exports2.epicWorkflowLock = exports2.applyEpicWorkflowSwitch = exports2.stageEpicWorkflowSwitch = exports2.planEpicWorkflowSwitch = exports2.EpicDescriptionError = exports2.setEpicDescription = exports2.lockedEpicDirError = exports2.EpicScaffoldError = exports2.epicsRoot = exports2.mapStepStatusToEpic = exports2.mirrorRunStateToEpic = exports2.scaffoldEpic = exports2.resolveArtifactCommitConfig = exports2.commitApprovedArtifacts = exports2.PipelineAdaptError = exports2.applyAdaptation = exports2.parseAdaptationVerdict = exports2.buildAdaptationPrompt = exports2.buildPhaseCatalog = exports2.slugEpicId = exports2.parseClassificationVerdict = exports2.buildClassificationPrompt = exports2.heuristicClassify = exports2.PipelineAssembleError = exports2.recipePipelineId = exports2.assemblePipeline = exports2.collectWorkspaceRefIssues = exports2.stepDagId = exports2.stepAgentId = exports2.normalizeStep = exports2.WorkspaceValidationError = exports2.validateWorkspace = exports2.WorkspaceSchema = void 0;
33051
+ exports2.epicPipelinePath = exports2.EPIC_PIPELINE_FILENAME = exports2.unusedAfterStepRemoval = exports2.skillReferences = exports2.agentReferences = exports2.renameSkillRefs = exports2.renameAgentRefs = exports2.dropRecipesForPipeline = exports2.dropRecipeStep = exports2.recipesDrawingFrom = exports2.WORKSPACE_DIR = exports2.WORKSPACE_FILENAME = exports2.WorkspaceParseError = exports2.WorkspaceNotFoundError = exports2.WorkspaceLoader = exports2.renderPlanMarkdown = exports2.generatePlan = exports2.collectContext = exports2.followUpHookFailures = exports2.pendingFollowUpDone = exports2.applyFollowUpHookRun = exports2.trackFollowUpChildren = exports2.writeFollowUpHookLedger = exports2.readFollowUpHookLedger = exports2.followUpHookLedgerPath = exports2.runFollowUpHook = exports2.fillFollowUpHookCommand = exports2.resolveFollowUpHooks = exports2.epicPipelineId = exports2.FOLLOW_UP_HOOK_TIMEOUT_MS = exports2.FOLLOW_UP_HOOKS_LEDGER = exports2.stepProducesFollowUps = exports2.collectFollowUpHookIssues = exports2.FOLLOW_UP_HOOK_PLACEHOLDERS = exports2.FOLLOW_UP_HOOK_KEYS = exports2.FOLLOW_UP_INTENT = exports2.FOLLOW_UPS_FILE = exports2.openManifestFollowUp = exports2.followUpChildId = exports2.followUpsOf = exports2.readEpicFollowUps = exports2.parseFollowUps = exports2.FollowUpsParseError = exports2.FollowUpManifestSchema = exports2.FollowUpItemSchema = exports2.SIGNAL_FILE = exports2.FOLLOW_UP_ARTIFACT = exports2.readEpicSignal = exports2.existingEpicIds = exports2.followUpIdFor = void 0;
33052
+ exports2.commandBodyIsStale = exports2.readEpicTags = exports2.normalizeTags = exports2.normalizeTag = exports2.epicMatchesTags = exports2.applyTagEdit = exports2.MAX_TAG_LENGTH = exports2.EPIC_TAGS_KEY = exports2.strictModeSection = exports2.resolveEpicStrictMode = exports2.epicStrictMode = exports2.commandBodyPredatesStrictMode = exports2.STRICT_MODE_KEY = exports2.STRICT_MODE_HEADING = exports2.writeUserEpicIdPrefix = exports2.userConfigPath = exports2.readUserConfig = exports2.readGitUserName = exports2.ensureUserConfigIgnored = exports2.USER_CONFIG_RELPATH = exports2.USER_CONFIG_IGNORE_LINE = exports2.suggestEpicId = exports2.resolveEpicIdPrefixChain = exports2.resolveEpicIdPrefix = exports2.epicIdDateStamp = exports2.deriveEpicIdPrefix = exports2.EPIC_ID_PREFIX_PATTERN = exports2.EPIC_ID_PREFIX_KEY = exports2.resolveArtifactLanguage = exports2.commandBodyPredatesArtifactLanguage = exports2.artifactLanguageSection = exports2.ARTIFACT_LANGUAGE_HEADING = exports2.stripPersonaDirectives = exports2.composeAgentPrompt = exports2.PROJECT_INSTRUCTION_FILES = exports2.findProjectInstructions = exports2.stripPersonaMetadata = exports2.PersonaLoader = exports2.SkillNotFoundError = exports2.SkillLoader = exports2.EnvVarMissingError = exports2.EnvResolver = exports2.epicPipelineReport = exports2.planEpicPipelineExtraction = exports2.epicOwningPipeline = exports2.unstageEpicPipeline = exports2.stageEpicPipeline = exports2.writeEpicPipelines = exports2.splitEpicPipelines = exports2.mergeEpicPipelines = void 0;
33053
+ exports2.providerAliases = exports2.ratesFromConfig = exports2.mergeRates = exports2.BUILTIN_RATES = exports2.checkBudget = exports2.isActiveStatus = exports2.isStepOptional = exports2.isRunComplete = exports2.deriveRunProgress = exports2.PipelineRunError = exports2.retryAutoReview = exports2.submitAutoReviewVerdict = exports2.requestStepUpdate = exports2.rerunStep = exports2.rejectStep = exports2.approveStep = exports2.undoStepDone = exports2.canUndoStepDone = exports2.markStepDone = exports2.canStartStep = exports2.startRun = exports2.activateBackendFromWorkspace = exports2.activateRunStateBackend = exports2.resolveRunStateBackend = exports2.GitRunStateStore = exports2.RUN_ID_PATTERN = exports2.FileRunStateStore = exports2.RunStateStore = exports2.harnessCapabilities = exports2.NO_HARNESS_CAPABILITIES = exports2.RunnerValidationError = exports2.validateRunnerExport = exports2.CustomRunnerLoader = exports2.buildClaudeSpawnEnv = exports2.hasClaudeLogin = exports2.isInsideClaudeCodeSession = exports2.createJsonSink = exports2.createLineSink = exports2.isCodexMcpConfigured = exports2.codexConfigPath = exports2.readProjectMcpServer = exports2.mcpRegistrarFor = exports2.codexMcpRegistrar = exports2.claudeMcpRegistrar = exports2.CodexRunner = exports2.DefaultRunner = exports2.RunnerRegistry = exports2.targetPath = exports2.scopePaths = exports2.discoverAssets = void 0;
33054
+ exports2.CANONICAL_PHASES = exports2.renderTemplate = exports2.DEFAULT_GLOBAL_WORKFLOW_IDS = exports2.detectGlobalBuiltinSource = exports2.uninstallWorkflowGlobalsByIds = exports2.isWorkflowGloballyInstalled = exports2.installWorkflowGlobalsByIds = exports2.installGlobalDefaults = exports2.resolveProviderModel = exports2.isClaudeTierAlias = exports2.CLAUDE_TIER_ALIASES = exports2.FAST_MODEL = exports2.CODING_MODEL = exports2.PLANNING_MODEL = exports2.isBuiltinPreset = exports2.BUILTIN_PRESET_IDS = exports2.writeBuiltinAutoReviewValidators = exports2.resolvePrimaryStack = exports2.getSdlcArtifactTemplates = exports2.getBuiltinArtifactTemplates = exports2.phaseArtifactFileName = exports2.sdlcClaudeCommand = exports2.builtinClaudeCommand = exports2.loadAllBuiltinPresets = exports2.loadBuiltinPreset = exports2.planRecipeMigration = exports2.getBuiltinRecipeSummaries = exports2.getAllBuiltinPipelineSummaries = exports2.getSdlcBuiltinPipelineSummary = exports2.getBuiltinPipelineSummary = exports2.getBuiltinWorkflowByPipelineId = exports2.getBuiltinWorkflow = exports2.workflowSlug = exports2.builtinTemplatesRoot = exports2.pipelineCommandId = exports2.PHASES = exports2.BUILTIN_WORKFLOWS = exports2.withBackfilledStepNames = exports2.describeDrift = exports2.reconcileRunSteps = exports2.RUN_STATE_SCHEMA_VERSION = exports2.migrateRunState = exports2.stepIdentity = exports2.resolvePath = exports2.AutoReviewerError = exports2.runAutoReview = exports2.renderRunReport = exports2.verifyRun = exports2.runExecLoop = exports2.estimateCostUsd = void 0;
33055
+ exports2.AIDLC_CORE_VERSION = exports2.AIDLC_CLI_GUIDE_TEXT = exports2.AIDLC_KNOWLEDGE = exports2.clearResolveCommandCache = exports2.resolveCommand = exports2.remapClaudePath = exports2.claudeConfigEnv = exports2.claudeJsonPath = exports2.isDefaultClaudeConfigDir = exports2.claudeConfigDir = exports2.resolveClaudeConfigDir = exports2.defaultClaudeConfigDir = exports2.getClaudeConfigDirOverride = exports2.setClaudeConfigDir = exports2.resolveDeclaredPath = exports2.expandHome = exports2.ProfileLoadError = exports2.UnknownStandardError = exports2.builtinProfiles = exports2.loadActiveProfile = exports2.loadProfile = exports2.listProfileManifests = exports2.resolveStandard = exports2.workspaceStandard = exports2.isBuiltinProfileId = exports2.ProfileSchema = exports2.TRACE_RULES = exports2.PROFILES_DIR = exports2.DEFAULT_PROFILE_ID = exports2.BUILTIN_PROFILE_IDS = exports2.setEpicMemoryHook = exports2.isEpicMemoryHookEnabled = exports2.installAnnotationTools = exports2.relativeEpicRoot = exports2.provisionDeclaredWorkflows = exports2.provisionWorkflowFiles = exports2.writeWorkflowArtifactTemplates = exports2.writeWorkflowCommands = exports2.provisionShortcutDocs = exports2.writeTwoLayerCommands = exports2.shortcutCommandDoc = exports2.backboneCommandDoc = exports2.unprovisionedPhases = exports2.nextEligiblePhase = exports2.resolveComposition = exports2.shortcutCommandId = exports2.getCanonicalPhase = exports2.isCanonicalPhase = exports2.BACKBONE_COMMAND_ID = exports2.CANONICAL_PHASE_IDS = void 0;
32228
33056
  var WorkspaceSchema_1 = require_WorkspaceSchema();
32229
33057
  Object.defineProperty(exports2, "WorkspaceSchema", { enumerable: true, get: function() {
32230
33058
  return WorkspaceSchema_1.WorkspaceSchema;
@@ -32309,6 +33137,35 @@ var require_dist = __commonJS({
32309
33137
  Object.defineProperty(exports2, "EpicScaffoldError", { enumerable: true, get: function() {
32310
33138
  return EpicScaffold_1.EpicScaffoldError;
32311
33139
  } });
33140
+ Object.defineProperty(exports2, "lockedEpicDirError", { enumerable: true, get: function() {
33141
+ return EpicScaffold_1.lockedEpicDirError;
33142
+ } });
33143
+ var EpicDescription_1 = require_EpicDescription();
33144
+ Object.defineProperty(exports2, "setEpicDescription", { enumerable: true, get: function() {
33145
+ return EpicDescription_1.setEpicDescription;
33146
+ } });
33147
+ Object.defineProperty(exports2, "EpicDescriptionError", { enumerable: true, get: function() {
33148
+ return EpicDescription_1.EpicDescriptionError;
33149
+ } });
33150
+ var EpicWorkflowSwitch_1 = require_EpicWorkflowSwitch();
33151
+ Object.defineProperty(exports2, "planEpicWorkflowSwitch", { enumerable: true, get: function() {
33152
+ return EpicWorkflowSwitch_1.planEpicWorkflowSwitch;
33153
+ } });
33154
+ Object.defineProperty(exports2, "stageEpicWorkflowSwitch", { enumerable: true, get: function() {
33155
+ return EpicWorkflowSwitch_1.stageEpicWorkflowSwitch;
33156
+ } });
33157
+ Object.defineProperty(exports2, "applyEpicWorkflowSwitch", { enumerable: true, get: function() {
33158
+ return EpicWorkflowSwitch_1.applyEpicWorkflowSwitch;
33159
+ } });
33160
+ Object.defineProperty(exports2, "epicWorkflowLock", { enumerable: true, get: function() {
33161
+ return EpicWorkflowSwitch_1.epicWorkflowLock;
33162
+ } });
33163
+ Object.defineProperty(exports2, "canSwitchEpicWorkflow", { enumerable: true, get: function() {
33164
+ return EpicWorkflowSwitch_1.canSwitchEpicWorkflow;
33165
+ } });
33166
+ Object.defineProperty(exports2, "EpicWorkflowSwitchError", { enumerable: true, get: function() {
33167
+ return EpicWorkflowSwitch_1.EpicWorkflowSwitchError;
33168
+ } });
32312
33169
  var EpicStepEdit_1 = require_EpicStepEdit();
32313
33170
  Object.defineProperty(exports2, "planAddEpicStep", { enumerable: true, get: function() {
32314
33171
  return EpicStepEdit_1.planAddEpicStep;
@@ -32483,6 +33340,32 @@ var require_dist = __commonJS({
32483
33340
  Object.defineProperty(exports2, "WORKSPACE_DIR", { enumerable: true, get: function() {
32484
33341
  return WorkspaceLoader_1.WORKSPACE_DIR;
32485
33342
  } });
33343
+ var recipeRefs_1 = require_recipeRefs();
33344
+ Object.defineProperty(exports2, "recipesDrawingFrom", { enumerable: true, get: function() {
33345
+ return recipeRefs_1.recipesDrawingFrom;
33346
+ } });
33347
+ Object.defineProperty(exports2, "dropRecipeStep", { enumerable: true, get: function() {
33348
+ return recipeRefs_1.dropRecipeStep;
33349
+ } });
33350
+ Object.defineProperty(exports2, "dropRecipesForPipeline", { enumerable: true, get: function() {
33351
+ return recipeRefs_1.dropRecipesForPipeline;
33352
+ } });
33353
+ var renameRefs_1 = require_renameRefs();
33354
+ Object.defineProperty(exports2, "renameAgentRefs", { enumerable: true, get: function() {
33355
+ return renameRefs_1.renameAgentRefs;
33356
+ } });
33357
+ Object.defineProperty(exports2, "renameSkillRefs", { enumerable: true, get: function() {
33358
+ return renameRefs_1.renameSkillRefs;
33359
+ } });
33360
+ Object.defineProperty(exports2, "agentReferences", { enumerable: true, get: function() {
33361
+ return renameRefs_1.agentReferences;
33362
+ } });
33363
+ Object.defineProperty(exports2, "skillReferences", { enumerable: true, get: function() {
33364
+ return renameRefs_1.skillReferences;
33365
+ } });
33366
+ Object.defineProperty(exports2, "unusedAfterStepRemoval", { enumerable: true, get: function() {
33367
+ return renameRefs_1.unusedAfterStepRemoval;
33368
+ } });
32486
33369
  var EpicPipelineStore_1 = require_EpicPipelineStore();
32487
33370
  Object.defineProperty(exports2, "EPIC_PIPELINE_FILENAME", { enumerable: true, get: function() {
32488
33371
  return EpicPipelineStore_1.EPIC_PIPELINE_FILENAME;
@@ -32760,6 +33643,12 @@ var require_dist = __commonJS({
32760
33643
  Object.defineProperty(exports2, "markStepDone", { enumerable: true, get: function() {
32761
33644
  return PipelineRunner_1.markStepDone;
32762
33645
  } });
33646
+ Object.defineProperty(exports2, "canUndoStepDone", { enumerable: true, get: function() {
33647
+ return PipelineRunner_1.canUndoStepDone;
33648
+ } });
33649
+ Object.defineProperty(exports2, "undoStepDone", { enumerable: true, get: function() {
33650
+ return PipelineRunner_1.undoStepDone;
33651
+ } });
32763
33652
  Object.defineProperty(exports2, "approveStep", { enumerable: true, get: function() {
32764
33653
  return PipelineRunner_1.approveStep;
32765
33654
  } });
@@ -41796,7 +42685,7 @@ var require_package = __commonJS({
41796
42685
  "package.json"(exports2, module2) {
41797
42686
  module2.exports = {
41798
42687
  name: "@delete101020/aidlc",
41799
- version: "4.0.10",
42688
+ version: "4.0.12",
41800
42689
  description: "AIDLC Native CLI, a fork of AIDLC \u2014 drive Claude through the six-stage AI-Native SDLC Playbook: Plan \u2192 Design \u2192 Build \u2192 Test \u2192 Deploy \u2192 Maintain, with artifact gates, requirements traceability, an incident loop back to Plan, and Spec Kit support.",
41801
42690
  license: "MIT",
41802
42691
  repository: {
@@ -48514,6 +49403,121 @@ Run ${import_chalk15.default.cyan(`${runCmd} ${epicId}`)} in Claude to begin.`);
48514
49403
  console.log(import_chalk15.default.dim(` was: ${before.join(" ")}`));
48515
49404
  }
48516
49405
  });
49406
+ cmd.command("describe <id> [text...]").description("Show or set an epic's description. Bare `describe <id>` prints it.").option("--clear", "empty the description").option("--json", "Output the result as JSON").action((id, text, opts, actionCmd) => {
49407
+ const root = resolveWorkspaceRoot(actionCmd);
49408
+ const doc = readYaml(root);
49409
+ const dir = path12.join((0, import_core19.epicsRoot)(root, doc), id);
49410
+ const file = path12.join(dir, "state.json");
49411
+ if (!fs10.existsSync(file)) {
49412
+ const all = listEpics(root, doc).map((e) => e.id);
49413
+ console.error(import_chalk15.default.red(`Epic "${id}" not found.`));
49414
+ if (all.length > 0) {
49415
+ console.error(import_chalk15.default.dim(`Available: ${all.join(", ")}`));
49416
+ }
49417
+ process.exit(1);
49418
+ }
49419
+ const words = text.join(" ").trim();
49420
+ if (!opts.clear && words === "") {
49421
+ let current = "";
49422
+ try {
49423
+ const state = JSON.parse(fs10.readFileSync(file, "utf8"));
49424
+ current = typeof state.description === "string" ? state.description : "";
49425
+ } catch (err) {
49426
+ console.error(import_chalk15.default.red(`Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`));
49427
+ process.exit(1);
49428
+ }
49429
+ if (opts.json) {
49430
+ console.log(JSON.stringify({ id, description: current }, null, 2));
49431
+ return;
49432
+ }
49433
+ console.log(current ? current : import_chalk15.default.dim("(no description)"));
49434
+ return;
49435
+ }
49436
+ try {
49437
+ const edit = (0, import_core19.setEpicDescription)(dir, id, opts.clear ? "" : words);
49438
+ if (opts.json) {
49439
+ console.log(JSON.stringify(edit, null, 2));
49440
+ return;
49441
+ }
49442
+ console.log(import_chalk15.default.green("\u2714") + ` ${import_chalk15.default.bold(id)}: ` + (edit.description ? edit.description : import_chalk15.default.dim("(no description)")));
49443
+ if (edit.previous) {
49444
+ console.log(import_chalk15.default.dim(` was: ${edit.previous}`));
49445
+ }
49446
+ console.log(import_chalk15.default.dim(` ${id}.md: ${edit.doc === "kept" ? "left as it is \u2014 written by hand since, and agents read it" : edit.doc === "created" ? "created" : edit.doc}`));
49447
+ } catch (err) {
49448
+ console.error(import_chalk15.default.red(err instanceof import_core19.EpicDescriptionError ? err.message : String(err)));
49449
+ process.exit(1);
49450
+ }
49451
+ });
49452
+ cmd.command("workflow <id>").description("Show or change the workflow an epic runs. Only before its first step moves.").option("--recipe <recipeId>", "rebuild the epic's own pipeline from this recipe").option("--pipeline <pipelineId>", "point the epic at a pipeline that already exists").option("--json", "Output the result as JSON").action((id, opts, actionCmd) => {
49453
+ const root = resolveWorkspaceRoot(actionCmd);
49454
+ const doc = requireYaml(root);
49455
+ const stateFile = path12.join((0, import_core19.epicsRoot)(root, doc), id, "state.json");
49456
+ if (!fs10.existsSync(stateFile)) {
49457
+ const all = listEpics(root, doc).map((e) => e.id);
49458
+ console.error(import_chalk15.default.red(`Epic "${id}" not found.`));
49459
+ if (all.length > 0) {
49460
+ console.error(import_chalk15.default.dim(`Available: ${all.join(", ")}`));
49461
+ }
49462
+ process.exit(1);
49463
+ }
49464
+ if (opts.recipe && opts.pipeline) {
49465
+ console.error(import_chalk15.default.red("Pass --recipe or --pipeline, not both."));
49466
+ process.exit(1);
49467
+ }
49468
+ const lock = (0, import_core19.epicWorkflowLock)(import_core19.RunStateStore.load(root, id));
49469
+ if (!opts.recipe && !opts.pipeline) {
49470
+ const state = JSON.parse(fs10.readFileSync(stateFile, "utf8"));
49471
+ const current = typeof state.pipeline === "string" && state.pipeline ? { kind: "pipeline", id: state.pipeline } : typeof state.agent === "string" && state.agent ? { kind: "agent", id: state.agent } : { kind: "none", id: null };
49472
+ const recipes = Array.isArray(doc.recipes) ? doc.recipes.map((r) => String(r.id ?? "")) : [];
49473
+ if (opts.json) {
49474
+ console.log(JSON.stringify({ id, current, canChange: lock === null, lock, recipes }, null, 2));
49475
+ return;
49476
+ }
49477
+ console.log(`${import_chalk15.default.bold(id)} runs ${current.id ? import_chalk15.default.cyan(`${current.kind} ${current.id}`) : import_chalk15.default.dim("(nothing)")}`);
49478
+ if (lock) {
49479
+ console.log(import_chalk15.default.dim(` locked: ${lock} \u2014 use \`aidlc epic step add/remove\` instead`));
49480
+ } else {
49481
+ console.log(import_chalk15.default.dim(" not started yet, so the workflow can still be changed:"));
49482
+ console.log(import_chalk15.default.dim(` aidlc epic workflow ${id} --recipe <${recipes.join("|") || "recipe"}>`));
49483
+ }
49484
+ return;
49485
+ }
49486
+ let config;
49487
+ try {
49488
+ config = (0, import_core19.validateWorkspace)(doc, ".aidlc/workspace.yaml");
49489
+ } catch (err) {
49490
+ console.error(import_chalk15.default.red("workspace.yaml is invalid \u2014 not changing anything:"));
49491
+ console.error(import_chalk15.default.dim(err instanceof Error ? err.message : String(err)));
49492
+ process.exit(1);
49493
+ }
49494
+ const target = opts.recipe ? { kind: "recipe", id: opts.recipe } : { kind: "pipeline", id: opts.pipeline };
49495
+ try {
49496
+ const plan = (0, import_core19.planEpicWorkflowSwitch)({ workspaceRoot: root, doc, config, epicId: id, target });
49497
+ (0, import_core19.stageEpicWorkflowSwitch)(doc, plan);
49498
+ (0, import_core19.validateWorkspace)(doc, ".aidlc/workspace.yaml");
49499
+ writeYaml(root, doc);
49500
+ const result = (0, import_core19.applyEpicWorkflowSwitch)(root, doc, plan);
49501
+ if (opts.json) {
49502
+ console.log(JSON.stringify(result, null, 2));
49503
+ return;
49504
+ }
49505
+ console.log(import_chalk15.default.green("\u2714") + ` ${import_chalk15.default.bold(id)} now runs ${import_chalk15.default.cyan(result.pipelineId)}` + (target.kind === "recipe" ? import_chalk15.default.dim(` (from recipe ${target.id})`) : ""));
49506
+ console.log(import_chalk15.default.dim(` steps: ${result.agents.join(" \u2192 ")}`));
49507
+ if (result.previousPipelineId && result.previousPipelineId !== result.pipelineId) {
49508
+ console.log(import_chalk15.default.dim(` was: ${result.previousPipelineId}`));
49509
+ }
49510
+ if (result.removedPipelineFile) {
49511
+ console.log(import_chalk15.default.dim(` removed ${path12.relative(root, result.removedPipelineFile)} \u2014 the shared pipeline takes over`));
49512
+ }
49513
+ } catch (err) {
49514
+ if (err instanceof import_core19.EpicWorkflowSwitchError || err instanceof import_core19.PipelineAssembleError) {
49515
+ console.error(import_chalk15.default.red(err.message));
49516
+ process.exit(1);
49517
+ }
49518
+ throw err;
49519
+ }
49520
+ });
48517
49521
  const pipelineCmd = cmd.command("pipeline").description("Move epic-owned pipelines out of the shared workspace.yaml");
48518
49522
  pipelineCmd.command("extract").description(
48519
49523
  "Move each epic's pipeline out of .aidlc/workspace.yaml into\n <state.root>/<epic>/pipeline.yaml. Only pipelines an epic names in its\n own state.json move; shared and hand-authored ones stay put."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delete101020/aidlc",
3
- "version": "4.0.10",
3
+ "version": "4.0.12",
4
4
  "description": "AIDLC Native CLI, a fork of AIDLC — drive Claude through the six-stage AI-Native SDLC Playbook: Plan → Design → Build → Test → Deploy → Maintain, with artifact gates, requirements traceability, an incident loop back to Plan, and Spec Kit support.",
5
5
  "license": "MIT",
6
6
  "repository": {