@delete101020/aidlc 4.0.25 → 4.0.27

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.
package/README.md CHANGED
@@ -299,7 +299,19 @@ aidlc epic start <id> --pipeline <pipelineId> # or use an existing pipeline as
299
299
  aidlc epic start <id> … --tag payment --tag "thanh toán" # tag it at creation
300
300
  aidlc epic tag <id> # print this epic's tags
301
301
  aidlc epic tag <id> payment "release q3" # add tags (also --add/--remove/--set/--clear)
302
- ```
302
+ aidlc epic follow-up <parent> --title "…" [--desc "…"] # park work found mid-epic as <parent>-F<n>
303
+ ```
304
+
305
+ **Follow-ups opened by hand.** Work that turns up while an epic is in flight —
306
+ a CR in build finds a second screen that needs the same change — goes into
307
+ `epic follow-up <parent>`. It scaffolds `<parent>-F1` (then `F2`, …) with
308
+ `from_epic` and `follow_up_key` in its `inputs.json`, so the parent's card links
309
+ to it exactly as it does to manifest and incident follow-ups. The child runs
310
+ the way the parent does (the recipe that assembled the parent's pipeline, else
311
+ the pipeline it was derived from) and inherits its tags; override with
312
+ `--recipe` / `--pipeline`, `--tag`, `--no-inherit-tags`, `--epic <id>`. Nothing
313
+ runs: the child waits at its first step until someone starts it. The
314
+ extension's card offers the same as **New follow-up epic**.
303
315
 
304
316
  **Tags** are free text in and one canonical form on disk: uppercase, ASCII,
305
317
  dash-separated — `"thanh toán VNPay"` is stored as `THANH-TOAN-VNPAY`, and the
@@ -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
@@ -27017,14 +27017,18 @@ var require_FollowUps = __commonJS({
27017
27017
  };
27018
27018
  }();
27019
27019
  Object.defineProperty(exports2, "__esModule", { value: true });
27020
- exports2.FollowUpsParseError = exports2.FollowUpManifestSchema = exports2.FollowUpItemSchema = exports2.FOLLOW_UP_INTENT = exports2.FOLLOW_UPS_FILE = void 0;
27020
+ exports2.MANUAL_FOLLOW_UP_KEY_PREFIX = exports2.FollowUpsParseError = exports2.FollowUpManifestSchema = exports2.FollowUpItemSchema = exports2.FOLLOW_UP_INTENT = exports2.FOLLOW_UPS_FILE = void 0;
27021
27021
  exports2.parseFollowUps = parseFollowUps;
27022
27022
  exports2.readEpicFollowUps = readEpicFollowUps;
27023
27023
  exports2.followUpsOf = followUpsOf;
27024
27024
  exports2.followUpChildId = followUpChildId;
27025
27025
  exports2.openManifestFollowUp = openManifestFollowUp;
27026
+ exports2.withFollowUpProvenance = withFollowUpProvenance2;
27027
+ exports2.nextManualFollowUpKey = nextManualFollowUpKey;
27028
+ exports2.followUpDefaults = followUpDefaults2;
27026
27029
  var fs14 = __importStar(require("fs"));
27027
27030
  var path17 = __importStar(require("path"));
27031
+ var yaml = __importStar(require_js_yaml());
27028
27032
  var zod_1 = require_zod();
27029
27033
  var WorkspaceSchema_1 = require_WorkspaceSchema();
27030
27034
  var EpicScaffold_1 = require_EpicScaffold();
@@ -27032,6 +27036,8 @@ var require_FollowUps = __commonJS({
27032
27036
  Object.defineProperty(exports2, "FOLLOW_UPS_FILE", { enumerable: true, get: function() {
27033
27037
  return FollowUpHookSchema_1.FOLLOW_UPS_FILE;
27034
27038
  } });
27039
+ var EpicPipelineStore_1 = require_EpicPipelineStore();
27040
+ var epicTags_1 = require_epicTags();
27035
27041
  exports2.FOLLOW_UP_INTENT = "intent.md";
27036
27042
  var nonEmpty = (field) => zod_1.z.string().trim().min(1, `\`${field}\` is required`);
27037
27043
  exports2.FollowUpItemSchema = zod_1.z.object({
@@ -27158,11 +27164,7 @@ var require_FollowUps = __commonJS({
27158
27164
  target: { kind: "pipeline", id: pipeline.id },
27159
27165
  agents,
27160
27166
  // Provenance last, so a manifest cannot overwrite the edge it is drawn from.
27161
- inputs: {
27162
- ...item.inputs,
27163
- from_epic: parentEpicId,
27164
- follow_up_key: item.key
27165
- },
27167
+ inputs: withFollowUpProvenance2(item.inputs, parentEpicId, item.key),
27166
27168
  pipeline,
27167
27169
  seedArtifacts: { [exports2.FOLLOW_UP_INTENT]: item.intent.endsWith("\n") ? item.intent : `${item.intent}
27168
27170
  ` },
@@ -27174,6 +27176,91 @@ var require_FollowUps = __commonJS({
27174
27176
  intentPath: path17.join(result.artifactsDir, exports2.FOLLOW_UP_INTENT)
27175
27177
  };
27176
27178
  }
27179
+ function withFollowUpProvenance2(inputs, parentEpicId, key) {
27180
+ return { ...inputs, from_epic: parentEpicId, follow_up_key: key };
27181
+ }
27182
+ exports2.MANUAL_FOLLOW_UP_KEY_PREFIX = "F";
27183
+ function nextManualFollowUpKey(taken) {
27184
+ const used = new Set([...taken].map((k) => k.toUpperCase()));
27185
+ for (let n = 1; n < 1e4; n++) {
27186
+ const key = `${exports2.MANUAL_FOLLOW_UP_KEY_PREFIX}${n}`;
27187
+ if (!used.has(key)) {
27188
+ return key;
27189
+ }
27190
+ }
27191
+ throw new EpicScaffold_1.EpicScaffoldError("Could not find a free follow-up key.");
27192
+ }
27193
+ function stepIds(steps) {
27194
+ if (!Array.isArray(steps)) {
27195
+ return [];
27196
+ }
27197
+ return steps.map((s) => {
27198
+ if (typeof s === "string") {
27199
+ return s;
27200
+ }
27201
+ const o = s ?? {};
27202
+ return String(o.name ?? o.agent ?? "");
27203
+ });
27204
+ }
27205
+ function parentTarget(own, pipelines, recipes) {
27206
+ if (!own) {
27207
+ return void 0;
27208
+ }
27209
+ const source = typeof own.derived_from === "string" ? own.derived_from : "";
27210
+ if (!source) {
27211
+ return typeof own.id === "string" && own.id ? { kind: "pipeline", id: own.id } : void 0;
27212
+ }
27213
+ const steps = stepIds(own.steps).join("\n");
27214
+ const recipe = recipes.find((r) => (r.from === void 0 || r.from === source) && stepIds(r.steps).join("\n") === steps);
27215
+ if (recipe && typeof recipe.id === "string") {
27216
+ return { kind: "recipe", id: recipe.id };
27217
+ }
27218
+ if (pipelines.some((p) => p.id === source)) {
27219
+ return { kind: "pipeline", id: source };
27220
+ }
27221
+ return void 0;
27222
+ }
27223
+ function followUpDefaults2(workspaceRoot, doc, parentEpicId) {
27224
+ const root = (0, EpicScaffold_1.epicsRoot)(workspaceRoot, doc);
27225
+ const dir = path17.join(root, parentEpicId);
27226
+ const stateFile = path17.join(dir, "state.json");
27227
+ if (!fs14.existsSync(stateFile)) {
27228
+ throw new EpicScaffold_1.EpicScaffoldError(`Epic "${parentEpicId}" not found \u2014 no state.json in ${dir}.`);
27229
+ }
27230
+ let state;
27231
+ try {
27232
+ state = JSON.parse(fs14.readFileSync(stateFile, "utf8"));
27233
+ } catch (err) {
27234
+ throw new EpicScaffold_1.EpicScaffoldError(`Epic "${parentEpicId}" has an unreadable state.json: ${err instanceof Error ? err.message : String(err)}`);
27235
+ }
27236
+ const pipelines = Array.isArray(doc?.pipelines) ? doc.pipelines : [];
27237
+ const recipes = Array.isArray(doc?.recipes) ? doc.recipes : [];
27238
+ let own = null;
27239
+ const ownFile = path17.join(dir, EpicPipelineStore_1.EPIC_PIPELINE_FILENAME);
27240
+ if (fs14.existsSync(ownFile)) {
27241
+ try {
27242
+ const parsed = yaml.load(fs14.readFileSync(ownFile, "utf8"));
27243
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
27244
+ own = parsed;
27245
+ }
27246
+ } catch {
27247
+ }
27248
+ }
27249
+ if (!own && typeof state.pipeline === "string") {
27250
+ own = pipelines.find((p) => p.id === state.pipeline) ?? null;
27251
+ }
27252
+ const key = nextManualFollowUpKey(followUpsOf(workspaceRoot, doc, parentEpicId).map((f) => f.key ?? ""));
27253
+ const taken = fs14.existsSync(root) ? fs14.readdirSync(root) : [];
27254
+ const target = parentTarget(own, pipelines, recipes);
27255
+ return {
27256
+ parentEpicId,
27257
+ parentTitle: typeof state.title === "string" ? state.title : "",
27258
+ key,
27259
+ epicId: followUpChildId(parentEpicId, key, taken),
27260
+ tags: (0, epicTags_1.normalizeTags)(state.tags),
27261
+ ...target ? { target } : {}
27262
+ };
27263
+ }
27177
27264
  }
27178
27265
  });
27179
27266
 
@@ -30122,8 +30209,9 @@ var require_userConfig = __commonJS({
30122
30209
  exports2.userConfigPath = userConfigPath;
30123
30210
  exports2.readUserConfig = readUserConfig3;
30124
30211
  exports2.writeUserEpicIdPrefix = writeUserEpicIdPrefix;
30212
+ exports2.updateUserConfig = updateUserConfig;
30125
30213
  exports2.readGitUserName = readGitUserName2;
30126
- exports2.ensureUserConfigIgnored = ensureUserConfigIgnored;
30214
+ exports2.ensureUserConfigIgnored = ensureUserConfigIgnored2;
30127
30215
  var child_process_1 = require("child_process");
30128
30216
  var fs_1 = __importDefault(require("fs"));
30129
30217
  var path_1 = __importDefault(require("path"));
@@ -30149,15 +30237,22 @@ var require_userConfig = __commonJS({
30149
30237
  "# Settings for this checkout only \u2014 NOT committed (see .gitignore).",
30150
30238
  "# epic_id_prefix is your own two letters, so the epics you open are filed",
30151
30239
  "# under you and never collide with a colleague's numbering.",
30240
+ "# active_epic / pinned_epics / recent_epics are your own working set; the",
30241
+ "# extension and `aidlc epic use|pin` keep them, and hand edits are fine.",
30152
30242
  ""
30153
30243
  ].join("\n");
30154
30244
  function writeUserEpicIdPrefix(root, prefix) {
30245
+ updateUserConfig(root, (doc) => {
30246
+ if (prefix) {
30247
+ doc.epic_id_prefix = prefix.toUpperCase();
30248
+ } else {
30249
+ delete doc.epic_id_prefix;
30250
+ }
30251
+ });
30252
+ }
30253
+ function updateUserConfig(root, mutate) {
30155
30254
  const doc = readUserConfig3(root) ?? {};
30156
- if (prefix) {
30157
- doc.epic_id_prefix = prefix.toUpperCase();
30158
- } else {
30159
- delete doc.epic_id_prefix;
30160
- }
30255
+ mutate(doc);
30161
30256
  const file = userConfigPath(root);
30162
30257
  fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
30163
30258
  if (Object.keys(doc).length === 0) {
@@ -30180,7 +30275,7 @@ var require_userConfig = __commonJS({
30180
30275
  return null;
30181
30276
  }
30182
30277
  }
30183
- function ensureUserConfigIgnored(root) {
30278
+ function ensureUserConfigIgnored2(root) {
30184
30279
  const file = path_1.default.join(root, ".gitignore");
30185
30280
  try {
30186
30281
  const existing = fs_1.default.existsSync(file) ? fs_1.default.readFileSync(file, "utf8") : "";
@@ -30190,7 +30285,7 @@ var require_userConfig = __commonJS({
30190
30285
  }
30191
30286
  const sep4 = existing === "" || existing.endsWith("\n") ? "" : "\n";
30192
30287
  fs_1.default.writeFileSync(file, `${existing}${sep4}
30193
- # Per-checkout AIDLC settings (epic_id_prefix) \u2014 never shared
30288
+ # Per-checkout AIDLC settings (epic_id_prefix, active/pinned epics) \u2014 never shared
30194
30289
  ${exports2.USER_CONFIG_IGNORE_LINE}
30195
30290
  `, "utf8");
30196
30291
  return true;
@@ -30201,6 +30296,114 @@ ${exports2.USER_CONFIG_IGNORE_LINE}
30201
30296
  }
30202
30297
  });
30203
30298
 
30299
+ // ../core/dist/loader/epicFocus.js
30300
+ var require_epicFocus = __commonJS({
30301
+ "../core/dist/loader/epicFocus.js"(exports2) {
30302
+ "use strict";
30303
+ Object.defineProperty(exports2, "__esModule", { value: true });
30304
+ exports2.RECENT_EPICS_LIMIT = exports2.RECENT_EPICS_KEY = exports2.PINNED_EPICS_KEY = exports2.ACTIVE_EPIC_KEY = void 0;
30305
+ exports2.epicFocusFrom = epicFocusFrom;
30306
+ exports2.readEpicFocus = readEpicFocus2;
30307
+ exports2.pushRecent = pushRecent;
30308
+ exports2.recordEpicOpened = recordEpicOpened;
30309
+ exports2.setActiveEpic = setActiveEpic2;
30310
+ exports2.setEpicPinned = setEpicPinned2;
30311
+ exports2.epicIdFromBranch = epicIdFromBranch;
30312
+ var userConfig_1 = require_userConfig();
30313
+ exports2.ACTIVE_EPIC_KEY = "active_epic";
30314
+ exports2.PINNED_EPICS_KEY = "pinned_epics";
30315
+ exports2.RECENT_EPICS_KEY = "recent_epics";
30316
+ exports2.RECENT_EPICS_LIMIT = 10;
30317
+ function asId(v) {
30318
+ return typeof v === "string" && v.trim() !== "" ? v.trim() : null;
30319
+ }
30320
+ function asIdList(v) {
30321
+ if (!Array.isArray(v)) {
30322
+ return [];
30323
+ }
30324
+ const out2 = [];
30325
+ for (const item of v) {
30326
+ const id = asId(item);
30327
+ if (id && !out2.includes(id)) {
30328
+ out2.push(id);
30329
+ }
30330
+ }
30331
+ return out2;
30332
+ }
30333
+ function epicFocusFrom(doc) {
30334
+ return {
30335
+ active: asId(doc?.[exports2.ACTIVE_EPIC_KEY]),
30336
+ pinned: asIdList(doc?.[exports2.PINNED_EPICS_KEY]),
30337
+ recent: asIdList(doc?.[exports2.RECENT_EPICS_KEY])
30338
+ };
30339
+ }
30340
+ function readEpicFocus2(root) {
30341
+ return epicFocusFrom((0, userConfig_1.readUserConfig)(root));
30342
+ }
30343
+ function pushRecent(list, id, limit = exports2.RECENT_EPICS_LIMIT) {
30344
+ return [id, ...list.filter((x) => x !== id)].slice(0, limit);
30345
+ }
30346
+ function writeList(doc, key, list) {
30347
+ if (list.length > 0) {
30348
+ doc[key] = list;
30349
+ } else {
30350
+ delete doc[key];
30351
+ }
30352
+ }
30353
+ function recordEpicOpened(root, id) {
30354
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30355
+ writeList(doc, exports2.RECENT_EPICS_KEY, pushRecent(asIdList(doc[exports2.RECENT_EPICS_KEY]), id));
30356
+ });
30357
+ }
30358
+ function setActiveEpic2(root, id) {
30359
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30360
+ if (id) {
30361
+ doc[exports2.ACTIVE_EPIC_KEY] = id;
30362
+ writeList(doc, exports2.RECENT_EPICS_KEY, pushRecent(asIdList(doc[exports2.RECENT_EPICS_KEY]), id));
30363
+ } else {
30364
+ delete doc[exports2.ACTIVE_EPIC_KEY];
30365
+ }
30366
+ });
30367
+ }
30368
+ function setEpicPinned2(root, id, pinned) {
30369
+ (0, userConfig_1.updateUserConfig)(root, (doc) => {
30370
+ const list = asIdList(doc[exports2.PINNED_EPICS_KEY]).filter((x) => x !== id);
30371
+ if (pinned) {
30372
+ list.push(id);
30373
+ }
30374
+ writeList(doc, exports2.PINNED_EPICS_KEY, list);
30375
+ });
30376
+ }
30377
+ function epicIdFromBranch(branch, knownIds) {
30378
+ if (!branch) {
30379
+ return null;
30380
+ }
30381
+ const hay = branch.toLowerCase();
30382
+ let best = null;
30383
+ for (const id of knownIds) {
30384
+ const needle = id.toLowerCase();
30385
+ let from = 0;
30386
+ for (; ; ) {
30387
+ const at = hay.indexOf(needle, from);
30388
+ if (at < 0) {
30389
+ break;
30390
+ }
30391
+ const before = at === 0 ? "" : hay[at - 1];
30392
+ const after = hay[at + needle.length] ?? "";
30393
+ if (!/[a-z0-9]/.test(before) && !/[0-9]/.test(after)) {
30394
+ if (!best || id.length > best.length) {
30395
+ best = id;
30396
+ }
30397
+ break;
30398
+ }
30399
+ from = at + 1;
30400
+ }
30401
+ }
30402
+ return best;
30403
+ }
30404
+ }
30405
+ });
30406
+
30204
30407
  // ../core/dist/presets/commandBodyFreshness.js
30205
30408
  var require_commandBodyFreshness = __commonJS({
30206
30409
  "../core/dist/presets/commandBodyFreshness.js"(exports2) {
@@ -33448,12 +33651,12 @@ var require_dist = __commonJS({
33448
33651
  "use strict";
33449
33652
  Object.defineProperty(exports2, "__esModule", { value: true });
33450
33653
  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;
33451
- 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 = exports2.followUpEpicId = exports2.renderIntentMarkdown = exports2.openFollowUpEpic = void 0;
33452
- 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 = exports2.epicPipelinePath = exports2.EPIC_PIPELINE_FILENAME = exports2.unusedAfterStepRemoval = void 0;
33453
- 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 = 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 = void 0;
33454
- 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 = exports2.providerAliases = exports2.ratesFromConfig = exports2.mergeRates = exports2.BUILTIN_RATES = exports2.checkBudget = exports2.weighStepProgress = exports2.isActiveStatus = exports2.isStepOptional = exports2.isRunComplete = void 0;
33455
- 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 = exports2.CANONICAL_PHASES = exports2.renderTemplate = exports2.DEFAULT_GLOBAL_WORKFLOW_IDS = exports2.detectGlobalBuiltinSource = exports2.uninstallWorkflowGlobalsByIds = exports2.isWorkflowGloballyInstalled = exports2.installWorkflowGlobalsByIds = exports2.installGlobalDefaults = exports2.resolveProviderModel = void 0;
33456
- exports2.AIDLC_CORE_VERSION = exports2.AIDLC_CLI_GUIDE_TEXT = exports2.AIDLC_KNOWLEDGE = exports2.clearResolveCommandCache = exports2.resolveCommand = exports2.remapClaudePath = exports2.claudeConfigEnv = exports2.claudeJsonPath = exports2.isDefaultClaudeConfigDir = void 0;
33654
+ 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;
33655
+ exports2.epicIdFromBranch = exports2.epicFocusFrom = exports2.RECENT_EPICS_LIMIT = exports2.RECENT_EPICS_KEY = exports2.PINNED_EPICS_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 = void 0;
33656
+ 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 = exports2.EPIC_TAGS_KEY = exports2.strictModeSection = exports2.resolveEpicStrictMode = exports2.epicStrictMode = exports2.commandBodyPredatesStrictMode = exports2.STRICT_MODE_KEY = exports2.STRICT_MODE_HEADING = exports2.setEpicPinned = exports2.setActiveEpic = exports2.recordEpicOpened = exports2.readEpicFocus = exports2.pushRecent = void 0;
33657
+ 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 = exports2.dirtyUpstreamOf = exports2.rerunApprovedStep = exports2.canRerunApprovedStep = exports2.chooseStepSkill = exports2.requestStepUpdate = exports2.rerunStep = exports2.rejectStep = exports2.approveStep = exports2.undoStepDone = exports2.canUndoStepDone = exports2.markStepDone = exports2.canStartStep = void 0;
33658
+ 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 = 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 = void 0;
33659
+ 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 = void 0;
33457
33660
  var WorkspaceSchema_1 = require_WorkspaceSchema();
33458
33661
  Object.defineProperty(exports2, "WorkspaceSchema", { enumerable: true, get: function() {
33459
33662
  return WorkspaceSchema_1.WorkspaceSchema;
@@ -33664,6 +33867,18 @@ var require_dist = __commonJS({
33664
33867
  Object.defineProperty(exports2, "openManifestFollowUp", { enumerable: true, get: function() {
33665
33868
  return FollowUps_1.openManifestFollowUp;
33666
33869
  } });
33870
+ Object.defineProperty(exports2, "withFollowUpProvenance", { enumerable: true, get: function() {
33871
+ return FollowUps_1.withFollowUpProvenance;
33872
+ } });
33873
+ Object.defineProperty(exports2, "nextManualFollowUpKey", { enumerable: true, get: function() {
33874
+ return FollowUps_1.nextManualFollowUpKey;
33875
+ } });
33876
+ Object.defineProperty(exports2, "followUpDefaults", { enumerable: true, get: function() {
33877
+ return FollowUps_1.followUpDefaults;
33878
+ } });
33879
+ Object.defineProperty(exports2, "MANUAL_FOLLOW_UP_KEY_PREFIX", { enumerable: true, get: function() {
33880
+ return FollowUps_1.MANUAL_FOLLOW_UP_KEY_PREFIX;
33881
+ } });
33667
33882
  Object.defineProperty(exports2, "FOLLOW_UPS_FILE", { enumerable: true, get: function() {
33668
33883
  return FollowUps_1.FOLLOW_UPS_FILE;
33669
33884
  } });
@@ -33893,12 +34108,49 @@ var require_dist = __commonJS({
33893
34108
  Object.defineProperty(exports2, "readUserConfig", { enumerable: true, get: function() {
33894
34109
  return userConfig_1.readUserConfig;
33895
34110
  } });
34111
+ Object.defineProperty(exports2, "updateUserConfig", { enumerable: true, get: function() {
34112
+ return userConfig_1.updateUserConfig;
34113
+ } });
33896
34114
  Object.defineProperty(exports2, "userConfigPath", { enumerable: true, get: function() {
33897
34115
  return userConfig_1.userConfigPath;
33898
34116
  } });
33899
34117
  Object.defineProperty(exports2, "writeUserEpicIdPrefix", { enumerable: true, get: function() {
33900
34118
  return userConfig_1.writeUserEpicIdPrefix;
33901
34119
  } });
34120
+ var epicFocus_1 = require_epicFocus();
34121
+ Object.defineProperty(exports2, "ACTIVE_EPIC_KEY", { enumerable: true, get: function() {
34122
+ return epicFocus_1.ACTIVE_EPIC_KEY;
34123
+ } });
34124
+ Object.defineProperty(exports2, "PINNED_EPICS_KEY", { enumerable: true, get: function() {
34125
+ return epicFocus_1.PINNED_EPICS_KEY;
34126
+ } });
34127
+ Object.defineProperty(exports2, "RECENT_EPICS_KEY", { enumerable: true, get: function() {
34128
+ return epicFocus_1.RECENT_EPICS_KEY;
34129
+ } });
34130
+ Object.defineProperty(exports2, "RECENT_EPICS_LIMIT", { enumerable: true, get: function() {
34131
+ return epicFocus_1.RECENT_EPICS_LIMIT;
34132
+ } });
34133
+ Object.defineProperty(exports2, "epicFocusFrom", { enumerable: true, get: function() {
34134
+ return epicFocus_1.epicFocusFrom;
34135
+ } });
34136
+ Object.defineProperty(exports2, "epicIdFromBranch", { enumerable: true, get: function() {
34137
+ return epicFocus_1.epicIdFromBranch;
34138
+ } });
34139
+ Object.defineProperty(exports2, "pushRecent", { enumerable: true, get: function() {
34140
+ return epicFocus_1.pushRecent;
34141
+ } });
34142
+ Object.defineProperty(exports2, "readEpicFocus", { enumerable: true, get: function() {
34143
+ return epicFocus_1.readEpicFocus;
34144
+ } });
34145
+ Object.defineProperty(exports2, "recordEpicOpened", { enumerable: true, get: function() {
34146
+ return epicFocus_1.recordEpicOpened;
34147
+ } });
34148
+ Object.defineProperty(exports2, "setActiveEpic", { enumerable: true, get: function() {
34149
+ return epicFocus_1.setActiveEpic;
34150
+ } });
34151
+ Object.defineProperty(exports2, "setEpicPinned", { enumerable: true, get: function() {
34152
+ return epicFocus_1.setEpicPinned;
34153
+ } });
33902
34154
  var strictMode_1 = require_strictMode();
33903
34155
  Object.defineProperty(exports2, "STRICT_MODE_HEADING", { enumerable: true, get: function() {
33904
34156
  return strictMode_1.STRICT_MODE_HEADING;
@@ -43113,7 +43365,7 @@ var require_package = __commonJS({
43113
43365
  "package.json"(exports2, module2) {
43114
43366
  module2.exports = {
43115
43367
  name: "@delete101020/aidlc",
43116
- version: "4.0.25",
43368
+ version: "4.0.27",
43117
43369
  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.",
43118
43370
  license: "MIT",
43119
43371
  repository: {
@@ -49752,22 +50004,14 @@ ${epics.length} epic${epics.length !== 1 ? "s" : ""}`));
49752
50004
  console.error(import_chalk15.default.dim(err instanceof Error ? err.message : String(err)));
49753
50005
  process.exit(1);
49754
50006
  }
49755
- let pipelineCfg;
49756
- if (opts.pipeline) {
49757
- const found = doc.pipelines.find((p) => String(p.id) === opts.pipeline);
49758
- if (!found) {
49759
- console.error(import_chalk15.default.red(`Pipeline "${opts.pipeline}" not found in workspace.yaml.`));
49760
- process.exit(1);
49761
- }
49762
- pipelineCfg = found;
49763
- } else {
50007
+ let recipeId = opts.recipe;
50008
+ if (!opts.pipeline) {
49764
50009
  if (config.recipes.length === 0) {
49765
50010
  console.error(import_chalk15.default.red("No recipes defined \u2014 task-type suggestion needs them."));
49766
50011
  console.error(import_chalk15.default.dim(" Back-fill from your existing pipeline: aidlc recipe init"));
49767
50012
  console.error(import_chalk15.default.dim(" Or apply the preset that ships them: aidlc preset apply sdlc"));
49768
50013
  process.exit(1);
49769
50014
  }
49770
- let recipeId = opts.recipe;
49771
50015
  if (!recipeId) {
49772
50016
  const brief = (opts.brief ?? []).join(" ").trim();
49773
50017
  const heur = (0, import_core19.heuristicClassify)(brief, config.recipes);
@@ -49779,34 +50023,12 @@ ${epics.length} epic${epics.length !== 1 ? "s" : ""}`));
49779
50023
  recipeId = verdict.recipeId;
49780
50024
  console.log(import_chalk15.default.dim(`Classified \u2192 ${import_chalk15.default.bold(recipeId)} (${verdict.confidence}, ${verdict.source})`));
49781
50025
  }
49782
- if (opts.from) {
49783
- const recipe = config.recipes.find((r) => r.id === recipeId);
49784
- if (recipe) {
49785
- recipe.from = opts.from;
49786
- }
49787
- }
49788
- const pipelineId = (0, import_core19.recipePipelineId)({ recipeId, epicId, taken: existingIds(doc.pipelines) });
49789
- try {
49790
- pipelineCfg = (0, import_core19.assemblePipeline)(config, { recipeId, pipelineId });
49791
- } catch (err) {
49792
- if (err instanceof import_core19.PipelineAssembleError) {
49793
- console.error(import_chalk15.default.red("Could not assemble pipeline: ") + import_chalk15.default.dim(err.message));
49794
- process.exit(1);
49795
- }
49796
- throw err;
49797
- }
49798
- doc.pipelines.push(pipelineCfg);
49799
- (0, import_core19.stageEpicPipeline)(doc, pipelineCfg.id, epicId);
49800
- try {
49801
- (0, import_core19.validateWorkspace)(doc, ".aidlc/workspace.yaml");
49802
- } catch (err) {
49803
- console.error(import_chalk15.default.red("Assembled pipeline failed validation \u2014 not written:"));
49804
- console.error(import_chalk15.default.dim(err instanceof Error ? err.message : String(err)));
49805
- process.exit(1);
49806
- }
49807
- writeYaml(root, doc);
49808
- console.log(import_chalk15.default.dim(`Assembled pipeline ${import_chalk15.default.bold(pipelineCfg.id)} from recipe ${import_chalk15.default.bold(recipeId)}`));
49809
50026
  }
50027
+ const pipelineCfg = resolveEpicPipeline(root, doc, config, {
50028
+ pipeline: opts.pipeline,
50029
+ recipe: recipeId,
50030
+ from: opts.from
50031
+ }, epicId);
49810
50032
  const agents = Array.isArray(pipelineCfg.steps) ? pipelineCfg.steps.map(import_core19.stepAgentId) : [];
49811
50033
  const inputs = {};
49812
50034
  for (const kv of opts.input) {
@@ -49841,13 +50063,7 @@ ${epics.length} epic${epics.length !== 1 ? "s" : ""}`));
49841
50063
  if (!opts.strict) {
49842
50064
  console.log(import_chalk15.default.dim(" Depth: strict_mode: false \u2014 phases stay proportional to the work"));
49843
50065
  }
49844
- const firstStep = Array.isArray(pipelineCfg.steps) ? pipelineCfg.steps[0] : void 0;
49845
- const firstName = firstStep?.name ?? firstStep?.agent ?? agents[0];
49846
- const slashCmds = Array.isArray(doc.slash_commands) ? doc.slash_commands : [];
49847
- const match = slashCmds.find(
49848
- (c) => typeof c.name === "string" && c.name.endsWith(`-${firstName}`) && c.agent === agents[0]
49849
- ) ?? slashCmds.find((c) => typeof c.name === "string" && c.name.endsWith(`-${firstName}`));
49850
- const runCmd = match?.name ?? `/${firstName}`;
50066
+ const runCmd = firstStepCommand(doc, pipelineCfg, agents);
49851
50067
  console.log(`
49852
50068
  Run ${import_chalk15.default.cyan(`${runCmd} ${epicId}`)} in Claude to begin.`);
49853
50069
  } catch (err) {
@@ -49858,6 +50074,87 @@ Run ${import_chalk15.default.cyan(`${runCmd} ${epicId}`)} in Claude to begin.`);
49858
50074
  throw err;
49859
50075
  }
49860
50076
  });
50077
+ cmd.command("follow-up <parentEpic>").description("Open a follow-up epic of <parentEpic> to park work found while doing it \u2014 nothing runs yet").requiredOption("--title <title>", "what the follow-up is").option("--desc <description>", "the note: what turned up, and why it is not part of the parent").option("--recipe <id>", "recipe for the follow-up (default: the one the parent runs on)").option("--pipeline <id>", "use an existing pipeline as-is (default: the parent's workflow)").option("--from <pipelineId>", "override the recipe's source pipeline").option("--epic <id>", "id for the follow-up (default: <parentEpic>-F<n>)").option("--input <kv>", "capability input as key=value (repeatable)", collectKv, []).option("--tag <tag>", "extra tag (repeatable) \u2014 added to the ones inherited from the parent", collectKv, []).option("--no-inherit-tags", "do not copy the parent's tags").option("--no-strict", "work to the size of this epic \u2014 no invented NFR / risk / alternatives sections").option("--json", "output the result as JSON").action((parentEpic, opts, actionCmd) => {
50078
+ const root = resolveWorkspaceRoot(actionCmd);
50079
+ const doc = requireYaml(root);
50080
+ if (opts.recipe && opts.pipeline) {
50081
+ console.error(import_chalk15.default.red("Pick at most one of --recipe <id> or --pipeline <id>."));
50082
+ process.exit(1);
50083
+ }
50084
+ let config;
50085
+ try {
50086
+ config = (0, import_core19.validateWorkspace)(doc, ".aidlc/workspace.yaml");
50087
+ } catch (err) {
50088
+ console.error(import_chalk15.default.red("workspace.yaml is invalid \u2014 fix it before opening a follow-up:"));
50089
+ console.error(import_chalk15.default.dim(err instanceof Error ? err.message : String(err)));
50090
+ process.exit(1);
50091
+ }
50092
+ let defaults;
50093
+ try {
50094
+ defaults = (0, import_core19.followUpDefaults)(root, doc, parentEpic);
50095
+ } catch (err) {
50096
+ console.error(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
50097
+ process.exit(1);
50098
+ }
50099
+ const epicId = opts.epic?.trim() || defaults.epicId;
50100
+ const target = opts.pipeline ? { pipeline: opts.pipeline } : opts.recipe ? { recipe: opts.recipe } : defaults.target?.kind === "pipeline" ? { pipeline: defaults.target.id } : defaults.target ? { recipe: defaults.target.id } : void 0;
50101
+ if (!target) {
50102
+ console.error(import_chalk15.default.red(`Could not tell which workflow "${parentEpic}" runs on.`));
50103
+ console.error(import_chalk15.default.dim(" Pass one: --recipe <id> or --pipeline <id>"));
50104
+ process.exit(1);
50105
+ }
50106
+ const pipelineCfg = resolveEpicPipeline(root, doc, config, { ...target, from: opts.from, quiet: opts.json }, epicId);
50107
+ const agents = Array.isArray(pipelineCfg.steps) ? pipelineCfg.steps.map(import_core19.stepAgentId) : [];
50108
+ const inputs = {};
50109
+ for (const kv of opts.input) {
50110
+ const eq = kv.indexOf("=");
50111
+ if (eq > 0) {
50112
+ inputs[kv.slice(0, eq).trim()] = kv.slice(eq + 1).trim();
50113
+ }
50114
+ }
50115
+ const tags = (0, import_core19.normalizeTags)([...opts.inheritTags ? defaults.tags : [], ...opts.tag]);
50116
+ try {
50117
+ const { epicDir } = (0, import_core19.scaffoldEpic)({
50118
+ workspaceRoot: root,
50119
+ doc,
50120
+ epicId,
50121
+ title: opts.title.trim(),
50122
+ description: opts.desc?.trim() ?? "",
50123
+ target: { kind: "pipeline", id: pipelineCfg.id },
50124
+ agents,
50125
+ inputs: (0, import_core19.withFollowUpProvenance)(inputs, parentEpic, defaults.key),
50126
+ pipeline: pipelineCfg,
50127
+ strictMode: opts.strict,
50128
+ tags
50129
+ });
50130
+ if (opts.json) {
50131
+ console.log(JSON.stringify({
50132
+ epicId,
50133
+ epicDir,
50134
+ pipeline: pipelineCfg.id,
50135
+ fromEpic: parentEpic,
50136
+ followUpKey: defaults.key,
50137
+ tags
50138
+ }, null, 2));
50139
+ return;
50140
+ }
50141
+ console.log(import_chalk15.default.green("\u2714") + ` Opened follow-up ${import_chalk15.default.bold(epicId)} of ${import_chalk15.default.bold(parentEpic)}`);
50142
+ console.log(import_chalk15.default.dim(` Pipeline: ${pipelineCfg.id}`));
50143
+ console.log(import_chalk15.default.dim(` Steps: ${agents.join(" \u2192 ")}`));
50144
+ console.log(import_chalk15.default.dim(` Dir: ${epicDir}`));
50145
+ if (tags.length > 0) {
50146
+ console.log(import_chalk15.default.dim(" Tags: ") + import_chalk15.default.cyan(tags.join(" ")));
50147
+ }
50148
+ console.log(`
50149
+ Parked \u2014 nothing runs yet. When it is time: ${import_chalk15.default.cyan(`${firstStepCommand(doc, pipelineCfg, agents)} ${epicId}`)}`);
50150
+ } catch (err) {
50151
+ if (err instanceof import_core19.EpicScaffoldError) {
50152
+ console.error(import_chalk15.default.red(err.message));
50153
+ process.exit(1);
50154
+ }
50155
+ throw err;
50156
+ }
50157
+ });
49861
50158
  cmd.command("tag <id> [tags...]").description("Show or edit an epic's tags. Bare `tag <id>` prints them; extra words are added.").option("--add <tag>", "add a tag (repeatable) \u2014 same as passing it positionally", collectKv, []).option("--remove <tag>", "remove a tag (repeatable)", collectKv, []).option("--set <tag>", "replace every tag with these (repeatable)", collectKv, []).option("--clear", "remove every tag").option("--json", "Output the resulting tags as JSON").action((id, positional, opts, actionCmd) => {
49862
50159
  const root = resolveWorkspaceRoot(actionCmd);
49863
50160
  const doc = readYaml(root);
@@ -50072,6 +50369,67 @@ ${plan.length} pipeline(s) would move. Re-run without --dry-run.`));
50072
50369
  });
50073
50370
  console.log((0, import_core19.suggestEpicId)(existing, prefix));
50074
50371
  });
50372
+ const requireKnownEpic = (root, id) => {
50373
+ const all = listEpics(root, readYaml(root)).map((e) => e.id);
50374
+ const hit = all.find((x) => x.toLowerCase() === id.toLowerCase());
50375
+ if (!hit) {
50376
+ console.error(import_chalk15.default.red(`Epic "${id}" not found.`));
50377
+ if (all.length > 0) {
50378
+ console.error(import_chalk15.default.dim(`Available: ${all.join(", ")}`));
50379
+ }
50380
+ process.exit(1);
50381
+ }
50382
+ return hit;
50383
+ };
50384
+ const noteIgnored = (root) => {
50385
+ if ((0, import_core19.ensureUserConfigIgnored)(root)) {
50386
+ console.log(import_chalk15.default.dim(` Added ${import_core19.USER_CONFIG_IGNORE_LINE} to .gitignore \u2014 it is yours, not the team's.`));
50387
+ }
50388
+ };
50389
+ cmd.command("current").description("Print the active epic id (from .aidlc/user.yaml); exits 1 when none is set").option("--json", "Print active, pinned and recent as JSON").action((opts, actionCmd) => {
50390
+ const root = resolveWorkspaceRoot(actionCmd);
50391
+ const focus = (0, import_core19.readEpicFocus)(root);
50392
+ if (opts.json) {
50393
+ console.log(JSON.stringify(focus, null, 2));
50394
+ return;
50395
+ }
50396
+ if (!focus.active) {
50397
+ process.exit(1);
50398
+ }
50399
+ console.log(focus.active);
50400
+ });
50401
+ 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) => {
50402
+ const root = resolveWorkspaceRoot(actionCmd);
50403
+ if (opts.clear) {
50404
+ (0, import_core19.setActiveEpic)(root, null);
50405
+ console.log(import_chalk15.default.green("\u2714") + " No active epic.");
50406
+ return;
50407
+ }
50408
+ if (!id) {
50409
+ console.error(import_chalk15.default.red("Give an epic id, or --clear."));
50410
+ process.exit(1);
50411
+ }
50412
+ const epicId = requireKnownEpic(root, id);
50413
+ (0, import_core19.setActiveEpic)(root, epicId);
50414
+ console.log(import_chalk15.default.green("\u2714") + ` Active epic: ${import_chalk15.default.bold(epicId)}`);
50415
+ noteIgnored(root);
50416
+ });
50417
+ for (const [name, pinned] of [["pin", true], ["unpin", false]]) {
50418
+ cmd.command(`${name} [id]`).description(pinned ? "Pin an epic to the top of the sidebar and Go to Epic picker; bare `pin` lists pins" : "Unpin an epic").action((id, _opts, actionCmd) => {
50419
+ const root = resolveWorkspaceRoot(actionCmd);
50420
+ if (!id) {
50421
+ const list = (0, import_core19.readEpicFocus)(root).pinned;
50422
+ console.log(list.length > 0 ? import_chalk15.default.cyan(list.join(" ")) : import_chalk15.default.dim("(nothing pinned)"));
50423
+ return;
50424
+ }
50425
+ const epicId = pinned ? requireKnownEpic(root, id) : id;
50426
+ (0, import_core19.setEpicPinned)(root, epicId, pinned);
50427
+ console.log(import_chalk15.default.green("\u2714") + ` ${pinned ? "Pinned" : "Unpinned"} ${import_chalk15.default.bold(epicId)}`);
50428
+ if (pinned) {
50429
+ noteIgnored(root);
50430
+ }
50431
+ });
50432
+ }
50075
50433
  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) => {
50076
50434
  const root = resolveWorkspaceRoot(actionCmd);
50077
50435
  const doc = requireYaml(root);
@@ -50260,6 +50618,61 @@ function collectKv(value, acc) {
50260
50618
  acc.push(value);
50261
50619
  return acc;
50262
50620
  }
50621
+ function firstStepCommand(doc, pipelineCfg, agents) {
50622
+ const firstStep = Array.isArray(pipelineCfg.steps) ? pipelineCfg.steps[0] : void 0;
50623
+ const firstName = firstStep?.name ?? firstStep?.agent ?? agents[0];
50624
+ const slashCmds = Array.isArray(doc.slash_commands) ? doc.slash_commands : [];
50625
+ const match = slashCmds.find(
50626
+ (c) => typeof c.name === "string" && c.name.endsWith(`-${firstName}`) && c.agent === agents[0]
50627
+ ) ?? slashCmds.find((c) => typeof c.name === "string" && c.name.endsWith(`-${firstName}`));
50628
+ return match?.name ?? `/${firstName}`;
50629
+ }
50630
+ function resolveEpicPipeline(root, doc, config, opts, epicId) {
50631
+ if (opts.pipeline) {
50632
+ const found = doc.pipelines.find((p) => String(p.id) === opts.pipeline);
50633
+ if (!found) {
50634
+ console.error(import_chalk15.default.red(`Pipeline "${opts.pipeline}" not found in workspace.yaml.`));
50635
+ process.exit(1);
50636
+ }
50637
+ return found;
50638
+ }
50639
+ const recipeId = opts.recipe ?? "";
50640
+ if (!config.recipes.some((r) => r.id === recipeId)) {
50641
+ console.error(import_chalk15.default.red(`Recipe "${recipeId}" is not defined in this workspace.`));
50642
+ process.exit(1);
50643
+ }
50644
+ if (opts.from) {
50645
+ const recipe = config.recipes.find((r) => r.id === recipeId);
50646
+ if (recipe) {
50647
+ recipe.from = opts.from;
50648
+ }
50649
+ }
50650
+ const pipelineId = (0, import_core19.recipePipelineId)({ recipeId, epicId, taken: existingIds(doc.pipelines) });
50651
+ let pipelineCfg;
50652
+ try {
50653
+ pipelineCfg = (0, import_core19.assemblePipeline)(config, { recipeId, pipelineId });
50654
+ } catch (err) {
50655
+ if (err instanceof import_core19.PipelineAssembleError) {
50656
+ console.error(import_chalk15.default.red("Could not assemble pipeline: ") + import_chalk15.default.dim(err.message));
50657
+ process.exit(1);
50658
+ }
50659
+ throw err;
50660
+ }
50661
+ doc.pipelines.push(pipelineCfg);
50662
+ (0, import_core19.stageEpicPipeline)(doc, pipelineCfg.id, epicId);
50663
+ try {
50664
+ (0, import_core19.validateWorkspace)(doc, ".aidlc/workspace.yaml");
50665
+ } catch (err) {
50666
+ console.error(import_chalk15.default.red("Assembled pipeline failed validation \u2014 not written:"));
50667
+ console.error(import_chalk15.default.dim(err instanceof Error ? err.message : String(err)));
50668
+ process.exit(1);
50669
+ }
50670
+ writeYaml(root, doc);
50671
+ if (!opts.quiet) {
50672
+ console.log(import_chalk15.default.dim(`Assembled pipeline ${import_chalk15.default.bold(pipelineCfg.id)} from recipe ${import_chalk15.default.bold(recipeId)}`));
50673
+ }
50674
+ return pipelineCfg;
50675
+ }
50263
50676
  function printEpicDetail(epic) {
50264
50677
  console.log();
50265
50678
  console.log(import_chalk15.default.bold(epic.id) + " " + colorEpicStatus(epic.status));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delete101020/aidlc",
3
- "version": "4.0.25",
3
+ "version": "4.0.27",
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": {
@@ -36,11 +36,19 @@
36
36
  "README.md",
37
37
  "AUTOMATION.md"
38
38
  ],
39
+ "scripts": {
40
+ "compile": "tsc -p ./",
41
+ "build": "tsc -p ./",
42
+ "clean": "rm -rf dist",
43
+ "bundle": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/bundle.js --external:fsevents && rm -rf dist/templates && cp -R ../core/templates dist/templates && rm -rf dist/tools dist/vendor dist/assets && cp -R ../../tools dist/tools && node -e \"require('fs').mkdirSync('dist/vendor', { recursive: true })\" && cp -R ../../vendor/annotron dist/vendor/annotron && cp -R ../extension/assets dist/assets",
44
+ "prepublishOnly": "pnpm bundle"
45
+ },
39
46
  "publishConfig": {
40
47
  "access": "public",
41
48
  "registry": "https://registry.npmjs.org/"
42
49
  },
43
50
  "devDependencies": {
51
+ "@aidlc/core": "workspace:*",
44
52
  "@types/js-yaml": "^4.0.9",
45
53
  "@types/node": "^20.0.0",
46
54
  "chalk": "^4.1.2",
@@ -48,13 +56,6 @@
48
56
  "cli-table3": "^0.6.5",
49
57
  "commander": "^11.1.0",
50
58
  "esbuild": "^0.21.0",
51
- "js-yaml": "^4.1.1",
52
- "@aidlc/core": "0.0.1"
53
- },
54
- "scripts": {
55
- "compile": "tsc -p ./",
56
- "build": "tsc -p ./",
57
- "clean": "rm -rf dist",
58
- "bundle": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/bundle.js --external:fsevents && rm -rf dist/templates && cp -R ../core/templates dist/templates && rm -rf dist/tools dist/vendor dist/assets && cp -R ../../tools dist/tools && node -e \"require('fs').mkdirSync('dist/vendor', { recursive: true })\" && cp -R ../../vendor/annotron dist/vendor/annotron && cp -R ../extension/assets dist/assets"
59
+ "js-yaml": "^4.1.1"
59
60
  }
60
- }
61
+ }