@pourkit/cli 0.0.0-next-20260625084151 → 0.0.0-next-20260627024509

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/dist/cli.js CHANGED
@@ -58,20 +58,20 @@ function createLogger(name, filePath) {
58
58
  );
59
59
  },
60
60
  async close() {
61
- await new Promise((resolve5) => {
61
+ await new Promise((resolve6) => {
62
62
  if (!fileStream) {
63
- resolve5();
63
+ resolve6();
64
64
  return;
65
65
  }
66
66
  const timer = setTimeout(() => {
67
67
  if (!fileStream.destroyed) {
68
68
  fileStream.destroy();
69
69
  }
70
- resolve5();
70
+ resolve6();
71
71
  }, 2e3);
72
72
  fileStream.end(() => {
73
73
  clearTimeout(timer);
74
- resolve5();
74
+ resolve6();
75
75
  });
76
76
  });
77
77
  }
@@ -266,7 +266,7 @@ async function execJson(command, args, options = {}) {
266
266
  return JSON.parse(result.stdout);
267
267
  }
268
268
  function sleep(ms) {
269
- return new Promise((resolve5) => setTimeout(resolve5, ms));
269
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
270
270
  }
271
271
  async function execCaptureWithRetry(command, args, options = {}) {
272
272
  const retries = options.retries ?? 3;
@@ -914,8 +914,8 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
914
914
  `No Pourkit config found at ${CANONICAL_CONFIG_PATH}. Run pourkit init or create ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
915
915
  );
916
916
  }
917
- const { readFile: readFile7 } = await import("fs/promises");
918
- const raw = await readFile7(configPath, "utf-8");
917
+ const { readFile: readFile8 } = await import("fs/promises");
918
+ const raw = await readFile8(configPath, "utf-8");
919
919
  let parsed;
920
920
  try {
921
921
  parsed = JSON.parse(raw);
@@ -926,10 +926,10 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
926
926
  return parseConfig(parsed);
927
927
  }
928
928
  async function loadConfig(configPath) {
929
- const { readFile: readFile7 } = await import("fs/promises");
929
+ const { readFile: readFile8 } = await import("fs/promises");
930
930
  const ext = configPath.split(".").pop()?.toLowerCase();
931
931
  if (ext === "json") {
932
- const raw = await readFile7(configPath, "utf-8");
932
+ const raw = await readFile8(configPath, "utf-8");
933
933
  return parseConfig(JSON.parse(raw));
934
934
  }
935
935
  if (ext === "mjs" || ext === "js" || ext === "ts") {
@@ -1086,10 +1086,10 @@ async function removeStaleWorktree(candidate, repoRoot2, logger) {
1086
1086
  }
1087
1087
  }
1088
1088
  async function removeExpiredFiles(dirPath, retentionDays) {
1089
- const { readdir: readdir4, stat, unlink, access: access2 } = await import("fs/promises");
1089
+ const { readdir: readdir5, stat, unlink, access: access2 } = await import("fs/promises");
1090
1090
  let entries;
1091
1091
  try {
1092
- entries = await readdir4(dirPath);
1092
+ entries = await readdir5(dirPath);
1093
1093
  } catch {
1094
1094
  return;
1095
1095
  }
@@ -1152,8 +1152,8 @@ async function cleanupRepository(options) {
1152
1152
 
1153
1153
  // commands/artifact-validation.ts
1154
1154
  import { execSync } from "child_process";
1155
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
1156
- import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1155
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
1156
+ import { isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "path";
1157
1157
 
1158
1158
  // pr/review-verdict.ts
1159
1159
  var ReviewVerdictProtocolError = class extends Error {
@@ -3237,6 +3237,13 @@ var ISSUE_FINAL_REVIEW_SCOPE_VERDICTS = [
3237
3237
  "incidental",
3238
3238
  "out-of-scope"
3239
3239
  ];
3240
+ function escapeRegExp(value) {
3241
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3242
+ }
3243
+ function normalizeChildRef(value) {
3244
+ const match = value.match(/^I-0*(\d+)$/i);
3245
+ return match ? `I-${match[1].padStart(2, "0")}` : value.toUpperCase();
3246
+ }
3240
3247
  function invalid(options, reason, diagnostics = []) {
3241
3248
  return {
3242
3249
  ok: false,
@@ -3586,11 +3593,177 @@ function validateIssueFinalReviewArtifact(parsed, options) {
3586
3593
  }
3587
3594
  return { ok: true, verdict: parsed.verdict };
3588
3595
  }
3596
+ function validatePrdPlanWorkspace(options) {
3597
+ const workspacePath = options.workspace || options.artifactPath;
3598
+ const prdRef = options.prdRef;
3599
+ if (!prdRef) {
3600
+ return invalid(options, "PRD plan workspace validation requires --prd-ref");
3601
+ }
3602
+ const diagnostics = [];
3603
+ const prdPath = join7(workspacePath, "PRD.md");
3604
+ if (!existsSync7(prdPath)) {
3605
+ return invalid(options, `PRD plan workspace missing PRD.md at ${prdPath}`);
3606
+ }
3607
+ let prdContent;
3608
+ try {
3609
+ prdContent = readFileSync7(prdPath, "utf-8");
3610
+ } catch {
3611
+ return invalid(options, `Cannot read PRD.md at ${prdPath}`);
3612
+ }
3613
+ const prdFirstLine = prdContent.split("\n")[0];
3614
+ const prdHeadingMatch = prdFirstLine.match(/^# (PRD-\d{4}):.*$/i);
3615
+ if (!prdFirstLine.startsWith("# ") || !prdHeadingMatch) {
3616
+ return invalid(
3617
+ options,
3618
+ "PRD.md must start with a heading: # PRD-NNNN: Title"
3619
+ );
3620
+ }
3621
+ if (prdHeadingMatch[1].toUpperCase() !== prdRef.toUpperCase()) {
3622
+ return invalid(
3623
+ options,
3624
+ `PRD.md heading ${prdHeadingMatch[1].toUpperCase()} does not match --prd-ref ${prdRef}`
3625
+ );
3626
+ }
3627
+ diagnostics.push(`PRD title: ${prdFirstLine.slice(2).trim()}`);
3628
+ const issuesDir = join7(workspacePath, "issues");
3629
+ if (!existsSync7(issuesDir)) {
3630
+ return invalid(
3631
+ options,
3632
+ `PRD plan workspace missing issues/ directory at ${issuesDir}`
3633
+ );
3634
+ }
3635
+ let childFiles;
3636
+ const CHILD_FILE_PATTERN2 = /^I-0*(\d+)-.+\.md$/i;
3637
+ try {
3638
+ childFiles = readdirSync3(issuesDir).filter(
3639
+ (f) => CHILD_FILE_PATTERN2.test(f) && !f.startsWith(".")
3640
+ );
3641
+ } catch {
3642
+ return invalid(options, `Cannot read issues/ directory at ${issuesDir}`);
3643
+ }
3644
+ childFiles.sort((a, b) => {
3645
+ const aMatch = a.match(CHILD_FILE_PATTERN2);
3646
+ const bMatch = b.match(CHILD_FILE_PATTERN2);
3647
+ if (!aMatch || !bMatch) return 0;
3648
+ return Number(aMatch[1]) - Number(bMatch[1]);
3649
+ });
3650
+ if (childFiles.length === 0) {
3651
+ return invalid(
3652
+ options,
3653
+ `PRD plan workspace must contain at least one issues/I-0N-*.md child Issue`
3654
+ );
3655
+ }
3656
+ const children = [];
3657
+ const CHILD_REF_PATTERN2 = /^\s*(I-0*\d+)\s*$/i;
3658
+ const BLOCKED_BY_SECTION_REGEX2 = /## Blocked by\s*\n([\s\S]*?)(?=\n## |$)/i;
3659
+ const PARENT_SECTION_REGEX2 = /## Parent\s*\n([\s\S]*?)(?=\n## |$)/i;
3660
+ for (const childFile of childFiles) {
3661
+ const childPath = join7(issuesDir, childFile);
3662
+ let childContent;
3663
+ try {
3664
+ childContent = readFileSync7(childPath, "utf-8");
3665
+ } catch {
3666
+ diagnostics.push(`${childFile}: cannot read`);
3667
+ continue;
3668
+ }
3669
+ const childFileMatch = childFile.match(CHILD_FILE_PATTERN2);
3670
+ if (!childFileMatch) continue;
3671
+ const childRef = `I-${childFileMatch[1].padStart(2, "0")}`;
3672
+ const firstChildLine = childContent.split("\n")[0];
3673
+ if (!firstChildLine.startsWith("# ") || !new RegExp(`^# ${prdRef} / ${childRef}:`).test(firstChildLine)) {
3674
+ return invalid(
3675
+ options,
3676
+ `${childFile}: expected heading: # ${prdRef} / ${childRef}: Title`
3677
+ );
3678
+ }
3679
+ const parentSection = childContent.match(PARENT_SECTION_REGEX2)?.[1];
3680
+ const parentPrdRegex = new RegExp(`\\b${escapeRegExp(prdRef)}\\b`, "i");
3681
+ if (!parentSection || !parentPrdRegex.test(parentSection)) {
3682
+ return invalid(
3683
+ options,
3684
+ `${childFile}: missing ## Parent section pointing to ${prdRef}`
3685
+ );
3686
+ }
3687
+ const section = childContent.match(BLOCKED_BY_SECTION_REGEX2)?.[1];
3688
+ const blockedByRefs = [];
3689
+ if (section) {
3690
+ for (const rawLine of section.split("\n")) {
3691
+ const line = rawLine.trim();
3692
+ if (!line) continue;
3693
+ for (const part of line.split(",")) {
3694
+ const trimmed = part.trim();
3695
+ const match = trimmed.match(CHILD_REF_PATTERN2);
3696
+ if (match) {
3697
+ blockedByRefs.push(match[1].toUpperCase());
3698
+ }
3699
+ }
3700
+ }
3701
+ }
3702
+ children.push({ childRef, blockedByRefs, sourcePath: childFile });
3703
+ diagnostics.push(`${childFile}: ${childRef}`);
3704
+ }
3705
+ const validChildRefs = new Set(children.map((c) => c.childRef));
3706
+ for (const child of children) {
3707
+ for (const blockedRef of child.blockedByRefs) {
3708
+ if (!validChildRefs.has(blockedRef.toUpperCase())) {
3709
+ return invalid(
3710
+ options,
3711
+ `Child ${child.childRef} references nonexistent child ${blockedRef} in Blocked by section`,
3712
+ [child.sourcePath]
3713
+ );
3714
+ }
3715
+ }
3716
+ }
3717
+ diagnostics.push(`children: ${children.length} total`);
3718
+ return valid(options, diagnostics);
3719
+ }
3720
+ function validatePrdPlan(content, options) {
3721
+ const firstLine = content.split("\n")[0];
3722
+ if (!firstLine.startsWith("# ") || !/^# PRD-\d{4}:/.test(firstLine)) {
3723
+ return invalid(
3724
+ options,
3725
+ "PRD plan must start with a heading: # PRD-NNNN: Title"
3726
+ );
3727
+ }
3728
+ const title = firstLine.slice(2).trim();
3729
+ return valid(options, [`title: ${title}`]);
3730
+ }
3731
+ function validatePrdChildIssue(content, options) {
3732
+ const firstLine = content.split("\n")[0];
3733
+ const match = firstLine.match(/^# (PRD-\d{4}) \/ (I-\d+):/);
3734
+ if (!firstLine.startsWith("# ") || !match) {
3735
+ return invalid(
3736
+ options,
3737
+ "Child issue must start with a heading: # PRD-NNNN / I-0N: Title"
3738
+ );
3739
+ }
3740
+ if (options.childId) {
3741
+ const normalizedHeadingChildId = normalizeChildRef(match[2]);
3742
+ const normalizedChildId = normalizeChildRef(options.childId);
3743
+ if (normalizedHeadingChildId !== normalizedChildId) {
3744
+ return invalid(
3745
+ options,
3746
+ `Child issue heading ${normalizedHeadingChildId} does not match --child-id ${normalizedChildId}`
3747
+ );
3748
+ }
3749
+ }
3750
+ const title = firstLine.slice(2).trim();
3751
+ return valid(options, [`title: ${title}`]);
3752
+ }
3589
3753
  function validateAgentArtifact(options) {
3754
+ if (options.kind === "prd-plan-workspace") {
3755
+ return validatePrdPlanWorkspace(options);
3756
+ }
3590
3757
  const artifact = readArtifact(options);
3591
3758
  if (!artifact.ok) return artifact.result;
3592
3759
  try {
3593
3760
  switch (options.kind) {
3761
+ case "prd-plan": {
3762
+ return validatePrdPlan(artifact.content, options);
3763
+ }
3764
+ case "prd-child-issue": {
3765
+ return validatePrdChildIssue(artifact.content, options);
3766
+ }
3594
3767
  case "reviewer": {
3595
3768
  const iteration = options.iteration ?? 1;
3596
3769
  const verdict = parseReviewVerdict(artifact.content);
@@ -3697,10 +3870,12 @@ function validateAgentArtifact(options) {
3697
3870
  function runValidateArtifactCommand(options) {
3698
3871
  const artifactPath = resolve2(options.repoRoot, options.artifactPath);
3699
3872
  const latestReviewArtifactPath = options.latestReviewArtifactPath ? resolve2(options.repoRoot, options.latestReviewArtifactPath) : void 0;
3873
+ const workspace = options.workspace ? resolve2(options.repoRoot, options.workspace) : void 0;
3700
3874
  return validateAgentArtifact({
3701
3875
  ...options,
3702
3876
  artifactPath,
3703
3877
  latestReviewArtifactPath,
3878
+ workspace,
3704
3879
  worktreePath: options.repoRoot,
3705
3880
  baseRef: options.baseRef
3706
3881
  });
@@ -3878,15 +4053,15 @@ function invalidateAfterBaseRefresh(state) {
3878
4053
 
3879
4054
  // shared/attempt-log.ts
3880
4055
  import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "fs";
3881
- import { dirname as dirname4, join as join7 } from "path";
4056
+ import { dirname as dirname4, join as join8 } from "path";
3882
4057
  var ATTEMPT_LOG_PATH = ".pourkit/attempt-log.jsonl";
3883
4058
  function writeAttemptLog(worktreePath, entry) {
3884
- const logPath = join7(worktreePath, ATTEMPT_LOG_PATH);
4059
+ const logPath = join8(worktreePath, ATTEMPT_LOG_PATH);
3885
4060
  mkdirSync6(dirname4(logPath), { recursive: true });
3886
4061
  appendFileSync(logPath, JSON.stringify(entry) + "\n", "utf-8");
3887
4062
  }
3888
4063
  function readAttemptLog(worktreePath) {
3889
- const logPath = join7(worktreePath, ATTEMPT_LOG_PATH);
4064
+ const logPath = join8(worktreePath, ATTEMPT_LOG_PATH);
3890
4065
  if (!existsSync8(logPath)) {
3891
4066
  return [];
3892
4067
  }
@@ -4010,12 +4185,12 @@ async function runBaseRefreshAttempt(options) {
4010
4185
 
4011
4186
  // commands/conflict-resolution.ts
4012
4187
  import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
4013
- import { join as join8 } from "path";
4188
+ import { join as join9 } from "path";
4014
4189
  init_common();
4015
4190
  var CONFLICT_MARKER_PATTERN2 = /<<<<<<<|=======|>>>>>>>/m;
4016
4191
  async function hasUnresolvedConflictMarkers(worktreePath, files) {
4017
4192
  for (const file of files) {
4018
- const filePath = join8(worktreePath, file);
4193
+ const filePath = join9(worktreePath, file);
4019
4194
  try {
4020
4195
  const content = readFileSync9(filePath, "utf-8");
4021
4196
  if (CONFLICT_MARKER_PATTERN2.test(content)) {
@@ -4029,7 +4204,7 @@ async function hasUnresolvedConflictMarkers(worktreePath, files) {
4029
4204
 
4030
4205
  // failure-resolution/failure-resolution-agent.ts
4031
4206
  import { readFileSync as readFileSync10 } from "fs";
4032
- import { join as join9 } from "path";
4207
+ import { join as join10 } from "path";
4033
4208
 
4034
4209
  // failure-resolution/recovery-policy.ts
4035
4210
  function isSecuritySensitiveFailure(failure) {
@@ -4105,7 +4280,7 @@ async function runFailureResolutionAgent(options) {
4105
4280
  } = options;
4106
4281
  const frConfig = target.strategy.failureResolution;
4107
4282
  const artifactPath = packet.artifactTarget;
4108
- const fullArtifactPath = join9(worktreePath, artifactPath);
4283
+ const fullArtifactPath = join10(worktreePath, artifactPath);
4109
4284
  const fingerprint = computeFailureFingerprint(packet.stageName, failure._tag);
4110
4285
  const prompt = [
4111
4286
  `# Failure Resolution: ${packet.failureType}`,
@@ -4731,7 +4906,7 @@ async function canReachMcp(url) {
4731
4906
  return true;
4732
4907
  } catch {
4733
4908
  if (attempt < 9) {
4734
- await new Promise((resolve5) => setTimeout(resolve5, 100));
4909
+ await new Promise((resolve6) => setTimeout(resolve6, 100));
4735
4910
  }
4736
4911
  }
4737
4912
  }
@@ -4958,11 +5133,11 @@ async function assertCanonicalBaseAncestor(options) {
4958
5133
 
4959
5134
  // issues/pr-description-agent.ts
4960
5135
  import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
4961
- import { isAbsolute as isAbsolute4, join as join12 } from "path";
5136
+ import { isAbsolute as isAbsolute4, join as join13 } from "path";
4962
5137
 
4963
5138
  // pr/pr-description-context.ts
4964
5139
  init_common();
4965
- import { join as join10 } from "path";
5140
+ import { join as join11 } from "path";
4966
5141
  import { readFile } from "fs/promises";
4967
5142
  import { Effect as Effect6 } from "effect";
4968
5143
  function collectFinalizerContextEffect(options) {
@@ -5027,7 +5202,7 @@ function remoteTargetBase(targetBase) {
5027
5202
  return targetBase.includes("/") ? targetBase : `origin/${targetBase}`;
5028
5203
  }
5029
5204
  function buildFinalizerPrompt(context, promptTemplate) {
5030
- const artifactPathInWorktree = join10(
5205
+ const artifactPathInWorktree = join11(
5031
5206
  ".pourkit",
5032
5207
  ".tmp",
5033
5208
  "finalizer",
@@ -5082,9 +5257,9 @@ Before handoff, run: pourkit validate-artifact finalizer ${artifactPathInWorktre
5082
5257
 
5083
5258
  // issues/issue-final-review.ts
5084
5259
  import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
5085
- import { isAbsolute as isAbsolute3, join as join11, relative as relative2, sep as sep2 } from "path";
5260
+ import { isAbsolute as isAbsolute3, join as join12, relative as relative2, sep as sep2 } from "path";
5086
5261
  init_common();
5087
- var ISSUE_FINAL_REVIEW_ARTIFACT_PATH = join11(
5262
+ var ISSUE_FINAL_REVIEW_ARTIFACT_PATH = join12(
5088
5263
  ".pourkit",
5089
5264
  ".tmp",
5090
5265
  "issue-final-review",
@@ -5102,7 +5277,7 @@ async function runIssueFinalReview(options) {
5102
5277
  logger
5103
5278
  } = options;
5104
5279
  const artifactPathInWorktree = ISSUE_FINAL_REVIEW_ARTIFACT_PATH;
5105
- const artifactPath = join11(worktreePath, artifactPathInWorktree);
5280
+ const artifactPath = join12(worktreePath, artifactPathInWorktree);
5106
5281
  const ifrFromState = options.worktreeState?.issueFinalReview;
5107
5282
  if (ifrFromState?.completed && ifrFromState.verdict === "pass") {
5108
5283
  if (!ifrFromState.artifactPath) {
@@ -5110,7 +5285,7 @@ async function runIssueFinalReview(options) {
5110
5285
  "Issue Final Review state is incomplete: missing artifactPath"
5111
5286
  );
5112
5287
  }
5113
- const cachedArtifactPath = isAbsolute3(ifrFromState.artifactPath) ? ifrFromState.artifactPath : join11(worktreePath, ifrFromState.artifactPath);
5288
+ const cachedArtifactPath = isAbsolute3(ifrFromState.artifactPath) ? ifrFromState.artifactPath : join12(worktreePath, ifrFromState.artifactPath);
5114
5289
  const validation2 = validateAgentArtifact({
5115
5290
  kind: "issue-final-review",
5116
5291
  artifactPath: cachedArtifactPath,
@@ -5505,20 +5680,20 @@ function loadFinalizerPromptEffect(repoRoot2, promptTemplate, fs) {
5505
5680
  }
5506
5681
  function persistGeneratedArtifactEffect(worktreePath, output, fs) {
5507
5682
  return Effect7.gen(function* () {
5508
- const dir = join12(worktreePath, ".pourkit", ".tmp", "finalizer");
5683
+ const dir = join13(worktreePath, ".pourkit", ".tmp", "finalizer");
5509
5684
  yield* fs.mkdir(dir).pipe(Effect7.catchAll(() => Effect7.void));
5510
- yield* fs.writeFile(join12(dir, "generated.md"), output).pipe(Effect7.catchAll(() => Effect7.void));
5685
+ yield* fs.writeFile(join13(dir, "generated.md"), output).pipe(Effect7.catchAll(() => Effect7.void));
5511
5686
  });
5512
5687
  }
5513
5688
  function runPrDescriptionAgent(options) {
5514
5689
  const { executionProvider } = options;
5515
- const artifactPathInWorktree = join12(
5690
+ const artifactPathInWorktree = join13(
5516
5691
  ".pourkit",
5517
5692
  ".tmp",
5518
5693
  "finalizer",
5519
5694
  "agent-output.md"
5520
5695
  );
5521
- const artifactPath = join12(options.worktreePath, artifactPathInWorktree);
5696
+ const artifactPath = join13(options.worktreePath, artifactPathInWorktree);
5522
5697
  const finalizerFromState = options.worktreeState?.finalizer?.completed ? options.worktreeState.finalizer : null;
5523
5698
  if (finalizerFromState) {
5524
5699
  return Effect7.gen(function* () {
@@ -5529,7 +5704,7 @@ function runPrDescriptionAgent(options) {
5529
5704
  })
5530
5705
  );
5531
5706
  }
5532
- const resolvedArtifactPath = isAbsolute4(finalizerFromState.artifactPath) ? finalizerFromState.artifactPath : join12(options.worktreePath, finalizerFromState.artifactPath);
5707
+ const resolvedArtifactPath = isAbsolute4(finalizerFromState.artifactPath) ? finalizerFromState.artifactPath : join13(options.worktreePath, finalizerFromState.artifactPath);
5533
5708
  if (!existsSync12(resolvedArtifactPath)) {
5534
5709
  return yield* Effect7.fail(
5535
5710
  new FinalizerFailure({
@@ -5651,7 +5826,7 @@ function runPrDescriptionAgent(options) {
5651
5826
  );
5652
5827
  }
5653
5828
  function runPrDescriptionFinalizerCore(options) {
5654
- const artifactPath = join12(
5829
+ const artifactPath = join13(
5655
5830
  options.worktreePath,
5656
5831
  options.artifactPathInWorktree
5657
5832
  );
@@ -5752,7 +5927,7 @@ function runPrDescriptionFinalizerCore(options) {
5752
5927
  // issues/github-issue-publication.ts
5753
5928
  init_common();
5754
5929
  import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
5755
- import { isAbsolute as isAbsolute5, join as join13 } from "path";
5930
+ import { isAbsolute as isAbsolute5, join as join14 } from "path";
5756
5931
 
5757
5932
  // issues/issue-transitions.ts
5758
5933
  function createIssueTransitions(deps, labels) {
@@ -5810,6 +5985,42 @@ function createIssueTransitions(deps, labels) {
5810
5985
  };
5811
5986
  }
5812
5987
 
5988
+ // beads/client.ts
5989
+ init_common();
5990
+ async function runBd(options) {
5991
+ return execCapture("bd", options.args, {
5992
+ cwd: options.repoRoot,
5993
+ logger: options.logger,
5994
+ label: options.label
5995
+ });
5996
+ }
5997
+ async function runBdJson(options) {
5998
+ return execJson("bd", options.args, {
5999
+ cwd: options.repoRoot,
6000
+ logger: options.logger,
6001
+ label: options.label
6002
+ });
6003
+ }
6004
+
6005
+ // beads/prd-completion.ts
6006
+ async function closeBeadsPrdChild(options) {
6007
+ const { repoRoot: repoRoot2, childId, reason, logger } = options;
6008
+ if (!childId || childId.trim().length === 0) {
6009
+ throw new Error(
6010
+ "childId is required and must be a non-empty Beads ID string"
6011
+ );
6012
+ }
6013
+ if (!reason || reason.trim().length === 0) {
6014
+ throw new Error("reason is required and must be a non-empty string");
6015
+ }
6016
+ const response = await runBdJson({
6017
+ repoRoot: repoRoot2,
6018
+ args: ["close", childId, "--reason", reason, "--json"],
6019
+ logger
6020
+ });
6021
+ return { ok: true, childId, response };
6022
+ }
6023
+
5813
6024
  // issues/stacked-issue.ts
5814
6025
  var BODY_PARENT_SECTION_REGEX = /## Parent\s*\n([\s\S]*?)(?=\n## |$)/i;
5815
6026
  var AFFECTED_CODE_PATHS_SECTION_REGEX = /## Affected code paths\s*\n([\s\S]*?)(?=\n## |$)/i;
@@ -6227,7 +6438,7 @@ function readIssueFinalReviewArtifact(worktreeState, worktreePath) {
6227
6438
  if (!configuredPath) {
6228
6439
  return null;
6229
6440
  }
6230
- const artifactPath = isAbsolute5(configuredPath) ? configuredPath : join13(worktreePath, configuredPath);
6441
+ const artifactPath = isAbsolute5(configuredPath) ? configuredPath : join14(worktreePath, configuredPath);
6231
6442
  if (!existsSync13(artifactPath)) return null;
6232
6443
  let parsed;
6233
6444
  try {
@@ -6334,19 +6545,56 @@ function formatTableCell(value) {
6334
6545
  return typeof value === "string" && value.trim() !== "" ? value.trim().replace(/\s+/g, " ").replace(/\|/g, "\\|") : "Unknown";
6335
6546
  }
6336
6547
  async function performCleanupAndClosure(options, pr) {
6337
- await options.issueProvider.removeLabel(
6338
- options.issue.number,
6339
- options.labels.agentInProgress
6340
- );
6341
- await options.issueProvider.removeLabel(
6342
- options.issue.number,
6343
- options.labels.prOpenAwaitingMerge
6344
- );
6548
+ if (options.beadsCompletion) {
6549
+ await closeBeadsPrdChild({
6550
+ repoRoot: options.repoRoot,
6551
+ childId: options.beadsCompletion.childId,
6552
+ reason: `Issue #${options.issue.number} merged`,
6553
+ logger: options.logger
6554
+ });
6555
+ }
6556
+ try {
6557
+ await options.issueProvider.removeLabel(
6558
+ options.issue.number,
6559
+ options.labels.agentInProgress
6560
+ );
6561
+ } catch (error) {
6562
+ if (options.beadsCompletion) {
6563
+ throw new Error(
6564
+ `Projection failure after Beads close: GitHub Issue #${options.issue.number} label ${options.labels.agentInProgress} removal failed (Beads child ${options.beadsCompletion.childId} remains completed). ${error instanceof Error ? error.message : String(error)}`
6565
+ );
6566
+ }
6567
+ options.logger.step(
6568
+ "warn",
6569
+ `Failed to remove ${options.labels.agentInProgress} label (cleanup): ${error instanceof Error ? error.message : String(error)}`
6570
+ );
6571
+ }
6572
+ try {
6573
+ await options.issueProvider.removeLabel(
6574
+ options.issue.number,
6575
+ options.labels.prOpenAwaitingMerge
6576
+ );
6577
+ } catch (error) {
6578
+ if (options.beadsCompletion) {
6579
+ throw new Error(
6580
+ `Projection failure after Beads close: GitHub Issue #${options.issue.number} label ${options.labels.prOpenAwaitingMerge} removal failed (Beads child ${options.beadsCompletion.childId} remains completed). ${error instanceof Error ? error.message : String(error)}`
6581
+ );
6582
+ }
6583
+ options.logger.step(
6584
+ "warn",
6585
+ `Failed to remove ${options.labels.prOpenAwaitingMerge} label (cleanup): ${error instanceof Error ? error.message : String(error)}`
6586
+ );
6587
+ }
6345
6588
  let childCloseSucceeded = false;
6346
6589
  try {
6347
6590
  await options.issueProvider.closeIssue(options.issue.number);
6348
6591
  childCloseSucceeded = true;
6349
6592
  } catch (error) {
6593
+ if (options.beadsCompletion) {
6594
+ throw new Error(
6595
+ `Projection failure after Beads close: GitHub Issue #${options.issue.number} close failed (Beads child ${options.beadsCompletion.childId} remains completed). ${error instanceof Error ? error.message : String(error)}`
6596
+ );
6597
+ }
6350
6598
  options.logger.step(
6351
6599
  "warn",
6352
6600
  `Issue #${options.issue.number} could not be closed: ${error instanceof Error ? error.message : String(error)}`
@@ -6545,7 +6793,7 @@ async function runFinalCommit(options) {
6545
6793
 
6546
6794
  // issues/issue-worktree-resolution.ts
6547
6795
  init_common();
6548
- import { join as join14 } from "path";
6796
+ import { join as join15 } from "path";
6549
6797
  async function resolveIssueWorktree(root, branchName, baseBranch, logger) {
6550
6798
  const worktreeList = await execCapture(
6551
6799
  "git",
@@ -6603,7 +6851,7 @@ async function resolveIssueWorktree(root, branchName, baseBranch, logger) {
6603
6851
  return { mode: "new", branchName, baseRef };
6604
6852
  }
6605
6853
  function issueWorktreePath(root, branchName) {
6606
- return join14(root, ".sandcastle", "worktrees", branchName.replace(/\//g, "-"));
6854
+ return join15(root, ".sandcastle", "worktrees", branchName.replace(/\//g, "-"));
6607
6855
  }
6608
6856
  function resolveRegisteredIssueWorktreePath(worktreeListPorcelain, root, branchName) {
6609
6857
  const branchWorktreePath = parseWorktreeListPorcelain(
@@ -6996,7 +7244,9 @@ async function completeIssueRun(options) {
6996
7244
  blocked: config.labels.blocked
6997
7245
  },
6998
7246
  checkWaitOptions,
6999
- secretLikeContentDetectionEnabled: true
7247
+ secretLikeContentDetectionEnabled: true,
7248
+ repoRoot: ROOT,
7249
+ beadsCompletion: options.beadsIssueRunContext
7000
7250
  });
7001
7251
  return {
7002
7252
  branchName,
@@ -7399,143 +7649,996 @@ async function runIssueCreateCommand(args, issueProvider, logger) {
7399
7649
  };
7400
7650
  }
7401
7651
 
7402
- // commands/prd-run.ts
7403
- import { lstatSync, realpathSync } from "fs";
7404
- import { join as join16 } from "path";
7405
-
7406
- // prd-run/state.ts
7407
- import {
7408
- existsSync as existsSync14,
7409
- mkdirSync as mkdirSync7,
7410
- readFileSync as readFileSync15,
7411
- readdirSync as readdirSync3,
7412
- writeFileSync as writeFileSync4
7413
- } from "fs";
7414
- import { join as join15 } from "path";
7415
- import { z as z2 } from "zod";
7652
+ // commands/prd-plan-workspace-publish.ts
7653
+ init_common();
7654
+ import { isAbsolute as isAbsolute6, relative as relative3, resolve as resolve3 } from "path";
7416
7655
 
7417
- // prd-run/repair-guidance.ts
7418
- import { z } from "zod";
7419
- var PrdRunRepairAttemptSchema = z.object({
7420
- attemptedAt: z.string().min(1),
7421
- outcome: z.enum(["blocked", "projection-failed", "unsupported-state"]),
7422
- failureCode: z.string().min(1),
7423
- summary: z.string().min(1)
7424
- }).strict();
7425
- var PrdRunRepairGuidanceSchema = z.object({
7426
- createdAt: z.string().min(1),
7427
- updatedAt: z.string().min(1),
7428
- blockedGate: z.enum([
7429
- "receipt-check",
7430
- "branch-state",
7431
- "git",
7432
- "queue",
7433
- "final-review"
7434
- ]),
7435
- blockedStage: z.string().min(1),
7436
- failureCode: z.string().regex(
7437
- /^[a-z][a-z0-9-]*$/,
7438
- "failureCode must be kebab-case (lowercase letters, digits, hyphens)"
7439
- ),
7440
- humanReadableReason: z.string().min(1),
7441
- repairability: z.enum([
7442
- "automatic-retry-safe",
7443
- "operator-action",
7444
- "unsupported-state"
7445
- ]),
7446
- nextAction: z.string().min(1),
7447
- nextRecommendedCommand: z.string().min(1),
7448
- diagnostics: z.array(z.string()).optional(),
7449
- offendingPaths: z.array(z.string()).optional(),
7450
- issue: z.object({
7451
- number: z.number().int().positive(),
7452
- title: z.string().optional(),
7453
- branch: z.string().optional(),
7454
- worktreePath: z.string().optional()
7455
- }).strict().optional(),
7456
- pr: z.object({
7457
- number: z.number().int().positive().optional(),
7458
- url: z.string().url().optional()
7459
- }).strict().optional(),
7460
- attemptHistory: z.array(PrdRunRepairAttemptSchema).optional(),
7461
- targetName: z.string().min(1).optional(),
7462
- baseBranch: z.string().min(1).optional()
7463
- }).strict();
7464
- function buildPrdRunRepairGuidance(input) {
7465
- const now = (/* @__PURE__ */ new Date()).toISOString();
7466
- return PrdRunRepairGuidanceSchema.parse({
7467
- createdAt: now,
7468
- updatedAt: now,
7469
- blockedGate: input.blockedGate,
7470
- blockedStage: input.blockedStage,
7471
- failureCode: input.failureCode,
7472
- humanReadableReason: input.humanReadableReason,
7473
- repairability: input.repairability,
7474
- nextAction: input.nextAction,
7475
- nextRecommendedCommand: input.nextRecommendedCommand,
7476
- diagnostics: input.diagnostics,
7477
- offendingPaths: input.offendingPaths,
7478
- issue: input.issue,
7479
- pr: input.pr,
7480
- targetName: input.targetName,
7481
- baseBranch: input.baseBranch,
7482
- attemptHistory: void 0
7483
- });
7656
+ // prd-plan-workspace/parser.ts
7657
+ import { readFile as readFile4, readdir } from "fs/promises";
7658
+ import { join as join16 } from "path";
7659
+ var CHILD_FILE_PATTERN = /^I-0*(\d+)-.+\.md$/i;
7660
+ var CHILD_REF_PATTERN = /^\s*(I-0*\d+)\s*$/i;
7661
+ var BLOCKED_BY_SECTION_REGEX = /## Blocked by\s*\n([\s\S]*?)(?=\n## |$)/i;
7662
+ var PARENT_SECTION_REGEX = /## Parent\s*\n([\s\S]*?)(?=\n## |$)/i;
7663
+ function escapeRegExp2(value) {
7664
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7484
7665
  }
7485
- function appendPrdRunRepairAttempt(guidance, attempt) {
7486
- const newAttempt = {
7487
- attemptedAt: attempt.attemptedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
7488
- outcome: attempt.outcome,
7489
- failureCode: attempt.failureCode,
7490
- summary: attempt.summary
7491
- };
7492
- const history = [...guidance.attemptHistory ?? [], newAttempt].slice(-5);
7493
- return PrdRunRepairGuidanceSchema.parse({
7494
- ...guidance,
7495
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7496
- attemptHistory: history
7497
- });
7666
+ function derivePrdBeadsId(prdRef) {
7667
+ const match = prdRef.match(/^PRD-0*(\d+)$/i);
7668
+ if (!match) {
7669
+ return prdRef.toLowerCase();
7670
+ }
7671
+ return `bd-${match[1].padStart(4, "0")}`;
7498
7672
  }
7499
- function validateActivePrdRunRepairGuidance(record) {
7500
- const now = (/* @__PURE__ */ new Date()).toISOString();
7501
- if (record.repairGuidance) {
7502
- const parsed = PrdRunRepairGuidanceSchema.safeParse(record.repairGuidance);
7503
- if (parsed.success) {
7504
- return { ok: true, guidance: parsed.data };
7505
- }
7506
- return {
7507
- ok: false,
7508
- guidance: {
7509
- createdAt: now,
7510
- updatedAt: now,
7511
- blockedGate: record.blockedGate ?? "queue",
7512
- blockedStage: "unknown",
7513
- failureCode: "malformed-repair-guidance",
7514
- humanReadableReason: "Repair guidance state is malformed or invalid. Inspect the PRD Run record.",
7515
- repairability: "unsupported-state",
7516
- nextAction: "inspect-prd-run-state",
7517
- nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
7518
- diagnostics: [
7519
- `Validation error: ${parsed.error?.message ?? "Unknown schema error"}`
7520
- ]
7521
- }
7522
- };
7673
+ function deriveChildBeadsId(prdRef, childRef) {
7674
+ const prdMatch = prdRef.match(/^PRD-0*(\d+)$/i);
7675
+ const childMatch = childRef.match(/^I-0*(\d+)$/i);
7676
+ if (!prdMatch || !childMatch) {
7677
+ return `${prdRef.toLowerCase()}.${childRef.toLowerCase()}`;
7523
7678
  }
7524
- return {
7525
- ok: false,
7526
- guidance: {
7527
- createdAt: now,
7528
- updatedAt: now,
7529
- blockedGate: record.blockedGate ?? "queue",
7530
- blockedStage: "unknown",
7531
- failureCode: "missing-repair-guidance",
7532
- humanReadableReason: "No active repair guidance found for a blocked or unresolved record.",
7533
- repairability: "unsupported-state",
7534
- nextAction: "inspect-prd-run-state",
7535
- nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
7536
- diagnostics: record.diagnostics ? [...record.diagnostics] : []
7537
- }
7538
- };
7679
+ return `bd-${prdMatch[1].padStart(4, "0")}.${childMatch[1]}`;
7680
+ }
7681
+ function parseChildRefsFromBody(body) {
7682
+ const section = body.match(BLOCKED_BY_SECTION_REGEX)?.[1];
7683
+ if (!section) return [];
7684
+ const refs = [];
7685
+ for (const rawLine of section.split("\n")) {
7686
+ const line = rawLine.trim();
7687
+ if (!line) continue;
7688
+ const parts = line.split(",");
7689
+ for (const part of parts) {
7690
+ const trimmed = part.trim();
7691
+ const match = trimmed.match(CHILD_REF_PATTERN);
7692
+ if (match) {
7693
+ refs.push(match[1].toUpperCase());
7694
+ }
7695
+ }
7696
+ }
7697
+ return refs;
7698
+ }
7699
+ function extractTitleBody(content) {
7700
+ const firstLine = content.split("\n")[0];
7701
+ const title = firstLine.startsWith("# ") ? firstLine.slice(2).trim() : "";
7702
+ const body = content.slice(firstLine.length).trim();
7703
+ return { title, body };
7704
+ }
7705
+ function hasParentSectionPointingToPrd(content, prdRef) {
7706
+ const section = content.match(PARENT_SECTION_REGEX)?.[1];
7707
+ if (!section) return false;
7708
+ return new RegExp(`\\b${escapeRegExp2(prdRef)}\\b`, "i").test(section);
7709
+ }
7710
+ async function parsePrdPlanWorkspace(options) {
7711
+ const { workspacePath, prdRef } = options;
7712
+ const diagnostics = [];
7713
+ const prdPath = join16(workspacePath, "PRD.md");
7714
+ const issuesDir = join16(workspacePath, "issues");
7715
+ let prdContent;
7716
+ try {
7717
+ prdContent = await readFile4(prdPath, "utf-8");
7718
+ } catch {
7719
+ diagnostics.push({
7720
+ severity: "error",
7721
+ message: `Missing PRD.md in workspace`,
7722
+ sourcePath: prdPath
7723
+ });
7724
+ return { ok: false, diagnostics };
7725
+ }
7726
+ const prdFile = extractTitleBody(prdContent);
7727
+ const prdHeadingMatch = prdFile.title.match(/^(PRD-\d{4}):/i);
7728
+ if (!prdHeadingMatch) {
7729
+ diagnostics.push({
7730
+ severity: "error",
7731
+ message: "PRD.md must start with a heading: # PRD-NNNN: Title",
7732
+ sourcePath: prdPath
7733
+ });
7734
+ } else if (prdHeadingMatch[1].toUpperCase() !== prdRef.toUpperCase()) {
7735
+ diagnostics.push({
7736
+ severity: "error",
7737
+ message: `PRD.md heading ${prdHeadingMatch[1].toUpperCase()} does not match ${prdRef}`,
7738
+ sourcePath: prdPath
7739
+ });
7740
+ }
7741
+ const parent = {
7742
+ prdRef,
7743
+ title: prdFile.title,
7744
+ body: prdFile.body,
7745
+ sourcePath: prdPath,
7746
+ beadsId: derivePrdBeadsId(prdRef)
7747
+ };
7748
+ let childFiles;
7749
+ try {
7750
+ childFiles = (await readdir(issuesDir)).filter(
7751
+ (f) => CHILD_FILE_PATTERN.test(f) && !f.startsWith(".")
7752
+ );
7753
+ } catch {
7754
+ diagnostics.push({
7755
+ severity: "error",
7756
+ message: `Missing issues/ directory`,
7757
+ sourcePath: issuesDir
7758
+ });
7759
+ return { ok: false, diagnostics };
7760
+ }
7761
+ childFiles.sort((a, b) => {
7762
+ const aMatch = a.match(CHILD_FILE_PATTERN);
7763
+ const bMatch = b.match(CHILD_FILE_PATTERN);
7764
+ if (!aMatch || !bMatch) return 0;
7765
+ return Number(aMatch[1]) - Number(bMatch[1]);
7766
+ });
7767
+ const children = [];
7768
+ for (const childFile of childFiles) {
7769
+ const childPath = join16(issuesDir, childFile);
7770
+ const childContent = await readFile4(childPath, "utf-8");
7771
+ const childFileData = extractTitleBody(childContent);
7772
+ const childRefMatch = childFile.match(CHILD_FILE_PATTERN);
7773
+ const childRef = childRefMatch ? `I-${childRefMatch[1].padStart(2, "0")}` : "";
7774
+ const childNumber = childRefMatch ? Number(childRefMatch[1]) : 0;
7775
+ const blockedByRefs = parseChildRefsFromBody(childContent);
7776
+ if (!childFileData.title.match(
7777
+ new RegExp(`^${escapeRegExp2(prdRef)} / ${escapeRegExp2(childRef)}:`)
7778
+ )) {
7779
+ diagnostics.push({
7780
+ severity: "error",
7781
+ message: `${childFile}: expected heading: # ${prdRef} / ${childRef}: Title`,
7782
+ sourcePath: childPath
7783
+ });
7784
+ }
7785
+ if (!hasParentSectionPointingToPrd(childContent, prdRef)) {
7786
+ diagnostics.push({
7787
+ severity: "error",
7788
+ message: `${childFile}: missing ## Parent section pointing to ${prdRef}`,
7789
+ sourcePath: childPath
7790
+ });
7791
+ }
7792
+ children.push({
7793
+ childRef,
7794
+ childNumber,
7795
+ title: childFileData.title,
7796
+ body: childFileData.body,
7797
+ sourcePath: childPath,
7798
+ beadsId: deriveChildBeadsId(prdRef, childRef),
7799
+ blockedByRefs
7800
+ });
7801
+ }
7802
+ const validChildRefs = new Set(children.map((c) => c.childRef));
7803
+ for (const child of children) {
7804
+ for (const blockedRef of child.blockedByRefs) {
7805
+ if (!validChildRefs.has(blockedRef.toUpperCase())) {
7806
+ diagnostics.push({
7807
+ severity: "error",
7808
+ message: `Child ${child.childRef} references nonexistent child ${blockedRef} in Blocked by`,
7809
+ sourcePath: child.sourcePath
7810
+ });
7811
+ }
7812
+ }
7813
+ }
7814
+ const model = {
7815
+ prdRef,
7816
+ workspacePath,
7817
+ parent,
7818
+ children,
7819
+ diagnostics
7820
+ };
7821
+ if (diagnostics.length > 0) {
7822
+ return { ok: false, diagnostics };
7823
+ }
7824
+ return { ok: true, workspace: model };
7825
+ }
7826
+
7827
+ // prd-plan-workspace/publish-receipt.ts
7828
+ import { mkdir as mkdir4, writeFile } from "fs/promises";
7829
+ import { dirname as dirname5, join as join17 } from "path";
7830
+ function yamlEntry(key, value, indent) {
7831
+ const pad = " ".repeat(indent);
7832
+ if (value === null || value === void 0) {
7833
+ return `${pad}${key}: ~`;
7834
+ }
7835
+ if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
7836
+ return `${pad}${key}: ${value}`;
7837
+ }
7838
+ if (Array.isArray(value)) {
7839
+ if (value.length === 0) {
7840
+ return `${pad}${key}: []`;
7841
+ }
7842
+ const itemPad = " ".repeat(indent + 2);
7843
+ const innerIndent = indent + 4;
7844
+ const lines = value.map((item) => {
7845
+ if (typeof item === "object" && item !== null) {
7846
+ const entries = Object.entries(item);
7847
+ const firstLine = `${itemPad}- ${entries[0][0]}: ${entries[0][1]}`;
7848
+ const restLines = entries.slice(1).map(([k, v]) => yamlEntry(k, v, innerIndent));
7849
+ return [firstLine, ...restLines].join("\n");
7850
+ }
7851
+ return `${itemPad}- ${String(item)}`;
7852
+ });
7853
+ return `${pad}${key}:
7854
+ ${lines.join("\n")}`;
7855
+ }
7856
+ if (typeof value === "object") {
7857
+ const inner = Object.entries(value).map(([k, v]) => yamlEntry(k, v, indent + 2)).join("\n");
7858
+ return `${pad}${key}:
7859
+ ${inner}`;
7860
+ }
7861
+ return `${pad}${key}: ${String(value)}`;
7862
+ }
7863
+ function renderPrdPublishReceipt(options) {
7864
+ const data = {
7865
+ workspace: options.workspacePath,
7866
+ route: "prd",
7867
+ parent: {
7868
+ prd_ref: options.parent.prdRef,
7869
+ beads_id: options.parent.beadsId,
7870
+ number: options.parent.githubIssue.number,
7871
+ url: options.parent.githubIssue.url
7872
+ },
7873
+ children: options.children.map((c) => ({
7874
+ child_ref: c.childRef,
7875
+ beads_id: c.beadsId,
7876
+ number: c.githubIssue.number,
7877
+ url: c.githubIssue.url,
7878
+ source_file: c.sourceFile
7879
+ })),
7880
+ dependency_mappings: options.dependencyMappings.map((d) => ({
7881
+ from: d.from,
7882
+ to: d.to,
7883
+ beads_edge_type: d.beadsEdgeType
7884
+ })),
7885
+ publication_timestamp: options.publicationTimestamp,
7886
+ authored_bodies_mutated: false,
7887
+ warnings: options.warnings
7888
+ };
7889
+ return Object.entries(data).map(([k, v]) => yamlEntry(k, v, 0)).join("\n") + "\n";
7890
+ }
7891
+ async function writePrdPublishReceipt(options) {
7892
+ const receipt = renderPrdPublishReceipt(options);
7893
+ const publishPath = join17(options.workspaceRoot, "PUBLISH.md");
7894
+ await mkdir4(dirname5(publishPath), { recursive: true });
7895
+ await writeFile(publishPath, receipt, "utf-8");
7896
+ return publishPath;
7897
+ }
7898
+
7899
+ // beads/preflight.ts
7900
+ init_common();
7901
+ async function checkBeadsAvailability(options) {
7902
+ try {
7903
+ await execCapture("which", ["bd"], {
7904
+ cwd: options.repoRoot,
7905
+ logger: options.logger
7906
+ });
7907
+ } catch {
7908
+ return {
7909
+ available: false,
7910
+ error: "bd CLI is not installed or not on PATH",
7911
+ repairGuidance: "Install Beads CLI (bd), then run your workflow again."
7912
+ };
7913
+ }
7914
+ try {
7915
+ await runBd({
7916
+ repoRoot: options.repoRoot,
7917
+ args: ["ready", "--json"],
7918
+ logger: options.logger
7919
+ });
7920
+ } catch {
7921
+ return {
7922
+ available: false,
7923
+ error: "Beads repository state is unusable",
7924
+ repairGuidance: "Run 'bd init' in the repository root to initialize Beads state, then run your workflow again."
7925
+ };
7926
+ }
7927
+ return { available: true };
7928
+ }
7929
+
7930
+ // beads/prd-publication.ts
7931
+ function isMissingIssueMessage(value) {
7932
+ return /no issues? found matching/i.test(value);
7933
+ }
7934
+ function parentMetadata(options) {
7935
+ return {
7936
+ pourkit: {
7937
+ prdRef: options.workspace.prdRef,
7938
+ workspacePath: options.workspace.workspacePath,
7939
+ sourcePath: options.workspace.parent.sourcePath,
7940
+ ...options.githubIssue ? {
7941
+ githubIssueNumber: options.githubIssue.number,
7942
+ githubIssueUrl: options.githubIssue.url
7943
+ } : {}
7944
+ }
7945
+ };
7946
+ }
7947
+ function childMetadata(options) {
7948
+ return {
7949
+ pourkit: {
7950
+ prdRef: options.workspace.prdRef,
7951
+ childRef: options.child.childRef,
7952
+ workspacePath: options.workspace.workspacePath,
7953
+ sourcePath: options.child.sourcePath,
7954
+ ...options.githubIssue ? {
7955
+ githubIssueNumber: options.githubIssue.number,
7956
+ githubIssueUrl: options.githubIssue.url
7957
+ } : {}
7958
+ }
7959
+ };
7960
+ }
7961
+ async function beadExists(options) {
7962
+ try {
7963
+ const result = await runBdJson({
7964
+ repoRoot: options.repoRoot,
7965
+ args: ["show", options.beadsId, "--json"],
7966
+ logger: options.logger
7967
+ });
7968
+ if (result?.error) {
7969
+ if (isMissingIssueMessage(result.error)) return false;
7970
+ throw new Error(result.error);
7971
+ }
7972
+ if (result?.id !== options.beadsId) {
7973
+ throw new Error(
7974
+ `Expected Beads issue ${options.beadsId}, but bd show returned ${result?.id ?? "no id"}.`
7975
+ );
7976
+ }
7977
+ return true;
7978
+ } catch (error) {
7979
+ const message = error instanceof Error ? error.message : String(error);
7980
+ if (isMissingIssueMessage(message)) return false;
7981
+ throw error;
7982
+ }
7983
+ }
7984
+ function assertExpectedBeadId(options) {
7985
+ if (options.response?.id !== options.expectedId) {
7986
+ throw new Error(
7987
+ `${options.action} returned ${options.response?.id ?? "no id"}; expected ${options.expectedId}.`
7988
+ );
7989
+ }
7990
+ }
7991
+ async function createOrUpdateParent(options) {
7992
+ const exists = await beadExists({
7993
+ repoRoot: options.repoRoot,
7994
+ beadsId: options.workspace.parent.beadsId,
7995
+ logger: options.logger
7996
+ });
7997
+ const response = await runBdJson({
7998
+ repoRoot: options.repoRoot,
7999
+ args: exists ? [
8000
+ "update",
8001
+ options.workspace.parent.beadsId,
8002
+ "--title",
8003
+ options.workspace.parent.title,
8004
+ "--description",
8005
+ options.workspace.parent.body,
8006
+ "-t",
8007
+ "epic",
8008
+ "--metadata",
8009
+ JSON.stringify(parentMetadata({ workspace: options.workspace })),
8010
+ "--json"
8011
+ ] : [
8012
+ "create",
8013
+ "--id",
8014
+ options.workspace.parent.beadsId,
8015
+ "--title",
8016
+ options.workspace.parent.title,
8017
+ "-t",
8018
+ "epic",
8019
+ "--description",
8020
+ options.workspace.parent.body,
8021
+ "--metadata",
8022
+ JSON.stringify(parentMetadata({ workspace: options.workspace })),
8023
+ "--json"
8024
+ ],
8025
+ logger: options.logger
8026
+ });
8027
+ if (!exists) {
8028
+ assertExpectedBeadId({
8029
+ expectedId: options.workspace.parent.beadsId,
8030
+ response,
8031
+ action: "bd create parent"
8032
+ });
8033
+ }
8034
+ }
8035
+ async function createOrUpdateChild(options) {
8036
+ const exists = await beadExists({
8037
+ repoRoot: options.repoRoot,
8038
+ beadsId: options.child.beadsId,
8039
+ logger: options.logger
8040
+ });
8041
+ const response = await runBdJson({
8042
+ repoRoot: options.repoRoot,
8043
+ args: exists ? [
8044
+ "update",
8045
+ options.child.beadsId,
8046
+ "--title",
8047
+ options.child.title,
8048
+ "--description",
8049
+ options.child.body,
8050
+ "--parent",
8051
+ options.workspace.parent.beadsId,
8052
+ "--metadata",
8053
+ JSON.stringify(
8054
+ childMetadata({
8055
+ workspace: options.workspace,
8056
+ child: options.child
8057
+ })
8058
+ ),
8059
+ "--json"
8060
+ ] : [
8061
+ "create",
8062
+ "--parent",
8063
+ options.workspace.parent.beadsId,
8064
+ "--title",
8065
+ options.child.title,
8066
+ "--description",
8067
+ options.child.body,
8068
+ "--metadata",
8069
+ JSON.stringify(
8070
+ childMetadata({
8071
+ workspace: options.workspace,
8072
+ child: options.child
8073
+ })
8074
+ ),
8075
+ "--json"
8076
+ ],
8077
+ logger: options.logger
8078
+ });
8079
+ if (!exists) {
8080
+ assertExpectedBeadId({
8081
+ expectedId: options.child.beadsId,
8082
+ response,
8083
+ action: "bd create child"
8084
+ });
8085
+ }
8086
+ }
8087
+ async function registerPrdWorkspaceInBeads(options) {
8088
+ const { repoRoot: repoRoot2, workspace, logger } = options;
8089
+ const preflight = await checkBeadsAvailability({ repoRoot: repoRoot2, logger });
8090
+ if (!preflight.available) {
8091
+ throw new Error(
8092
+ `Beads is not available: ${preflight.error}. ${preflight.repairGuidance}`
8093
+ );
8094
+ }
8095
+ await createOrUpdateParent({
8096
+ repoRoot: repoRoot2,
8097
+ workspace,
8098
+ logger
8099
+ });
8100
+ const children = [];
8101
+ const dependencyMappings = [];
8102
+ for (const child of workspace.children) {
8103
+ await createOrUpdateChild({
8104
+ repoRoot: repoRoot2,
8105
+ workspace,
8106
+ child,
8107
+ logger
8108
+ });
8109
+ children.push({
8110
+ childRef: child.childRef,
8111
+ beadsId: child.beadsId,
8112
+ sourcePath: child.sourcePath
8113
+ });
8114
+ }
8115
+ for (const child of workspace.children) {
8116
+ for (const blockedRef of child.blockedByRefs) {
8117
+ const blocker = workspace.children.find((c) => c.childRef === blockedRef);
8118
+ if (!blocker) continue;
8119
+ await runBdJson({
8120
+ repoRoot: repoRoot2,
8121
+ args: ["dep", "add", child.beadsId, blocker.beadsId, "--json"],
8122
+ logger
8123
+ });
8124
+ dependencyMappings.push({
8125
+ from: child.childRef,
8126
+ to: blocker.childRef,
8127
+ beadsEdgeType: "blocks"
8128
+ });
8129
+ }
8130
+ }
8131
+ return {
8132
+ parent: {
8133
+ prdRef: workspace.prdRef,
8134
+ beadsId: workspace.parent.beadsId
8135
+ },
8136
+ children,
8137
+ dependencyMappings
8138
+ };
8139
+ }
8140
+ async function backfillPrdWorkspaceBeadsGithubMetadata(options) {
8141
+ const childIssueByRef = new Map(
8142
+ options.childGithubIssues.map((child) => [
8143
+ child.childRef,
8144
+ child.githubIssue
8145
+ ])
8146
+ );
8147
+ await runBdJson({
8148
+ repoRoot: options.repoRoot,
8149
+ args: [
8150
+ "update",
8151
+ options.workspace.parent.beadsId,
8152
+ "--metadata",
8153
+ JSON.stringify(
8154
+ parentMetadata({
8155
+ workspace: options.workspace,
8156
+ githubIssue: options.parentGithubIssue
8157
+ })
8158
+ ),
8159
+ "--json"
8160
+ ],
8161
+ logger: options.logger
8162
+ });
8163
+ for (const child of options.workspace.children) {
8164
+ const githubIssue = childIssueByRef.get(child.childRef);
8165
+ if (!githubIssue) {
8166
+ throw new Error(
8167
+ `Missing GitHub issue projection for Beads child ${child.beadsId} (${child.childRef}).`
8168
+ );
8169
+ }
8170
+ await runBdJson({
8171
+ repoRoot: options.repoRoot,
8172
+ args: [
8173
+ "update",
8174
+ child.beadsId,
8175
+ "--metadata",
8176
+ JSON.stringify(
8177
+ childMetadata({
8178
+ workspace: options.workspace,
8179
+ child,
8180
+ githubIssue
8181
+ })
8182
+ ),
8183
+ "--json"
8184
+ ],
8185
+ logger: options.logger
8186
+ });
8187
+ }
8188
+ }
8189
+
8190
+ // commands/prd-plan-workspace-publish.ts
8191
+ var DEFAULT_LABELS = {
8192
+ readyForAgent: "ready-for-agent",
8193
+ blocked: "blocked",
8194
+ needsTriage: "needs-triage"
8195
+ };
8196
+ function normalizePrdRef(prdRef) {
8197
+ const match = prdRef.trim().toUpperCase().match(/^PRD-(\d+)$/);
8198
+ if (!match) {
8199
+ throw new Error(
8200
+ `Invalid PRD ref "${prdRef}". Expected format: PRD-<number> (e.g., PRD-0078)`
8201
+ );
8202
+ }
8203
+ return `PRD-${match[1].padStart(4, "0")}`;
8204
+ }
8205
+ function repoRelativePath(repoRoot2, absolutePath) {
8206
+ const rel = relative3(repoRoot2, absolutePath).replace(/\\/g, "/");
8207
+ if (rel === "") return ".";
8208
+ if (rel === ".." || rel.startsWith("../") || isAbsolute6(rel)) {
8209
+ throw new Error(`Path is outside the repository: ${absolutePath}`);
8210
+ }
8211
+ return rel;
8212
+ }
8213
+ function resolveWorkspaceRoot(options) {
8214
+ const workspaceRoot = isAbsolute6(options.workspacePath) ? options.workspacePath : resolve3(options.repoRoot, options.workspacePath);
8215
+ const workspaceRelPath = repoRelativePath(options.repoRoot, workspaceRoot);
8216
+ if (!workspaceRelPath.startsWith(".pourkit/plans/")) {
8217
+ throw new Error(
8218
+ `PRD Plan Workspace must be under .pourkit/plans/: ${workspaceRelPath}`
8219
+ );
8220
+ }
8221
+ return { workspaceRoot, workspaceRelPath };
8222
+ }
8223
+ function relativizeWorkspaceModel(options) {
8224
+ return {
8225
+ ...options.workspace,
8226
+ workspacePath: repoRelativePath(
8227
+ options.repoRoot,
8228
+ resolve3(options.workspace.workspacePath)
8229
+ ),
8230
+ parent: {
8231
+ ...options.workspace.parent,
8232
+ sourcePath: repoRelativePath(
8233
+ options.repoRoot,
8234
+ resolve3(options.workspace.parent.sourcePath)
8235
+ )
8236
+ },
8237
+ children: options.workspace.children.map((child) => ({
8238
+ ...child,
8239
+ sourcePath: repoRelativePath(options.repoRoot, resolve3(child.sourcePath))
8240
+ }))
8241
+ };
8242
+ }
8243
+ function formatValidationFailure(result) {
8244
+ const diagnostics = result.diagnostics.length ? ` Diagnostics: ${result.diagnostics.join("; ")}` : "";
8245
+ return `PRD Plan Workspace Publication Gate failed: ${result.reason}.${diagnostics}`;
8246
+ }
8247
+ function extractTypeLabels(body) {
8248
+ const labelsLine = body.match(/^Labels:\s*(.+)$/im)?.[1];
8249
+ if (!labelsLine) return [];
8250
+ const backtickLabels = Array.from(labelsLine.matchAll(/`([^`]+)`/g)).map(
8251
+ (match) => match[1].trim()
8252
+ );
8253
+ const labels = backtickLabels.length > 0 ? backtickLabels : labelsLine.split(/[\s,]+/).map((label) => label.trim());
8254
+ return labels.filter(
8255
+ (label) => TYPE_LABELS.includes(label)
8256
+ );
8257
+ }
8258
+ function unique(values) {
8259
+ return Array.from(new Set(values));
8260
+ }
8261
+ function logStep(logger, level, message) {
8262
+ logger?.step?.(level, message);
8263
+ }
8264
+ function childLabels(options) {
8265
+ const typeLabels = extractTypeLabels(options.body);
8266
+ let typeLabel = typeLabels[0];
8267
+ if (!typeLabel) {
8268
+ typeLabel = "type:feature";
8269
+ options.warnings.push(
8270
+ `${options.childRef}: no type:* label found; defaulted to type:feature.`
8271
+ );
8272
+ } else if (typeLabels.length > 1) {
8273
+ options.warnings.push(
8274
+ `${options.childRef}: multiple type:* labels found; kept ${typeLabel}.`
8275
+ );
8276
+ }
8277
+ return unique([
8278
+ options.labels.readyForAgent,
8279
+ typeLabel,
8280
+ ...options.blocked ? [options.labels.blocked] : []
8281
+ ]);
8282
+ }
8283
+ async function syncManagedLabels(options) {
8284
+ const desired = new Set(options.desiredLabels);
8285
+ const managed = /* @__PURE__ */ new Set([
8286
+ options.labels.readyForAgent,
8287
+ options.labels.blocked,
8288
+ options.labels.needsTriage,
8289
+ ...TYPE_LABELS
8290
+ ]);
8291
+ for (const label of options.existingLabels) {
8292
+ if (managed.has(label) && !desired.has(label)) {
8293
+ await options.issueProvider.removeLabel(options.issueNumber, label);
8294
+ }
8295
+ }
8296
+ const existing = new Set(options.existingLabels);
8297
+ const missing = options.desiredLabels.filter((label) => !existing.has(label));
8298
+ if (missing.length > 0) {
8299
+ await options.issueProvider.addLabels(options.issueNumber, missing);
8300
+ }
8301
+ }
8302
+ async function upsertGithubIssue(options) {
8303
+ const existing = await options.issueProvider.resolveIssueByCanonicalRef(
8304
+ options.canonicalRef
8305
+ );
8306
+ if (existing) {
8307
+ const updated = await options.issueProvider.updateIssue(existing.number, {
8308
+ title: options.title,
8309
+ body: options.body
8310
+ });
8311
+ await syncManagedLabels({
8312
+ issueProvider: options.issueProvider,
8313
+ issueNumber: existing.number,
8314
+ existingLabels: existing.labels,
8315
+ desiredLabels: options.labels,
8316
+ labels: options.publishLabels
8317
+ });
8318
+ return { ...updated, labels: options.labels };
8319
+ }
8320
+ const created = await options.issueProvider.createIssue({
8321
+ title: options.title,
8322
+ body: options.body,
8323
+ labels: options.labels
8324
+ });
8325
+ return { ...created, labels: options.labels };
8326
+ }
8327
+ async function publishWorkspaceToGithub(options) {
8328
+ const parent = await upsertGithubIssue({
8329
+ issueProvider: options.issueProvider,
8330
+ canonicalRef: options.workspace.prdRef,
8331
+ title: options.workspace.parent.title,
8332
+ body: options.workspace.parent.body,
8333
+ labels: [options.labels.needsTriage],
8334
+ publishLabels: options.labels
8335
+ });
8336
+ const children = [];
8337
+ for (const child of options.workspace.children) {
8338
+ const labels = childLabels({
8339
+ body: child.body,
8340
+ blocked: child.blockedByRefs.length > 0,
8341
+ labels: options.labels,
8342
+ warnings: options.warnings,
8343
+ childRef: child.childRef
8344
+ });
8345
+ const githubIssue = await upsertGithubIssue({
8346
+ issueProvider: options.issueProvider,
8347
+ canonicalRef: `${options.workspace.prdRef} / ${child.childRef}`,
8348
+ title: child.title,
8349
+ body: child.body,
8350
+ labels,
8351
+ publishLabels: options.labels
8352
+ });
8353
+ children.push({ childRef: child.childRef, githubIssue });
8354
+ }
8355
+ return { parent, children };
8356
+ }
8357
+ async function runPrdPlanWorkspacePublishCommand(options) {
8358
+ const prdRef = normalizePrdRef(options.prdRef);
8359
+ const labels = { ...DEFAULT_LABELS, ...options.labels };
8360
+ const { workspaceRoot, workspaceRelPath } = resolveWorkspaceRoot({
8361
+ repoRoot: options.repoRoot,
8362
+ workspacePath: options.workspacePath
8363
+ });
8364
+ logStep(
8365
+ options.logger,
8366
+ "info",
8367
+ `Publishing PRD Plan Workspace ${workspaceRelPath} for ${prdRef}.`
8368
+ );
8369
+ logStep(
8370
+ options.logger,
8371
+ "info",
8372
+ `Running PRD Plan Workspace Publication Gate for ${prdRef}.`
8373
+ );
8374
+ const validation = runValidateArtifactCommand({
8375
+ repoRoot: options.repoRoot,
8376
+ kind: "prd-plan-workspace",
8377
+ artifactPath: workspaceRoot,
8378
+ workspace: workspaceRoot,
8379
+ prdRef
8380
+ });
8381
+ if (!validation.ok) {
8382
+ throw new Error(formatValidationFailure(validation));
8383
+ }
8384
+ const parsed = await parsePrdPlanWorkspace({
8385
+ workspacePath: workspaceRoot,
8386
+ prdRef
8387
+ });
8388
+ if (!parsed.ok) {
8389
+ throw new Error(
8390
+ `PRD Plan Workspace parse failed: ${parsed.diagnostics.map((d) => d.message).join("; ")}`
8391
+ );
8392
+ }
8393
+ if (parsed.workspace.children.length === 0) {
8394
+ throw new Error(
8395
+ "PRD Plan Workspace must contain at least one child Issue."
8396
+ );
8397
+ }
8398
+ const workspace = relativizeWorkspaceModel({
8399
+ repoRoot: options.repoRoot,
8400
+ workspace: parsed.workspace
8401
+ });
8402
+ logStep(
8403
+ options.logger,
8404
+ "info",
8405
+ `Publication gate passed for ${prdRef}: ${workspace.children.length} child Issue(s).`
8406
+ );
8407
+ logStep(
8408
+ options.logger,
8409
+ "info",
8410
+ `Registering Beads Work Graph for ${prdRef}: ${workspace.parent.beadsId}.`
8411
+ );
8412
+ const beadsPublication = await registerPrdWorkspaceInBeads({
8413
+ repoRoot: options.repoRoot,
8414
+ workspace,
8415
+ logger: options.logger
8416
+ });
8417
+ logStep(
8418
+ options.logger,
8419
+ "info",
8420
+ `Registered Beads Work Graph for ${prdRef}: ${beadsPublication.children.length} child bead(s), ${beadsPublication.dependencyMappings.length} dependency edge(s).`
8421
+ );
8422
+ const warnings = [];
8423
+ logStep(options.logger, "info", `Projecting ${prdRef} to GitHub Issues.`);
8424
+ const githubPublication = await publishWorkspaceToGithub({
8425
+ workspace,
8426
+ issueProvider: options.issueProvider,
8427
+ labels,
8428
+ warnings
8429
+ });
8430
+ logStep(
8431
+ options.logger,
8432
+ "info",
8433
+ `Projected ${prdRef} to GitHub: parent #${githubPublication.parent.number}, ${githubPublication.children.length} child Issue(s).`
8434
+ );
8435
+ logStep(
8436
+ options.logger,
8437
+ "info",
8438
+ `Backfilling Beads metadata for ${prdRef} with GitHub Issue numbers and URLs.`
8439
+ );
8440
+ await backfillPrdWorkspaceBeadsGithubMetadata({
8441
+ repoRoot: options.repoRoot,
8442
+ workspace,
8443
+ parentGithubIssue: githubPublication.parent,
8444
+ childGithubIssues: githubPublication.children.map((child) => ({
8445
+ childRef: child.childRef,
8446
+ githubIssue: child.githubIssue
8447
+ })),
8448
+ logger: options.logger
8449
+ });
8450
+ logStep(options.logger, "info", `Backfilled Beads metadata for ${prdRef}.`);
8451
+ const children = workspace.children.map((child) => {
8452
+ const githubChild = githubPublication.children.find(
8453
+ (published) => published.childRef === child.childRef
8454
+ );
8455
+ if (!githubChild) {
8456
+ throw new Error(
8457
+ `Missing GitHub publication result for ${child.childRef}.`
8458
+ );
8459
+ }
8460
+ return {
8461
+ childRef: child.childRef,
8462
+ beadsId: child.beadsId,
8463
+ githubIssue: githubChild.githubIssue,
8464
+ sourceFile: child.sourcePath
8465
+ };
8466
+ });
8467
+ const publishPath = await writePrdPublishReceipt({
8468
+ workspaceRoot,
8469
+ workspacePath: workspace.workspacePath,
8470
+ parent: {
8471
+ prdRef,
8472
+ beadsId: beadsPublication.parent.beadsId,
8473
+ githubIssue: githubPublication.parent
8474
+ },
8475
+ children,
8476
+ dependencyMappings: beadsPublication.dependencyMappings,
8477
+ publicationTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
8478
+ authoredBodiesMutated: false,
8479
+ warnings
8480
+ });
8481
+ logStep(
8482
+ options.logger,
8483
+ "info",
8484
+ `Wrote PRD publication receipt for ${prdRef}: ${publishPath}.`
8485
+ );
8486
+ return {
8487
+ status: "published",
8488
+ prdRef,
8489
+ workspacePath: workspace.workspacePath,
8490
+ parent: {
8491
+ prdRef,
8492
+ beadsId: beadsPublication.parent.beadsId,
8493
+ githubIssue: githubPublication.parent
8494
+ },
8495
+ children,
8496
+ dependencyMappings: beadsPublication.dependencyMappings,
8497
+ publishPath,
8498
+ warnings
8499
+ };
8500
+ }
8501
+
8502
+ // commands/prd-run.ts
8503
+ import { lstatSync, realpathSync } from "fs";
8504
+ import { join as join19 } from "path";
8505
+
8506
+ // prd-run/state.ts
8507
+ import {
8508
+ existsSync as existsSync14,
8509
+ mkdirSync as mkdirSync7,
8510
+ readFileSync as readFileSync15,
8511
+ readdirSync as readdirSync4,
8512
+ writeFileSync as writeFileSync4
8513
+ } from "fs";
8514
+ import { join as join18 } from "path";
8515
+ import { z as z2 } from "zod";
8516
+
8517
+ // prd-run/repair-guidance.ts
8518
+ import { z } from "zod";
8519
+ var PrdRunRepairAttemptSchema = z.object({
8520
+ attemptedAt: z.string().min(1),
8521
+ outcome: z.enum(["blocked", "projection-failed", "unsupported-state"]),
8522
+ failureCode: z.string().min(1),
8523
+ summary: z.string().min(1)
8524
+ }).strict();
8525
+ var PrdRunRepairGuidanceSchema = z.object({
8526
+ createdAt: z.string().min(1),
8527
+ updatedAt: z.string().min(1),
8528
+ blockedGate: z.enum([
8529
+ "receipt-check",
8530
+ "branch-state",
8531
+ "git",
8532
+ "queue",
8533
+ "final-review"
8534
+ ]),
8535
+ blockedStage: z.string().min(1),
8536
+ failureCode: z.string().regex(
8537
+ /^[a-z][a-z0-9-]*$/,
8538
+ "failureCode must be kebab-case (lowercase letters, digits, hyphens)"
8539
+ ),
8540
+ humanReadableReason: z.string().min(1),
8541
+ repairability: z.enum([
8542
+ "automatic-retry-safe",
8543
+ "operator-action",
8544
+ "unsupported-state"
8545
+ ]),
8546
+ nextAction: z.string().min(1),
8547
+ nextRecommendedCommand: z.string().min(1),
8548
+ diagnostics: z.array(z.string()).optional(),
8549
+ offendingPaths: z.array(z.string()).optional(),
8550
+ issue: z.object({
8551
+ number: z.number().int().positive(),
8552
+ title: z.string().optional(),
8553
+ branch: z.string().optional(),
8554
+ worktreePath: z.string().optional(),
8555
+ beadsChildId: z.string().min(1).optional(),
8556
+ beadsChildRef: z.string().min(1).optional(),
8557
+ beadsChildClosed: z.boolean().optional()
8558
+ }).strict().optional(),
8559
+ pr: z.object({
8560
+ number: z.number().int().positive().optional(),
8561
+ url: z.string().url().optional()
8562
+ }).strict().optional(),
8563
+ attemptHistory: z.array(PrdRunRepairAttemptSchema).optional(),
8564
+ targetName: z.string().min(1).optional(),
8565
+ baseBranch: z.string().min(1).optional()
8566
+ }).strict();
8567
+ function buildPrdRunRepairGuidance(input) {
8568
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8569
+ return PrdRunRepairGuidanceSchema.parse({
8570
+ createdAt: now,
8571
+ updatedAt: now,
8572
+ blockedGate: input.blockedGate,
8573
+ blockedStage: input.blockedStage,
8574
+ failureCode: input.failureCode,
8575
+ humanReadableReason: input.humanReadableReason,
8576
+ repairability: input.repairability,
8577
+ nextAction: input.nextAction,
8578
+ nextRecommendedCommand: input.nextRecommendedCommand,
8579
+ diagnostics: input.diagnostics,
8580
+ offendingPaths: input.offendingPaths,
8581
+ issue: input.issue,
8582
+ pr: input.pr,
8583
+ targetName: input.targetName,
8584
+ baseBranch: input.baseBranch,
8585
+ attemptHistory: void 0
8586
+ });
8587
+ }
8588
+ function appendPrdRunRepairAttempt(guidance, attempt) {
8589
+ const newAttempt = {
8590
+ attemptedAt: attempt.attemptedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
8591
+ outcome: attempt.outcome,
8592
+ failureCode: attempt.failureCode,
8593
+ summary: attempt.summary
8594
+ };
8595
+ const history = [...guidance.attemptHistory ?? [], newAttempt].slice(-5);
8596
+ return PrdRunRepairGuidanceSchema.parse({
8597
+ ...guidance,
8598
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8599
+ attemptHistory: history
8600
+ });
8601
+ }
8602
+ function validateActivePrdRunRepairGuidance(record) {
8603
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8604
+ if (record.repairGuidance) {
8605
+ const parsed = PrdRunRepairGuidanceSchema.safeParse(record.repairGuidance);
8606
+ if (parsed.success) {
8607
+ return { ok: true, guidance: parsed.data };
8608
+ }
8609
+ return {
8610
+ ok: false,
8611
+ guidance: {
8612
+ createdAt: now,
8613
+ updatedAt: now,
8614
+ blockedGate: record.blockedGate ?? "queue",
8615
+ blockedStage: "unknown",
8616
+ failureCode: "malformed-repair-guidance",
8617
+ humanReadableReason: "Repair guidance state is malformed or invalid. Inspect the PRD Run record.",
8618
+ repairability: "unsupported-state",
8619
+ nextAction: "inspect-prd-run-state",
8620
+ nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
8621
+ diagnostics: [
8622
+ `Validation error: ${parsed.error?.message ?? "Unknown schema error"}`
8623
+ ]
8624
+ }
8625
+ };
8626
+ }
8627
+ return {
8628
+ ok: false,
8629
+ guidance: {
8630
+ createdAt: now,
8631
+ updatedAt: now,
8632
+ blockedGate: record.blockedGate ?? "queue",
8633
+ blockedStage: "unknown",
8634
+ failureCode: "missing-repair-guidance",
8635
+ humanReadableReason: "No active repair guidance found for a blocked or unresolved record.",
8636
+ repairability: "unsupported-state",
8637
+ nextAction: "inspect-prd-run-state",
8638
+ nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
8639
+ diagnostics: record.diagnostics ? [...record.diagnostics] : []
8640
+ }
8641
+ };
7539
8642
  }
7540
8643
  function renderPrdRunRepairGuidance(guidance) {
7541
8644
  const lines = [];
@@ -7716,17 +8819,17 @@ function readPrdRun(repoRoot2, prdRef) {
7716
8819
  }
7717
8820
  }
7718
8821
  function listPrdRuns(repoRoot2) {
7719
- const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
8822
+ const stateDir = join18(repoRoot2, PRD_RUN_STATE_DIR);
7720
8823
  if (!existsSync14(stateDir)) {
7721
8824
  return { records: [], diagnostics: [] };
7722
8825
  }
7723
8826
  const records = [];
7724
8827
  const diagnostics = [];
7725
- for (const entry of readdirSync3(stateDir, { withFileTypes: true }).sort(
8828
+ for (const entry of readdirSync4(stateDir, { withFileTypes: true }).sort(
7726
8829
  (left, right) => left.name.localeCompare(right.name)
7727
8830
  )) {
7728
8831
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
7729
- const recordPath = join15(stateDir, entry.name);
8832
+ const recordPath = join18(stateDir, entry.name);
7730
8833
  try {
7731
8834
  const record = PrdRunRecordSchema.parse(
7732
8835
  JSON.parse(readFileSync15(recordPath, "utf-8"))
@@ -7743,13 +8846,13 @@ function listPrdRuns(repoRoot2) {
7743
8846
  function writePrdRunRecord(repoRoot2, record) {
7744
8847
  const normalized = normalizePrdRunRef(record.prdRef);
7745
8848
  const parsed = PrdRunRecordSchema.parse({ ...record, prdRef: normalized });
7746
- const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
8849
+ const stateDir = join18(repoRoot2, PRD_RUN_STATE_DIR);
7747
8850
  const recordPath = getRecordPath(repoRoot2, normalized);
7748
8851
  mkdirSync7(stateDir, { recursive: true });
7749
8852
  writeFileSync4(recordPath, JSON.stringify(parsed, null, 2), "utf-8");
7750
8853
  }
7751
8854
  function getRecordPath(repoRoot2, prdRef) {
7752
- return join15(
8855
+ return join18(
7753
8856
  repoRoot2,
7754
8857
  PRD_RUN_STATE_DIR,
7755
8858
  `${normalizePrdRunRef(prdRef)}.json`
@@ -7919,9 +9022,11 @@ async function anyBlockerStillOpen(refs, getIssueState) {
7919
9022
  // commands/queue.ts
7920
9023
  var QueueProviderError = class extends Error {
7921
9024
  _tag = "QueueProviderError";
7922
- constructor(message) {
9025
+ issue;
9026
+ constructor(message, issue) {
7923
9027
  super(message);
7924
9028
  this.name = "QueueProviderError";
9029
+ this.issue = issue;
7925
9030
  }
7926
9031
  };
7927
9032
  function issueDataToCandidate(issue) {
@@ -8154,11 +9259,14 @@ function runOneQueueIssueEffect(options) {
8154
9259
  catch: (e) => {
8155
9260
  if (e instanceof Error && e.name === "HumanHandoffStop") {
8156
9261
  return new QueueProviderError(
8157
- `Issue Final Review requires human handoff: ${e.message}`
9262
+ `Issue Final Review requires human handoff: ${e.message}`,
9263
+ selectedIssue
8158
9264
  );
8159
9265
  }
8160
- if (e instanceof Error) return e;
8161
- throw e;
9266
+ return new QueueProviderError(
9267
+ e instanceof Error ? e.message : String(e),
9268
+ selectedIssue
9269
+ );
8162
9270
  }
8163
9271
  });
8164
9272
  logger.raw("Issue completed successfully:");
@@ -8247,6 +9355,26 @@ async function runQueueCommand(options) {
8247
9355
  }
8248
9356
  return runEffectAndMapExit(runQueueLoop(queueOptions));
8249
9357
  }
9358
+ async function runClaimedPrdIssueCommand(options) {
9359
+ const baseBranchOverride = options.queueRunContext.prdBranch;
9360
+ const runResult = await runIssueCommand({
9361
+ issueNumber: options.issue.number,
9362
+ targetName: options.targetName,
9363
+ config: options.config,
9364
+ issueProvider: options.issueProvider,
9365
+ prProvider: options.prProvider,
9366
+ executionProvider: options.executionProvider,
9367
+ force: options.force,
9368
+ logger: options.logger,
9369
+ repoRoot: options.repoRoot,
9370
+ ...baseBranchOverride ? { baseBranchOverride } : {},
9371
+ ...options.queueRunContext.beadsChild ? { beadsIssueRunContext: options.queueRunContext.beadsChild } : {}
9372
+ });
9373
+ return {
9374
+ selected: options.issue,
9375
+ runResult
9376
+ };
9377
+ }
8250
9378
 
8251
9379
  // prd-run/evidence.ts
8252
9380
  function validatePrdRunStartEvidence(record, context) {
@@ -8459,6 +9587,185 @@ function validatePrdRunTerminalEvidence(record, context) {
8459
9587
  return { ok: true };
8460
9588
  }
8461
9589
 
9590
+ // beads/prd-ready.ts
9591
+ function derivePrdEpicId(prdRef) {
9592
+ const trimmed = prdRef.trim().toUpperCase();
9593
+ const match = trimmed.match(/^(?:PRD-)?(\d+)$/);
9594
+ if (!match) {
9595
+ throw new Error(
9596
+ `Invalid PRD ref "${prdRef}". Expected format: PRD-<number> (e.g., PRD-0078)`
9597
+ );
9598
+ }
9599
+ const padded = match[1].padStart(4, "0");
9600
+ return `bd-${padded}`;
9601
+ }
9602
+ async function claimReadyPrdChild(options) {
9603
+ const epicId = derivePrdEpicId(options.prdRef);
9604
+ const result = await runBdJson({
9605
+ repoRoot: options.repoRoot,
9606
+ args: ["ready", "--parent", epicId, "--claim", "--json"],
9607
+ logger: options.logger
9608
+ });
9609
+ if (!result || !result.id) {
9610
+ return { ok: false, epicId, reason: "no-ready-child" };
9611
+ }
9612
+ return {
9613
+ ok: true,
9614
+ epicId,
9615
+ child: {
9616
+ id: result.id,
9617
+ title: result.title,
9618
+ metadata: result.metadata
9619
+ }
9620
+ };
9621
+ }
9622
+ function isMissingIssueMessage2(value) {
9623
+ return /no issues? found matching/i.test(value);
9624
+ }
9625
+ function getPourkitPrdRef(metadata) {
9626
+ if (!metadata || typeof metadata !== "object") return void 0;
9627
+ const pourkit = metadata.pourkit;
9628
+ if (!pourkit || typeof pourkit !== "object") return void 0;
9629
+ const prdRef = pourkit.prdRef;
9630
+ return typeof prdRef === "string" ? prdRef : void 0;
9631
+ }
9632
+ async function checkPrdBeadsEpicPresence(options) {
9633
+ const expectedPrdRef = options.prdRef.trim().toUpperCase();
9634
+ const epicId = derivePrdEpicId(expectedPrdRef);
9635
+ let result;
9636
+ try {
9637
+ result = await runBdJson({
9638
+ repoRoot: options.repoRoot,
9639
+ args: ["show", epicId, "--json"],
9640
+ logger: options.logger
9641
+ });
9642
+ } catch (error) {
9643
+ const message = error instanceof Error ? error.message : String(error);
9644
+ if (isMissingIssueMessage2(message)) {
9645
+ return { ok: true, epicId, exists: false, reason: "missing-epic" };
9646
+ }
9647
+ return { ok: false, epicId, reason: message };
9648
+ }
9649
+ if (!result) {
9650
+ return {
9651
+ ok: false,
9652
+ epicId,
9653
+ reason: `bd show ${epicId} returned no result.`
9654
+ };
9655
+ }
9656
+ if (result.error) {
9657
+ if (isMissingIssueMessage2(result.error)) {
9658
+ return { ok: true, epicId, exists: false, reason: "missing-epic" };
9659
+ }
9660
+ return { ok: false, epicId, reason: result.error };
9661
+ }
9662
+ if (result.id !== epicId) {
9663
+ return {
9664
+ ok: false,
9665
+ epicId,
9666
+ reason: `Expected Beads epic ${epicId}, but bd show returned ${result.id ?? "no id"}.`
9667
+ };
9668
+ }
9669
+ const metadataPrdRef = getPourkitPrdRef(result.metadata)?.toUpperCase();
9670
+ if (metadataPrdRef !== expectedPrdRef) {
9671
+ return {
9672
+ ok: false,
9673
+ epicId,
9674
+ reason: `Beads epic ${epicId} is missing matching pourkit.prdRef metadata for ${expectedPrdRef}.`
9675
+ };
9676
+ }
9677
+ return { ok: true, epicId, exists: true };
9678
+ }
9679
+
9680
+ // beads/issue-context.ts
9681
+ function parsePourkitBeadsMetadata(metadata) {
9682
+ if (typeof metadata !== "object" || metadata === null) {
9683
+ return { ok: false, reason: "metadata must be an object" };
9684
+ }
9685
+ const obj = metadata;
9686
+ if (typeof obj.pourkit !== "object" || obj.pourkit === null) {
9687
+ return {
9688
+ ok: false,
9689
+ reason: "metadata must contain top-level pourkit object"
9690
+ };
9691
+ }
9692
+ const pourkit = obj.pourkit;
9693
+ if (typeof pourkit.prdRef !== "string" || pourkit.prdRef.trim() === "") {
9694
+ return { ok: false, reason: "pourkit.prdRef must be a non-empty string" };
9695
+ }
9696
+ if (typeof pourkit.childRef !== "string" || !/^I-\d+$/.test(pourkit.childRef)) {
9697
+ return { ok: false, reason: "pourkit.childRef must be an I-0N string" };
9698
+ }
9699
+ if (typeof pourkit.githubIssueNumber !== "number" || !Number.isInteger(pourkit.githubIssueNumber) || pourkit.githubIssueNumber <= 0) {
9700
+ return {
9701
+ ok: false,
9702
+ reason: "pourkit.githubIssueNumber must be a positive integer"
9703
+ };
9704
+ }
9705
+ if (pourkit.workspacePath !== void 0 && typeof pourkit.workspacePath !== "string") {
9706
+ return {
9707
+ ok: false,
9708
+ reason: "pourkit.workspacePath must be a string when present"
9709
+ };
9710
+ }
9711
+ if (pourkit.sourcePath !== void 0 && typeof pourkit.sourcePath !== "string") {
9712
+ return {
9713
+ ok: false,
9714
+ reason: "pourkit.sourcePath must be a string when present"
9715
+ };
9716
+ }
9717
+ if (pourkit.githubIssueUrl !== void 0 && typeof pourkit.githubIssueUrl !== "string") {
9718
+ return {
9719
+ ok: false,
9720
+ reason: "pourkit.githubIssueUrl must be a string when present"
9721
+ };
9722
+ }
9723
+ return {
9724
+ ok: true,
9725
+ prdRef: pourkit.prdRef,
9726
+ childRef: pourkit.childRef,
9727
+ githubIssueNumber: pourkit.githubIssueNumber,
9728
+ workspacePath: pourkit.workspacePath,
9729
+ sourcePath: pourkit.sourcePath,
9730
+ githubIssueUrl: pourkit.githubIssueUrl
9731
+ };
9732
+ }
9733
+ async function buildIssueContextFromBeadsChild(options) {
9734
+ const parsed = parsePourkitBeadsMetadata(options.child.metadata);
9735
+ if (!parsed.ok) {
9736
+ return { ok: false, reason: parsed.reason };
9737
+ }
9738
+ if (options.expectedPrdRef !== void 0 && parsed.prdRef !== options.expectedPrdRef) {
9739
+ return {
9740
+ ok: false,
9741
+ reason: `PRD ref mismatch: expected "${options.expectedPrdRef}", got "${parsed.prdRef}"`
9742
+ };
9743
+ }
9744
+ try {
9745
+ const issue = await options.issueProvider.fetchIssue(
9746
+ parsed.githubIssueNumber
9747
+ );
9748
+ return {
9749
+ ok: true,
9750
+ issue,
9751
+ beads: {
9752
+ prdRef: parsed.prdRef,
9753
+ childRef: parsed.childRef,
9754
+ childId: options.child.id,
9755
+ workspacePath: parsed.workspacePath,
9756
+ sourcePath: parsed.sourcePath,
9757
+ githubIssueNumber: parsed.githubIssueNumber,
9758
+ githubIssueUrl: parsed.githubIssueUrl
9759
+ }
9760
+ };
9761
+ } catch (error) {
9762
+ return {
9763
+ ok: false,
9764
+ reason: `Failed to fetch GitHub issue #${parsed.githubIssueNumber}: ${error instanceof Error ? error.message : String(error)}`
9765
+ };
9766
+ }
9767
+ }
9768
+
8462
9769
  // prd-run/coordinator.ts
8463
9770
  function canRetryFinalReviewBlock(record) {
8464
9771
  if (record.status !== "blocked" || record.blockedGate !== "final-review") {
@@ -9252,6 +10559,44 @@ function validateRecordedIssueResumeEvidence(options) {
9252
10559
  baseBranch: guidance.baseBranch
9253
10560
  });
9254
10561
  }
10562
+ var QUEUE_FAILURE_CODES_WITHOUT_SELECTED_ISSUE = /* @__PURE__ */ new Set([
10563
+ "missing-github-providers",
10564
+ "missing-config",
10565
+ "zero-processed",
10566
+ "queue-blocked",
10567
+ "queue-exception",
10568
+ "missing-drain-evidence",
10569
+ "missing-queue-started-at",
10570
+ "missing-queue-command",
10571
+ "missing-queue-drained-at",
10572
+ "missing-queue-processed-count",
10573
+ "prd-branch-mismatch",
10574
+ "missing-child-finalization-evidence"
10575
+ ]);
10576
+ function canRetryQueueGuidanceWithoutIssue(options) {
10577
+ const { guidance, startReceipt } = options;
10578
+ if (guidance.blockedGate !== "queue") return false;
10579
+ if (QUEUE_FAILURE_CODES_WITHOUT_SELECTED_ISSUE.has(guidance.failureCode)) {
10580
+ return true;
10581
+ }
10582
+ return guidance.failureCode === "missing-issue-identity" && !startReceipt?.queueStartedAt;
10583
+ }
10584
+ function extractIssueFromQueueError(error) {
10585
+ if (!error || typeof error !== "object" || !("issue" in error)) {
10586
+ return void 0;
10587
+ }
10588
+ const issue = error.issue;
10589
+ if (!issue || typeof issue !== "object") return void 0;
10590
+ const number = issue.number;
10591
+ if (typeof number !== "number" || !Number.isInteger(number) || number <= 0) {
10592
+ return void 0;
10593
+ }
10594
+ const title = issue.title;
10595
+ return {
10596
+ number,
10597
+ ...typeof title === "string" ? { title } : {}
10598
+ };
10599
+ }
9255
10600
  async function attemptRecordedIssueLabelProjection(options, prdRef, resumeIssueNumber, originalGuidance) {
9256
10601
  if (resumeIssueNumber === void 0) return true;
9257
10602
  try {
@@ -9280,6 +10625,107 @@ async function attemptRecordedIssueLabelProjection(options, prdRef, resumeIssueN
9280
10625
  return false;
9281
10626
  }
9282
10627
  }
10628
+ function repairGuidanceRequiresOpenBeadsChild(guidance) {
10629
+ return Boolean(
10630
+ guidance?.issue?.beadsChildId && guidance.issue.beadsChildClosed !== true
10631
+ );
10632
+ }
10633
+ function logCoordinatorStep(logger, level, message) {
10634
+ logger?.step?.(level, message);
10635
+ }
10636
+ async function selectPrdRunCoordinatorMode(options) {
10637
+ const preflight = await checkBeadsAvailability({
10638
+ repoRoot: options.repoRoot,
10639
+ logger: options.logger
10640
+ });
10641
+ if (!preflight.available) {
10642
+ if (!options.requiresBeads) {
10643
+ logCoordinatorStep(
10644
+ options.logger,
10645
+ "info",
10646
+ `PRD Run ${options.prdRef} queue drain mode: GitHub-compatible (Beads unavailable: ${preflight.error}).`
10647
+ );
10648
+ return { mode: "github" };
10649
+ }
10650
+ logCoordinatorStep(
10651
+ options.logger,
10652
+ "warn",
10653
+ `PRD Run ${options.prdRef} queue drain blocked: active Beads child state requires Beads, but Beads is unavailable (${preflight.error}).`
10654
+ );
10655
+ return {
10656
+ mode: "blocked",
10657
+ guidance: buildPrdRunRepairGuidance({
10658
+ blockedGate: "queue",
10659
+ blockedStage: "queue-drain",
10660
+ failureCode: "beads-unavailable",
10661
+ humanReadableReason: `Beads is not available: ${preflight.error}. ${preflight.repairGuidance}`,
10662
+ repairability: "operator-action",
10663
+ nextAction: "inspect-and-retry",
10664
+ nextRecommendedCommand: `pourkit prd-run status ${options.prdRef}`,
10665
+ diagnostics: [preflight.error, preflight.repairGuidance]
10666
+ })
10667
+ };
10668
+ }
10669
+ const epicPresence = await checkPrdBeadsEpicPresence({
10670
+ repoRoot: options.repoRoot,
10671
+ prdRef: options.prdRef,
10672
+ logger: options.logger
10673
+ });
10674
+ if (epicPresence.ok && epicPresence.exists) {
10675
+ logCoordinatorStep(
10676
+ options.logger,
10677
+ "info",
10678
+ `PRD Run ${options.prdRef} queue drain mode: Beads-backed (found epic ${epicPresence.epicId}).`
10679
+ );
10680
+ return { mode: "beads" };
10681
+ }
10682
+ if (epicPresence.ok && !epicPresence.exists) {
10683
+ if (!options.requiresBeads) {
10684
+ logCoordinatorStep(
10685
+ options.logger,
10686
+ "info",
10687
+ `PRD Run ${options.prdRef} queue drain mode: GitHub-compatible (Beads epic ${epicPresence.epicId} not found).`
10688
+ );
10689
+ return { mode: "github" };
10690
+ }
10691
+ logCoordinatorStep(
10692
+ options.logger,
10693
+ "warn",
10694
+ `PRD Run ${options.prdRef} queue drain blocked: Beads epic ${epicPresence.epicId} is missing but active Beads child state prevents GitHub fallback.`
10695
+ );
10696
+ return {
10697
+ mode: "blocked",
10698
+ guidance: buildPrdRunRepairGuidance({
10699
+ blockedGate: "queue",
10700
+ blockedStage: "queue-drain",
10701
+ failureCode: "beads-epic-missing",
10702
+ humanReadableReason: `Beads epic ${epicPresence.epicId} for ${options.prdRef} is missing, but this PRD Run has active Beads child state and cannot safely fall back to GitHub selection.`,
10703
+ repairability: "operator-action",
10704
+ nextAction: "inspect-and-retry",
10705
+ nextRecommendedCommand: `pourkit prd-run status ${options.prdRef}`,
10706
+ diagnostics: [`Missing Beads epic: ${epicPresence.epicId}`]
10707
+ })
10708
+ };
10709
+ }
10710
+ logCoordinatorStep(
10711
+ options.logger,
10712
+ "warn",
10713
+ `PRD Run ${options.prdRef} queue drain blocked: failed to verify Beads epic ${epicPresence.epicId} (${epicPresence.reason}).`
10714
+ );
10715
+ return {
10716
+ mode: "blocked",
10717
+ guidance: buildPrdRunRepairGuidance({
10718
+ blockedGate: "queue",
10719
+ blockedStage: "queue-drain",
10720
+ failureCode: "beads-epic-check-failed",
10721
+ humanReadableReason: `Failed to verify Beads epic for ${options.prdRef}: ${epicPresence.reason}`,
10722
+ repairability: "operator-action",
10723
+ nextAction: "inspect-and-retry",
10724
+ nextRecommendedCommand: `pourkit prd-run status ${options.prdRef}`,
10725
+ diagnostics: [epicPresence.reason]
10726
+ })
10727
+ };
10728
+ }
9283
10729
  async function launchPrdRun(options) {
9284
10730
  const prdRef = normalizePrdRunRef(options.prdRef);
9285
10731
  const existingRecord = readPrdRun(options.repoRoot, prdRef);
@@ -9502,7 +10948,10 @@ async function launchPrdRun(options) {
9502
10948
  }
9503
10949
  if (validation.guidance.issue?.number) {
9504
10950
  resumeIssueNumber = validation.guidance.issue.number;
9505
- } else {
10951
+ } else if (!canRetryQueueGuidanceWithoutIssue({
10952
+ guidance: validation.guidance,
10953
+ startReceipt
10954
+ })) {
9506
10955
  return buildLaunchBlockedOutcome(
9507
10956
  options.repoRoot,
9508
10957
  prdRef,
@@ -9529,15 +10978,45 @@ async function launchPrdRun(options) {
9529
10978
  startReceipt.queueStartedAt = (/* @__PURE__ */ new Date()).toISOString();
9530
10979
  startReceipt.queueCommand = "queue-run";
9531
10980
  }
9532
- writePrdRunRecord(options.repoRoot, {
9533
- prdRef,
9534
- status: "running",
9535
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9536
- targetName,
9537
- start: startReceipt,
9538
- repairGuidance: currentRecord?.repairGuidance
9539
- });
9540
- return await launchGithubQueueDrain(
10981
+ writePrdRunRecord(options.repoRoot, {
10982
+ prdRef,
10983
+ status: "running",
10984
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10985
+ targetName,
10986
+ start: startReceipt,
10987
+ repairGuidance: currentRecord?.repairGuidance
10988
+ });
10989
+ const modeSelection = await selectPrdRunCoordinatorMode({
10990
+ repoRoot: options.repoRoot,
10991
+ prdRef,
10992
+ logger: options.logger,
10993
+ requiresBeads: repairGuidanceRequiresOpenBeadsChild(
10994
+ currentRecord?.repairGuidance
10995
+ )
10996
+ });
10997
+ if (modeSelection.mode === "blocked") {
10998
+ return buildLaunchBlockedOutcome(
10999
+ options.repoRoot,
11000
+ prdRef,
11001
+ modeSelection.guidance,
11002
+ plan,
11003
+ startOutcome,
11004
+ targetName,
11005
+ startReceipt
11006
+ );
11007
+ }
11008
+ if (modeSelection.mode === "github") {
11009
+ return await launchGithubQueueDrain(
11010
+ options,
11011
+ prdRef,
11012
+ startReceipt,
11013
+ targetName,
11014
+ plan,
11015
+ startOutcome,
11016
+ resumeIssueNumber
11017
+ );
11018
+ }
11019
+ return await launchBeadsQueueDrain(
9541
11020
  options,
9542
11021
  prdRef,
9543
11022
  startReceipt,
@@ -9615,6 +11094,11 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9615
11094
  const executionProvider = options.executionProvider;
9616
11095
  const logger = options.logger;
9617
11096
  const originalGuidance = readPrdRun(options.repoRoot, prdRef).record?.repairGuidance;
11097
+ logCoordinatorStep(
11098
+ logger,
11099
+ "info",
11100
+ `Starting GitHub-compatible PRD queue drain for ${prdRef}.`
11101
+ );
9618
11102
  try {
9619
11103
  const outcome = await runQueueCommand({
9620
11104
  targetName,
@@ -9702,78 +11186,489 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9702
11186
  );
9703
11187
  }
9704
11188
  }
9705
- writeTerminalRecord(
11189
+ writeTerminalRecord(
11190
+ options.repoRoot,
11191
+ prdRef,
11192
+ "completed_prd_branch",
11193
+ targetName,
11194
+ startReceipt,
11195
+ prdBranch
11196
+ );
11197
+ return {
11198
+ kind: "completed",
11199
+ prdRef,
11200
+ status: "completed_prd_branch",
11201
+ prdBranch,
11202
+ diagnostics: [
11203
+ `PRD Run completed on branch ${prdBranch}. Next: checkout ${prdBranch} for human review, or use release promotion workflow when ready.`
11204
+ ],
11205
+ start: startOutcome,
11206
+ resume: plan
11207
+ };
11208
+ }
11209
+ if (outcome.selected === null) {
11210
+ const diagnostics = [outcome.reason];
11211
+ const result2 = buildLaunchBlockedOutcome(
11212
+ options.repoRoot,
11213
+ prdRef,
11214
+ buildPrdRunRepairGuidance({
11215
+ blockedGate: "queue",
11216
+ blockedStage: "queue-drain",
11217
+ failureCode: "queue-blocked",
11218
+ humanReadableReason: outcome.reason,
11219
+ repairability: "automatic-retry-safe",
11220
+ nextAction: "rerun-prd-run-launch",
11221
+ nextRecommendedCommand: "pourkit prd-run launch",
11222
+ diagnostics
11223
+ }),
11224
+ plan,
11225
+ startOutcome,
11226
+ targetName,
11227
+ startReceipt
11228
+ );
11229
+ const projectionOk2 = await attemptRecordedIssueLabelProjection(
11230
+ options,
11231
+ prdRef,
11232
+ resumeIssueNumber,
11233
+ originalGuidance
11234
+ );
11235
+ if (!projectionOk2 && originalGuidance) {
11236
+ const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
11237
+ return {
11238
+ kind: "blocked",
11239
+ prdRef,
11240
+ guidance: updatedRecord.repairGuidance,
11241
+ start: startOutcome,
11242
+ resume: plan
11243
+ };
11244
+ }
11245
+ return result2;
11246
+ }
11247
+ const outcomeReason = `Unexpected queue outcome: selected issue ${outcome.selected.number}`;
11248
+ const outcomeDiagnostics = [outcomeReason];
11249
+ const result = buildLaunchBlockedOutcome(
11250
+ options.repoRoot,
11251
+ prdRef,
11252
+ buildPrdRunRepairGuidance({
11253
+ blockedGate: "queue",
11254
+ blockedStage: "queue-drain",
11255
+ failureCode: "unexpected-queue-outcome",
11256
+ humanReadableReason: outcomeReason,
11257
+ repairability: "operator-action",
11258
+ nextAction: "inspect-and-retry",
11259
+ nextRecommendedCommand: "pourkit prd-run launch",
11260
+ diagnostics: outcomeDiagnostics,
11261
+ issue: {
11262
+ number: outcome.selected.number,
11263
+ title: outcome.selected.title
11264
+ }
11265
+ }),
11266
+ plan,
11267
+ startOutcome,
11268
+ targetName,
11269
+ startReceipt
11270
+ );
11271
+ const projectionOk = await attemptRecordedIssueLabelProjection(
11272
+ options,
11273
+ prdRef,
11274
+ resumeIssueNumber,
11275
+ originalGuidance
11276
+ );
11277
+ if (!projectionOk && originalGuidance) {
11278
+ const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
11279
+ return {
11280
+ kind: "blocked",
11281
+ prdRef,
11282
+ guidance: updatedRecord.repairGuidance,
11283
+ start: startOutcome,
11284
+ resume: plan
11285
+ };
11286
+ }
11287
+ return result;
11288
+ } catch (error) {
11289
+ const msg = error instanceof Error ? error.message : String(error);
11290
+ const diagnostics = [msg];
11291
+ const issue = extractIssueFromQueueError(error);
11292
+ const result = buildLaunchBlockedOutcome(
11293
+ options.repoRoot,
11294
+ prdRef,
11295
+ buildPrdRunRepairGuidance({
11296
+ blockedGate: "queue",
11297
+ blockedStage: "queue-drain",
11298
+ failureCode: "queue-exception",
11299
+ humanReadableReason: msg,
11300
+ repairability: "automatic-retry-safe",
11301
+ nextAction: "rerun-prd-run-launch",
11302
+ nextRecommendedCommand: "pourkit prd-run launch",
11303
+ diagnostics,
11304
+ issue
11305
+ }),
11306
+ plan,
11307
+ startOutcome,
11308
+ targetName,
11309
+ startReceipt
11310
+ );
11311
+ const projectionOk = await attemptRecordedIssueLabelProjection(
11312
+ options,
11313
+ prdRef,
11314
+ resumeIssueNumber,
11315
+ originalGuidance
11316
+ );
11317
+ if (!projectionOk && originalGuidance) {
11318
+ const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
11319
+ return {
11320
+ kind: "blocked",
11321
+ prdRef,
11322
+ guidance: updatedRecord.repairGuidance,
11323
+ start: startOutcome,
11324
+ resume: plan
11325
+ };
11326
+ }
11327
+ return result;
11328
+ }
11329
+ }
11330
+ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName, plan, startOutcome, resumeIssueNumber) {
11331
+ if (!startReceipt) {
11332
+ return buildLaunchBlockedOutcome(
11333
+ options.repoRoot,
11334
+ prdRef,
11335
+ buildPrdRunRepairGuidance({
11336
+ blockedGate: "branch-state",
11337
+ blockedStage: "start-validation",
11338
+ failureCode: "missing-start-receipt",
11339
+ humanReadableReason: `PRD Run ${prdRef} is missing start receipt before Beads queue drain.`,
11340
+ repairability: "automatic-retry-safe",
11341
+ nextAction: "rerun-prd-run-launch",
11342
+ nextRecommendedCommand: "pourkit prd-run launch"
11343
+ }),
11344
+ plan,
11345
+ startOutcome,
11346
+ targetName
11347
+ );
11348
+ }
11349
+ if (!options.config) {
11350
+ return buildLaunchBlockedOutcome(
11351
+ options.repoRoot,
11352
+ prdRef,
11353
+ buildPrdRunRepairGuidance({
11354
+ blockedGate: "queue",
11355
+ blockedStage: "queue-drain",
11356
+ failureCode: "missing-config",
11357
+ humanReadableReason: `PRD Run ${prdRef} requires config for Beads-backed queue drain.`,
11358
+ repairability: "operator-action",
11359
+ nextAction: "inspect-and-retry",
11360
+ nextRecommendedCommand: "pourkit prd-run launch --with-config",
11361
+ diagnostics: ["Missing config for Beads-backed mode."]
11362
+ }),
11363
+ plan,
11364
+ startOutcome,
11365
+ targetName,
11366
+ startReceipt
11367
+ );
11368
+ }
11369
+ if (!options.issueProvider) {
11370
+ return buildLaunchBlockedOutcome(
11371
+ options.repoRoot,
11372
+ prdRef,
11373
+ buildPrdRunRepairGuidance({
11374
+ blockedGate: "queue",
11375
+ blockedStage: "queue-drain",
11376
+ failureCode: "missing-issue-provider",
11377
+ humanReadableReason: `PRD Run ${prdRef} requires issueProvider for Beads-backed queue drain.`,
11378
+ repairability: "operator-action",
11379
+ nextAction: "inspect-and-retry",
11380
+ nextRecommendedCommand: "pourkit prd-run launch --with-providers",
11381
+ diagnostics: ["Missing issueProvider for Beads-backed mode."]
11382
+ }),
11383
+ plan,
11384
+ startOutcome,
11385
+ targetName,
11386
+ startReceipt
11387
+ );
11388
+ }
11389
+ if (!options.prProvider || !options.executionProvider || !options.logger) {
11390
+ const reason = `PRD Run ${prdRef} requires prProvider, executionProvider, and logger for Beads-backed queue drain.`;
11391
+ return buildLaunchBlockedOutcome(
11392
+ options.repoRoot,
11393
+ prdRef,
11394
+ buildPrdRunRepairGuidance({
11395
+ blockedGate: "queue",
11396
+ blockedStage: "queue-drain",
11397
+ failureCode: "missing-providers",
11398
+ humanReadableReason: reason,
11399
+ repairability: "operator-action",
11400
+ nextAction: "inspect-and-retry",
11401
+ nextRecommendedCommand: "pourkit prd-run launch --with-providers",
11402
+ diagnostics: [
11403
+ "Missing one or more required providers for Beads-backed mode."
11404
+ ]
11405
+ }),
11406
+ plan,
11407
+ startOutcome,
11408
+ targetName,
11409
+ startReceipt
11410
+ );
11411
+ }
11412
+ const issueProvider = options.issueProvider;
11413
+ const prProvider = options.prProvider;
11414
+ const executionProvider = options.executionProvider;
11415
+ const logger = options.logger;
11416
+ const processedResults = [];
11417
+ const originalGuidance = readPrdRun(options.repoRoot, prdRef).record?.repairGuidance;
11418
+ logCoordinatorStep(
11419
+ logger,
11420
+ "info",
11421
+ `Starting Beads-backed PRD queue drain for ${prdRef}.`
11422
+ );
11423
+ const preflight = await checkBeadsAvailability({
11424
+ repoRoot: options.repoRoot,
11425
+ logger
11426
+ });
11427
+ if (!preflight.available) {
11428
+ return buildLaunchBlockedOutcome(
11429
+ options.repoRoot,
11430
+ prdRef,
11431
+ buildPrdRunRepairGuidance({
11432
+ blockedGate: "queue",
11433
+ blockedStage: "queue-drain",
11434
+ failureCode: "beads-unavailable",
11435
+ humanReadableReason: `Beads is not available: ${preflight.error}. ${preflight.repairGuidance}`,
11436
+ repairability: "operator-action",
11437
+ nextAction: "inspect-and-retry",
11438
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
11439
+ diagnostics: [preflight.error, preflight.repairGuidance]
11440
+ }),
11441
+ plan,
11442
+ startOutcome,
11443
+ targetName,
11444
+ startReceipt
11445
+ );
11446
+ }
11447
+ if (resumeIssueNumber !== void 0) {
11448
+ const resumeIssue = originalGuidance?.issue;
11449
+ const resumeBeadsContext = resumeIssue?.beadsChildId && resumeIssue.beadsChildRef && !resumeIssue.beadsChildClosed ? {
11450
+ prdRef,
11451
+ childRef: resumeIssue.beadsChildRef,
11452
+ childId: resumeIssue.beadsChildId,
11453
+ githubIssueNumber: resumeIssueNumber
11454
+ } : void 0;
11455
+ let fetchedIssue;
11456
+ try {
11457
+ fetchedIssue = await issueProvider.fetchIssue(resumeIssueNumber);
11458
+ } catch (error) {
11459
+ return buildLaunchBlockedOutcome(
11460
+ options.repoRoot,
11461
+ prdRef,
11462
+ buildPrdRunRepairGuidance({
11463
+ blockedGate: "queue",
11464
+ blockedStage: "queue-drain",
11465
+ failureCode: "resume-fetch-failed",
11466
+ humanReadableReason: `Failed to fetch resume issue #${resumeIssueNumber} from Beads metadata.`,
11467
+ repairability: "automatic-retry-safe",
11468
+ nextAction: "rerun-prd-run-launch",
11469
+ nextRecommendedCommand: "pourkit prd-run launch",
11470
+ diagnostics: [error instanceof Error ? error.message : String(error)]
11471
+ }),
11472
+ plan,
11473
+ startOutcome,
11474
+ targetName,
11475
+ startReceipt
11476
+ );
11477
+ }
11478
+ try {
11479
+ const runResult = await runClaimedPrdIssueCommand({
11480
+ targetName,
11481
+ config: options.config,
11482
+ issueProvider,
11483
+ prProvider,
11484
+ executionProvider,
11485
+ force: false,
11486
+ logger,
11487
+ repoRoot: options.repoRoot,
11488
+ issue: fetchedIssue,
11489
+ queueRunContext: {
11490
+ prdRef,
11491
+ prdBranch: startReceipt.prdBranch,
11492
+ ...resumeBeadsContext ? { beadsChild: resumeBeadsContext } : {}
11493
+ }
11494
+ });
11495
+ processedResults.push({
11496
+ beadsChildId: resumeIssue?.beadsChildId ?? "resumed",
11497
+ issueNumber: runResult.selected.number,
11498
+ publicationStatus: runResult.runResult.publicationStatus
11499
+ });
11500
+ startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
11501
+ startReceipt.queueProcessedCount = processedResults.length;
11502
+ } catch (error) {
11503
+ const msg = error instanceof Error ? error.message : String(error);
11504
+ return buildLaunchBlockedOutcome(
11505
+ options.repoRoot,
11506
+ prdRef,
11507
+ buildPrdRunRepairGuidance({
11508
+ blockedGate: "queue",
11509
+ blockedStage: "queue-drain",
11510
+ failureCode: "resume-issue-failed",
11511
+ humanReadableReason: msg,
11512
+ repairability: "automatic-retry-safe",
11513
+ nextAction: "rerun-prd-run-launch",
11514
+ nextRecommendedCommand: "pourkit prd-run launch",
11515
+ diagnostics: [msg],
11516
+ issue: resumeIssue ? {
11517
+ number: fetchedIssue.number,
11518
+ title: fetchedIssue.title,
11519
+ beadsChildId: resumeIssue.beadsChildId,
11520
+ beadsChildRef: resumeIssue.beadsChildRef,
11521
+ beadsChildClosed: resumeIssue.beadsChildClosed || msg.startsWith("Projection failure after Beads close")
11522
+ } : void 0
11523
+ }),
11524
+ plan,
11525
+ startOutcome,
11526
+ targetName,
11527
+ startReceipt
11528
+ );
11529
+ }
11530
+ }
11531
+ let beadsClaimAttempted = false;
11532
+ while (true) {
11533
+ const claimResult = await claimReadyPrdChild({
11534
+ repoRoot: options.repoRoot,
11535
+ prdRef,
11536
+ logger
11537
+ });
11538
+ if (!claimResult.ok) {
11539
+ if (claimResult.reason === "no-ready-child") {
11540
+ logCoordinatorStep(
11541
+ logger,
11542
+ "info",
11543
+ `Beads-backed PRD queue drain for ${prdRef} found no additional ready child work.`
11544
+ );
11545
+ break;
11546
+ }
11547
+ return buildLaunchBlockedOutcome(
9706
11548
  options.repoRoot,
9707
11549
  prdRef,
9708
- "completed_prd_branch",
11550
+ buildPrdRunRepairGuidance({
11551
+ blockedGate: "queue",
11552
+ blockedStage: "queue-drain",
11553
+ failureCode: "beads-claim-failed",
11554
+ humanReadableReason: `Beads claim failed for PRD ${prdRef} epic ${claimResult.epicId}.`,
11555
+ repairability: "operator-action",
11556
+ nextAction: "inspect-and-retry",
11557
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
11558
+ diagnostics: [`Beads claim error for epic ${claimResult.epicId}.`]
11559
+ }),
11560
+ plan,
11561
+ startOutcome,
9709
11562
  targetName,
9710
- startReceipt,
9711
- prdBranch
11563
+ startReceipt
9712
11564
  );
9713
- return {
9714
- kind: "completed",
11565
+ }
11566
+ beadsClaimAttempted = true;
11567
+ logCoordinatorStep(
11568
+ logger,
11569
+ "info",
11570
+ `Claimed Beads child ${claimResult.child.id} for ${prdRef}; mapping to GitHub Issue context.`
11571
+ );
11572
+ const contextResult = await buildIssueContextFromBeadsChild({
11573
+ child: claimResult.child,
11574
+ issueProvider,
11575
+ expectedPrdRef: prdRef
11576
+ });
11577
+ if (!contextResult.ok) {
11578
+ return buildLaunchBlockedOutcome(
11579
+ options.repoRoot,
9715
11580
  prdRef,
9716
- status: "completed_prd_branch",
9717
- prdBranch,
9718
- diagnostics: [
9719
- `PRD Run completed on branch ${prdBranch}. Next: checkout ${prdBranch} for human review, or use release promotion workflow when ready.`
9720
- ],
9721
- start: startOutcome,
9722
- resume: plan
9723
- };
11581
+ buildPrdRunRepairGuidance({
11582
+ blockedGate: "queue",
11583
+ blockedStage: "queue-drain",
11584
+ failureCode: "beads-context-mapping-failed",
11585
+ humanReadableReason: `Failed to map Beads child ${claimResult.child.id} to Issue context: ${contextResult.reason}`,
11586
+ repairability: "operator-action",
11587
+ nextAction: "inspect-and-retry",
11588
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
11589
+ diagnostics: [contextResult.reason]
11590
+ }),
11591
+ plan,
11592
+ startOutcome,
11593
+ targetName,
11594
+ startReceipt
11595
+ );
9724
11596
  }
9725
- if (outcome.selected === null) {
9726
- const diagnostics = [outcome.reason];
9727
- const result2 = buildLaunchBlockedOutcome(
11597
+ try {
11598
+ logCoordinatorStep(
11599
+ logger,
11600
+ "info",
11601
+ `Running GitHub Issue #${contextResult.issue.number} from Beads child ${contextResult.beads.childId}.`
11602
+ );
11603
+ const runResult = await runClaimedPrdIssueCommand({
11604
+ targetName,
11605
+ config: options.config,
11606
+ issueProvider,
11607
+ prProvider,
11608
+ executionProvider,
11609
+ force: false,
11610
+ logger,
11611
+ repoRoot: options.repoRoot,
11612
+ issue: contextResult.issue,
11613
+ queueRunContext: {
11614
+ prdRef,
11615
+ prdBranch: startReceipt.prdBranch,
11616
+ beadsChild: contextResult.beads
11617
+ }
11618
+ });
11619
+ processedResults.push({
11620
+ beadsChildId: claimResult.child.id,
11621
+ issueNumber: runResult.selected.number,
11622
+ publicationStatus: runResult.runResult.publicationStatus
11623
+ });
11624
+ } catch (error) {
11625
+ const msg = error instanceof Error ? error.message : String(error);
11626
+ const beadsChildClosed = msg.startsWith(
11627
+ "Projection failure after Beads close"
11628
+ );
11629
+ return buildLaunchBlockedOutcome(
9728
11630
  options.repoRoot,
9729
11631
  prdRef,
9730
11632
  buildPrdRunRepairGuidance({
9731
11633
  blockedGate: "queue",
9732
11634
  blockedStage: "queue-drain",
9733
- failureCode: "queue-blocked",
9734
- humanReadableReason: outcome.reason,
11635
+ failureCode: "beads-issue-run-failed",
11636
+ humanReadableReason: msg,
9735
11637
  repairability: "automatic-retry-safe",
9736
11638
  nextAction: "rerun-prd-run-launch",
9737
11639
  nextRecommendedCommand: "pourkit prd-run launch",
9738
- diagnostics
11640
+ diagnostics: [msg],
11641
+ issue: {
11642
+ number: contextResult.issue.number,
11643
+ title: contextResult.issue.title,
11644
+ beadsChildId: contextResult.beads.childId,
11645
+ beadsChildRef: contextResult.beads.childRef,
11646
+ beadsChildClosed
11647
+ }
9739
11648
  }),
9740
11649
  plan,
9741
11650
  startOutcome,
9742
11651
  targetName,
9743
11652
  startReceipt
9744
11653
  );
9745
- const projectionOk2 = await attemptRecordedIssueLabelProjection(
9746
- options,
9747
- prdRef,
9748
- resumeIssueNumber,
9749
- originalGuidance
9750
- );
9751
- if (!projectionOk2 && originalGuidance) {
9752
- const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
9753
- return {
9754
- kind: "blocked",
9755
- prdRef,
9756
- guidance: updatedRecord.repairGuidance,
9757
- start: startOutcome,
9758
- resume: plan
9759
- };
9760
- }
9761
- return result2;
9762
11654
  }
9763
- const outcomeReason = `Unexpected queue outcome: selected issue ${outcome.selected.number}`;
9764
- const outcomeDiagnostics = [outcomeReason];
11655
+ }
11656
+ if (processedResults.length === 0 && !beadsClaimAttempted) {
11657
+ const diagnostics = [
11658
+ "Beads-backed PRD drain found no ready children. Zero child work is not terminal completion evidence."
11659
+ ];
9765
11660
  const result = buildLaunchBlockedOutcome(
9766
11661
  options.repoRoot,
9767
11662
  prdRef,
9768
11663
  buildPrdRunRepairGuidance({
9769
11664
  blockedGate: "queue",
9770
11665
  blockedStage: "queue-drain",
9771
- failureCode: "unexpected-queue-outcome",
9772
- humanReadableReason: outcomeReason,
9773
- repairability: "operator-action",
9774
- nextAction: "inspect-and-retry",
11666
+ failureCode: "zero-processed",
11667
+ humanReadableReason: diagnostics[0],
11668
+ repairability: "automatic-retry-safe",
11669
+ nextAction: "rerun-prd-run-launch",
9775
11670
  nextRecommendedCommand: "pourkit prd-run launch",
9776
- diagnostics: outcomeDiagnostics
11671
+ diagnostics
9777
11672
  }),
9778
11673
  plan,
9779
11674
  startOutcome,
@@ -9797,45 +11692,54 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9797
11692
  };
9798
11693
  }
9799
11694
  return result;
9800
- } catch (error) {
9801
- const msg = error instanceof Error ? error.message : String(error);
9802
- const diagnostics = [msg];
9803
- const result = buildLaunchBlockedOutcome(
9804
- options.repoRoot,
9805
- prdRef,
9806
- buildPrdRunRepairGuidance({
9807
- blockedGate: "queue",
9808
- blockedStage: "queue-drain",
9809
- failureCode: "queue-exception",
9810
- humanReadableReason: msg,
9811
- repairability: "automatic-retry-safe",
9812
- nextAction: "rerun-prd-run-launch",
9813
- nextRecommendedCommand: "pourkit prd-run launch",
9814
- diagnostics
9815
- }),
9816
- plan,
9817
- startOutcome,
9818
- targetName,
9819
- startReceipt
9820
- );
9821
- const projectionOk = await attemptRecordedIssueLabelProjection(
9822
- options,
11695
+ }
11696
+ startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
11697
+ startReceipt.queueProcessedCount = processedResults.length;
11698
+ const prdBranch = startReceipt.prdBranch ?? prdRef;
11699
+ writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
11700
+ const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
11701
+ if (drainRecord) {
11702
+ const drainContext = {
9823
11703
  prdRef,
9824
- resumeIssueNumber,
9825
- originalGuidance
11704
+ baseBranch: startReceipt.startBaseBranch,
11705
+ prdBranch,
11706
+ allChildIssuesFinalizedIntoPrdBranch: processedResults.length > 0 && processedResults.every((r) => r.publicationStatus === "merged")
11707
+ };
11708
+ const terminalEvidence = validatePrdRunTerminalEvidence(
11709
+ drainRecord,
11710
+ drainContext
9826
11711
  );
9827
- if (!projectionOk && originalGuidance) {
9828
- const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
9829
- return {
9830
- kind: "blocked",
11712
+ if (!terminalEvidence.ok) {
11713
+ return buildLaunchBlockedOutcome(
11714
+ options.repoRoot,
9831
11715
  prdRef,
9832
- guidance: updatedRecord.repairGuidance,
9833
- start: startOutcome,
9834
- resume: plan
9835
- };
11716
+ terminalEvidence.guidance,
11717
+ plan,
11718
+ startOutcome,
11719
+ targetName,
11720
+ startReceipt
11721
+ );
9836
11722
  }
9837
- return result;
9838
11723
  }
11724
+ writeTerminalRecord(
11725
+ options.repoRoot,
11726
+ prdRef,
11727
+ "completed_prd_branch",
11728
+ targetName,
11729
+ startReceipt,
11730
+ prdBranch
11731
+ );
11732
+ return {
11733
+ kind: "completed",
11734
+ prdRef,
11735
+ status: "completed_prd_branch",
11736
+ prdBranch,
11737
+ diagnostics: [
11738
+ `PRD Run completed on branch ${prdBranch}. Next: checkout ${prdBranch} for human review, or use release promotion workflow when ready.`
11739
+ ],
11740
+ start: startOutcome,
11741
+ resume: plan
11742
+ };
9839
11743
  }
9840
11744
 
9841
11745
  // prd-run/final-review-validation.ts
@@ -10365,11 +12269,11 @@ async function runPrMergeCommand(args, logger, prProvider, config) {
10365
12269
  import { existsSync as existsSync16, statSync } from "fs";
10366
12270
  import {
10367
12271
  copyFile,
10368
- mkdir as mkdir4,
10369
- readFile as readFile4,
10370
- readdir,
12272
+ mkdir as mkdir5,
12273
+ readFile as readFile5,
12274
+ readdir as readdir2,
10371
12275
  rename,
10372
- writeFile
12276
+ writeFile as writeFile2
10373
12277
  } from "fs/promises";
10374
12278
  import { createHash, randomUUID } from "crypto";
10375
12279
  import path5 from "path";
@@ -10678,12 +12582,12 @@ icm --db .pourkit/icm/memories.db topics # list a
10678
12582
  var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
10679
12583
  var MANAGED_BLOCK_END = "<!-- END POURKIT MANAGED BLOCK -->";
10680
12584
  var MANAGED_BLOCK_PATTERN = new RegExp(
10681
- `${escapeRegExp(MANAGED_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp(
12585
+ `${escapeRegExp3(MANAGED_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp3(
10682
12586
  MANAGED_BLOCK_END
10683
12587
  )}[ \\t]*(?:\\r?\\n)?`,
10684
12588
  "g"
10685
12589
  );
10686
- function escapeRegExp(value) {
12590
+ function escapeRegExp3(value) {
10687
12591
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10688
12592
  }
10689
12593
  function buildManagedBlock(content) {
@@ -11064,7 +12968,7 @@ function generateGitignoreBlock() {
11064
12968
  }
11065
12969
  async function walkDir(dir) {
11066
12970
  const files = [];
11067
- const entries = await readdir(dir, { withFileTypes: true });
12971
+ const entries = await readdir2(dir, { withFileTypes: true });
11068
12972
  for (const entry of entries) {
11069
12973
  const full = path5.join(dir, entry.name);
11070
12974
  if (entry.isDirectory()) {
@@ -11076,7 +12980,7 @@ async function walkDir(dir) {
11076
12980
  return files;
11077
12981
  }
11078
12982
  async function computeFileChecksum(filePath) {
11079
- const content = await readFile4(filePath);
12983
+ const content = await readFile5(filePath);
11080
12984
  return createHash("sha256").update(content).digest("hex");
11081
12985
  }
11082
12986
  function lockfileExists(root, name) {
@@ -11165,7 +13069,7 @@ async function discoverOpenCodeAgentFiles(sourceRoot) {
11165
13069
  (candidate) => existsSync16(candidate) && statSync(candidate).isDirectory()
11166
13070
  );
11167
13071
  if (!agentsDir) return [];
11168
- const entries = await readdir(agentsDir, { withFileTypes: true });
13072
+ const entries = await readdir2(agentsDir, { withFileTypes: true });
11169
13073
  return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path5.join(agentsDir, entry.name));
11170
13074
  }
11171
13075
  async function discoverRootDomainDocs(root) {
@@ -11178,7 +13082,7 @@ async function discoverRootDomainDocs(root) {
11178
13082
  }
11179
13083
  const adrDir = path5.join(root, "docs", "adr");
11180
13084
  if (existsSync16(adrDir)) {
11181
- const entries = await readdir(adrDir, { withFileTypes: true });
13085
+ const entries = await readdir2(adrDir, { withFileTypes: true });
11182
13086
  for (const entry of entries) {
11183
13087
  if (entry.isFile() && entry.name.endsWith(".md")) {
11184
13088
  docs.push(path5.join(adrDir, entry.name));
@@ -11493,7 +13397,7 @@ async function planInit(options) {
11493
13397
  "skills"
11494
13398
  );
11495
13399
  const projectionSkillsDir = path5.join(targetRoot, ".agents", "skills");
11496
- const entries = await readdir(s, { withFileTypes: true });
13400
+ const entries = await readdir2(s, { withFileTypes: true });
11497
13401
  const skillNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
11498
13402
  const addonManagedDir = path5.join(
11499
13403
  targetRoot,
@@ -11559,7 +13463,7 @@ async function planInit(options) {
11559
13463
  });
11560
13464
  continue;
11561
13465
  }
11562
- const sourceContent = await readFile4(file, "utf-8");
13466
+ const sourceContent = await readFile5(file, "utf-8");
11563
13467
  const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
11564
13468
  operations.push({
11565
13469
  kind: "create",
@@ -11638,7 +13542,7 @@ async function planInit(options) {
11638
13542
  }
11639
13543
  let hasPackageJson = true;
11640
13544
  try {
11641
- await readFile4(path5.join(targetRoot, "package.json"), "utf-8");
13545
+ await readFile5(path5.join(targetRoot, "package.json"), "utf-8");
11642
13546
  } catch {
11643
13547
  hasPackageJson = false;
11644
13548
  }
@@ -12426,17 +14330,17 @@ async function promptForInitChoices(plan, current) {
12426
14330
  }
12427
14331
  async function writeFileAtomic(filePath, content) {
12428
14332
  const tmpPath = `${filePath}.tmp.${randomUUID()}`;
12429
- await writeFile(tmpPath, content, "utf-8");
14333
+ await writeFile2(tmpPath, content, "utf-8");
12430
14334
  await rename(tmpPath, filePath);
12431
14335
  }
12432
14336
  async function updateManagedBlock(filePath, content) {
12433
14337
  if (!existsSync16(filePath)) {
12434
14338
  const dir = path5.dirname(filePath);
12435
- await mkdir4(dir, { recursive: true });
14339
+ await mkdir5(dir, { recursive: true });
12436
14340
  await writeFileAtomic(filePath, buildManagedBlock(content));
12437
14341
  return;
12438
14342
  }
12439
- const existing = await readFile4(filePath, "utf-8");
14343
+ const existing = await readFile5(filePath, "utf-8");
12440
14344
  await writeFileAtomic(filePath, upsertManagedBlock(existing, content));
12441
14345
  }
12442
14346
  async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
@@ -12475,7 +14379,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
12475
14379
  workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
12476
14380
  assets
12477
14381
  };
12478
- await mkdir4(manifestDir, { recursive: true });
14382
+ await mkdir5(manifestDir, { recursive: true });
12479
14383
  await writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
12480
14384
  return manifest;
12481
14385
  }
@@ -12501,7 +14405,7 @@ async function applyInitPlan(plan, options) {
12501
14405
  continue;
12502
14406
  }
12503
14407
  const dir = path5.dirname(op.path);
12504
- await mkdir4(dir, { recursive: true });
14408
+ await mkdir5(dir, { recursive: true });
12505
14409
  await writeFileAtomic(op.path, op.content ?? "");
12506
14410
  applied++;
12507
14411
  break;
@@ -12520,7 +14424,7 @@ async function applyInitPlan(plan, options) {
12520
14424
  skipped++;
12521
14425
  } else {
12522
14426
  const dir = path5.dirname(op.path);
12523
- await mkdir4(dir, { recursive: true });
14427
+ await mkdir5(dir, { recursive: true });
12524
14428
  await copyFile(op.sourcePath, op.path);
12525
14429
  applied++;
12526
14430
  }
@@ -12545,7 +14449,7 @@ async function applyInitPlan(plan, options) {
12545
14449
  continue;
12546
14450
  }
12547
14451
  const dir = path5.dirname(op.path);
12548
- await mkdir4(dir, { recursive: true });
14452
+ await mkdir5(dir, { recursive: true });
12549
14453
  await rename(op.sourcePath, op.path);
12550
14454
  applied++;
12551
14455
  break;
@@ -12862,13 +14766,13 @@ Init applied: ${result.applied} operations applied, ${result.skipped} skipped.`
12862
14766
  // commands/memory-init.ts
12863
14767
  import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync5 } from "fs";
12864
14768
  import { execFile as execFile3 } from "child_process";
12865
- import { join as join17 } from "path";
14769
+ import { join as join20 } from "path";
12866
14770
  import { promisify as promisify3 } from "util";
12867
14771
  var execFileAsync3 = promisify3(execFile3);
12868
14772
  async function runMemoryInitCommand(options) {
12869
14773
  const cwd = options.cwd ?? process.cwd();
12870
14774
  const execCommand = options.execCommand ?? execFileAsync3;
12871
- const configPath = join17(cwd, ".pourkit", "config.json");
14775
+ const configPath = join20(cwd, ".pourkit", "config.json");
12872
14776
  if (!existsSync17(configPath)) {
12873
14777
  return {
12874
14778
  ok: false,
@@ -12900,8 +14804,8 @@ async function runMemoryInitCommand(options) {
12900
14804
  const next = { ...raw, memory };
12901
14805
  writeFileSync5(configPath, JSON.stringify(next, null, 2) + "\n");
12902
14806
  }
12903
- const memoryDir = join17(cwd, ".pourkit", "icm");
12904
- const memoryDbPath = join17(memoryDir, "memories.db");
14807
+ const memoryDir = join20(cwd, ".pourkit", "icm");
14808
+ const memoryDbPath = join20(memoryDir, "memories.db");
12905
14809
  mkdirSync8(memoryDir, { recursive: true });
12906
14810
  let dbInitialized = false;
12907
14811
  let warning;
@@ -12915,7 +14819,7 @@ async function runMemoryInitCommand(options) {
12915
14819
  } catch {
12916
14820
  warning = "ICM is not available or could not initialize .pourkit/icm/memories.db. Install ICM and run `pourkit doctor` to verify repo-scoped memory setup.";
12917
14821
  }
12918
- const gitignorePath = join17(cwd, ".gitignore");
14822
+ const gitignorePath = join20(cwd, ".gitignore");
12919
14823
  updateManagedBlockSync(gitignorePath, generateGitignoreBlock());
12920
14824
  const managed = await generateManagedAgentInstructions({
12921
14825
  sourceRoot: cwd,
@@ -12923,14 +14827,14 @@ async function runMemoryInitCommand(options) {
12923
14827
  });
12924
14828
  let updatedAgentFile = false;
12925
14829
  for (const fileName of ["AGENTS.md", "CLAUDE.md"]) {
12926
- const agentPath = join17(cwd, fileName);
14830
+ const agentPath = join20(cwd, fileName);
12927
14831
  if (existsSync17(agentPath)) {
12928
14832
  updateManagedBlockSync(agentPath, managed);
12929
14833
  updatedAgentFile = true;
12930
14834
  }
12931
14835
  }
12932
14836
  if (!updatedAgentFile) {
12933
- updateManagedBlockSync(join17(cwd, "AGENTS.md"), managed);
14837
+ updateManagedBlockSync(join20(cwd, "AGENTS.md"), managed);
12934
14838
  }
12935
14839
  return {
12936
14840
  ok: true,
@@ -13066,17 +14970,17 @@ async function runSerenaStatusCommand(options) {
13066
14970
 
13067
14971
  // commands/config-schema.ts
13068
14972
  import { readFileSync as readFileSync18, existsSync as existsSync18 } from "fs";
13069
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile2, readdir as readdir2 } from "fs/promises";
13070
- import { resolve as resolve3, dirname as dirname5, relative as relative3 } from "path";
14973
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir3 } from "fs/promises";
14974
+ import { resolve as resolve4, dirname as dirname6, relative as relative4 } from "path";
13071
14975
  import { fileURLToPath as fileURLToPath3 } from "url";
13072
14976
  import Ajv2 from "ajv";
13073
14977
  init_common();
13074
14978
  var __filename3 = fileURLToPath3(import.meta.url);
13075
- var __dirname3 = dirname5(__filename3);
14979
+ var __dirname3 = dirname6(__filename3);
13076
14980
  function resolvePackagedAssetPath2(fileName) {
13077
14981
  const candidates = [
13078
- resolve3(__dirname3, "schema", fileName),
13079
- resolve3(__dirname3, "../schema", fileName)
14982
+ resolve4(__dirname3, "schema", fileName),
14983
+ resolve4(__dirname3, "../schema", fileName)
13080
14984
  ];
13081
14985
  return candidates.find((candidate) => existsSync18(candidate)) ?? candidates[0];
13082
14986
  }
@@ -13103,15 +15007,15 @@ function readPackagedHash() {
13103
15007
  return readFileSync18(PACKAGED_HASH_PATH, "utf-8");
13104
15008
  }
13105
15009
  function localSchemaDir(repoRoot2) {
13106
- return resolve3(repoRoot2, ".pourkit/schema");
15010
+ return resolve4(repoRoot2, ".pourkit/schema");
13107
15011
  }
13108
15012
  var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
13109
15013
  var MANAGED_BLOCK_BEGIN_MARKER = MANAGED_BLOCK_BEGIN2.trim();
13110
15014
  var MANAGED_BLOCK_END_MARKER = "<!-- END POURKIT MANAGED BLOCK -->";
13111
15015
  var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
13112
15016
  function resolvePackagedManagedPath(...segments) {
13113
- const bundledPath = resolve3(__dirname3, "managed", ...segments);
13114
- const sourcePath = resolve3(__dirname3, "..", "managed", ...segments);
15017
+ const bundledPath = resolve4(__dirname3, "managed", ...segments);
15018
+ const sourcePath = resolve4(__dirname3, "..", "managed", ...segments);
13115
15019
  const candidates = [bundledPath, sourcePath];
13116
15020
  return candidates.find((candidate) => existsSync18(candidate)) ?? bundledPath;
13117
15021
  }
@@ -13129,21 +15033,21 @@ function resolvePackagedReleaseAddonDocPath(docName) {
13129
15033
  }
13130
15034
  async function readPackagedTextFile(filePath) {
13131
15035
  try {
13132
- return await readFile5(filePath, "utf-8");
15036
+ return await readFile6(filePath, "utf-8");
13133
15037
  } catch {
13134
15038
  return null;
13135
15039
  }
13136
15040
  }
13137
15041
  function isPathContained(parent, child) {
13138
- const relativePath = relative3(parent, child);
15042
+ const relativePath = relative4(parent, child);
13139
15043
  return !relativePath.startsWith("..") && !relativePath.startsWith("../");
13140
15044
  }
13141
15045
  async function walkDir2(dir) {
13142
15046
  const files = [];
13143
15047
  try {
13144
- const entries = await readdir2(dir, { withFileTypes: true });
15048
+ const entries = await readdir3(dir, { withFileTypes: true });
13145
15049
  for (const entry of entries) {
13146
- const full = resolve3(dir, entry.name);
15050
+ const full = resolve4(dir, entry.name);
13147
15051
  if (entry.isDirectory()) {
13148
15052
  files.push(...await walkDir2(full));
13149
15053
  } else {
@@ -13225,7 +15129,7 @@ async function validateWorkflowPack(cwd) {
13225
15129
  const catalog = getWorkflowPackCatalog();
13226
15130
  const baselineSkills = getBaselineManagedSkillNames();
13227
15131
  for (const docName of catalog.docs) {
13228
- const localPath = resolve3(cwd, `.pourkit/managed/docs/agents/${docName}`);
15132
+ const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
13229
15133
  if (!existsSync18(localPath)) {
13230
15134
  failures.push({
13231
15135
  severity: "failure",
@@ -13238,9 +15142,9 @@ async function validateWorkflowPack(cwd) {
13238
15142
  const packagedContent = await readPackagedTextFile(packagedPath);
13239
15143
  if (packagedContent !== null) {
13240
15144
  try {
13241
- const localContent = await readFile5(localPath, "utf-8");
15145
+ const localContent = await readFile6(localPath, "utf-8");
13242
15146
  if (localContent !== packagedContent) {
13243
- const overridePath = resolve3(
15147
+ const overridePath = resolve4(
13244
15148
  cwd,
13245
15149
  `.pourkit/overrides/docs/agents/${docName}`
13246
15150
  );
@@ -13266,7 +15170,7 @@ async function validateWorkflowPack(cwd) {
13266
15170
  }
13267
15171
  }
13268
15172
  for (const skillName of baselineSkills) {
13269
- const skillDir = resolve3(cwd, `.pourkit/managed/skills/${skillName}`);
15173
+ const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
13270
15174
  if (!existsSync18(skillDir)) {
13271
15175
  failures.push({
13272
15176
  severity: "failure",
@@ -13275,7 +15179,7 @@ async function validateWorkflowPack(cwd) {
13275
15179
  message: `Missing managed skill: ${skillName}`
13276
15180
  });
13277
15181
  } else {
13278
- const skillSourcePath = resolve3(skillDir, "SKILL.md");
15182
+ const skillSourcePath = resolve4(skillDir, "SKILL.md");
13279
15183
  if (existsSync18(skillSourcePath)) {
13280
15184
  const packagedSkillPath = resolvePackagedManagedPath(
13281
15185
  "skills",
@@ -13285,9 +15189,9 @@ async function validateWorkflowPack(cwd) {
13285
15189
  const packagedContent = await readPackagedTextFile(packagedSkillPath);
13286
15190
  if (packagedContent !== null) {
13287
15191
  try {
13288
- const localContent = await readFile5(skillSourcePath, "utf-8");
15192
+ const localContent = await readFile6(skillSourcePath, "utf-8");
13289
15193
  if (localContent !== packagedContent) {
13290
- const overridePath = resolve3(
15194
+ const overridePath = resolve4(
13291
15195
  cwd,
13292
15196
  `.pourkit/overrides/skills/${skillName}/SKILL.md`
13293
15197
  );
@@ -13314,7 +15218,7 @@ async function validateWorkflowPack(cwd) {
13314
15218
  }
13315
15219
  }
13316
15220
  for (const promptName of catalog.prompts) {
13317
- const promptPath = resolve3(cwd, `.pourkit/managed/prompts/${promptName}`);
15221
+ const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
13318
15222
  if (!existsSync18(promptPath)) {
13319
15223
  failures.push({
13320
15224
  severity: "failure",
@@ -13325,7 +15229,7 @@ async function validateWorkflowPack(cwd) {
13325
15229
  }
13326
15230
  }
13327
15231
  for (const skillName of baselineSkills) {
13328
- const projectionDir = resolve3(cwd, `.agents/skills/${skillName}`);
15232
+ const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
13329
15233
  if (!existsSync18(projectionDir)) {
13330
15234
  warnings.push({
13331
15235
  severity: "warning",
@@ -13334,7 +15238,7 @@ async function validateWorkflowPack(cwd) {
13334
15238
  message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
13335
15239
  });
13336
15240
  } else {
13337
- const projectionPath = resolve3(projectionDir, "SKILL.md");
15241
+ const projectionPath = resolve4(projectionDir, "SKILL.md");
13338
15242
  if (existsSync18(projectionPath)) {
13339
15243
  const managedSourcePath = resolvePackagedManagedPath(
13340
15244
  "skills",
@@ -13344,7 +15248,7 @@ async function validateWorkflowPack(cwd) {
13344
15248
  const packagedContent = await readPackagedTextFile(managedSourcePath);
13345
15249
  if (packagedContent !== null) {
13346
15250
  try {
13347
- const projectionContent = await readFile5(projectionPath, "utf-8");
15251
+ const projectionContent = await readFile6(projectionPath, "utf-8");
13348
15252
  const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
13349
15253
  if (projectionContent !== buildOpenCodeSkillProjectionContent(
13350
15254
  managedSourceRelativePath,
@@ -13363,10 +15267,10 @@ async function validateWorkflowPack(cwd) {
13363
15267
  }
13364
15268
  }
13365
15269
  }
13366
- const agentsPath = resolve3(cwd, "AGENTS.md");
15270
+ const agentsPath = resolve4(cwd, "AGENTS.md");
13367
15271
  if (existsSync18(agentsPath)) {
13368
15272
  try {
13369
- const agentsContent = await readFile5(agentsPath, "utf-8");
15273
+ const agentsContent = await readFile6(agentsPath, "utf-8");
13370
15274
  if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
13371
15275
  failures.push({
13372
15276
  severity: "failure",
@@ -13386,11 +15290,11 @@ async function validateWorkflowPack(cwd) {
13386
15290
  } catch {
13387
15291
  }
13388
15292
  }
13389
- const overridesDir = resolve3(cwd, ".pourkit/overrides");
15293
+ const overridesDir = resolve4(cwd, ".pourkit/overrides");
13390
15294
  if (existsSync18(overridesDir)) {
13391
15295
  const overrideFiles = await walkDir2(overridesDir);
13392
15296
  for (const file of overrideFiles) {
13393
- const relPath = relative3(cwd, file);
15297
+ const relPath = relative4(cwd, file);
13394
15298
  if (!warnings.some((w) => w.kind === "overridden" && w.path === relPath)) {
13395
15299
  warnings.push({
13396
15300
  severity: "warning",
@@ -13401,15 +15305,15 @@ async function validateWorkflowPack(cwd) {
13401
15305
  }
13402
15306
  }
13403
15307
  }
13404
- const configPath = resolve3(cwd, ".pourkit/config.json");
15308
+ const configPath = resolve4(cwd, ".pourkit/config.json");
13405
15309
  if (existsSync18(configPath)) {
13406
15310
  try {
13407
- const configContent = await readFile5(configPath, "utf-8");
15311
+ const configContent = await readFile6(configPath, "utf-8");
13408
15312
  const parsed = JSON.parse(configContent);
13409
15313
  const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
13410
15314
  if (releaseEnabled) {
13411
15315
  for (const skillName of catalog.releaseAddonSkills) {
13412
- const addonDir = resolve3(
15316
+ const addonDir = resolve4(
13413
15317
  cwd,
13414
15318
  `.pourkit/managed/addons/release/skills/${skillName}`
13415
15319
  );
@@ -13423,7 +15327,7 @@ async function validateWorkflowPack(cwd) {
13423
15327
  }
13424
15328
  }
13425
15329
  for (const docName of catalog.releaseAddonDocs) {
13426
- const addonDocPath = resolve3(
15330
+ const addonDocPath = resolve4(
13427
15331
  cwd,
13428
15332
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13429
15333
  );
@@ -13439,7 +15343,7 @@ async function validateWorkflowPack(cwd) {
13439
15343
  resolvePackagedReleaseAddonDocPath(docName)
13440
15344
  );
13441
15345
  if (packagedContent !== null) {
13442
- const localContent = await readFile5(addonDocPath, "utf-8");
15346
+ const localContent = await readFile6(addonDocPath, "utf-8");
13443
15347
  if (localContent !== packagedContent) {
13444
15348
  failures.push({
13445
15349
  severity: "failure",
@@ -13468,7 +15372,7 @@ async function validateWorkflowPack(cwd) {
13468
15372
  }
13469
15373
  } else {
13470
15374
  for (const skillName of catalog.releaseAddonSkills) {
13471
- const addonDir = resolve3(
15375
+ const addonDir = resolve4(
13472
15376
  cwd,
13473
15377
  `.pourkit/managed/addons/release/skills/${skillName}`
13474
15378
  );
@@ -13482,7 +15386,7 @@ async function validateWorkflowPack(cwd) {
13482
15386
  }
13483
15387
  }
13484
15388
  for (const docName of catalog.releaseAddonDocs) {
13485
- const addonDocPath = resolve3(
15389
+ const addonDocPath = resolve4(
13486
15390
  cwd,
13487
15391
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13488
15392
  );
@@ -13499,16 +15403,16 @@ async function validateWorkflowPack(cwd) {
13499
15403
  } catch {
13500
15404
  }
13501
15405
  }
13502
- const manifestPath = resolve3(cwd, ".pourkit/manifest.json");
15406
+ const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
13503
15407
  if (existsSync18(manifestPath)) {
13504
15408
  try {
13505
- const manifestContent = await readFile5(manifestPath, "utf-8");
15409
+ const manifestContent = await readFile6(manifestPath, "utf-8");
13506
15410
  const manifest = JSON.parse(manifestContent);
13507
15411
  const wp = manifest.workflowPack;
13508
15412
  if (wp?.projections) {
13509
15413
  for (const proj of wp.projections) {
13510
15414
  if (proj.path) {
13511
- const resolved = resolve3(cwd, proj.path);
15415
+ const resolved = resolve4(cwd, proj.path);
13512
15416
  if (!isPathContained(cwd, resolved)) {
13513
15417
  failures.push({
13514
15418
  severity: "failure",
@@ -13522,7 +15426,7 @@ async function validateWorkflowPack(cwd) {
13522
15426
  } catch {
13523
15427
  }
13524
15428
  }
13525
- const pathEscapeOverrideDir = resolve3(cwd, ".pourkit/overrides");
15429
+ const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
13526
15430
  if (existsSync18(pathEscapeOverrideDir)) {
13527
15431
  const overrideFiles = await walkDir2(pathEscapeOverrideDir);
13528
15432
  for (const file of overrideFiles) {
@@ -13530,20 +15434,20 @@ async function validateWorkflowPack(cwd) {
13530
15434
  failures.push({
13531
15435
  severity: "failure",
13532
15436
  kind: "path_escape",
13533
- path: relative3(cwd, file),
13534
- message: `Override file path escapes repository: ${relative3(cwd, file)}`
15437
+ path: relative4(cwd, file),
15438
+ message: `Override file path escapes repository: ${relative4(cwd, file)}`
13535
15439
  });
13536
15440
  }
13537
15441
  }
13538
15442
  }
13539
- const managedDocsDir = resolve3(cwd, ".pourkit/managed/docs/agents");
15443
+ const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
13540
15444
  if (existsSync18(managedDocsDir)) {
13541
15445
  const docFiles = await walkDir2(managedDocsDir);
13542
15446
  for (const file of docFiles) {
13543
15447
  if (!file.endsWith(".md")) continue;
13544
15448
  try {
13545
- const content = await readFile5(file, "utf-8");
13546
- const relPath = relative3(cwd, file);
15449
+ const content = await readFile6(file, "utf-8");
15450
+ const relPath = relative4(cwd, file);
13547
15451
  const violations = scanManagedPolicyText(content, relPath);
13548
15452
  for (const v of violations) {
13549
15453
  failures.push({
@@ -13557,16 +15461,16 @@ async function validateWorkflowPack(cwd) {
13557
15461
  }
13558
15462
  }
13559
15463
  }
13560
- const managedSkillsDir = resolve3(cwd, ".pourkit/managed/skills");
15464
+ const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
13561
15465
  if (existsSync18(managedSkillsDir)) {
13562
- const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
15466
+ const skillDirs = await readdir3(managedSkillsDir, { withFileTypes: true });
13563
15467
  for (const entry of skillDirs) {
13564
15468
  if (!entry.isDirectory()) continue;
13565
- const skillFile = resolve3(managedSkillsDir, entry.name, "SKILL.md");
15469
+ const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
13566
15470
  if (!existsSync18(skillFile)) continue;
13567
15471
  try {
13568
- const content = await readFile5(skillFile, "utf-8");
13569
- const relPath = relative3(cwd, skillFile);
15472
+ const content = await readFile6(skillFile, "utf-8");
15473
+ const relPath = relative4(cwd, skillFile);
13570
15474
  const violations = scanManagedPolicyText(content, relPath);
13571
15475
  for (const v of violations) {
13572
15476
  failures.push({
@@ -13580,14 +15484,14 @@ async function validateWorkflowPack(cwd) {
13580
15484
  }
13581
15485
  }
13582
15486
  }
13583
- const managedPromptsDir = resolve3(cwd, ".pourkit/managed/prompts");
15487
+ const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
13584
15488
  if (existsSync18(managedPromptsDir)) {
13585
15489
  for (const promptName of catalog.prompts) {
13586
- const promptFile = resolve3(managedPromptsDir, promptName);
15490
+ const promptFile = resolve4(managedPromptsDir, promptName);
13587
15491
  if (!existsSync18(promptFile)) continue;
13588
15492
  try {
13589
- const content = await readFile5(promptFile, "utf-8");
13590
- const relPath = relative3(cwd, promptFile);
15493
+ const content = await readFile6(promptFile, "utf-8");
15494
+ const relPath = relative4(cwd, promptFile);
13591
15495
  const violations = scanManagedPolicyText(content, relPath);
13592
15496
  for (const v of violations) {
13593
15497
  failures.push({
@@ -13602,14 +15506,14 @@ async function validateWorkflowPack(cwd) {
13602
15506
  }
13603
15507
  }
13604
15508
  for (const skillName of catalog.releaseAddonSkills) {
13605
- const addonSkillFile = resolve3(
15509
+ const addonSkillFile = resolve4(
13606
15510
  cwd,
13607
15511
  `.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
13608
15512
  );
13609
15513
  if (!existsSync18(addonSkillFile)) continue;
13610
15514
  try {
13611
- const content = await readFile5(addonSkillFile, "utf-8");
13612
- const relPath = relative3(cwd, addonSkillFile);
15515
+ const content = await readFile6(addonSkillFile, "utf-8");
15516
+ const relPath = relative4(cwd, addonSkillFile);
13613
15517
  const violations = scanManagedPolicyText(content, relPath);
13614
15518
  for (const v of violations) {
13615
15519
  failures.push({
@@ -13623,14 +15527,14 @@ async function validateWorkflowPack(cwd) {
13623
15527
  }
13624
15528
  }
13625
15529
  for (const docName of catalog.releaseAddonDocs) {
13626
- const addonDocFile = resolve3(
15530
+ const addonDocFile = resolve4(
13627
15531
  cwd,
13628
15532
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13629
15533
  );
13630
15534
  if (!existsSync18(addonDocFile)) continue;
13631
15535
  try {
13632
- const content = await readFile5(addonDocFile, "utf-8");
13633
- const relPath = relative3(cwd, addonDocFile);
15536
+ const content = await readFile6(addonDocFile, "utf-8");
15537
+ const relPath = relative4(cwd, addonDocFile);
13634
15538
  const violations = scanManagedPolicyText(content, relPath);
13635
15539
  for (const v of violations) {
13636
15540
  failures.push({
@@ -13660,7 +15564,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13660
15564
  ok: icmPath !== null,
13661
15565
  detail: icmPath !== null ? icmPath : "icm not found on PATH"
13662
15566
  });
13663
- const gitignorePath = resolve3(cwd, ".gitignore");
15567
+ const gitignorePath = resolve4(cwd, ".gitignore");
13664
15568
  const gitignoreContent = existsSync18(gitignorePath) ? readFileSync18(gitignorePath, "utf-8") : "";
13665
15569
  const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
13666
15570
  checks.push({
@@ -13668,7 +15572,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13668
15572
  ok: hasGitignoreEntry,
13669
15573
  detail: hasGitignoreEntry ? ".gitignore covers .pourkit/icm/" : ".gitignore does not cover .pourkit/icm/"
13670
15574
  });
13671
- const memoryDbPath = resolve3(cwd, ".pourkit", "icm", "memories.db");
15575
+ const memoryDbPath = resolve4(cwd, ".pourkit", "icm", "memories.db");
13672
15576
  const hasMemoryDb = existsSync18(memoryDbPath);
13673
15577
  checks.push({
13674
15578
  name: "icm-db-exists",
@@ -13682,7 +15586,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13682
15586
  detail: shapeValid ? "memory: { enabled: true, provider: icm }" : `invalid memory config: enabled=${memoryConfig.enabled}, provider=${memoryConfig.provider}`
13683
15587
  });
13684
15588
  const staleAgentFiles = ["AGENTS.md", "CLAUDE.md"].filter((fileName) => {
13685
- const filePath = resolve3(cwd, fileName);
15589
+ const filePath = resolve4(cwd, fileName);
13686
15590
  if (!existsSync18(filePath)) return false;
13687
15591
  const content = readFileSync18(filePath, "utf-8");
13688
15592
  const managedBlock = extractManagedBlockContent(content);
@@ -13718,8 +15622,8 @@ function extractManagedBlockContent(content) {
13718
15622
  async function runDoctorCommand(options) {
13719
15623
  const repoRootPath = options.cwd ?? process.cwd();
13720
15624
  const schemaDir = localSchemaDir(repoRootPath);
13721
- const localSchemaPath = resolve3(schemaDir, "pourkit.schema.json");
13722
- const localHashPath = resolve3(schemaDir, "pourkit.schema.hash");
15625
+ const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
15626
+ const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
13723
15627
  const localSchemaExists = existsSync18(localSchemaPath);
13724
15628
  const localHashExists = existsSync18(localHashPath);
13725
15629
  let packagedHash = null;
@@ -13730,14 +15634,14 @@ async function runDoctorCommand(options) {
13730
15634
  let localHashContent = null;
13731
15635
  if (localHashExists) {
13732
15636
  try {
13733
- localHashContent = await readFile5(localHashPath, "utf-8");
15637
+ localHashContent = await readFile6(localHashPath, "utf-8");
13734
15638
  } catch {
13735
15639
  }
13736
15640
  }
13737
15641
  const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
13738
15642
  const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
13739
15643
  const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
13740
- const configPath = resolve3(repoRootPath, ".pourkit/config.json");
15644
+ const configPath = resolve4(repoRootPath, ".pourkit/config.json");
13741
15645
  let configValidation;
13742
15646
  let rawMemoryConfig;
13743
15647
  if (existsSync18(configPath)) {
@@ -13775,7 +15679,7 @@ async function runDoctorCommand(options) {
13775
15679
  "pourkit.config.mjs",
13776
15680
  "pourkit.config.js",
13777
15681
  "pourkit.json"
13778
- ].filter((p) => existsSync18(resolve3(repoRootPath, p)));
15682
+ ].filter((p) => existsSync18(resolve4(repoRootPath, p)));
13779
15683
  const workflowPack = await validateWorkflowPack(repoRootPath);
13780
15684
  const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
13781
15685
  let recommendation = null;
@@ -13818,31 +15722,31 @@ async function runDoctorCommand(options) {
13818
15722
  async function runConfigSyncSchemaCommand(options) {
13819
15723
  const repoRootPath = options.cwd ?? process.cwd();
13820
15724
  const schemaDir = localSchemaDir(repoRootPath);
13821
- await mkdir5(schemaDir, { recursive: true });
13822
- const packagedSchema = await readFile5(PACKAGED_SCHEMA_PATH, "utf-8");
13823
- const packagedHash = await readFile5(PACKAGED_HASH_PATH, "utf-8");
13824
- const localSchemaPath = resolve3(schemaDir, "pourkit.schema.json");
13825
- const localHashPath = resolve3(schemaDir, "pourkit.schema.hash");
15725
+ await mkdir6(schemaDir, { recursive: true });
15726
+ const packagedSchema = await readFile6(PACKAGED_SCHEMA_PATH, "utf-8");
15727
+ const packagedHash = await readFile6(PACKAGED_HASH_PATH, "utf-8");
15728
+ const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
15729
+ const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
13826
15730
  let schemaWritten = false;
13827
15731
  let hashWritten = false;
13828
15732
  if (existsSync18(localSchemaPath)) {
13829
- const existing = await readFile5(localSchemaPath, "utf-8");
15733
+ const existing = await readFile6(localSchemaPath, "utf-8");
13830
15734
  if (existing !== packagedSchema) {
13831
- await writeFile2(localSchemaPath, packagedSchema, "utf-8");
15735
+ await writeFile3(localSchemaPath, packagedSchema, "utf-8");
13832
15736
  schemaWritten = true;
13833
15737
  }
13834
15738
  } else {
13835
- await writeFile2(localSchemaPath, packagedSchema, "utf-8");
15739
+ await writeFile3(localSchemaPath, packagedSchema, "utf-8");
13836
15740
  schemaWritten = true;
13837
15741
  }
13838
15742
  if (existsSync18(localHashPath)) {
13839
- const existing = await readFile5(localHashPath, "utf-8");
15743
+ const existing = await readFile6(localHashPath, "utf-8");
13840
15744
  if (existing !== packagedHash) {
13841
- await writeFile2(localHashPath, packagedHash, "utf-8");
15745
+ await writeFile3(localHashPath, packagedHash, "utf-8");
13842
15746
  hashWritten = true;
13843
15747
  }
13844
15748
  } else {
13845
- await writeFile2(localHashPath, packagedHash, "utf-8");
15749
+ await writeFile3(localHashPath, packagedHash, "utf-8");
13846
15750
  hashWritten = true;
13847
15751
  }
13848
15752
  return {
@@ -13853,12 +15757,12 @@ async function runConfigSyncSchemaCommand(options) {
13853
15757
  }
13854
15758
 
13855
15759
  // commands/workflow-pack-sync.ts
13856
- import { existsSync as existsSync19, readdirSync as readdirSync4 } from "fs";
13857
- import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir3 } from "fs/promises";
13858
- import { resolve as resolve4, dirname as dirname6, relative as relative4, normalize as normalize2 } from "path";
15760
+ import { existsSync as existsSync19, readdirSync as readdirSync5 } from "fs";
15761
+ import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile4, readdir as readdir4 } from "fs/promises";
15762
+ import { resolve as resolve5, dirname as dirname7, relative as relative5, normalize as normalize2 } from "path";
13859
15763
  import { fileURLToPath as fileURLToPath4 } from "url";
13860
15764
  var __filename4 = fileURLToPath4(import.meta.url);
13861
- var __dirname4 = dirname6(__filename4);
15765
+ var __dirname4 = dirname7(__filename4);
13862
15766
  var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
13863
15767
  var PROJECT_OWNED_DIRECTORIES = [
13864
15768
  ".pourkit/docs/adr",
@@ -13867,8 +15771,8 @@ var PROJECT_OWNED_DIRECTORIES = [
13867
15771
  ];
13868
15772
  var OVERRIDES_DIR = ".pourkit/overrides";
13869
15773
  function resolvePackagedManagedPath2(...segments) {
13870
- const bundledPath = resolve4(__dirname4, "managed", ...segments);
13871
- const sourcePath = resolve4(__dirname4, "..", "managed", ...segments);
15774
+ const bundledPath = resolve5(__dirname4, "managed", ...segments);
15775
+ const sourcePath = resolve5(__dirname4, "..", "managed", ...segments);
13872
15776
  const candidates = [bundledPath, sourcePath];
13873
15777
  return candidates.find((candidate) => existsSync19(candidate)) ?? bundledPath;
13874
15778
  }
@@ -13892,13 +15796,13 @@ function resolvePackagedManagedPromptPath(promptName) {
13892
15796
  }
13893
15797
  async function readPackagedTextFile2(filePath) {
13894
15798
  try {
13895
- return await readFile6(filePath, "utf-8");
15799
+ return await readFile7(filePath, "utf-8");
13896
15800
  } catch {
13897
15801
  return null;
13898
15802
  }
13899
15803
  }
13900
15804
  function isPathContained2(parent, child) {
13901
- const relativePath = relative4(parent, child);
15805
+ const relativePath = relative5(parent, child);
13902
15806
  return !relativePath.startsWith("..") && !relativePath.startsWith("../");
13903
15807
  }
13904
15808
  function isProjectOwnedPath(relativePath) {
@@ -13949,9 +15853,9 @@ function checkForPathEscape(cwd, resolvedPath) {
13949
15853
  async function walkDir3(dir) {
13950
15854
  const files = [];
13951
15855
  try {
13952
- const entries = await readdir3(dir, { withFileTypes: true });
15856
+ const entries = await readdir4(dir, { withFileTypes: true });
13953
15857
  for (const entry of entries) {
13954
- const full = resolve4(dir, entry.name);
15858
+ const full = resolve5(dir, entry.name);
13955
15859
  if (entry.isDirectory()) {
13956
15860
  files.push(...await walkDir3(full));
13957
15861
  } else {
@@ -13969,28 +15873,28 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
13969
15873
  if (sourceContent === null) {
13970
15874
  return false;
13971
15875
  }
13972
- const targetPath = resolve4(cwd, targetRelativePath);
15876
+ const targetPath = resolve5(cwd, targetRelativePath);
13973
15877
  const escapeError = checkForPathEscape(cwd, targetPath);
13974
15878
  if (escapeError) {
13975
15879
  errors.push(escapeError);
13976
15880
  return false;
13977
15881
  }
13978
- await mkdir6(resolve4(targetPath, ".."), { recursive: true });
15882
+ await mkdir7(resolve5(targetPath, ".."), { recursive: true });
13979
15883
  if (existsSync19(targetPath)) {
13980
- const existing = await readFile6(targetPath, "utf-8");
15884
+ const existing = await readFile7(targetPath, "utf-8");
13981
15885
  if (existing === sourceContent) {
13982
15886
  return false;
13983
15887
  }
13984
15888
  }
13985
- await writeFile3(targetPath, sourceContent, "utf-8");
15889
+ await writeFile4(targetPath, sourceContent, "utf-8");
13986
15890
  return true;
13987
15891
  }
13988
15892
  function walkDirSync(dir) {
13989
15893
  const result = [];
13990
15894
  try {
13991
- const entries = readdirSync4(dir, { withFileTypes: true });
15895
+ const entries = readdirSync5(dir, { withFileTypes: true });
13992
15896
  for (const entry of entries) {
13993
- const full = resolve4(dir, entry.name);
15897
+ const full = resolve5(dir, entry.name);
13994
15898
  if (entry.isDirectory()) {
13995
15899
  result.push(...walkDirSync(full));
13996
15900
  } else {
@@ -14002,27 +15906,27 @@ function walkDirSync(dir) {
14002
15906
  return result;
14003
15907
  }
14004
15908
  async function detectOverrides(cwd) {
14005
- const overridesDir = resolve4(cwd, OVERRIDES_DIR);
15909
+ const overridesDir = resolve5(cwd, OVERRIDES_DIR);
14006
15910
  if (!existsSync19(overridesDir)) {
14007
15911
  return [];
14008
15912
  }
14009
15913
  const files = await walkDir3(overridesDir);
14010
- return files.map((f) => relative4(cwd, f));
15914
+ return files.map((f) => relative5(cwd, f));
14011
15915
  }
14012
15916
  async function detectProjectOwnedFiles(cwd) {
14013
15917
  const found = [];
14014
15918
  for (const ownedPath of PROJECT_OWNED_PATHS) {
14015
- const fullPath = resolve4(cwd, ownedPath);
15919
+ const fullPath = resolve5(cwd, ownedPath);
14016
15920
  if (existsSync19(fullPath)) {
14017
15921
  found.push(ownedPath);
14018
15922
  }
14019
15923
  }
14020
15924
  for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
14021
- const fullDir = resolve4(cwd, ownedDir);
15925
+ const fullDir = resolve5(cwd, ownedDir);
14022
15926
  if (existsSync19(fullDir)) {
14023
15927
  const files = await walkDir3(fullDir);
14024
15928
  for (const file of files) {
14025
- found.push(relative4(cwd, file));
15929
+ found.push(relative5(cwd, file));
14026
15930
  }
14027
15931
  }
14028
15932
  }
@@ -14030,8 +15934,8 @@ async function detectProjectOwnedFiles(cwd) {
14030
15934
  }
14031
15935
  async function isReleaseWorkflowEnabled(cwd) {
14032
15936
  try {
14033
- const configPath = resolve4(cwd, ".pourkit", "config.json");
14034
- const content = await readFile6(configPath, "utf-8");
15937
+ const configPath = resolve5(cwd, ".pourkit", "config.json");
15938
+ const content = await readFile7(configPath, "utf-8");
14035
15939
  const parsed = JSON.parse(content);
14036
15940
  return parsed.releaseWorkflow?.enabled === true;
14037
15941
  } catch {
@@ -14160,7 +16064,7 @@ async function runWorkflowPackSyncCommand(options) {
14160
16064
  const schemaResult = await runConfigSyncSchemaCommand({ cwd });
14161
16065
  let configMemory;
14162
16066
  try {
14163
- const configPath = resolve4(cwd, ".pourkit", "config.json");
16067
+ const configPath = resolve5(cwd, ".pourkit", "config.json");
14164
16068
  const config = await loadConfig(configPath);
14165
16069
  if (config.memory?.enabled === true && config.memory?.provider === "icm") {
14166
16070
  configMemory = config.memory;
@@ -14170,16 +16074,16 @@ async function runWorkflowPackSyncCommand(options) {
14170
16074
  const managedBlockContent = generateManagedBlockContent(configMemory);
14171
16075
  const fullManagedBlock = buildManagedBlock(managedBlockContent);
14172
16076
  async function syncManagedAgentFile(fileName, options2) {
14173
- const filePath = resolve4(cwd, fileName);
16077
+ const filePath = resolve5(cwd, fileName);
14174
16078
  if (!existsSync19(filePath)) {
14175
16079
  if (!options2.createIfMissing) return false;
14176
- await writeFile3(filePath, fullManagedBlock, "utf-8");
16080
+ await writeFile4(filePath, fullManagedBlock, "utf-8");
14177
16081
  return true;
14178
16082
  }
14179
- const existing = await readFile6(filePath, "utf-8");
16083
+ const existing = await readFile7(filePath, "utf-8");
14180
16084
  const newContent = upsertManagedBlock(existing, managedBlockContent);
14181
16085
  if (newContent !== existing) {
14182
- await writeFile3(filePath, newContent, "utf-8");
16086
+ await writeFile4(filePath, newContent, "utf-8");
14183
16087
  return true;
14184
16088
  }
14185
16089
  return false;
@@ -14192,11 +16096,11 @@ async function runWorkflowPackSyncCommand(options) {
14192
16096
  }
14193
16097
  const enabledAddons = releaseEnabled ? ["release"] : [];
14194
16098
  const manifestMeta = buildWorkflowPackMetadata(enabledAddons);
14195
- const manifestPath = resolve4(cwd, ".pourkit", "manifest.json");
16099
+ const manifestPath = resolve5(cwd, ".pourkit", "manifest.json");
14196
16100
  let manifestWritten = false;
14197
16101
  let previousManifestWorkflowPack = void 0;
14198
16102
  try {
14199
- const existingRaw = await readFile6(manifestPath, "utf-8").catch(() => null);
16103
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(() => null);
14200
16104
  if (existingRaw) {
14201
16105
  const existing = JSON.parse(existingRaw);
14202
16106
  previousManifestWorkflowPack = existing.workflowPack;
@@ -14205,13 +16109,13 @@ async function runWorkflowPackSyncCommand(options) {
14205
16109
  }
14206
16110
  if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
14207
16111
  try {
14208
- const existingRaw = await readFile6(manifestPath, "utf-8").catch(
16112
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(
14209
16113
  () => null
14210
16114
  );
14211
16115
  let manifest = existingRaw ? JSON.parse(existingRaw) : {};
14212
16116
  manifest.workflowPack = manifestMeta;
14213
- await mkdir6(resolve4(manifestPath, ".."), { recursive: true });
14214
- await writeFile3(
16117
+ await mkdir7(resolve5(manifestPath, ".."), { recursive: true });
16118
+ await writeFile4(
14215
16119
  manifestPath,
14216
16120
  JSON.stringify(manifest, null, 2) + "\n",
14217
16121
  "utf-8"
@@ -14260,7 +16164,7 @@ async function syncSkill(skillName, catalog, acc, ctx) {
14260
16164
  return;
14261
16165
  }
14262
16166
  for (const sourceFile of skillFiles) {
14263
- const relPath = relative4(skillDir, sourceFile);
16167
+ const relPath = relative5(skillDir, sourceFile);
14264
16168
  const targetRelativePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
14265
16169
  if (isProjectOwnedPath(targetRelativePath)) {
14266
16170
  skippedProjectOwned.push(targetRelativePath);
@@ -14336,7 +16240,7 @@ async function syncAddonSkill(skillName, acc, ctx) {
14336
16240
  return;
14337
16241
  }
14338
16242
  for (const sourceFile of skillFiles) {
14339
- const relPath = relative4(addonSkillDir, sourceFile);
16243
+ const relPath = relative5(addonSkillDir, sourceFile);
14340
16244
  const targetRelativePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
14341
16245
  if (isProjectOwnedPath(targetRelativePath)) {
14342
16246
  skippedProjectOwned.push(targetRelativePath);
@@ -14417,6 +16321,16 @@ var GitHubIssueProvider = class {
14417
16321
  });
14418
16322
  return { number: data.number, url: data.html_url, title: data.title };
14419
16323
  }
16324
+ async updateIssue(issueNumber, options) {
16325
+ const { data } = await this.client.octokit.rest.issues.update({
16326
+ owner: this.client.owner,
16327
+ repo: this.client.repo,
16328
+ issue_number: issueNumber,
16329
+ ...options.title !== void 0 ? { title: options.title } : {},
16330
+ ...options.body !== void 0 ? { body: options.body } : {}
16331
+ });
16332
+ return { number: data.number, url: data.html_url, title: data.title };
16333
+ }
14420
16334
  async fetchIssue(number) {
14421
16335
  const { data } = await this.client.octokit.rest.issues.get({
14422
16336
  owner: this.client.owner,
@@ -14939,21 +16853,21 @@ init_common();
14939
16853
 
14940
16854
  // execution/sandcastle-execution.ts
14941
16855
  import { existsSync as existsSync21, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
14942
- import { join as join20 } from "path";
16856
+ import { join as join23 } from "path";
14943
16857
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
14944
16858
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
14945
16859
 
14946
16860
  // execution/execution-provider.ts
14947
16861
  init_common();
14948
16862
  import { mkdtempSync } from "fs";
14949
- import { writeFile as writeFile4 } from "fs/promises";
16863
+ import { writeFile as writeFile5 } from "fs/promises";
14950
16864
  import { tmpdir } from "os";
14951
- import { dirname as dirname7, join as join18 } from "path";
16865
+ import { dirname as dirname8, join as join21 } from "path";
14952
16866
  async function writeExecutionArtifacts(worktreePath, artifacts) {
14953
16867
  for (const artifact of artifacts) {
14954
- const filePath = join18(worktreePath, artifact.path);
14955
- await ensureDir(dirname7(filePath));
14956
- await writeFile4(filePath, artifact.content, "utf-8");
16868
+ const filePath = join21(worktreePath, artifact.path);
16869
+ await ensureDir(dirname8(filePath));
16870
+ await writeFile5(filePath, artifact.content, "utf-8");
14957
16871
  }
14958
16872
  }
14959
16873
 
@@ -15013,7 +16927,7 @@ async function createSandboxFromExistingWorktree(options) {
15013
16927
  }
15014
16928
 
15015
16929
  // execution/sandbox-options.ts
15016
- import { join as join19 } from "path";
16930
+ import { join as join22 } from "path";
15017
16931
  var POURKIT_ICM_CONTAINER_DIR = "/home/agent/.pourkit/icm";
15018
16932
  function buildSandboxOptions(repoRoot2, sandbox, memory) {
15019
16933
  const mounts = [];
@@ -15022,12 +16936,12 @@ function buildSandboxOptions(repoRoot2, sandbox, memory) {
15022
16936
  }
15023
16937
  if (memory?.available) {
15024
16938
  mounts.push({
15025
- hostPath: join19(repoRoot2, ".pourkit", "icm"),
16939
+ hostPath: join22(repoRoot2, ".pourkit", "icm"),
15026
16940
  sandboxPath: POURKIT_ICM_CONTAINER_DIR,
15027
16941
  readonly: false
15028
16942
  });
15029
16943
  mounts.push({
15030
- hostPath: join19(repoRoot2, ".pourkit", "icm", "cache"),
16944
+ hostPath: join22(repoRoot2, ".pourkit", "icm", "cache"),
15031
16945
  sandboxPath: "/home/agent/.cache/icm",
15032
16946
  readonly: false
15033
16947
  });
@@ -15161,13 +17075,13 @@ var SandcastleExecutionSession = class {
15161
17075
  "Memory enabled but memory context is invalid. Run `pourkit memory init` or install ICM for host planning use."
15162
17076
  );
15163
17077
  }
15164
- const hostMemoryDir = join20(repoRoot2, ".pourkit", "icm");
17078
+ const hostMemoryDir = join23(repoRoot2, ".pourkit", "icm");
15165
17079
  if (!existsSync21(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
15166
17080
  throw new MissingHostMemoryDirError(
15167
17081
  "Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
15168
17082
  );
15169
17083
  }
15170
- mkdirSync9(join20(hostMemoryDir, "cache"), { recursive: true });
17084
+ mkdirSync9(join23(hostMemoryDir, "cache"), { recursive: true });
15171
17085
  }
15172
17086
  const sandboxOptions = buildSandboxOptions(
15173
17087
  repoRoot2,
@@ -15326,7 +17240,7 @@ function sanitizeBranch(branchName) {
15326
17240
  }
15327
17241
  function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
15328
17242
  return paths.filter((relativePath) => {
15329
- const source = join20(repoRoot2, relativePath);
17243
+ const source = join23(repoRoot2, relativePath);
15330
17244
  if (!existsSync21(source)) {
15331
17245
  return true;
15332
17246
  }
@@ -15337,7 +17251,7 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
15337
17251
  } catch {
15338
17252
  return true;
15339
17253
  }
15340
- const destination = join20(worktreePath, relativePath);
17254
+ const destination = join23(worktreePath, relativePath);
15341
17255
  return !existsSync21(destination);
15342
17256
  });
15343
17257
  }
@@ -15402,17 +17316,17 @@ function isPlainObject(value) {
15402
17316
  return typeof value === "object" && value !== null && !Array.isArray(value);
15403
17317
  }
15404
17318
  function savePromptToFile(repoRoot2, stage, iteration, prompt) {
15405
- const promptsDir = join20(repoRoot2, ".pourkit", ".tmp", "prompts");
17319
+ const promptsDir = join23(repoRoot2, ".pourkit", ".tmp", "prompts");
15406
17320
  mkdirSync9(promptsDir, { recursive: true });
15407
17321
  const timestamp2 = Date.now();
15408
17322
  const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
15409
17323
  const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
15410
- const filePath = join20(promptsDir, filename);
17324
+ const filePath = join23(promptsDir, filename);
15411
17325
  writeFileSync6(filePath, prompt, "utf-8");
15412
17326
  }
15413
17327
 
15414
17328
  // cli.ts
15415
- function normalizePrdRef(ref) {
17329
+ function normalizePrdRef2(ref) {
15416
17330
  const normalized = ref.trim().toUpperCase();
15417
17331
  if (!/^PRD-\d+$/.test(normalized)) {
15418
17332
  throw new Error(
@@ -15477,6 +17391,7 @@ function createCliProgram(version) {
15477
17391
  const program = new Command();
15478
17392
  program.name("pourkit").version(version).exitOverride().description("AI-driven issue-to-PR workflow for GitHub repositories.");
15479
17393
  const issueCommand = program.command("issue");
17394
+ const prdPlanWorkspaceCommand = program.command("prd-plan-workspace").description("PRD Plan Workspace commands");
15480
17395
  program.command("run-verification").description("Run configured verification commands for a target").option("--target <name>", "target name").option("--cwd <path>", "target repository directory").action(async (options) => {
15481
17396
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15482
17397
  const logPath = path8.join(
@@ -15508,7 +17423,7 @@ function createCliProgram(version) {
15508
17423
  });
15509
17424
  program.command("validate-artifact").description("Validate an agent handoff artifact").argument(
15510
17425
  "<kind>",
15511
- "artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution, or issue-final-review"
17426
+ "artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution, issue-final-review, prd-plan, prd-child-issue, or prd-plan-workspace"
15512
17427
  ).argument("<artifactPath>", "artifact path to validate").argument("[extraPaths...]", "unused compatibility argument").option("--iteration <number>", "review/refactor iteration", (value) => {
15513
17428
  const parsed = Number.parseInt(value, 10);
15514
17429
  if (!Number.isInteger(parsed) || parsed < 1) {
@@ -15561,7 +17476,13 @@ function createCliProgram(version) {
15561
17476
  ).option(
15562
17477
  "--base-ref <ref>",
15563
17478
  "canonical base ref for issue-final-review changedFiles coverage validation"
15564
- ).option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
17479
+ ).option(
17480
+ "--prd-ref <ref>",
17481
+ "PRD reference for PRD plan validation artifacts"
17482
+ ).option(
17483
+ "--workspace <path>",
17484
+ "PRD Plan Workspace path for prd-plan-workspace validation"
17485
+ ).option("--child-id <id>", "child issue ID for prd-child-issue validation").option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
15565
17486
  (kind, artifactPath, _extraPaths, options) => {
15566
17487
  const allowedKinds = /* @__PURE__ */ new Set([
15567
17488
  "reviewer",
@@ -15569,7 +17490,10 @@ function createCliProgram(version) {
15569
17490
  "finalizer",
15570
17491
  "conflict-resolution",
15571
17492
  "failure-resolution",
15572
- "issue-final-review"
17493
+ "issue-final-review",
17494
+ "prd-plan",
17495
+ "prd-child-issue",
17496
+ "prd-plan-workspace"
15573
17497
  ]);
15574
17498
  if (!allowedKinds.has(kind)) {
15575
17499
  throw new CommanderError(
@@ -15591,7 +17515,10 @@ function createCliProgram(version) {
15591
17515
  issueNumber: options.issueNumber,
15592
17516
  branchName: options.branchName,
15593
17517
  baseRef: options.baseRef,
15594
- checkConflictMarkers: options.checkConflictMarkers
17518
+ checkConflictMarkers: options.checkConflictMarkers,
17519
+ prdRef: options.prdRef,
17520
+ workspace: options.workspace,
17521
+ childId: options.childId
15595
17522
  });
15596
17523
  console.log(JSON.stringify(result, null, 2));
15597
17524
  if (!result.ok) {
@@ -15599,6 +17526,40 @@ function createCliProgram(version) {
15599
17526
  }
15600
17527
  }
15601
17528
  );
17529
+ prdPlanWorkspaceCommand.command("publish").description("Publish a PRD Plan Workspace through Beads and GitHub").argument("<workspace>", "PRD Plan Workspace path").requiredOption("--prd-ref <ref>", "PRD reference to publish").option("--cwd <path>", "target repository directory").action(
17530
+ async (workspace, options) => {
17531
+ const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
17532
+ const logPath = path8.join(
17533
+ targetRepoRoot,
17534
+ ".pourkit",
17535
+ "logs",
17536
+ "prd-plan-workspace-publish.log"
17537
+ );
17538
+ const logger = createLogger("pourkit", logPath);
17539
+ try {
17540
+ const config = await loadRepoConfig(targetRepoRoot);
17541
+ const client = await requireGitHubClient({ cwd: targetRepoRoot });
17542
+ const issueProvider = new GitHubIssueProvider(client, {
17543
+ readyForAgentLabel: config.labels.readyForAgent,
17544
+ blockedLabel: config.labels.blocked,
17545
+ issueListLimit: config.checks.issueListLimit
17546
+ });
17547
+ const result = await runPrdPlanWorkspacePublishCommand({
17548
+ repoRoot: targetRepoRoot,
17549
+ workspacePath: workspace,
17550
+ prdRef: options.prdRef,
17551
+ issueProvider,
17552
+ labels: config.labels,
17553
+ logger
17554
+ });
17555
+ console.log(JSON.stringify(result, null, 2));
17556
+ } catch (error) {
17557
+ await handleError(logger, error);
17558
+ } finally {
17559
+ await logger.close();
17560
+ }
17561
+ }
17562
+ );
15602
17563
  issueCommand.command("create").description("Create a new GitHub issue").requiredOption("--title <title>", "issue title").option("--body <body>", "issue body text").option("--body-file <path>", "file to read issue body from").option(
15603
17564
  "--label <label>",
15604
17565
  "label to apply (repeatable)",
@@ -15738,7 +17699,7 @@ function createCliProgram(version) {
15738
17699
  );
15739
17700
  program.command("queue-run").requiredOption("--target <name>", "target name").option("--force", "bypass issue gates").option("--loop", "process runnable issues until the queue is drained").option("--cwd <path>", "target repository directory").option("--prd <ref>", "limit queue selection to child issues under a PRD").action(
15740
17701
  async (options) => {
15741
- const prdRef = options.prd ? normalizePrdRef(options.prd) : void 0;
17702
+ const prdRef = options.prd ? normalizePrdRef2(options.prd) : void 0;
15742
17703
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15743
17704
  const config = await loadRepoConfig(targetRepoRoot);
15744
17705
  const logPath = path8.join(
@@ -15806,7 +17767,7 @@ function createCliProgram(version) {
15806
17767
  prdRun.command("start").argument("<prdRef>", "PRD ref").requiredOption("--target <name>", "target name").option("--base <branch>", "override target baseBranch for this PRD Run").option("--adopt-existing-branch", "adopt an existing PRD branch").option("--cwd <path>", "target repository directory").action(
15807
17768
  async (prdRef, options) => {
15808
17769
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15809
- const normalizedPrdRef = normalizePrdRef(prdRef);
17770
+ const normalizedPrdRef = normalizePrdRef2(prdRef);
15810
17771
  const logPath = path8.join(
15811
17772
  targetRepoRoot,
15812
17773
  ".pourkit",
@@ -15852,7 +17813,7 @@ function createCliProgram(version) {
15852
17813
  prdRun.command("launch").argument("<prdRef>", "PRD ref").requiredOption("--target <name>", "target name").option("--base <branch>", "override target baseBranch for this PRD Run").option("--cwd <path>", "target repository directory").option("--no-auto-merge", "skip auto-merge for manual review").option("--adopt-existing-branch", "adopt an existing PRD branch").action(
15853
17814
  async (prdRef, options) => {
15854
17815
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15855
- const normalizedPrdRef = normalizePrdRef(prdRef);
17816
+ const normalizedPrdRef = normalizePrdRef2(prdRef);
15856
17817
  const logPath = path8.join(
15857
17818
  targetRepoRoot,
15858
17819
  ".pourkit",
@@ -16128,11 +18089,11 @@ function createCliProgram(version) {
16128
18089
  return program;
16129
18090
  }
16130
18091
  async function resolveCliVersion() {
16131
- if (isPackageVersion("0.0.0-next-20260625084151")) {
16132
- return "0.0.0-next-20260625084151";
18092
+ if (isPackageVersion("0.0.0-next-20260627024509")) {
18093
+ return "0.0.0-next-20260627024509";
16133
18094
  }
16134
- if (isReleaseVersion("0.0.0-next-20260625084151")) {
16135
- return "0.0.0-next-20260625084151";
18095
+ if (isReleaseVersion("0.0.0-next-20260627024509")) {
18096
+ return "0.0.0-next-20260627024509";
16136
18097
  }
16137
18098
  try {
16138
18099
  const root = repoRoot();