@delete101020/aidlc 4.0.26 → 4.0.28

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: epic-context
3
- description: Load and maintain an epic's compact memory so continuing it (with any agent) is cheap on tokens. Reads docs/epics/<epic>/epic-memory.json FIRST as primary context, appends durable decisions/constraints as you work, and records a reflection on how to prompt/work better next time. Invoke /epic-context <epic> when starting or continuing an epic.
3
+ description: Load and maintain an epic's compact memory so continuing it (with any agent) is cheap on tokens. Reads docs/epics/<epic>/epic-memory.json FIRST as primary context, appends durable decisions/constraints as you work, and records a reflection on how to prompt/work better next time. Invoke /epic-context <epic> when starting or continuing an epic (no id = the active epic from .aidlc/user.yaml).
4
4
  ---
5
5
 
6
6
  # /epic-context — cheap, portable epic memory
@@ -20,9 +20,16 @@ Each write is auto-attributed (git identity, hostname fallback) + timestamped.
20
20
 
21
21
  ## Arguments
22
22
 
23
- `/epic-context <epic>` — e.g. `/epic-context EPIC-001`. Paths are relative to the
23
+ `/epic-context [epic]` — e.g. `/epic-context EPIC-001`. Paths are relative to the
24
24
  open project: `EPIC_DIR = docs/epics/<epic>`.
25
25
 
26
+ No id given → use the **active epic**: the `active_epic:` line of
27
+ `.aidlc/user.yaml` (set from the VS Code status bar / Go to Epic, or with
28
+ `aidlc epic use <id>`). Read it with
29
+ `grep -m1 '^active_epic:' .aidlc/user.yaml | sed 's/^active_epic:[[:space:]]*//'`.
30
+ Say which epic you picked in one line. If there is no active epic either, ask
31
+ for the id — don't guess from the epics folder.
32
+
26
33
  ## On start (continuing an epic)
27
34
 
28
35
  1. **Load memory first**: `$M show "$EPIC_DIR"`.
package/dist/bundle.js CHANGED
@@ -19806,7 +19806,7 @@ var require_WorkspaceSchema = __commonJS({
19806
19806
  // invalidate every workspace.yaml already on disk and buy nothing
19807
19807
  // (MULTI_PROVIDER_ALIGNMENT.md P0/D1). A closed enum is what lets
19808
19808
  // `aidlc validate` reject a typo instead of failing at spawn time.
19809
- runner: zod_1.z.enum(["default", "custom", "codex", "gemini"]).default("default"),
19809
+ runner: zod_1.z.enum(["default", "custom", "codex", "gemini", "copilot"]).default("default"),
19810
19810
  /** Required when runner === 'custom'. Relative path to .js or .ts file. */
19811
19811
  runner_path: zod_1.z.string().optional(),
19812
19812
  /** Per-agent env overrides (layered over workspace.environment). */
@@ -19981,6 +19981,13 @@ var require_WorkspaceSchema = __commonJS({
19981
19981
  * pipelines, which are their own source.
19982
19982
  */
19983
19983
  derived_from: zod_1.z.string().min(1).optional(),
19984
+ /**
19985
+ * Set by {@link assemblePipeline} to the recipe that picked the steps. A
19986
+ * creation-time label only — `steps` stays the source of truth, and a hand
19987
+ * edit may leave the two apart. Needed because two recipes can select the
19988
+ * same steps, so the recipe cannot be recovered from the step list.
19989
+ */
19990
+ recipe: zod_1.z.string().min(1).optional(),
19984
19991
  steps: zod_1.z.array(PipelineStepSchema).min(1),
19985
19992
  on_failure: zod_1.z.enum(["stop", "continue"]).default("stop"),
19986
19993
  budget: PipelineBudgetSchema.optional()
@@ -20361,6 +20368,7 @@ var require_PipelineAssembler = __commonJS({
20361
20368
  exports2.PipelineAssembleError = void 0;
20362
20369
  exports2.assemblePipeline = assemblePipeline4;
20363
20370
  exports2.recipePipelineId = recipePipelineId4;
20371
+ exports2.pipelineRecipeLabel = pipelineRecipeLabel;
20364
20372
  var WorkspaceSchema_1 = require_WorkspaceSchema();
20365
20373
  var PipelineAssembleError4 = class extends Error {
20366
20374
  constructor(message) {
@@ -20448,6 +20456,7 @@ var require_PipelineAssembler = __commonJS({
20448
20456
  const assembled = {
20449
20457
  id: opts.pipelineId ?? recipe.id,
20450
20458
  derived_from: source.id,
20459
+ recipe: recipe.id,
20451
20460
  steps,
20452
20461
  on_failure: source.on_failure
20453
20462
  };
@@ -20478,6 +20487,20 @@ var require_PipelineAssembler = __commonJS({
20478
20487
  }
20479
20488
  }
20480
20489
  }
20490
+ function pipelineRecipeLabel(pipeline, recipes) {
20491
+ if (typeof pipeline.recipe !== "string" || !pipeline.recipe) {
20492
+ return void 0;
20493
+ }
20494
+ const id = pipeline.recipe;
20495
+ const recipe = recipes?.find((r) => r.id === id);
20496
+ if (!recipe || !Array.isArray(recipe.steps) || !Array.isArray(pipeline.steps)) {
20497
+ return { id, modified: false };
20498
+ }
20499
+ const own = pipeline.steps.map((s) => (0, WorkspaceSchema_1.stepDagId)(s));
20500
+ const listed = recipe.steps.map(String);
20501
+ const modified = own.length !== listed.length || own.some((s, i) => s !== listed[i]);
20502
+ return { id, modified };
20503
+ }
20481
20504
  function resolveSource(config, recipe) {
20482
20505
  if (recipe.from) {
20483
20506
  const found = config.pipelines.find((p) => p.id === recipe.from);
@@ -25980,7 +26003,7 @@ var require_EpicArtifactCommit = __commonJS({
25980
26003
  if (!fs14.existsSync(p) || !fs14.statSync(p).isFile()) {
25981
26004
  continue;
25982
26005
  }
25983
- const rel = path17.relative(repoRoot, p).split(path17.sep).join("/");
26006
+ const rel = path17.relative(repoRoot, fs14.realpathSync(p)).split(path17.sep).join("/");
25984
26007
  if (rel.startsWith("../")) {
25985
26008
  continue;
25986
26009
  }
@@ -27210,6 +27233,10 @@ var require_FollowUps = __commonJS({
27210
27233
  if (!source) {
27211
27234
  return typeof own.id === "string" && own.id ? { kind: "pipeline", id: own.id } : void 0;
27212
27235
  }
27236
+ const labelled = typeof own.recipe === "string" ? own.recipe : "";
27237
+ if (labelled && recipes.some((r) => r.id === labelled)) {
27238
+ return { kind: "recipe", id: labelled };
27239
+ }
27213
27240
  const steps = stepIds(own.steps).join("\n");
27214
27241
  const recipe = recipes.find((r) => (r.from === void 0 || r.from === source) && stepIds(r.steps).join("\n") === steps);
27215
27242
  if (recipe && typeof recipe.id === "string") {
@@ -28887,6 +28914,170 @@ ${task}
28887
28914
  }
28888
28915
  });
28889
28916
 
28917
+ // ../core/dist/runner/CopilotRunner.js
28918
+ var require_CopilotRunner = __commonJS({
28919
+ "../core/dist/runner/CopilotRunner.js"(exports2) {
28920
+ "use strict";
28921
+ Object.defineProperty(exports2, "__esModule", { value: true });
28922
+ exports2.CopilotRunner = void 0;
28923
+ exports2.copilotGraphServer = copilotGraphServer;
28924
+ exports2.copilotMcpConfig = copilotMcpConfig;
28925
+ var child_process_1 = require("child_process");
28926
+ var CodexRunner_1 = require_CodexRunner();
28927
+ var claudeHome_1 = require_claudeHome();
28928
+ var mcp_1 = require_mcp();
28929
+ var ndjson_1 = require_ndjson();
28930
+ var models_1 = require_models();
28931
+ var resolveCommand_1 = require_resolveCommand();
28932
+ var GRAPH_SERVERS2 = ["ast-graph", "codegraph"];
28933
+ var CopilotRunner2 = class {
28934
+ opts;
28935
+ /**
28936
+ * `projectInstructions` is `false` although Copilot reads a root `CLAUDE.md`
28937
+ * and `AGENTS.md` by itself: it does not read `.claude/CLAUDE.md`, and the
28938
+ * composer cannot know which layout a repo uses. Inlining costs a duplicate
28939
+ * when the file is at the root; not inlining would cost the conventions when
28940
+ * it is not. `CLAUDE.md` is preferred over `AGENTS.md` because a repo that
28941
+ * keeps both usually has tool-generated boilerplate in the latter.
28942
+ *
28943
+ * `astGraph` is `true` because the runner attaches the server on every run
28944
+ * that has one registered; `doctor` reports the workspaces that do not.
28945
+ */
28946
+ capabilities = {
28947
+ persona: false,
28948
+ projectInstructions: false,
28949
+ astGraph: true,
28950
+ instructionFile: "CLAUDE.md"
28951
+ };
28952
+ constructor(opts = {}) {
28953
+ this.opts = opts;
28954
+ }
28955
+ async run(ctx) {
28956
+ const bin = this.opts.copilotBin ?? "copilot";
28957
+ const model = (0, models_1.resolveProviderModel)("copilot", ctx.model, ctx.modelAliases);
28958
+ const graph = (this.opts.graphServer ?? copilotGraphServer)(ctx.workspaceRoot);
28959
+ const args = [
28960
+ // Required for non-interactive runs: without it every tool call waits for
28961
+ // a confirmation nobody is there to give.
28962
+ "--allow-all-tools",
28963
+ "--output-format",
28964
+ "json",
28965
+ "--no-color",
28966
+ ...this.opts.keepBuiltinMcps ? [] : ["--disable-builtin-mcps"],
28967
+ ...graph ? ["--additional-mcp-config", copilotMcpConfig(graph)] : [],
28968
+ ...model ? ["--model", model] : [],
28969
+ ...this.opts.extraArgs ?? []
28970
+ ];
28971
+ const cmd = (0, resolveCommand_1.resolveCommand)(bin);
28972
+ const proc = (0, child_process_1.spawn)(cmd.command, [...cmd.args, ...args], {
28973
+ cwd: ctx.workspaceRoot,
28974
+ env: { ...process.env, ...ctx.env },
28975
+ stdio: ["pipe", "pipe", "pipe"]
28976
+ });
28977
+ let streamed = "";
28978
+ let lastMessage = "";
28979
+ const deltaSeen = /* @__PURE__ */ new Set();
28980
+ let premiumRequests;
28981
+ const emit = (text) => {
28982
+ streamed += text;
28983
+ ctx.onOutput(text);
28984
+ };
28985
+ const handle = (evt) => {
28986
+ const d = evt.data ?? {};
28987
+ switch (evt.type) {
28988
+ case "assistant.message_delta":
28989
+ if (d.deltaContent) {
28990
+ if (d.messageId) {
28991
+ deltaSeen.add(d.messageId);
28992
+ }
28993
+ emit(d.deltaContent);
28994
+ }
28995
+ return;
28996
+ case "assistant.message":
28997
+ if (!d.content) {
28998
+ return;
28999
+ }
29000
+ lastMessage = d.content;
29001
+ if (!d.messageId || !deltaSeen.has(d.messageId)) {
29002
+ emit(d.content);
29003
+ }
29004
+ emit("\n");
29005
+ return;
29006
+ case "tool.execution_start":
29007
+ if (d.toolName) {
29008
+ ctx.onOutput(`[${d.toolTitle ?? d.toolName}]
29009
+ `);
29010
+ }
29011
+ return;
29012
+ case "session.mcp_server_status_changed":
29013
+ if (d.status === "failed") {
29014
+ ctx.onError(`MCP server ${d.serverName ?? "?"} failed to start: ${d.error ?? "unknown error"}
29015
+ `);
29016
+ }
29017
+ return;
29018
+ case "result":
29019
+ if (typeof evt.usage?.premiumRequests === "number") {
29020
+ premiumRequests = evt.usage.premiumRequests;
29021
+ }
29022
+ return;
29023
+ default:
29024
+ if (evt.type?.endsWith(".error")) {
29025
+ ctx.onError(`${d.message ?? d.error ?? "copilot reported an error"}
29026
+ `);
29027
+ }
29028
+ }
29029
+ };
29030
+ const sink = (0, ndjson_1.createJsonSink)(handle, (line) => ctx.onOutput(line + "\n"));
29031
+ proc.stdout.on("data", (d) => sink.push(d.toString("utf8")));
29032
+ proc.stderr.on("data", (d) => ctx.onError(d.toString("utf8")));
29033
+ proc.stdin.on("error", () => {
29034
+ });
29035
+ proc.stdin.end((0, CodexRunner_1.buildPrompt)(ctx));
29036
+ return new Promise((resolve5) => {
29037
+ proc.on("error", (err) => {
29038
+ ctx.onError(`Failed to spawn ${bin}: ${err.message}
29039
+ `);
29040
+ resolve5({ success: false, output: "" });
29041
+ });
29042
+ proc.on("close", (code) => {
29043
+ sink.flush();
29044
+ resolve5({
29045
+ success: code === 0,
29046
+ output: lastMessage || streamed,
29047
+ // No costUsd or usage: Copilot bills premium requests, and turning
29048
+ // those into dollars or tokens would be a guess (P0, D5).
29049
+ ...premiumRequests !== void 0 ? { data: { premiumRequests } } : {}
29050
+ });
29051
+ });
29052
+ });
29053
+ }
29054
+ };
29055
+ exports2.CopilotRunner = CopilotRunner2;
29056
+ function copilotGraphServer(workspaceRoot) {
29057
+ for (const name of GRAPH_SERVERS2) {
29058
+ const server = (0, mcp_1.readProjectMcpServer)(workspaceRoot, name, (0, claudeHome_1.claudeJsonPath)());
29059
+ if (server) {
29060
+ return server;
29061
+ }
29062
+ }
29063
+ return null;
29064
+ }
29065
+ function copilotMcpConfig(server) {
29066
+ return JSON.stringify({
29067
+ mcpServers: {
29068
+ [server.name]: {
29069
+ type: "local",
29070
+ command: server.command,
29071
+ args: server.args,
29072
+ ...server.env && Object.keys(server.env).length ? { env: server.env } : {},
29073
+ tools: ["*"]
29074
+ }
29075
+ }
29076
+ });
29077
+ }
29078
+ }
29079
+ });
29080
+
28890
29081
  // ../core/dist/runner/types.js
28891
29082
  var require_types = __commonJS({
28892
29083
  "../core/dist/runner/types.js"(exports2) {
@@ -29071,6 +29262,7 @@ var require_RunnerRegistry = __commonJS({
29071
29262
  var path17 = __importStar(require("path"));
29072
29263
  var DefaultRunner_1 = require_DefaultRunner();
29073
29264
  var CodexRunner_1 = require_CodexRunner();
29265
+ var CopilotRunner_1 = require_CopilotRunner();
29074
29266
  var CustomRunnerLoader_1 = require_CustomRunnerLoader();
29075
29267
  var RunnerRegistry = class {
29076
29268
  builtins = /* @__PURE__ */ new Map();
@@ -29080,6 +29272,7 @@ var require_RunnerRegistry = __commonJS({
29080
29272
  this.loader = new CustomRunnerLoader_1.CustomRunnerLoader(workspaceRoot);
29081
29273
  this.register("default", new DefaultRunner_1.DefaultRunner());
29082
29274
  this.register("codex", new CodexRunner_1.CodexRunner());
29275
+ this.register("copilot", new CopilotRunner_1.CopilotRunner());
29083
29276
  }
29084
29277
  /** Add or replace a builtin runner. */
29085
29278
  register(id, runner) {
@@ -30209,8 +30402,9 @@ var require_userConfig = __commonJS({
30209
30402
  exports2.userConfigPath = userConfigPath;
30210
30403
  exports2.readUserConfig = readUserConfig3;
30211
30404
  exports2.writeUserEpicIdPrefix = writeUserEpicIdPrefix;
30405
+ exports2.updateUserConfig = updateUserConfig;
30212
30406
  exports2.readGitUserName = readGitUserName2;
30213
- exports2.ensureUserConfigIgnored = ensureUserConfigIgnored;
30407
+ exports2.ensureUserConfigIgnored = ensureUserConfigIgnored2;
30214
30408
  var child_process_1 = require("child_process");
30215
30409
  var fs_1 = __importDefault(require("fs"));
30216
30410
  var path_1 = __importDefault(require("path"));
@@ -30236,15 +30430,22 @@ var require_userConfig = __commonJS({
30236
30430
  "# Settings for this checkout only \u2014 NOT committed (see .gitignore).",
30237
30431
  "# epic_id_prefix is your own two letters, so the epics you open are filed",
30238
30432
  "# under you and never collide with a colleague's numbering.",
30433
+ "# active_epic / recent_epics / watched_epics / epic_sort are your own; the",
30434
+ "# extension and `aidlc epic use|watch` keep them, and hand edits are fine.",
30239
30435
  ""
30240
30436
  ].join("\n");
30241
30437
  function writeUserEpicIdPrefix(root, prefix) {
30438
+ updateUserConfig(root, (doc) => {
30439
+ if (prefix) {
30440
+ doc.epic_id_prefix = prefix.toUpperCase();
30441
+ } else {
30442
+ delete doc.epic_id_prefix;
30443
+ }
30444
+ });
30445
+ }
30446
+ function updateUserConfig(root, mutate) {
30242
30447
  const doc = readUserConfig3(root) ?? {};
30243
- if (prefix) {
30244
- doc.epic_id_prefix = prefix.toUpperCase();
30245
- } else {
30246
- delete doc.epic_id_prefix;
30247
- }
30448
+ mutate(doc);
30248
30449
  const file = userConfigPath(root);
30249
30450
  fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
30250
30451
  if (Object.keys(doc).length === 0) {
@@ -30267,7 +30468,7 @@ var require_userConfig = __commonJS({
30267
30468
  return null;
30268
30469
  }
30269
30470
  }
30270
- function ensureUserConfigIgnored(root) {
30471
+ function ensureUserConfigIgnored2(root) {
30271
30472
  const file = path_1.default.join(root, ".gitignore");
30272
30473
  try {
30273
30474
  const existing = fs_1.default.existsSync(file) ? fs_1.default.readFileSync(file, "utf8") : "";
@@ -30277,7 +30478,7 @@ var require_userConfig = __commonJS({
30277
30478
  }
30278
30479
  const sep4 = existing === "" || existing.endsWith("\n") ? "" : "\n";
30279
30480
  fs_1.default.writeFileSync(file, `${existing}${sep4}
30280
- # Per-checkout AIDLC settings (epic_id_prefix) \u2014 never shared
30481
+ # Per-checkout AIDLC settings (epic_id_prefix, active/watched epics) \u2014 never shared
30281
30482
  ${exports2.USER_CONFIG_IGNORE_LINE}
30282
30483
  `, "utf8");
30283
30484
  return true;
@@ -30288,6 +30489,134 @@ ${exports2.USER_CONFIG_IGNORE_LINE}
30288
30489
  }
30289
30490
  });
30290
30491
 
30492
+ // ../core/dist/loader/epicFocus.js
30493
+ var require_epicFocus = __commonJS({
30494
+ "../core/dist/loader/epicFocus.js"(exports2) {
30495
+ "use strict";
30496
+ Object.defineProperty(exports2, "__esModule", { value: true });
30497
+ exports2.RECENT_EPICS_LIMIT = exports2.EPIC_SORT_KEY = exports2.WATCHED_EPICS_KEY = exports2.RECENT_EPICS_KEY = exports2.ACTIVE_EPIC_KEY = void 0;
30498
+ exports2.epicFocusFrom = epicFocusFrom;
30499
+ exports2.readEpicFocus = readEpicFocus2;
30500
+ exports2.pushRecent = pushRecent;
30501
+ exports2.recordEpicOpened = recordEpicOpened;
30502
+ exports2.setActiveEpic = setActiveEpic2;
30503
+ exports2.setEpicWatched = setEpicWatched2;
30504
+ exports2.epicSortFrom = epicSortFrom;
30505
+ exports2.readEpicSort = readEpicSort;
30506
+ exports2.setEpicSort = setEpicSort;
30507
+ exports2.epicIdFromBranch = epicIdFromBranch;
30508
+ var userConfig_1 = require_userConfig();
30509
+ exports2.ACTIVE_EPIC_KEY = "active_epic";
30510
+ exports2.RECENT_EPICS_KEY = "recent_epics";
30511
+ exports2.WATCHED_EPICS_KEY = "watched_epics";
30512
+ exports2.EPIC_SORT_KEY = "epic_sort";
30513
+ exports2.RECENT_EPICS_LIMIT = 10;
30514
+ function asId(v) {
30515
+ return typeof v === "string" && v.trim() !== "" ? v.trim() : null;
30516
+ }
30517
+ function asIdList(v) {
30518
+ if (!Array.isArray(v)) {
30519
+ return [];
30520
+ }
30521
+ const out2 = [];
30522
+ for (const item of v) {
30523
+ const id = asId(item);
30524
+ if (id && !out2.includes(id)) {
30525
+ out2.push(id);
30526
+ }
30527
+ }
30528
+ return out2;
30529
+ }
30530
+ function epicFocusFrom(doc) {
30531
+ return {
30532
+ active: asId(doc?.[exports2.ACTIVE_EPIC_KEY]),
30533
+ recent: asIdList(doc?.[exports2.RECENT_EPICS_KEY]),
30534
+ watched: asIdList(doc?.[exports2.WATCHED_EPICS_KEY])
30535
+ };
30536
+ }
30537
+ function readEpicFocus2(root) {
30538
+ return epicFocusFrom((0, userConfig_1.readUserConfig)(root));
30539
+ }
30540
+ function pushRecent(list, id, limit = exports2.RECENT_EPICS_LIMIT) {
30541
+ return [id, ...list.filter((x) => x !== id)].slice(0, limit);
30542
+ }
30543
+ function writeList(doc, key, list) {
30544
+ if (list.length > 0) {
30545
+ doc[key] = list;
30546
+ } else {
30547
+ delete doc[key];
30548
+ }
30549
+ }
30550
+ function recordEpicOpened(root, id) {
30551
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30552
+ writeList(doc, exports2.RECENT_EPICS_KEY, pushRecent(asIdList(doc[exports2.RECENT_EPICS_KEY]), id));
30553
+ });
30554
+ }
30555
+ function setActiveEpic2(root, id) {
30556
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30557
+ if (id) {
30558
+ doc[exports2.ACTIVE_EPIC_KEY] = id;
30559
+ writeList(doc, exports2.RECENT_EPICS_KEY, pushRecent(asIdList(doc[exports2.RECENT_EPICS_KEY]), id));
30560
+ } else {
30561
+ delete doc[exports2.ACTIVE_EPIC_KEY];
30562
+ }
30563
+ });
30564
+ }
30565
+ function setEpicWatched2(root, id, watched) {
30566
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30567
+ const list = asIdList(doc[exports2.WATCHED_EPICS_KEY]);
30568
+ if (watched === list.includes(id)) {
30569
+ return;
30570
+ }
30571
+ writeList(doc, exports2.WATCHED_EPICS_KEY, watched ? [...list, id] : list.filter((x) => x !== id));
30572
+ });
30573
+ }
30574
+ function epicSortFrom(doc) {
30575
+ const v = doc?.[exports2.EPIC_SORT_KEY];
30576
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
30577
+ return null;
30578
+ }
30579
+ const by = asId(v.by);
30580
+ return by ? { by, reversed: v.reversed === true } : null;
30581
+ }
30582
+ function readEpicSort(root) {
30583
+ return epicSortFrom((0, userConfig_1.readUserConfig)(root));
30584
+ }
30585
+ function setEpicSort(root, pref) {
30586
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30587
+ doc[exports2.EPIC_SORT_KEY] = pref.reversed ? { by: pref.by, reversed: true } : { by: pref.by };
30588
+ });
30589
+ }
30590
+ function epicIdFromBranch(branch, knownIds) {
30591
+ if (!branch) {
30592
+ return null;
30593
+ }
30594
+ const hay = branch.toLowerCase();
30595
+ let best = null;
30596
+ for (const id of knownIds) {
30597
+ const needle = id.toLowerCase();
30598
+ let from = 0;
30599
+ for (; ; ) {
30600
+ const at = hay.indexOf(needle, from);
30601
+ if (at < 0) {
30602
+ break;
30603
+ }
30604
+ const before = at === 0 ? "" : hay[at - 1];
30605
+ const after = hay[at + needle.length] ?? "";
30606
+ if (!/[a-z0-9]/.test(before) && !/[0-9]/.test(after)) {
30607
+ if (!best || id.length > best.length) {
30608
+ best = id;
30609
+ }
30610
+ break;
30611
+ }
30612
+ from = at + 1;
30613
+ }
30614
+ }
30615
+ return best;
30616
+ }
30617
+ }
30618
+ });
30619
+
30291
30620
  // ../core/dist/presets/commandBodyFreshness.js
30292
30621
  var require_commandBodyFreshness = __commonJS({
30293
30622
  "../core/dist/presets/commandBodyFreshness.js"(exports2) {
@@ -30642,6 +30971,98 @@ var require_budget = __commonJS({
30642
30971
  }
30643
30972
  });
30644
30973
 
30974
+ // ../core/dist/runs/producesSnapshot.js
30975
+ var require_producesSnapshot = __commonJS({
30976
+ "../core/dist/runs/producesSnapshot.js"(exports2) {
30977
+ "use strict";
30978
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
30979
+ if (k2 === void 0) k2 = k;
30980
+ var desc = Object.getOwnPropertyDescriptor(m, k);
30981
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30982
+ desc = { enumerable: true, get: function() {
30983
+ return m[k];
30984
+ } };
30985
+ }
30986
+ Object.defineProperty(o, k2, desc);
30987
+ } : function(o, m, k, k2) {
30988
+ if (k2 === void 0) k2 = k;
30989
+ o[k2] = m[k];
30990
+ });
30991
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
30992
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30993
+ } : function(o, v) {
30994
+ o["default"] = v;
30995
+ });
30996
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
30997
+ var ownKeys = function(o) {
30998
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
30999
+ var ar = [];
31000
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
31001
+ return ar;
31002
+ };
31003
+ return ownKeys(o);
31004
+ };
31005
+ return function(mod) {
31006
+ if (mod && mod.__esModule) return mod;
31007
+ var result = {};
31008
+ if (mod != null) {
31009
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31010
+ }
31011
+ __setModuleDefault(result, mod);
31012
+ return result;
31013
+ };
31014
+ }();
31015
+ Object.defineProperty(exports2, "__esModule", { value: true });
31016
+ exports2.snapshotProduces = snapshotProduces;
31017
+ exports2.unchangedProduces = unchangedProduces;
31018
+ var crypto_1 = require("crypto");
31019
+ var fs14 = __importStar(require("fs"));
31020
+ var path17 = __importStar(require("path"));
31021
+ var WorkspaceSchema_1 = require_WorkspaceSchema();
31022
+ var RunState_1 = require_RunState();
31023
+ function snapshotProduces(args) {
31024
+ const snap = /* @__PURE__ */ new Map();
31025
+ const stepCfg = args.pipeline.steps[args.stepIdx];
31026
+ if (!stepCfg) {
31027
+ return snap;
31028
+ }
31029
+ for (const p of (0, WorkspaceSchema_1.normalizeStep)(stepCfg).produces) {
31030
+ const rel = (0, RunState_1.resolvePath)(p, args.state.context);
31031
+ snap.set(rel, fingerprint(absolute(args.workspaceRoot, rel)));
31032
+ }
31033
+ return snap;
31034
+ }
31035
+ function unchangedProduces(before, workspaceRoot) {
31036
+ const stale = [];
31037
+ for (const [rel, prior] of before) {
31038
+ const now = fingerprint(absolute(workspaceRoot, rel));
31039
+ if (now === null) {
31040
+ continue;
31041
+ }
31042
+ if (prior === null || prior !== now) {
31043
+ return [];
31044
+ }
31045
+ stale.push(rel);
31046
+ }
31047
+ return stale;
31048
+ }
31049
+ function absolute(root, rel) {
31050
+ return path17.isAbsolute(rel) ? rel : path17.join(root, rel);
31051
+ }
31052
+ function fingerprint(abs) {
31053
+ try {
31054
+ const st = fs14.statSync(abs);
31055
+ if (!st.isFile()) {
31056
+ return `dir:${st.mtimeMs}`;
31057
+ }
31058
+ return (0, crypto_1.createHash)("sha1").update(fs14.readFileSync(abs)).digest("hex");
31059
+ } catch {
31060
+ return null;
31061
+ }
31062
+ }
31063
+ }
31064
+ });
31065
+
30645
31066
  // ../core/dist/runs/pricing.js
30646
31067
  var require_pricing = __commonJS({
30647
31068
  "../core/dist/runs/pricing.js"(exports2) {
@@ -30866,6 +31287,7 @@ var require_execEngine = __commonJS({
30866
31287
  var EpicScaffold_1 = require_EpicScaffold();
30867
31288
  var runProgress_1 = require_runProgress();
30868
31289
  var budget_1 = require_budget();
31290
+ var producesSnapshot_1 = require_producesSnapshot();
30869
31291
  var pricing_1 = require_pricing();
30870
31292
  var models_1 = require_models();
30871
31293
  var WorkspaceSchema_1 = require_WorkspaceSchema();
@@ -31091,6 +31513,7 @@ var require_execEngine = __commonJS({
31091
31513
  });
31092
31514
  const aliases = (0, pricing_1.providerAliases)(ws.config.providers, agent.runner);
31093
31515
  const resolvedModel = (0, models_1.resolveProviderModel)(agent.runner, agent.model, aliases);
31516
+ const producedBefore = (0, producesSnapshot_1.snapshotProduces)({ state, pipeline, workspaceRoot: root, stepIdx });
31094
31517
  const result = await runner.run({
31095
31518
  skill: skillText,
31096
31519
  env,
@@ -31106,6 +31529,15 @@ var require_execEngine = __commonJS({
31106
31529
  hooks.onStepFailed?.({ stepIdx, agent: agentId, message: `Step "${agentId}" failed (non-zero exit).` });
31107
31530
  return false;
31108
31531
  }
31532
+ const stale = (0, producesSnapshot_1.unchangedProduces)(producedBefore, root);
31533
+ if (stale.length > 0) {
31534
+ hooks.onStepFailed?.({
31535
+ stepIdx,
31536
+ agent: agentId,
31537
+ message: `Step "${agentId}" reported success but left its artifacts unchanged: ${stale.join(", ")}. The agent may have failed to write them. Check its output, then rerun the step.`
31538
+ });
31539
+ return false;
31540
+ }
31109
31541
  let next;
31110
31542
  let freshState;
31111
31543
  try {
@@ -33534,13 +33966,13 @@ var require_dist = __commonJS({
33534
33966
  "../core/dist/index.js"(exports2) {
33535
33967
  "use strict";
33536
33968
  Object.defineProperty(exports2, "__esModule", { value: true });
33537
- 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.resolveStepSkills = exports2.stepSkillAlternatives = exports2.stepDagId = exports2.stepAgentId = exports2.producesEntries = exports2.normalizeStep = exports2.WorkspaceValidationError = exports2.validateWorkspace = exports2.WorkspaceSchema = void 0;
33538
- 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.MANUAL_FOLLOW_UP_KEY_PREFIX = exports2.followUpDefaults = exports2.nextManualFollowUpKey = exports2.withFollowUpProvenance = 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 = exports2.followUpEpicId = exports2.renderIntentMarkdown = exports2.openFollowUpEpic = void 0;
33539
- 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 = exports2.epicPipelinePath = exports2.EPIC_PIPELINE_FILENAME = exports2.unusedAfterStepRemoval = exports2.skillReferences = exports2.agentReferences = exports2.renameSkillRefs = exports2.renameAgentRefs = void 0;
33540
- exports2.dirtyUpstreamOf = exports2.rerunApprovedStep = exports2.canRerunApprovedStep = exports2.chooseStepSkill = 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.claudeModelArg = 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 = void 0;
33541
- 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 = exports2.providerAliases = exports2.ratesFromConfig = exports2.mergeRates = exports2.BUILTIN_RATES = exports2.checkBudget = exports2.weighStepProgress = exports2.isActiveStatus = exports2.isStepOptional = exports2.isRunComplete = exports2.deriveRunProgress = exports2.PipelineRunError = exports2.retryAutoReview = exports2.submitAutoReviewVerdict = void 0;
33542
- 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 = 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 = void 0;
33543
- 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 = void 0;
33969
+ 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.pipelineRecipeLabel = exports2.recipePipelineId = exports2.assemblePipeline = exports2.collectWorkspaceRefIssues = exports2.resolveStepSkills = exports2.stepSkillAlternatives = exports2.stepDagId = exports2.stepAgentId = exports2.producesEntries = exports2.normalizeStep = exports2.WorkspaceValidationError = exports2.validateWorkspace = exports2.WorkspaceSchema = void 0;
33970
+ 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.MANUAL_FOLLOW_UP_KEY_PREFIX = exports2.followUpDefaults = exports2.nextManualFollowUpKey = exports2.withFollowUpProvenance = 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 = exports2.followUpEpicId = exports2.renderIntentMarkdown = exports2.openFollowUpEpic = exports2.openIncidentEpic = void 0;
33971
+ exports2.WATCHED_EPICS_KEY = exports2.RECENT_EPICS_LIMIT = exports2.RECENT_EPICS_KEY = exports2.EPIC_SORT_KEY = exports2.ACTIVE_EPIC_KEY = exports2.writeUserEpicIdPrefix = exports2.userConfigPath = exports2.updateUserConfig = 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 = exports2.epicPipelinePath = exports2.EPIC_PIPELINE_FILENAME = exports2.unusedAfterStepRemoval = exports2.skillReferences = exports2.agentReferences = exports2.renameSkillRefs = exports2.renameAgentRefs = exports2.dropRecipesForPipeline = void 0;
33972
+ 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.copilotMcpConfig = exports2.copilotGraphServer = exports2.CopilotRunner = exports2.CodexRunner = exports2.claudeModelArg = 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.setEpicWatched = exports2.setEpicSort = exports2.setActiveEpic = exports2.recordEpicOpened = exports2.readEpicSort = exports2.readEpicFocus = exports2.pushRecent = exports2.epicSortFrom = exports2.epicIdFromBranch = exports2.epicFocusFrom = void 0;
33973
+ 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.unchangedProduces = exports2.snapshotProduces = exports2.checkBudget = exports2.weighStepProgress = exports2.isActiveStatus = exports2.isStepOptional = exports2.isRunComplete = exports2.deriveRunProgress = exports2.PipelineRunError = exports2.retryAutoReview = exports2.submitAutoReviewVerdict = exports2.dirtyUpstreamOf = exports2.rerunApprovedStep = exports2.canRerunApprovedStep = exports2.chooseStepSkill = 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 = void 0;
33974
+ 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 = exports2.getBuiltinWorkflow = exports2.workflowSlug = exports2.builtinTemplatesRoot = exports2.pipelineCommandId = void 0;
33975
+ 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 = void 0;
33544
33976
  var WorkspaceSchema_1 = require_WorkspaceSchema();
33545
33977
  Object.defineProperty(exports2, "WorkspaceSchema", { enumerable: true, get: function() {
33546
33978
  return WorkspaceSchema_1.WorkspaceSchema;
@@ -33579,6 +34011,9 @@ var require_dist = __commonJS({
33579
34011
  Object.defineProperty(exports2, "recipePipelineId", { enumerable: true, get: function() {
33580
34012
  return PipelineAssembler_1.recipePipelineId;
33581
34013
  } });
34014
+ Object.defineProperty(exports2, "pipelineRecipeLabel", { enumerable: true, get: function() {
34015
+ return PipelineAssembler_1.pipelineRecipeLabel;
34016
+ } });
33582
34017
  Object.defineProperty(exports2, "PipelineAssembleError", { enumerable: true, get: function() {
33583
34018
  return PipelineAssembler_1.PipelineAssembleError;
33584
34019
  } });
@@ -33992,12 +34427,61 @@ var require_dist = __commonJS({
33992
34427
  Object.defineProperty(exports2, "readUserConfig", { enumerable: true, get: function() {
33993
34428
  return userConfig_1.readUserConfig;
33994
34429
  } });
34430
+ Object.defineProperty(exports2, "updateUserConfig", { enumerable: true, get: function() {
34431
+ return userConfig_1.updateUserConfig;
34432
+ } });
33995
34433
  Object.defineProperty(exports2, "userConfigPath", { enumerable: true, get: function() {
33996
34434
  return userConfig_1.userConfigPath;
33997
34435
  } });
33998
34436
  Object.defineProperty(exports2, "writeUserEpicIdPrefix", { enumerable: true, get: function() {
33999
34437
  return userConfig_1.writeUserEpicIdPrefix;
34000
34438
  } });
34439
+ var epicFocus_1 = require_epicFocus();
34440
+ Object.defineProperty(exports2, "ACTIVE_EPIC_KEY", { enumerable: true, get: function() {
34441
+ return epicFocus_1.ACTIVE_EPIC_KEY;
34442
+ } });
34443
+ Object.defineProperty(exports2, "EPIC_SORT_KEY", { enumerable: true, get: function() {
34444
+ return epicFocus_1.EPIC_SORT_KEY;
34445
+ } });
34446
+ Object.defineProperty(exports2, "RECENT_EPICS_KEY", { enumerable: true, get: function() {
34447
+ return epicFocus_1.RECENT_EPICS_KEY;
34448
+ } });
34449
+ Object.defineProperty(exports2, "RECENT_EPICS_LIMIT", { enumerable: true, get: function() {
34450
+ return epicFocus_1.RECENT_EPICS_LIMIT;
34451
+ } });
34452
+ Object.defineProperty(exports2, "WATCHED_EPICS_KEY", { enumerable: true, get: function() {
34453
+ return epicFocus_1.WATCHED_EPICS_KEY;
34454
+ } });
34455
+ Object.defineProperty(exports2, "epicFocusFrom", { enumerable: true, get: function() {
34456
+ return epicFocus_1.epicFocusFrom;
34457
+ } });
34458
+ Object.defineProperty(exports2, "epicIdFromBranch", { enumerable: true, get: function() {
34459
+ return epicFocus_1.epicIdFromBranch;
34460
+ } });
34461
+ Object.defineProperty(exports2, "epicSortFrom", { enumerable: true, get: function() {
34462
+ return epicFocus_1.epicSortFrom;
34463
+ } });
34464
+ Object.defineProperty(exports2, "pushRecent", { enumerable: true, get: function() {
34465
+ return epicFocus_1.pushRecent;
34466
+ } });
34467
+ Object.defineProperty(exports2, "readEpicFocus", { enumerable: true, get: function() {
34468
+ return epicFocus_1.readEpicFocus;
34469
+ } });
34470
+ Object.defineProperty(exports2, "readEpicSort", { enumerable: true, get: function() {
34471
+ return epicFocus_1.readEpicSort;
34472
+ } });
34473
+ Object.defineProperty(exports2, "recordEpicOpened", { enumerable: true, get: function() {
34474
+ return epicFocus_1.recordEpicOpened;
34475
+ } });
34476
+ Object.defineProperty(exports2, "setActiveEpic", { enumerable: true, get: function() {
34477
+ return epicFocus_1.setActiveEpic;
34478
+ } });
34479
+ Object.defineProperty(exports2, "setEpicSort", { enumerable: true, get: function() {
34480
+ return epicFocus_1.setEpicSort;
34481
+ } });
34482
+ Object.defineProperty(exports2, "setEpicWatched", { enumerable: true, get: function() {
34483
+ return epicFocus_1.setEpicWatched;
34484
+ } });
34001
34485
  var strictMode_1 = require_strictMode();
34002
34486
  Object.defineProperty(exports2, "STRICT_MODE_HEADING", { enumerable: true, get: function() {
34003
34487
  return strictMode_1.STRICT_MODE_HEADING;
@@ -34068,6 +34552,16 @@ var require_dist = __commonJS({
34068
34552
  Object.defineProperty(exports2, "CodexRunner", { enumerable: true, get: function() {
34069
34553
  return CodexRunner_1.CodexRunner;
34070
34554
  } });
34555
+ var CopilotRunner_1 = require_CopilotRunner();
34556
+ Object.defineProperty(exports2, "CopilotRunner", { enumerable: true, get: function() {
34557
+ return CopilotRunner_1.CopilotRunner;
34558
+ } });
34559
+ Object.defineProperty(exports2, "copilotGraphServer", { enumerable: true, get: function() {
34560
+ return CopilotRunner_1.copilotGraphServer;
34561
+ } });
34562
+ Object.defineProperty(exports2, "copilotMcpConfig", { enumerable: true, get: function() {
34563
+ return CopilotRunner_1.copilotMcpConfig;
34564
+ } });
34071
34565
  var mcp_1 = require_mcp();
34072
34566
  Object.defineProperty(exports2, "claudeMcpRegistrar", { enumerable: true, get: function() {
34073
34567
  return mcp_1.claudeMcpRegistrar;
@@ -34214,6 +34708,13 @@ var require_dist = __commonJS({
34214
34708
  Object.defineProperty(exports2, "checkBudget", { enumerable: true, get: function() {
34215
34709
  return budget_1.checkBudget;
34216
34710
  } });
34711
+ var producesSnapshot_1 = require_producesSnapshot();
34712
+ Object.defineProperty(exports2, "snapshotProduces", { enumerable: true, get: function() {
34713
+ return producesSnapshot_1.snapshotProduces;
34714
+ } });
34715
+ Object.defineProperty(exports2, "unchangedProduces", { enumerable: true, get: function() {
34716
+ return producesSnapshot_1.unchangedProduces;
34717
+ } });
34217
34718
  var pricing_1 = require_pricing();
34218
34719
  Object.defineProperty(exports2, "BUILTIN_RATES", { enumerable: true, get: function() {
34219
34720
  return pricing_1.BUILTIN_RATES;
@@ -43212,7 +43713,7 @@ var require_package = __commonJS({
43212
43713
  "package.json"(exports2, module2) {
43213
43714
  module2.exports = {
43214
43715
  name: "@delete101020/aidlc",
43215
- version: "4.0.26",
43716
+ version: "4.0.28",
43216
43717
  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.",
43217
43718
  license: "MIT",
43218
43719
  repository: {
@@ -46344,6 +46845,9 @@ function parityChecks(root, ws) {
46344
46845
  if (runner === "codex") {
46345
46846
  return new import_core5.CodexRunner().capabilities;
46346
46847
  }
46848
+ if (runner === "copilot") {
46849
+ return new import_core5.CopilotRunner().capabilities;
46850
+ }
46347
46851
  return import_core5.NO_HARNESS_CAPABILITIES;
46348
46852
  };
46349
46853
  const runners = [...new Set(ws.config.agents.map((a) => a.runner))];
@@ -46393,6 +46897,12 @@ function parityChecks(root, ws) {
46393
46897
  `${engine} via codex`,
46394
46898
  'not in ~/.codex/config.toml \u2014 run "aidlc mcp register --runner codex"'
46395
46899
  ));
46900
+ } else if (runner === "copilot") {
46901
+ const server = (0, import_core5.readProjectMcpServer)(root, engine, (0, import_core5.claudeJsonPath)());
46902
+ checks.push(server ? ok(`${engine} via copilot`, "attached to each run from this project's Claude registration") : warn(
46903
+ `${engine} via copilot`,
46904
+ 'no project registration to attach \u2014 run "AIDLC: Rescan AST Graph" in VS Code'
46905
+ ));
46396
46906
  } else {
46397
46907
  checks.push(warn(
46398
46908
  `${engine} via ${runner}`,
@@ -50216,6 +50726,67 @@ ${plan.length} pipeline(s) would move. Re-run without --dry-run.`));
50216
50726
  });
50217
50727
  console.log((0, import_core19.suggestEpicId)(existing, prefix));
50218
50728
  });
50729
+ const requireKnownEpic = (root, id) => {
50730
+ const all = listEpics(root, readYaml(root)).map((e) => e.id);
50731
+ const hit = all.find((x) => x.toLowerCase() === id.toLowerCase());
50732
+ if (!hit) {
50733
+ console.error(import_chalk15.default.red(`Epic "${id}" not found.`));
50734
+ if (all.length > 0) {
50735
+ console.error(import_chalk15.default.dim(`Available: ${all.join(", ")}`));
50736
+ }
50737
+ process.exit(1);
50738
+ }
50739
+ return hit;
50740
+ };
50741
+ const noteIgnored = (root) => {
50742
+ if ((0, import_core19.ensureUserConfigIgnored)(root)) {
50743
+ console.log(import_chalk15.default.dim(` Added ${import_core19.USER_CONFIG_IGNORE_LINE} to .gitignore \u2014 it is yours, not the team's.`));
50744
+ }
50745
+ };
50746
+ cmd.command("current").description("Print the active epic id (from .aidlc/user.yaml); exits 1 when none is set").option("--json", "Print active, recent and watched as JSON").action((opts, actionCmd) => {
50747
+ const root = resolveWorkspaceRoot(actionCmd);
50748
+ const focus = (0, import_core19.readEpicFocus)(root);
50749
+ if (opts.json) {
50750
+ console.log(JSON.stringify(focus, null, 2));
50751
+ return;
50752
+ }
50753
+ if (!focus.active) {
50754
+ process.exit(1);
50755
+ }
50756
+ console.log(focus.active);
50757
+ });
50758
+ cmd.command("use [id]").description("Make an epic the active one \u2014 what skills like /epic-context pick up when given no id").option("--clear", "unset the active epic").action((id, opts, actionCmd) => {
50759
+ const root = resolveWorkspaceRoot(actionCmd);
50760
+ if (opts.clear) {
50761
+ (0, import_core19.setActiveEpic)(root, null);
50762
+ console.log(import_chalk15.default.green("\u2714") + " No active epic.");
50763
+ return;
50764
+ }
50765
+ if (!id) {
50766
+ console.error(import_chalk15.default.red("Give an epic id, or --clear."));
50767
+ process.exit(1);
50768
+ }
50769
+ const epicId = requireKnownEpic(root, id);
50770
+ (0, import_core19.setActiveEpic)(root, epicId);
50771
+ console.log(import_chalk15.default.green("\u2714") + ` Active epic: ${import_chalk15.default.bold(epicId)}`);
50772
+ noteIgnored(root);
50773
+ });
50774
+ for (const [name, watched] of [["watch", true], ["unwatch", false]]) {
50775
+ cmd.command(`${name} [id]`).description(watched ? "Watch an epic \u2014 listed under My epics in the sidebar; bare `watch` lists them" : "Stop watching an epic").action((id, _opts, actionCmd) => {
50776
+ const root = resolveWorkspaceRoot(actionCmd);
50777
+ if (!id) {
50778
+ const list = (0, import_core19.readEpicFocus)(root).watched;
50779
+ console.log(list.length > 0 ? import_chalk15.default.cyan(list.join(" ")) : import_chalk15.default.dim("(nothing watched)"));
50780
+ return;
50781
+ }
50782
+ const epicId = watched ? requireKnownEpic(root, id) : id;
50783
+ (0, import_core19.setEpicWatched)(root, epicId, watched);
50784
+ console.log(import_chalk15.default.green("\u2714") + ` ${watched ? "Watching" : "Stopped watching"} ${import_chalk15.default.bold(epicId)}`);
50785
+ if (watched) {
50786
+ noteIgnored(root);
50787
+ }
50788
+ });
50789
+ }
50219
50790
  cmd.command("strict <epicId> [value]").description("Show or set an epic's depth of work (strict_mode in its state.json)").action((epicId, value, _opts, actionCmd) => {
50220
50791
  const root = resolveWorkspaceRoot(actionCmd);
50221
50792
  const doc = requireYaml(root);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delete101020/aidlc",
3
- "version": "4.0.26",
3
+ "version": "4.0.28",
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": {