@pourkit/cli 0.0.0-next-20260715085442 → 0.0.0-next-20260723092204

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((resolve7) => {
61
+ await new Promise((resolve8) => {
62
62
  if (!fileStream) {
63
- resolve7();
63
+ resolve8();
64
64
  return;
65
65
  }
66
66
  const timer = setTimeout(() => {
67
67
  if (!fileStream.destroyed) {
68
68
  fileStream.destroy();
69
69
  }
70
- resolve7();
70
+ resolve8();
71
71
  }, 2e3);
72
72
  fileStream.end(() => {
73
73
  clearTimeout(timer);
74
- resolve7();
74
+ resolve8();
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((resolve7) => setTimeout(resolve7, ms));
269
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
270
270
  }
271
271
  async function execCaptureWithRetry(command, args, options = {}) {
272
272
  const retries = options.retries ?? 3;
@@ -378,18 +378,6 @@ function applyStageAgentDefaults(agent) {
378
378
  outputRetries: applyOutputRetriesDefaults(agent.outputRetries)
379
379
  };
380
380
  }
381
- var DEFAULT_POST_COMPLETION_FINAL_REVIEW_PROMPT = "post-completion-final-review.prompt.md";
382
- function applyPostCompletionFinalReviewDefaults(agent, issueFinalReview) {
383
- if (agent) return applyStageAgentDefaults(agent);
384
- return applyStageAgentDefaults({
385
- agent: issueFinalReview.agent,
386
- model: issueFinalReview.model,
387
- variant: issueFinalReview.variant,
388
- env: issueFinalReview.env,
389
- promptTemplate: DEFAULT_POST_COMPLETION_FINAL_REVIEW_PROMPT,
390
- outputRetries: issueFinalReview.outputRetries
391
- });
392
- }
393
381
  function assertRepoRelativePath(value, location) {
394
382
  const normalized = normalize(value);
395
383
  if (isAbsolute(value) || isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${sep}`)) {
@@ -526,9 +514,8 @@ function normalizePourkitConfig(input) {
526
514
  ),
527
515
  maxAttempts: t.strategy.issueFinalReview.maxAttempts
528
516
  },
529
- postCompletionFinalReview: applyPostCompletionFinalReviewDefaults(
530
- t.strategy.postCompletionFinalReview,
531
- t.strategy.issueFinalReview
517
+ postCompletionFinalReview: applyStageAgentDefaults(
518
+ t.strategy.postCompletionFinalReview
532
519
  ),
533
520
  finalize: {
534
521
  prDescriptionAgent: applyStageAgentDefaults(
@@ -636,6 +623,16 @@ function getValidator() {
636
623
  }
637
624
  return _validate;
638
625
  }
626
+ function getPackagedConfigSchemaVersion() {
627
+ const schema = _schema ?? JSON.parse(readFileSync(SCHEMA_PATH, "utf-8"));
628
+ _schema = schema;
629
+ const props = schema.properties;
630
+ const schemaVersion = props?.schemaVersion?.const;
631
+ if (typeof schemaVersion !== "number" || !Number.isInteger(schemaVersion)) {
632
+ throw new Error("Packaged Pourkit config schema is missing schemaVersion");
633
+ }
634
+ return schemaVersion;
635
+ }
639
636
  function resolveMissingOrEmptyOutputRetries(config) {
640
637
  return config?.outputRetries?.missingOrEmpty ?? DEFAULT_MISSING_OR_EMPTY_OUTPUT_RETRIES;
641
638
  }
@@ -905,11 +902,11 @@ var OBSOLETE_CONFIG_PATHS = [
905
902
  ];
906
903
  var CANONICAL_CONFIG_PATH = ".pourkit/config.json";
907
904
  async function loadRepoConfig(repoRoot2, _configFileName) {
908
- const { existsSync: existsSync25 } = await import("fs");
905
+ const { existsSync: existsSync26 } = await import("fs");
909
906
  const { join: pjoin } = await import("path");
910
907
  for (const obPath of OBSOLETE_CONFIG_PATHS) {
911
908
  const fullPath = pjoin(repoRoot2, obPath);
912
- if (existsSync25(fullPath)) {
909
+ if (existsSync26(fullPath)) {
913
910
  const isRootJson = obPath === "pourkit.json";
914
911
  throw new Error(
915
912
  isRootJson ? `Found root ${obPath}, but Pourkit config now lives at ${CANONICAL_CONFIG_PATH}. Move the file and update "$schema" to "./schema/pourkit.schema.json".` : `Found ${obPath}, but executable config is no longer supported. Move configuration to ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
@@ -917,13 +914,13 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
917
914
  }
918
915
  }
919
916
  const configPath = pjoin(repoRoot2, CANONICAL_CONFIG_PATH);
920
- if (!existsSync25(configPath)) {
917
+ if (!existsSync26(configPath)) {
921
918
  throw new Error(
922
919
  `No Pourkit config found at ${CANONICAL_CONFIG_PATH}. Run pourkit init or create ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
923
920
  );
924
921
  }
925
- const { readFile: readFile9 } = await import("fs/promises");
926
- const raw = await readFile9(configPath, "utf-8");
922
+ const { readFile: readFile10 } = await import("fs/promises");
923
+ const raw = await readFile10(configPath, "utf-8");
927
924
  let parsed;
928
925
  try {
929
926
  parsed = JSON.parse(raw);
@@ -934,10 +931,10 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
934
931
  return parseConfig(parsed);
935
932
  }
936
933
  async function loadConfig(configPath) {
937
- const { readFile: readFile9 } = await import("fs/promises");
934
+ const { readFile: readFile10 } = await import("fs/promises");
938
935
  const ext = configPath.split(".").pop()?.toLowerCase();
939
936
  if (ext === "json") {
940
- const raw = await readFile9(configPath, "utf-8");
937
+ const raw = await readFile10(configPath, "utf-8");
941
938
  return parseConfig(JSON.parse(raw));
942
939
  }
943
940
  if (ext === "mjs" || ext === "js" || ext === "ts") {
@@ -4281,19 +4278,12 @@ var POST_COMPLETION_FINAL_REVIEW_ARTIFACT_PATH = join9(
4281
4278
  "post-completion-final-review",
4282
4279
  "agent-output.json"
4283
4280
  );
4284
- var DEFAULT_POST_COMPLETION_FINAL_REVIEW_PROMPT2 = "post-completion-final-review.prompt.md";
4285
4281
  function resolvePostCompletionFinalReviewAgent(target) {
4286
4282
  const configured = target.strategy.postCompletionFinalReview;
4287
4283
  if (configured) return configured;
4288
- const fallback = target.strategy.issueFinalReview;
4289
- return {
4290
- agent: fallback.agent,
4291
- model: fallback.model,
4292
- variant: fallback.variant,
4293
- env: fallback.env,
4294
- outputRetries: fallback.outputRetries,
4295
- promptTemplate: DEFAULT_POST_COMPLETION_FINAL_REVIEW_PROMPT2
4296
- };
4284
+ throw new Error(
4285
+ "Post-Completion Final Review requires targets[].strategy.postCompletionFinalReview config."
4286
+ );
4297
4287
  }
4298
4288
  async function runPostCompletionFinalReview(options) {
4299
4289
  const {
@@ -6618,7 +6608,7 @@ async function canReachMcp(url) {
6618
6608
  return true;
6619
6609
  } catch {
6620
6610
  if (attempt < 9) {
6621
- await new Promise((resolve7) => setTimeout(resolve7, 100));
6611
+ await new Promise((resolve8) => setTimeout(resolve8, 100));
6622
6612
  }
6623
6613
  }
6624
6614
  }
@@ -8659,7 +8649,10 @@ async function runIssueLifecycle(options) {
8659
8649
  startResult,
8660
8650
  reviewArtifactPath
8661
8651
  });
8662
- if (result.publicationStatus === "merged" && !startResult.parentPrdIssue) {
8652
+ const isPrdChildIssue = Boolean(
8653
+ startResult.parentPrdIssue || options.runOptions.beadsIssueRunContext
8654
+ );
8655
+ if (result.publicationStatus === "merged" && !isPrdChildIssue) {
8663
8656
  await options.deps.runOneshotPostCompletionFinalReview({
8664
8657
  issueNumber: options.issueNumber,
8665
8658
  repoRoot: options.repoRoot,
@@ -10598,7 +10591,7 @@ async function resolveGithubProjectionState(options) {
10598
10591
  expectedKind: "parent",
10599
10592
  prdRef: options.workspace.prdRef,
10600
10593
  githubIssueNumber: parentGithubIssueNumber,
10601
- allowMissingCanonicalMatch: parentGithubIssueNumber === void 0 && options.currentState.parent === null,
10594
+ allowMissingCanonicalMatch: parentGithubIssueNumber === void 0,
10602
10595
  affected: [
10603
10596
  {
10604
10597
  kind: "prd",
@@ -10620,7 +10613,7 @@ async function resolveGithubProjectionState(options) {
10620
10613
  expectedKind: "child",
10621
10614
  prdRef: options.workspace.prdRef,
10622
10615
  githubIssueNumber,
10623
- allowMissingCanonicalMatch: action?.type === "create_child" && githubIssueNumber === void 0,
10616
+ allowMissingCanonicalMatch: (action?.type === "create_child" || action?.type === "update_child") && githubIssueNumber === void 0,
10624
10617
  affected: [
10625
10618
  {
10626
10619
  kind: "child",
@@ -10845,7 +10838,7 @@ async function executeGithubProjectionActions(options) {
10845
10838
  child.childRef
10846
10839
  ),
10847
10840
  githubIssueNumber: action?.type === "update_child" ? action.githubIssueNumber : void 0,
10848
- allowMissingCanonicalMatch: action?.type !== "update_child"
10841
+ allowMissingCanonicalMatch: action?.type !== "update_child" || action.githubIssueNumber === void 0
10849
10842
  });
10850
10843
  children.push({ childRef: child.childRef, githubIssue });
10851
10844
  }
@@ -12396,15 +12389,19 @@ function inspectRemotePrdBranch(repoRoot2, prdRef) {
12396
12389
  return { ok: true, exists: true, headSha };
12397
12390
  }
12398
12391
  function fetchPrdBranch(repoRoot2, prdRef) {
12399
- const result = spawnSync2("git", ["fetch", "origin", prdRef], {
12400
- cwd: repoRoot2,
12401
- encoding: "utf8"
12402
- });
12392
+ const result = spawnSync2(
12393
+ "git",
12394
+ ["fetch", "origin", `${prdRef}:refs/remotes/origin/${prdRef}`],
12395
+ {
12396
+ cwd: repoRoot2,
12397
+ encoding: "utf8"
12398
+ }
12399
+ );
12403
12400
  if (result.status !== 0) {
12404
12401
  return {
12405
12402
  ok: false,
12406
12403
  gate: "git",
12407
- reason: `PRD Run start failed while fetching origin/${prdRef}.`,
12404
+ reason: `PRD Run failed while fetching origin/${prdRef}.`,
12408
12405
  diagnostics: [
12409
12406
  result.error instanceof Error ? result.error.message : void 0,
12410
12407
  result.stderr?.toString?.() ?? String(result.stderr ?? ""),
@@ -12415,6 +12412,65 @@ function fetchPrdBranch(repoRoot2, prdRef) {
12415
12412
  }
12416
12413
  return { ok: true };
12417
12414
  }
12415
+ function postCompletionRetouchBranch(prdRef) {
12416
+ return `pourkit/post-completion/${prdRef}`;
12417
+ }
12418
+ function postCompletionRetouchWorktreePath(repoRoot2, retouchBranch) {
12419
+ return join23(
12420
+ repoRoot2,
12421
+ ".sandcastle",
12422
+ "worktrees",
12423
+ retouchBranch.replace(/\//g, "-")
12424
+ );
12425
+ }
12426
+ async function ensurePostCompletionRetouchWorktree(options) {
12427
+ const { repoRoot: repoRoot2, retouchBranch, baseRef, logger } = options;
12428
+ const gitLogger = typeof logger.step === "function" ? logger : void 0;
12429
+ const worktreeList = await execCapture(
12430
+ "git",
12431
+ ["worktree", "list", "--porcelain"],
12432
+ {
12433
+ cwd: repoRoot2,
12434
+ logger: gitLogger,
12435
+ label: "git worktree list"
12436
+ }
12437
+ );
12438
+ const registeredWorktreePath = parseWorktreeListPorcelain(
12439
+ worktreeList.stdout,
12440
+ retouchBranch
12441
+ );
12442
+ if (registeredWorktreePath) {
12443
+ return { worktreePath: registeredWorktreePath };
12444
+ }
12445
+ const worktreePath = postCompletionRetouchWorktreePath(
12446
+ repoRoot2,
12447
+ retouchBranch
12448
+ );
12449
+ let branchExists = false;
12450
+ try {
12451
+ await execCapture(
12452
+ "git",
12453
+ ["show-ref", "--verify", "--quiet", `refs/heads/${retouchBranch}`],
12454
+ {
12455
+ cwd: repoRoot2,
12456
+ logger: gitLogger,
12457
+ label: "git branch exists"
12458
+ }
12459
+ );
12460
+ branchExists = true;
12461
+ } catch {
12462
+ }
12463
+ await execCapture(
12464
+ "git",
12465
+ branchExists ? ["worktree", "add", worktreePath, retouchBranch] : ["worktree", "add", "-b", retouchBranch, worktreePath, baseRef],
12466
+ {
12467
+ cwd: repoRoot2,
12468
+ logger: gitLogger,
12469
+ label: "git worktree add post-completion retouch"
12470
+ }
12471
+ );
12472
+ return { worktreePath };
12473
+ }
12418
12474
  async function ensurePrdBranchPublished(repoRoot2, prdRef, startBaseCommit) {
12419
12475
  const pushResult = spawnSync2(
12420
12476
  "git",
@@ -13049,7 +13105,27 @@ async function prdParentIssueIsClosed(options) {
13049
13105
  }
13050
13106
  }
13051
13107
  async function reconcileBlockedBeadsIssueProjection(options) {
13052
- const { issue, issueProvider, config, prdRef, logger } = options;
13108
+ const { issue, issueProvider, config, prdRef, logger, retryRepair } = options;
13109
+ if (shouldRestoreRetryableBeadsProjection({ issue, config, retryRepair })) {
13110
+ logCoordinatorStep(
13111
+ logger,
13112
+ "info",
13113
+ `Restoring retryable GitHub projection for Beads Issue #${issue.number}.`
13114
+ );
13115
+ for (const label of [
13116
+ config.labels.blocked,
13117
+ config.labels.agentInProgress,
13118
+ config.labels.readyForHuman
13119
+ ]) {
13120
+ if (!issue.labels.includes(label)) continue;
13121
+ try {
13122
+ await issueProvider.removeLabel(issue.number, label);
13123
+ } catch {
13124
+ }
13125
+ }
13126
+ await issueProvider.addLabels(issue.number, [config.labels.readyForAgent]);
13127
+ return await issueProvider.fetchIssue(issue.number);
13128
+ }
13053
13129
  if (!issue.labels.includes(config.labels.blocked)) {
13054
13130
  return issue;
13055
13131
  }
@@ -13109,6 +13185,16 @@ async function reconcileBlockedBeadsIssueProjection(options) {
13109
13185
  }
13110
13186
  return refreshed;
13111
13187
  }
13188
+ function shouldRestoreRetryableBeadsProjection(options) {
13189
+ const { issue, config, retryRepair } = options;
13190
+ if (!retryRepair) return false;
13191
+ if (issue.state !== "open") return false;
13192
+ const guidance = retryRepair.guidance;
13193
+ if (guidance.repairability !== "automatic-retry-safe") return false;
13194
+ if (guidance.blockedGate !== "queue") return false;
13195
+ if (issue.labels.includes(config.labels.blocked)) return false;
13196
+ return !issue.labels.includes(config.labels.readyForAgent) || issue.labels.includes(config.labels.agentInProgress) || issue.labels.includes(config.labels.readyForHuman);
13197
+ }
13112
13198
  async function releaseSkippedBlockedBeadsChildren(options) {
13113
13199
  for (const child of options.children) {
13114
13200
  let result;
@@ -13135,6 +13221,19 @@ async function releaseSkippedBlockedBeadsChildren(options) {
13135
13221
  }
13136
13222
  }
13137
13223
  }
13224
+ async function closeBeadsChildForClosedIssueProjection(options) {
13225
+ logCoordinatorStep(
13226
+ options.logger,
13227
+ "info",
13228
+ `Closing Beads child ${options.beadsChildId} because GitHub Issue #${options.issue.number} is already closed.`
13229
+ );
13230
+ await closeBeadsPrdChild({
13231
+ repoRoot: options.repoRoot,
13232
+ childId: options.beadsChildId,
13233
+ reason: `Issue #${options.issue.number} already closed`,
13234
+ logger: options.logger
13235
+ });
13236
+ }
13138
13237
  function extractIssueFromQueueError(error) {
13139
13238
  if (!error || typeof error !== "object" || !("issue" in error)) {
13140
13239
  return void 0;
@@ -13452,6 +13551,70 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13452
13551
  options.config,
13453
13552
  record.targetName ?? options.targetName
13454
13553
  );
13554
+ const remotePrdBranch = inspectRemotePrdBranch(options.repoRoot, prdBranch);
13555
+ if (!remotePrdBranch.ok) {
13556
+ return buildLaunchBlockedOutcome(
13557
+ options.repoRoot,
13558
+ prdRef,
13559
+ buildPrdRunRepairGuidance({
13560
+ blockedGate: remotePrdBranch.gate,
13561
+ blockedStage: "post-completion-final-review",
13562
+ failureCode: "post-completion-branch-fetch-failed",
13563
+ humanReadableReason: remotePrdBranch.reason,
13564
+ repairability: "operator-action",
13565
+ nextAction: "inspect-and-retry",
13566
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
13567
+ diagnostics: [...remotePrdBranch.diagnostics],
13568
+ offendingPaths: [...remotePrdBranch.offendingPaths]
13569
+ }),
13570
+ resumeFacts,
13571
+ startOutcome,
13572
+ record.targetName,
13573
+ record.start
13574
+ );
13575
+ }
13576
+ if (!remotePrdBranch.exists) {
13577
+ return buildLaunchBlockedOutcome(
13578
+ options.repoRoot,
13579
+ prdRef,
13580
+ buildPrdRunRepairGuidance({
13581
+ blockedGate: "git",
13582
+ blockedStage: "post-completion-final-review",
13583
+ failureCode: "post-completion-branch-missing",
13584
+ humanReadableReason: `Post-Completion Final Review for ${prdRef} requires origin/${prdBranch}, but the remote PRD branch does not exist.`,
13585
+ repairability: "operator-action",
13586
+ nextAction: "inspect-and-retry",
13587
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
13588
+ diagnostics: [`Missing remote PRD branch: origin/${prdBranch}`]
13589
+ }),
13590
+ resumeFacts,
13591
+ startOutcome,
13592
+ record.targetName,
13593
+ record.start
13594
+ );
13595
+ }
13596
+ const fetchPrdResult = fetchPrdBranch(options.repoRoot, prdBranch);
13597
+ if (!fetchPrdResult.ok) {
13598
+ return buildLaunchBlockedOutcome(
13599
+ options.repoRoot,
13600
+ prdRef,
13601
+ buildPrdRunRepairGuidance({
13602
+ blockedGate: fetchPrdResult.gate,
13603
+ blockedStage: "post-completion-final-review",
13604
+ failureCode: "post-completion-branch-fetch-failed",
13605
+ humanReadableReason: fetchPrdResult.reason,
13606
+ repairability: "operator-action",
13607
+ nextAction: "inspect-and-retry",
13608
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
13609
+ diagnostics: [...fetchPrdResult.diagnostics],
13610
+ offendingPaths: [...fetchPrdResult.offendingPaths]
13611
+ }),
13612
+ resumeFacts,
13613
+ startOutcome,
13614
+ record.targetName,
13615
+ record.start
13616
+ );
13617
+ }
13455
13618
  const sourceDocResult = await resolvePrdPostCompletionSourceDocument({
13456
13619
  repoRoot: options.repoRoot,
13457
13620
  prdRef
@@ -13499,7 +13662,36 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13499
13662
  record.start
13500
13663
  );
13501
13664
  }
13502
- const worktreePath = options.repoRoot;
13665
+ const retouchBranch = postCompletionRetouchBranch(prdRef);
13666
+ let worktreePath;
13667
+ try {
13668
+ worktreePath = (await ensurePostCompletionRetouchWorktree({
13669
+ repoRoot: options.repoRoot,
13670
+ retouchBranch,
13671
+ baseRef: `origin/${prdBranch}`,
13672
+ logger: options.logger
13673
+ })).worktreePath;
13674
+ } catch (error) {
13675
+ const msg = error instanceof Error ? error.message : String(error);
13676
+ return buildLaunchBlockedOutcome(
13677
+ options.repoRoot,
13678
+ prdRef,
13679
+ buildPrdRunRepairGuidance({
13680
+ blockedGate: "git",
13681
+ blockedStage: "post-completion-final-review",
13682
+ failureCode: "post-completion-worktree-failed",
13683
+ humanReadableReason: `Post-Completion Final Review for ${prdRef} could not prepare a retouch worktree: ${msg}`,
13684
+ repairability: "operator-action",
13685
+ nextAction: "inspect-and-retry",
13686
+ nextRecommendedCommand: `pourkit prd-run status ${prdRef}`,
13687
+ diagnostics: [msg]
13688
+ }),
13689
+ resumeFacts,
13690
+ startOutcome,
13691
+ record.targetName,
13692
+ record.start
13693
+ );
13694
+ }
13503
13695
  const sourceDocumentPath = sourceDocResult.relativePath;
13504
13696
  stateAdapter.write({
13505
13697
  status: "in_progress",
@@ -13595,7 +13787,6 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13595
13787
  resume: resumeFacts
13596
13788
  };
13597
13789
  }
13598
- const retouchBranch = `pourkit/post-completion/${prdRef}`;
13599
13790
  stateAdapter.write({ retouchBranch });
13600
13791
  const prDescriptionResult = await runEffectAndMapExit(
13601
13792
  runPostCompletionPrDescriptionAgent({
@@ -14679,7 +14870,13 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14679
14870
  issueProvider,
14680
14871
  config: options.config,
14681
14872
  prdRef,
14682
- logger
14873
+ logger,
14874
+ ...resumeBeadsContext ? {
14875
+ retryRepair: {
14876
+ guidance: originalGuidance,
14877
+ beadsChildId: resumeBeadsContext.childId
14878
+ }
14879
+ } : {}
14683
14880
  });
14684
14881
  if (fetchedIssue.labels.includes(options.config.labels.blocked)) {
14685
14882
  logCoordinatorStep(
@@ -14829,8 +15026,24 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14829
15026
  );
14830
15027
  }
14831
15028
  try {
14832
- const issue = await reconcileBlockedBeadsIssueProjection({
14833
- issue: contextResult.issue,
15029
+ let issue = contextResult.issue;
15030
+ if (issue.state === "closed") {
15031
+ beadsClaimAttempted = true;
15032
+ await closeBeadsChildForClosedIssueProjection({
15033
+ repoRoot: options.repoRoot,
15034
+ issue,
15035
+ beadsChildId: contextResult.beads.childId,
15036
+ logger
15037
+ });
15038
+ processedResults.push({
15039
+ beadsChildId: contextResult.beads.childId,
15040
+ issueNumber: issue.number,
15041
+ publicationStatus: "merged"
15042
+ });
15043
+ continue;
15044
+ }
15045
+ issue = await reconcileBlockedBeadsIssueProjection({
15046
+ issue,
14834
15047
  issueProvider,
14835
15048
  config: options.config,
14836
15049
  prdRef,
@@ -16177,7 +16390,7 @@ function generateConfigTemplate(options) {
16177
16390
  };
16178
16391
  const config = {
16179
16392
  $schema: "./schema/pourkit.schema.json",
16180
- schemaVersion: 4,
16393
+ schemaVersion: getPackagedConfigSchemaVersion(),
16181
16394
  targets: [target],
16182
16395
  workflowPack: {
16183
16396
  version: "1.0.0",
@@ -18323,12 +18536,20 @@ async function runSerenaStatusCommand(options) {
18323
18536
  }
18324
18537
 
18325
18538
  // commands/config-schema.ts
18326
- import { readFileSync as readFileSync20, existsSync as existsSync21, readdirSync as readdirSync5 } from "fs";
18327
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile3, readdir as readdir4 } from "fs/promises";
18328
- import { resolve as resolve5, dirname as dirname6, relative as relative4, basename } from "path";
18539
+ import { readFileSync as readFileSync21, existsSync as existsSync22, readdirSync as readdirSync5 } from "fs";
18540
+ import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile4, readdir as readdir4 } from "fs/promises";
18541
+ import { resolve as resolve6, dirname as dirname7, relative as relative4, basename } from "path";
18542
+ import { fileURLToPath as fileURLToPath4 } from "url";
18543
+ import Ajv3 from "ajv";
18544
+ init_common();
18545
+
18546
+ // commands/config-migration.ts
18547
+ import { existsSync as existsSync21, readFileSync as readFileSync20 } from "fs";
18548
+ import { writeFile as writeFile3, rename as rename2 } from "fs/promises";
18549
+ import { resolve as resolve5, dirname as dirname6 } from "path";
18329
18550
  import { fileURLToPath as fileURLToPath3 } from "url";
18551
+ import { randomUUID as randomUUID2 } from "crypto";
18330
18552
  import Ajv2 from "ajv";
18331
- init_common();
18332
18553
  var __filename3 = fileURLToPath3(import.meta.url);
18333
18554
  var __dirname3 = dirname6(__filename3);
18334
18555
  function resolvePackagedAssetPath2(fileName) {
@@ -18336,16 +18557,454 @@ function resolvePackagedAssetPath2(fileName) {
18336
18557
  resolve5(__dirname3, "schema", fileName),
18337
18558
  resolve5(__dirname3, "../schema", fileName)
18338
18559
  ];
18339
- return candidates.find((candidate) => existsSync21(candidate)) ?? candidates[0];
18560
+ return candidates.find((c) => existsSync21(c)) ?? candidates[0];
18340
18561
  }
18341
18562
  var PACKAGED_SCHEMA_PATH = resolvePackagedAssetPath2("pourkit.schema.json");
18342
- var PACKAGED_HASH_PATH = resolvePackagedAssetPath2("pourkit.schema.hash");
18563
+ var SAFE_DEFAULTS = {
18564
+ "#$schema": "./schema/pourkit.schema.json",
18565
+ "/targets/*/strategy#postCompletionFinalReview": {
18566
+ agent: "pourkit-reviewer",
18567
+ model: "openai/gpt-5.5",
18568
+ variant: "max",
18569
+ promptTemplate: ".pourkit/managed/prompts/post-completion-final-review.prompt.md"
18570
+ }
18571
+ };
18572
+ function normalizePathForLookup(instancePath) {
18573
+ return instancePath.replace(/\/\d+/g, "/*");
18574
+ }
18575
+ function getCurrentSchemaVersion(schema) {
18576
+ const props = schema.properties;
18577
+ const svProp = props?.schemaVersion;
18578
+ return svProp?.const ?? 0;
18579
+ }
18580
+ function getSchema() {
18581
+ return JSON.parse(readFileSync20(PACKAGED_SCHEMA_PATH, "utf-8"));
18582
+ }
18583
+ function setNestedValue(obj, instancePath, property, value) {
18584
+ let target = obj;
18585
+ if (instancePath) {
18586
+ const segments = instancePath.split("/").filter(Boolean);
18587
+ for (const seg of segments) {
18588
+ if (!target || typeof target !== "object") return;
18589
+ if (Array.isArray(target)) {
18590
+ const idx = Number(seg);
18591
+ if (isNaN(idx) || idx < 0) return;
18592
+ target = target[idx];
18593
+ } else {
18594
+ const t2 = target;
18595
+ const next = t2[seg];
18596
+ if (next !== void 0 && typeof next === "object") {
18597
+ target = next;
18598
+ } else {
18599
+ return;
18600
+ }
18601
+ }
18602
+ }
18603
+ }
18604
+ if (!target || typeof target !== "object") return;
18605
+ const t = target;
18606
+ if (!(property in t)) {
18607
+ t[property] = structuredClone(value);
18608
+ }
18609
+ }
18610
+ function planConfigMigration(rawConfig, options = {}) {
18611
+ const schema = options.schema ?? getSchema();
18612
+ const currentSchemaVersion = getCurrentSchemaVersion(schema);
18613
+ let existingSchemaVersion;
18614
+ if (rawConfig && typeof rawConfig === "object") {
18615
+ const sv = rawConfig.schemaVersion;
18616
+ if (typeof sv === "number") {
18617
+ existingSchemaVersion = sv;
18618
+ }
18619
+ }
18620
+ const ajv = new Ajv2({ strict: true, allErrors: true });
18621
+ ajv.addKeyword("x-pourkit-schema-version");
18622
+ const validate = ajv.compile(schema);
18623
+ let workingConfig = structuredClone(rawConfig);
18624
+ const plannedChanges = [];
18625
+ const blockers = [];
18626
+ let schemaVersionPlanned = false;
18627
+ for (let iteration = 0; iteration < 100; iteration++) {
18628
+ const valid2 = validate(workingConfig);
18629
+ if (valid2) {
18630
+ if (plannedChanges.length === 0 && !schemaVersionPlanned) {
18631
+ return {
18632
+ status: "current",
18633
+ safe: true,
18634
+ available: false,
18635
+ command: null,
18636
+ plannedChanges: [],
18637
+ blockers: [],
18638
+ existingSchemaVersion,
18639
+ currentSchemaVersion,
18640
+ schemaSyncPlanned: false
18641
+ };
18642
+ }
18643
+ const migrated = structuredClone(workingConfig);
18644
+ if (existingSchemaVersion !== void 0 && existingSchemaVersion !== currentSchemaVersion) {
18645
+ migrated.schemaVersion = currentSchemaVersion;
18646
+ }
18647
+ return {
18648
+ status: "migratable",
18649
+ safe: true,
18650
+ available: true,
18651
+ command: "pourkit config migrate",
18652
+ plannedChanges,
18653
+ blockers: [],
18654
+ existingSchemaVersion,
18655
+ currentSchemaVersion,
18656
+ migratedConfig: migrated,
18657
+ schemaSyncPlanned: true
18658
+ };
18659
+ }
18660
+ const errors = validate.errors ?? [];
18661
+ const appliedSafeDefaults = applySafeDefaults(
18662
+ errors,
18663
+ workingConfig,
18664
+ plannedChanges
18665
+ );
18666
+ if (appliedSafeDefaults) {
18667
+ continue;
18668
+ }
18669
+ if (!schemaVersionPlanned) {
18670
+ for (const err of errors) {
18671
+ if (err.keyword === "const" && err.instancePath === "/schemaVersion") {
18672
+ if (existingSchemaVersion === void 0 || !Number.isInteger(existingSchemaVersion)) {
18673
+ blockers.push(
18674
+ `/schemaVersion: invalid value; migration only updates older integer schema versions to supported schema version ${currentSchemaVersion}`
18675
+ );
18676
+ continue;
18677
+ }
18678
+ if (existingSchemaVersion > currentSchemaVersion) {
18679
+ blockers.push(
18680
+ `/schemaVersion: ${existingSchemaVersion} is newer than supported schema version ${currentSchemaVersion}`
18681
+ );
18682
+ continue;
18683
+ }
18684
+ schemaVersionPlanned = true;
18685
+ plannedChanges.push(
18686
+ `Update schemaVersion from ${existingSchemaVersion ?? "?"} to ${currentSchemaVersion}`
18687
+ );
18688
+ workingConfig.schemaVersion = currentSchemaVersion;
18689
+ }
18690
+ }
18691
+ if (schemaVersionPlanned) {
18692
+ continue;
18693
+ }
18694
+ if (blockers.length > 0) {
18695
+ return {
18696
+ status: "blocked",
18697
+ safe: false,
18698
+ available: false,
18699
+ command: null,
18700
+ plannedChanges,
18701
+ blockers,
18702
+ existingSchemaVersion,
18703
+ currentSchemaVersion,
18704
+ schemaSyncPlanned: false
18705
+ };
18706
+ }
18707
+ }
18708
+ for (const err of errors) {
18709
+ if (err.keyword === "required") {
18710
+ const missingProperty = err.params.missingProperty;
18711
+ const concretePath = err.instancePath ? `${err.instancePath}/${missingProperty}` : missingProperty;
18712
+ const lookupKey = `${normalizePathForLookup(err.instancePath)}#${missingProperty}`;
18713
+ if (!(lookupKey in SAFE_DEFAULTS)) {
18714
+ blockers.push(
18715
+ `${concretePath}: required property missing, no registered safe default`
18716
+ );
18717
+ }
18718
+ } else if (err.keyword === "additionalProperties") {
18719
+ const additionalProp = err.params.additionalProperty;
18720
+ const path9 = err.instancePath || "";
18721
+ blockers.push(
18722
+ `${path9 ? `${path9}.${additionalProp}` : additionalProp}: unknown property, removal not supported`
18723
+ );
18724
+ } else if (err.keyword === "const" && err.instancePath === "/schemaVersion") {
18725
+ continue;
18726
+ } else if (err.keyword === "type") {
18727
+ const expected = err.params.type;
18728
+ const path9 = err.instancePath || "";
18729
+ blockers.push(
18730
+ `${path9 || "Config"} must be ${expected === "integer" ? "an integer" : `a ${expected}`}`
18731
+ );
18732
+ } else {
18733
+ const path9 = err.instancePath || "";
18734
+ blockers.push(`${path9 || "Config"} ${err.message || "is invalid"}`);
18735
+ }
18736
+ }
18737
+ if (blockers.length > 0) {
18738
+ return {
18739
+ status: "blocked",
18740
+ safe: false,
18741
+ available: false,
18742
+ command: null,
18743
+ plannedChanges,
18744
+ blockers,
18745
+ existingSchemaVersion,
18746
+ currentSchemaVersion,
18747
+ schemaSyncPlanned: false
18748
+ };
18749
+ }
18750
+ return {
18751
+ status: "blocked",
18752
+ safe: false,
18753
+ available: false,
18754
+ command: null,
18755
+ plannedChanges,
18756
+ blockers: ["Unexpected validation state"],
18757
+ existingSchemaVersion,
18758
+ currentSchemaVersion,
18759
+ schemaSyncPlanned: false
18760
+ };
18761
+ }
18762
+ return {
18763
+ status: "blocked",
18764
+ safe: false,
18765
+ available: false,
18766
+ command: null,
18767
+ plannedChanges,
18768
+ blockers: ["Too many validation iterations"],
18769
+ existingSchemaVersion,
18770
+ currentSchemaVersion,
18771
+ schemaSyncPlanned: false
18772
+ };
18773
+ }
18774
+ function applySafeDefaults(errors, workingConfig, plannedChanges) {
18775
+ let applied = false;
18776
+ for (const err of errors) {
18777
+ if (err.keyword !== "required") continue;
18778
+ const instancePath = err.instancePath;
18779
+ const missingProperty = err.params.missingProperty;
18780
+ const lookupKey = `${normalizePathForLookup(instancePath)}#${missingProperty}`;
18781
+ const concretePath = instancePath ? `${instancePath}/${missingProperty}` : missingProperty;
18782
+ if (lookupKey in SAFE_DEFAULTS) {
18783
+ const defaultValue = SAFE_DEFAULTS[lookupKey];
18784
+ const targetExists = checkPropertyExists(
18785
+ workingConfig,
18786
+ instancePath,
18787
+ missingProperty
18788
+ );
18789
+ if (!targetExists) {
18790
+ setNestedValue(
18791
+ workingConfig,
18792
+ instancePath,
18793
+ missingProperty,
18794
+ defaultValue
18795
+ );
18796
+ plannedChanges.push(`Add ${concretePath}`);
18797
+ applied = true;
18798
+ }
18799
+ }
18800
+ }
18801
+ return applied;
18802
+ }
18803
+ function checkPropertyExists(obj, instancePath, property) {
18804
+ let target = obj;
18805
+ if (instancePath) {
18806
+ const segments = instancePath.split("/").filter(Boolean);
18807
+ for (const seg of segments) {
18808
+ if (!target || typeof target !== "object") return true;
18809
+ if (Array.isArray(target)) {
18810
+ const idx = Number(seg);
18811
+ if (isNaN(idx) || idx < 0) return true;
18812
+ target = target[idx];
18813
+ } else {
18814
+ const t = target;
18815
+ const next = t[seg];
18816
+ if (next !== void 0 && typeof next === "object") {
18817
+ target = next;
18818
+ } else {
18819
+ return true;
18820
+ }
18821
+ }
18822
+ }
18823
+ }
18824
+ if (!target || typeof target !== "object") return true;
18825
+ return property in target;
18826
+ }
18827
+ async function writeFileAtomic2(filePath, content) {
18828
+ const tmpPath = `${filePath}.tmp.${randomUUID2()}`;
18829
+ await writeFile3(tmpPath, content, "utf-8");
18830
+ await rename2(tmpPath, filePath);
18831
+ }
18832
+ async function runConfigMigrateCommand(options) {
18833
+ const repoRootPath = options.cwd ?? process.cwd();
18834
+ const configPath = resolve5(repoRootPath, ".pourkit/config.json");
18835
+ if (!existsSync21(configPath)) {
18836
+ return {
18837
+ status: "blocked",
18838
+ safe: false,
18839
+ plannedChanges: [],
18840
+ blockers: [".pourkit/config.json not found"],
18841
+ configPath,
18842
+ schemaSync: null,
18843
+ currentSchemaVersion: getCurrentSchemaVersion(getSchema())
18844
+ };
18845
+ }
18846
+ let rawConfig;
18847
+ try {
18848
+ const content = readFileSync20(configPath, "utf-8");
18849
+ rawConfig = JSON.parse(content);
18850
+ } catch (err) {
18851
+ const msg = err instanceof SyntaxError ? err.message : String(err);
18852
+ return {
18853
+ status: "blocked",
18854
+ safe: false,
18855
+ plannedChanges: [],
18856
+ blockers: [`.pourkit/config.json: ${msg}`],
18857
+ configPath,
18858
+ schemaSync: null,
18859
+ currentSchemaVersion: getCurrentSchemaVersion(getSchema())
18860
+ };
18861
+ }
18862
+ const plan = planConfigMigration(rawConfig);
18863
+ if (plan.status === "current") {
18864
+ return {
18865
+ status: "current",
18866
+ safe: true,
18867
+ plannedChanges: [],
18868
+ blockers: [],
18869
+ configPath,
18870
+ schemaSync: null,
18871
+ existingSchemaVersion: plan.existingSchemaVersion,
18872
+ currentSchemaVersion: plan.currentSchemaVersion
18873
+ };
18874
+ }
18875
+ if (plan.status === "blocked") {
18876
+ return {
18877
+ status: "blocked",
18878
+ safe: false,
18879
+ plannedChanges: plan.plannedChanges,
18880
+ blockers: plan.blockers,
18881
+ configPath,
18882
+ schemaSync: null,
18883
+ existingSchemaVersion: plan.existingSchemaVersion,
18884
+ currentSchemaVersion: plan.currentSchemaVersion
18885
+ };
18886
+ }
18887
+ if (options.dryRun) {
18888
+ return {
18889
+ status: "dry-run",
18890
+ safe: plan.safe,
18891
+ plannedChanges: plan.plannedChanges,
18892
+ blockers: plan.blockers,
18893
+ configPath,
18894
+ schemaSync: null,
18895
+ existingSchemaVersion: plan.existingSchemaVersion,
18896
+ currentSchemaVersion: plan.currentSchemaVersion
18897
+ };
18898
+ }
18899
+ const migratedConfig = plan.migratedConfig;
18900
+ if (!migratedConfig) {
18901
+ return {
18902
+ status: "blocked",
18903
+ safe: false,
18904
+ plannedChanges: plan.plannedChanges,
18905
+ blockers: ["Migration plan did not produce migrated config"],
18906
+ configPath,
18907
+ schemaSync: null,
18908
+ existingSchemaVersion: plan.existingSchemaVersion,
18909
+ currentSchemaVersion: plan.currentSchemaVersion
18910
+ };
18911
+ }
18912
+ const configContent = JSON.stringify(migratedConfig, null, 2) + "\n";
18913
+ await writeFileAtomic2(configPath, configContent);
18914
+ const schemaSync = await runConfigSyncSchemaCommand({ cwd: repoRootPath });
18915
+ return {
18916
+ status: "migrated",
18917
+ safe: true,
18918
+ plannedChanges: plan.plannedChanges,
18919
+ blockers: [],
18920
+ configPath,
18921
+ schemaSync,
18922
+ existingSchemaVersion: plan.existingSchemaVersion,
18923
+ currentSchemaVersion: plan.currentSchemaVersion
18924
+ };
18925
+ }
18926
+ function renderConfigMigrateResult(result) {
18927
+ const lines = [];
18928
+ switch (result.status) {
18929
+ case "current":
18930
+ lines.push(
18931
+ `Config is already current for schema version ${result.currentSchemaVersion}.`
18932
+ );
18933
+ lines.push("No changes needed.");
18934
+ break;
18935
+ case "dry-run":
18936
+ lines.push(
18937
+ "Dry-run: config migration would apply the following changes:"
18938
+ );
18939
+ if (result.plannedChanges.length > 0) {
18940
+ lines.push("");
18941
+ for (const change of result.plannedChanges) {
18942
+ lines.push(` ${change}`);
18943
+ }
18944
+ }
18945
+ if (result.blockers.length > 0) {
18946
+ lines.push("");
18947
+ lines.push("Blockers:");
18948
+ for (const blocker of result.blockers) {
18949
+ lines.push(` ${blocker}`);
18950
+ }
18951
+ }
18952
+ lines.push("");
18953
+ lines.push("No files were written.");
18954
+ break;
18955
+ case "migrated":
18956
+ lines.push("Config migration completed successfully.");
18957
+ if (result.plannedChanges.length > 0) {
18958
+ lines.push("");
18959
+ for (const change of result.plannedChanges) {
18960
+ lines.push(` ${change}`);
18961
+ }
18962
+ }
18963
+ if (result.schemaSync) {
18964
+ if (result.schemaSync.schemaWritten) {
18965
+ lines.push("Schema file updated.");
18966
+ }
18967
+ if (result.schemaSync.hashWritten) {
18968
+ lines.push("Schema hash updated.");
18969
+ }
18970
+ }
18971
+ break;
18972
+ case "blocked":
18973
+ lines.push("Config migration is blocked.");
18974
+ if (result.blockers.length > 0) {
18975
+ lines.push("");
18976
+ for (const blocker of result.blockers) {
18977
+ lines.push(` ${blocker}`);
18978
+ }
18979
+ }
18980
+ lines.push("");
18981
+ lines.push("No files were written.");
18982
+ break;
18983
+ }
18984
+ return lines.join("\n") + "\n";
18985
+ }
18986
+ function renderConfigMigrateResultJson(result) {
18987
+ return JSON.stringify(result, null, 2);
18988
+ }
18989
+
18990
+ // commands/config-schema.ts
18991
+ var __filename4 = fileURLToPath4(import.meta.url);
18992
+ var __dirname4 = dirname7(__filename4);
18993
+ function resolvePackagedAssetPath3(fileName) {
18994
+ const candidates = [
18995
+ resolve6(__dirname4, "schema", fileName),
18996
+ resolve6(__dirname4, "../schema", fileName)
18997
+ ];
18998
+ return candidates.find((candidate) => existsSync22(candidate)) ?? candidates[0];
18999
+ }
19000
+ var PACKAGED_SCHEMA_PATH2 = resolvePackagedAssetPath3("pourkit.schema.json");
19001
+ var PACKAGED_HASH_PATH = resolvePackagedAssetPath3("pourkit.schema.hash");
18343
19002
  var _schemaValidator = null;
18344
19003
  var _schemaErrors = null;
18345
19004
  function getSchemaValidator() {
18346
19005
  if (!_schemaValidator) {
18347
- const schema = JSON.parse(readFileSync20(PACKAGED_SCHEMA_PATH, "utf-8"));
18348
- const ajv = new Ajv2({ strict: true });
19006
+ const schema = JSON.parse(readFileSync21(PACKAGED_SCHEMA_PATH2, "utf-8"));
19007
+ const ajv = new Ajv3({ strict: true });
18349
19008
  ajv.addKeyword("x-pourkit-schema-version");
18350
19009
  const validate = ajv.compile(schema);
18351
19010
  _schemaValidator = (data) => {
@@ -18358,41 +19017,41 @@ function getSchemaValidator() {
18358
19017
  return _schemaValidator;
18359
19018
  }
18360
19019
  function readPackagedHash() {
18361
- return readFileSync20(PACKAGED_HASH_PATH, "utf-8");
19020
+ return readFileSync21(PACKAGED_HASH_PATH, "utf-8");
18362
19021
  }
18363
19022
  function localSchemaDir(repoRoot2) {
18364
- return resolve5(repoRoot2, ".pourkit/schema");
19023
+ return resolve6(repoRoot2, ".pourkit/schema");
18365
19024
  }
18366
19025
  var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
18367
19026
  var MANAGED_BLOCK_BEGIN_MARKER = MANAGED_BLOCK_BEGIN2.trim();
18368
19027
  var MANAGED_BLOCK_END_MARKER = "<!-- END POURKIT MANAGED BLOCK -->";
18369
19028
  var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
18370
19029
  function resolvePackagedManagedPath(...segments) {
18371
- const bundledPath = resolve5(__dirname3, "managed", ...segments);
18372
- const sourcePath = resolve5(__dirname3, "..", "managed", ...segments);
19030
+ const bundledPath = resolve6(__dirname4, "managed", ...segments);
19031
+ const sourcePath = resolve6(__dirname4, "..", "managed", ...segments);
18373
19032
  const candidates = [bundledPath, sourcePath];
18374
- return candidates.find((candidate) => existsSync21(candidate)) ?? bundledPath;
19033
+ return candidates.find((candidate) => existsSync22(candidate)) ?? bundledPath;
18375
19034
  }
18376
19035
  function resolvePackagedOpenCodeAgentsDir() {
18377
19036
  const candidates = [
18378
- resolve5(__dirname3, ".opencode", "agents"),
18379
- resolve5(__dirname3, "..", ".opencode", "agents"),
18380
- resolve5(__dirname3, "..", "..", ".opencode", "agents")
19037
+ resolve6(__dirname4, ".opencode", "agents"),
19038
+ resolve6(__dirname4, "..", ".opencode", "agents"),
19039
+ resolve6(__dirname4, "..", "..", ".opencode", "agents")
18381
19040
  ];
18382
- return candidates.find((candidate) => existsSync21(candidate)) ?? candidates[0];
19041
+ return candidates.find((candidate) => existsSync22(candidate)) ?? candidates[0];
18383
19042
  }
18384
19043
  function listPackagedOpenCodeAgentFiles() {
18385
19044
  const agentsDir = resolvePackagedOpenCodeAgentsDir();
18386
19045
  try {
18387
- return readdirSync5(agentsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => resolve5(agentsDir, entry.name));
19046
+ return readdirSync5(agentsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => resolve6(agentsDir, entry.name));
18388
19047
  } catch {
18389
19048
  return [];
18390
19049
  }
18391
19050
  }
18392
19051
  async function readManifestAssets(cwd) {
18393
- const manifestPath = resolve5(cwd, ".pourkit/manifest.json");
19052
+ const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
18394
19053
  try {
18395
- const content = await readFile7(manifestPath, "utf-8");
19054
+ const content = await readFile8(manifestPath, "utf-8");
18396
19055
  const manifest = JSON.parse(content);
18397
19056
  if (typeof manifest.assets !== "object" || manifest.assets === null || Array.isArray(manifest.assets)) {
18398
19057
  return {};
@@ -18416,7 +19075,7 @@ function resolvePackagedReleaseAddonDocPath(docName) {
18416
19075
  }
18417
19076
  async function readPackagedTextFile(filePath) {
18418
19077
  try {
18419
- return await readFile7(filePath, "utf-8");
19078
+ return await readFile8(filePath, "utf-8");
18420
19079
  } catch {
18421
19080
  return null;
18422
19081
  }
@@ -18430,7 +19089,7 @@ async function walkDir2(dir) {
18430
19089
  try {
18431
19090
  const entries = await readdir4(dir, { withFileTypes: true });
18432
19091
  for (const entry of entries) {
18433
- const full = resolve5(dir, entry.name);
19092
+ const full = resolve6(dir, entry.name);
18434
19093
  if (entry.isDirectory()) {
18435
19094
  files.push(...await walkDir2(full));
18436
19095
  } else {
@@ -18513,8 +19172,8 @@ async function validateWorkflowPack(cwd) {
18513
19172
  const baselineSkills = getBaselineManagedSkillNames();
18514
19173
  const manifestAssets = await readManifestAssets(cwd);
18515
19174
  for (const docName of catalog.docs) {
18516
- const localPath = resolve5(cwd, `.pourkit/managed/docs/agents/${docName}`);
18517
- if (!existsSync21(localPath)) {
19175
+ const localPath = resolve6(cwd, `.pourkit/managed/docs/agents/${docName}`);
19176
+ if (!existsSync22(localPath)) {
18518
19177
  failures.push({
18519
19178
  severity: "failure",
18520
19179
  kind: "missing_managed_asset",
@@ -18526,13 +19185,13 @@ async function validateWorkflowPack(cwd) {
18526
19185
  const packagedContent = await readPackagedTextFile(packagedPath);
18527
19186
  if (packagedContent !== null) {
18528
19187
  try {
18529
- const localContent = await readFile7(localPath, "utf-8");
19188
+ const localContent = await readFile8(localPath, "utf-8");
18530
19189
  if (localContent !== packagedContent) {
18531
- const overridePath = resolve5(
19190
+ const overridePath = resolve6(
18532
19191
  cwd,
18533
19192
  `.pourkit/overrides/docs/agents/${docName}`
18534
19193
  );
18535
- if (existsSync21(overridePath)) {
19194
+ if (existsSync22(overridePath)) {
18536
19195
  warnings.push({
18537
19196
  severity: "warning",
18538
19197
  kind: "overridden",
@@ -18554,16 +19213,16 @@ async function validateWorkflowPack(cwd) {
18554
19213
  }
18555
19214
  }
18556
19215
  for (const skillName of baselineSkills) {
18557
- const skillDir = resolve5(cwd, `.pourkit/managed/skills/${skillName}`);
18558
- const skillSourcePath = resolve5(skillDir, "SKILL.md");
18559
- if (!existsSync21(skillDir)) {
19216
+ const skillDir = resolve6(cwd, `.pourkit/managed/skills/${skillName}`);
19217
+ const skillSourcePath = resolve6(skillDir, "SKILL.md");
19218
+ if (!existsSync22(skillDir)) {
18560
19219
  failures.push({
18561
19220
  severity: "failure",
18562
19221
  kind: "missing_managed_asset",
18563
19222
  path: `.pourkit/managed/skills/${skillName}/SKILL.md`,
18564
19223
  message: `Missing managed skill: ${skillName}`
18565
19224
  });
18566
- } else if (!existsSync21(skillSourcePath)) {
19225
+ } else if (!existsSync22(skillSourcePath)) {
18567
19226
  failures.push({
18568
19227
  severity: "failure",
18569
19228
  kind: "missing_managed_asset",
@@ -18579,13 +19238,13 @@ async function validateWorkflowPack(cwd) {
18579
19238
  const packagedContent = await readPackagedTextFile(packagedSkillPath);
18580
19239
  if (packagedContent !== null) {
18581
19240
  try {
18582
- const localContent = await readFile7(skillSourcePath, "utf-8");
19241
+ const localContent = await readFile8(skillSourcePath, "utf-8");
18583
19242
  if (localContent !== packagedContent) {
18584
- const overridePath = resolve5(
19243
+ const overridePath = resolve6(
18585
19244
  cwd,
18586
19245
  `.pourkit/overrides/skills/${skillName}/SKILL.md`
18587
19246
  );
18588
- if (existsSync21(overridePath)) {
19247
+ if (existsSync22(overridePath)) {
18589
19248
  warnings.push({
18590
19249
  severity: "warning",
18591
19250
  kind: "overridden",
@@ -18607,8 +19266,8 @@ async function validateWorkflowPack(cwd) {
18607
19266
  }
18608
19267
  }
18609
19268
  for (const promptName of catalog.prompts) {
18610
- const promptPath = resolve5(cwd, `.pourkit/managed/prompts/${promptName}`);
18611
- if (!existsSync21(promptPath)) {
19269
+ const promptPath = resolve6(cwd, `.pourkit/managed/prompts/${promptName}`);
19270
+ if (!existsSync22(promptPath)) {
18612
19271
  failures.push({
18613
19272
  severity: "failure",
18614
19273
  kind: "missing_managed_asset",
@@ -18618,16 +19277,16 @@ async function validateWorkflowPack(cwd) {
18618
19277
  }
18619
19278
  }
18620
19279
  for (const skillName of baselineSkills) {
18621
- const projectionDir = resolve5(cwd, `.agents/skills/${skillName}`);
18622
- const projectionPath = resolve5(projectionDir, "SKILL.md");
18623
- if (!existsSync21(projectionDir)) {
19280
+ const projectionDir = resolve6(cwd, `.agents/skills/${skillName}`);
19281
+ const projectionPath = resolve6(projectionDir, "SKILL.md");
19282
+ if (!existsSync22(projectionDir)) {
18624
19283
  warnings.push({
18625
19284
  severity: "warning",
18626
19285
  kind: "projection_stale",
18627
19286
  path: `.agents/skills/${skillName}/SKILL.md`,
18628
19287
  message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
18629
19288
  });
18630
- } else if (!existsSync21(projectionPath)) {
19289
+ } else if (!existsSync22(projectionPath)) {
18631
19290
  warnings.push({
18632
19291
  severity: "warning",
18633
19292
  kind: "projection_stale",
@@ -18643,7 +19302,7 @@ async function validateWorkflowPack(cwd) {
18643
19302
  const packagedContent = await readPackagedTextFile(managedSourcePath);
18644
19303
  if (packagedContent !== null) {
18645
19304
  try {
18646
- const projectionContent = await readFile7(projectionPath, "utf-8");
19305
+ const projectionContent = await readFile8(projectionPath, "utf-8");
18647
19306
  const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
18648
19307
  if (projectionContent !== buildOpenCodeSkillProjectionContent(
18649
19308
  managedSourceRelativePath,
@@ -18664,8 +19323,8 @@ async function validateWorkflowPack(cwd) {
18664
19323
  for (const agentFile of listPackagedOpenCodeAgentFiles()) {
18665
19324
  const fileName = basename(agentFile);
18666
19325
  const relativePath = `.opencode/agents/${fileName}`;
18667
- const localPath = resolve5(cwd, relativePath);
18668
- if (!existsSync21(localPath)) {
19326
+ const localPath = resolve6(cwd, relativePath);
19327
+ if (!existsSync22(localPath)) {
18669
19328
  failures.push({
18670
19329
  severity: "failure",
18671
19330
  kind: "missing_managed_asset",
@@ -18680,7 +19339,7 @@ async function validateWorkflowPack(cwd) {
18680
19339
  const packagedContent = await readPackagedTextFile(agentFile);
18681
19340
  if (packagedContent === null) continue;
18682
19341
  try {
18683
- const localContent = await readFile7(localPath, "utf-8");
19342
+ const localContent = await readFile8(localPath, "utf-8");
18684
19343
  if (localContent !== packagedContent) {
18685
19344
  failures.push({
18686
19345
  severity: "failure",
@@ -18692,10 +19351,10 @@ async function validateWorkflowPack(cwd) {
18692
19351
  } catch {
18693
19352
  }
18694
19353
  }
18695
- const agentsPath = resolve5(cwd, "AGENTS.md");
18696
- if (existsSync21(agentsPath)) {
19354
+ const agentsPath = resolve6(cwd, "AGENTS.md");
19355
+ if (existsSync22(agentsPath)) {
18697
19356
  try {
18698
- const agentsContent = await readFile7(agentsPath, "utf-8");
19357
+ const agentsContent = await readFile8(agentsPath, "utf-8");
18699
19358
  if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
18700
19359
  failures.push({
18701
19360
  severity: "failure",
@@ -18715,8 +19374,8 @@ async function validateWorkflowPack(cwd) {
18715
19374
  } catch {
18716
19375
  }
18717
19376
  }
18718
- const overridesDir = resolve5(cwd, ".pourkit/overrides");
18719
- if (existsSync21(overridesDir)) {
19377
+ const overridesDir = resolve6(cwd, ".pourkit/overrides");
19378
+ if (existsSync22(overridesDir)) {
18720
19379
  const overrideFiles = await walkDir2(overridesDir);
18721
19380
  for (const file of overrideFiles) {
18722
19381
  const relPath = relative4(cwd, file);
@@ -18730,19 +19389,19 @@ async function validateWorkflowPack(cwd) {
18730
19389
  }
18731
19390
  }
18732
19391
  }
18733
- const configPath = resolve5(cwd, ".pourkit/config.json");
18734
- if (existsSync21(configPath)) {
19392
+ const configPath = resolve6(cwd, ".pourkit/config.json");
19393
+ if (existsSync22(configPath)) {
18735
19394
  try {
18736
- const configContent = await readFile7(configPath, "utf-8");
19395
+ const configContent = await readFile8(configPath, "utf-8");
18737
19396
  const parsed = JSON.parse(configContent);
18738
19397
  const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
18739
19398
  if (releaseEnabled) {
18740
19399
  for (const skillName of catalog.releaseAddonSkills) {
18741
- const addonDir = resolve5(
19400
+ const addonDir = resolve6(
18742
19401
  cwd,
18743
19402
  `.pourkit/managed/addons/release/skills/${skillName}`
18744
19403
  );
18745
- if (!existsSync21(addonDir)) {
19404
+ if (!existsSync22(addonDir)) {
18746
19405
  failures.push({
18747
19406
  severity: "failure",
18748
19407
  kind: "release_config_conflict",
@@ -18752,11 +19411,11 @@ async function validateWorkflowPack(cwd) {
18752
19411
  }
18753
19412
  }
18754
19413
  for (const docName of catalog.releaseAddonDocs) {
18755
- const addonDocPath = resolve5(
19414
+ const addonDocPath = resolve6(
18756
19415
  cwd,
18757
19416
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18758
19417
  );
18759
- if (!existsSync21(addonDocPath)) {
19418
+ if (!existsSync22(addonDocPath)) {
18760
19419
  failures.push({
18761
19420
  severity: "failure",
18762
19421
  kind: "release_config_conflict",
@@ -18768,7 +19427,7 @@ async function validateWorkflowPack(cwd) {
18768
19427
  resolvePackagedReleaseAddonDocPath(docName)
18769
19428
  );
18770
19429
  if (packagedContent !== null) {
18771
- const localContent = await readFile7(addonDocPath, "utf-8");
19430
+ const localContent = await readFile8(addonDocPath, "utf-8");
18772
19431
  if (localContent !== packagedContent) {
18773
19432
  failures.push({
18774
19433
  severity: "failure",
@@ -18797,11 +19456,11 @@ async function validateWorkflowPack(cwd) {
18797
19456
  }
18798
19457
  } else {
18799
19458
  for (const skillName of catalog.releaseAddonSkills) {
18800
- const addonDir = resolve5(
19459
+ const addonDir = resolve6(
18801
19460
  cwd,
18802
19461
  `.pourkit/managed/addons/release/skills/${skillName}`
18803
19462
  );
18804
- if (existsSync21(addonDir)) {
19463
+ if (existsSync22(addonDir)) {
18805
19464
  failures.push({
18806
19465
  severity: "failure",
18807
19466
  kind: "release_config_conflict",
@@ -18811,11 +19470,11 @@ async function validateWorkflowPack(cwd) {
18811
19470
  }
18812
19471
  }
18813
19472
  for (const docName of catalog.releaseAddonDocs) {
18814
- const addonDocPath = resolve5(
19473
+ const addonDocPath = resolve6(
18815
19474
  cwd,
18816
19475
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18817
19476
  );
18818
- if (existsSync21(addonDocPath)) {
19477
+ if (existsSync22(addonDocPath)) {
18819
19478
  failures.push({
18820
19479
  severity: "failure",
18821
19480
  kind: "release_config_conflict",
@@ -18828,16 +19487,16 @@ async function validateWorkflowPack(cwd) {
18828
19487
  } catch {
18829
19488
  }
18830
19489
  }
18831
- const manifestPath = resolve5(cwd, ".pourkit/manifest.json");
18832
- if (existsSync21(manifestPath)) {
19490
+ const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
19491
+ if (existsSync22(manifestPath)) {
18833
19492
  try {
18834
- const manifestContent = await readFile7(manifestPath, "utf-8");
19493
+ const manifestContent = await readFile8(manifestPath, "utf-8");
18835
19494
  const manifest = JSON.parse(manifestContent);
18836
19495
  const wp = manifest.workflowPack;
18837
19496
  if (wp?.projections) {
18838
19497
  for (const proj of wp.projections) {
18839
19498
  if (proj.path) {
18840
- const resolved = resolve5(cwd, proj.path);
19499
+ const resolved = resolve6(cwd, proj.path);
18841
19500
  if (!isPathContained(cwd, resolved)) {
18842
19501
  failures.push({
18843
19502
  severity: "failure",
@@ -18851,8 +19510,8 @@ async function validateWorkflowPack(cwd) {
18851
19510
  } catch {
18852
19511
  }
18853
19512
  }
18854
- const pathEscapeOverrideDir = resolve5(cwd, ".pourkit/overrides");
18855
- if (existsSync21(pathEscapeOverrideDir)) {
19513
+ const pathEscapeOverrideDir = resolve6(cwd, ".pourkit/overrides");
19514
+ if (existsSync22(pathEscapeOverrideDir)) {
18856
19515
  const overrideFiles = await walkDir2(pathEscapeOverrideDir);
18857
19516
  for (const file of overrideFiles) {
18858
19517
  if (!isPathContained(cwd, file)) {
@@ -18865,13 +19524,13 @@ async function validateWorkflowPack(cwd) {
18865
19524
  }
18866
19525
  }
18867
19526
  }
18868
- const managedDocsDir = resolve5(cwd, ".pourkit/managed/docs/agents");
18869
- if (existsSync21(managedDocsDir)) {
19527
+ const managedDocsDir = resolve6(cwd, ".pourkit/managed/docs/agents");
19528
+ if (existsSync22(managedDocsDir)) {
18870
19529
  const docFiles = await walkDir2(managedDocsDir);
18871
19530
  for (const file of docFiles) {
18872
19531
  if (!file.endsWith(".md")) continue;
18873
19532
  try {
18874
- const content = await readFile7(file, "utf-8");
19533
+ const content = await readFile8(file, "utf-8");
18875
19534
  const relPath = relative4(cwd, file);
18876
19535
  const violations = scanManagedPolicyText(content, relPath);
18877
19536
  for (const v of violations) {
@@ -18886,15 +19545,15 @@ async function validateWorkflowPack(cwd) {
18886
19545
  }
18887
19546
  }
18888
19547
  }
18889
- const managedSkillsDir = resolve5(cwd, ".pourkit/managed/skills");
18890
- if (existsSync21(managedSkillsDir)) {
19548
+ const managedSkillsDir = resolve6(cwd, ".pourkit/managed/skills");
19549
+ if (existsSync22(managedSkillsDir)) {
18891
19550
  const skillDirs = await readdir4(managedSkillsDir, { withFileTypes: true });
18892
19551
  for (const entry of skillDirs) {
18893
19552
  if (!entry.isDirectory()) continue;
18894
- const skillFile = resolve5(managedSkillsDir, entry.name, "SKILL.md");
18895
- if (!existsSync21(skillFile)) continue;
19553
+ const skillFile = resolve6(managedSkillsDir, entry.name, "SKILL.md");
19554
+ if (!existsSync22(skillFile)) continue;
18896
19555
  try {
18897
- const content = await readFile7(skillFile, "utf-8");
19556
+ const content = await readFile8(skillFile, "utf-8");
18898
19557
  const relPath = relative4(cwd, skillFile);
18899
19558
  const violations = scanManagedPolicyText(content, relPath);
18900
19559
  for (const v of violations) {
@@ -18909,13 +19568,13 @@ async function validateWorkflowPack(cwd) {
18909
19568
  }
18910
19569
  }
18911
19570
  }
18912
- const managedPromptsDir = resolve5(cwd, ".pourkit/managed/prompts");
18913
- if (existsSync21(managedPromptsDir)) {
19571
+ const managedPromptsDir = resolve6(cwd, ".pourkit/managed/prompts");
19572
+ if (existsSync22(managedPromptsDir)) {
18914
19573
  for (const promptName of catalog.prompts) {
18915
- const promptFile = resolve5(managedPromptsDir, promptName);
18916
- if (!existsSync21(promptFile)) continue;
19574
+ const promptFile = resolve6(managedPromptsDir, promptName);
19575
+ if (!existsSync22(promptFile)) continue;
18917
19576
  try {
18918
- const content = await readFile7(promptFile, "utf-8");
19577
+ const content = await readFile8(promptFile, "utf-8");
18919
19578
  const relPath = relative4(cwd, promptFile);
18920
19579
  const violations = scanManagedPolicyText(content, relPath);
18921
19580
  for (const v of violations) {
@@ -18931,13 +19590,13 @@ async function validateWorkflowPack(cwd) {
18931
19590
  }
18932
19591
  }
18933
19592
  for (const skillName of catalog.releaseAddonSkills) {
18934
- const addonSkillFile = resolve5(
19593
+ const addonSkillFile = resolve6(
18935
19594
  cwd,
18936
19595
  `.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
18937
19596
  );
18938
- if (!existsSync21(addonSkillFile)) continue;
19597
+ if (!existsSync22(addonSkillFile)) continue;
18939
19598
  try {
18940
- const content = await readFile7(addonSkillFile, "utf-8");
19599
+ const content = await readFile8(addonSkillFile, "utf-8");
18941
19600
  const relPath = relative4(cwd, addonSkillFile);
18942
19601
  const violations = scanManagedPolicyText(content, relPath);
18943
19602
  for (const v of violations) {
@@ -18952,13 +19611,13 @@ async function validateWorkflowPack(cwd) {
18952
19611
  }
18953
19612
  }
18954
19613
  for (const docName of catalog.releaseAddonDocs) {
18955
- const addonDocFile = resolve5(
19614
+ const addonDocFile = resolve6(
18956
19615
  cwd,
18957
19616
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18958
19617
  );
18959
- if (!existsSync21(addonDocFile)) continue;
19618
+ if (!existsSync22(addonDocFile)) continue;
18960
19619
  try {
18961
- const content = await readFile7(addonDocFile, "utf-8");
19620
+ const content = await readFile8(addonDocFile, "utf-8");
18962
19621
  const relPath = relative4(cwd, addonDocFile);
18963
19622
  const violations = scanManagedPolicyText(content, relPath);
18964
19623
  for (const v of violations) {
@@ -18989,16 +19648,16 @@ async function validateMemoryHealth(cwd, memoryConfig) {
18989
19648
  ok: icmPath !== null,
18990
19649
  detail: icmPath !== null ? icmPath : "icm not found on PATH"
18991
19650
  });
18992
- const gitignorePath = resolve5(cwd, ".gitignore");
18993
- const gitignoreContent = existsSync21(gitignorePath) ? readFileSync20(gitignorePath, "utf-8") : "";
19651
+ const gitignorePath = resolve6(cwd, ".gitignore");
19652
+ const gitignoreContent = existsSync22(gitignorePath) ? readFileSync21(gitignorePath, "utf-8") : "";
18994
19653
  const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
18995
19654
  checks.push({
18996
19655
  name: "icm-gitignore",
18997
19656
  ok: hasGitignoreEntry,
18998
19657
  detail: hasGitignoreEntry ? ".gitignore covers .pourkit/icm/" : ".gitignore does not cover .pourkit/icm/"
18999
19658
  });
19000
- const memoryDbPath = resolve5(cwd, ".pourkit", "icm", "memories.db");
19001
- const hasMemoryDb = existsSync21(memoryDbPath);
19659
+ const memoryDbPath = resolve6(cwd, ".pourkit", "icm", "memories.db");
19660
+ const hasMemoryDb = existsSync22(memoryDbPath);
19002
19661
  checks.push({
19003
19662
  name: "icm-db-exists",
19004
19663
  ok: hasMemoryDb,
@@ -19011,9 +19670,9 @@ async function validateMemoryHealth(cwd, memoryConfig) {
19011
19670
  detail: shapeValid ? "memory: { enabled: true, provider: icm }" : `invalid memory config: enabled=${memoryConfig.enabled}, provider=${memoryConfig.provider}`
19012
19671
  });
19013
19672
  const staleAgentFiles = ["AGENTS.md", "CLAUDE.md"].filter((fileName) => {
19014
- const filePath = resolve5(cwd, fileName);
19015
- if (!existsSync21(filePath)) return false;
19016
- const content = readFileSync20(filePath, "utf-8");
19673
+ const filePath = resolve6(cwd, fileName);
19674
+ if (!existsSync22(filePath)) return false;
19675
+ const content = readFileSync21(filePath, "utf-8");
19017
19676
  const managedBlock = extractManagedBlockContent(content);
19018
19677
  return managedBlock !== null && hasBareIcmMemoryCommand(managedBlock);
19019
19678
  });
@@ -19047,10 +19706,10 @@ function extractManagedBlockContent(content) {
19047
19706
  async function runDoctorCommand(options) {
19048
19707
  const repoRootPath = options.cwd ?? process.cwd();
19049
19708
  const schemaDir = localSchemaDir(repoRootPath);
19050
- const localSchemaPath = resolve5(schemaDir, "pourkit.schema.json");
19051
- const localHashPath = resolve5(schemaDir, "pourkit.schema.hash");
19052
- const localSchemaExists = existsSync21(localSchemaPath);
19053
- const localHashExists = existsSync21(localHashPath);
19709
+ const localSchemaPath = resolve6(schemaDir, "pourkit.schema.json");
19710
+ const localHashPath = resolve6(schemaDir, "pourkit.schema.hash");
19711
+ const localSchemaExists = existsSync22(localSchemaPath);
19712
+ const localHashExists = existsSync22(localHashPath);
19054
19713
  let packagedHash = null;
19055
19714
  try {
19056
19715
  packagedHash = readPackagedHash();
@@ -19059,19 +19718,20 @@ async function runDoctorCommand(options) {
19059
19718
  let localHashContent = null;
19060
19719
  if (localHashExists) {
19061
19720
  try {
19062
- localHashContent = await readFile7(localHashPath, "utf-8");
19721
+ localHashContent = await readFile8(localHashPath, "utf-8");
19063
19722
  } catch {
19064
19723
  }
19065
19724
  }
19066
19725
  const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
19067
19726
  const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
19068
19727
  const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
19069
- const configPath = resolve5(repoRootPath, ".pourkit/config.json");
19728
+ const configPath = resolve6(repoRootPath, ".pourkit/config.json");
19070
19729
  let configValidation;
19071
19730
  let rawMemoryConfig;
19072
- if (existsSync21(configPath)) {
19731
+ let configMigration;
19732
+ if (existsSync22(configPath)) {
19073
19733
  try {
19074
- const raw = JSON.parse(readFileSync20(configPath, "utf-8"));
19734
+ const raw = JSON.parse(readFileSync21(configPath, "utf-8"));
19075
19735
  rawMemoryConfig = raw.memory;
19076
19736
  const validate = getSchemaValidator();
19077
19737
  const valid2 = validate(raw);
@@ -19086,25 +19746,47 @@ async function runDoctorCommand(options) {
19086
19746
  )
19087
19747
  };
19088
19748
  }
19749
+ const plan = planConfigMigration(raw);
19750
+ configMigration = {
19751
+ available: plan.available,
19752
+ safe: plan.safe,
19753
+ command: plan.command,
19754
+ plannedChanges: plan.plannedChanges,
19755
+ blockers: plan.blockers
19756
+ };
19089
19757
  } catch (err) {
19090
19758
  const msg = err instanceof SyntaxError ? err.message : String(err);
19091
19759
  configValidation = {
19092
19760
  ok: false,
19093
19761
  errors: [`.pourkit/config.json: ${msg}`]
19094
19762
  };
19763
+ configMigration = {
19764
+ available: false,
19765
+ safe: false,
19766
+ command: null,
19767
+ plannedChanges: [],
19768
+ blockers: [`.pourkit/config.json: ${msg}`]
19769
+ };
19095
19770
  }
19096
19771
  } else {
19097
19772
  configValidation = {
19098
19773
  ok: false,
19099
19774
  errors: [".pourkit/config.json not found"]
19100
19775
  };
19776
+ configMigration = {
19777
+ available: false,
19778
+ safe: false,
19779
+ command: null,
19780
+ plannedChanges: [],
19781
+ blockers: [".pourkit/config.json not found"]
19782
+ };
19101
19783
  }
19102
19784
  const obsoleteConfigs = [
19103
19785
  "pourkit.config.ts",
19104
19786
  "pourkit.config.mjs",
19105
19787
  "pourkit.config.js",
19106
19788
  "pourkit.json"
19107
- ].filter((p) => existsSync21(resolve5(repoRootPath, p)));
19789
+ ].filter((p) => existsSync22(resolve6(repoRootPath, p)));
19108
19790
  const workflowPack = await validateWorkflowPack(repoRootPath);
19109
19791
  const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
19110
19792
  let recommendation = null;
@@ -19114,7 +19796,15 @@ async function runDoctorCommand(options) {
19114
19796
  `Found obsolete config files: ${obsoleteConfigs.join(", ")}. Move configuration to .pourkit/config.json with "$schema": "./schema/pourkit.schema.json".`
19115
19797
  );
19116
19798
  }
19117
- if (!configValidation.ok) {
19799
+ if (configMigration.available && configMigration.safe) {
19800
+ recParts.push(
19801
+ 'A safe config migration is available. Run "pourkit config migrate" to apply it.'
19802
+ );
19803
+ } else if (!configMigration.available && configMigration.blockers.length > 0) {
19804
+ recParts.push(
19805
+ "Config migration is blocked; fix errors in .pourkit/config.json."
19806
+ );
19807
+ } else if (!configValidation.ok) {
19118
19808
  recParts.push(
19119
19809
  "Config validation failed; fix errors in .pourkit/config.json."
19120
19810
  );
@@ -19139,6 +19829,7 @@ async function runDoctorCommand(options) {
19139
19829
  configValidation,
19140
19830
  obsoleteConfigs,
19141
19831
  schemaAssets: { schema, hash, overall },
19832
+ configMigration,
19142
19833
  workflowPack,
19143
19834
  memory,
19144
19835
  recommendation
@@ -19148,30 +19839,30 @@ async function runConfigSyncSchemaCommand(options) {
19148
19839
  const repoRootPath = options.cwd ?? process.cwd();
19149
19840
  const schemaDir = localSchemaDir(repoRootPath);
19150
19841
  await mkdir6(schemaDir, { recursive: true });
19151
- const packagedSchema = await readFile7(PACKAGED_SCHEMA_PATH, "utf-8");
19152
- const packagedHash = await readFile7(PACKAGED_HASH_PATH, "utf-8");
19153
- const localSchemaPath = resolve5(schemaDir, "pourkit.schema.json");
19154
- const localHashPath = resolve5(schemaDir, "pourkit.schema.hash");
19842
+ const packagedSchema = await readFile8(PACKAGED_SCHEMA_PATH2, "utf-8");
19843
+ const packagedHash = await readFile8(PACKAGED_HASH_PATH, "utf-8");
19844
+ const localSchemaPath = resolve6(schemaDir, "pourkit.schema.json");
19845
+ const localHashPath = resolve6(schemaDir, "pourkit.schema.hash");
19155
19846
  let schemaWritten = false;
19156
19847
  let hashWritten = false;
19157
- if (existsSync21(localSchemaPath)) {
19158
- const existing = await readFile7(localSchemaPath, "utf-8");
19848
+ if (existsSync22(localSchemaPath)) {
19849
+ const existing = await readFile8(localSchemaPath, "utf-8");
19159
19850
  if (existing !== packagedSchema) {
19160
- await writeFile3(localSchemaPath, packagedSchema, "utf-8");
19851
+ await writeFile4(localSchemaPath, packagedSchema, "utf-8");
19161
19852
  schemaWritten = true;
19162
19853
  }
19163
19854
  } else {
19164
- await writeFile3(localSchemaPath, packagedSchema, "utf-8");
19855
+ await writeFile4(localSchemaPath, packagedSchema, "utf-8");
19165
19856
  schemaWritten = true;
19166
19857
  }
19167
- if (existsSync21(localHashPath)) {
19168
- const existing = await readFile7(localHashPath, "utf-8");
19858
+ if (existsSync22(localHashPath)) {
19859
+ const existing = await readFile8(localHashPath, "utf-8");
19169
19860
  if (existing !== packagedHash) {
19170
- await writeFile3(localHashPath, packagedHash, "utf-8");
19861
+ await writeFile4(localHashPath, packagedHash, "utf-8");
19171
19862
  hashWritten = true;
19172
19863
  }
19173
19864
  } else {
19174
- await writeFile3(localHashPath, packagedHash, "utf-8");
19865
+ await writeFile4(localHashPath, packagedHash, "utf-8");
19175
19866
  hashWritten = true;
19176
19867
  }
19177
19868
  return {
@@ -19182,13 +19873,13 @@ async function runConfigSyncSchemaCommand(options) {
19182
19873
  }
19183
19874
 
19184
19875
  // commands/workflow-pack-sync.ts
19185
- import { existsSync as existsSync22, readdirSync as readdirSync6 } from "fs";
19186
- import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile4, readdir as readdir5 } from "fs/promises";
19876
+ import { existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
19877
+ import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile5, readdir as readdir5 } from "fs/promises";
19187
19878
  import { createHash as createHash2 } from "crypto";
19188
- import { resolve as resolve6, dirname as dirname7, relative as relative5, normalize as normalize2, basename as basename2 } from "path";
19189
- import { fileURLToPath as fileURLToPath4 } from "url";
19190
- var __filename4 = fileURLToPath4(import.meta.url);
19191
- var __dirname4 = dirname7(__filename4);
19879
+ import { resolve as resolve7, dirname as dirname8, relative as relative5, normalize as normalize2, basename as basename2 } from "path";
19880
+ import { fileURLToPath as fileURLToPath5 } from "url";
19881
+ var __filename5 = fileURLToPath5(import.meta.url);
19882
+ var __dirname5 = dirname8(__filename5);
19192
19883
  var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
19193
19884
  var PROJECT_OWNED_DIRECTORIES = [
19194
19885
  ".pourkit/docs/adr",
@@ -19197,23 +19888,23 @@ var PROJECT_OWNED_DIRECTORIES = [
19197
19888
  ];
19198
19889
  var OVERRIDES_DIR = ".pourkit/overrides";
19199
19890
  function resolvePackagedManagedPath2(...segments) {
19200
- const bundledPath = resolve6(__dirname4, "managed", ...segments);
19201
- const sourcePath = resolve6(__dirname4, "..", "managed", ...segments);
19891
+ const bundledPath = resolve7(__dirname5, "managed", ...segments);
19892
+ const sourcePath = resolve7(__dirname5, "..", "managed", ...segments);
19202
19893
  const candidates = [bundledPath, sourcePath];
19203
- return candidates.find((candidate) => existsSync22(candidate)) ?? bundledPath;
19894
+ return candidates.find((candidate) => existsSync23(candidate)) ?? bundledPath;
19204
19895
  }
19205
19896
  function resolvePackagedOpenCodeAgentsDir2() {
19206
19897
  const candidates = [
19207
- resolve6(__dirname4, ".opencode", "agents"),
19208
- resolve6(__dirname4, "..", ".opencode", "agents"),
19209
- resolve6(__dirname4, "..", "..", ".opencode", "agents")
19898
+ resolve7(__dirname5, ".opencode", "agents"),
19899
+ resolve7(__dirname5, "..", ".opencode", "agents"),
19900
+ resolve7(__dirname5, "..", "..", ".opencode", "agents")
19210
19901
  ];
19211
- return candidates.find((candidate) => existsSync22(candidate)) ?? candidates[0];
19902
+ return candidates.find((candidate) => existsSync23(candidate)) ?? candidates[0];
19212
19903
  }
19213
19904
  function listPackagedOpenCodeAgentFiles2() {
19214
19905
  const agentsDir = resolvePackagedOpenCodeAgentsDir2();
19215
19906
  try {
19216
- return readdirSync6(agentsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => resolve6(agentsDir, entry.name));
19907
+ return readdirSync6(agentsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => resolve7(agentsDir, entry.name));
19217
19908
  } catch {
19218
19909
  return [];
19219
19910
  }
@@ -19247,7 +19938,7 @@ function resolvePackagedManagedPromptPath(promptName) {
19247
19938
  }
19248
19939
  async function readPackagedTextFile2(filePath) {
19249
19940
  try {
19250
- return await readFile8(filePath, "utf-8");
19941
+ return await readFile9(filePath, "utf-8");
19251
19942
  } catch {
19252
19943
  return null;
19253
19944
  }
@@ -19306,7 +19997,7 @@ async function walkDir3(dir) {
19306
19997
  try {
19307
19998
  const entries = await readdir5(dir, { withFileTypes: true });
19308
19999
  for (const entry of entries) {
19309
- const full = resolve6(dir, entry.name);
20000
+ const full = resolve7(dir, entry.name);
19310
20001
  if (entry.isDirectory()) {
19311
20002
  files.push(...await walkDir3(full));
19312
20003
  } else {
@@ -19324,20 +20015,20 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
19324
20015
  if (sourceContent === null) {
19325
20016
  return false;
19326
20017
  }
19327
- const targetPath = resolve6(cwd, targetRelativePath);
20018
+ const targetPath = resolve7(cwd, targetRelativePath);
19328
20019
  const escapeError = checkForPathEscape(cwd, targetPath);
19329
20020
  if (escapeError) {
19330
20021
  errors.push(escapeError);
19331
20022
  return false;
19332
20023
  }
19333
- await mkdir7(resolve6(targetPath, ".."), { recursive: true });
19334
- if (existsSync22(targetPath)) {
19335
- const existing = await readFile8(targetPath, "utf-8");
20024
+ await mkdir7(resolve7(targetPath, ".."), { recursive: true });
20025
+ if (existsSync23(targetPath)) {
20026
+ const existing = await readFile9(targetPath, "utf-8");
19336
20027
  if (existing === sourceContent) {
19337
20028
  return false;
19338
20029
  }
19339
20030
  }
19340
- await writeFile4(targetPath, sourceContent, "utf-8");
20031
+ await writeFile5(targetPath, sourceContent, "utf-8");
19341
20032
  return true;
19342
20033
  }
19343
20034
  function walkDirSync(dir) {
@@ -19345,7 +20036,7 @@ function walkDirSync(dir) {
19345
20036
  try {
19346
20037
  const entries = readdirSync6(dir, { withFileTypes: true });
19347
20038
  for (const entry of entries) {
19348
- const full = resolve6(dir, entry.name);
20039
+ const full = resolve7(dir, entry.name);
19349
20040
  if (entry.isDirectory()) {
19350
20041
  result.push(...walkDirSync(full));
19351
20042
  } else {
@@ -19357,8 +20048,8 @@ function walkDirSync(dir) {
19357
20048
  return result;
19358
20049
  }
19359
20050
  async function detectOverrides(cwd) {
19360
- const overridesDir = resolve6(cwd, OVERRIDES_DIR);
19361
- if (!existsSync22(overridesDir)) {
20051
+ const overridesDir = resolve7(cwd, OVERRIDES_DIR);
20052
+ if (!existsSync23(overridesDir)) {
19362
20053
  return [];
19363
20054
  }
19364
20055
  const files = await walkDir3(overridesDir);
@@ -19367,14 +20058,14 @@ async function detectOverrides(cwd) {
19367
20058
  async function detectProjectOwnedFiles(cwd) {
19368
20059
  const found = [];
19369
20060
  for (const ownedPath of PROJECT_OWNED_PATHS) {
19370
- const fullPath = resolve6(cwd, ownedPath);
19371
- if (existsSync22(fullPath)) {
20061
+ const fullPath = resolve7(cwd, ownedPath);
20062
+ if (existsSync23(fullPath)) {
19372
20063
  found.push(ownedPath);
19373
20064
  }
19374
20065
  }
19375
20066
  for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
19376
- const fullDir = resolve6(cwd, ownedDir);
19377
- if (existsSync22(fullDir)) {
20067
+ const fullDir = resolve7(cwd, ownedDir);
20068
+ if (existsSync23(fullDir)) {
19378
20069
  const files = await walkDir3(fullDir);
19379
20070
  for (const file of files) {
19380
20071
  found.push(relative5(cwd, file));
@@ -19385,14 +20076,35 @@ async function detectProjectOwnedFiles(cwd) {
19385
20076
  }
19386
20077
  async function isReleaseWorkflowEnabled(cwd) {
19387
20078
  try {
19388
- const configPath = resolve6(cwd, ".pourkit", "config.json");
19389
- const content = await readFile8(configPath, "utf-8");
20079
+ const configPath = resolve7(cwd, ".pourkit", "config.json");
20080
+ const content = await readFile9(configPath, "utf-8");
19390
20081
  const parsed = JSON.parse(content);
19391
20082
  return parsed.releaseWorkflow?.enabled === true;
19392
20083
  } catch {
19393
20084
  return false;
19394
20085
  }
19395
20086
  }
20087
+ async function readConfiguredMemory(cwd) {
20088
+ const configPath = resolve7(cwd, ".pourkit", "config.json");
20089
+ try {
20090
+ const config = await loadConfig(configPath);
20091
+ if (config.memory?.enabled === true && config.memory?.provider === "icm") {
20092
+ return config.memory;
20093
+ }
20094
+ } catch {
20095
+ }
20096
+ try {
20097
+ const content = await readFile9(configPath, "utf-8");
20098
+ const parsed = JSON.parse(content);
20099
+ const memory = parsed.memory;
20100
+ if (typeof memory === "object" && memory !== null && !Array.isArray(memory) && memory.enabled === true && memory.provider === "icm") {
20101
+ return { enabled: true, provider: "icm" };
20102
+ }
20103
+ } catch {
20104
+ return void 0;
20105
+ }
20106
+ return void 0;
20107
+ }
19396
20108
  async function runWorkflowPackSyncCommand(options) {
19397
20109
  const cwd = options.cwd ?? process.cwd();
19398
20110
  const catalog = getWorkflowPackCatalog();
@@ -19401,10 +20113,10 @@ async function runWorkflowPackSyncCommand(options) {
19401
20113
  const skippedProjectOwned = [];
19402
20114
  const overrides = [];
19403
20115
  const errors = [];
19404
- const manifestPath = resolve6(cwd, ".pourkit", "manifest.json");
20116
+ const manifestPath = resolve7(cwd, ".pourkit", "manifest.json");
19405
20117
  let manifestReadFailed = false;
19406
20118
  let manifest = {};
19407
- const existingRawManifest = await readFile8(manifestPath, "utf-8").catch(
20119
+ const existingRawManifest = await readFile9(manifestPath, "utf-8").catch(
19408
20120
  () => null
19409
20121
  );
19410
20122
  if (existingRawManifest) {
@@ -19534,28 +20246,20 @@ async function runWorkflowPackSyncCommand(options) {
19534
20246
  });
19535
20247
  }
19536
20248
  const schemaResult = await runConfigSyncSchemaCommand({ cwd });
19537
- let configMemory;
19538
- try {
19539
- const configPath = resolve6(cwd, ".pourkit", "config.json");
19540
- const config = await loadConfig(configPath);
19541
- if (config.memory?.enabled === true && config.memory?.provider === "icm") {
19542
- configMemory = config.memory;
19543
- }
19544
- } catch {
19545
- }
20249
+ const configMemory = await readConfiguredMemory(cwd);
19546
20250
  const managedBlockContent = generateManagedBlockContent(configMemory);
19547
20251
  const fullManagedBlock = buildManagedBlock(managedBlockContent);
19548
20252
  async function syncManagedAgentFile(fileName, options2) {
19549
- const filePath = resolve6(cwd, fileName);
19550
- if (!existsSync22(filePath)) {
20253
+ const filePath = resolve7(cwd, fileName);
20254
+ if (!existsSync23(filePath)) {
19551
20255
  if (!options2.createIfMissing) return false;
19552
- await writeFile4(filePath, fullManagedBlock, "utf-8");
20256
+ await writeFile5(filePath, fullManagedBlock, "utf-8");
19553
20257
  return true;
19554
20258
  }
19555
- const existing = await readFile8(filePath, "utf-8");
20259
+ const existing = await readFile9(filePath, "utf-8");
19556
20260
  const newContent = upsertManagedBlock(existing, managedBlockContent);
19557
20261
  if (newContent !== existing) {
19558
- await writeFile4(filePath, newContent, "utf-8");
20262
+ await writeFile5(filePath, newContent, "utf-8");
19559
20263
  return true;
19560
20264
  }
19561
20265
  return false;
@@ -19590,8 +20294,8 @@ async function runWorkflowPackSyncCommand(options) {
19590
20294
  errors.push("Failed to write manifest metadata");
19591
20295
  } else {
19592
20296
  try {
19593
- await mkdir7(resolve6(manifestPath, ".."), { recursive: true });
19594
- await writeFile4(
20297
+ await mkdir7(resolve7(manifestPath, ".."), { recursive: true });
20298
+ await writeFile5(
19595
20299
  manifestPath,
19596
20300
  JSON.stringify(manifest, null, 2) + "\n",
19597
20301
  "utf-8"
@@ -19631,7 +20335,7 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19631
20335
  const sourceContent = await readPackagedTextFile2(sourceFile);
19632
20336
  const fileName = basename2(sourceFile);
19633
20337
  const targetRelativePath = `.opencode/agents/${fileName}`;
19634
- const targetPath = resolve6(cwd, targetRelativePath);
20338
+ const targetPath = resolve7(cwd, targetRelativePath);
19635
20339
  const escapeError = checkForPathEscape(cwd, targetPath);
19636
20340
  if (escapeError) {
19637
20341
  errors.push(escapeError);
@@ -19645,14 +20349,14 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19645
20349
  ownership: "managed",
19646
20350
  sha256: sha256Text(sourceContent)
19647
20351
  };
19648
- if (!existsSync22(targetPath)) {
19649
- await mkdir7(resolve6(targetPath, ".."), { recursive: true });
19650
- await writeFile4(targetPath, sourceContent, "utf-8");
20352
+ if (!existsSync23(targetPath)) {
20353
+ await mkdir7(resolve7(targetPath, ".."), { recursive: true });
20354
+ await writeFile5(targetPath, sourceContent, "utf-8");
19651
20355
  updated.push(targetRelativePath);
19652
20356
  manifestState.manifestAssetUpdates[targetRelativePath] = nextAsset;
19653
20357
  return;
19654
20358
  }
19655
- const existingContent = await readFile8(targetPath, "utf-8");
20359
+ const existingContent = await readFile9(targetPath, "utf-8");
19656
20360
  const existingAsset = manifestState.existingManifestAssets[targetRelativePath];
19657
20361
  const isManifestManaged = existingAsset?.ownership === "managed";
19658
20362
  if (existingContent === sourceContent) {
@@ -19665,7 +20369,7 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19665
20369
  }
19666
20370
  return;
19667
20371
  }
19668
- await writeFile4(targetPath, sourceContent, "utf-8");
20372
+ await writeFile5(targetPath, sourceContent, "utf-8");
19669
20373
  updated.push(targetRelativePath);
19670
20374
  manifestState.manifestAssetUpdates[targetRelativePath] = nextAsset;
19671
20375
  }
@@ -20459,7 +21163,7 @@ function formatChecks2(checks) {
20459
21163
  init_common();
20460
21164
 
20461
21165
  // execution/sandcastle-execution.ts
20462
- import { existsSync as existsSync24, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
21166
+ import { existsSync as existsSync25, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
20463
21167
  import { join as join28 } from "path";
20464
21168
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
20465
21169
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
@@ -20467,14 +21171,14 @@ import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
20467
21171
  // execution/execution-provider.ts
20468
21172
  init_common();
20469
21173
  import { mkdtempSync } from "fs";
20470
- import { writeFile as writeFile5 } from "fs/promises";
21174
+ import { writeFile as writeFile6 } from "fs/promises";
20471
21175
  import { tmpdir } from "os";
20472
- import { dirname as dirname8, join as join26 } from "path";
21176
+ import { dirname as dirname9, join as join26 } from "path";
20473
21177
  async function writeExecutionArtifacts(worktreePath, artifacts) {
20474
21178
  for (const artifact of artifacts) {
20475
21179
  const filePath = join26(worktreePath, artifact.path);
20476
- await ensureDir(dirname8(filePath));
20477
- await writeFile5(filePath, artifact.content, "utf-8");
21180
+ await ensureDir(dirname9(filePath));
21181
+ await writeFile6(filePath, artifact.content, "utf-8");
20478
21182
  }
20479
21183
  }
20480
21184
 
@@ -20487,18 +21191,18 @@ import path7 from "path";
20487
21191
 
20488
21192
  // execution/sandbox-image.ts
20489
21193
  import { createHash as createHash3 } from "crypto";
20490
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
21194
+ import { existsSync as existsSync24, readFileSync as readFileSync22 } from "fs";
20491
21195
  import path6 from "path";
20492
21196
  function sandboxImageName(repoRoot2, installIcm) {
20493
21197
  const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
20494
21198
  const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
20495
21199
  const baseName = sanitized || "local";
20496
21200
  const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
20497
- if (!existsSync23(dockerfilePath)) {
21201
+ if (!existsSync24(dockerfilePath)) {
20498
21202
  const base2 = `sandcastle:${baseName}`;
20499
21203
  return installIcm ? `${base2}-icm` : base2;
20500
21204
  }
20501
- const fingerprint = createHash3("sha256").update(readFileSync21(dockerfilePath)).digest("hex").slice(0, 8);
21205
+ const fingerprint = createHash3("sha256").update(readFileSync22(dockerfilePath)).digest("hex").slice(0, 8);
20502
21206
  const base = `sandcastle:${baseName}-${fingerprint}`;
20503
21207
  return installIcm ? `${base}-icm` : base;
20504
21208
  }
@@ -20683,7 +21387,7 @@ var SandcastleExecutionSession = class {
20683
21387
  );
20684
21388
  }
20685
21389
  const hostMemoryDir = join28(repoRoot2, ".pourkit", "icm");
20686
- if (!existsSync24(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
21390
+ if (!existsSync25(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
20687
21391
  throw new MissingHostMemoryDirError(
20688
21392
  "Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
20689
21393
  );
@@ -20848,7 +21552,7 @@ function sanitizeBranch(branchName) {
20848
21552
  function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
20849
21553
  return paths.filter((relativePath) => {
20850
21554
  const source = join28(repoRoot2, relativePath);
20851
- if (!existsSync24(source)) {
21555
+ if (!existsSync25(source)) {
20852
21556
  return true;
20853
21557
  }
20854
21558
  try {
@@ -20859,7 +21563,7 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
20859
21563
  return true;
20860
21564
  }
20861
21565
  const destination = join28(worktreePath, relativePath);
20862
- return !existsSync24(destination);
21566
+ return !existsSync25(destination);
20863
21567
  });
20864
21568
  }
20865
21569
  function formatAgentStreamEvent(event) {
@@ -21555,6 +22259,24 @@ function createCliProgram(version) {
21555
22259
  console.log("Schema assets are up to date.");
21556
22260
  }
21557
22261
  });
22262
+ configCommand.command("migrate").description("Migrate config to the current schema version").option("--cwd <path>", "target repository directory").option("--dry-run", "show planned changes without writing").option("--json", "output machine-readable JSON").action(
22263
+ async (options) => {
22264
+ const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
22265
+ const result = await runConfigMigrateCommand({
22266
+ cwd: targetRepoRoot,
22267
+ dryRun: options.dryRun ?? false,
22268
+ json: options.json ?? false
22269
+ });
22270
+ if (options.json) {
22271
+ console.log(renderConfigMigrateResultJson(result));
22272
+ } else {
22273
+ console.log(renderConfigMigrateResult(result));
22274
+ }
22275
+ if (result.status === "blocked") {
22276
+ process.exitCode = 1;
22277
+ }
22278
+ }
22279
+ );
21558
22280
  const syncCommand = program.command("sync").description("Sync commands for refreshing managed assets");
21559
22281
  syncCommand.command("workflow-pack").description("Refresh managed workflow pack assets from packaged sources").option("--cwd <path>", "target repository directory").action(async (options) => {
21560
22282
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
@@ -21712,11 +22434,11 @@ function createCliProgram(version) {
21712
22434
  return program;
21713
22435
  }
21714
22436
  async function resolveCliVersion() {
21715
- if (isPackageVersion("0.0.0-next-20260715085442")) {
21716
- return "0.0.0-next-20260715085442";
22437
+ if (isPackageVersion("0.0.0-next-20260723092204")) {
22438
+ return "0.0.0-next-20260723092204";
21717
22439
  }
21718
- if (isReleaseVersion("0.0.0-next-20260715085442")) {
21719
- return "0.0.0-next-20260715085442";
22440
+ if (isReleaseVersion("0.0.0-next-20260723092204")) {
22441
+ return "0.0.0-next-20260723092204";
21720
22442
  }
21721
22443
  try {
21722
22444
  const root = repoRoot();