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