@pourkit/cli 0.0.0-next-20260714073403 → 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;
@@ -370,7 +370,11 @@ function applyOutputRetriesDefaults(retries) {
370
370
  }
371
371
  function applyStageAgentDefaults(agent) {
372
372
  return {
373
- ...agent,
373
+ agent: agent.agent,
374
+ model: agent.model,
375
+ variant: agent.variant,
376
+ env: agent.env,
377
+ promptTemplate: agent.promptTemplate,
374
378
  outputRetries: applyOutputRetriesDefaults(agent.outputRetries)
375
379
  };
376
380
  }
@@ -432,6 +436,10 @@ function validateConfigSemantics(input) {
432
436
  target.strategy.issueFinalReview,
433
437
  `targets[${targetIndex}].strategy.issueFinalReview`
434
438
  );
439
+ assertStageAgentPath(
440
+ target.strategy.postCompletionFinalReview,
441
+ `targets[${targetIndex}].strategy.postCompletionFinalReview`
442
+ );
435
443
  assertStageAgentPath(
436
444
  target.strategy.finalize.prDescriptionAgent,
437
445
  `targets[${targetIndex}].strategy.finalize.prDescriptionAgent`
@@ -506,6 +514,9 @@ function normalizePourkitConfig(input) {
506
514
  ),
507
515
  maxAttempts: t.strategy.issueFinalReview.maxAttempts
508
516
  },
517
+ postCompletionFinalReview: applyStageAgentDefaults(
518
+ t.strategy.postCompletionFinalReview
519
+ ),
509
520
  finalize: {
510
521
  prDescriptionAgent: applyStageAgentDefaults(
511
522
  t.strategy.finalize.prDescriptionAgent
@@ -612,6 +623,16 @@ function getValidator() {
612
623
  }
613
624
  return _validate;
614
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
+ }
615
636
  function resolveMissingOrEmptyOutputRetries(config) {
616
637
  return config?.outputRetries?.missingOrEmpty ?? DEFAULT_MISSING_OR_EMPTY_OUTPUT_RETRIES;
617
638
  }
@@ -881,11 +902,11 @@ var OBSOLETE_CONFIG_PATHS = [
881
902
  ];
882
903
  var CANONICAL_CONFIG_PATH = ".pourkit/config.json";
883
904
  async function loadRepoConfig(repoRoot2, _configFileName) {
884
- const { existsSync: existsSync25 } = await import("fs");
905
+ const { existsSync: existsSync26 } = await import("fs");
885
906
  const { join: pjoin } = await import("path");
886
907
  for (const obPath of OBSOLETE_CONFIG_PATHS) {
887
908
  const fullPath = pjoin(repoRoot2, obPath);
888
- if (existsSync25(fullPath)) {
909
+ if (existsSync26(fullPath)) {
889
910
  const isRootJson = obPath === "pourkit.json";
890
911
  throw new Error(
891
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".`
@@ -893,13 +914,13 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
893
914
  }
894
915
  }
895
916
  const configPath = pjoin(repoRoot2, CANONICAL_CONFIG_PATH);
896
- if (!existsSync25(configPath)) {
917
+ if (!existsSync26(configPath)) {
897
918
  throw new Error(
898
919
  `No Pourkit config found at ${CANONICAL_CONFIG_PATH}. Run pourkit init or create ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
899
920
  );
900
921
  }
901
- const { readFile: readFile9 } = await import("fs/promises");
902
- const raw = await readFile9(configPath, "utf-8");
922
+ const { readFile: readFile10 } = await import("fs/promises");
923
+ const raw = await readFile10(configPath, "utf-8");
903
924
  let parsed;
904
925
  try {
905
926
  parsed = JSON.parse(raw);
@@ -910,10 +931,10 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
910
931
  return parseConfig(parsed);
911
932
  }
912
933
  async function loadConfig(configPath) {
913
- const { readFile: readFile9 } = await import("fs/promises");
934
+ const { readFile: readFile10 } = await import("fs/promises");
914
935
  const ext = configPath.split(".").pop()?.toLowerCase();
915
936
  if (ext === "json") {
916
- const raw = await readFile9(configPath, "utf-8");
937
+ const raw = await readFile10(configPath, "utf-8");
917
938
  return parseConfig(JSON.parse(raw));
918
939
  }
919
940
  if (ext === "mjs" || ext === "js" || ext === "ts") {
@@ -4257,6 +4278,13 @@ var POST_COMPLETION_FINAL_REVIEW_ARTIFACT_PATH = join9(
4257
4278
  "post-completion-final-review",
4258
4279
  "agent-output.json"
4259
4280
  );
4281
+ function resolvePostCompletionFinalReviewAgent(target) {
4282
+ const configured = target.strategy.postCompletionFinalReview;
4283
+ if (configured) return configured;
4284
+ throw new Error(
4285
+ "Post-Completion Final Review requires targets[].strategy.postCompletionFinalReview config."
4286
+ );
4287
+ }
4260
4288
  async function runPostCompletionFinalReview(options) {
4261
4289
  const {
4262
4290
  executionProvider,
@@ -4308,9 +4336,10 @@ async function runPostCompletionFinalReview(options) {
4308
4336
  };
4309
4337
  }
4310
4338
  const strategy = target.strategy;
4311
- const agent = strategy.issueFinalReview;
4339
+ const agent = resolvePostCompletionFinalReviewAgent(target);
4312
4340
  const prompt = loadPostCompletionFinalReviewPrompt({
4313
4341
  repoRoot: repoRoot2,
4342
+ promptTemplate: agent.promptTemplate,
4314
4343
  sourceDocumentPath,
4315
4344
  completedBranch,
4316
4345
  validationCommand: `pourkit validate-artifact post-completion-final-review ${artifactPathInWorktree}`,
@@ -4452,11 +4481,14 @@ Completed branch: ${completedBranch}`,
4452
4481
  );
4453
4482
  }
4454
4483
  function loadPostCompletionFinalReviewPrompt(options) {
4455
- const { repoRoot: repoRoot2, sourceDocumentPath, completedBranch, validationCommand } = options;
4456
- const promptPath = resolvePromptTemplatePath(
4457
- repoRoot2,
4458
- "post-completion-final-review.prompt.md"
4459
- );
4484
+ const {
4485
+ repoRoot: repoRoot2,
4486
+ promptTemplate,
4487
+ sourceDocumentPath,
4488
+ completedBranch,
4489
+ validationCommand
4490
+ } = options;
4491
+ const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
4460
4492
  const promptBody = existsSync9(promptPath) ? readFileSync8(promptPath, "utf-8") : `Review the completed branch against the source document.
4461
4493
 
4462
4494
  ## Source Document
@@ -6576,7 +6608,7 @@ async function canReachMcp(url) {
6576
6608
  return true;
6577
6609
  } catch {
6578
6610
  if (attempt < 9) {
6579
- await new Promise((resolve7) => setTimeout(resolve7, 100));
6611
+ await new Promise((resolve8) => setTimeout(resolve8, 100));
6580
6612
  }
6581
6613
  }
6582
6614
  }
@@ -8617,7 +8649,10 @@ async function runIssueLifecycle(options) {
8617
8649
  startResult,
8618
8650
  reviewArtifactPath
8619
8651
  });
8620
- if (result.publicationStatus === "merged" && !startResult.parentPrdIssue) {
8652
+ const isPrdChildIssue = Boolean(
8653
+ startResult.parentPrdIssue || options.runOptions.beadsIssueRunContext
8654
+ );
8655
+ if (result.publicationStatus === "merged" && !isPrdChildIssue) {
8621
8656
  await options.deps.runOneshotPostCompletionFinalReview({
8622
8657
  issueNumber: options.issueNumber,
8623
8658
  repoRoot: options.repoRoot,
@@ -10556,7 +10591,7 @@ async function resolveGithubProjectionState(options) {
10556
10591
  expectedKind: "parent",
10557
10592
  prdRef: options.workspace.prdRef,
10558
10593
  githubIssueNumber: parentGithubIssueNumber,
10559
- allowMissingCanonicalMatch: parentGithubIssueNumber === void 0 && options.currentState.parent === null,
10594
+ allowMissingCanonicalMatch: parentGithubIssueNumber === void 0,
10560
10595
  affected: [
10561
10596
  {
10562
10597
  kind: "prd",
@@ -10578,7 +10613,7 @@ async function resolveGithubProjectionState(options) {
10578
10613
  expectedKind: "child",
10579
10614
  prdRef: options.workspace.prdRef,
10580
10615
  githubIssueNumber,
10581
- allowMissingCanonicalMatch: action?.type === "create_child" && githubIssueNumber === void 0,
10616
+ allowMissingCanonicalMatch: (action?.type === "create_child" || action?.type === "update_child") && githubIssueNumber === void 0,
10582
10617
  affected: [
10583
10618
  {
10584
10619
  kind: "child",
@@ -10803,7 +10838,7 @@ async function executeGithubProjectionActions(options) {
10803
10838
  child.childRef
10804
10839
  ),
10805
10840
  githubIssueNumber: action?.type === "update_child" ? action.githubIssueNumber : void 0,
10806
- allowMissingCanonicalMatch: action?.type !== "update_child"
10841
+ allowMissingCanonicalMatch: action?.type !== "update_child" || action.githubIssueNumber === void 0
10807
10842
  });
10808
10843
  children.push({ childRef: child.childRef, githubIssue });
10809
10844
  }
@@ -11090,10 +11125,11 @@ async function runPrdPlanWorkspacePublishCommand(options) {
11090
11125
 
11091
11126
  // commands/prd-run.ts
11092
11127
  import { lstatSync, realpathSync } from "fs";
11093
- import { join as join23 } from "path";
11128
+ import { join as join24 } from "path";
11094
11129
 
11095
11130
  // prd-run/coordinator.ts
11096
11131
  import { spawnSync as spawnSync2 } from "child_process";
11132
+ import { join as join23 } from "path";
11097
11133
  import { Match, pipe } from "effect";
11098
11134
  init_common();
11099
11135
 
@@ -11810,7 +11846,8 @@ function validatePrdRunTerminalEvidence(record, context) {
11810
11846
  humanReadableReason: `PRD Run ${context.prdRef} is missing proof that all child Issues finalized into ${context.prdBranch}.`,
11811
11847
  repairability: "operator-action",
11812
11848
  nextAction: "inspect-and-retry",
11813
- nextRecommendedCommand: "pourkit prd-run launch"
11849
+ nextRecommendedCommand: "pourkit prd-run launch",
11850
+ diagnostics: [...context.childFinalizationDiagnostics ?? []]
11814
11851
  })
11815
11852
  };
11816
11853
  }
@@ -12352,15 +12389,19 @@ function inspectRemotePrdBranch(repoRoot2, prdRef) {
12352
12389
  return { ok: true, exists: true, headSha };
12353
12390
  }
12354
12391
  function fetchPrdBranch(repoRoot2, prdRef) {
12355
- const result = spawnSync2("git", ["fetch", "origin", prdRef], {
12356
- cwd: repoRoot2,
12357
- encoding: "utf8"
12358
- });
12392
+ const result = spawnSync2(
12393
+ "git",
12394
+ ["fetch", "origin", `${prdRef}:refs/remotes/origin/${prdRef}`],
12395
+ {
12396
+ cwd: repoRoot2,
12397
+ encoding: "utf8"
12398
+ }
12399
+ );
12359
12400
  if (result.status !== 0) {
12360
12401
  return {
12361
12402
  ok: false,
12362
12403
  gate: "git",
12363
- reason: `PRD Run start failed while fetching origin/${prdRef}.`,
12404
+ reason: `PRD Run failed while fetching origin/${prdRef}.`,
12364
12405
  diagnostics: [
12365
12406
  result.error instanceof Error ? result.error.message : void 0,
12366
12407
  result.stderr?.toString?.() ?? String(result.stderr ?? ""),
@@ -12371,6 +12412,65 @@ function fetchPrdBranch(repoRoot2, prdRef) {
12371
12412
  }
12372
12413
  return { ok: true };
12373
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
+ }
12374
12474
  async function ensurePrdBranchPublished(repoRoot2, prdRef, startBaseCommit) {
12375
12475
  const pushResult = spawnSync2(
12376
12476
  "git",
@@ -13005,7 +13105,27 @@ async function prdParentIssueIsClosed(options) {
13005
13105
  }
13006
13106
  }
13007
13107
  async function reconcileBlockedBeadsIssueProjection(options) {
13008
- 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
+ }
13009
13129
  if (!issue.labels.includes(config.labels.blocked)) {
13010
13130
  return issue;
13011
13131
  }
@@ -13065,6 +13185,16 @@ async function reconcileBlockedBeadsIssueProjection(options) {
13065
13185
  }
13066
13186
  return refreshed;
13067
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
+ }
13068
13198
  async function releaseSkippedBlockedBeadsChildren(options) {
13069
13199
  for (const child of options.children) {
13070
13200
  let result;
@@ -13091,6 +13221,19 @@ async function releaseSkippedBlockedBeadsChildren(options) {
13091
13221
  }
13092
13222
  }
13093
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
+ }
13094
13237
  function extractIssueFromQueueError(error) {
13095
13238
  if (!error || typeof error !== "object" || !("issue" in error)) {
13096
13239
  return void 0;
@@ -13239,6 +13382,105 @@ async function selectPrdRunCoordinatorMode(options) {
13239
13382
  })
13240
13383
  };
13241
13384
  }
13385
+ function escapeRegExp3(value) {
13386
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13387
+ }
13388
+ function normalizeChildRef2(value) {
13389
+ const match = value.match(/^I-0*(\d+)$/i);
13390
+ if (!match) return null;
13391
+ return `I-${match[1].padStart(2, "0")}`;
13392
+ }
13393
+ function issueChildRef(prdRef, issue) {
13394
+ const match = issue.title.match(
13395
+ new RegExp(`^${escapeRegExp3(prdRef)}\\s*/\\s*(I-0*\\d+)\\s*:`, "i")
13396
+ );
13397
+ return match ? normalizeChildRef2(match[1]) : null;
13398
+ }
13399
+ function issueSummary(issue) {
13400
+ return `#${issue.number} ${issue.title}`;
13401
+ }
13402
+ async function readPlannedChildRefs(options) {
13403
+ const sourceDocument = await resolvePrdPostCompletionSourceDocument(options);
13404
+ if (!sourceDocument || sourceDocument.status !== "resolved") {
13405
+ return {
13406
+ ok: false,
13407
+ diagnostics: sourceDocument && sourceDocument.status === "blocked" ? [...sourceDocument.diagnostics] : [
13408
+ sourceDocument?.reason ?? `Unable to resolve source document for ${options.prdRef}.`
13409
+ ]
13410
+ };
13411
+ }
13412
+ const parsed = await parsePrdPlanWorkspace({
13413
+ workspacePath: join23(options.repoRoot, sourceDocument.workspacePath),
13414
+ prdRef: options.prdRef
13415
+ });
13416
+ if (!parsed.ok) {
13417
+ return {
13418
+ ok: false,
13419
+ diagnostics: parsed.diagnostics.map((diagnostic) => diagnostic.message)
13420
+ };
13421
+ }
13422
+ return {
13423
+ ok: true,
13424
+ childRefs: parsed.workspace.children.map((child) => child.childRef)
13425
+ };
13426
+ }
13427
+ async function verifyPrdChildFinalizationEvidence(options) {
13428
+ const diagnostics = [];
13429
+ const unfinalizedProcessed = options.processedResults.filter(
13430
+ (result) => !result.noOp && result.publicationStatus !== "merged"
13431
+ );
13432
+ if (unfinalizedProcessed.length > 0) {
13433
+ diagnostics.push(
13434
+ `Processed child Issues missing PRD Branch merge/no-op evidence: ${unfinalizedProcessed.map((result) => `#${result.issueNumber}`).join(", ")}.`
13435
+ );
13436
+ }
13437
+ let relatedIssues;
13438
+ try {
13439
+ relatedIssues = await options.issueProvider.listRelatedIssues(
13440
+ options.prdRef
13441
+ );
13442
+ } catch (error) {
13443
+ return {
13444
+ ok: false,
13445
+ diagnostics: [
13446
+ `Failed to list child Issues for ${options.prdRef}: ${error instanceof Error ? error.message : String(error)}`
13447
+ ]
13448
+ };
13449
+ }
13450
+ if (relatedIssues.length === 0) {
13451
+ diagnostics.push(`No child Issue projections found for ${options.prdRef}.`);
13452
+ }
13453
+ const planned = await readPlannedChildRefs({
13454
+ repoRoot: options.repoRoot,
13455
+ prdRef: options.prdRef
13456
+ });
13457
+ if (!planned.ok) {
13458
+ diagnostics.push(...planned.diagnostics);
13459
+ } else if (planned.childRefs.length === 0) {
13460
+ diagnostics.push(`No planned child Issues found for ${options.prdRef}.`);
13461
+ } else {
13462
+ const relatedRefs = new Set(
13463
+ relatedIssues.map((issue) => issueChildRef(options.prdRef, issue)).filter((ref) => ref !== null)
13464
+ );
13465
+ const missingRefs = planned.childRefs.filter(
13466
+ (ref) => !relatedRefs.has(ref)
13467
+ );
13468
+ if (missingRefs.length > 0) {
13469
+ diagnostics.push(
13470
+ `Planned child Issues without published Issue projections: ${missingRefs.join(", ")}.`
13471
+ );
13472
+ }
13473
+ }
13474
+ const openRelatedIssues = relatedIssues.filter(
13475
+ (issue) => issue.state !== "closed"
13476
+ );
13477
+ if (openRelatedIssues.length > 0) {
13478
+ diagnostics.push(
13479
+ `Child Issues still open: ${openRelatedIssues.map(issueSummary).join(", ")}.`
13480
+ );
13481
+ }
13482
+ return { ok: diagnostics.length === 0, diagnostics };
13483
+ }
13242
13484
  async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFacts, startOutcome) {
13243
13485
  const receipt = record.postCompletionFinalReview;
13244
13486
  if (receipt?.status === "passed" || receipt?.status === "overridden") {
@@ -13309,6 +13551,70 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13309
13551
  options.config,
13310
13552
  record.targetName ?? options.targetName
13311
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
+ }
13312
13618
  const sourceDocResult = await resolvePrdPostCompletionSourceDocument({
13313
13619
  repoRoot: options.repoRoot,
13314
13620
  prdRef
@@ -13356,7 +13662,36 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13356
13662
  record.start
13357
13663
  );
13358
13664
  }
13359
- 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
+ }
13360
13695
  const sourceDocumentPath = sourceDocResult.relativePath;
13361
13696
  stateAdapter.write({
13362
13697
  status: "in_progress",
@@ -13452,7 +13787,6 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13452
13787
  resume: resumeFacts
13453
13788
  };
13454
13789
  }
13455
- const retouchBranch = `pourkit/post-completion/${prdRef}`;
13456
13790
  stateAdapter.write({ retouchBranch });
13457
13791
  const prDescriptionResult = await runEffectAndMapExit(
13458
13792
  runPostCompletionPrDescriptionAgent({
@@ -13798,17 +14132,25 @@ async function launchPrdRun(options) {
13798
14132
  logger: options.logger
13799
14133
  });
13800
14134
  }
13801
- if (currentRecord && startReceipt && activeRepairGuidance?.failureCode === "missing-child-finalization-evidence" && await prdParentIssueIsClosed({
13802
- issueProvider: options.issueProvider,
14135
+ const parentClosureIssueProvider = options.issueProvider;
14136
+ if (currentRecord && startReceipt && parentClosureIssueProvider && activeRepairGuidance?.failureCode === "missing-child-finalization-evidence" && await prdParentIssueIsClosed({
14137
+ issueProvider: parentClosureIssueProvider,
13803
14138
  prdRef,
13804
14139
  logger: options.logger
13805
14140
  })) {
13806
14141
  const prdBranch = startReceipt.prdBranch ?? prdRef;
14142
+ const childFinalizationEvidence = await verifyPrdChildFinalizationEvidence({
14143
+ repoRoot: options.repoRoot,
14144
+ prdRef,
14145
+ issueProvider: parentClosureIssueProvider,
14146
+ processedResults: []
14147
+ });
13807
14148
  const terminalEvidence = validatePrdRunTerminalEvidence(currentRecord, {
13808
14149
  prdRef,
13809
14150
  baseBranch: startReceipt.startBaseBranch,
13810
14151
  prdBranch,
13811
- allChildIssuesFinalizedIntoPrdBranch: true
14152
+ allChildIssuesFinalizedIntoPrdBranch: childFinalizationEvidence.ok,
14153
+ childFinalizationDiagnostics: childFinalizationEvidence.diagnostics
13812
14154
  });
13813
14155
  if (!terminalEvidence.ok) {
13814
14156
  return buildLaunchBlockedOutcome(
@@ -14042,11 +14384,22 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
14042
14384
  writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
14043
14385
  const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
14044
14386
  if (drainRecord) {
14387
+ const childFinalizationEvidence = await verifyPrdChildFinalizationEvidence({
14388
+ repoRoot: options.repoRoot,
14389
+ prdRef,
14390
+ issueProvider,
14391
+ processedResults: outcome.results.map((result2) => ({
14392
+ issueNumber: result2.selected.number,
14393
+ noOp: result2.runResult.noOp,
14394
+ publicationStatus: result2.runResult.publicationStatus
14395
+ }))
14396
+ });
14045
14397
  const drainContext = {
14046
14398
  prdRef,
14047
14399
  baseBranch: startReceipt.startBaseBranch,
14048
14400
  prdBranch,
14049
- allChildIssuesFinalizedIntoPrdBranch: outcome.allProcessedIssuesFinalizedIntoPrdBranch === true
14401
+ allChildIssuesFinalizedIntoPrdBranch: childFinalizationEvidence.ok,
14402
+ childFinalizationDiagnostics: childFinalizationEvidence.diagnostics
14050
14403
  };
14051
14404
  const terminalEvidence = validatePrdRunTerminalEvidence(
14052
14405
  drainRecord,
@@ -14517,7 +14870,13 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14517
14870
  issueProvider,
14518
14871
  config: options.config,
14519
14872
  prdRef,
14520
- logger
14873
+ logger,
14874
+ ...resumeBeadsContext ? {
14875
+ retryRepair: {
14876
+ guidance: originalGuidance,
14877
+ beadsChildId: resumeBeadsContext.childId
14878
+ }
14879
+ } : {}
14521
14880
  });
14522
14881
  if (fetchedIssue.labels.includes(options.config.labels.blocked)) {
14523
14882
  logCoordinatorStep(
@@ -14667,8 +15026,24 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14667
15026
  );
14668
15027
  }
14669
15028
  try {
14670
- const issue = await reconcileBlockedBeadsIssueProjection({
14671
- 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,
14672
15047
  issueProvider,
14673
15048
  config: options.config,
14674
15049
  prdRef,
@@ -14801,13 +15176,18 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14801
15176
  writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
14802
15177
  const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
14803
15178
  if (drainRecord) {
15179
+ const childFinalizationEvidence = await verifyPrdChildFinalizationEvidence({
15180
+ repoRoot: options.repoRoot,
15181
+ prdRef,
15182
+ issueProvider,
15183
+ processedResults
15184
+ });
14804
15185
  const drainContext = {
14805
15186
  prdRef,
14806
15187
  baseBranch: startReceipt.startBaseBranch,
14807
15188
  prdBranch,
14808
- allChildIssuesFinalizedIntoPrdBranch: processedResults.length > 0 && processedResults.every(
14809
- (r) => r.noOp || r.publicationStatus === "merged"
14810
- )
15189
+ allChildIssuesFinalizedIntoPrdBranch: childFinalizationEvidence.ok,
15190
+ childFinalizationDiagnostics: childFinalizationEvidence.diagnostics
14811
15191
  };
14812
15192
  const terminalEvidence = validatePrdRunTerminalEvidence(
14813
15193
  drainRecord,
@@ -15764,12 +16144,12 @@ icm --db .pourkit/icm/memories.db topics # list a
15764
16144
  var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
15765
16145
  var MANAGED_BLOCK_END = "<!-- END POURKIT MANAGED BLOCK -->";
15766
16146
  var MANAGED_BLOCK_PATTERN = new RegExp(
15767
- `${escapeRegExp3(MANAGED_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp3(
16147
+ `${escapeRegExp4(MANAGED_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp4(
15768
16148
  MANAGED_BLOCK_END
15769
16149
  )}[ \\t]*(?:\\r?\\n)?`,
15770
16150
  "g"
15771
16151
  );
15772
- function escapeRegExp3(value) {
16152
+ function escapeRegExp4(value) {
15773
16153
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15774
16154
  }
15775
16155
  function buildManagedBlock(content) {
@@ -15984,6 +16364,12 @@ function generateConfigTemplate(options) {
15984
16364
  promptTemplate: ".pourkit/managed/prompts/issue-final-review.prompt.md",
15985
16365
  maxAttempts: 3
15986
16366
  },
16367
+ postCompletionFinalReview: {
16368
+ agent: "pourkit-reviewer",
16369
+ model: "openai/gpt-5.5",
16370
+ variant: "max",
16371
+ promptTemplate: ".pourkit/managed/prompts/post-completion-final-review.prompt.md"
16372
+ },
15987
16373
  finalize: {
15988
16374
  prDescriptionAgent: {
15989
16375
  agent: "pourkit-pr-description",
@@ -16004,7 +16390,7 @@ function generateConfigTemplate(options) {
16004
16390
  };
16005
16391
  const config = {
16006
16392
  $schema: "./schema/pourkit.schema.json",
16007
- schemaVersion: 3,
16393
+ schemaVersion: getPackagedConfigSchemaVersion(),
16008
16394
  targets: [target],
16009
16395
  workflowPack: {
16010
16396
  version: "1.0.0",
@@ -17947,13 +18333,13 @@ Init applied: ${result.applied} operations applied, ${result.skipped} skipped.`
17947
18333
  // commands/memory-init.ts
17948
18334
  import { existsSync as existsSync20, mkdirSync as mkdirSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync5 } from "fs";
17949
18335
  import { execFile as execFile3 } from "child_process";
17950
- import { join as join24 } from "path";
18336
+ import { join as join25 } from "path";
17951
18337
  import { promisify as promisify3 } from "util";
17952
18338
  var execFileAsync3 = promisify3(execFile3);
17953
18339
  async function runMemoryInitCommand(options) {
17954
18340
  const cwd = options.cwd ?? process.cwd();
17955
18341
  const execCommand = options.execCommand ?? execFileAsync3;
17956
- const configPath = join24(cwd, ".pourkit", "config.json");
18342
+ const configPath = join25(cwd, ".pourkit", "config.json");
17957
18343
  if (!existsSync20(configPath)) {
17958
18344
  return {
17959
18345
  ok: false,
@@ -17985,8 +18371,8 @@ async function runMemoryInitCommand(options) {
17985
18371
  const next = { ...raw, memory };
17986
18372
  writeFileSync5(configPath, JSON.stringify(next, null, 2) + "\n");
17987
18373
  }
17988
- const memoryDir = join24(cwd, ".pourkit", "icm");
17989
- const memoryDbPath = join24(memoryDir, "memories.db");
18374
+ const memoryDir = join25(cwd, ".pourkit", "icm");
18375
+ const memoryDbPath = join25(memoryDir, "memories.db");
17990
18376
  mkdirSync8(memoryDir, { recursive: true });
17991
18377
  let dbInitialized = false;
17992
18378
  let warning;
@@ -18000,7 +18386,7 @@ async function runMemoryInitCommand(options) {
18000
18386
  } catch {
18001
18387
  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.";
18002
18388
  }
18003
- const gitignorePath = join24(cwd, ".gitignore");
18389
+ const gitignorePath = join25(cwd, ".gitignore");
18004
18390
  updateManagedBlockSync(gitignorePath, generateGitignoreBlock());
18005
18391
  const managed = await generateManagedAgentInstructions({
18006
18392
  sourceRoot: cwd,
@@ -18008,14 +18394,14 @@ async function runMemoryInitCommand(options) {
18008
18394
  });
18009
18395
  let updatedAgentFile = false;
18010
18396
  for (const fileName of ["AGENTS.md", "CLAUDE.md"]) {
18011
- const agentPath = join24(cwd, fileName);
18397
+ const agentPath = join25(cwd, fileName);
18012
18398
  if (existsSync20(agentPath)) {
18013
18399
  updateManagedBlockSync(agentPath, managed);
18014
18400
  updatedAgentFile = true;
18015
18401
  }
18016
18402
  }
18017
18403
  if (!updatedAgentFile) {
18018
- updateManagedBlockSync(join24(cwd, "AGENTS.md"), managed);
18404
+ updateManagedBlockSync(join25(cwd, "AGENTS.md"), managed);
18019
18405
  }
18020
18406
  return {
18021
18407
  ok: true,
@@ -18150,12 +18536,20 @@ async function runSerenaStatusCommand(options) {
18150
18536
  }
18151
18537
 
18152
18538
  // commands/config-schema.ts
18153
- import { readFileSync as readFileSync20, existsSync as existsSync21, readdirSync as readdirSync5 } from "fs";
18154
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile3, readdir as readdir4 } from "fs/promises";
18155
- 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";
18156
18550
  import { fileURLToPath as fileURLToPath3 } from "url";
18551
+ import { randomUUID as randomUUID2 } from "crypto";
18157
18552
  import Ajv2 from "ajv";
18158
- init_common();
18159
18553
  var __filename3 = fileURLToPath3(import.meta.url);
18160
18554
  var __dirname3 = dirname6(__filename3);
18161
18555
  function resolvePackagedAssetPath2(fileName) {
@@ -18163,16 +18557,454 @@ function resolvePackagedAssetPath2(fileName) {
18163
18557
  resolve5(__dirname3, "schema", fileName),
18164
18558
  resolve5(__dirname3, "../schema", fileName)
18165
18559
  ];
18166
- return candidates.find((candidate) => existsSync21(candidate)) ?? candidates[0];
18560
+ return candidates.find((c) => existsSync21(c)) ?? candidates[0];
18167
18561
  }
18168
18562
  var PACKAGED_SCHEMA_PATH = resolvePackagedAssetPath2("pourkit.schema.json");
18169
- 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");
18170
19002
  var _schemaValidator = null;
18171
19003
  var _schemaErrors = null;
18172
19004
  function getSchemaValidator() {
18173
19005
  if (!_schemaValidator) {
18174
- const schema = JSON.parse(readFileSync20(PACKAGED_SCHEMA_PATH, "utf-8"));
18175
- const ajv = new Ajv2({ strict: true });
19006
+ const schema = JSON.parse(readFileSync21(PACKAGED_SCHEMA_PATH2, "utf-8"));
19007
+ const ajv = new Ajv3({ strict: true });
18176
19008
  ajv.addKeyword("x-pourkit-schema-version");
18177
19009
  const validate = ajv.compile(schema);
18178
19010
  _schemaValidator = (data) => {
@@ -18185,41 +19017,41 @@ function getSchemaValidator() {
18185
19017
  return _schemaValidator;
18186
19018
  }
18187
19019
  function readPackagedHash() {
18188
- return readFileSync20(PACKAGED_HASH_PATH, "utf-8");
19020
+ return readFileSync21(PACKAGED_HASH_PATH, "utf-8");
18189
19021
  }
18190
19022
  function localSchemaDir(repoRoot2) {
18191
- return resolve5(repoRoot2, ".pourkit/schema");
19023
+ return resolve6(repoRoot2, ".pourkit/schema");
18192
19024
  }
18193
19025
  var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
18194
19026
  var MANAGED_BLOCK_BEGIN_MARKER = MANAGED_BLOCK_BEGIN2.trim();
18195
19027
  var MANAGED_BLOCK_END_MARKER = "<!-- END POURKIT MANAGED BLOCK -->";
18196
19028
  var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
18197
19029
  function resolvePackagedManagedPath(...segments) {
18198
- const bundledPath = resolve5(__dirname3, "managed", ...segments);
18199
- const sourcePath = resolve5(__dirname3, "..", "managed", ...segments);
19030
+ const bundledPath = resolve6(__dirname4, "managed", ...segments);
19031
+ const sourcePath = resolve6(__dirname4, "..", "managed", ...segments);
18200
19032
  const candidates = [bundledPath, sourcePath];
18201
- return candidates.find((candidate) => existsSync21(candidate)) ?? bundledPath;
19033
+ return candidates.find((candidate) => existsSync22(candidate)) ?? bundledPath;
18202
19034
  }
18203
19035
  function resolvePackagedOpenCodeAgentsDir() {
18204
19036
  const candidates = [
18205
- resolve5(__dirname3, ".opencode", "agents"),
18206
- resolve5(__dirname3, "..", ".opencode", "agents"),
18207
- resolve5(__dirname3, "..", "..", ".opencode", "agents")
19037
+ resolve6(__dirname4, ".opencode", "agents"),
19038
+ resolve6(__dirname4, "..", ".opencode", "agents"),
19039
+ resolve6(__dirname4, "..", "..", ".opencode", "agents")
18208
19040
  ];
18209
- return candidates.find((candidate) => existsSync21(candidate)) ?? candidates[0];
19041
+ return candidates.find((candidate) => existsSync22(candidate)) ?? candidates[0];
18210
19042
  }
18211
19043
  function listPackagedOpenCodeAgentFiles() {
18212
19044
  const agentsDir = resolvePackagedOpenCodeAgentsDir();
18213
19045
  try {
18214
- 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));
18215
19047
  } catch {
18216
19048
  return [];
18217
19049
  }
18218
19050
  }
18219
19051
  async function readManifestAssets(cwd) {
18220
- const manifestPath = resolve5(cwd, ".pourkit/manifest.json");
19052
+ const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
18221
19053
  try {
18222
- const content = await readFile7(manifestPath, "utf-8");
19054
+ const content = await readFile8(manifestPath, "utf-8");
18223
19055
  const manifest = JSON.parse(content);
18224
19056
  if (typeof manifest.assets !== "object" || manifest.assets === null || Array.isArray(manifest.assets)) {
18225
19057
  return {};
@@ -18243,7 +19075,7 @@ function resolvePackagedReleaseAddonDocPath(docName) {
18243
19075
  }
18244
19076
  async function readPackagedTextFile(filePath) {
18245
19077
  try {
18246
- return await readFile7(filePath, "utf-8");
19078
+ return await readFile8(filePath, "utf-8");
18247
19079
  } catch {
18248
19080
  return null;
18249
19081
  }
@@ -18257,7 +19089,7 @@ async function walkDir2(dir) {
18257
19089
  try {
18258
19090
  const entries = await readdir4(dir, { withFileTypes: true });
18259
19091
  for (const entry of entries) {
18260
- const full = resolve5(dir, entry.name);
19092
+ const full = resolve6(dir, entry.name);
18261
19093
  if (entry.isDirectory()) {
18262
19094
  files.push(...await walkDir2(full));
18263
19095
  } else {
@@ -18340,8 +19172,8 @@ async function validateWorkflowPack(cwd) {
18340
19172
  const baselineSkills = getBaselineManagedSkillNames();
18341
19173
  const manifestAssets = await readManifestAssets(cwd);
18342
19174
  for (const docName of catalog.docs) {
18343
- const localPath = resolve5(cwd, `.pourkit/managed/docs/agents/${docName}`);
18344
- if (!existsSync21(localPath)) {
19175
+ const localPath = resolve6(cwd, `.pourkit/managed/docs/agents/${docName}`);
19176
+ if (!existsSync22(localPath)) {
18345
19177
  failures.push({
18346
19178
  severity: "failure",
18347
19179
  kind: "missing_managed_asset",
@@ -18353,13 +19185,13 @@ async function validateWorkflowPack(cwd) {
18353
19185
  const packagedContent = await readPackagedTextFile(packagedPath);
18354
19186
  if (packagedContent !== null) {
18355
19187
  try {
18356
- const localContent = await readFile7(localPath, "utf-8");
19188
+ const localContent = await readFile8(localPath, "utf-8");
18357
19189
  if (localContent !== packagedContent) {
18358
- const overridePath = resolve5(
19190
+ const overridePath = resolve6(
18359
19191
  cwd,
18360
19192
  `.pourkit/overrides/docs/agents/${docName}`
18361
19193
  );
18362
- if (existsSync21(overridePath)) {
19194
+ if (existsSync22(overridePath)) {
18363
19195
  warnings.push({
18364
19196
  severity: "warning",
18365
19197
  kind: "overridden",
@@ -18381,16 +19213,16 @@ async function validateWorkflowPack(cwd) {
18381
19213
  }
18382
19214
  }
18383
19215
  for (const skillName of baselineSkills) {
18384
- const skillDir = resolve5(cwd, `.pourkit/managed/skills/${skillName}`);
18385
- const skillSourcePath = resolve5(skillDir, "SKILL.md");
18386
- if (!existsSync21(skillDir)) {
19216
+ const skillDir = resolve6(cwd, `.pourkit/managed/skills/${skillName}`);
19217
+ const skillSourcePath = resolve6(skillDir, "SKILL.md");
19218
+ if (!existsSync22(skillDir)) {
18387
19219
  failures.push({
18388
19220
  severity: "failure",
18389
19221
  kind: "missing_managed_asset",
18390
19222
  path: `.pourkit/managed/skills/${skillName}/SKILL.md`,
18391
19223
  message: `Missing managed skill: ${skillName}`
18392
19224
  });
18393
- } else if (!existsSync21(skillSourcePath)) {
19225
+ } else if (!existsSync22(skillSourcePath)) {
18394
19226
  failures.push({
18395
19227
  severity: "failure",
18396
19228
  kind: "missing_managed_asset",
@@ -18406,13 +19238,13 @@ async function validateWorkflowPack(cwd) {
18406
19238
  const packagedContent = await readPackagedTextFile(packagedSkillPath);
18407
19239
  if (packagedContent !== null) {
18408
19240
  try {
18409
- const localContent = await readFile7(skillSourcePath, "utf-8");
19241
+ const localContent = await readFile8(skillSourcePath, "utf-8");
18410
19242
  if (localContent !== packagedContent) {
18411
- const overridePath = resolve5(
19243
+ const overridePath = resolve6(
18412
19244
  cwd,
18413
19245
  `.pourkit/overrides/skills/${skillName}/SKILL.md`
18414
19246
  );
18415
- if (existsSync21(overridePath)) {
19247
+ if (existsSync22(overridePath)) {
18416
19248
  warnings.push({
18417
19249
  severity: "warning",
18418
19250
  kind: "overridden",
@@ -18434,8 +19266,8 @@ async function validateWorkflowPack(cwd) {
18434
19266
  }
18435
19267
  }
18436
19268
  for (const promptName of catalog.prompts) {
18437
- const promptPath = resolve5(cwd, `.pourkit/managed/prompts/${promptName}`);
18438
- if (!existsSync21(promptPath)) {
19269
+ const promptPath = resolve6(cwd, `.pourkit/managed/prompts/${promptName}`);
19270
+ if (!existsSync22(promptPath)) {
18439
19271
  failures.push({
18440
19272
  severity: "failure",
18441
19273
  kind: "missing_managed_asset",
@@ -18445,16 +19277,16 @@ async function validateWorkflowPack(cwd) {
18445
19277
  }
18446
19278
  }
18447
19279
  for (const skillName of baselineSkills) {
18448
- const projectionDir = resolve5(cwd, `.agents/skills/${skillName}`);
18449
- const projectionPath = resolve5(projectionDir, "SKILL.md");
18450
- if (!existsSync21(projectionDir)) {
19280
+ const projectionDir = resolve6(cwd, `.agents/skills/${skillName}`);
19281
+ const projectionPath = resolve6(projectionDir, "SKILL.md");
19282
+ if (!existsSync22(projectionDir)) {
18451
19283
  warnings.push({
18452
19284
  severity: "warning",
18453
19285
  kind: "projection_stale",
18454
19286
  path: `.agents/skills/${skillName}/SKILL.md`,
18455
19287
  message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
18456
19288
  });
18457
- } else if (!existsSync21(projectionPath)) {
19289
+ } else if (!existsSync22(projectionPath)) {
18458
19290
  warnings.push({
18459
19291
  severity: "warning",
18460
19292
  kind: "projection_stale",
@@ -18470,7 +19302,7 @@ async function validateWorkflowPack(cwd) {
18470
19302
  const packagedContent = await readPackagedTextFile(managedSourcePath);
18471
19303
  if (packagedContent !== null) {
18472
19304
  try {
18473
- const projectionContent = await readFile7(projectionPath, "utf-8");
19305
+ const projectionContent = await readFile8(projectionPath, "utf-8");
18474
19306
  const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
18475
19307
  if (projectionContent !== buildOpenCodeSkillProjectionContent(
18476
19308
  managedSourceRelativePath,
@@ -18491,8 +19323,8 @@ async function validateWorkflowPack(cwd) {
18491
19323
  for (const agentFile of listPackagedOpenCodeAgentFiles()) {
18492
19324
  const fileName = basename(agentFile);
18493
19325
  const relativePath = `.opencode/agents/${fileName}`;
18494
- const localPath = resolve5(cwd, relativePath);
18495
- if (!existsSync21(localPath)) {
19326
+ const localPath = resolve6(cwd, relativePath);
19327
+ if (!existsSync22(localPath)) {
18496
19328
  failures.push({
18497
19329
  severity: "failure",
18498
19330
  kind: "missing_managed_asset",
@@ -18507,7 +19339,7 @@ async function validateWorkflowPack(cwd) {
18507
19339
  const packagedContent = await readPackagedTextFile(agentFile);
18508
19340
  if (packagedContent === null) continue;
18509
19341
  try {
18510
- const localContent = await readFile7(localPath, "utf-8");
19342
+ const localContent = await readFile8(localPath, "utf-8");
18511
19343
  if (localContent !== packagedContent) {
18512
19344
  failures.push({
18513
19345
  severity: "failure",
@@ -18519,10 +19351,10 @@ async function validateWorkflowPack(cwd) {
18519
19351
  } catch {
18520
19352
  }
18521
19353
  }
18522
- const agentsPath = resolve5(cwd, "AGENTS.md");
18523
- if (existsSync21(agentsPath)) {
19354
+ const agentsPath = resolve6(cwd, "AGENTS.md");
19355
+ if (existsSync22(agentsPath)) {
18524
19356
  try {
18525
- const agentsContent = await readFile7(agentsPath, "utf-8");
19357
+ const agentsContent = await readFile8(agentsPath, "utf-8");
18526
19358
  if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
18527
19359
  failures.push({
18528
19360
  severity: "failure",
@@ -18542,8 +19374,8 @@ async function validateWorkflowPack(cwd) {
18542
19374
  } catch {
18543
19375
  }
18544
19376
  }
18545
- const overridesDir = resolve5(cwd, ".pourkit/overrides");
18546
- if (existsSync21(overridesDir)) {
19377
+ const overridesDir = resolve6(cwd, ".pourkit/overrides");
19378
+ if (existsSync22(overridesDir)) {
18547
19379
  const overrideFiles = await walkDir2(overridesDir);
18548
19380
  for (const file of overrideFiles) {
18549
19381
  const relPath = relative4(cwd, file);
@@ -18557,19 +19389,19 @@ async function validateWorkflowPack(cwd) {
18557
19389
  }
18558
19390
  }
18559
19391
  }
18560
- const configPath = resolve5(cwd, ".pourkit/config.json");
18561
- if (existsSync21(configPath)) {
19392
+ const configPath = resolve6(cwd, ".pourkit/config.json");
19393
+ if (existsSync22(configPath)) {
18562
19394
  try {
18563
- const configContent = await readFile7(configPath, "utf-8");
19395
+ const configContent = await readFile8(configPath, "utf-8");
18564
19396
  const parsed = JSON.parse(configContent);
18565
19397
  const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
18566
19398
  if (releaseEnabled) {
18567
19399
  for (const skillName of catalog.releaseAddonSkills) {
18568
- const addonDir = resolve5(
19400
+ const addonDir = resolve6(
18569
19401
  cwd,
18570
19402
  `.pourkit/managed/addons/release/skills/${skillName}`
18571
19403
  );
18572
- if (!existsSync21(addonDir)) {
19404
+ if (!existsSync22(addonDir)) {
18573
19405
  failures.push({
18574
19406
  severity: "failure",
18575
19407
  kind: "release_config_conflict",
@@ -18579,11 +19411,11 @@ async function validateWorkflowPack(cwd) {
18579
19411
  }
18580
19412
  }
18581
19413
  for (const docName of catalog.releaseAddonDocs) {
18582
- const addonDocPath = resolve5(
19414
+ const addonDocPath = resolve6(
18583
19415
  cwd,
18584
19416
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18585
19417
  );
18586
- if (!existsSync21(addonDocPath)) {
19418
+ if (!existsSync22(addonDocPath)) {
18587
19419
  failures.push({
18588
19420
  severity: "failure",
18589
19421
  kind: "release_config_conflict",
@@ -18595,7 +19427,7 @@ async function validateWorkflowPack(cwd) {
18595
19427
  resolvePackagedReleaseAddonDocPath(docName)
18596
19428
  );
18597
19429
  if (packagedContent !== null) {
18598
- const localContent = await readFile7(addonDocPath, "utf-8");
19430
+ const localContent = await readFile8(addonDocPath, "utf-8");
18599
19431
  if (localContent !== packagedContent) {
18600
19432
  failures.push({
18601
19433
  severity: "failure",
@@ -18624,11 +19456,11 @@ async function validateWorkflowPack(cwd) {
18624
19456
  }
18625
19457
  } else {
18626
19458
  for (const skillName of catalog.releaseAddonSkills) {
18627
- const addonDir = resolve5(
19459
+ const addonDir = resolve6(
18628
19460
  cwd,
18629
19461
  `.pourkit/managed/addons/release/skills/${skillName}`
18630
19462
  );
18631
- if (existsSync21(addonDir)) {
19463
+ if (existsSync22(addonDir)) {
18632
19464
  failures.push({
18633
19465
  severity: "failure",
18634
19466
  kind: "release_config_conflict",
@@ -18638,11 +19470,11 @@ async function validateWorkflowPack(cwd) {
18638
19470
  }
18639
19471
  }
18640
19472
  for (const docName of catalog.releaseAddonDocs) {
18641
- const addonDocPath = resolve5(
19473
+ const addonDocPath = resolve6(
18642
19474
  cwd,
18643
19475
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18644
19476
  );
18645
- if (existsSync21(addonDocPath)) {
19477
+ if (existsSync22(addonDocPath)) {
18646
19478
  failures.push({
18647
19479
  severity: "failure",
18648
19480
  kind: "release_config_conflict",
@@ -18655,16 +19487,16 @@ async function validateWorkflowPack(cwd) {
18655
19487
  } catch {
18656
19488
  }
18657
19489
  }
18658
- const manifestPath = resolve5(cwd, ".pourkit/manifest.json");
18659
- if (existsSync21(manifestPath)) {
19490
+ const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
19491
+ if (existsSync22(manifestPath)) {
18660
19492
  try {
18661
- const manifestContent = await readFile7(manifestPath, "utf-8");
19493
+ const manifestContent = await readFile8(manifestPath, "utf-8");
18662
19494
  const manifest = JSON.parse(manifestContent);
18663
19495
  const wp = manifest.workflowPack;
18664
19496
  if (wp?.projections) {
18665
19497
  for (const proj of wp.projections) {
18666
19498
  if (proj.path) {
18667
- const resolved = resolve5(cwd, proj.path);
19499
+ const resolved = resolve6(cwd, proj.path);
18668
19500
  if (!isPathContained(cwd, resolved)) {
18669
19501
  failures.push({
18670
19502
  severity: "failure",
@@ -18678,8 +19510,8 @@ async function validateWorkflowPack(cwd) {
18678
19510
  } catch {
18679
19511
  }
18680
19512
  }
18681
- const pathEscapeOverrideDir = resolve5(cwd, ".pourkit/overrides");
18682
- if (existsSync21(pathEscapeOverrideDir)) {
19513
+ const pathEscapeOverrideDir = resolve6(cwd, ".pourkit/overrides");
19514
+ if (existsSync22(pathEscapeOverrideDir)) {
18683
19515
  const overrideFiles = await walkDir2(pathEscapeOverrideDir);
18684
19516
  for (const file of overrideFiles) {
18685
19517
  if (!isPathContained(cwd, file)) {
@@ -18692,13 +19524,13 @@ async function validateWorkflowPack(cwd) {
18692
19524
  }
18693
19525
  }
18694
19526
  }
18695
- const managedDocsDir = resolve5(cwd, ".pourkit/managed/docs/agents");
18696
- if (existsSync21(managedDocsDir)) {
19527
+ const managedDocsDir = resolve6(cwd, ".pourkit/managed/docs/agents");
19528
+ if (existsSync22(managedDocsDir)) {
18697
19529
  const docFiles = await walkDir2(managedDocsDir);
18698
19530
  for (const file of docFiles) {
18699
19531
  if (!file.endsWith(".md")) continue;
18700
19532
  try {
18701
- const content = await readFile7(file, "utf-8");
19533
+ const content = await readFile8(file, "utf-8");
18702
19534
  const relPath = relative4(cwd, file);
18703
19535
  const violations = scanManagedPolicyText(content, relPath);
18704
19536
  for (const v of violations) {
@@ -18713,15 +19545,15 @@ async function validateWorkflowPack(cwd) {
18713
19545
  }
18714
19546
  }
18715
19547
  }
18716
- const managedSkillsDir = resolve5(cwd, ".pourkit/managed/skills");
18717
- if (existsSync21(managedSkillsDir)) {
19548
+ const managedSkillsDir = resolve6(cwd, ".pourkit/managed/skills");
19549
+ if (existsSync22(managedSkillsDir)) {
18718
19550
  const skillDirs = await readdir4(managedSkillsDir, { withFileTypes: true });
18719
19551
  for (const entry of skillDirs) {
18720
19552
  if (!entry.isDirectory()) continue;
18721
- const skillFile = resolve5(managedSkillsDir, entry.name, "SKILL.md");
18722
- if (!existsSync21(skillFile)) continue;
19553
+ const skillFile = resolve6(managedSkillsDir, entry.name, "SKILL.md");
19554
+ if (!existsSync22(skillFile)) continue;
18723
19555
  try {
18724
- const content = await readFile7(skillFile, "utf-8");
19556
+ const content = await readFile8(skillFile, "utf-8");
18725
19557
  const relPath = relative4(cwd, skillFile);
18726
19558
  const violations = scanManagedPolicyText(content, relPath);
18727
19559
  for (const v of violations) {
@@ -18736,13 +19568,13 @@ async function validateWorkflowPack(cwd) {
18736
19568
  }
18737
19569
  }
18738
19570
  }
18739
- const managedPromptsDir = resolve5(cwd, ".pourkit/managed/prompts");
18740
- if (existsSync21(managedPromptsDir)) {
19571
+ const managedPromptsDir = resolve6(cwd, ".pourkit/managed/prompts");
19572
+ if (existsSync22(managedPromptsDir)) {
18741
19573
  for (const promptName of catalog.prompts) {
18742
- const promptFile = resolve5(managedPromptsDir, promptName);
18743
- if (!existsSync21(promptFile)) continue;
19574
+ const promptFile = resolve6(managedPromptsDir, promptName);
19575
+ if (!existsSync22(promptFile)) continue;
18744
19576
  try {
18745
- const content = await readFile7(promptFile, "utf-8");
19577
+ const content = await readFile8(promptFile, "utf-8");
18746
19578
  const relPath = relative4(cwd, promptFile);
18747
19579
  const violations = scanManagedPolicyText(content, relPath);
18748
19580
  for (const v of violations) {
@@ -18758,13 +19590,13 @@ async function validateWorkflowPack(cwd) {
18758
19590
  }
18759
19591
  }
18760
19592
  for (const skillName of catalog.releaseAddonSkills) {
18761
- const addonSkillFile = resolve5(
19593
+ const addonSkillFile = resolve6(
18762
19594
  cwd,
18763
19595
  `.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
18764
19596
  );
18765
- if (!existsSync21(addonSkillFile)) continue;
19597
+ if (!existsSync22(addonSkillFile)) continue;
18766
19598
  try {
18767
- const content = await readFile7(addonSkillFile, "utf-8");
19599
+ const content = await readFile8(addonSkillFile, "utf-8");
18768
19600
  const relPath = relative4(cwd, addonSkillFile);
18769
19601
  const violations = scanManagedPolicyText(content, relPath);
18770
19602
  for (const v of violations) {
@@ -18779,13 +19611,13 @@ async function validateWorkflowPack(cwd) {
18779
19611
  }
18780
19612
  }
18781
19613
  for (const docName of catalog.releaseAddonDocs) {
18782
- const addonDocFile = resolve5(
19614
+ const addonDocFile = resolve6(
18783
19615
  cwd,
18784
19616
  `.pourkit/managed/addons/release/docs/agents/${docName}`
18785
19617
  );
18786
- if (!existsSync21(addonDocFile)) continue;
19618
+ if (!existsSync22(addonDocFile)) continue;
18787
19619
  try {
18788
- const content = await readFile7(addonDocFile, "utf-8");
19620
+ const content = await readFile8(addonDocFile, "utf-8");
18789
19621
  const relPath = relative4(cwd, addonDocFile);
18790
19622
  const violations = scanManagedPolicyText(content, relPath);
18791
19623
  for (const v of violations) {
@@ -18816,16 +19648,16 @@ async function validateMemoryHealth(cwd, memoryConfig) {
18816
19648
  ok: icmPath !== null,
18817
19649
  detail: icmPath !== null ? icmPath : "icm not found on PATH"
18818
19650
  });
18819
- const gitignorePath = resolve5(cwd, ".gitignore");
18820
- const gitignoreContent = existsSync21(gitignorePath) ? readFileSync20(gitignorePath, "utf-8") : "";
19651
+ const gitignorePath = resolve6(cwd, ".gitignore");
19652
+ const gitignoreContent = existsSync22(gitignorePath) ? readFileSync21(gitignorePath, "utf-8") : "";
18821
19653
  const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
18822
19654
  checks.push({
18823
19655
  name: "icm-gitignore",
18824
19656
  ok: hasGitignoreEntry,
18825
19657
  detail: hasGitignoreEntry ? ".gitignore covers .pourkit/icm/" : ".gitignore does not cover .pourkit/icm/"
18826
19658
  });
18827
- const memoryDbPath = resolve5(cwd, ".pourkit", "icm", "memories.db");
18828
- const hasMemoryDb = existsSync21(memoryDbPath);
19659
+ const memoryDbPath = resolve6(cwd, ".pourkit", "icm", "memories.db");
19660
+ const hasMemoryDb = existsSync22(memoryDbPath);
18829
19661
  checks.push({
18830
19662
  name: "icm-db-exists",
18831
19663
  ok: hasMemoryDb,
@@ -18838,9 +19670,9 @@ async function validateMemoryHealth(cwd, memoryConfig) {
18838
19670
  detail: shapeValid ? "memory: { enabled: true, provider: icm }" : `invalid memory config: enabled=${memoryConfig.enabled}, provider=${memoryConfig.provider}`
18839
19671
  });
18840
19672
  const staleAgentFiles = ["AGENTS.md", "CLAUDE.md"].filter((fileName) => {
18841
- const filePath = resolve5(cwd, fileName);
18842
- if (!existsSync21(filePath)) return false;
18843
- 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");
18844
19676
  const managedBlock = extractManagedBlockContent(content);
18845
19677
  return managedBlock !== null && hasBareIcmMemoryCommand(managedBlock);
18846
19678
  });
@@ -18874,10 +19706,10 @@ function extractManagedBlockContent(content) {
18874
19706
  async function runDoctorCommand(options) {
18875
19707
  const repoRootPath = options.cwd ?? process.cwd();
18876
19708
  const schemaDir = localSchemaDir(repoRootPath);
18877
- const localSchemaPath = resolve5(schemaDir, "pourkit.schema.json");
18878
- const localHashPath = resolve5(schemaDir, "pourkit.schema.hash");
18879
- const localSchemaExists = existsSync21(localSchemaPath);
18880
- 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);
18881
19713
  let packagedHash = null;
18882
19714
  try {
18883
19715
  packagedHash = readPackagedHash();
@@ -18886,19 +19718,20 @@ async function runDoctorCommand(options) {
18886
19718
  let localHashContent = null;
18887
19719
  if (localHashExists) {
18888
19720
  try {
18889
- localHashContent = await readFile7(localHashPath, "utf-8");
19721
+ localHashContent = await readFile8(localHashPath, "utf-8");
18890
19722
  } catch {
18891
19723
  }
18892
19724
  }
18893
19725
  const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
18894
19726
  const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
18895
19727
  const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
18896
- const configPath = resolve5(repoRootPath, ".pourkit/config.json");
19728
+ const configPath = resolve6(repoRootPath, ".pourkit/config.json");
18897
19729
  let configValidation;
18898
19730
  let rawMemoryConfig;
18899
- if (existsSync21(configPath)) {
19731
+ let configMigration;
19732
+ if (existsSync22(configPath)) {
18900
19733
  try {
18901
- const raw = JSON.parse(readFileSync20(configPath, "utf-8"));
19734
+ const raw = JSON.parse(readFileSync21(configPath, "utf-8"));
18902
19735
  rawMemoryConfig = raw.memory;
18903
19736
  const validate = getSchemaValidator();
18904
19737
  const valid2 = validate(raw);
@@ -18913,25 +19746,47 @@ async function runDoctorCommand(options) {
18913
19746
  )
18914
19747
  };
18915
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
+ };
18916
19757
  } catch (err) {
18917
19758
  const msg = err instanceof SyntaxError ? err.message : String(err);
18918
19759
  configValidation = {
18919
19760
  ok: false,
18920
19761
  errors: [`.pourkit/config.json: ${msg}`]
18921
19762
  };
19763
+ configMigration = {
19764
+ available: false,
19765
+ safe: false,
19766
+ command: null,
19767
+ plannedChanges: [],
19768
+ blockers: [`.pourkit/config.json: ${msg}`]
19769
+ };
18922
19770
  }
18923
19771
  } else {
18924
19772
  configValidation = {
18925
19773
  ok: false,
18926
19774
  errors: [".pourkit/config.json not found"]
18927
19775
  };
19776
+ configMigration = {
19777
+ available: false,
19778
+ safe: false,
19779
+ command: null,
19780
+ plannedChanges: [],
19781
+ blockers: [".pourkit/config.json not found"]
19782
+ };
18928
19783
  }
18929
19784
  const obsoleteConfigs = [
18930
19785
  "pourkit.config.ts",
18931
19786
  "pourkit.config.mjs",
18932
19787
  "pourkit.config.js",
18933
19788
  "pourkit.json"
18934
- ].filter((p) => existsSync21(resolve5(repoRootPath, p)));
19789
+ ].filter((p) => existsSync22(resolve6(repoRootPath, p)));
18935
19790
  const workflowPack = await validateWorkflowPack(repoRootPath);
18936
19791
  const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
18937
19792
  let recommendation = null;
@@ -18941,7 +19796,15 @@ async function runDoctorCommand(options) {
18941
19796
  `Found obsolete config files: ${obsoleteConfigs.join(", ")}. Move configuration to .pourkit/config.json with "$schema": "./schema/pourkit.schema.json".`
18942
19797
  );
18943
19798
  }
18944
- 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) {
18945
19808
  recParts.push(
18946
19809
  "Config validation failed; fix errors in .pourkit/config.json."
18947
19810
  );
@@ -18966,6 +19829,7 @@ async function runDoctorCommand(options) {
18966
19829
  configValidation,
18967
19830
  obsoleteConfigs,
18968
19831
  schemaAssets: { schema, hash, overall },
19832
+ configMigration,
18969
19833
  workflowPack,
18970
19834
  memory,
18971
19835
  recommendation
@@ -18975,30 +19839,30 @@ async function runConfigSyncSchemaCommand(options) {
18975
19839
  const repoRootPath = options.cwd ?? process.cwd();
18976
19840
  const schemaDir = localSchemaDir(repoRootPath);
18977
19841
  await mkdir6(schemaDir, { recursive: true });
18978
- const packagedSchema = await readFile7(PACKAGED_SCHEMA_PATH, "utf-8");
18979
- const packagedHash = await readFile7(PACKAGED_HASH_PATH, "utf-8");
18980
- const localSchemaPath = resolve5(schemaDir, "pourkit.schema.json");
18981
- 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");
18982
19846
  let schemaWritten = false;
18983
19847
  let hashWritten = false;
18984
- if (existsSync21(localSchemaPath)) {
18985
- const existing = await readFile7(localSchemaPath, "utf-8");
19848
+ if (existsSync22(localSchemaPath)) {
19849
+ const existing = await readFile8(localSchemaPath, "utf-8");
18986
19850
  if (existing !== packagedSchema) {
18987
- await writeFile3(localSchemaPath, packagedSchema, "utf-8");
19851
+ await writeFile4(localSchemaPath, packagedSchema, "utf-8");
18988
19852
  schemaWritten = true;
18989
19853
  }
18990
19854
  } else {
18991
- await writeFile3(localSchemaPath, packagedSchema, "utf-8");
19855
+ await writeFile4(localSchemaPath, packagedSchema, "utf-8");
18992
19856
  schemaWritten = true;
18993
19857
  }
18994
- if (existsSync21(localHashPath)) {
18995
- const existing = await readFile7(localHashPath, "utf-8");
19858
+ if (existsSync22(localHashPath)) {
19859
+ const existing = await readFile8(localHashPath, "utf-8");
18996
19860
  if (existing !== packagedHash) {
18997
- await writeFile3(localHashPath, packagedHash, "utf-8");
19861
+ await writeFile4(localHashPath, packagedHash, "utf-8");
18998
19862
  hashWritten = true;
18999
19863
  }
19000
19864
  } else {
19001
- await writeFile3(localHashPath, packagedHash, "utf-8");
19865
+ await writeFile4(localHashPath, packagedHash, "utf-8");
19002
19866
  hashWritten = true;
19003
19867
  }
19004
19868
  return {
@@ -19009,13 +19873,13 @@ async function runConfigSyncSchemaCommand(options) {
19009
19873
  }
19010
19874
 
19011
19875
  // commands/workflow-pack-sync.ts
19012
- import { existsSync as existsSync22, readdirSync as readdirSync6 } from "fs";
19013
- 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";
19014
19878
  import { createHash as createHash2 } from "crypto";
19015
- import { resolve as resolve6, dirname as dirname7, relative as relative5, normalize as normalize2, basename as basename2 } from "path";
19016
- import { fileURLToPath as fileURLToPath4 } from "url";
19017
- var __filename4 = fileURLToPath4(import.meta.url);
19018
- 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);
19019
19883
  var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
19020
19884
  var PROJECT_OWNED_DIRECTORIES = [
19021
19885
  ".pourkit/docs/adr",
@@ -19024,23 +19888,23 @@ var PROJECT_OWNED_DIRECTORIES = [
19024
19888
  ];
19025
19889
  var OVERRIDES_DIR = ".pourkit/overrides";
19026
19890
  function resolvePackagedManagedPath2(...segments) {
19027
- const bundledPath = resolve6(__dirname4, "managed", ...segments);
19028
- const sourcePath = resolve6(__dirname4, "..", "managed", ...segments);
19891
+ const bundledPath = resolve7(__dirname5, "managed", ...segments);
19892
+ const sourcePath = resolve7(__dirname5, "..", "managed", ...segments);
19029
19893
  const candidates = [bundledPath, sourcePath];
19030
- return candidates.find((candidate) => existsSync22(candidate)) ?? bundledPath;
19894
+ return candidates.find((candidate) => existsSync23(candidate)) ?? bundledPath;
19031
19895
  }
19032
19896
  function resolvePackagedOpenCodeAgentsDir2() {
19033
19897
  const candidates = [
19034
- resolve6(__dirname4, ".opencode", "agents"),
19035
- resolve6(__dirname4, "..", ".opencode", "agents"),
19036
- resolve6(__dirname4, "..", "..", ".opencode", "agents")
19898
+ resolve7(__dirname5, ".opencode", "agents"),
19899
+ resolve7(__dirname5, "..", ".opencode", "agents"),
19900
+ resolve7(__dirname5, "..", "..", ".opencode", "agents")
19037
19901
  ];
19038
- return candidates.find((candidate) => existsSync22(candidate)) ?? candidates[0];
19902
+ return candidates.find((candidate) => existsSync23(candidate)) ?? candidates[0];
19039
19903
  }
19040
19904
  function listPackagedOpenCodeAgentFiles2() {
19041
19905
  const agentsDir = resolvePackagedOpenCodeAgentsDir2();
19042
19906
  try {
19043
- 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));
19044
19908
  } catch {
19045
19909
  return [];
19046
19910
  }
@@ -19074,7 +19938,7 @@ function resolvePackagedManagedPromptPath(promptName) {
19074
19938
  }
19075
19939
  async function readPackagedTextFile2(filePath) {
19076
19940
  try {
19077
- return await readFile8(filePath, "utf-8");
19941
+ return await readFile9(filePath, "utf-8");
19078
19942
  } catch {
19079
19943
  return null;
19080
19944
  }
@@ -19133,7 +19997,7 @@ async function walkDir3(dir) {
19133
19997
  try {
19134
19998
  const entries = await readdir5(dir, { withFileTypes: true });
19135
19999
  for (const entry of entries) {
19136
- const full = resolve6(dir, entry.name);
20000
+ const full = resolve7(dir, entry.name);
19137
20001
  if (entry.isDirectory()) {
19138
20002
  files.push(...await walkDir3(full));
19139
20003
  } else {
@@ -19151,20 +20015,20 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
19151
20015
  if (sourceContent === null) {
19152
20016
  return false;
19153
20017
  }
19154
- const targetPath = resolve6(cwd, targetRelativePath);
20018
+ const targetPath = resolve7(cwd, targetRelativePath);
19155
20019
  const escapeError = checkForPathEscape(cwd, targetPath);
19156
20020
  if (escapeError) {
19157
20021
  errors.push(escapeError);
19158
20022
  return false;
19159
20023
  }
19160
- await mkdir7(resolve6(targetPath, ".."), { recursive: true });
19161
- if (existsSync22(targetPath)) {
19162
- 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");
19163
20027
  if (existing === sourceContent) {
19164
20028
  return false;
19165
20029
  }
19166
20030
  }
19167
- await writeFile4(targetPath, sourceContent, "utf-8");
20031
+ await writeFile5(targetPath, sourceContent, "utf-8");
19168
20032
  return true;
19169
20033
  }
19170
20034
  function walkDirSync(dir) {
@@ -19172,7 +20036,7 @@ function walkDirSync(dir) {
19172
20036
  try {
19173
20037
  const entries = readdirSync6(dir, { withFileTypes: true });
19174
20038
  for (const entry of entries) {
19175
- const full = resolve6(dir, entry.name);
20039
+ const full = resolve7(dir, entry.name);
19176
20040
  if (entry.isDirectory()) {
19177
20041
  result.push(...walkDirSync(full));
19178
20042
  } else {
@@ -19184,8 +20048,8 @@ function walkDirSync(dir) {
19184
20048
  return result;
19185
20049
  }
19186
20050
  async function detectOverrides(cwd) {
19187
- const overridesDir = resolve6(cwd, OVERRIDES_DIR);
19188
- if (!existsSync22(overridesDir)) {
20051
+ const overridesDir = resolve7(cwd, OVERRIDES_DIR);
20052
+ if (!existsSync23(overridesDir)) {
19189
20053
  return [];
19190
20054
  }
19191
20055
  const files = await walkDir3(overridesDir);
@@ -19194,14 +20058,14 @@ async function detectOverrides(cwd) {
19194
20058
  async function detectProjectOwnedFiles(cwd) {
19195
20059
  const found = [];
19196
20060
  for (const ownedPath of PROJECT_OWNED_PATHS) {
19197
- const fullPath = resolve6(cwd, ownedPath);
19198
- if (existsSync22(fullPath)) {
20061
+ const fullPath = resolve7(cwd, ownedPath);
20062
+ if (existsSync23(fullPath)) {
19199
20063
  found.push(ownedPath);
19200
20064
  }
19201
20065
  }
19202
20066
  for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
19203
- const fullDir = resolve6(cwd, ownedDir);
19204
- if (existsSync22(fullDir)) {
20067
+ const fullDir = resolve7(cwd, ownedDir);
20068
+ if (existsSync23(fullDir)) {
19205
20069
  const files = await walkDir3(fullDir);
19206
20070
  for (const file of files) {
19207
20071
  found.push(relative5(cwd, file));
@@ -19212,14 +20076,35 @@ async function detectProjectOwnedFiles(cwd) {
19212
20076
  }
19213
20077
  async function isReleaseWorkflowEnabled(cwd) {
19214
20078
  try {
19215
- const configPath = resolve6(cwd, ".pourkit", "config.json");
19216
- const content = await readFile8(configPath, "utf-8");
20079
+ const configPath = resolve7(cwd, ".pourkit", "config.json");
20080
+ const content = await readFile9(configPath, "utf-8");
19217
20081
  const parsed = JSON.parse(content);
19218
20082
  return parsed.releaseWorkflow?.enabled === true;
19219
20083
  } catch {
19220
20084
  return false;
19221
20085
  }
19222
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
+ }
19223
20108
  async function runWorkflowPackSyncCommand(options) {
19224
20109
  const cwd = options.cwd ?? process.cwd();
19225
20110
  const catalog = getWorkflowPackCatalog();
@@ -19228,10 +20113,10 @@ async function runWorkflowPackSyncCommand(options) {
19228
20113
  const skippedProjectOwned = [];
19229
20114
  const overrides = [];
19230
20115
  const errors = [];
19231
- const manifestPath = resolve6(cwd, ".pourkit", "manifest.json");
20116
+ const manifestPath = resolve7(cwd, ".pourkit", "manifest.json");
19232
20117
  let manifestReadFailed = false;
19233
20118
  let manifest = {};
19234
- const existingRawManifest = await readFile8(manifestPath, "utf-8").catch(
20119
+ const existingRawManifest = await readFile9(manifestPath, "utf-8").catch(
19235
20120
  () => null
19236
20121
  );
19237
20122
  if (existingRawManifest) {
@@ -19361,28 +20246,20 @@ async function runWorkflowPackSyncCommand(options) {
19361
20246
  });
19362
20247
  }
19363
20248
  const schemaResult = await runConfigSyncSchemaCommand({ cwd });
19364
- let configMemory;
19365
- try {
19366
- const configPath = resolve6(cwd, ".pourkit", "config.json");
19367
- const config = await loadConfig(configPath);
19368
- if (config.memory?.enabled === true && config.memory?.provider === "icm") {
19369
- configMemory = config.memory;
19370
- }
19371
- } catch {
19372
- }
20249
+ const configMemory = await readConfiguredMemory(cwd);
19373
20250
  const managedBlockContent = generateManagedBlockContent(configMemory);
19374
20251
  const fullManagedBlock = buildManagedBlock(managedBlockContent);
19375
20252
  async function syncManagedAgentFile(fileName, options2) {
19376
- const filePath = resolve6(cwd, fileName);
19377
- if (!existsSync22(filePath)) {
20253
+ const filePath = resolve7(cwd, fileName);
20254
+ if (!existsSync23(filePath)) {
19378
20255
  if (!options2.createIfMissing) return false;
19379
- await writeFile4(filePath, fullManagedBlock, "utf-8");
20256
+ await writeFile5(filePath, fullManagedBlock, "utf-8");
19380
20257
  return true;
19381
20258
  }
19382
- const existing = await readFile8(filePath, "utf-8");
20259
+ const existing = await readFile9(filePath, "utf-8");
19383
20260
  const newContent = upsertManagedBlock(existing, managedBlockContent);
19384
20261
  if (newContent !== existing) {
19385
- await writeFile4(filePath, newContent, "utf-8");
20262
+ await writeFile5(filePath, newContent, "utf-8");
19386
20263
  return true;
19387
20264
  }
19388
20265
  return false;
@@ -19417,8 +20294,8 @@ async function runWorkflowPackSyncCommand(options) {
19417
20294
  errors.push("Failed to write manifest metadata");
19418
20295
  } else {
19419
20296
  try {
19420
- await mkdir7(resolve6(manifestPath, ".."), { recursive: true });
19421
- await writeFile4(
20297
+ await mkdir7(resolve7(manifestPath, ".."), { recursive: true });
20298
+ await writeFile5(
19422
20299
  manifestPath,
19423
20300
  JSON.stringify(manifest, null, 2) + "\n",
19424
20301
  "utf-8"
@@ -19458,7 +20335,7 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19458
20335
  const sourceContent = await readPackagedTextFile2(sourceFile);
19459
20336
  const fileName = basename2(sourceFile);
19460
20337
  const targetRelativePath = `.opencode/agents/${fileName}`;
19461
- const targetPath = resolve6(cwd, targetRelativePath);
20338
+ const targetPath = resolve7(cwd, targetRelativePath);
19462
20339
  const escapeError = checkForPathEscape(cwd, targetPath);
19463
20340
  if (escapeError) {
19464
20341
  errors.push(escapeError);
@@ -19472,14 +20349,14 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19472
20349
  ownership: "managed",
19473
20350
  sha256: sha256Text(sourceContent)
19474
20351
  };
19475
- if (!existsSync22(targetPath)) {
19476
- await mkdir7(resolve6(targetPath, ".."), { recursive: true });
19477
- await writeFile4(targetPath, sourceContent, "utf-8");
20352
+ if (!existsSync23(targetPath)) {
20353
+ await mkdir7(resolve7(targetPath, ".."), { recursive: true });
20354
+ await writeFile5(targetPath, sourceContent, "utf-8");
19478
20355
  updated.push(targetRelativePath);
19479
20356
  manifestState.manifestAssetUpdates[targetRelativePath] = nextAsset;
19480
20357
  return;
19481
20358
  }
19482
- const existingContent = await readFile8(targetPath, "utf-8");
20359
+ const existingContent = await readFile9(targetPath, "utf-8");
19483
20360
  const existingAsset = manifestState.existingManifestAssets[targetRelativePath];
19484
20361
  const isManifestManaged = existingAsset?.ownership === "managed";
19485
20362
  if (existingContent === sourceContent) {
@@ -19492,7 +20369,7 @@ async function syncOpenCodeAgentFile(sourceFile, acc, ctx, manifestState) {
19492
20369
  }
19493
20370
  return;
19494
20371
  }
19495
- await writeFile4(targetPath, sourceContent, "utf-8");
20372
+ await writeFile5(targetPath, sourceContent, "utf-8");
19496
20373
  updated.push(targetRelativePath);
19497
20374
  manifestState.manifestAssetUpdates[targetRelativePath] = nextAsset;
19498
20375
  }
@@ -20286,22 +21163,22 @@ function formatChecks2(checks) {
20286
21163
  init_common();
20287
21164
 
20288
21165
  // execution/sandcastle-execution.ts
20289
- import { existsSync as existsSync24, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
20290
- import { join as join27 } from "path";
21166
+ import { existsSync as existsSync25, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
21167
+ import { join as join28 } from "path";
20291
21168
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
20292
21169
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
20293
21170
 
20294
21171
  // execution/execution-provider.ts
20295
21172
  init_common();
20296
21173
  import { mkdtempSync } from "fs";
20297
- import { writeFile as writeFile5 } from "fs/promises";
21174
+ import { writeFile as writeFile6 } from "fs/promises";
20298
21175
  import { tmpdir } from "os";
20299
- import { dirname as dirname8, join as join25 } from "path";
21176
+ import { dirname as dirname9, join as join26 } from "path";
20300
21177
  async function writeExecutionArtifacts(worktreePath, artifacts) {
20301
21178
  for (const artifact of artifacts) {
20302
- const filePath = join25(worktreePath, artifact.path);
20303
- await ensureDir(dirname8(filePath));
20304
- await writeFile5(filePath, artifact.content, "utf-8");
21179
+ const filePath = join26(worktreePath, artifact.path);
21180
+ await ensureDir(dirname9(filePath));
21181
+ await writeFile6(filePath, artifact.content, "utf-8");
20305
21182
  }
20306
21183
  }
20307
21184
 
@@ -20314,18 +21191,18 @@ import path7 from "path";
20314
21191
 
20315
21192
  // execution/sandbox-image.ts
20316
21193
  import { createHash as createHash3 } from "crypto";
20317
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
21194
+ import { existsSync as existsSync24, readFileSync as readFileSync22 } from "fs";
20318
21195
  import path6 from "path";
20319
21196
  function sandboxImageName(repoRoot2, installIcm) {
20320
21197
  const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
20321
21198
  const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
20322
21199
  const baseName = sanitized || "local";
20323
21200
  const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
20324
- if (!existsSync23(dockerfilePath)) {
21201
+ if (!existsSync24(dockerfilePath)) {
20325
21202
  const base2 = `sandcastle:${baseName}`;
20326
21203
  return installIcm ? `${base2}-icm` : base2;
20327
21204
  }
20328
- 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);
20329
21206
  const base = `sandcastle:${baseName}-${fingerprint}`;
20330
21207
  return installIcm ? `${base}-icm` : base;
20331
21208
  }
@@ -20361,7 +21238,7 @@ async function createSandboxFromExistingWorktree(options) {
20361
21238
  }
20362
21239
 
20363
21240
  // execution/sandbox-options.ts
20364
- import { join as join26 } from "path";
21241
+ import { join as join27 } from "path";
20365
21242
  var POURKIT_ICM_CONTAINER_DIR = "/home/agent/.pourkit/icm";
20366
21243
  function buildSandboxOptions(repoRoot2, sandbox, memory) {
20367
21244
  const mounts = [];
@@ -20370,12 +21247,12 @@ function buildSandboxOptions(repoRoot2, sandbox, memory) {
20370
21247
  }
20371
21248
  if (memory?.available) {
20372
21249
  mounts.push({
20373
- hostPath: join26(repoRoot2, ".pourkit", "icm"),
21250
+ hostPath: join27(repoRoot2, ".pourkit", "icm"),
20374
21251
  sandboxPath: POURKIT_ICM_CONTAINER_DIR,
20375
21252
  readonly: false
20376
21253
  });
20377
21254
  mounts.push({
20378
- hostPath: join26(repoRoot2, ".pourkit", "icm", "cache"),
21255
+ hostPath: join27(repoRoot2, ".pourkit", "icm", "cache"),
20379
21256
  sandboxPath: "/home/agent/.cache/icm",
20380
21257
  readonly: false
20381
21258
  });
@@ -20509,13 +21386,13 @@ var SandcastleExecutionSession = class {
20509
21386
  "Memory enabled but memory context is invalid. Run `pourkit memory init` or install ICM for host planning use."
20510
21387
  );
20511
21388
  }
20512
- const hostMemoryDir = join27(repoRoot2, ".pourkit", "icm");
20513
- if (!existsSync24(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
21389
+ const hostMemoryDir = join28(repoRoot2, ".pourkit", "icm");
21390
+ if (!existsSync25(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
20514
21391
  throw new MissingHostMemoryDirError(
20515
21392
  "Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
20516
21393
  );
20517
21394
  }
20518
- mkdirSync9(join27(hostMemoryDir, "cache"), { recursive: true });
21395
+ mkdirSync9(join28(hostMemoryDir, "cache"), { recursive: true });
20519
21396
  }
20520
21397
  const sandboxOptions = buildSandboxOptions(
20521
21398
  repoRoot2,
@@ -20674,8 +21551,8 @@ function sanitizeBranch(branchName) {
20674
21551
  }
20675
21552
  function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
20676
21553
  return paths.filter((relativePath) => {
20677
- const source = join27(repoRoot2, relativePath);
20678
- if (!existsSync24(source)) {
21554
+ const source = join28(repoRoot2, relativePath);
21555
+ if (!existsSync25(source)) {
20679
21556
  return true;
20680
21557
  }
20681
21558
  try {
@@ -20685,8 +21562,8 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
20685
21562
  } catch {
20686
21563
  return true;
20687
21564
  }
20688
- const destination = join27(worktreePath, relativePath);
20689
- return !existsSync24(destination);
21565
+ const destination = join28(worktreePath, relativePath);
21566
+ return !existsSync25(destination);
20690
21567
  });
20691
21568
  }
20692
21569
  function formatAgentStreamEvent(event) {
@@ -20750,12 +21627,12 @@ function isPlainObject(value) {
20750
21627
  return typeof value === "object" && value !== null && !Array.isArray(value);
20751
21628
  }
20752
21629
  function savePromptToFile(repoRoot2, stage, iteration, prompt) {
20753
- const promptsDir = join27(repoRoot2, ".pourkit", ".tmp", "prompts");
21630
+ const promptsDir = join28(repoRoot2, ".pourkit", ".tmp", "prompts");
20754
21631
  mkdirSync9(promptsDir, { recursive: true });
20755
21632
  const timestamp2 = Date.now();
20756
21633
  const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
20757
21634
  const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
20758
- const filePath = join27(promptsDir, filename);
21635
+ const filePath = join28(promptsDir, filename);
20759
21636
  writeFileSync6(filePath, prompt, "utf-8");
20760
21637
  }
20761
21638
 
@@ -21382,6 +22259,24 @@ function createCliProgram(version) {
21382
22259
  console.log("Schema assets are up to date.");
21383
22260
  }
21384
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
+ );
21385
22280
  const syncCommand = program.command("sync").description("Sync commands for refreshing managed assets");
21386
22281
  syncCommand.command("workflow-pack").description("Refresh managed workflow pack assets from packaged sources").option("--cwd <path>", "target repository directory").action(async (options) => {
21387
22282
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
@@ -21539,11 +22434,11 @@ function createCliProgram(version) {
21539
22434
  return program;
21540
22435
  }
21541
22436
  async function resolveCliVersion() {
21542
- if (isPackageVersion("0.0.0-next-20260714073403")) {
21543
- return "0.0.0-next-20260714073403";
22437
+ if (isPackageVersion("0.0.0-next-20260723092204")) {
22438
+ return "0.0.0-next-20260723092204";
21544
22439
  }
21545
- if (isReleaseVersion("0.0.0-next-20260714073403")) {
21546
- return "0.0.0-next-20260714073403";
22440
+ if (isReleaseVersion("0.0.0-next-20260723092204")) {
22441
+ return "0.0.0-next-20260723092204";
21547
22442
  }
21548
22443
  try {
21549
22444
  const root = repoRoot();