@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 +1144 -249
- package/dist/cli.js.map +1 -1
- package/dist/e2e/run-live-e2e.js +33 -8
- package/dist/e2e/run-live-e2e.js.map +1 -1
- package/dist/schema/pourkit.schema.hash +1 -1
- package/dist/schema/pourkit.schema.json +281 -90
- package/package.json +1 -1
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((
|
|
61
|
+
await new Promise((resolve8) => {
|
|
62
62
|
if (!fileStream) {
|
|
63
|
-
|
|
63
|
+
resolve8();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
const timer = setTimeout(() => {
|
|
67
67
|
if (!fileStream.destroyed) {
|
|
68
68
|
fileStream.destroy();
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
resolve8();
|
|
71
71
|
}, 2e3);
|
|
72
72
|
fileStream.end(() => {
|
|
73
73
|
clearTimeout(timer);
|
|
74
|
-
|
|
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((
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
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 (!
|
|
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:
|
|
902
|
-
const raw = await
|
|
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:
|
|
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
|
|
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 =
|
|
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 {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
12356
|
-
|
|
12357
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
13802
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
14671
|
-
|
|
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:
|
|
14809
|
-
|
|
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
|
-
`${
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
17989
|
-
const memoryDbPath =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
18154
|
-
import { mkdir as mkdir6, readFile as
|
|
18155
|
-
import { resolve as
|
|
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((
|
|
18560
|
+
return candidates.find((c) => existsSync21(c)) ?? candidates[0];
|
|
18167
18561
|
}
|
|
18168
18562
|
var PACKAGED_SCHEMA_PATH = resolvePackagedAssetPath2("pourkit.schema.json");
|
|
18169
|
-
var
|
|
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(
|
|
18175
|
-
const ajv = new
|
|
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
|
|
19020
|
+
return readFileSync21(PACKAGED_HASH_PATH, "utf-8");
|
|
18189
19021
|
}
|
|
18190
19022
|
function localSchemaDir(repoRoot2) {
|
|
18191
|
-
return
|
|
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 =
|
|
18199
|
-
const sourcePath =
|
|
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) =>
|
|
19033
|
+
return candidates.find((candidate) => existsSync22(candidate)) ?? bundledPath;
|
|
18202
19034
|
}
|
|
18203
19035
|
function resolvePackagedOpenCodeAgentsDir() {
|
|
18204
19036
|
const candidates = [
|
|
18205
|
-
|
|
18206
|
-
|
|
18207
|
-
|
|
19037
|
+
resolve6(__dirname4, ".opencode", "agents"),
|
|
19038
|
+
resolve6(__dirname4, "..", ".opencode", "agents"),
|
|
19039
|
+
resolve6(__dirname4, "..", "..", ".opencode", "agents")
|
|
18208
19040
|
];
|
|
18209
|
-
return candidates.find((candidate) =>
|
|
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) =>
|
|
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 =
|
|
19052
|
+
const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
|
|
18221
19053
|
try {
|
|
18222
|
-
const content = await
|
|
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
|
|
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 =
|
|
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 =
|
|
18344
|
-
if (!
|
|
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
|
|
19188
|
+
const localContent = await readFile8(localPath, "utf-8");
|
|
18357
19189
|
if (localContent !== packagedContent) {
|
|
18358
|
-
const overridePath =
|
|
19190
|
+
const overridePath = resolve6(
|
|
18359
19191
|
cwd,
|
|
18360
19192
|
`.pourkit/overrides/docs/agents/${docName}`
|
|
18361
19193
|
);
|
|
18362
|
-
if (
|
|
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 =
|
|
18385
|
-
const skillSourcePath =
|
|
18386
|
-
if (!
|
|
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 (!
|
|
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
|
|
19241
|
+
const localContent = await readFile8(skillSourcePath, "utf-8");
|
|
18410
19242
|
if (localContent !== packagedContent) {
|
|
18411
|
-
const overridePath =
|
|
19243
|
+
const overridePath = resolve6(
|
|
18412
19244
|
cwd,
|
|
18413
19245
|
`.pourkit/overrides/skills/${skillName}/SKILL.md`
|
|
18414
19246
|
);
|
|
18415
|
-
if (
|
|
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 =
|
|
18438
|
-
if (!
|
|
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 =
|
|
18449
|
-
const projectionPath =
|
|
18450
|
-
if (!
|
|
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 (!
|
|
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
|
|
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 =
|
|
18495
|
-
if (!
|
|
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
|
|
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 =
|
|
18523
|
-
if (
|
|
19354
|
+
const agentsPath = resolve6(cwd, "AGENTS.md");
|
|
19355
|
+
if (existsSync22(agentsPath)) {
|
|
18524
19356
|
try {
|
|
18525
|
-
const agentsContent = await
|
|
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 =
|
|
18546
|
-
if (
|
|
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 =
|
|
18561
|
-
if (
|
|
19392
|
+
const configPath = resolve6(cwd, ".pourkit/config.json");
|
|
19393
|
+
if (existsSync22(configPath)) {
|
|
18562
19394
|
try {
|
|
18563
|
-
const configContent = await
|
|
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 =
|
|
19400
|
+
const addonDir = resolve6(
|
|
18569
19401
|
cwd,
|
|
18570
19402
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
18571
19403
|
);
|
|
18572
|
-
if (!
|
|
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 =
|
|
19414
|
+
const addonDocPath = resolve6(
|
|
18583
19415
|
cwd,
|
|
18584
19416
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
18585
19417
|
);
|
|
18586
|
-
if (!
|
|
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
|
|
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 =
|
|
19459
|
+
const addonDir = resolve6(
|
|
18628
19460
|
cwd,
|
|
18629
19461
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
18630
19462
|
);
|
|
18631
|
-
if (
|
|
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 =
|
|
19473
|
+
const addonDocPath = resolve6(
|
|
18642
19474
|
cwd,
|
|
18643
19475
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
18644
19476
|
);
|
|
18645
|
-
if (
|
|
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 =
|
|
18659
|
-
if (
|
|
19490
|
+
const manifestPath = resolve6(cwd, ".pourkit/manifest.json");
|
|
19491
|
+
if (existsSync22(manifestPath)) {
|
|
18660
19492
|
try {
|
|
18661
|
-
const manifestContent = await
|
|
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 =
|
|
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 =
|
|
18682
|
-
if (
|
|
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 =
|
|
18696
|
-
if (
|
|
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
|
|
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 =
|
|
18717
|
-
if (
|
|
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 =
|
|
18722
|
-
if (!
|
|
19553
|
+
const skillFile = resolve6(managedSkillsDir, entry.name, "SKILL.md");
|
|
19554
|
+
if (!existsSync22(skillFile)) continue;
|
|
18723
19555
|
try {
|
|
18724
|
-
const content = await
|
|
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 =
|
|
18740
|
-
if (
|
|
19571
|
+
const managedPromptsDir = resolve6(cwd, ".pourkit/managed/prompts");
|
|
19572
|
+
if (existsSync22(managedPromptsDir)) {
|
|
18741
19573
|
for (const promptName of catalog.prompts) {
|
|
18742
|
-
const promptFile =
|
|
18743
|
-
if (!
|
|
19574
|
+
const promptFile = resolve6(managedPromptsDir, promptName);
|
|
19575
|
+
if (!existsSync22(promptFile)) continue;
|
|
18744
19576
|
try {
|
|
18745
|
-
const content = await
|
|
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 =
|
|
19593
|
+
const addonSkillFile = resolve6(
|
|
18762
19594
|
cwd,
|
|
18763
19595
|
`.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
|
|
18764
19596
|
);
|
|
18765
|
-
if (!
|
|
19597
|
+
if (!existsSync22(addonSkillFile)) continue;
|
|
18766
19598
|
try {
|
|
18767
|
-
const content = await
|
|
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 =
|
|
19614
|
+
const addonDocFile = resolve6(
|
|
18783
19615
|
cwd,
|
|
18784
19616
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
18785
19617
|
);
|
|
18786
|
-
if (!
|
|
19618
|
+
if (!existsSync22(addonDocFile)) continue;
|
|
18787
19619
|
try {
|
|
18788
|
-
const content = await
|
|
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 =
|
|
18820
|
-
const gitignoreContent =
|
|
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 =
|
|
18828
|
-
const hasMemoryDb =
|
|
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 =
|
|
18842
|
-
if (!
|
|
18843
|
-
const content =
|
|
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 =
|
|
18878
|
-
const localHashPath =
|
|
18879
|
-
const localSchemaExists =
|
|
18880
|
-
const localHashExists =
|
|
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
|
|
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 =
|
|
19728
|
+
const configPath = resolve6(repoRootPath, ".pourkit/config.json");
|
|
18897
19729
|
let configValidation;
|
|
18898
19730
|
let rawMemoryConfig;
|
|
18899
|
-
|
|
19731
|
+
let configMigration;
|
|
19732
|
+
if (existsSync22(configPath)) {
|
|
18900
19733
|
try {
|
|
18901
|
-
const raw = JSON.parse(
|
|
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) =>
|
|
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 (
|
|
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
|
|
18979
|
-
const packagedHash = await
|
|
18980
|
-
const localSchemaPath =
|
|
18981
|
-
const localHashPath =
|
|
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 (
|
|
18985
|
-
const existing = await
|
|
19848
|
+
if (existsSync22(localSchemaPath)) {
|
|
19849
|
+
const existing = await readFile8(localSchemaPath, "utf-8");
|
|
18986
19850
|
if (existing !== packagedSchema) {
|
|
18987
|
-
await
|
|
19851
|
+
await writeFile4(localSchemaPath, packagedSchema, "utf-8");
|
|
18988
19852
|
schemaWritten = true;
|
|
18989
19853
|
}
|
|
18990
19854
|
} else {
|
|
18991
|
-
await
|
|
19855
|
+
await writeFile4(localSchemaPath, packagedSchema, "utf-8");
|
|
18992
19856
|
schemaWritten = true;
|
|
18993
19857
|
}
|
|
18994
|
-
if (
|
|
18995
|
-
const existing = await
|
|
19858
|
+
if (existsSync22(localHashPath)) {
|
|
19859
|
+
const existing = await readFile8(localHashPath, "utf-8");
|
|
18996
19860
|
if (existing !== packagedHash) {
|
|
18997
|
-
await
|
|
19861
|
+
await writeFile4(localHashPath, packagedHash, "utf-8");
|
|
18998
19862
|
hashWritten = true;
|
|
18999
19863
|
}
|
|
19000
19864
|
} else {
|
|
19001
|
-
await
|
|
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
|
|
19013
|
-
import { mkdir as mkdir7, readFile as
|
|
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
|
|
19016
|
-
import { fileURLToPath as
|
|
19017
|
-
var
|
|
19018
|
-
var
|
|
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 =
|
|
19028
|
-
const sourcePath =
|
|
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) =>
|
|
19894
|
+
return candidates.find((candidate) => existsSync23(candidate)) ?? bundledPath;
|
|
19031
19895
|
}
|
|
19032
19896
|
function resolvePackagedOpenCodeAgentsDir2() {
|
|
19033
19897
|
const candidates = [
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19898
|
+
resolve7(__dirname5, ".opencode", "agents"),
|
|
19899
|
+
resolve7(__dirname5, "..", ".opencode", "agents"),
|
|
19900
|
+
resolve7(__dirname5, "..", "..", ".opencode", "agents")
|
|
19037
19901
|
];
|
|
19038
|
-
return candidates.find((candidate) =>
|
|
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) =>
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
19161
|
-
if (
|
|
19162
|
-
const existing = await
|
|
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
|
|
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 =
|
|
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 =
|
|
19188
|
-
if (!
|
|
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 =
|
|
19198
|
-
if (
|
|
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 =
|
|
19204
|
-
if (
|
|
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 =
|
|
19216
|
-
const content = await
|
|
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 =
|
|
20116
|
+
const manifestPath = resolve7(cwd, ".pourkit", "manifest.json");
|
|
19232
20117
|
let manifestReadFailed = false;
|
|
19233
20118
|
let manifest = {};
|
|
19234
|
-
const existingRawManifest = await
|
|
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
|
-
|
|
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 =
|
|
19377
|
-
if (!
|
|
20253
|
+
const filePath = resolve7(cwd, fileName);
|
|
20254
|
+
if (!existsSync23(filePath)) {
|
|
19378
20255
|
if (!options2.createIfMissing) return false;
|
|
19379
|
-
await
|
|
20256
|
+
await writeFile5(filePath, fullManagedBlock, "utf-8");
|
|
19380
20257
|
return true;
|
|
19381
20258
|
}
|
|
19382
|
-
const existing = await
|
|
20259
|
+
const existing = await readFile9(filePath, "utf-8");
|
|
19383
20260
|
const newContent = upsertManagedBlock(existing, managedBlockContent);
|
|
19384
20261
|
if (newContent !== existing) {
|
|
19385
|
-
await
|
|
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(
|
|
19421
|
-
await
|
|
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 =
|
|
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 (!
|
|
19476
|
-
await mkdir7(
|
|
19477
|
-
await
|
|
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
|
|
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
|
|
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
|
|
20290
|
-
import { join as
|
|
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
|
|
21174
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
20298
21175
|
import { tmpdir } from "os";
|
|
20299
|
-
import { dirname as
|
|
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 =
|
|
20303
|
-
await ensureDir(
|
|
20304
|
-
await
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
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:
|
|
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:
|
|
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 =
|
|
20513
|
-
if (!
|
|
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(
|
|
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 =
|
|
20678
|
-
if (!
|
|
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 =
|
|
20689
|
-
return !
|
|
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 =
|
|
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 =
|
|
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-
|
|
21543
|
-
return "0.0.0-next-
|
|
22437
|
+
if (isPackageVersion("0.0.0-next-20260723092204")) {
|
|
22438
|
+
return "0.0.0-next-20260723092204";
|
|
21544
22439
|
}
|
|
21545
|
-
if (isReleaseVersion("0.0.0-next-
|
|
21546
|
-
return "0.0.0-next-
|
|
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();
|