@pourkit/cli 0.0.0-next-20260625073725 → 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,141 +7649,994 @@ 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, "\\$&");
7665
+ }
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")}`;
7484
7672
  }
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
- });
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()}`;
7678
+ }
7679
+ return `bd-${prdMatch[1].padStart(4, "0")}.${childMatch[1]}`;
7498
7680
  }
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
- ]
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());
7521
7694
  }
7522
- };
7523
- }
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] : []
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] : []
7537
8640
  }
7538
8641
  };
7539
8642
  }
@@ -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 {
@@ -9277,8 +10622,109 @@ async function attemptRecordedIssueLabelProjection(options, prdRef, resumeIssueN
9277
10622
  });
9278
10623
  }
9279
10624
  }
9280
- return false;
10625
+ return false;
10626
+ }
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
+ };
9281
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
+ };
9282
10728
  }
9283
10729
  async function launchPrdRun(options) {
9284
10730
  const prdRef = normalizePrdRunRef(options.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,
@@ -9537,7 +10986,37 @@ async function launchPrdRun(options) {
9537
10986
  start: startReceipt,
9538
10987
  repairGuidance: currentRecord?.repairGuidance
9539
10988
  });
9540
- return await launchGithubQueueDrain(
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,
@@ -9556,21 +11035,299 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9556
11035
  buildPrdRunRepairGuidance({
9557
11036
  blockedGate: "queue",
9558
11037
  blockedStage: "queue-drain",
9559
- failureCode: "missing-github-providers",
9560
- humanReadableReason: reason,
11038
+ failureCode: "missing-github-providers",
11039
+ humanReadableReason: reason,
11040
+ repairability: "operator-action",
11041
+ nextAction: "inspect-and-retry",
11042
+ nextRecommendedCommand: "pourkit prd-run launch --with-providers",
11043
+ diagnostics: [
11044
+ "Missing one or more required providers for GitHub-backed mode."
11045
+ ]
11046
+ }),
11047
+ plan,
11048
+ startOutcome,
11049
+ targetName,
11050
+ startReceipt
11051
+ );
11052
+ }
11053
+ if (!startReceipt) {
11054
+ return buildLaunchBlockedOutcome(
11055
+ options.repoRoot,
11056
+ prdRef,
11057
+ buildPrdRunRepairGuidance({
11058
+ blockedGate: "branch-state",
11059
+ blockedStage: "start-validation",
11060
+ failureCode: "missing-start-receipt",
11061
+ humanReadableReason: `PRD Run ${prdRef} is missing start receipt before GitHub queue drain.`,
11062
+ repairability: "automatic-retry-safe",
11063
+ nextAction: "rerun-prd-run-launch",
11064
+ nextRecommendedCommand: "pourkit prd-run launch"
11065
+ }),
11066
+ plan,
11067
+ startOutcome,
11068
+ targetName
11069
+ );
11070
+ }
11071
+ if (!options.config) {
11072
+ const reason = `PRD Run ${prdRef} requires config for GitHub-backed queue drain.`;
11073
+ return buildLaunchBlockedOutcome(
11074
+ options.repoRoot,
11075
+ prdRef,
11076
+ buildPrdRunRepairGuidance({
11077
+ blockedGate: "queue",
11078
+ blockedStage: "queue-drain",
11079
+ failureCode: "missing-config",
11080
+ humanReadableReason: reason,
11081
+ repairability: "operator-action",
11082
+ nextAction: "inspect-and-retry",
11083
+ nextRecommendedCommand: "pourkit prd-run launch --with-config",
11084
+ diagnostics: ["Missing config for GitHub-backed mode."]
11085
+ }),
11086
+ plan,
11087
+ startOutcome,
11088
+ targetName,
11089
+ startReceipt
11090
+ );
11091
+ }
11092
+ const issueProvider = options.issueProvider;
11093
+ const prProvider = options.prProvider;
11094
+ const executionProvider = options.executionProvider;
11095
+ const logger = options.logger;
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
+ );
11102
+ try {
11103
+ const outcome = await runQueueCommand({
11104
+ targetName,
11105
+ config: options.config,
11106
+ issueProvider,
11107
+ prProvider,
11108
+ executionProvider,
11109
+ force: false,
11110
+ loop: true,
11111
+ logger,
11112
+ repoRoot: options.repoRoot,
11113
+ prdRef,
11114
+ queueRunContext: {
11115
+ prdRef,
11116
+ prdBranch: startReceipt.prdBranch,
11117
+ ...resumeIssueNumber !== void 0 ? { resumeIssueNumber } : {}
11118
+ }
11119
+ });
11120
+ if (outcome.selected === null && outcome.code === "drained") {
11121
+ if (outcome.processedCount === 0) {
11122
+ const diagnostics = [
11123
+ "Queue drained without processing issues. No PRD-scoped candidates or runnable Issues."
11124
+ ];
11125
+ const result2 = buildLaunchBlockedOutcome(
11126
+ options.repoRoot,
11127
+ prdRef,
11128
+ buildPrdRunRepairGuidance({
11129
+ blockedGate: "queue",
11130
+ blockedStage: "queue-drain",
11131
+ failureCode: "zero-processed",
11132
+ humanReadableReason: diagnostics[0],
11133
+ repairability: "automatic-retry-safe",
11134
+ nextAction: "rerun-prd-run-launch",
11135
+ nextRecommendedCommand: "pourkit prd-run launch",
11136
+ diagnostics
11137
+ }),
11138
+ plan,
11139
+ startOutcome,
11140
+ targetName,
11141
+ startReceipt
11142
+ );
11143
+ const projectionOk2 = await attemptRecordedIssueLabelProjection(
11144
+ options,
11145
+ prdRef,
11146
+ resumeIssueNumber,
11147
+ originalGuidance
11148
+ );
11149
+ if (!projectionOk2 && originalGuidance) {
11150
+ const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
11151
+ return {
11152
+ kind: "blocked",
11153
+ prdRef,
11154
+ guidance: updatedRecord.repairGuidance,
11155
+ start: startOutcome,
11156
+ resume: plan
11157
+ };
11158
+ }
11159
+ return result2;
11160
+ }
11161
+ startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
11162
+ startReceipt.queueProcessedCount = outcome.processedCount;
11163
+ const prdBranch = startReceipt.prdBranch ?? prdRef;
11164
+ writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
11165
+ const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
11166
+ if (drainRecord) {
11167
+ const drainContext = {
11168
+ prdRef,
11169
+ baseBranch: startReceipt.startBaseBranch,
11170
+ prdBranch,
11171
+ allChildIssuesFinalizedIntoPrdBranch: outcome.allProcessedIssuesFinalizedIntoPrdBranch === true
11172
+ };
11173
+ const terminalEvidence = validatePrdRunTerminalEvidence(
11174
+ drainRecord,
11175
+ drainContext
11176
+ );
11177
+ if (!terminalEvidence.ok) {
11178
+ return buildLaunchBlockedOutcome(
11179
+ options.repoRoot,
11180
+ prdRef,
11181
+ terminalEvidence.guidance,
11182
+ plan,
11183
+ startOutcome,
11184
+ targetName,
11185
+ startReceipt
11186
+ );
11187
+ }
11188
+ }
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,
9561
11257
  repairability: "operator-action",
9562
11258
  nextAction: "inspect-and-retry",
9563
- nextRecommendedCommand: "pourkit prd-run launch --with-providers",
9564
- diagnostics: [
9565
- "Missing one or more required providers for GitHub-backed mode."
9566
- ]
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
9567
11305
  }),
9568
11306
  plan,
9569
11307
  startOutcome,
9570
11308
  targetName,
9571
11309
  startReceipt
9572
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;
9573
11328
  }
11329
+ }
11330
+ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName, plan, startOutcome, resumeIssueNumber) {
9574
11331
  if (!startReceipt) {
9575
11332
  return buildLaunchBlockedOutcome(
9576
11333
  options.repoRoot,
@@ -9579,7 +11336,7 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9579
11336
  blockedGate: "branch-state",
9580
11337
  blockedStage: "start-validation",
9581
11338
  failureCode: "missing-start-receipt",
9582
- humanReadableReason: `PRD Run ${prdRef} is missing start receipt before GitHub queue drain.`,
11339
+ humanReadableReason: `PRD Run ${prdRef} is missing start receipt before Beads queue drain.`,
9583
11340
  repairability: "automatic-retry-safe",
9584
11341
  nextAction: "rerun-prd-run-launch",
9585
11342
  nextRecommendedCommand: "pourkit prd-run launch"
@@ -9590,7 +11347,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9590
11347
  );
9591
11348
  }
9592
11349
  if (!options.config) {
9593
- const reason = `PRD Run ${prdRef} requires config for GitHub-backed queue drain.`;
9594
11350
  return buildLaunchBlockedOutcome(
9595
11351
  options.repoRoot,
9596
11352
  prdRef,
@@ -9598,11 +11354,54 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9598
11354
  blockedGate: "queue",
9599
11355
  blockedStage: "queue-drain",
9600
11356
  failureCode: "missing-config",
9601
- humanReadableReason: reason,
11357
+ humanReadableReason: `PRD Run ${prdRef} requires config for Beads-backed queue drain.`,
9602
11358
  repairability: "operator-action",
9603
11359
  nextAction: "inspect-and-retry",
9604
11360
  nextRecommendedCommand: "pourkit prd-run launch --with-config",
9605
- diagnostics: ["Missing config for GitHub-backed mode."]
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
+ ]
9606
11405
  }),
9607
11406
  plan,
9608
11407
  startOutcome,
@@ -9614,166 +11413,262 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
9614
11413
  const prProvider = options.prProvider;
9615
11414
  const executionProvider = options.executionProvider;
9616
11415
  const logger = options.logger;
11416
+ const processedResults = [];
9617
11417
  const originalGuidance = readPrdRun(options.repoRoot, prdRef).record?.repairGuidance;
9618
- try {
9619
- const outcome = await runQueueCommand({
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,
9620
11443
  targetName,
9621
- config: options.config,
9622
- issueProvider,
9623
- prProvider,
9624
- executionProvider,
9625
- force: false,
9626
- loop: true,
9627
- logger,
9628
- repoRoot: options.repoRoot,
11444
+ startReceipt
11445
+ );
11446
+ }
11447
+ if (resumeIssueNumber !== void 0) {
11448
+ const resumeIssue = originalGuidance?.issue;
11449
+ const resumeBeadsContext = resumeIssue?.beadsChildId && resumeIssue.beadsChildRef && !resumeIssue.beadsChildClosed ? {
9629
11450
  prdRef,
9630
- queueRunContext: {
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,
9631
11461
  prdRef,
9632
- prdBranch: startReceipt.prdBranch,
9633
- ...resumeIssueNumber !== void 0 ? { resumeIssueNumber } : {}
9634
- }
9635
- });
9636
- if (outcome.selected === null && outcome.code === "drained") {
9637
- if (outcome.processedCount === 0) {
9638
- const diagnostics = [
9639
- "Queue drained without processing issues. No PRD-scoped candidates or runnable Issues."
9640
- ];
9641
- const result2 = buildLaunchBlockedOutcome(
9642
- options.repoRoot,
9643
- prdRef,
9644
- buildPrdRunRepairGuidance({
9645
- blockedGate: "queue",
9646
- blockedStage: "queue-drain",
9647
- failureCode: "zero-processed",
9648
- humanReadableReason: diagnostics[0],
9649
- repairability: "automatic-retry-safe",
9650
- nextAction: "rerun-prd-run-launch",
9651
- nextRecommendedCommand: "pourkit prd-run launch",
9652
- diagnostics
9653
- }),
9654
- plan,
9655
- startOutcome,
9656
- targetName,
9657
- startReceipt
9658
- );
9659
- const projectionOk2 = await attemptRecordedIssueLabelProjection(
9660
- options,
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: {
9661
11490
  prdRef,
9662
- resumeIssueNumber,
9663
- originalGuidance
9664
- );
9665
- if (!projectionOk2 && originalGuidance) {
9666
- const updatedRecord = readPrdRun(options.repoRoot, prdRef).record;
9667
- return {
9668
- kind: "blocked",
9669
- prdRef,
9670
- guidance: updatedRecord.repairGuidance,
9671
- start: startOutcome,
9672
- resume: plan
9673
- };
11491
+ prdBranch: startReceipt.prdBranch,
11492
+ ...resumeBeadsContext ? { beadsChild: resumeBeadsContext } : {}
9674
11493
  }
9675
- return result2;
9676
- }
11494
+ });
11495
+ processedResults.push({
11496
+ beadsChildId: resumeIssue?.beadsChildId ?? "resumed",
11497
+ issueNumber: runResult.selected.number,
11498
+ publicationStatus: runResult.runResult.publicationStatus
11499
+ });
9677
11500
  startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
9678
- startReceipt.queueProcessedCount = outcome.processedCount;
9679
- const prdBranch = startReceipt.prdBranch ?? prdRef;
9680
- writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
9681
- const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
9682
- if (drainRecord) {
9683
- const drainContext = {
9684
- prdRef,
9685
- baseBranch: startReceipt.startBaseBranch,
9686
- prdBranch,
9687
- allChildIssuesFinalizedIntoPrdBranch: outcome.allProcessedIssuesFinalizedIntoPrdBranch === true
9688
- };
9689
- const terminalEvidence = validatePrdRunTerminalEvidence(
9690
- drainRecord,
9691
- drainContext
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.`
9692
11544
  );
9693
- if (!terminalEvidence.ok) {
9694
- return buildLaunchBlockedOutcome(
9695
- options.repoRoot,
9696
- prdRef,
9697
- terminalEvidence.guidance,
9698
- plan,
9699
- startOutcome,
9700
- targetName,
9701
- startReceipt
9702
- );
9703
- }
11545
+ break;
9704
11546
  }
9705
- writeTerminalRecord(
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";
@@ -10674,6 +12578,50 @@ icm --db .pourkit/icm/memories.db topics # list a
10674
12578
  <!-- icm:end -->`;
10675
12579
  }
10676
12580
 
12581
+ // shared/managed-block.ts
12582
+ var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
12583
+ var MANAGED_BLOCK_END = "<!-- END POURKIT MANAGED BLOCK -->";
12584
+ var MANAGED_BLOCK_PATTERN = new RegExp(
12585
+ `${escapeRegExp3(MANAGED_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp3(
12586
+ MANAGED_BLOCK_END
12587
+ )}[ \\t]*(?:\\r?\\n)?`,
12588
+ "g"
12589
+ );
12590
+ function escapeRegExp3(value) {
12591
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12592
+ }
12593
+ function buildManagedBlock(content) {
12594
+ const body = content.endsWith("\n") ? content : `${content}
12595
+ `;
12596
+ return `${MANAGED_BLOCK_BEGIN}
12597
+ ${body}${MANAGED_BLOCK_END}
12598
+ `;
12599
+ }
12600
+ function upsertManagedBlock(existing, content) {
12601
+ const blockContent = buildManagedBlock(content);
12602
+ const matches = [...existing.matchAll(MANAGED_BLOCK_PATTERN)];
12603
+ if (matches.length === 0) {
12604
+ if (existing.length === 0) {
12605
+ return blockContent;
12606
+ }
12607
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
12608
+ return `${existing}${separator}${blockContent}`;
12609
+ }
12610
+ let next = "";
12611
+ let cursor = 0;
12612
+ for (const [index, match] of matches.entries()) {
12613
+ const start = match.index ?? 0;
12614
+ const end = start + match[0].length;
12615
+ next += existing.slice(cursor, start);
12616
+ if (index === 0) {
12617
+ next += blockContent;
12618
+ }
12619
+ cursor = end;
12620
+ }
12621
+ next += existing.slice(cursor);
12622
+ return next;
12623
+ }
12624
+
10677
12625
  // commands/init.ts
10678
12626
  var execFileAsync2 = promisify2(execFile2);
10679
12627
  var __filename2 = fileURLToPath2(import.meta.url);
@@ -11020,7 +12968,7 @@ function generateGitignoreBlock() {
11020
12968
  }
11021
12969
  async function walkDir(dir) {
11022
12970
  const files = [];
11023
- const entries = await readdir(dir, { withFileTypes: true });
12971
+ const entries = await readdir2(dir, { withFileTypes: true });
11024
12972
  for (const entry of entries) {
11025
12973
  const full = path5.join(dir, entry.name);
11026
12974
  if (entry.isDirectory()) {
@@ -11032,7 +12980,7 @@ async function walkDir(dir) {
11032
12980
  return files;
11033
12981
  }
11034
12982
  async function computeFileChecksum(filePath) {
11035
- const content = await readFile4(filePath);
12983
+ const content = await readFile5(filePath);
11036
12984
  return createHash("sha256").update(content).digest("hex");
11037
12985
  }
11038
12986
  function lockfileExists(root, name) {
@@ -11121,7 +13069,7 @@ async function discoverOpenCodeAgentFiles(sourceRoot) {
11121
13069
  (candidate) => existsSync16(candidate) && statSync(candidate).isDirectory()
11122
13070
  );
11123
13071
  if (!agentsDir) return [];
11124
- const entries = await readdir(agentsDir, { withFileTypes: true });
13072
+ const entries = await readdir2(agentsDir, { withFileTypes: true });
11125
13073
  return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path5.join(agentsDir, entry.name));
11126
13074
  }
11127
13075
  async function discoverRootDomainDocs(root) {
@@ -11134,7 +13082,7 @@ async function discoverRootDomainDocs(root) {
11134
13082
  }
11135
13083
  const adrDir = path5.join(root, "docs", "adr");
11136
13084
  if (existsSync16(adrDir)) {
11137
- const entries = await readdir(adrDir, { withFileTypes: true });
13085
+ const entries = await readdir2(adrDir, { withFileTypes: true });
11138
13086
  for (const entry of entries) {
11139
13087
  if (entry.isFile() && entry.name.endsWith(".md")) {
11140
13088
  docs.push(path5.join(adrDir, entry.name));
@@ -11449,7 +13397,7 @@ async function planInit(options) {
11449
13397
  "skills"
11450
13398
  );
11451
13399
  const projectionSkillsDir = path5.join(targetRoot, ".agents", "skills");
11452
- const entries = await readdir(s, { withFileTypes: true });
13400
+ const entries = await readdir2(s, { withFileTypes: true });
11453
13401
  const skillNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
11454
13402
  const addonManagedDir = path5.join(
11455
13403
  targetRoot,
@@ -11515,7 +13463,7 @@ async function planInit(options) {
11515
13463
  });
11516
13464
  continue;
11517
13465
  }
11518
- const sourceContent = await readFile4(file, "utf-8");
13466
+ const sourceContent = await readFile5(file, "utf-8");
11519
13467
  const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
11520
13468
  operations.push({
11521
13469
  kind: "create",
@@ -11594,7 +13542,7 @@ async function planInit(options) {
11594
13542
  }
11595
13543
  let hasPackageJson = true;
11596
13544
  try {
11597
- await readFile4(path5.join(targetRoot, "package.json"), "utf-8");
13545
+ await readFile5(path5.join(targetRoot, "package.json"), "utf-8");
11598
13546
  } catch {
11599
13547
  hasPackageJson = false;
11600
13548
  }
@@ -11930,9 +13878,7 @@ Do not edit this file.
11930
13878
  reason: "Init AGENTS.md with Pourkit managed block",
11931
13879
  requiresConfirmation: false,
11932
13880
  destructive: false,
11933
- content: `${MANAGED_BLOCK_BEGIN}
11934
- ${managedAgentContent}${MANAGED_BLOCK_END}
11935
- `
13881
+ content: buildManagedBlock(managedAgentContent)
11936
13882
  });
11937
13883
  }
11938
13884
  const hasExistingClaude = operations.some(
@@ -11946,9 +13892,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
11946
13892
  reason: "Init CLAUDE.md with Pourkit managed block",
11947
13893
  requiresConfirmation: false,
11948
13894
  destructive: false,
11949
- content: `${MANAGED_BLOCK_BEGIN}
11950
- ${managedAgentContent}${MANAGED_BLOCK_END}
11951
- `
13895
+ content: buildManagedBlock(managedAgentContent)
11952
13896
  });
11953
13897
  }
11954
13898
  const gitignoreTarget = path5.join(targetRoot, ".gitignore");
@@ -11961,9 +13905,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
11961
13905
  reason: "Init .gitignore with Pourkit entries",
11962
13906
  requiresConfirmation: false,
11963
13907
  destructive: false,
11964
- content: `${MANAGED_BLOCK_BEGIN}
11965
- ${gitignoreContent}${MANAGED_BLOCK_END}
11966
- `
13908
+ content: buildManagedBlock(gitignoreContent)
11967
13909
  });
11968
13910
  } else {
11969
13911
  operations.push({
@@ -12388,32 +14330,18 @@ async function promptForInitChoices(plan, current) {
12388
14330
  }
12389
14331
  async function writeFileAtomic(filePath, content) {
12390
14332
  const tmpPath = `${filePath}.tmp.${randomUUID()}`;
12391
- await writeFile(tmpPath, content, "utf-8");
14333
+ await writeFile2(tmpPath, content, "utf-8");
12392
14334
  await rename(tmpPath, filePath);
12393
14335
  }
12394
- var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
12395
- var MANAGED_BLOCK_END = "<!-- END POURKIT MANAGED BLOCK -->";
12396
14336
  async function updateManagedBlock(filePath, content) {
12397
- const blockContent = `${MANAGED_BLOCK_BEGIN}
12398
- ${content}${MANAGED_BLOCK_END}
12399
- `;
12400
14337
  if (!existsSync16(filePath)) {
12401
14338
  const dir = path5.dirname(filePath);
12402
- await mkdir4(dir, { recursive: true });
12403
- await writeFileAtomic(filePath, blockContent);
14339
+ await mkdir5(dir, { recursive: true });
14340
+ await writeFileAtomic(filePath, buildManagedBlock(content));
12404
14341
  return;
12405
14342
  }
12406
- const existing = await readFile4(filePath, "utf-8");
12407
- const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
12408
- const endIdx = existing.indexOf(MANAGED_BLOCK_END);
12409
- if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
12410
- const before = existing.slice(0, beginIdx);
12411
- const after = existing.slice(endIdx + MANAGED_BLOCK_END.length);
12412
- await writeFileAtomic(filePath, before + blockContent + after);
12413
- } else {
12414
- const suffix = existing.endsWith("\n") ? "" : "\n";
12415
- await writeFileAtomic(filePath, existing + suffix + blockContent + "\n");
12416
- }
14343
+ const existing = await readFile5(filePath, "utf-8");
14344
+ await writeFileAtomic(filePath, upsertManagedBlock(existing, content));
12417
14345
  }
12418
14346
  async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
12419
14347
  const manifestDir = path5.join(plan.targetRoot, ".pourkit");
@@ -12451,7 +14379,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
12451
14379
  workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
12452
14380
  assets
12453
14381
  };
12454
- await mkdir4(manifestDir, { recursive: true });
14382
+ await mkdir5(manifestDir, { recursive: true });
12455
14383
  await writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
12456
14384
  return manifest;
12457
14385
  }
@@ -12477,7 +14405,7 @@ async function applyInitPlan(plan, options) {
12477
14405
  continue;
12478
14406
  }
12479
14407
  const dir = path5.dirname(op.path);
12480
- await mkdir4(dir, { recursive: true });
14408
+ await mkdir5(dir, { recursive: true });
12481
14409
  await writeFileAtomic(op.path, op.content ?? "");
12482
14410
  applied++;
12483
14411
  break;
@@ -12496,7 +14424,7 @@ async function applyInitPlan(plan, options) {
12496
14424
  skipped++;
12497
14425
  } else {
12498
14426
  const dir = path5.dirname(op.path);
12499
- await mkdir4(dir, { recursive: true });
14427
+ await mkdir5(dir, { recursive: true });
12500
14428
  await copyFile(op.sourcePath, op.path);
12501
14429
  applied++;
12502
14430
  }
@@ -12521,7 +14449,7 @@ async function applyInitPlan(plan, options) {
12521
14449
  continue;
12522
14450
  }
12523
14451
  const dir = path5.dirname(op.path);
12524
- await mkdir4(dir, { recursive: true });
14452
+ await mkdir5(dir, { recursive: true });
12525
14453
  await rename(op.sourcePath, op.path);
12526
14454
  applied++;
12527
14455
  break;
@@ -12838,13 +14766,13 @@ Init applied: ${result.applied} operations applied, ${result.skipped} skipped.`
12838
14766
  // commands/memory-init.ts
12839
14767
  import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync5 } from "fs";
12840
14768
  import { execFile as execFile3 } from "child_process";
12841
- import { join as join17 } from "path";
14769
+ import { join as join20 } from "path";
12842
14770
  import { promisify as promisify3 } from "util";
12843
14771
  var execFileAsync3 = promisify3(execFile3);
12844
14772
  async function runMemoryInitCommand(options) {
12845
14773
  const cwd = options.cwd ?? process.cwd();
12846
14774
  const execCommand = options.execCommand ?? execFileAsync3;
12847
- const configPath = join17(cwd, ".pourkit", "config.json");
14775
+ const configPath = join20(cwd, ".pourkit", "config.json");
12848
14776
  if (!existsSync17(configPath)) {
12849
14777
  return {
12850
14778
  ok: false,
@@ -12876,8 +14804,8 @@ async function runMemoryInitCommand(options) {
12876
14804
  const next = { ...raw, memory };
12877
14805
  writeFileSync5(configPath, JSON.stringify(next, null, 2) + "\n");
12878
14806
  }
12879
- const memoryDir = join17(cwd, ".pourkit", "icm");
12880
- const memoryDbPath = join17(memoryDir, "memories.db");
14807
+ const memoryDir = join20(cwd, ".pourkit", "icm");
14808
+ const memoryDbPath = join20(memoryDir, "memories.db");
12881
14809
  mkdirSync8(memoryDir, { recursive: true });
12882
14810
  let dbInitialized = false;
12883
14811
  let warning;
@@ -12891,7 +14819,7 @@ async function runMemoryInitCommand(options) {
12891
14819
  } catch {
12892
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.";
12893
14821
  }
12894
- const gitignorePath = join17(cwd, ".gitignore");
14822
+ const gitignorePath = join20(cwd, ".gitignore");
12895
14823
  updateManagedBlockSync(gitignorePath, generateGitignoreBlock());
12896
14824
  const managed = await generateManagedAgentInstructions({
12897
14825
  sourceRoot: cwd,
@@ -12899,14 +14827,14 @@ async function runMemoryInitCommand(options) {
12899
14827
  });
12900
14828
  let updatedAgentFile = false;
12901
14829
  for (const fileName of ["AGENTS.md", "CLAUDE.md"]) {
12902
- const agentPath = join17(cwd, fileName);
14830
+ const agentPath = join20(cwd, fileName);
12903
14831
  if (existsSync17(agentPath)) {
12904
14832
  updateManagedBlockSync(agentPath, managed);
12905
14833
  updatedAgentFile = true;
12906
14834
  }
12907
14835
  }
12908
14836
  if (!updatedAgentFile) {
12909
- updateManagedBlockSync(join17(cwd, "AGENTS.md"), managed);
14837
+ updateManagedBlockSync(join20(cwd, "AGENTS.md"), managed);
12910
14838
  }
12911
14839
  return {
12912
14840
  ok: true,
@@ -12917,24 +14845,12 @@ async function runMemoryInitCommand(options) {
12917
14845
  };
12918
14846
  }
12919
14847
  function updateManagedBlockSync(filePath, content) {
12920
- const blockContent = `${MANAGED_BLOCK_BEGIN}
12921
- ${content}${MANAGED_BLOCK_END}
12922
- `;
12923
14848
  if (!existsSync17(filePath)) {
12924
- writeFileSync5(filePath, blockContent);
14849
+ writeFileSync5(filePath, buildManagedBlock(content));
12925
14850
  return;
12926
14851
  }
12927
14852
  const existing = readFileSync17(filePath, "utf8");
12928
- const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
12929
- const endIdx = existing.indexOf(MANAGED_BLOCK_END);
12930
- if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
12931
- const before = existing.slice(0, beginIdx);
12932
- const after = existing.slice(endIdx + MANAGED_BLOCK_END.length);
12933
- writeFileSync5(filePath, before + blockContent + after);
12934
- return;
12935
- }
12936
- const suffix = existing.endsWith("\n") ? "" : "\n";
12937
- writeFileSync5(filePath, `${existing}${suffix}${blockContent}`);
14853
+ writeFileSync5(filePath, upsertManagedBlock(existing, content));
12938
14854
  }
12939
14855
 
12940
14856
  // commands/serena.ts
@@ -13054,17 +14970,17 @@ async function runSerenaStatusCommand(options) {
13054
14970
 
13055
14971
  // commands/config-schema.ts
13056
14972
  import { readFileSync as readFileSync18, existsSync as existsSync18 } from "fs";
13057
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile2, readdir as readdir2 } from "fs/promises";
13058
- 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";
13059
14975
  import { fileURLToPath as fileURLToPath3 } from "url";
13060
14976
  import Ajv2 from "ajv";
13061
14977
  init_common();
13062
14978
  var __filename3 = fileURLToPath3(import.meta.url);
13063
- var __dirname3 = dirname5(__filename3);
14979
+ var __dirname3 = dirname6(__filename3);
13064
14980
  function resolvePackagedAssetPath2(fileName) {
13065
14981
  const candidates = [
13066
- resolve3(__dirname3, "schema", fileName),
13067
- resolve3(__dirname3, "../schema", fileName)
14982
+ resolve4(__dirname3, "schema", fileName),
14983
+ resolve4(__dirname3, "../schema", fileName)
13068
14984
  ];
13069
14985
  return candidates.find((candidate) => existsSync18(candidate)) ?? candidates[0];
13070
14986
  }
@@ -13091,15 +15007,15 @@ function readPackagedHash() {
13091
15007
  return readFileSync18(PACKAGED_HASH_PATH, "utf-8");
13092
15008
  }
13093
15009
  function localSchemaDir(repoRoot2) {
13094
- return resolve3(repoRoot2, ".pourkit/schema");
15010
+ return resolve4(repoRoot2, ".pourkit/schema");
13095
15011
  }
13096
15012
  var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
13097
15013
  var MANAGED_BLOCK_BEGIN_MARKER = MANAGED_BLOCK_BEGIN2.trim();
13098
15014
  var MANAGED_BLOCK_END_MARKER = "<!-- END POURKIT MANAGED BLOCK -->";
13099
15015
  var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
13100
15016
  function resolvePackagedManagedPath(...segments) {
13101
- const bundledPath = resolve3(__dirname3, "managed", ...segments);
13102
- const sourcePath = resolve3(__dirname3, "..", "managed", ...segments);
15017
+ const bundledPath = resolve4(__dirname3, "managed", ...segments);
15018
+ const sourcePath = resolve4(__dirname3, "..", "managed", ...segments);
13103
15019
  const candidates = [bundledPath, sourcePath];
13104
15020
  return candidates.find((candidate) => existsSync18(candidate)) ?? bundledPath;
13105
15021
  }
@@ -13117,21 +15033,21 @@ function resolvePackagedReleaseAddonDocPath(docName) {
13117
15033
  }
13118
15034
  async function readPackagedTextFile(filePath) {
13119
15035
  try {
13120
- return await readFile5(filePath, "utf-8");
15036
+ return await readFile6(filePath, "utf-8");
13121
15037
  } catch {
13122
15038
  return null;
13123
15039
  }
13124
15040
  }
13125
15041
  function isPathContained(parent, child) {
13126
- const relativePath = relative3(parent, child);
15042
+ const relativePath = relative4(parent, child);
13127
15043
  return !relativePath.startsWith("..") && !relativePath.startsWith("../");
13128
15044
  }
13129
15045
  async function walkDir2(dir) {
13130
15046
  const files = [];
13131
15047
  try {
13132
- const entries = await readdir2(dir, { withFileTypes: true });
15048
+ const entries = await readdir3(dir, { withFileTypes: true });
13133
15049
  for (const entry of entries) {
13134
- const full = resolve3(dir, entry.name);
15050
+ const full = resolve4(dir, entry.name);
13135
15051
  if (entry.isDirectory()) {
13136
15052
  files.push(...await walkDir2(full));
13137
15053
  } else {
@@ -13213,7 +15129,7 @@ async function validateWorkflowPack(cwd) {
13213
15129
  const catalog = getWorkflowPackCatalog();
13214
15130
  const baselineSkills = getBaselineManagedSkillNames();
13215
15131
  for (const docName of catalog.docs) {
13216
- const localPath = resolve3(cwd, `.pourkit/managed/docs/agents/${docName}`);
15132
+ const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
13217
15133
  if (!existsSync18(localPath)) {
13218
15134
  failures.push({
13219
15135
  severity: "failure",
@@ -13226,9 +15142,9 @@ async function validateWorkflowPack(cwd) {
13226
15142
  const packagedContent = await readPackagedTextFile(packagedPath);
13227
15143
  if (packagedContent !== null) {
13228
15144
  try {
13229
- const localContent = await readFile5(localPath, "utf-8");
15145
+ const localContent = await readFile6(localPath, "utf-8");
13230
15146
  if (localContent !== packagedContent) {
13231
- const overridePath = resolve3(
15147
+ const overridePath = resolve4(
13232
15148
  cwd,
13233
15149
  `.pourkit/overrides/docs/agents/${docName}`
13234
15150
  );
@@ -13254,7 +15170,7 @@ async function validateWorkflowPack(cwd) {
13254
15170
  }
13255
15171
  }
13256
15172
  for (const skillName of baselineSkills) {
13257
- const skillDir = resolve3(cwd, `.pourkit/managed/skills/${skillName}`);
15173
+ const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
13258
15174
  if (!existsSync18(skillDir)) {
13259
15175
  failures.push({
13260
15176
  severity: "failure",
@@ -13263,7 +15179,7 @@ async function validateWorkflowPack(cwd) {
13263
15179
  message: `Missing managed skill: ${skillName}`
13264
15180
  });
13265
15181
  } else {
13266
- const skillSourcePath = resolve3(skillDir, "SKILL.md");
15182
+ const skillSourcePath = resolve4(skillDir, "SKILL.md");
13267
15183
  if (existsSync18(skillSourcePath)) {
13268
15184
  const packagedSkillPath = resolvePackagedManagedPath(
13269
15185
  "skills",
@@ -13273,9 +15189,9 @@ async function validateWorkflowPack(cwd) {
13273
15189
  const packagedContent = await readPackagedTextFile(packagedSkillPath);
13274
15190
  if (packagedContent !== null) {
13275
15191
  try {
13276
- const localContent = await readFile5(skillSourcePath, "utf-8");
15192
+ const localContent = await readFile6(skillSourcePath, "utf-8");
13277
15193
  if (localContent !== packagedContent) {
13278
- const overridePath = resolve3(
15194
+ const overridePath = resolve4(
13279
15195
  cwd,
13280
15196
  `.pourkit/overrides/skills/${skillName}/SKILL.md`
13281
15197
  );
@@ -13302,7 +15218,7 @@ async function validateWorkflowPack(cwd) {
13302
15218
  }
13303
15219
  }
13304
15220
  for (const promptName of catalog.prompts) {
13305
- const promptPath = resolve3(cwd, `.pourkit/managed/prompts/${promptName}`);
15221
+ const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
13306
15222
  if (!existsSync18(promptPath)) {
13307
15223
  failures.push({
13308
15224
  severity: "failure",
@@ -13313,7 +15229,7 @@ async function validateWorkflowPack(cwd) {
13313
15229
  }
13314
15230
  }
13315
15231
  for (const skillName of baselineSkills) {
13316
- const projectionDir = resolve3(cwd, `.agents/skills/${skillName}`);
15232
+ const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
13317
15233
  if (!existsSync18(projectionDir)) {
13318
15234
  warnings.push({
13319
15235
  severity: "warning",
@@ -13322,7 +15238,7 @@ async function validateWorkflowPack(cwd) {
13322
15238
  message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
13323
15239
  });
13324
15240
  } else {
13325
- const projectionPath = resolve3(projectionDir, "SKILL.md");
15241
+ const projectionPath = resolve4(projectionDir, "SKILL.md");
13326
15242
  if (existsSync18(projectionPath)) {
13327
15243
  const managedSourcePath = resolvePackagedManagedPath(
13328
15244
  "skills",
@@ -13332,7 +15248,7 @@ async function validateWorkflowPack(cwd) {
13332
15248
  const packagedContent = await readPackagedTextFile(managedSourcePath);
13333
15249
  if (packagedContent !== null) {
13334
15250
  try {
13335
- const projectionContent = await readFile5(projectionPath, "utf-8");
15251
+ const projectionContent = await readFile6(projectionPath, "utf-8");
13336
15252
  const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
13337
15253
  if (projectionContent !== buildOpenCodeSkillProjectionContent(
13338
15254
  managedSourceRelativePath,
@@ -13351,10 +15267,10 @@ async function validateWorkflowPack(cwd) {
13351
15267
  }
13352
15268
  }
13353
15269
  }
13354
- const agentsPath = resolve3(cwd, "AGENTS.md");
15270
+ const agentsPath = resolve4(cwd, "AGENTS.md");
13355
15271
  if (existsSync18(agentsPath)) {
13356
15272
  try {
13357
- const agentsContent = await readFile5(agentsPath, "utf-8");
15273
+ const agentsContent = await readFile6(agentsPath, "utf-8");
13358
15274
  if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
13359
15275
  failures.push({
13360
15276
  severity: "failure",
@@ -13374,11 +15290,11 @@ async function validateWorkflowPack(cwd) {
13374
15290
  } catch {
13375
15291
  }
13376
15292
  }
13377
- const overridesDir = resolve3(cwd, ".pourkit/overrides");
15293
+ const overridesDir = resolve4(cwd, ".pourkit/overrides");
13378
15294
  if (existsSync18(overridesDir)) {
13379
15295
  const overrideFiles = await walkDir2(overridesDir);
13380
15296
  for (const file of overrideFiles) {
13381
- const relPath = relative3(cwd, file);
15297
+ const relPath = relative4(cwd, file);
13382
15298
  if (!warnings.some((w) => w.kind === "overridden" && w.path === relPath)) {
13383
15299
  warnings.push({
13384
15300
  severity: "warning",
@@ -13389,15 +15305,15 @@ async function validateWorkflowPack(cwd) {
13389
15305
  }
13390
15306
  }
13391
15307
  }
13392
- const configPath = resolve3(cwd, ".pourkit/config.json");
15308
+ const configPath = resolve4(cwd, ".pourkit/config.json");
13393
15309
  if (existsSync18(configPath)) {
13394
15310
  try {
13395
- const configContent = await readFile5(configPath, "utf-8");
15311
+ const configContent = await readFile6(configPath, "utf-8");
13396
15312
  const parsed = JSON.parse(configContent);
13397
15313
  const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
13398
15314
  if (releaseEnabled) {
13399
15315
  for (const skillName of catalog.releaseAddonSkills) {
13400
- const addonDir = resolve3(
15316
+ const addonDir = resolve4(
13401
15317
  cwd,
13402
15318
  `.pourkit/managed/addons/release/skills/${skillName}`
13403
15319
  );
@@ -13411,7 +15327,7 @@ async function validateWorkflowPack(cwd) {
13411
15327
  }
13412
15328
  }
13413
15329
  for (const docName of catalog.releaseAddonDocs) {
13414
- const addonDocPath = resolve3(
15330
+ const addonDocPath = resolve4(
13415
15331
  cwd,
13416
15332
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13417
15333
  );
@@ -13427,7 +15343,7 @@ async function validateWorkflowPack(cwd) {
13427
15343
  resolvePackagedReleaseAddonDocPath(docName)
13428
15344
  );
13429
15345
  if (packagedContent !== null) {
13430
- const localContent = await readFile5(addonDocPath, "utf-8");
15346
+ const localContent = await readFile6(addonDocPath, "utf-8");
13431
15347
  if (localContent !== packagedContent) {
13432
15348
  failures.push({
13433
15349
  severity: "failure",
@@ -13456,7 +15372,7 @@ async function validateWorkflowPack(cwd) {
13456
15372
  }
13457
15373
  } else {
13458
15374
  for (const skillName of catalog.releaseAddonSkills) {
13459
- const addonDir = resolve3(
15375
+ const addonDir = resolve4(
13460
15376
  cwd,
13461
15377
  `.pourkit/managed/addons/release/skills/${skillName}`
13462
15378
  );
@@ -13470,7 +15386,7 @@ async function validateWorkflowPack(cwd) {
13470
15386
  }
13471
15387
  }
13472
15388
  for (const docName of catalog.releaseAddonDocs) {
13473
- const addonDocPath = resolve3(
15389
+ const addonDocPath = resolve4(
13474
15390
  cwd,
13475
15391
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13476
15392
  );
@@ -13487,16 +15403,16 @@ async function validateWorkflowPack(cwd) {
13487
15403
  } catch {
13488
15404
  }
13489
15405
  }
13490
- const manifestPath = resolve3(cwd, ".pourkit/manifest.json");
15406
+ const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
13491
15407
  if (existsSync18(manifestPath)) {
13492
15408
  try {
13493
- const manifestContent = await readFile5(manifestPath, "utf-8");
15409
+ const manifestContent = await readFile6(manifestPath, "utf-8");
13494
15410
  const manifest = JSON.parse(manifestContent);
13495
15411
  const wp = manifest.workflowPack;
13496
15412
  if (wp?.projections) {
13497
15413
  for (const proj of wp.projections) {
13498
15414
  if (proj.path) {
13499
- const resolved = resolve3(cwd, proj.path);
15415
+ const resolved = resolve4(cwd, proj.path);
13500
15416
  if (!isPathContained(cwd, resolved)) {
13501
15417
  failures.push({
13502
15418
  severity: "failure",
@@ -13510,7 +15426,7 @@ async function validateWorkflowPack(cwd) {
13510
15426
  } catch {
13511
15427
  }
13512
15428
  }
13513
- const pathEscapeOverrideDir = resolve3(cwd, ".pourkit/overrides");
15429
+ const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
13514
15430
  if (existsSync18(pathEscapeOverrideDir)) {
13515
15431
  const overrideFiles = await walkDir2(pathEscapeOverrideDir);
13516
15432
  for (const file of overrideFiles) {
@@ -13518,20 +15434,20 @@ async function validateWorkflowPack(cwd) {
13518
15434
  failures.push({
13519
15435
  severity: "failure",
13520
15436
  kind: "path_escape",
13521
- path: relative3(cwd, file),
13522
- message: `Override file path escapes repository: ${relative3(cwd, file)}`
15437
+ path: relative4(cwd, file),
15438
+ message: `Override file path escapes repository: ${relative4(cwd, file)}`
13523
15439
  });
13524
15440
  }
13525
15441
  }
13526
15442
  }
13527
- const managedDocsDir = resolve3(cwd, ".pourkit/managed/docs/agents");
15443
+ const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
13528
15444
  if (existsSync18(managedDocsDir)) {
13529
15445
  const docFiles = await walkDir2(managedDocsDir);
13530
15446
  for (const file of docFiles) {
13531
15447
  if (!file.endsWith(".md")) continue;
13532
15448
  try {
13533
- const content = await readFile5(file, "utf-8");
13534
- const relPath = relative3(cwd, file);
15449
+ const content = await readFile6(file, "utf-8");
15450
+ const relPath = relative4(cwd, file);
13535
15451
  const violations = scanManagedPolicyText(content, relPath);
13536
15452
  for (const v of violations) {
13537
15453
  failures.push({
@@ -13545,16 +15461,16 @@ async function validateWorkflowPack(cwd) {
13545
15461
  }
13546
15462
  }
13547
15463
  }
13548
- const managedSkillsDir = resolve3(cwd, ".pourkit/managed/skills");
15464
+ const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
13549
15465
  if (existsSync18(managedSkillsDir)) {
13550
- const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
15466
+ const skillDirs = await readdir3(managedSkillsDir, { withFileTypes: true });
13551
15467
  for (const entry of skillDirs) {
13552
15468
  if (!entry.isDirectory()) continue;
13553
- const skillFile = resolve3(managedSkillsDir, entry.name, "SKILL.md");
15469
+ const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
13554
15470
  if (!existsSync18(skillFile)) continue;
13555
15471
  try {
13556
- const content = await readFile5(skillFile, "utf-8");
13557
- const relPath = relative3(cwd, skillFile);
15472
+ const content = await readFile6(skillFile, "utf-8");
15473
+ const relPath = relative4(cwd, skillFile);
13558
15474
  const violations = scanManagedPolicyText(content, relPath);
13559
15475
  for (const v of violations) {
13560
15476
  failures.push({
@@ -13568,14 +15484,14 @@ async function validateWorkflowPack(cwd) {
13568
15484
  }
13569
15485
  }
13570
15486
  }
13571
- const managedPromptsDir = resolve3(cwd, ".pourkit/managed/prompts");
15487
+ const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
13572
15488
  if (existsSync18(managedPromptsDir)) {
13573
15489
  for (const promptName of catalog.prompts) {
13574
- const promptFile = resolve3(managedPromptsDir, promptName);
15490
+ const promptFile = resolve4(managedPromptsDir, promptName);
13575
15491
  if (!existsSync18(promptFile)) continue;
13576
15492
  try {
13577
- const content = await readFile5(promptFile, "utf-8");
13578
- const relPath = relative3(cwd, promptFile);
15493
+ const content = await readFile6(promptFile, "utf-8");
15494
+ const relPath = relative4(cwd, promptFile);
13579
15495
  const violations = scanManagedPolicyText(content, relPath);
13580
15496
  for (const v of violations) {
13581
15497
  failures.push({
@@ -13590,14 +15506,14 @@ async function validateWorkflowPack(cwd) {
13590
15506
  }
13591
15507
  }
13592
15508
  for (const skillName of catalog.releaseAddonSkills) {
13593
- const addonSkillFile = resolve3(
15509
+ const addonSkillFile = resolve4(
13594
15510
  cwd,
13595
15511
  `.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
13596
15512
  );
13597
15513
  if (!existsSync18(addonSkillFile)) continue;
13598
15514
  try {
13599
- const content = await readFile5(addonSkillFile, "utf-8");
13600
- const relPath = relative3(cwd, addonSkillFile);
15515
+ const content = await readFile6(addonSkillFile, "utf-8");
15516
+ const relPath = relative4(cwd, addonSkillFile);
13601
15517
  const violations = scanManagedPolicyText(content, relPath);
13602
15518
  for (const v of violations) {
13603
15519
  failures.push({
@@ -13611,14 +15527,14 @@ async function validateWorkflowPack(cwd) {
13611
15527
  }
13612
15528
  }
13613
15529
  for (const docName of catalog.releaseAddonDocs) {
13614
- const addonDocFile = resolve3(
15530
+ const addonDocFile = resolve4(
13615
15531
  cwd,
13616
15532
  `.pourkit/managed/addons/release/docs/agents/${docName}`
13617
15533
  );
13618
15534
  if (!existsSync18(addonDocFile)) continue;
13619
15535
  try {
13620
- const content = await readFile5(addonDocFile, "utf-8");
13621
- const relPath = relative3(cwd, addonDocFile);
15536
+ const content = await readFile6(addonDocFile, "utf-8");
15537
+ const relPath = relative4(cwd, addonDocFile);
13622
15538
  const violations = scanManagedPolicyText(content, relPath);
13623
15539
  for (const v of violations) {
13624
15540
  failures.push({
@@ -13648,7 +15564,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13648
15564
  ok: icmPath !== null,
13649
15565
  detail: icmPath !== null ? icmPath : "icm not found on PATH"
13650
15566
  });
13651
- const gitignorePath = resolve3(cwd, ".gitignore");
15567
+ const gitignorePath = resolve4(cwd, ".gitignore");
13652
15568
  const gitignoreContent = existsSync18(gitignorePath) ? readFileSync18(gitignorePath, "utf-8") : "";
13653
15569
  const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
13654
15570
  checks.push({
@@ -13656,7 +15572,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13656
15572
  ok: hasGitignoreEntry,
13657
15573
  detail: hasGitignoreEntry ? ".gitignore covers .pourkit/icm/" : ".gitignore does not cover .pourkit/icm/"
13658
15574
  });
13659
- const memoryDbPath = resolve3(cwd, ".pourkit", "icm", "memories.db");
15575
+ const memoryDbPath = resolve4(cwd, ".pourkit", "icm", "memories.db");
13660
15576
  const hasMemoryDb = existsSync18(memoryDbPath);
13661
15577
  checks.push({
13662
15578
  name: "icm-db-exists",
@@ -13670,7 +15586,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
13670
15586
  detail: shapeValid ? "memory: { enabled: true, provider: icm }" : `invalid memory config: enabled=${memoryConfig.enabled}, provider=${memoryConfig.provider}`
13671
15587
  });
13672
15588
  const staleAgentFiles = ["AGENTS.md", "CLAUDE.md"].filter((fileName) => {
13673
- const filePath = resolve3(cwd, fileName);
15589
+ const filePath = resolve4(cwd, fileName);
13674
15590
  if (!existsSync18(filePath)) return false;
13675
15591
  const content = readFileSync18(filePath, "utf-8");
13676
15592
  const managedBlock = extractManagedBlockContent(content);
@@ -13706,8 +15622,8 @@ function extractManagedBlockContent(content) {
13706
15622
  async function runDoctorCommand(options) {
13707
15623
  const repoRootPath = options.cwd ?? process.cwd();
13708
15624
  const schemaDir = localSchemaDir(repoRootPath);
13709
- const localSchemaPath = resolve3(schemaDir, "pourkit.schema.json");
13710
- const localHashPath = resolve3(schemaDir, "pourkit.schema.hash");
15625
+ const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
15626
+ const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
13711
15627
  const localSchemaExists = existsSync18(localSchemaPath);
13712
15628
  const localHashExists = existsSync18(localHashPath);
13713
15629
  let packagedHash = null;
@@ -13718,14 +15634,14 @@ async function runDoctorCommand(options) {
13718
15634
  let localHashContent = null;
13719
15635
  if (localHashExists) {
13720
15636
  try {
13721
- localHashContent = await readFile5(localHashPath, "utf-8");
15637
+ localHashContent = await readFile6(localHashPath, "utf-8");
13722
15638
  } catch {
13723
15639
  }
13724
15640
  }
13725
15641
  const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
13726
15642
  const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
13727
15643
  const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
13728
- const configPath = resolve3(repoRootPath, ".pourkit/config.json");
15644
+ const configPath = resolve4(repoRootPath, ".pourkit/config.json");
13729
15645
  let configValidation;
13730
15646
  let rawMemoryConfig;
13731
15647
  if (existsSync18(configPath)) {
@@ -13763,7 +15679,7 @@ async function runDoctorCommand(options) {
13763
15679
  "pourkit.config.mjs",
13764
15680
  "pourkit.config.js",
13765
15681
  "pourkit.json"
13766
- ].filter((p) => existsSync18(resolve3(repoRootPath, p)));
15682
+ ].filter((p) => existsSync18(resolve4(repoRootPath, p)));
13767
15683
  const workflowPack = await validateWorkflowPack(repoRootPath);
13768
15684
  const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
13769
15685
  let recommendation = null;
@@ -13806,31 +15722,31 @@ async function runDoctorCommand(options) {
13806
15722
  async function runConfigSyncSchemaCommand(options) {
13807
15723
  const repoRootPath = options.cwd ?? process.cwd();
13808
15724
  const schemaDir = localSchemaDir(repoRootPath);
13809
- await mkdir5(schemaDir, { recursive: true });
13810
- const packagedSchema = await readFile5(PACKAGED_SCHEMA_PATH, "utf-8");
13811
- const packagedHash = await readFile5(PACKAGED_HASH_PATH, "utf-8");
13812
- const localSchemaPath = resolve3(schemaDir, "pourkit.schema.json");
13813
- 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");
13814
15730
  let schemaWritten = false;
13815
15731
  let hashWritten = false;
13816
15732
  if (existsSync18(localSchemaPath)) {
13817
- const existing = await readFile5(localSchemaPath, "utf-8");
15733
+ const existing = await readFile6(localSchemaPath, "utf-8");
13818
15734
  if (existing !== packagedSchema) {
13819
- await writeFile2(localSchemaPath, packagedSchema, "utf-8");
15735
+ await writeFile3(localSchemaPath, packagedSchema, "utf-8");
13820
15736
  schemaWritten = true;
13821
15737
  }
13822
15738
  } else {
13823
- await writeFile2(localSchemaPath, packagedSchema, "utf-8");
15739
+ await writeFile3(localSchemaPath, packagedSchema, "utf-8");
13824
15740
  schemaWritten = true;
13825
15741
  }
13826
15742
  if (existsSync18(localHashPath)) {
13827
- const existing = await readFile5(localHashPath, "utf-8");
15743
+ const existing = await readFile6(localHashPath, "utf-8");
13828
15744
  if (existing !== packagedHash) {
13829
- await writeFile2(localHashPath, packagedHash, "utf-8");
15745
+ await writeFile3(localHashPath, packagedHash, "utf-8");
13830
15746
  hashWritten = true;
13831
15747
  }
13832
15748
  } else {
13833
- await writeFile2(localHashPath, packagedHash, "utf-8");
15749
+ await writeFile3(localHashPath, packagedHash, "utf-8");
13834
15750
  hashWritten = true;
13835
15751
  }
13836
15752
  return {
@@ -13841,12 +15757,12 @@ async function runConfigSyncSchemaCommand(options) {
13841
15757
  }
13842
15758
 
13843
15759
  // commands/workflow-pack-sync.ts
13844
- import { existsSync as existsSync19, readdirSync as readdirSync4 } from "fs";
13845
- import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir3 } from "fs/promises";
13846
- 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";
13847
15763
  import { fileURLToPath as fileURLToPath4 } from "url";
13848
15764
  var __filename4 = fileURLToPath4(import.meta.url);
13849
- var __dirname4 = dirname6(__filename4);
15765
+ var __dirname4 = dirname7(__filename4);
13850
15766
  var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
13851
15767
  var PROJECT_OWNED_DIRECTORIES = [
13852
15768
  ".pourkit/docs/adr",
@@ -13854,11 +15770,9 @@ var PROJECT_OWNED_DIRECTORIES = [
13854
15770
  ".pourkit/handoffs"
13855
15771
  ];
13856
15772
  var OVERRIDES_DIR = ".pourkit/overrides";
13857
- var MANAGED_BLOCK_BEGIN3 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
13858
- var MANAGED_BLOCK_END2 = "\n<!-- END POURKIT MANAGED BLOCK -->";
13859
15773
  function resolvePackagedManagedPath2(...segments) {
13860
- const bundledPath = resolve4(__dirname4, "managed", ...segments);
13861
- const sourcePath = resolve4(__dirname4, "..", "managed", ...segments);
15774
+ const bundledPath = resolve5(__dirname4, "managed", ...segments);
15775
+ const sourcePath = resolve5(__dirname4, "..", "managed", ...segments);
13862
15776
  const candidates = [bundledPath, sourcePath];
13863
15777
  return candidates.find((candidate) => existsSync19(candidate)) ?? bundledPath;
13864
15778
  }
@@ -13882,13 +15796,13 @@ function resolvePackagedManagedPromptPath(promptName) {
13882
15796
  }
13883
15797
  async function readPackagedTextFile2(filePath) {
13884
15798
  try {
13885
- return await readFile6(filePath, "utf-8");
15799
+ return await readFile7(filePath, "utf-8");
13886
15800
  } catch {
13887
15801
  return null;
13888
15802
  }
13889
15803
  }
13890
15804
  function isPathContained2(parent, child) {
13891
- const relativePath = relative4(parent, child);
15805
+ const relativePath = relative5(parent, child);
13892
15806
  return !relativePath.startsWith("..") && !relativePath.startsWith("../");
13893
15807
  }
13894
15808
  function isProjectOwnedPath(relativePath) {
@@ -13939,9 +15853,9 @@ function checkForPathEscape(cwd, resolvedPath) {
13939
15853
  async function walkDir3(dir) {
13940
15854
  const files = [];
13941
15855
  try {
13942
- const entries = await readdir3(dir, { withFileTypes: true });
15856
+ const entries = await readdir4(dir, { withFileTypes: true });
13943
15857
  for (const entry of entries) {
13944
- const full = resolve4(dir, entry.name);
15858
+ const full = resolve5(dir, entry.name);
13945
15859
  if (entry.isDirectory()) {
13946
15860
  files.push(...await walkDir3(full));
13947
15861
  } else {
@@ -13959,28 +15873,28 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
13959
15873
  if (sourceContent === null) {
13960
15874
  return false;
13961
15875
  }
13962
- const targetPath = resolve4(cwd, targetRelativePath);
15876
+ const targetPath = resolve5(cwd, targetRelativePath);
13963
15877
  const escapeError = checkForPathEscape(cwd, targetPath);
13964
15878
  if (escapeError) {
13965
15879
  errors.push(escapeError);
13966
15880
  return false;
13967
15881
  }
13968
- await mkdir6(resolve4(targetPath, ".."), { recursive: true });
15882
+ await mkdir7(resolve5(targetPath, ".."), { recursive: true });
13969
15883
  if (existsSync19(targetPath)) {
13970
- const existing = await readFile6(targetPath, "utf-8");
15884
+ const existing = await readFile7(targetPath, "utf-8");
13971
15885
  if (existing === sourceContent) {
13972
15886
  return false;
13973
15887
  }
13974
15888
  }
13975
- await writeFile3(targetPath, sourceContent, "utf-8");
15889
+ await writeFile4(targetPath, sourceContent, "utf-8");
13976
15890
  return true;
13977
15891
  }
13978
15892
  function walkDirSync(dir) {
13979
15893
  const result = [];
13980
15894
  try {
13981
- const entries = readdirSync4(dir, { withFileTypes: true });
15895
+ const entries = readdirSync5(dir, { withFileTypes: true });
13982
15896
  for (const entry of entries) {
13983
- const full = resolve4(dir, entry.name);
15897
+ const full = resolve5(dir, entry.name);
13984
15898
  if (entry.isDirectory()) {
13985
15899
  result.push(...walkDirSync(full));
13986
15900
  } else {
@@ -13992,27 +15906,27 @@ function walkDirSync(dir) {
13992
15906
  return result;
13993
15907
  }
13994
15908
  async function detectOverrides(cwd) {
13995
- const overridesDir = resolve4(cwd, OVERRIDES_DIR);
15909
+ const overridesDir = resolve5(cwd, OVERRIDES_DIR);
13996
15910
  if (!existsSync19(overridesDir)) {
13997
15911
  return [];
13998
15912
  }
13999
15913
  const files = await walkDir3(overridesDir);
14000
- return files.map((f) => relative4(cwd, f));
15914
+ return files.map((f) => relative5(cwd, f));
14001
15915
  }
14002
15916
  async function detectProjectOwnedFiles(cwd) {
14003
15917
  const found = [];
14004
15918
  for (const ownedPath of PROJECT_OWNED_PATHS) {
14005
- const fullPath = resolve4(cwd, ownedPath);
15919
+ const fullPath = resolve5(cwd, ownedPath);
14006
15920
  if (existsSync19(fullPath)) {
14007
15921
  found.push(ownedPath);
14008
15922
  }
14009
15923
  }
14010
15924
  for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
14011
- const fullDir = resolve4(cwd, ownedDir);
15925
+ const fullDir = resolve5(cwd, ownedDir);
14012
15926
  if (existsSync19(fullDir)) {
14013
15927
  const files = await walkDir3(fullDir);
14014
15928
  for (const file of files) {
14015
- found.push(relative4(cwd, file));
15929
+ found.push(relative5(cwd, file));
14016
15930
  }
14017
15931
  }
14018
15932
  }
@@ -14020,8 +15934,8 @@ async function detectProjectOwnedFiles(cwd) {
14020
15934
  }
14021
15935
  async function isReleaseWorkflowEnabled(cwd) {
14022
15936
  try {
14023
- const configPath = resolve4(cwd, ".pourkit", "config.json");
14024
- const content = await readFile6(configPath, "utf-8");
15937
+ const configPath = resolve5(cwd, ".pourkit", "config.json");
15938
+ const content = await readFile7(configPath, "utf-8");
14025
15939
  const parsed = JSON.parse(content);
14026
15940
  return parsed.releaseWorkflow?.enabled === true;
14027
15941
  } catch {
@@ -14150,7 +16064,7 @@ async function runWorkflowPackSyncCommand(options) {
14150
16064
  const schemaResult = await runConfigSyncSchemaCommand({ cwd });
14151
16065
  let configMemory;
14152
16066
  try {
14153
- const configPath = resolve4(cwd, ".pourkit", "config.json");
16067
+ const configPath = resolve5(cwd, ".pourkit", "config.json");
14154
16068
  const config = await loadConfig(configPath);
14155
16069
  if (config.memory?.enabled === true && config.memory?.provider === "icm") {
14156
16070
  configMemory = config.memory;
@@ -14158,34 +16072,19 @@ async function runWorkflowPackSyncCommand(options) {
14158
16072
  } catch {
14159
16073
  }
14160
16074
  const managedBlockContent = generateManagedBlockContent(configMemory);
14161
- const fullManagedBlock = `${MANAGED_BLOCK_BEGIN3}${managedBlockContent}${MANAGED_BLOCK_END2}`;
16075
+ const fullManagedBlock = buildManagedBlock(managedBlockContent);
14162
16076
  async function syncManagedAgentFile(fileName, options2) {
14163
- const filePath = resolve4(cwd, fileName);
16077
+ const filePath = resolve5(cwd, fileName);
14164
16078
  if (!existsSync19(filePath)) {
14165
16079
  if (!options2.createIfMissing) return false;
14166
- await writeFile3(filePath, fullManagedBlock + "\n", "utf-8");
16080
+ await writeFile4(filePath, fullManagedBlock, "utf-8");
14167
16081
  return true;
14168
16082
  }
14169
- const existing = await readFile6(filePath, "utf-8");
14170
- const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN3);
14171
- const endIdx = existing.indexOf(MANAGED_BLOCK_END2);
14172
- if (beginIdx !== -1 && endIdx !== -1) {
14173
- const beforeBlock = existing.slice(0, beginIdx);
14174
- const afterBlock = existing.slice(endIdx + MANAGED_BLOCK_END2.length);
14175
- const newContent = `${beforeBlock}${fullManagedBlock}${afterBlock}`;
14176
- if (newContent !== existing) {
14177
- await writeFile3(filePath, newContent, "utf-8");
14178
- return true;
14179
- }
14180
- } else {
14181
- const newContent = `${existing}
14182
-
14183
- ${fullManagedBlock}
14184
- `;
14185
- if (newContent !== existing) {
14186
- await writeFile3(filePath, newContent, "utf-8");
14187
- return true;
14188
- }
16083
+ const existing = await readFile7(filePath, "utf-8");
16084
+ const newContent = upsertManagedBlock(existing, managedBlockContent);
16085
+ if (newContent !== existing) {
16086
+ await writeFile4(filePath, newContent, "utf-8");
16087
+ return true;
14189
16088
  }
14190
16089
  return false;
14191
16090
  }
@@ -14197,11 +16096,11 @@ ${fullManagedBlock}
14197
16096
  }
14198
16097
  const enabledAddons = releaseEnabled ? ["release"] : [];
14199
16098
  const manifestMeta = buildWorkflowPackMetadata(enabledAddons);
14200
- const manifestPath = resolve4(cwd, ".pourkit", "manifest.json");
16099
+ const manifestPath = resolve5(cwd, ".pourkit", "manifest.json");
14201
16100
  let manifestWritten = false;
14202
16101
  let previousManifestWorkflowPack = void 0;
14203
16102
  try {
14204
- const existingRaw = await readFile6(manifestPath, "utf-8").catch(() => null);
16103
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(() => null);
14205
16104
  if (existingRaw) {
14206
16105
  const existing = JSON.parse(existingRaw);
14207
16106
  previousManifestWorkflowPack = existing.workflowPack;
@@ -14210,13 +16109,13 @@ ${fullManagedBlock}
14210
16109
  }
14211
16110
  if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
14212
16111
  try {
14213
- const existingRaw = await readFile6(manifestPath, "utf-8").catch(
16112
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(
14214
16113
  () => null
14215
16114
  );
14216
16115
  let manifest = existingRaw ? JSON.parse(existingRaw) : {};
14217
16116
  manifest.workflowPack = manifestMeta;
14218
- await mkdir6(resolve4(manifestPath, ".."), { recursive: true });
14219
- await writeFile3(
16117
+ await mkdir7(resolve5(manifestPath, ".."), { recursive: true });
16118
+ await writeFile4(
14220
16119
  manifestPath,
14221
16120
  JSON.stringify(manifest, null, 2) + "\n",
14222
16121
  "utf-8"
@@ -14265,7 +16164,7 @@ async function syncSkill(skillName, catalog, acc, ctx) {
14265
16164
  return;
14266
16165
  }
14267
16166
  for (const sourceFile of skillFiles) {
14268
- const relPath = relative4(skillDir, sourceFile);
16167
+ const relPath = relative5(skillDir, sourceFile);
14269
16168
  const targetRelativePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
14270
16169
  if (isProjectOwnedPath(targetRelativePath)) {
14271
16170
  skippedProjectOwned.push(targetRelativePath);
@@ -14341,7 +16240,7 @@ async function syncAddonSkill(skillName, acc, ctx) {
14341
16240
  return;
14342
16241
  }
14343
16242
  for (const sourceFile of skillFiles) {
14344
- const relPath = relative4(addonSkillDir, sourceFile);
16243
+ const relPath = relative5(addonSkillDir, sourceFile);
14345
16244
  const targetRelativePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
14346
16245
  if (isProjectOwnedPath(targetRelativePath)) {
14347
16246
  skippedProjectOwned.push(targetRelativePath);
@@ -14422,6 +16321,16 @@ var GitHubIssueProvider = class {
14422
16321
  });
14423
16322
  return { number: data.number, url: data.html_url, title: data.title };
14424
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
+ }
14425
16334
  async fetchIssue(number) {
14426
16335
  const { data } = await this.client.octokit.rest.issues.get({
14427
16336
  owner: this.client.owner,
@@ -14944,21 +16853,21 @@ init_common();
14944
16853
 
14945
16854
  // execution/sandcastle-execution.ts
14946
16855
  import { existsSync as existsSync21, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
14947
- import { join as join20 } from "path";
16856
+ import { join as join23 } from "path";
14948
16857
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
14949
16858
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
14950
16859
 
14951
16860
  // execution/execution-provider.ts
14952
16861
  init_common();
14953
16862
  import { mkdtempSync } from "fs";
14954
- import { writeFile as writeFile4 } from "fs/promises";
16863
+ import { writeFile as writeFile5 } from "fs/promises";
14955
16864
  import { tmpdir } from "os";
14956
- import { dirname as dirname7, join as join18 } from "path";
16865
+ import { dirname as dirname8, join as join21 } from "path";
14957
16866
  async function writeExecutionArtifacts(worktreePath, artifacts) {
14958
16867
  for (const artifact of artifacts) {
14959
- const filePath = join18(worktreePath, artifact.path);
14960
- await ensureDir(dirname7(filePath));
14961
- 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");
14962
16871
  }
14963
16872
  }
14964
16873
 
@@ -15018,7 +16927,7 @@ async function createSandboxFromExistingWorktree(options) {
15018
16927
  }
15019
16928
 
15020
16929
  // execution/sandbox-options.ts
15021
- import { join as join19 } from "path";
16930
+ import { join as join22 } from "path";
15022
16931
  var POURKIT_ICM_CONTAINER_DIR = "/home/agent/.pourkit/icm";
15023
16932
  function buildSandboxOptions(repoRoot2, sandbox, memory) {
15024
16933
  const mounts = [];
@@ -15027,12 +16936,12 @@ function buildSandboxOptions(repoRoot2, sandbox, memory) {
15027
16936
  }
15028
16937
  if (memory?.available) {
15029
16938
  mounts.push({
15030
- hostPath: join19(repoRoot2, ".pourkit", "icm"),
16939
+ hostPath: join22(repoRoot2, ".pourkit", "icm"),
15031
16940
  sandboxPath: POURKIT_ICM_CONTAINER_DIR,
15032
16941
  readonly: false
15033
16942
  });
15034
16943
  mounts.push({
15035
- hostPath: join19(repoRoot2, ".pourkit", "icm", "cache"),
16944
+ hostPath: join22(repoRoot2, ".pourkit", "icm", "cache"),
15036
16945
  sandboxPath: "/home/agent/.cache/icm",
15037
16946
  readonly: false
15038
16947
  });
@@ -15166,13 +17075,13 @@ var SandcastleExecutionSession = class {
15166
17075
  "Memory enabled but memory context is invalid. Run `pourkit memory init` or install ICM for host planning use."
15167
17076
  );
15168
17077
  }
15169
- const hostMemoryDir = join20(repoRoot2, ".pourkit", "icm");
17078
+ const hostMemoryDir = join23(repoRoot2, ".pourkit", "icm");
15170
17079
  if (!existsSync21(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
15171
17080
  throw new MissingHostMemoryDirError(
15172
17081
  "Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
15173
17082
  );
15174
17083
  }
15175
- mkdirSync9(join20(hostMemoryDir, "cache"), { recursive: true });
17084
+ mkdirSync9(join23(hostMemoryDir, "cache"), { recursive: true });
15176
17085
  }
15177
17086
  const sandboxOptions = buildSandboxOptions(
15178
17087
  repoRoot2,
@@ -15331,7 +17240,7 @@ function sanitizeBranch(branchName) {
15331
17240
  }
15332
17241
  function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
15333
17242
  return paths.filter((relativePath) => {
15334
- const source = join20(repoRoot2, relativePath);
17243
+ const source = join23(repoRoot2, relativePath);
15335
17244
  if (!existsSync21(source)) {
15336
17245
  return true;
15337
17246
  }
@@ -15342,7 +17251,7 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
15342
17251
  } catch {
15343
17252
  return true;
15344
17253
  }
15345
- const destination = join20(worktreePath, relativePath);
17254
+ const destination = join23(worktreePath, relativePath);
15346
17255
  return !existsSync21(destination);
15347
17256
  });
15348
17257
  }
@@ -15407,17 +17316,17 @@ function isPlainObject(value) {
15407
17316
  return typeof value === "object" && value !== null && !Array.isArray(value);
15408
17317
  }
15409
17318
  function savePromptToFile(repoRoot2, stage, iteration, prompt) {
15410
- const promptsDir = join20(repoRoot2, ".pourkit", ".tmp", "prompts");
17319
+ const promptsDir = join23(repoRoot2, ".pourkit", ".tmp", "prompts");
15411
17320
  mkdirSync9(promptsDir, { recursive: true });
15412
17321
  const timestamp2 = Date.now();
15413
17322
  const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
15414
17323
  const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
15415
- const filePath = join20(promptsDir, filename);
17324
+ const filePath = join23(promptsDir, filename);
15416
17325
  writeFileSync6(filePath, prompt, "utf-8");
15417
17326
  }
15418
17327
 
15419
17328
  // cli.ts
15420
- function normalizePrdRef(ref) {
17329
+ function normalizePrdRef2(ref) {
15421
17330
  const normalized = ref.trim().toUpperCase();
15422
17331
  if (!/^PRD-\d+$/.test(normalized)) {
15423
17332
  throw new Error(
@@ -15482,6 +17391,7 @@ function createCliProgram(version) {
15482
17391
  const program = new Command();
15483
17392
  program.name("pourkit").version(version).exitOverride().description("AI-driven issue-to-PR workflow for GitHub repositories.");
15484
17393
  const issueCommand = program.command("issue");
17394
+ const prdPlanWorkspaceCommand = program.command("prd-plan-workspace").description("PRD Plan Workspace commands");
15485
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) => {
15486
17396
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15487
17397
  const logPath = path8.join(
@@ -15513,7 +17423,7 @@ function createCliProgram(version) {
15513
17423
  });
15514
17424
  program.command("validate-artifact").description("Validate an agent handoff artifact").argument(
15515
17425
  "<kind>",
15516
- "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"
15517
17427
  ).argument("<artifactPath>", "artifact path to validate").argument("[extraPaths...]", "unused compatibility argument").option("--iteration <number>", "review/refactor iteration", (value) => {
15518
17428
  const parsed = Number.parseInt(value, 10);
15519
17429
  if (!Number.isInteger(parsed) || parsed < 1) {
@@ -15566,7 +17476,13 @@ function createCliProgram(version) {
15566
17476
  ).option(
15567
17477
  "--base-ref <ref>",
15568
17478
  "canonical base ref for issue-final-review changedFiles coverage validation"
15569
- ).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(
15570
17486
  (kind, artifactPath, _extraPaths, options) => {
15571
17487
  const allowedKinds = /* @__PURE__ */ new Set([
15572
17488
  "reviewer",
@@ -15574,7 +17490,10 @@ function createCliProgram(version) {
15574
17490
  "finalizer",
15575
17491
  "conflict-resolution",
15576
17492
  "failure-resolution",
15577
- "issue-final-review"
17493
+ "issue-final-review",
17494
+ "prd-plan",
17495
+ "prd-child-issue",
17496
+ "prd-plan-workspace"
15578
17497
  ]);
15579
17498
  if (!allowedKinds.has(kind)) {
15580
17499
  throw new CommanderError(
@@ -15596,7 +17515,10 @@ function createCliProgram(version) {
15596
17515
  issueNumber: options.issueNumber,
15597
17516
  branchName: options.branchName,
15598
17517
  baseRef: options.baseRef,
15599
- checkConflictMarkers: options.checkConflictMarkers
17518
+ checkConflictMarkers: options.checkConflictMarkers,
17519
+ prdRef: options.prdRef,
17520
+ workspace: options.workspace,
17521
+ childId: options.childId
15600
17522
  });
15601
17523
  console.log(JSON.stringify(result, null, 2));
15602
17524
  if (!result.ok) {
@@ -15604,6 +17526,40 @@ function createCliProgram(version) {
15604
17526
  }
15605
17527
  }
15606
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
+ );
15607
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(
15608
17564
  "--label <label>",
15609
17565
  "label to apply (repeatable)",
@@ -15743,7 +17699,7 @@ function createCliProgram(version) {
15743
17699
  );
15744
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(
15745
17701
  async (options) => {
15746
- const prdRef = options.prd ? normalizePrdRef(options.prd) : void 0;
17702
+ const prdRef = options.prd ? normalizePrdRef2(options.prd) : void 0;
15747
17703
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15748
17704
  const config = await loadRepoConfig(targetRepoRoot);
15749
17705
  const logPath = path8.join(
@@ -15811,7 +17767,7 @@ function createCliProgram(version) {
15811
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(
15812
17768
  async (prdRef, options) => {
15813
17769
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15814
- const normalizedPrdRef = normalizePrdRef(prdRef);
17770
+ const normalizedPrdRef = normalizePrdRef2(prdRef);
15815
17771
  const logPath = path8.join(
15816
17772
  targetRepoRoot,
15817
17773
  ".pourkit",
@@ -15857,7 +17813,7 @@ function createCliProgram(version) {
15857
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(
15858
17814
  async (prdRef, options) => {
15859
17815
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15860
- const normalizedPrdRef = normalizePrdRef(prdRef);
17816
+ const normalizedPrdRef = normalizePrdRef2(prdRef);
15861
17817
  const logPath = path8.join(
15862
17818
  targetRepoRoot,
15863
17819
  ".pourkit",
@@ -16133,11 +18089,11 @@ function createCliProgram(version) {
16133
18089
  return program;
16134
18090
  }
16135
18091
  async function resolveCliVersion() {
16136
- if (isPackageVersion("0.0.0-next-20260625073725")) {
16137
- return "0.0.0-next-20260625073725";
18092
+ if (isPackageVersion("0.0.0-next-20260627024509")) {
18093
+ return "0.0.0-next-20260627024509";
16138
18094
  }
16139
- if (isReleaseVersion("0.0.0-next-20260625073725")) {
16140
- return "0.0.0-next-20260625073725";
18095
+ if (isReleaseVersion("0.0.0-next-20260627024509")) {
18096
+ return "0.0.0-next-20260627024509";
16141
18097
  }
16142
18098
  try {
16143
18099
  const root = repoRoot();