@pourkit/cli 0.0.0-next-20260619045442 → 0.0.0-next-20260619094345
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 +452 -2652
- package/dist/cli.js.map +1 -1
- package/dist/e2e/run-live-e2e.js +43 -416
- package/dist/e2e/run-live-e2e.js.map +1 -1
- package/dist/managed/skills/to-issues/SKILL.md +13 -14
- package/dist/managed/skills/to-plan/SKILL.md +1 -1
- package/dist/schema/pourkit.schema.hash +1 -1
- package/dist/schema/pourkit.schema.json +2 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -869,20 +869,6 @@ function parseConfig(raw) {
|
|
|
869
869
|
function getVerificationCommands(target) {
|
|
870
870
|
return target.strategy.verify?.commands ?? [];
|
|
871
871
|
}
|
|
872
|
-
function resolvePrdRunMode(target, opts) {
|
|
873
|
-
if (opts?.localOverride === true) {
|
|
874
|
-
return { mode: "local", source: "cli-override", targetName: target.name };
|
|
875
|
-
}
|
|
876
|
-
const configMode = target.prdRun?.mode;
|
|
877
|
-
if (configMode) {
|
|
878
|
-
return {
|
|
879
|
-
mode: configMode,
|
|
880
|
-
source: "target-config",
|
|
881
|
-
targetName: target.name
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
return { mode: "github", source: "default", targetName: target.name };
|
|
885
|
-
}
|
|
886
872
|
function shouldCleanupRepositoryAfterPrdDrain(target) {
|
|
887
873
|
return target.prdRun?.cleanupRepository ?? true;
|
|
888
874
|
}
|
|
@@ -894,11 +880,11 @@ var OBSOLETE_CONFIG_PATHS = [
|
|
|
894
880
|
];
|
|
895
881
|
var CANONICAL_CONFIG_PATH = ".pourkit/config.json";
|
|
896
882
|
async function loadRepoConfig(repoRoot2, _configFileName) {
|
|
897
|
-
const { existsSync:
|
|
883
|
+
const { existsSync: existsSync21 } = await import("fs");
|
|
898
884
|
const { join: pjoin } = await import("path");
|
|
899
885
|
for (const obPath of OBSOLETE_CONFIG_PATHS) {
|
|
900
886
|
const fullPath = pjoin(repoRoot2, obPath);
|
|
901
|
-
if (
|
|
887
|
+
if (existsSync21(fullPath)) {
|
|
902
888
|
const isRootJson = obPath === "pourkit.json";
|
|
903
889
|
throw new Error(
|
|
904
890
|
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".`
|
|
@@ -906,13 +892,13 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
|
|
|
906
892
|
}
|
|
907
893
|
}
|
|
908
894
|
const configPath = pjoin(repoRoot2, CANONICAL_CONFIG_PATH);
|
|
909
|
-
if (!
|
|
895
|
+
if (!existsSync21(configPath)) {
|
|
910
896
|
throw new Error(
|
|
911
897
|
`No Pourkit config found at ${CANONICAL_CONFIG_PATH}. Run pourkit init or create ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
|
|
912
898
|
);
|
|
913
899
|
}
|
|
914
|
-
const { readFile:
|
|
915
|
-
const raw = await
|
|
900
|
+
const { readFile: readFile7 } = await import("fs/promises");
|
|
901
|
+
const raw = await readFile7(configPath, "utf-8");
|
|
916
902
|
let parsed;
|
|
917
903
|
try {
|
|
918
904
|
parsed = JSON.parse(raw);
|
|
@@ -923,10 +909,10 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
|
|
|
923
909
|
return parseConfig(parsed);
|
|
924
910
|
}
|
|
925
911
|
async function loadConfig(configPath) {
|
|
926
|
-
const { readFile:
|
|
912
|
+
const { readFile: readFile7 } = await import("fs/promises");
|
|
927
913
|
const ext = configPath.split(".").pop()?.toLowerCase();
|
|
928
914
|
if (ext === "json") {
|
|
929
|
-
const raw = await
|
|
915
|
+
const raw = await readFile7(configPath, "utf-8");
|
|
930
916
|
return parseConfig(JSON.parse(raw));
|
|
931
917
|
}
|
|
932
918
|
if (ext === "mjs" || ext === "js" || ext === "ts") {
|
|
@@ -1148,10 +1134,9 @@ async function cleanupRepository(options) {
|
|
|
1148
1134
|
}
|
|
1149
1135
|
|
|
1150
1136
|
// commands/artifact-validation.ts
|
|
1151
|
-
import { createHash } from "crypto";
|
|
1152
1137
|
import { execSync } from "child_process";
|
|
1153
|
-
import { existsSync as existsSync7,
|
|
1154
|
-
import { isAbsolute as isAbsolute3,
|
|
1138
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
1139
|
+
import { isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
|
|
1155
1140
|
|
|
1156
1141
|
// pr/review-verdict.ts
|
|
1157
1142
|
var ReviewVerdictProtocolError = class extends Error {
|
|
@@ -3670,18 +3655,6 @@ function validateAgentArtifact(options) {
|
|
|
3670
3655
|
}
|
|
3671
3656
|
return valid(options, [`decision: ${parsed.json.recoveryDecision}`]);
|
|
3672
3657
|
}
|
|
3673
|
-
case "local-prd":
|
|
3674
|
-
case "local-issue":
|
|
3675
|
-
case "local-triage":
|
|
3676
|
-
return invalid(
|
|
3677
|
-
options,
|
|
3678
|
-
`Local artifact kind must be validated through runLocalValidateArtifactCommand`
|
|
3679
|
-
);
|
|
3680
|
-
case "local-prepare":
|
|
3681
|
-
return invalid(
|
|
3682
|
-
options,
|
|
3683
|
-
`Artifact kind "${options.kind}" has been removed from PRD Run validation`
|
|
3684
|
-
);
|
|
3685
3658
|
}
|
|
3686
3659
|
} catch (error) {
|
|
3687
3660
|
if (error instanceof ReviewArtifactValidationError || error instanceof ReviewVerdictProtocolError || error instanceof PrDescriptionProtocolError || error instanceof ConflictResolutionArtifactProtocolError || error instanceof RecoveryArtifactInvalid || error instanceof Error) {
|
|
@@ -3735,874 +3708,127 @@ function isIssueFinalReviewAuditedPath(path9) {
|
|
|
3735
3708
|
function shellQuote(value) {
|
|
3736
3709
|
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
3737
3710
|
}
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
break;
|
|
3748
|
-
case "local-issue":
|
|
3749
|
-
localResult = validateLocalIssue(resolvedPath);
|
|
3750
|
-
break;
|
|
3751
|
-
case "local-triage":
|
|
3752
|
-
localResult = validateLocalTriage(resolvedPath, resolvedExtra);
|
|
3753
|
-
break;
|
|
3754
|
-
default:
|
|
3755
|
-
return {
|
|
3756
|
-
ok: false,
|
|
3757
|
-
kind: options.kind,
|
|
3758
|
-
artifactPath: resolvedPath,
|
|
3759
|
-
reason: `Unsupported local artifact kind "${options.kind}"`,
|
|
3760
|
-
diagnostics: []
|
|
3761
|
-
};
|
|
3762
|
-
}
|
|
3763
|
-
if (localResult.ok) {
|
|
3764
|
-
return {
|
|
3765
|
-
ok: true,
|
|
3766
|
-
kind: options.kind,
|
|
3767
|
-
artifactPath: resolvedPath,
|
|
3768
|
-
diagnostics: []
|
|
3769
|
-
};
|
|
3770
|
-
}
|
|
3771
|
-
const diagnostics = [];
|
|
3772
|
-
if (localResult.gate) diagnostics.push(`gate: ${localResult.gate}`);
|
|
3773
|
-
if (localResult.failureCode)
|
|
3774
|
-
diagnostics.push(`failureCode: ${localResult.failureCode}`);
|
|
3775
|
-
if (localResult.offendingPath)
|
|
3776
|
-
diagnostics.push(`offendingPath: ${localResult.offendingPath}`);
|
|
3777
|
-
if (localResult.message) diagnostics.push(localResult.message);
|
|
3778
|
-
return {
|
|
3779
|
-
ok: false,
|
|
3780
|
-
kind: options.kind,
|
|
3781
|
-
artifactPath: resolvedPath,
|
|
3782
|
-
reason: localResult.message ?? "Validation failed",
|
|
3783
|
-
diagnostics
|
|
3784
|
-
};
|
|
3711
|
+
|
|
3712
|
+
// commands/issue-run.ts
|
|
3713
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
|
|
3714
|
+
import { isAbsolute as isAbsolute5, join as join14 } from "path";
|
|
3715
|
+
|
|
3716
|
+
// pr/templates.ts
|
|
3717
|
+
init_common();
|
|
3718
|
+
function renderBranchName(template, issue) {
|
|
3719
|
+
return renderTemplate(template, issue);
|
|
3785
3720
|
}
|
|
3786
|
-
function
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
const parsed = JSON.parse(raw);
|
|
3790
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3791
|
-
return {
|
|
3792
|
-
ok: false,
|
|
3793
|
-
failureCode: failureCode ?? "invalid_prd_artifact",
|
|
3794
|
-
offendingPath: path9,
|
|
3795
|
-
message: `Expected a JSON object, got ${Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed}`
|
|
3796
|
-
};
|
|
3797
|
-
}
|
|
3798
|
-
return { ok: true, data: parsed };
|
|
3799
|
-
} catch (e) {
|
|
3800
|
-
return {
|
|
3801
|
-
ok: false,
|
|
3802
|
-
failureCode: failureCode ?? "invalid_prd_artifact",
|
|
3803
|
-
offendingPath: path9,
|
|
3804
|
-
message: `Failed to read or parse: ${e}`
|
|
3805
|
-
};
|
|
3806
|
-
}
|
|
3721
|
+
function renderTemplate(template, issue) {
|
|
3722
|
+
const issueSlug = slugify(issue.title);
|
|
3723
|
+
return template.replace(/\{\{issue\.number\}\}/g, String(issue.number)).replace(/\{\{issue\.title\}\}/g, issue.title).replace(/\{\{issue\.body\}\}/g, issue.body).replace(/\{\{issue\.slug\}\}/g, issueSlug).replace(/\{\{issue\.state\}\}/g, issue.state);
|
|
3807
3724
|
}
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
var TO_ISSUES_REQUIRED_HEADINGS = [
|
|
3829
|
-
"Parent",
|
|
3830
|
-
"Plan Documents",
|
|
3831
|
-
"Source of truth for behavior",
|
|
3832
|
-
"What to build",
|
|
3833
|
-
"High-level explanation",
|
|
3834
|
-
"Affected code paths",
|
|
3835
|
-
"Exploration evidence",
|
|
3836
|
-
"Contract decisions",
|
|
3837
|
-
"Execution path matrix",
|
|
3838
|
-
"Regression contract (CRITICAL)",
|
|
3839
|
-
"Step-by-step implementation",
|
|
3840
|
-
"Contracts / interfaces",
|
|
3841
|
-
"Edge cases",
|
|
3842
|
-
"Validation",
|
|
3843
|
-
"Out of scope",
|
|
3844
|
-
"Priority",
|
|
3845
|
-
"Acceptance criteria",
|
|
3846
|
-
"Blocked by"
|
|
3847
|
-
];
|
|
3848
|
-
var VALID_LOCAL_ISSUE_STATUSES = [
|
|
3849
|
-
"needs-triage",
|
|
3850
|
-
"ready-for-agent",
|
|
3851
|
-
"blocked",
|
|
3852
|
-
"ready-for-human",
|
|
3853
|
-
"wontfix",
|
|
3854
|
-
"running",
|
|
3855
|
-
"complete"
|
|
3856
|
-
];
|
|
3857
|
-
var VALID_ISSUE_TRIAGE_LABELS = [
|
|
3858
|
-
"needs-triage",
|
|
3859
|
-
"ready-for-agent",
|
|
3860
|
-
"blocked",
|
|
3861
|
-
"ready-for-human",
|
|
3862
|
-
"wontfix",
|
|
3863
|
-
"running",
|
|
3864
|
-
"complete"
|
|
3865
|
-
];
|
|
3866
|
-
var VALID_ISSUE_CATEGORY_LABELS = ["bug", "enhancement"];
|
|
3867
|
-
var VALID_ISSUE_TYPE_LABELS = [
|
|
3868
|
-
"type:bugfix",
|
|
3869
|
-
"type:infra",
|
|
3870
|
-
"type:feature",
|
|
3871
|
-
"type:polish",
|
|
3872
|
-
"type:refactor"
|
|
3873
|
-
];
|
|
3874
|
-
var ISSUE_RECEIPT_FIELDS = [
|
|
3875
|
-
"createdAt",
|
|
3876
|
-
"updatedAt",
|
|
3877
|
-
"preparedAt",
|
|
3878
|
-
"startedAt",
|
|
3879
|
-
"completedAt",
|
|
3880
|
-
"reviewedAt",
|
|
3881
|
-
"mergedAt"
|
|
3882
|
-
];
|
|
3883
|
-
var ISSUE_PROJECTION_FIELDS = [
|
|
3884
|
-
"issueNumber",
|
|
3885
|
-
"issueUrl",
|
|
3886
|
-
"publishedAt",
|
|
3887
|
-
"syncedAt"
|
|
3888
|
-
];
|
|
3889
|
-
function getMarkdownHeadings(markdown) {
|
|
3890
|
-
const headingRegex = /^## (.+)$/gm;
|
|
3891
|
-
const headings = [];
|
|
3892
|
-
let match;
|
|
3893
|
-
while ((match = headingRegex.exec(markdown)) !== null) {
|
|
3894
|
-
headings.push(match[1].trim());
|
|
3725
|
+
|
|
3726
|
+
// commands/issue-run.ts
|
|
3727
|
+
init_common();
|
|
3728
|
+
|
|
3729
|
+
// commands/base-refresh.ts
|
|
3730
|
+
init_common();
|
|
3731
|
+
async function isIssueBranchStale(worktreePath, baseBranch, logger) {
|
|
3732
|
+
try {
|
|
3733
|
+
await execCapture(
|
|
3734
|
+
"git",
|
|
3735
|
+
["merge-base", "--is-ancestor", baseBranch, "HEAD"],
|
|
3736
|
+
{
|
|
3737
|
+
cwd: worktreePath,
|
|
3738
|
+
logger,
|
|
3739
|
+
label: "git merge-base --is-ancestor"
|
|
3740
|
+
}
|
|
3741
|
+
);
|
|
3742
|
+
return false;
|
|
3743
|
+
} catch {
|
|
3744
|
+
return true;
|
|
3895
3745
|
}
|
|
3896
|
-
return headings;
|
|
3897
3746
|
}
|
|
3898
|
-
function
|
|
3899
|
-
const
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
if (data.kind !== "prd") {
|
|
3911
|
-
return {
|
|
3912
|
-
ok: false,
|
|
3913
|
-
failureCode: "invalid_prd_artifact",
|
|
3914
|
-
offendingPath: path9,
|
|
3915
|
-
message: "kind must be prd"
|
|
3916
|
-
};
|
|
3917
|
-
}
|
|
3918
|
-
if (typeof data.id !== "string" || data.id.trim() === "") {
|
|
3919
|
-
return {
|
|
3920
|
-
ok: false,
|
|
3921
|
-
failureCode: "invalid_prd_artifact",
|
|
3922
|
-
offendingPath: path9,
|
|
3923
|
-
message: "id must be a non-empty string"
|
|
3924
|
-
};
|
|
3925
|
-
}
|
|
3926
|
-
if (typeof data.title !== "string" || data.title.trim() === "") {
|
|
3927
|
-
return {
|
|
3928
|
-
ok: false,
|
|
3929
|
-
failureCode: "invalid_prd_artifact",
|
|
3930
|
-
offendingPath: path9,
|
|
3931
|
-
message: "title must be a non-empty string"
|
|
3932
|
-
};
|
|
3933
|
-
}
|
|
3934
|
-
if (typeof data.status !== "string" || !VALID_LOCAL_PRD_STATUSES.includes(data.status)) {
|
|
3935
|
-
return {
|
|
3936
|
-
ok: false,
|
|
3937
|
-
failureCode: "invalid_prd_artifact",
|
|
3938
|
-
offendingPath: path9,
|
|
3939
|
-
message: `status must be one of: ${VALID_LOCAL_PRD_STATUSES.join(", ")}`
|
|
3940
|
-
};
|
|
3941
|
-
}
|
|
3942
|
-
if (!Array.isArray(data.childIssueIds)) {
|
|
3943
|
-
return {
|
|
3944
|
-
ok: false,
|
|
3945
|
-
failureCode: "invalid_prd_artifact",
|
|
3946
|
-
offendingPath: path9,
|
|
3947
|
-
message: "childIssueIds must be an array"
|
|
3948
|
-
};
|
|
3949
|
-
}
|
|
3950
|
-
if (!Array.isArray(data.linkedDecisionIds)) {
|
|
3951
|
-
return {
|
|
3952
|
-
ok: false,
|
|
3953
|
-
failureCode: "invalid_prd_artifact",
|
|
3954
|
-
offendingPath: path9,
|
|
3955
|
-
message: "linkedDecisionIds must be an array"
|
|
3956
|
-
};
|
|
3957
|
-
}
|
|
3958
|
-
if (!data.reconciliationTarget || typeof data.reconciliationTarget !== "object") {
|
|
3959
|
-
return {
|
|
3960
|
-
ok: false,
|
|
3961
|
-
failureCode: "invalid_prd_artifact",
|
|
3962
|
-
offendingPath: path9,
|
|
3963
|
-
message: "reconciliationTarget must be an object"
|
|
3964
|
-
};
|
|
3965
|
-
}
|
|
3966
|
-
const reconciliationTarget = data.reconciliationTarget;
|
|
3967
|
-
if (typeof reconciliationTarget.localPrdBranch !== "string" || reconciliationTarget.localPrdBranch.trim() === "") {
|
|
3968
|
-
return {
|
|
3969
|
-
ok: false,
|
|
3970
|
-
failureCode: "invalid_prd_artifact",
|
|
3971
|
-
offendingPath: path9,
|
|
3972
|
-
message: "reconciliationTarget.localPrdBranch must be a non-empty string"
|
|
3973
|
-
};
|
|
3974
|
-
}
|
|
3975
|
-
if (typeof reconciliationTarget.integrationBranch !== "string" || reconciliationTarget.integrationBranch.trim() === "") {
|
|
3976
|
-
return {
|
|
3977
|
-
ok: false,
|
|
3978
|
-
failureCode: "invalid_prd_artifact",
|
|
3979
|
-
offendingPath: path9,
|
|
3980
|
-
message: "reconciliationTarget.integrationBranch must be a non-empty string"
|
|
3981
|
-
};
|
|
3982
|
-
}
|
|
3983
|
-
if (typeof data.bodyMarkdown !== "string" || data.bodyMarkdown.trim() === "") {
|
|
3984
|
-
return {
|
|
3985
|
-
ok: false,
|
|
3986
|
-
failureCode: "invalid_prd_artifact",
|
|
3987
|
-
offendingPath: path9,
|
|
3988
|
-
message: "bodyMarkdown must be a non-empty string"
|
|
3989
|
-
};
|
|
3990
|
-
}
|
|
3991
|
-
if (!data.bodyContract || typeof data.bodyContract !== "object") {
|
|
3992
|
-
return {
|
|
3993
|
-
ok: false,
|
|
3994
|
-
failureCode: "invalid_prd_artifact",
|
|
3995
|
-
offendingPath: path9,
|
|
3996
|
-
message: "bodyContract must be an object"
|
|
3997
|
-
};
|
|
3998
|
-
}
|
|
3999
|
-
const bodyContract = data.bodyContract;
|
|
4000
|
-
if (bodyContract.contract !== "to-prd") {
|
|
4001
|
-
return {
|
|
4002
|
-
ok: false,
|
|
4003
|
-
failureCode: "invalid_prd_artifact",
|
|
4004
|
-
offendingPath: path9,
|
|
4005
|
-
message: 'bodyContract.contract must be "to-prd"'
|
|
4006
|
-
};
|
|
4007
|
-
}
|
|
4008
|
-
if (!Array.isArray(bodyContract.requiredHeadings)) {
|
|
4009
|
-
return {
|
|
4010
|
-
ok: false,
|
|
4011
|
-
failureCode: "invalid_prd_artifact",
|
|
4012
|
-
offendingPath: path9,
|
|
4013
|
-
message: "bodyContract.requiredHeadings must be an array"
|
|
4014
|
-
};
|
|
4015
|
-
}
|
|
4016
|
-
const requiredHeadings = bodyContract.requiredHeadings;
|
|
4017
|
-
const missingHeadings = TO_PRD_REQUIRED_HEADINGS.filter(
|
|
4018
|
-
(h) => !requiredHeadings.includes(h)
|
|
4019
|
-
);
|
|
4020
|
-
if (missingHeadings.length > 0) {
|
|
4021
|
-
return {
|
|
4022
|
-
ok: false,
|
|
4023
|
-
failureCode: "invalid_prd_artifact",
|
|
4024
|
-
offendingPath: path9,
|
|
4025
|
-
message: `bodyContract.requiredHeadings missing: ${missingHeadings.join(", ")}`
|
|
4026
|
-
};
|
|
4027
|
-
}
|
|
4028
|
-
const markdownHeadings = getMarkdownHeadings(data.bodyMarkdown);
|
|
4029
|
-
const missingInMarkdown = TO_PRD_REQUIRED_HEADINGS.filter(
|
|
4030
|
-
(h) => !markdownHeadings.includes(h)
|
|
4031
|
-
);
|
|
4032
|
-
if (missingInMarkdown.length > 0) {
|
|
4033
|
-
return {
|
|
4034
|
-
ok: false,
|
|
4035
|
-
failureCode: "invalid_prd_artifact",
|
|
4036
|
-
offendingPath: path9,
|
|
4037
|
-
message: `bodyMarkdown missing headings: ${missingInMarkdown.join(", ")}`
|
|
4038
|
-
};
|
|
3747
|
+
async function refreshStaleIssueBranch(options) {
|
|
3748
|
+
const {
|
|
3749
|
+
worktreePath,
|
|
3750
|
+
baseBranch,
|
|
3751
|
+
localGitBaseRef,
|
|
3752
|
+
logger,
|
|
3753
|
+
prNumber,
|
|
3754
|
+
prState
|
|
3755
|
+
} = options;
|
|
3756
|
+
const stale = await isIssueBranchStale(worktreePath, localGitBaseRef, logger);
|
|
3757
|
+
if (!stale) {
|
|
3758
|
+
return { status: "skipped-current" };
|
|
4039
3759
|
}
|
|
4040
|
-
if (
|
|
3760
|
+
if (prNumber !== void 0 && prState !== void 0) {
|
|
4041
3761
|
return {
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
message: "receipts must be an object"
|
|
3762
|
+
status: "refused-published-history",
|
|
3763
|
+
prNumber,
|
|
3764
|
+
prState
|
|
4046
3765
|
};
|
|
4047
3766
|
}
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
"
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
};
|
|
3767
|
+
try {
|
|
3768
|
+
await execCapture("git", ["rebase", "--autostash", localGitBaseRef], {
|
|
3769
|
+
cwd: worktreePath,
|
|
3770
|
+
logger,
|
|
3771
|
+
label: "git rebase --autostash"
|
|
3772
|
+
});
|
|
3773
|
+
return { status: "refreshed" };
|
|
3774
|
+
} catch (error) {
|
|
3775
|
+
let conflictedPaths = [];
|
|
3776
|
+
try {
|
|
3777
|
+
const statusResult = await execCapture("git", ["status", "--porcelain"], {
|
|
3778
|
+
cwd: worktreePath,
|
|
3779
|
+
logger,
|
|
3780
|
+
label: "git status"
|
|
3781
|
+
});
|
|
3782
|
+
conflictedPaths = statusResult.stdout.split("\n").filter((line) => /^(AA|DD|UU|AU|UA|DU|UD)\s/.test(line)).map((line) => line.slice(3).trim()).filter(Boolean);
|
|
3783
|
+
} catch {
|
|
4066
3784
|
}
|
|
4067
|
-
}
|
|
4068
|
-
if (!data.githubProjection || typeof data.githubProjection !== "object") {
|
|
4069
3785
|
return {
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
message: "githubProjection must be an object"
|
|
3786
|
+
status: "conflicted",
|
|
3787
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3788
|
+
conflictedPaths
|
|
4074
3789
|
};
|
|
4075
3790
|
}
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
};
|
|
3791
|
+
}
|
|
3792
|
+
function invalidateAfterBaseRefresh(state) {
|
|
3793
|
+
return {
|
|
3794
|
+
issueNumber: state.issueNumber,
|
|
3795
|
+
targetName: state.targetName,
|
|
3796
|
+
branchName: state.branchName,
|
|
3797
|
+
baseBranch: state.baseBranch,
|
|
3798
|
+
createdAt: state.createdAt,
|
|
3799
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3800
|
+
completedStages: {
|
|
3801
|
+
builder: state.completedStages.builder
|
|
3802
|
+
},
|
|
3803
|
+
review: {
|
|
3804
|
+
lifetimeIterations: 0
|
|
4091
3805
|
}
|
|
4092
|
-
}
|
|
4093
|
-
return { ok: true };
|
|
3806
|
+
};
|
|
4094
3807
|
}
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
if (data.kind !== "issue") {
|
|
4115
|
-
return {
|
|
4116
|
-
ok: false,
|
|
4117
|
-
failureCode: "invalid_issue_artifact",
|
|
4118
|
-
offendingPath: path9,
|
|
4119
|
-
message: 'kind must be "issue"'
|
|
4120
|
-
};
|
|
4121
|
-
}
|
|
4122
|
-
if (typeof data.id !== "string" || data.id.trim() === "") {
|
|
4123
|
-
return {
|
|
4124
|
-
ok: false,
|
|
4125
|
-
failureCode: "invalid_issue_artifact",
|
|
4126
|
-
offendingPath: path9,
|
|
4127
|
-
message: "id must be a non-empty string"
|
|
4128
|
-
};
|
|
4129
|
-
}
|
|
4130
|
-
if (typeof data.parentPrdId !== "string" || !/^PRD-\d{4}$/.test(data.parentPrdId.trim())) {
|
|
4131
|
-
return {
|
|
4132
|
-
ok: false,
|
|
4133
|
-
failureCode: "invalid_issue_artifact",
|
|
4134
|
-
offendingPath: path9,
|
|
4135
|
-
message: "parentPrdId must match PRD-0000N format"
|
|
4136
|
-
};
|
|
3808
|
+
|
|
3809
|
+
// failure-resolution/effect-runtime.ts
|
|
3810
|
+
import { Effect as Effect4 } from "effect";
|
|
3811
|
+
|
|
3812
|
+
// shared/effect-runtime.ts
|
|
3813
|
+
import { Effect as Effect3, Exit } from "effect";
|
|
3814
|
+
import { UnknownException } from "effect/Cause";
|
|
3815
|
+
var initialized = false;
|
|
3816
|
+
function initializeEffectRuntime() {
|
|
3817
|
+
if (initialized) return;
|
|
3818
|
+
initialized = true;
|
|
3819
|
+
}
|
|
3820
|
+
function runEffect(program) {
|
|
3821
|
+
ensureEffectRuntime();
|
|
3822
|
+
return Effect3.runPromiseExit(program);
|
|
3823
|
+
}
|
|
3824
|
+
function ensureEffectRuntime() {
|
|
3825
|
+
if (!initialized) {
|
|
3826
|
+
initializeEffectRuntime();
|
|
4137
3827
|
}
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
offendingPath: path9,
|
|
4143
|
-
message: "title must be a non-empty string"
|
|
4144
|
-
};
|
|
4145
|
-
}
|
|
4146
|
-
if (typeof data.status !== "string" || !VALID_LOCAL_ISSUE_STATUSES.includes(data.status)) {
|
|
4147
|
-
return {
|
|
4148
|
-
ok: false,
|
|
4149
|
-
failureCode: "invalid_issue_artifact",
|
|
4150
|
-
offendingPath: path9,
|
|
4151
|
-
message: `status must be one of: ${VALID_LOCAL_ISSUE_STATUSES.join(", ")}`
|
|
4152
|
-
};
|
|
4153
|
-
}
|
|
4154
|
-
if (!Array.isArray(data.labels)) {
|
|
4155
|
-
return {
|
|
4156
|
-
ok: false,
|
|
4157
|
-
failureCode: "invalid_issue_artifact",
|
|
4158
|
-
offendingPath: path9,
|
|
4159
|
-
message: "labels must be an array"
|
|
4160
|
-
};
|
|
4161
|
-
}
|
|
4162
|
-
const labels = data.labels;
|
|
4163
|
-
const triageLabel = labels.find((l) => VALID_ISSUE_TRIAGE_LABELS.includes(l));
|
|
4164
|
-
const categoryLabel = labels.find(
|
|
4165
|
-
(l) => VALID_ISSUE_CATEGORY_LABELS.includes(l)
|
|
4166
|
-
);
|
|
4167
|
-
const typeLabel = labels.find((l) => VALID_ISSUE_TYPE_LABELS.includes(l));
|
|
4168
|
-
const unknownLabels = labels.filter(
|
|
4169
|
-
(l) => !VALID_ISSUE_TRIAGE_LABELS.includes(l) && !VALID_ISSUE_CATEGORY_LABELS.includes(l) && !VALID_ISSUE_TYPE_LABELS.includes(l)
|
|
4170
|
-
);
|
|
4171
|
-
if (!triageLabel) {
|
|
4172
|
-
return {
|
|
4173
|
-
ok: false,
|
|
4174
|
-
failureCode: "invalid_issue_artifact",
|
|
4175
|
-
offendingPath: path9,
|
|
4176
|
-
message: `labels must include one triage-state label from: ${VALID_ISSUE_TRIAGE_LABELS.join(", ")}`
|
|
4177
|
-
};
|
|
4178
|
-
}
|
|
4179
|
-
if (!categoryLabel) {
|
|
4180
|
-
return {
|
|
4181
|
-
ok: false,
|
|
4182
|
-
failureCode: "invalid_issue_artifact",
|
|
4183
|
-
offendingPath: path9,
|
|
4184
|
-
message: `labels must include one category label from: ${VALID_ISSUE_CATEGORY_LABELS.join(", ")}`
|
|
4185
|
-
};
|
|
4186
|
-
}
|
|
4187
|
-
if (!typeLabel) {
|
|
4188
|
-
return {
|
|
4189
|
-
ok: false,
|
|
4190
|
-
failureCode: "invalid_issue_artifact",
|
|
4191
|
-
offendingPath: path9,
|
|
4192
|
-
message: `labels must include one type label from: ${VALID_ISSUE_TYPE_LABELS.join(", ")}`
|
|
4193
|
-
};
|
|
4194
|
-
}
|
|
4195
|
-
if (data.status !== "running" && data.status !== "complete" && triageLabel !== data.status) {
|
|
4196
|
-
return {
|
|
4197
|
-
ok: false,
|
|
4198
|
-
failureCode: "invalid_issue_artifact",
|
|
4199
|
-
offendingPath: path9,
|
|
4200
|
-
message: `triage-state label must match status: expected "${data.status}", got "${triageLabel}"`
|
|
4201
|
-
};
|
|
4202
|
-
}
|
|
4203
|
-
const typeLabels = labels.filter((l) => l.startsWith("type:"));
|
|
4204
|
-
if (typeLabels.length > 1) {
|
|
4205
|
-
return {
|
|
4206
|
-
ok: false,
|
|
4207
|
-
failureCode: "invalid_issue_artifact",
|
|
4208
|
-
offendingPath: path9,
|
|
4209
|
-
message: "more than one type:* label found"
|
|
4210
|
-
};
|
|
4211
|
-
}
|
|
4212
|
-
if (labels.length !== 3 || unknownLabels.length > 0) {
|
|
4213
|
-
return {
|
|
4214
|
-
ok: false,
|
|
4215
|
-
failureCode: "invalid_issue_artifact",
|
|
4216
|
-
offendingPath: path9,
|
|
4217
|
-
message: "labels must contain exactly 3 entries: one triage-state, one category, one type:*"
|
|
4218
|
-
};
|
|
4219
|
-
}
|
|
4220
|
-
if (!Array.isArray(data.dependencyIssueIds)) {
|
|
4221
|
-
return {
|
|
4222
|
-
ok: false,
|
|
4223
|
-
failureCode: "invalid_issue_artifact",
|
|
4224
|
-
offendingPath: path9,
|
|
4225
|
-
message: "dependencyIssueIds must be an array"
|
|
4226
|
-
};
|
|
4227
|
-
}
|
|
4228
|
-
if (typeof data.branchName !== "string" || !/^local\/PRD-\d{4}\/I-\d{2}-[a-z0-9-]+$/.test(data.branchName)) {
|
|
4229
|
-
return {
|
|
4230
|
-
ok: false,
|
|
4231
|
-
failureCode: "invalid_issue_artifact",
|
|
4232
|
-
offendingPath: path9,
|
|
4233
|
-
message: "branchName must match local/PRD-0000N/I-0N-slug pattern"
|
|
4234
|
-
};
|
|
4235
|
-
}
|
|
4236
|
-
if (!(data.blockedReason === null || typeof data.blockedReason === "string")) {
|
|
4237
|
-
return {
|
|
4238
|
-
ok: false,
|
|
4239
|
-
failureCode: "invalid_issue_artifact",
|
|
4240
|
-
offendingPath: path9,
|
|
4241
|
-
message: "blockedReason must be a string or null"
|
|
4242
|
-
};
|
|
4243
|
-
}
|
|
4244
|
-
if (!(data.readyForHumanReason === null || typeof data.readyForHumanReason === "string")) {
|
|
4245
|
-
return {
|
|
4246
|
-
ok: false,
|
|
4247
|
-
failureCode: "invalid_issue_artifact",
|
|
4248
|
-
offendingPath: path9,
|
|
4249
|
-
message: "readyForHumanReason must be a string or null"
|
|
4250
|
-
};
|
|
4251
|
-
}
|
|
4252
|
-
if (typeof data.bodyMarkdown !== "string" || data.bodyMarkdown.trim() === "") {
|
|
4253
|
-
return {
|
|
4254
|
-
ok: false,
|
|
4255
|
-
failureCode: "invalid_issue_artifact",
|
|
4256
|
-
offendingPath: path9,
|
|
4257
|
-
message: "bodyMarkdown must be a non-empty string"
|
|
4258
|
-
};
|
|
4259
|
-
}
|
|
4260
|
-
if (!data.bodyContract || typeof data.bodyContract !== "object") {
|
|
4261
|
-
return {
|
|
4262
|
-
ok: false,
|
|
4263
|
-
failureCode: "invalid_issue_artifact",
|
|
4264
|
-
offendingPath: path9,
|
|
4265
|
-
message: "bodyContract must be an object"
|
|
4266
|
-
};
|
|
4267
|
-
}
|
|
4268
|
-
const bodyContract = data.bodyContract;
|
|
4269
|
-
if (bodyContract.contract !== "to-issues") {
|
|
4270
|
-
return {
|
|
4271
|
-
ok: false,
|
|
4272
|
-
failureCode: "invalid_issue_artifact",
|
|
4273
|
-
offendingPath: path9,
|
|
4274
|
-
message: 'bodyContract.contract must be "to-issues"'
|
|
4275
|
-
};
|
|
4276
|
-
}
|
|
4277
|
-
if (!Array.isArray(bodyContract.requiredHeadings)) {
|
|
4278
|
-
return {
|
|
4279
|
-
ok: false,
|
|
4280
|
-
failureCode: "invalid_issue_artifact",
|
|
4281
|
-
offendingPath: path9,
|
|
4282
|
-
message: "bodyContract.requiredHeadings must be an array"
|
|
4283
|
-
};
|
|
4284
|
-
}
|
|
4285
|
-
const requiredHeadings = bodyContract.requiredHeadings;
|
|
4286
|
-
const missingHeadings = TO_ISSUES_REQUIRED_HEADINGS.filter(
|
|
4287
|
-
(h) => !requiredHeadings.includes(h)
|
|
4288
|
-
);
|
|
4289
|
-
if (missingHeadings.length > 0) {
|
|
4290
|
-
return {
|
|
4291
|
-
ok: false,
|
|
4292
|
-
failureCode: "invalid_issue_artifact",
|
|
4293
|
-
offendingPath: path9,
|
|
4294
|
-
message: `bodyContract.requiredHeadings missing: ${missingHeadings.join(", ")}`
|
|
4295
|
-
};
|
|
4296
|
-
}
|
|
4297
|
-
const markdownHeadings = getMarkdownHeadings(data.bodyMarkdown);
|
|
4298
|
-
const missingInMarkdown = TO_ISSUES_REQUIRED_HEADINGS.filter(
|
|
4299
|
-
(h) => !markdownHeadings.includes(h)
|
|
4300
|
-
);
|
|
4301
|
-
if (missingInMarkdown.length > 0) {
|
|
4302
|
-
return {
|
|
4303
|
-
ok: false,
|
|
4304
|
-
failureCode: "invalid_issue_artifact",
|
|
4305
|
-
offendingPath: path9,
|
|
4306
|
-
message: `bodyMarkdown missing headings: ${missingInMarkdown.join(", ")}`
|
|
4307
|
-
};
|
|
4308
|
-
}
|
|
4309
|
-
if (!data.receipts || typeof data.receipts !== "object") {
|
|
4310
|
-
return {
|
|
4311
|
-
ok: false,
|
|
4312
|
-
failureCode: "invalid_issue_artifact",
|
|
4313
|
-
offendingPath: path9,
|
|
4314
|
-
message: "receipts must be an object"
|
|
4315
|
-
};
|
|
4316
|
-
}
|
|
4317
|
-
const receipts = data.receipts;
|
|
4318
|
-
for (const field of ISSUE_RECEIPT_FIELDS) {
|
|
4319
|
-
if (!(field in receipts)) {
|
|
4320
|
-
return {
|
|
4321
|
-
ok: false,
|
|
4322
|
-
failureCode: "invalid_issue_artifact",
|
|
4323
|
-
offendingPath: path9,
|
|
4324
|
-
message: `receipts.${field} is missing`
|
|
4325
|
-
};
|
|
4326
|
-
}
|
|
4327
|
-
}
|
|
4328
|
-
if (!data.githubProjection || typeof data.githubProjection !== "object") {
|
|
4329
|
-
return {
|
|
4330
|
-
ok: false,
|
|
4331
|
-
failureCode: "invalid_issue_artifact",
|
|
4332
|
-
offendingPath: path9,
|
|
4333
|
-
message: "githubProjection must be an object"
|
|
4334
|
-
};
|
|
4335
|
-
}
|
|
4336
|
-
const githubProjection = data.githubProjection;
|
|
4337
|
-
for (const field of ISSUE_PROJECTION_FIELDS) {
|
|
4338
|
-
if (!(field in githubProjection)) {
|
|
4339
|
-
return {
|
|
4340
|
-
ok: false,
|
|
4341
|
-
failureCode: "invalid_issue_artifact",
|
|
4342
|
-
offendingPath: path9,
|
|
4343
|
-
message: `githubProjection.${field} is missing`
|
|
4344
|
-
};
|
|
4345
|
-
}
|
|
4346
|
-
}
|
|
4347
|
-
return { ok: true };
|
|
4348
|
-
}
|
|
4349
|
-
function validateLocalTriage(prdPath, issuePaths) {
|
|
4350
|
-
const prd = readLocalArtifact(
|
|
4351
|
-
prdPath,
|
|
4352
|
-
"triage_inconsistent"
|
|
4353
|
-
);
|
|
4354
|
-
if (!prd.ok || !prd.data) {
|
|
4355
|
-
return {
|
|
4356
|
-
ok: false,
|
|
4357
|
-
failureCode: "triage_inconsistent",
|
|
4358
|
-
offendingPath: prdPath,
|
|
4359
|
-
message: prd.message ?? "Invalid PRD artifact"
|
|
4360
|
-
};
|
|
4361
|
-
}
|
|
4362
|
-
if (issuePaths.length === 0) {
|
|
4363
|
-
return { ok: true };
|
|
4364
|
-
}
|
|
4365
|
-
const issues = [];
|
|
4366
|
-
for (const issuePath of issuePaths) {
|
|
4367
|
-
const issue = readLocalArtifact(
|
|
4368
|
-
issuePath,
|
|
4369
|
-
"triage_inconsistent"
|
|
4370
|
-
);
|
|
4371
|
-
if (!issue.ok || !issue.data) {
|
|
4372
|
-
return {
|
|
4373
|
-
ok: false,
|
|
4374
|
-
failureCode: "triage_inconsistent",
|
|
4375
|
-
offendingPath: issuePath,
|
|
4376
|
-
message: issue.message ?? "Invalid Issue artifact"
|
|
4377
|
-
};
|
|
4378
|
-
}
|
|
4379
|
-
issues.push({ path: issuePath, data: issue.data });
|
|
4380
|
-
}
|
|
4381
|
-
const issuesById = new Map(issues.map((i) => [i.data.id, i]));
|
|
4382
|
-
const needsTriage = issues.filter((i) => i.data.status === "needs-triage");
|
|
4383
|
-
if (needsTriage.length > 0) {
|
|
4384
|
-
return {
|
|
4385
|
-
ok: false,
|
|
4386
|
-
failureCode: "triage_inconsistent",
|
|
4387
|
-
message: `Parent PRD cannot prepare: child Issue(s) still in needs-triage: ${needsTriage.map((i) => i.data.id).join(", ")}`
|
|
4388
|
-
};
|
|
4389
|
-
}
|
|
4390
|
-
for (const issue of issues.filter((i) => i.data.status === "blocked")) {
|
|
4391
|
-
const deps = issue.data.dependencyIssueIds ?? [];
|
|
4392
|
-
const blockedReason = issue.data.blockedReason;
|
|
4393
|
-
const readyForHumanReason = issue.data.readyForHumanReason;
|
|
4394
|
-
if (deps.length === 0 && blockedReason == null && readyForHumanReason == null) {
|
|
4395
|
-
return {
|
|
4396
|
-
ok: false,
|
|
4397
|
-
failureCode: "triage_inconsistent",
|
|
4398
|
-
offendingPath: issue.path,
|
|
4399
|
-
message: `Blocked Issue ${issue.data.id} requires at least one blocker source: dependencyIssueIds, blockedReason, or readyForHumanReason`
|
|
4400
|
-
};
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4403
|
-
for (const issue of issues.filter(
|
|
4404
|
-
(i) => i.data.status === "ready-for-human"
|
|
4405
|
-
)) {
|
|
4406
|
-
if (issue.data.readyForHumanReason == null) {
|
|
4407
|
-
return {
|
|
4408
|
-
ok: false,
|
|
4409
|
-
failureCode: "triage_inconsistent",
|
|
4410
|
-
offendingPath: issue.path,
|
|
4411
|
-
message: `ready-for-human Issue ${issue.data.id} must have a readyForHumanReason`
|
|
4412
|
-
};
|
|
4413
|
-
}
|
|
4414
|
-
}
|
|
4415
|
-
for (const issue of issues.filter((i) => i.data.status === "wontfix")) {
|
|
4416
|
-
if (issue.data.blockedReason == null) {
|
|
4417
|
-
return {
|
|
4418
|
-
ok: false,
|
|
4419
|
-
failureCode: "triage_inconsistent",
|
|
4420
|
-
offendingPath: issue.path,
|
|
4421
|
-
message: `wontfix Issue ${issue.data.id} must have a blockedReason`
|
|
4422
|
-
};
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
const blockedCount = issues.filter((i) => i.data.status === "blocked").length;
|
|
4426
|
-
if (blockedCount > 0) {
|
|
4427
|
-
const readyForAgentCount = issues.filter(
|
|
4428
|
-
(i) => i.data.status === "ready-for-agent"
|
|
4429
|
-
).length;
|
|
4430
|
-
if (readyForAgentCount === 0) {
|
|
4431
|
-
return {
|
|
4432
|
-
ok: false,
|
|
4433
|
-
failureCode: "triage_inconsistent",
|
|
4434
|
-
message: "At least one child Issue must be ready-for-agent when blocked children exist"
|
|
4435
|
-
};
|
|
4436
|
-
}
|
|
4437
|
-
}
|
|
4438
|
-
for (const issue of issues) {
|
|
4439
|
-
const labels = issue.data.labels ?? [];
|
|
4440
|
-
const status = issue.data.status;
|
|
4441
|
-
const triageLabel = labels.find(
|
|
4442
|
-
(l) => VALID_ISSUE_TRIAGE_LABELS.includes(l)
|
|
4443
|
-
);
|
|
4444
|
-
if (triageLabel && status !== "running" && status !== "complete" && triageLabel !== status) {
|
|
4445
|
-
return {
|
|
4446
|
-
ok: false,
|
|
4447
|
-
failureCode: "triage_inconsistent",
|
|
4448
|
-
offendingPath: issue.path,
|
|
4449
|
-
message: `Issue ${issue.data.id} has status "${status}" but triage-state label "${triageLabel}"`
|
|
4450
|
-
};
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
for (const issue of issues) {
|
|
4454
|
-
const labels = issue.data.labels ?? [];
|
|
4455
|
-
const triageLabels = labels.filter(
|
|
4456
|
-
(l) => VALID_ISSUE_TRIAGE_LABELS.includes(l)
|
|
4457
|
-
);
|
|
4458
|
-
if (triageLabels.length !== 1) {
|
|
4459
|
-
return {
|
|
4460
|
-
ok: false,
|
|
4461
|
-
failureCode: "triage_inconsistent",
|
|
4462
|
-
offendingPath: issue.path,
|
|
4463
|
-
message: `Issue ${issue.data.id} must have exactly one triage-state label, got ${triageLabels.length}`
|
|
4464
|
-
};
|
|
4465
|
-
}
|
|
4466
|
-
}
|
|
4467
|
-
for (const issue of issues.filter(
|
|
4468
|
-
(i) => i.data.status === "ready-for-agent"
|
|
4469
|
-
)) {
|
|
4470
|
-
const deps = issue.data.dependencyIssueIds ?? [];
|
|
4471
|
-
for (const depId of deps) {
|
|
4472
|
-
const dep = issuesById.get(depId);
|
|
4473
|
-
if (dep && dep.data.status !== "complete") {
|
|
4474
|
-
return {
|
|
4475
|
-
ok: false,
|
|
4476
|
-
failureCode: "triage_inconsistent",
|
|
4477
|
-
offendingPath: issue.path,
|
|
4478
|
-
message: `ready-for-agent Issue ${issue.data.id} has unresolved dependency ${depId} (status: ${dep.data.status})`
|
|
4479
|
-
};
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
return { ok: true };
|
|
4484
|
-
}
|
|
4485
|
-
|
|
4486
|
-
// commands/issue-run.ts
|
|
4487
|
-
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
4488
|
-
import { isAbsolute as isAbsolute5, join as join16 } from "path";
|
|
4489
|
-
|
|
4490
|
-
// pr/templates.ts
|
|
4491
|
-
init_common();
|
|
4492
|
-
function renderBranchName(template, issue) {
|
|
4493
|
-
return renderTemplate(template, issue);
|
|
4494
|
-
}
|
|
4495
|
-
function renderTemplate(template, issue) {
|
|
4496
|
-
const issueSlug = slugify(issue.title);
|
|
4497
|
-
return template.replace(/\{\{issue\.number\}\}/g, String(issue.number)).replace(/\{\{issue\.title\}\}/g, issue.title).replace(/\{\{issue\.body\}\}/g, issue.body).replace(/\{\{issue\.slug\}\}/g, issueSlug).replace(/\{\{issue\.state\}\}/g, issue.state);
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
// commands/issue-run.ts
|
|
4501
|
-
init_common();
|
|
4502
|
-
|
|
4503
|
-
// commands/base-refresh.ts
|
|
4504
|
-
init_common();
|
|
4505
|
-
async function isIssueBranchStale(worktreePath, baseBranch, logger) {
|
|
4506
|
-
try {
|
|
4507
|
-
await execCapture(
|
|
4508
|
-
"git",
|
|
4509
|
-
["merge-base", "--is-ancestor", baseBranch, "HEAD"],
|
|
4510
|
-
{
|
|
4511
|
-
cwd: worktreePath,
|
|
4512
|
-
logger,
|
|
4513
|
-
label: "git merge-base --is-ancestor"
|
|
4514
|
-
}
|
|
4515
|
-
);
|
|
4516
|
-
return false;
|
|
4517
|
-
} catch {
|
|
4518
|
-
return true;
|
|
4519
|
-
}
|
|
4520
|
-
}
|
|
4521
|
-
async function refreshStaleIssueBranch(options) {
|
|
4522
|
-
const {
|
|
4523
|
-
worktreePath,
|
|
4524
|
-
baseBranch,
|
|
4525
|
-
localGitBaseRef,
|
|
4526
|
-
logger,
|
|
4527
|
-
prNumber,
|
|
4528
|
-
prState
|
|
4529
|
-
} = options;
|
|
4530
|
-
const stale = await isIssueBranchStale(worktreePath, localGitBaseRef, logger);
|
|
4531
|
-
if (!stale) {
|
|
4532
|
-
return { status: "skipped-current" };
|
|
4533
|
-
}
|
|
4534
|
-
if (prNumber !== void 0 && prState !== void 0) {
|
|
4535
|
-
return {
|
|
4536
|
-
status: "refused-published-history",
|
|
4537
|
-
prNumber,
|
|
4538
|
-
prState
|
|
4539
|
-
};
|
|
4540
|
-
}
|
|
4541
|
-
try {
|
|
4542
|
-
await execCapture("git", ["rebase", "--autostash", localGitBaseRef], {
|
|
4543
|
-
cwd: worktreePath,
|
|
4544
|
-
logger,
|
|
4545
|
-
label: "git rebase --autostash"
|
|
4546
|
-
});
|
|
4547
|
-
return { status: "refreshed" };
|
|
4548
|
-
} catch (error) {
|
|
4549
|
-
let conflictedPaths = [];
|
|
4550
|
-
try {
|
|
4551
|
-
const statusResult = await execCapture("git", ["status", "--porcelain"], {
|
|
4552
|
-
cwd: worktreePath,
|
|
4553
|
-
logger,
|
|
4554
|
-
label: "git status"
|
|
4555
|
-
});
|
|
4556
|
-
conflictedPaths = statusResult.stdout.split("\n").filter((line) => /^(AA|DD|UU|AU|UA|DU|UD)\s/.test(line)).map((line) => line.slice(3).trim()).filter(Boolean);
|
|
4557
|
-
} catch {
|
|
4558
|
-
}
|
|
4559
|
-
return {
|
|
4560
|
-
status: "conflicted",
|
|
4561
|
-
message: error instanceof Error ? error.message : String(error),
|
|
4562
|
-
conflictedPaths
|
|
4563
|
-
};
|
|
4564
|
-
}
|
|
4565
|
-
}
|
|
4566
|
-
function invalidateAfterBaseRefresh(state) {
|
|
4567
|
-
return {
|
|
4568
|
-
issueNumber: state.issueNumber,
|
|
4569
|
-
targetName: state.targetName,
|
|
4570
|
-
branchName: state.branchName,
|
|
4571
|
-
baseBranch: state.baseBranch,
|
|
4572
|
-
createdAt: state.createdAt,
|
|
4573
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4574
|
-
completedStages: {
|
|
4575
|
-
builder: state.completedStages.builder
|
|
4576
|
-
},
|
|
4577
|
-
review: {
|
|
4578
|
-
lifetimeIterations: 0
|
|
4579
|
-
}
|
|
4580
|
-
};
|
|
4581
|
-
}
|
|
4582
|
-
|
|
4583
|
-
// failure-resolution/effect-runtime.ts
|
|
4584
|
-
import { Effect as Effect4 } from "effect";
|
|
4585
|
-
|
|
4586
|
-
// shared/effect-runtime.ts
|
|
4587
|
-
import { Effect as Effect3, Exit } from "effect";
|
|
4588
|
-
import { UnknownException } from "effect/Cause";
|
|
4589
|
-
var initialized = false;
|
|
4590
|
-
function initializeEffectRuntime() {
|
|
4591
|
-
if (initialized) return;
|
|
4592
|
-
initialized = true;
|
|
4593
|
-
}
|
|
4594
|
-
function runEffect(program) {
|
|
4595
|
-
ensureEffectRuntime();
|
|
4596
|
-
return Effect3.runPromiseExit(program);
|
|
4597
|
-
}
|
|
4598
|
-
function ensureEffectRuntime() {
|
|
4599
|
-
if (!initialized) {
|
|
4600
|
-
initializeEffectRuntime();
|
|
4601
|
-
}
|
|
4602
|
-
}
|
|
4603
|
-
function unwrapError(error) {
|
|
4604
|
-
if (error instanceof UnknownException && error.error !== void 0) {
|
|
4605
|
-
return error.error;
|
|
3828
|
+
}
|
|
3829
|
+
function unwrapError(error) {
|
|
3830
|
+
if (error instanceof UnknownException && error.error !== void 0) {
|
|
3831
|
+
return error.error;
|
|
4606
3832
|
}
|
|
4607
3833
|
return error;
|
|
4608
3834
|
}
|
|
@@ -4621,15 +3847,15 @@ function runEffectAndMapExit(program) {
|
|
|
4621
3847
|
|
|
4622
3848
|
// shared/attempt-log.ts
|
|
4623
3849
|
import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "fs";
|
|
4624
|
-
import { dirname as dirname4, join as
|
|
3850
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
4625
3851
|
var ATTEMPT_LOG_PATH = ".pourkit/attempt-log.jsonl";
|
|
4626
3852
|
function writeAttemptLog(worktreePath, entry) {
|
|
4627
|
-
const logPath =
|
|
3853
|
+
const logPath = join7(worktreePath, ATTEMPT_LOG_PATH);
|
|
4628
3854
|
mkdirSync6(dirname4(logPath), { recursive: true });
|
|
4629
3855
|
appendFileSync(logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
4630
3856
|
}
|
|
4631
3857
|
function readAttemptLog(worktreePath) {
|
|
4632
|
-
const logPath =
|
|
3858
|
+
const logPath = join7(worktreePath, ATTEMPT_LOG_PATH);
|
|
4633
3859
|
if (!existsSync8(logPath)) {
|
|
4634
3860
|
return [];
|
|
4635
3861
|
}
|
|
@@ -4753,12 +3979,12 @@ async function runBaseRefreshAttempt(options) {
|
|
|
4753
3979
|
|
|
4754
3980
|
// commands/conflict-resolution.ts
|
|
4755
3981
|
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
4756
|
-
import { join as
|
|
3982
|
+
import { join as join8 } from "path";
|
|
4757
3983
|
init_common();
|
|
4758
3984
|
var CONFLICT_MARKER_PATTERN2 = /<<<<<<<|=======|>>>>>>>/m;
|
|
4759
3985
|
async function hasUnresolvedConflictMarkers(worktreePath, files) {
|
|
4760
3986
|
for (const file of files) {
|
|
4761
|
-
const filePath =
|
|
3987
|
+
const filePath = join8(worktreePath, file);
|
|
4762
3988
|
try {
|
|
4763
3989
|
const content = readFileSync9(filePath, "utf-8");
|
|
4764
3990
|
if (CONFLICT_MARKER_PATTERN2.test(content)) {
|
|
@@ -5092,7 +4318,7 @@ async function prepareSerenaForTarget(options) {
|
|
|
5092
4318
|
|
|
5093
4319
|
// failure-resolution/failure-resolution-agent.ts
|
|
5094
4320
|
import { readFileSync as readFileSync10 } from "fs";
|
|
5095
|
-
import { join as
|
|
4321
|
+
import { join as join9 } from "path";
|
|
5096
4322
|
|
|
5097
4323
|
// failure-resolution/recovery-policy.ts
|
|
5098
4324
|
function isSecuritySensitiveFailure(failure) {
|
|
@@ -5168,7 +4394,7 @@ async function runFailureResolutionAgent(options) {
|
|
|
5168
4394
|
} = options;
|
|
5169
4395
|
const frConfig = target.strategy.failureResolution;
|
|
5170
4396
|
const artifactPath = packet.artifactTarget;
|
|
5171
|
-
const fullArtifactPath =
|
|
4397
|
+
const fullArtifactPath = join9(worktreePath, artifactPath);
|
|
5172
4398
|
const fingerprint = computeFailureFingerprint(packet.stageName, failure._tag);
|
|
5173
4399
|
const prompt = [
|
|
5174
4400
|
`# Failure Resolution: ${packet.failureType}`,
|
|
@@ -5339,12 +4565,12 @@ async function writeRecoveryAttempt(worktreePath, outcome, fingerprint, summary,
|
|
|
5339
4565
|
}
|
|
5340
4566
|
|
|
5341
4567
|
// commands/pr-description-agent.ts
|
|
5342
|
-
import { join as
|
|
4568
|
+
import { join as join11 } from "path";
|
|
5343
4569
|
import { readFileSync as readFileSync11 } from "fs";
|
|
5344
4570
|
|
|
5345
4571
|
// pr/pr-description-context.ts
|
|
5346
4572
|
init_common();
|
|
5347
|
-
import { join as
|
|
4573
|
+
import { join as join10 } from "path";
|
|
5348
4574
|
import { readFile } from "fs/promises";
|
|
5349
4575
|
import { Effect as Effect5 } from "effect";
|
|
5350
4576
|
function collectFinalizerContextEffect(options) {
|
|
@@ -5409,7 +4635,7 @@ function remoteTargetBase(targetBase) {
|
|
|
5409
4635
|
return targetBase.includes("/") ? targetBase : `origin/${targetBase}`;
|
|
5410
4636
|
}
|
|
5411
4637
|
function buildFinalizerPrompt(context, promptTemplate) {
|
|
5412
|
-
const artifactPathInWorktree =
|
|
4638
|
+
const artifactPathInWorktree = join10(
|
|
5413
4639
|
".pourkit",
|
|
5414
4640
|
".tmp",
|
|
5415
4641
|
"finalizer",
|
|
@@ -5479,13 +4705,13 @@ function bridgeExecutionProvider2(ep) {
|
|
|
5479
4705
|
}
|
|
5480
4706
|
function runFinalizerAgent(options) {
|
|
5481
4707
|
const { executionProvider } = options;
|
|
5482
|
-
const artifactPathInWorktree =
|
|
4708
|
+
const artifactPathInWorktree = join11(
|
|
5483
4709
|
".pourkit",
|
|
5484
4710
|
".tmp",
|
|
5485
4711
|
"finalizer",
|
|
5486
4712
|
"agent-output.md"
|
|
5487
4713
|
);
|
|
5488
|
-
const artifactPath =
|
|
4714
|
+
const artifactPath = join11(options.worktreePath, artifactPathInWorktree);
|
|
5489
4715
|
const program = Effect6.gen(function* () {
|
|
5490
4716
|
const fs = yield* FileSystem;
|
|
5491
4717
|
const context = yield* collectFinalizerContextEffect({
|
|
@@ -5547,7 +4773,7 @@ function runFinalizerAgent(options) {
|
|
|
5547
4773
|
);
|
|
5548
4774
|
}
|
|
5549
4775
|
function runPrDescriptionFinalizerCore(options) {
|
|
5550
|
-
const artifactPath =
|
|
4776
|
+
const artifactPath = join11(
|
|
5551
4777
|
options.worktreePath,
|
|
5552
4778
|
options.artifactPathInWorktree
|
|
5553
4779
|
);
|
|
@@ -5656,298 +4882,10 @@ function loadFinalizerPromptEffect(repoRoot2, promptTemplate, fs) {
|
|
|
5656
4882
|
}
|
|
5657
4883
|
function persistGeneratedArtifactEffect(worktreePath, output, fs) {
|
|
5658
4884
|
return Effect6.gen(function* () {
|
|
5659
|
-
const dir =
|
|
4885
|
+
const dir = join11(worktreePath, ".pourkit", ".tmp", "finalizer");
|
|
5660
4886
|
yield* fs.mkdir(dir).pipe(Effect6.catchAll(() => Effect6.void));
|
|
5661
|
-
yield* fs.writeFile(
|
|
5662
|
-
});
|
|
5663
|
-
}
|
|
5664
|
-
|
|
5665
|
-
// prd-run/local-merge-coordinator.ts
|
|
5666
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
5667
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
5668
|
-
import { join as join13 } from "path";
|
|
5669
|
-
|
|
5670
|
-
// prd-run/local-branches.ts
|
|
5671
|
-
import { execFileSync, spawnSync as spawnSync2 } from "child_process";
|
|
5672
|
-
function getLocalPrdBranchName(prdId) {
|
|
5673
|
-
return `local/${prdId}`;
|
|
5674
|
-
}
|
|
5675
|
-
function materializeLocalPrdBranch(prdId, startBaseCommit, repoRoot2) {
|
|
5676
|
-
const branch = getLocalPrdBranchName(prdId);
|
|
5677
|
-
const root = repoRoot2 ?? process.cwd();
|
|
5678
|
-
const branchExists = (() => {
|
|
5679
|
-
try {
|
|
5680
|
-
return spawnSync2("git", ["rev-parse", "--verify", "--quiet", branch], {
|
|
5681
|
-
cwd: root,
|
|
5682
|
-
encoding: "utf8",
|
|
5683
|
-
stdio: "pipe"
|
|
5684
|
-
}).status === 0;
|
|
5685
|
-
} catch {
|
|
5686
|
-
return false;
|
|
5687
|
-
}
|
|
5688
|
-
})();
|
|
5689
|
-
if (branchExists) {
|
|
5690
|
-
const currentCommit = spawnSync2("git", ["rev-parse", branch], {
|
|
5691
|
-
cwd: root,
|
|
5692
|
-
encoding: "utf8",
|
|
5693
|
-
stdio: "pipe"
|
|
5694
|
-
}).stdout?.toString?.().trim() ?? "";
|
|
5695
|
-
if (currentCommit !== startBaseCommit) {
|
|
5696
|
-
return {
|
|
5697
|
-
ok: false,
|
|
5698
|
-
failureCode: "branch_commit_mismatch",
|
|
5699
|
-
message: `Local PRD branch ${branch} exists at commit ${currentCommit} but expected commit ${startBaseCommit}. The branch has diverged from the start base. To resolve, delete the local branch and rerun start, or manually reset it to the expected commit.`
|
|
5700
|
-
};
|
|
5701
|
-
}
|
|
5702
|
-
return {
|
|
5703
|
-
ok: true,
|
|
5704
|
-
created: false
|
|
5705
|
-
};
|
|
5706
|
-
}
|
|
5707
|
-
const branchResult = spawnSync2("git", ["branch", branch, startBaseCommit], {
|
|
5708
|
-
cwd: root,
|
|
5709
|
-
encoding: "utf8",
|
|
5710
|
-
stdio: "pipe"
|
|
4887
|
+
yield* fs.writeFile(join11(dir, "generated.md"), output).pipe(Effect6.catchAll(() => Effect6.void));
|
|
5711
4888
|
});
|
|
5712
|
-
if (branchResult.status !== 0) {
|
|
5713
|
-
const stderr = branchResult.stderr?.toString?.() ?? "unknown error";
|
|
5714
|
-
return {
|
|
5715
|
-
ok: false,
|
|
5716
|
-
failureCode: "branch_creation_failed",
|
|
5717
|
-
message: `Failed to create local PRD branch ${branch} at ${startBaseCommit}: ${stderr}`
|
|
5718
|
-
};
|
|
5719
|
-
}
|
|
5720
|
-
return { ok: true, created: true };
|
|
5721
|
-
}
|
|
5722
|
-
var PROTECTED_BRANCHES = /* @__PURE__ */ new Set(["dev", "next", "main"]);
|
|
5723
|
-
var LOCAL_BRANCH_PATTERN = /^local\/PRD-\d{4}(\/(I-\d{2}(-[a-z0-9-]+)?)?)?$/;
|
|
5724
|
-
function validateLocalBranchName(name) {
|
|
5725
|
-
if (!name || name.length === 0) {
|
|
5726
|
-
return {
|
|
5727
|
-
ok: false,
|
|
5728
|
-
failureCode: "invalid_format",
|
|
5729
|
-
message: "Branch name is empty."
|
|
5730
|
-
};
|
|
5731
|
-
}
|
|
5732
|
-
if (isProtectedBranch(name)) {
|
|
5733
|
-
return {
|
|
5734
|
-
ok: false,
|
|
5735
|
-
failureCode: "protected_branch",
|
|
5736
|
-
message: `Branch "${name}" is protected.`
|
|
5737
|
-
};
|
|
5738
|
-
}
|
|
5739
|
-
if (!LOCAL_BRANCH_PATTERN.test(name)) {
|
|
5740
|
-
return {
|
|
5741
|
-
ok: false,
|
|
5742
|
-
failureCode: "invalid_format",
|
|
5743
|
-
message: `Branch "${name}" does not match the local branch pattern.`
|
|
5744
|
-
};
|
|
5745
|
-
}
|
|
5746
|
-
return { ok: true };
|
|
5747
|
-
}
|
|
5748
|
-
function isProtectedBranch(name) {
|
|
5749
|
-
return PROTECTED_BRANCHES.has(name);
|
|
5750
|
-
}
|
|
5751
|
-
|
|
5752
|
-
// prd-run/local-merge-coordinator.ts
|
|
5753
|
-
function getLocalStorePath(repoRoot2, prdId) {
|
|
5754
|
-
return join13(repoRoot2, ".pourkit", "local-prd-runs", prdId);
|
|
5755
|
-
}
|
|
5756
|
-
function getMergeReceiptPath(repoRoot2, prdId, issueId) {
|
|
5757
|
-
return join13(
|
|
5758
|
-
getLocalStorePath(repoRoot2, prdId),
|
|
5759
|
-
"merge-receipts",
|
|
5760
|
-
`${issueId}.json`
|
|
5761
|
-
);
|
|
5762
|
-
}
|
|
5763
|
-
function getIssueArtifactPath(repoRoot2, prdId, issueId) {
|
|
5764
|
-
return join13(getLocalStorePath(repoRoot2, prdId), "issues", `${issueId}.json`);
|
|
5765
|
-
}
|
|
5766
|
-
function readIssueBranchName(repoRoot2, prdId, issueId) {
|
|
5767
|
-
const issuePath = getIssueArtifactPath(repoRoot2, prdId, issueId);
|
|
5768
|
-
if (!existsSync10(issuePath)) return null;
|
|
5769
|
-
try {
|
|
5770
|
-
const content = readFileSync12(issuePath, "utf-8");
|
|
5771
|
-
const parsed = JSON.parse(content);
|
|
5772
|
-
return typeof parsed.branchName === "string" && parsed.branchName ? parsed.branchName : null;
|
|
5773
|
-
} catch {
|
|
5774
|
-
return null;
|
|
5775
|
-
}
|
|
5776
|
-
}
|
|
5777
|
-
async function hasLocalIssueMergeReceipt(prdId, issueId, repoRoot2) {
|
|
5778
|
-
const root = repoRoot2 ?? process.cwd();
|
|
5779
|
-
const receiptPath = getMergeReceiptPath(root, prdId, issueId);
|
|
5780
|
-
if (!existsSync10(receiptPath)) return null;
|
|
5781
|
-
try {
|
|
5782
|
-
const content = readFileSync12(receiptPath, "utf-8");
|
|
5783
|
-
const parsed = JSON.parse(content);
|
|
5784
|
-
if (typeof parsed.prdId === "string" && typeof parsed.issueId === "string" && typeof parsed.stage === "string" && typeof parsed.sourceBranch === "string" && typeof parsed.localPrdBranch === "string" && typeof parsed.mergeCommit === "string" && typeof parsed.completedAt === "string") {
|
|
5785
|
-
return parsed;
|
|
5786
|
-
}
|
|
5787
|
-
return null;
|
|
5788
|
-
} catch {
|
|
5789
|
-
return null;
|
|
5790
|
-
}
|
|
5791
|
-
}
|
|
5792
|
-
async function squashMergeLocalIssue(prdId, issueId, input, repoRoot2) {
|
|
5793
|
-
const root = repoRoot2 ?? process.cwd();
|
|
5794
|
-
const receiptPath = getMergeReceiptPath(root, prdId, issueId);
|
|
5795
|
-
if (existsSync10(receiptPath)) {
|
|
5796
|
-
try {
|
|
5797
|
-
const existing = JSON.parse(
|
|
5798
|
-
readFileSync12(receiptPath, "utf-8")
|
|
5799
|
-
);
|
|
5800
|
-
if (existing.prdId === prdId && existing.issueId === issueId && existing.mergeCommit) {
|
|
5801
|
-
return {
|
|
5802
|
-
ok: false,
|
|
5803
|
-
failureCode: "already_merged",
|
|
5804
|
-
repairGuidance: `Issue ${issueId} was already merged into ${existing.localPrdBranch}. Merge commit: ${existing.mergeCommit}`
|
|
5805
|
-
};
|
|
5806
|
-
}
|
|
5807
|
-
return {
|
|
5808
|
-
ok: false,
|
|
5809
|
-
failureCode: "invalid_receipt",
|
|
5810
|
-
repairGuidance: `Merge receipt for ${issueId} belongs to different prd/issue. Remove it manually: ${receiptPath}`
|
|
5811
|
-
};
|
|
5812
|
-
} catch {
|
|
5813
|
-
return {
|
|
5814
|
-
ok: false,
|
|
5815
|
-
failureCode: "invalid_receipt",
|
|
5816
|
-
repairGuidance: `Merge receipt for ${issueId} is corrupted. Remove it manually: ${receiptPath}`
|
|
5817
|
-
};
|
|
5818
|
-
}
|
|
5819
|
-
}
|
|
5820
|
-
const sourceBranch = input?.sourceBranch ?? readIssueBranchName(root, prdId, issueId);
|
|
5821
|
-
if (!sourceBranch) {
|
|
5822
|
-
return {
|
|
5823
|
-
ok: false,
|
|
5824
|
-
failureCode: "not_found",
|
|
5825
|
-
repairGuidance: `No source branch found for issue ${issueId} under PRD ${prdId}. Ensure the issue artifact exists with a valid branchName field.`
|
|
5826
|
-
};
|
|
5827
|
-
}
|
|
5828
|
-
try {
|
|
5829
|
-
execFileSync2(
|
|
5830
|
-
"git",
|
|
5831
|
-
["show-ref", "--verify", "--quiet", `refs/heads/${sourceBranch}`],
|
|
5832
|
-
{ cwd: root, encoding: "utf8", stdio: "pipe" }
|
|
5833
|
-
);
|
|
5834
|
-
} catch {
|
|
5835
|
-
return {
|
|
5836
|
-
ok: false,
|
|
5837
|
-
failureCode: "not_found",
|
|
5838
|
-
repairGuidance: `Source branch "${sourceBranch}" does not exist locally. Check that the branch was created and not deleted.`
|
|
5839
|
-
};
|
|
5840
|
-
}
|
|
5841
|
-
const targetBranch = getLocalPrdBranchName(prdId);
|
|
5842
|
-
try {
|
|
5843
|
-
execFileSync2(
|
|
5844
|
-
"git",
|
|
5845
|
-
["show-ref", "--verify", "--quiet", `refs/heads/${targetBranch}`],
|
|
5846
|
-
{ cwd: root, encoding: "utf8", stdio: "pipe" }
|
|
5847
|
-
);
|
|
5848
|
-
} catch {
|
|
5849
|
-
return {
|
|
5850
|
-
ok: false,
|
|
5851
|
-
failureCode: "not_found",
|
|
5852
|
-
repairGuidance: `Local PRD branch "${targetBranch}" does not exist. Run \`prd-run start\` first to create it.`
|
|
5853
|
-
};
|
|
5854
|
-
}
|
|
5855
|
-
try {
|
|
5856
|
-
execFileSync2("git", ["checkout", targetBranch], {
|
|
5857
|
-
cwd: root,
|
|
5858
|
-
encoding: "utf8",
|
|
5859
|
-
stdio: "pipe"
|
|
5860
|
-
});
|
|
5861
|
-
let preMergeHead;
|
|
5862
|
-
try {
|
|
5863
|
-
preMergeHead = execFileSync2("git", ["rev-parse", "HEAD"], {
|
|
5864
|
-
cwd: root,
|
|
5865
|
-
encoding: "utf8",
|
|
5866
|
-
stdio: "pipe"
|
|
5867
|
-
}).trim();
|
|
5868
|
-
} catch {
|
|
5869
|
-
return {
|
|
5870
|
-
ok: false,
|
|
5871
|
-
failureCode: "merge_error",
|
|
5872
|
-
repairGuidance: "Failed to read current HEAD before merge."
|
|
5873
|
-
};
|
|
5874
|
-
}
|
|
5875
|
-
execFileSync2("git", ["merge", "--squash", sourceBranch], {
|
|
5876
|
-
cwd: root,
|
|
5877
|
-
encoding: "utf8",
|
|
5878
|
-
stdio: "pipe"
|
|
5879
|
-
});
|
|
5880
|
-
if (input) {
|
|
5881
|
-
execFileSync2(
|
|
5882
|
-
"git",
|
|
5883
|
-
["commit", "-m", input.finalizerTitle, "-m", input.finalizerBody],
|
|
5884
|
-
{ cwd: root, encoding: "utf8", stdio: "pipe" }
|
|
5885
|
-
);
|
|
5886
|
-
} else {
|
|
5887
|
-
execFileSync2(
|
|
5888
|
-
"git",
|
|
5889
|
-
["commit", "-m", `Squash merge ${sourceBranch} into ${targetBranch}`],
|
|
5890
|
-
{ cwd: root, encoding: "utf8", stdio: "pipe" }
|
|
5891
|
-
);
|
|
5892
|
-
}
|
|
5893
|
-
const revParseResult = execFileSync2("git", ["rev-parse", "HEAD"], {
|
|
5894
|
-
cwd: root,
|
|
5895
|
-
encoding: "utf8",
|
|
5896
|
-
stdio: "pipe"
|
|
5897
|
-
});
|
|
5898
|
-
const mergeCommit = revParseResult.trim();
|
|
5899
|
-
const receipt = {
|
|
5900
|
-
prdId,
|
|
5901
|
-
issueId,
|
|
5902
|
-
stage: "child-issue",
|
|
5903
|
-
sourceBranch,
|
|
5904
|
-
localPrdBranch: targetBranch,
|
|
5905
|
-
mergeCommit,
|
|
5906
|
-
finalizerArtifactPath: input?.finalizerArtifactPath ?? "",
|
|
5907
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5908
|
-
};
|
|
5909
|
-
try {
|
|
5910
|
-
const receiptsDir = join13(
|
|
5911
|
-
root,
|
|
5912
|
-
".pourkit",
|
|
5913
|
-
"local-prd-runs",
|
|
5914
|
-
prdId,
|
|
5915
|
-
"merge-receipts"
|
|
5916
|
-
);
|
|
5917
|
-
mkdirSync7(receiptsDir, { recursive: true });
|
|
5918
|
-
writeFileSync4(receiptPath, JSON.stringify(receipt, null, 2), "utf-8");
|
|
5919
|
-
} catch {
|
|
5920
|
-
try {
|
|
5921
|
-
execFileSync2("git", ["reset", "--hard", preMergeHead], {
|
|
5922
|
-
cwd: root,
|
|
5923
|
-
encoding: "utf8",
|
|
5924
|
-
stdio: "pipe"
|
|
5925
|
-
});
|
|
5926
|
-
} catch {
|
|
5927
|
-
}
|
|
5928
|
-
return {
|
|
5929
|
-
ok: false,
|
|
5930
|
-
failureCode: "receipt_error",
|
|
5931
|
-
repairGuidance: "Failed to write merge receipt. Check disk space and permissions on .pourkit/local-prd-runs/. The merge commit has been rolled back."
|
|
5932
|
-
};
|
|
5933
|
-
}
|
|
5934
|
-
return { ok: true, receipt };
|
|
5935
|
-
} catch (error) {
|
|
5936
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5937
|
-
if (message.toLowerCase().includes("conflict")) {
|
|
5938
|
-
return {
|
|
5939
|
-
ok: false,
|
|
5940
|
-
failureCode: "conflict",
|
|
5941
|
-
repairGuidance: "Merge conflict during squash. Resolve conflicts in the working tree, then retry."
|
|
5942
|
-
};
|
|
5943
|
-
}
|
|
5944
|
-
const stderr = error instanceof Error && "stderr" in error ? error.stderr : void 0;
|
|
5945
|
-
return {
|
|
5946
|
-
ok: false,
|
|
5947
|
-
failureCode: "merge_error",
|
|
5948
|
-
repairGuidance: stderr ? `Merge failed: ${stderr}` : `Merge failed: ${message}`
|
|
5949
|
-
};
|
|
5950
|
-
}
|
|
5951
4889
|
}
|
|
5952
4890
|
|
|
5953
4891
|
// pr/pr-body.ts
|
|
@@ -6020,8 +4958,8 @@ function extractClosingRefs(body) {
|
|
|
6020
4958
|
|
|
6021
4959
|
// issues/github-issue-publication.ts
|
|
6022
4960
|
init_common();
|
|
6023
|
-
import { existsSync as
|
|
6024
|
-
import { isAbsolute as isAbsolute4, join as
|
|
4961
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
|
|
4962
|
+
import { isAbsolute as isAbsolute4, join as join12 } from "path";
|
|
6025
4963
|
|
|
6026
4964
|
// issues/issue-transitions.ts
|
|
6027
4965
|
function createIssueTransitions(deps, labels) {
|
|
@@ -6496,11 +5434,11 @@ function readIssueFinalReviewArtifact(worktreeState, worktreePath) {
|
|
|
6496
5434
|
if (!configuredPath) {
|
|
6497
5435
|
return null;
|
|
6498
5436
|
}
|
|
6499
|
-
const artifactPath = isAbsolute4(configuredPath) ? configuredPath :
|
|
6500
|
-
if (!
|
|
5437
|
+
const artifactPath = isAbsolute4(configuredPath) ? configuredPath : join12(worktreePath, configuredPath);
|
|
5438
|
+
if (!existsSync10(artifactPath)) return null;
|
|
6501
5439
|
let parsed;
|
|
6502
5440
|
try {
|
|
6503
|
-
parsed = JSON.parse(
|
|
5441
|
+
parsed = JSON.parse(readFileSync12(artifactPath, "utf-8"));
|
|
6504
5442
|
} catch {
|
|
6505
5443
|
return null;
|
|
6506
5444
|
}
|
|
@@ -6755,9 +5693,9 @@ function isAllowedSecretPlaceholder(value) {
|
|
|
6755
5693
|
}
|
|
6756
5694
|
|
|
6757
5695
|
// commands/issue-final-review-agent.ts
|
|
6758
|
-
import { existsSync as
|
|
6759
|
-
import { join as
|
|
6760
|
-
var ISSUE_FINAL_REVIEW_ARTIFACT_PATH =
|
|
5696
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
|
|
5697
|
+
import { join as join13 } from "path";
|
|
5698
|
+
var ISSUE_FINAL_REVIEW_ARTIFACT_PATH = join13(
|
|
6761
5699
|
".pourkit",
|
|
6762
5700
|
".tmp",
|
|
6763
5701
|
"issue-final-review",
|
|
@@ -6775,7 +5713,7 @@ async function runIssueFinalReviewAgent(options) {
|
|
|
6775
5713
|
logger
|
|
6776
5714
|
} = options;
|
|
6777
5715
|
const artifactPathInWorktree = ISSUE_FINAL_REVIEW_ARTIFACT_PATH;
|
|
6778
|
-
const artifactPath =
|
|
5716
|
+
const artifactPath = join13(worktreePath, artifactPathInWorktree);
|
|
6779
5717
|
const strategy = target.strategy;
|
|
6780
5718
|
const issueFinalReview = strategy.issueFinalReview;
|
|
6781
5719
|
const agent = issueFinalReview;
|
|
@@ -6880,7 +5818,7 @@ function extractChangedPaths(parsed) {
|
|
|
6880
5818
|
function loadIssueFinalReviewPrompt(options) {
|
|
6881
5819
|
const { repoRoot: repoRoot2, promptTemplate, validationCommand } = options;
|
|
6882
5820
|
const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
|
|
6883
|
-
const promptBody =
|
|
5821
|
+
const promptBody = existsSync11(promptPath) ? readFileSync13(promptPath, "utf-8") : promptTemplate;
|
|
6884
5822
|
return appendProtectedWorkGuidance(`${promptBody}
|
|
6885
5823
|
|
|
6886
5824
|
## Shared Run Context
|
|
@@ -6991,7 +5929,7 @@ async function advanceIssueFinalReview(options) {
|
|
|
6991
5929
|
"Issue Final Review state is incomplete: missing artifactPath"
|
|
6992
5930
|
);
|
|
6993
5931
|
}
|
|
6994
|
-
const artifactPath = isAbsolute5(ifrFromState.artifactPath) ? ifrFromState.artifactPath :
|
|
5932
|
+
const artifactPath = isAbsolute5(ifrFromState.artifactPath) ? ifrFromState.artifactPath : join14(worktreePath, ifrFromState.artifactPath);
|
|
6995
5933
|
const validation = validateAgentArtifact({
|
|
6996
5934
|
kind: "issue-final-review",
|
|
6997
5935
|
artifactPath,
|
|
@@ -7396,7 +6334,6 @@ async function completeIssueRun(options) {
|
|
|
7396
6334
|
let prTitle = issue.title;
|
|
7397
6335
|
let prBody;
|
|
7398
6336
|
let finalizerResult;
|
|
7399
|
-
const isLocalModeForFinalizer = options.prdRunMode?.mode === "local";
|
|
7400
6337
|
const finalizerFromState = worktreeState?.finalizer?.completed ? worktreeState.finalizer : null;
|
|
7401
6338
|
if (finalizerFromState) {
|
|
7402
6339
|
try {
|
|
@@ -7404,12 +6341,12 @@ async function completeIssueRun(options) {
|
|
|
7404
6341
|
prTitle = finalizerFromState.title;
|
|
7405
6342
|
prBody = finalizerFromState.body;
|
|
7406
6343
|
} else if (finalizerFromState.artifactPath) {
|
|
7407
|
-
if (!
|
|
6344
|
+
if (!existsSync12(finalizerFromState.artifactPath)) {
|
|
7408
6345
|
throw new FinalizerFailure({
|
|
7409
6346
|
message: `Finalizer artifact missing at ${finalizerFromState.artifactPath}`
|
|
7410
6347
|
});
|
|
7411
6348
|
}
|
|
7412
|
-
const artifactContent =
|
|
6349
|
+
const artifactContent = readFileSync14(
|
|
7413
6350
|
finalizerFromState.artifactPath,
|
|
7414
6351
|
"utf-8"
|
|
7415
6352
|
);
|
|
@@ -7422,17 +6359,6 @@ async function completeIssueRun(options) {
|
|
|
7422
6359
|
});
|
|
7423
6360
|
}
|
|
7424
6361
|
} catch (error) {
|
|
7425
|
-
if (isLocalModeForFinalizer) {
|
|
7426
|
-
return {
|
|
7427
|
-
branchName,
|
|
7428
|
-
target,
|
|
7429
|
-
issue,
|
|
7430
|
-
noOp: false,
|
|
7431
|
-
mode: "local",
|
|
7432
|
-
failureCode: "finalizer_failed",
|
|
7433
|
-
repairGuidance: error instanceof Error ? error.message : "Finalizer state is invalid"
|
|
7434
|
-
};
|
|
7435
|
-
}
|
|
7436
6362
|
throw error;
|
|
7437
6363
|
}
|
|
7438
6364
|
} else {
|
|
@@ -7453,17 +6379,6 @@ async function completeIssueRun(options) {
|
|
|
7453
6379
|
})
|
|
7454
6380
|
);
|
|
7455
6381
|
} catch (error) {
|
|
7456
|
-
if (isLocalModeForFinalizer) {
|
|
7457
|
-
return {
|
|
7458
|
-
branchName,
|
|
7459
|
-
target,
|
|
7460
|
-
issue,
|
|
7461
|
-
noOp: false,
|
|
7462
|
-
mode: "local",
|
|
7463
|
-
failureCode: "finalizer_failed",
|
|
7464
|
-
repairGuidance: error instanceof Error ? error.message : "Finalizer execution failed"
|
|
7465
|
-
};
|
|
7466
|
-
}
|
|
7467
6382
|
throw error;
|
|
7468
6383
|
}
|
|
7469
6384
|
prTitle = finalizerResult.title;
|
|
@@ -7495,153 +6410,28 @@ async function completeIssueRun(options) {
|
|
|
7495
6410
|
}
|
|
7496
6411
|
});
|
|
7497
6412
|
}
|
|
7498
|
-
const finalCommitFromState = worktreeState?.finalCommit?.completed;
|
|
7499
|
-
if (!finalCommitFromState) {
|
|
7500
|
-
await finalizeWorktreeCommit({
|
|
7501
|
-
worktreePath: executionResult.worktreePath,
|
|
7502
|
-
baseRef: `origin/${effectiveBaseBranch}`,
|
|
7503
|
-
title: prTitle,
|
|
7504
|
-
body: finalBody,
|
|
7505
|
-
logger
|
|
7506
|
-
});
|
|
7507
|
-
if (executionResult.worktreePath) {
|
|
7508
|
-
const revParse = await execCapture("git", ["rev-parse", "HEAD"], {
|
|
7509
|
-
cwd: executionResult.worktreePath,
|
|
7510
|
-
logger,
|
|
7511
|
-
label: "git rev-parse HEAD"
|
|
7512
|
-
});
|
|
7513
|
-
updateWorktreeRunState(executionResult.worktreePath, {
|
|
7514
|
-
finalCommit: {
|
|
7515
|
-
completed: true,
|
|
7516
|
-
sha: revParse.stdout.trim()
|
|
7517
|
-
}
|
|
7518
|
-
});
|
|
7519
|
-
}
|
|
7520
|
-
}
|
|
7521
|
-
const isLocalMode = options.prdRunMode?.mode === "local";
|
|
7522
|
-
if (isLocalMode) {
|
|
7523
|
-
const parsed = parseStackedIssue(issue.title, issue.body);
|
|
7524
|
-
const prdId = parsed.parentRef;
|
|
7525
|
-
if (!prdId) {
|
|
7526
|
-
return {
|
|
7527
|
-
branchName,
|
|
7528
|
-
target,
|
|
7529
|
-
issue,
|
|
7530
|
-
noOp: false,
|
|
7531
|
-
mode: "local",
|
|
7532
|
-
failureCode: "finalizer_failed",
|
|
7533
|
-
repairGuidance: "Local mode requires a PRD reference (## Parent) in the issue body or title. Add the parent PRD reference and retry."
|
|
7534
|
-
};
|
|
7535
|
-
}
|
|
7536
|
-
const finalizerFromWorktreeState = worktreeState?.finalizer;
|
|
7537
|
-
if (finalizerFromWorktreeState && !finalizerFromWorktreeState.completed) {
|
|
7538
|
-
return {
|
|
7539
|
-
branchName,
|
|
7540
|
-
target,
|
|
7541
|
-
issue,
|
|
7542
|
-
noOp: false,
|
|
7543
|
-
mode: "local",
|
|
7544
|
-
failureCode: "finalizer_failed",
|
|
7545
|
-
repairGuidance: "Finalizer did not complete on previous run. Re-run the issue finalizer and try again."
|
|
7546
|
-
};
|
|
7547
|
-
}
|
|
7548
|
-
if (!prTitle || !finalBody) {
|
|
7549
|
-
return {
|
|
7550
|
-
branchName,
|
|
7551
|
-
target,
|
|
7552
|
-
issue,
|
|
7553
|
-
noOp: false,
|
|
7554
|
-
mode: "local",
|
|
7555
|
-
failureCode: "finalizer_failed",
|
|
7556
|
-
repairGuidance: "Finalizer produced empty title or body. Re-run the issue finalizer and try again."
|
|
7557
|
-
};
|
|
7558
|
-
}
|
|
7559
|
-
const existingReceipt = await hasLocalIssueMergeReceipt(
|
|
7560
|
-
prdId,
|
|
7561
|
-
`issue-${issueNumber}`,
|
|
7562
|
-
ROOT
|
|
7563
|
-
);
|
|
7564
|
-
if (existingReceipt) {
|
|
7565
|
-
return {
|
|
7566
|
-
branchName,
|
|
7567
|
-
target,
|
|
7568
|
-
issue,
|
|
7569
|
-
noOp: false,
|
|
7570
|
-
mode: "local",
|
|
7571
|
-
failureCode: "already_merged",
|
|
7572
|
-
localPrdBranch: existingReceipt.localPrdBranch,
|
|
7573
|
-
mergeCommit: existingReceipt.mergeCommit,
|
|
7574
|
-
receiptPath: join16(
|
|
7575
|
-
ROOT,
|
|
7576
|
-
".pourkit",
|
|
7577
|
-
"local-prd-runs",
|
|
7578
|
-
prdId,
|
|
7579
|
-
"merge-receipts",
|
|
7580
|
-
`issue-${issueNumber}.json`
|
|
7581
|
-
)
|
|
7582
|
-
};
|
|
7583
|
-
}
|
|
7584
|
-
const mergeResult = await squashMergeLocalIssue(
|
|
7585
|
-
prdId,
|
|
7586
|
-
`issue-${issueNumber}`,
|
|
7587
|
-
{
|
|
7588
|
-
finalizerTitle: prTitle,
|
|
7589
|
-
finalizerBody: finalBody,
|
|
7590
|
-
finalizerArtifactPath: worktreeState?.finalizer?.artifactPath ?? "",
|
|
7591
|
-
sourceBranch: branchName
|
|
7592
|
-
},
|
|
7593
|
-
ROOT
|
|
7594
|
-
);
|
|
7595
|
-
if (!mergeResult.ok) {
|
|
7596
|
-
return {
|
|
7597
|
-
branchName,
|
|
7598
|
-
target,
|
|
7599
|
-
issue,
|
|
7600
|
-
noOp: false,
|
|
7601
|
-
mode: "local",
|
|
7602
|
-
failureCode: mergeResult.failureCode,
|
|
7603
|
-
repairGuidance: mergeResult.repairGuidance ?? `Local squash-merge failed: ${mergeResult.failureCode}`
|
|
7604
|
-
};
|
|
7605
|
-
}
|
|
7606
|
-
await issueProvider.removeLabel(issueNumber, config.labels.agentInProgress);
|
|
7607
|
-
await issueProvider.removeLabel(
|
|
7608
|
-
issueNumber,
|
|
7609
|
-
config.labels.prOpenAwaitingMerge
|
|
7610
|
-
);
|
|
7611
|
-
let childCloseSucceeded = false;
|
|
7612
|
-
try {
|
|
7613
|
-
await issueProvider.closeIssue(issueNumber);
|
|
7614
|
-
childCloseSucceeded = true;
|
|
7615
|
-
} catch (error) {
|
|
7616
|
-
logger.step(
|
|
7617
|
-
"warn",
|
|
7618
|
-
`Issue #${issueNumber} could not be closed: ${error instanceof Error ? error.message : String(error)}`
|
|
7619
|
-
);
|
|
7620
|
-
}
|
|
7621
|
-
if (childCloseSucceeded) {
|
|
7622
|
-
await maybeCloseParentAfterChildCompletion(
|
|
7623
|
-
issueProvider,
|
|
7624
|
-
issueNumber,
|
|
7625
|
-
logger
|
|
7626
|
-
);
|
|
6413
|
+
const finalCommitFromState = worktreeState?.finalCommit?.completed;
|
|
6414
|
+
if (!finalCommitFromState) {
|
|
6415
|
+
await finalizeWorktreeCommit({
|
|
6416
|
+
worktreePath: executionResult.worktreePath,
|
|
6417
|
+
baseRef: `origin/${effectiveBaseBranch}`,
|
|
6418
|
+
title: prTitle,
|
|
6419
|
+
body: finalBody,
|
|
6420
|
+
logger
|
|
6421
|
+
});
|
|
6422
|
+
if (executionResult.worktreePath) {
|
|
6423
|
+
const revParse = await execCapture("git", ["rev-parse", "HEAD"], {
|
|
6424
|
+
cwd: executionResult.worktreePath,
|
|
6425
|
+
logger,
|
|
6426
|
+
label: "git rev-parse HEAD"
|
|
6427
|
+
});
|
|
6428
|
+
updateWorktreeRunState(executionResult.worktreePath, {
|
|
6429
|
+
finalCommit: {
|
|
6430
|
+
completed: true,
|
|
6431
|
+
sha: revParse.stdout.trim()
|
|
6432
|
+
}
|
|
6433
|
+
});
|
|
7627
6434
|
}
|
|
7628
|
-
return {
|
|
7629
|
-
branchName,
|
|
7630
|
-
target,
|
|
7631
|
-
issue,
|
|
7632
|
-
noOp: false,
|
|
7633
|
-
mode: "local",
|
|
7634
|
-
localPrdBranch: getLocalPrdBranchName(prdId),
|
|
7635
|
-
mergeCommit: mergeResult.receipt.mergeCommit,
|
|
7636
|
-
receiptPath: join16(
|
|
7637
|
-
ROOT,
|
|
7638
|
-
".pourkit",
|
|
7639
|
-
"local-prd-runs",
|
|
7640
|
-
prdId,
|
|
7641
|
-
"merge-receipts",
|
|
7642
|
-
`issue-${issueNumber}.json`
|
|
7643
|
-
)
|
|
7644
|
-
};
|
|
7645
6435
|
}
|
|
7646
6436
|
if (!executionResult.worktreePath) {
|
|
7647
6437
|
throw new Error(
|
|
@@ -8008,7 +6798,7 @@ async function resolveIssueWorktree(root, branchName, baseBranch, logger) {
|
|
|
8008
6798
|
return { mode: "new", branchName, baseRef };
|
|
8009
6799
|
}
|
|
8010
6800
|
function issueWorktreePath(root, branchName) {
|
|
8011
|
-
return
|
|
6801
|
+
return join14(root, ".sandcastle", "worktrees", branchName.replace(/\//g, "-"));
|
|
8012
6802
|
}
|
|
8013
6803
|
function resolveRegisteredIssueWorktreePath(worktreeListPorcelain, root, branchName) {
|
|
8014
6804
|
const branchWorktreePath = parseWorktreeListPorcelain(
|
|
@@ -8036,7 +6826,7 @@ async function syncTargetBranch(root, baseBranch, logger) {
|
|
|
8036
6826
|
}
|
|
8037
6827
|
function loadBuilderPrompt(repoRoot2, promptTemplate) {
|
|
8038
6828
|
const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
|
|
8039
|
-
const promptBody =
|
|
6829
|
+
const promptBody = existsSync12(promptPath) ? readFileSync14(promptPath, "utf-8") : promptTemplate;
|
|
8040
6830
|
return appendProtectedWorkGuidance(`${promptBody}
|
|
8041
6831
|
|
|
8042
6832
|
## Shared Run Context
|
|
@@ -8531,18 +7321,17 @@ async function runIssueCreateCommand(args, issueProvider, logger) {
|
|
|
8531
7321
|
|
|
8532
7322
|
// commands/prd-run.ts
|
|
8533
7323
|
import { lstatSync, realpathSync } from "fs";
|
|
8534
|
-
import { join as
|
|
7324
|
+
import { join as join16 } from "path";
|
|
8535
7325
|
|
|
8536
7326
|
// prd-run/state.ts
|
|
8537
7327
|
import {
|
|
8538
|
-
existsSync as
|
|
8539
|
-
mkdirSync as
|
|
8540
|
-
readFileSync as
|
|
8541
|
-
readdirSync as
|
|
8542
|
-
writeFileSync as
|
|
7328
|
+
existsSync as existsSync13,
|
|
7329
|
+
mkdirSync as mkdirSync7,
|
|
7330
|
+
readFileSync as readFileSync15,
|
|
7331
|
+
readdirSync as readdirSync3,
|
|
7332
|
+
writeFileSync as writeFileSync4
|
|
8543
7333
|
} from "fs";
|
|
8544
|
-
import {
|
|
8545
|
-
import { join as join17 } from "path";
|
|
7334
|
+
import { join as join15 } from "path";
|
|
8546
7335
|
import { z } from "zod";
|
|
8547
7336
|
var PRD_RUN_STATE_DIR = ".pourkit/prd-runs";
|
|
8548
7337
|
var PrdRunRecordSchema = z.object({
|
|
@@ -8559,11 +7348,9 @@ var PrdRunRecordSchema = z.object({
|
|
|
8559
7348
|
"waiting_for_integration",
|
|
8560
7349
|
"finalizing",
|
|
8561
7350
|
"final_reviewed",
|
|
8562
|
-
"complete"
|
|
8563
|
-
"completed_local_branch"
|
|
7351
|
+
"complete"
|
|
8564
7352
|
]),
|
|
8565
7353
|
updatedAt: z.string().min(1),
|
|
8566
|
-
mode: z.enum(["github", "local"]).optional(),
|
|
8567
7354
|
blockedGate: z.enum(["receipt-check", "branch-state", "git", "queue", "final-review"]).optional(),
|
|
8568
7355
|
targetName: z.string().min(1).optional(),
|
|
8569
7356
|
prdBranch: z.string().min(1).optional(),
|
|
@@ -8633,16 +7420,16 @@ function normalizePrdRunRef(ref) {
|
|
|
8633
7420
|
function readPrdRun(repoRoot2, prdRef) {
|
|
8634
7421
|
const normalized = normalizePrdRunRef(prdRef);
|
|
8635
7422
|
const recordPath = getRecordPath(repoRoot2, normalized);
|
|
8636
|
-
if (!
|
|
7423
|
+
if (!existsSync13(recordPath)) {
|
|
8637
7424
|
return { record: null, diagnostics: [] };
|
|
8638
7425
|
}
|
|
8639
7426
|
try {
|
|
8640
|
-
const raw = JSON.parse(
|
|
7427
|
+
const raw = JSON.parse(readFileSync15(recordPath, "utf-8"));
|
|
8641
7428
|
const parsed = normalizeLegacyPrdRunRecord(PrdRunRecordSchema.parse(raw));
|
|
8642
7429
|
return { record: parsed, diagnostics: [] };
|
|
8643
7430
|
} catch (error) {
|
|
8644
7431
|
try {
|
|
8645
|
-
const raw = JSON.parse(
|
|
7432
|
+
const raw = JSON.parse(readFileSync15(recordPath, "utf-8"));
|
|
8646
7433
|
if (raw && typeof raw === "object" && raw.start && typeof raw.start === "object" && raw.start.startBaseBranch === void 0) {
|
|
8647
7434
|
return {
|
|
8648
7435
|
record: raw,
|
|
@@ -8663,20 +7450,20 @@ function readPrdRun(repoRoot2, prdRef) {
|
|
|
8663
7450
|
}
|
|
8664
7451
|
}
|
|
8665
7452
|
function listPrdRuns(repoRoot2) {
|
|
8666
|
-
const stateDir =
|
|
8667
|
-
if (!
|
|
7453
|
+
const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
|
|
7454
|
+
if (!existsSync13(stateDir)) {
|
|
8668
7455
|
return { records: [], diagnostics: [] };
|
|
8669
7456
|
}
|
|
8670
7457
|
const records = [];
|
|
8671
7458
|
const diagnostics = [];
|
|
8672
|
-
for (const entry of
|
|
7459
|
+
for (const entry of readdirSync3(stateDir, { withFileTypes: true }).sort(
|
|
8673
7460
|
(left, right) => left.name.localeCompare(right.name)
|
|
8674
7461
|
)) {
|
|
8675
7462
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
8676
|
-
const recordPath =
|
|
7463
|
+
const recordPath = join15(stateDir, entry.name);
|
|
8677
7464
|
try {
|
|
8678
7465
|
const record = normalizeLegacyPrdRunRecord(
|
|
8679
|
-
PrdRunRecordSchema.parse(JSON.parse(
|
|
7466
|
+
PrdRunRecordSchema.parse(JSON.parse(readFileSync15(recordPath, "utf-8")))
|
|
8680
7467
|
);
|
|
8681
7468
|
records.push(record);
|
|
8682
7469
|
} catch (error) {
|
|
@@ -8689,10 +7476,10 @@ function listPrdRuns(repoRoot2) {
|
|
|
8689
7476
|
}
|
|
8690
7477
|
function writePrdRunRecord(repoRoot2, record) {
|
|
8691
7478
|
const normalized = normalizePrdRunRef(record.prdRef);
|
|
8692
|
-
const stateDir =
|
|
7479
|
+
const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
|
|
8693
7480
|
const recordPath = getRecordPath(repoRoot2, normalized);
|
|
8694
|
-
|
|
8695
|
-
|
|
7481
|
+
mkdirSync7(stateDir, { recursive: true });
|
|
7482
|
+
writeFileSync4(
|
|
8696
7483
|
recordPath,
|
|
8697
7484
|
JSON.stringify({ ...record, prdRef: normalized }, null, 2),
|
|
8698
7485
|
"utf-8"
|
|
@@ -8708,43 +7495,8 @@ function normalizeLegacyPrdRunRecord(record) {
|
|
|
8708
7495
|
offendingPaths: void 0
|
|
8709
7496
|
};
|
|
8710
7497
|
}
|
|
8711
|
-
var LocalPrdRunRecordSchema = z.object({
|
|
8712
|
-
prdId: z.string().regex(
|
|
8713
|
-
/^PRD-\d{4}$/,
|
|
8714
|
-
"PRD id must use four-digit format (e.g., PRD-0052)"
|
|
8715
|
-
),
|
|
8716
|
-
createdAt: z.string().min(1),
|
|
8717
|
-
receipts: z.object({
|
|
8718
|
-
start: z.object({
|
|
8719
|
-
startedAt: z.string().min(1),
|
|
8720
|
-
branch: z.string().min(1),
|
|
8721
|
-
baseCommit: z.string().min(1)
|
|
8722
|
-
}).strict().optional(),
|
|
8723
|
-
queue: z.object({ completedAt: z.string().min(1) }).strict().optional(),
|
|
8724
|
-
finalReview: z.object({
|
|
8725
|
-
completedAt: z.string().min(1),
|
|
8726
|
-
targetName: z.string().optional(),
|
|
8727
|
-
prdBranch: z.string().optional(),
|
|
8728
|
-
mergeBase: z.string().optional(),
|
|
8729
|
-
verdict: z.enum([
|
|
8730
|
-
"pass_no_changes",
|
|
8731
|
-
"pass_with_retouch",
|
|
8732
|
-
"needs_human_review",
|
|
8733
|
-
"blocked"
|
|
8734
|
-
]).optional(),
|
|
8735
|
-
diagnostics: z.array(z.string()).optional(),
|
|
8736
|
-
artifactPath: z.string().optional()
|
|
8737
|
-
}).strict().optional(),
|
|
8738
|
-
complete: z.object({
|
|
8739
|
-
completedAt: z.string().min(1),
|
|
8740
|
-
branch: z.string().min(1),
|
|
8741
|
-
stages: z.array(z.string())
|
|
8742
|
-
}).strict().optional()
|
|
8743
|
-
}).strict(),
|
|
8744
|
-
metadata: z.record(z.unknown())
|
|
8745
|
-
}).strict();
|
|
8746
7498
|
function getRecordPath(repoRoot2, prdRef) {
|
|
8747
|
-
return
|
|
7499
|
+
return join15(
|
|
8748
7500
|
repoRoot2,
|
|
8749
7501
|
PRD_RUN_STATE_DIR,
|
|
8750
7502
|
`${normalizePrdRunRef(prdRef)}.json`
|
|
@@ -8752,567 +7504,9 @@ function getRecordPath(repoRoot2, prdRef) {
|
|
|
8752
7504
|
}
|
|
8753
7505
|
|
|
8754
7506
|
// prd-run/coordinator.ts
|
|
8755
|
-
import { spawnSync as
|
|
8756
|
-
import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
|
|
8757
|
-
import { join as join20 } from "path";
|
|
7507
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
8758
7508
|
import { Match, pipe } from "effect";
|
|
8759
7509
|
|
|
8760
|
-
// prd-run/local-queue-loop.ts
|
|
8761
|
-
import { readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
|
|
8762
|
-
import { join as join19 } from "path";
|
|
8763
|
-
|
|
8764
|
-
// prd-run/local-artifacts.ts
|
|
8765
|
-
import { existsSync as existsSync15 } from "fs";
|
|
8766
|
-
import { join as join18 } from "path";
|
|
8767
|
-
var REQUIRED_PRD_FIELDS = [
|
|
8768
|
-
"schemaVersion",
|
|
8769
|
-
"kind",
|
|
8770
|
-
"id",
|
|
8771
|
-
"title",
|
|
8772
|
-
"status",
|
|
8773
|
-
"childIssueIds",
|
|
8774
|
-
"linkedDecisionIds",
|
|
8775
|
-
"reconciliationTarget",
|
|
8776
|
-
"bodyMarkdown",
|
|
8777
|
-
"bodyContract",
|
|
8778
|
-
"receipts",
|
|
8779
|
-
"githubProjection"
|
|
8780
|
-
];
|
|
8781
|
-
var REQUIRED_ISSUE_FIELDS = [
|
|
8782
|
-
"schemaVersion",
|
|
8783
|
-
"kind",
|
|
8784
|
-
"id",
|
|
8785
|
-
"parentPrdId",
|
|
8786
|
-
"title",
|
|
8787
|
-
"status",
|
|
8788
|
-
"labels",
|
|
8789
|
-
"dependencyIssueIds",
|
|
8790
|
-
"branchName",
|
|
8791
|
-
"blockedReason",
|
|
8792
|
-
"readyForHumanReason",
|
|
8793
|
-
"bodyMarkdown",
|
|
8794
|
-
"bodyContract",
|
|
8795
|
-
"receipts",
|
|
8796
|
-
"githubProjection"
|
|
8797
|
-
];
|
|
8798
|
-
function prdStorePath(repoRoot2, prdId) {
|
|
8799
|
-
return join18(repoRoot2, ".pourkit", "local-prd-runs", prdId);
|
|
8800
|
-
}
|
|
8801
|
-
function prdArtifactPath(repoRoot2, prdId) {
|
|
8802
|
-
return join18(prdStorePath(repoRoot2, prdId), "prd.json");
|
|
8803
|
-
}
|
|
8804
|
-
function issueArtifactPath(repoRoot2, prdId, issueId) {
|
|
8805
|
-
return join18(prdStorePath(repoRoot2, prdId), "issues", `${issueId}.json`);
|
|
8806
|
-
}
|
|
8807
|
-
function hasRequiredFields(data, requiredFields) {
|
|
8808
|
-
for (const field of requiredFields) {
|
|
8809
|
-
if (!(field in data)) {
|
|
8810
|
-
return String(field);
|
|
8811
|
-
}
|
|
8812
|
-
}
|
|
8813
|
-
return null;
|
|
8814
|
-
}
|
|
8815
|
-
async function resolveLocalPrdArtifact(prdId, repoRoot2) {
|
|
8816
|
-
const root = repoRoot2 ?? process.cwd();
|
|
8817
|
-
const prdPath = prdArtifactPath(root, prdId);
|
|
8818
|
-
if (!existsSync15(prdPath)) {
|
|
8819
|
-
return {
|
|
8820
|
-
ok: false,
|
|
8821
|
-
failureCode: "missing_prd_artifact",
|
|
8822
|
-
message: `Local PRD artifact not found at ${prdPath}. Create the PRD artifact at .pourkit/local-prd-runs/${prdId}/prd.json using to-prd-local.`
|
|
8823
|
-
};
|
|
8824
|
-
}
|
|
8825
|
-
const result = readLocalArtifact(prdPath);
|
|
8826
|
-
if (!result.ok || !result.data) {
|
|
8827
|
-
return {
|
|
8828
|
-
ok: false,
|
|
8829
|
-
failureCode: "invalid_artifact",
|
|
8830
|
-
message: `Failed to read or parse PRD artifact at ${prdPath}: ${result.message}`
|
|
8831
|
-
};
|
|
8832
|
-
}
|
|
8833
|
-
const missing = hasRequiredFields(
|
|
8834
|
-
result.data,
|
|
8835
|
-
REQUIRED_PRD_FIELDS
|
|
8836
|
-
);
|
|
8837
|
-
if (missing) {
|
|
8838
|
-
return {
|
|
8839
|
-
ok: false,
|
|
8840
|
-
failureCode: "invalid_artifact",
|
|
8841
|
-
message: `PRD artifact at ${prdPath} is missing required field: ${missing}`
|
|
8842
|
-
};
|
|
8843
|
-
}
|
|
8844
|
-
if (result.data.kind !== "prd") {
|
|
8845
|
-
return {
|
|
8846
|
-
ok: false,
|
|
8847
|
-
failureCode: "invalid_artifact",
|
|
8848
|
-
message: `PRD artifact at ${prdPath} has kind "${result.data.kind}", expected "prd"`
|
|
8849
|
-
};
|
|
8850
|
-
}
|
|
8851
|
-
return { ok: true, data: result.data };
|
|
8852
|
-
}
|
|
8853
|
-
async function resolveLocalIssueArtifacts(prdId, repoRoot2) {
|
|
8854
|
-
const root = repoRoot2 ?? process.cwd();
|
|
8855
|
-
const prdResult = await resolveLocalPrdArtifact(prdId, root);
|
|
8856
|
-
if (!prdResult.ok) {
|
|
8857
|
-
return prdResult;
|
|
8858
|
-
}
|
|
8859
|
-
const childIssueIds = prdResult.data.childIssueIds;
|
|
8860
|
-
const issues = [];
|
|
8861
|
-
for (const childId of childIssueIds) {
|
|
8862
|
-
const issuePath = issueArtifactPath(root, prdId, childId);
|
|
8863
|
-
if (!existsSync15(issuePath)) {
|
|
8864
|
-
return {
|
|
8865
|
-
ok: false,
|
|
8866
|
-
failureCode: "missing_child_issue",
|
|
8867
|
-
missingId: childId,
|
|
8868
|
-
message: `Child Issue artifact "${childId}" not found at ${issuePath}. Ensure all child issues listed in childIssueIds have corresponding JSON artifacts under .pourkit/local-prd-runs/${prdId}/issues/.`
|
|
8869
|
-
};
|
|
8870
|
-
}
|
|
8871
|
-
const result = readLocalArtifact(issuePath);
|
|
8872
|
-
if (!result.ok || !result.data) {
|
|
8873
|
-
return {
|
|
8874
|
-
ok: false,
|
|
8875
|
-
failureCode: "invalid_artifact",
|
|
8876
|
-
missingId: childId,
|
|
8877
|
-
message: `Failed to read or parse Issue artifact at ${issuePath}: ${result.message}`
|
|
8878
|
-
};
|
|
8879
|
-
}
|
|
8880
|
-
const missing = hasRequiredFields(
|
|
8881
|
-
result.data,
|
|
8882
|
-
REQUIRED_ISSUE_FIELDS
|
|
8883
|
-
);
|
|
8884
|
-
if (missing) {
|
|
8885
|
-
return {
|
|
8886
|
-
ok: false,
|
|
8887
|
-
failureCode: "invalid_artifact",
|
|
8888
|
-
missingId: childId,
|
|
8889
|
-
message: `Issue artifact at ${issuePath} is missing required field: ${missing}`
|
|
8890
|
-
};
|
|
8891
|
-
}
|
|
8892
|
-
if (result.data.kind !== "issue") {
|
|
8893
|
-
return {
|
|
8894
|
-
ok: false,
|
|
8895
|
-
failureCode: "invalid_artifact",
|
|
8896
|
-
missingId: childId,
|
|
8897
|
-
message: `Issue artifact at ${issuePath} has kind "${result.data.kind}", expected "issue"`
|
|
8898
|
-
};
|
|
8899
|
-
}
|
|
8900
|
-
issues.push(result.data);
|
|
8901
|
-
}
|
|
8902
|
-
return { ok: true, data: issues };
|
|
8903
|
-
}
|
|
8904
|
-
|
|
8905
|
-
// prd-run/local-issue-run.ts
|
|
8906
|
-
import { execFileSync as execFileSync3 } from "child_process";
|
|
8907
|
-
init_common();
|
|
8908
|
-
async function createLocalIssueBranch(prdId, issueId, repoRoot2) {
|
|
8909
|
-
const root = repoRoot2 ?? process.cwd();
|
|
8910
|
-
const issuesResult = await resolveLocalIssueArtifacts(prdId, root);
|
|
8911
|
-
if (!issuesResult.ok) {
|
|
8912
|
-
throw new Error(
|
|
8913
|
-
`Cannot create local issue branch: ${issuesResult.message}`
|
|
8914
|
-
);
|
|
8915
|
-
}
|
|
8916
|
-
const issue = issuesResult.data.find((i) => i.id === issueId);
|
|
8917
|
-
if (!issue) {
|
|
8918
|
-
throw new Error(`Issue "${issueId}" not found under PRD "${prdId}"`);
|
|
8919
|
-
}
|
|
8920
|
-
const slug = slugify(issue.title);
|
|
8921
|
-
const branchName = `local/${prdId}/${issueId}-${slug}`;
|
|
8922
|
-
const validation = validateLocalBranchName(branchName);
|
|
8923
|
-
if (!validation.ok) {
|
|
8924
|
-
throw new Error(
|
|
8925
|
-
`Invalid local issue branch name "${branchName}": ${validation.message}`
|
|
8926
|
-
);
|
|
8927
|
-
}
|
|
8928
|
-
const localPrdBranch = `local/${prdId}`;
|
|
8929
|
-
try {
|
|
8930
|
-
execFileSync3(
|
|
8931
|
-
"git",
|
|
8932
|
-
["show-ref", "--verify", "--quiet", `refs/heads/${localPrdBranch}`],
|
|
8933
|
-
{
|
|
8934
|
-
cwd: root,
|
|
8935
|
-
encoding: "utf8",
|
|
8936
|
-
stdio: "pipe"
|
|
8937
|
-
}
|
|
8938
|
-
);
|
|
8939
|
-
} catch {
|
|
8940
|
-
throw new Error(
|
|
8941
|
-
`Local PRD branch "${localPrdBranch}" does not exist. Create it first.`
|
|
8942
|
-
);
|
|
8943
|
-
}
|
|
8944
|
-
let branchExists = false;
|
|
8945
|
-
try {
|
|
8946
|
-
execFileSync3(
|
|
8947
|
-
"git",
|
|
8948
|
-
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
8949
|
-
{
|
|
8950
|
-
cwd: root,
|
|
8951
|
-
encoding: "utf8",
|
|
8952
|
-
stdio: "pipe"
|
|
8953
|
-
}
|
|
8954
|
-
);
|
|
8955
|
-
branchExists = true;
|
|
8956
|
-
} catch {
|
|
8957
|
-
}
|
|
8958
|
-
if (!branchExists) {
|
|
8959
|
-
execFileSync3("git", ["checkout", "-b", branchName, localPrdBranch], {
|
|
8960
|
-
cwd: root,
|
|
8961
|
-
encoding: "utf8",
|
|
8962
|
-
stdio: "pipe"
|
|
8963
|
-
});
|
|
8964
|
-
}
|
|
8965
|
-
return { branchName, issue };
|
|
8966
|
-
}
|
|
8967
|
-
async function runLocalIssue(prdId, issueId, builder, repoRoot2, memory) {
|
|
8968
|
-
try {
|
|
8969
|
-
const root = repoRoot2 ?? process.cwd();
|
|
8970
|
-
const { branchName: branch, issue } = await createLocalIssueBranch(
|
|
8971
|
-
prdId,
|
|
8972
|
-
issueId,
|
|
8973
|
-
root
|
|
8974
|
-
);
|
|
8975
|
-
if (builder) {
|
|
8976
|
-
const result = await builder.execute({
|
|
8977
|
-
prdId,
|
|
8978
|
-
issueId,
|
|
8979
|
-
branch,
|
|
8980
|
-
bodyMarkdown: issue.bodyMarkdown,
|
|
8981
|
-
...memory ? { memory } : {}
|
|
8982
|
-
});
|
|
8983
|
-
if (!result.ok) {
|
|
8984
|
-
return {
|
|
8985
|
-
ok: false,
|
|
8986
|
-
issueId,
|
|
8987
|
-
branch,
|
|
8988
|
-
failureCode: result.error ?? "Builder execution failed"
|
|
8989
|
-
};
|
|
8990
|
-
}
|
|
8991
|
-
if (!result.issueFinalReviewPassedAt || !result.finalizerTitle || !result.finalizerBody) {
|
|
8992
|
-
return {
|
|
8993
|
-
ok: false,
|
|
8994
|
-
issueId,
|
|
8995
|
-
branch,
|
|
8996
|
-
failureCode: "Local issue run did not produce Issue Final Review and finalizer receipts"
|
|
8997
|
-
};
|
|
8998
|
-
}
|
|
8999
|
-
return {
|
|
9000
|
-
ok: true,
|
|
9001
|
-
issueId,
|
|
9002
|
-
branch,
|
|
9003
|
-
issueFinalReviewPassedAt: result.issueFinalReviewPassedAt,
|
|
9004
|
-
finalizerTitle: result.finalizerTitle,
|
|
9005
|
-
finalizerBody: result.finalizerBody,
|
|
9006
|
-
finalizerArtifactPath: result.finalizerArtifactPath
|
|
9007
|
-
};
|
|
9008
|
-
}
|
|
9009
|
-
return {
|
|
9010
|
-
ok: false,
|
|
9011
|
-
issueId,
|
|
9012
|
-
branch,
|
|
9013
|
-
failureCode: "Local issue runner requires Issue Final Review and finalizer lifecycle execution before merge"
|
|
9014
|
-
};
|
|
9015
|
-
} catch (error) {
|
|
9016
|
-
return {
|
|
9017
|
-
ok: false,
|
|
9018
|
-
issueId,
|
|
9019
|
-
branch: "",
|
|
9020
|
-
failureCode: error instanceof Error ? error.message : String(error)
|
|
9021
|
-
};
|
|
9022
|
-
}
|
|
9023
|
-
}
|
|
9024
|
-
|
|
9025
|
-
// prd-run/local-issue-adapter.ts
|
|
9026
|
-
var VALID_TRIAGE_LABELS = /* @__PURE__ */ new Set([
|
|
9027
|
-
"needs-triage",
|
|
9028
|
-
"ready-for-agent",
|
|
9029
|
-
"blocked",
|
|
9030
|
-
"ready-for-human",
|
|
9031
|
-
"wontfix"
|
|
9032
|
-
]);
|
|
9033
|
-
function countTypeLabels(labels) {
|
|
9034
|
-
return labels.filter((l) => l.startsWith("type:")).length;
|
|
9035
|
-
}
|
|
9036
|
-
function countTriageLabels(labels) {
|
|
9037
|
-
return labels.filter((l) => VALID_TRIAGE_LABELS.has(l)).length;
|
|
9038
|
-
}
|
|
9039
|
-
function countCategoryLabels(labels) {
|
|
9040
|
-
const triageCount = countTriageLabels(labels);
|
|
9041
|
-
const typeCount = countTypeLabels(labels);
|
|
9042
|
-
return labels.length - triageCount - typeCount;
|
|
9043
|
-
}
|
|
9044
|
-
function hasValidLabels(labels) {
|
|
9045
|
-
if (countTriageLabels(labels) !== 1) return false;
|
|
9046
|
-
if (!labels.includes("ready-for-agent")) return false;
|
|
9047
|
-
if (countTypeLabels(labels) !== 1) return false;
|
|
9048
|
-
if (countCategoryLabels(labels) !== 1) return false;
|
|
9049
|
-
return true;
|
|
9050
|
-
}
|
|
9051
|
-
function isIssueBlockedByDependencies(issue, issues) {
|
|
9052
|
-
const blockers = [];
|
|
9053
|
-
for (const depId of issue.dependencyIssueIds) {
|
|
9054
|
-
const dep = issues.find((i) => i.id === depId);
|
|
9055
|
-
if (!dep || dep.receipts.completedAt === null) {
|
|
9056
|
-
blockers.push(depId);
|
|
9057
|
-
}
|
|
9058
|
-
}
|
|
9059
|
-
return blockers;
|
|
9060
|
-
}
|
|
9061
|
-
async function getRunnableLocalIssues(prdId, repoRoot2) {
|
|
9062
|
-
const issuesResult = await resolveLocalIssueArtifacts(prdId, repoRoot2);
|
|
9063
|
-
if (!issuesResult.ok) return [];
|
|
9064
|
-
const issues = issuesResult.data;
|
|
9065
|
-
const runnable = [];
|
|
9066
|
-
for (const issue of issues) {
|
|
9067
|
-
if (!hasValidLabels(issue.labels)) continue;
|
|
9068
|
-
if (issue.status !== "ready-for-agent") continue;
|
|
9069
|
-
const blockers = isIssueBlockedByDependencies(issue, issues);
|
|
9070
|
-
runnable.push({
|
|
9071
|
-
id: issue.id,
|
|
9072
|
-
title: issue.title,
|
|
9073
|
-
labels: issue.labels,
|
|
9074
|
-
blockedBy: blockers.length > 0 ? blockers : void 0
|
|
9075
|
-
});
|
|
9076
|
-
}
|
|
9077
|
-
const filtered = runnable.filter(
|
|
9078
|
-
(i) => !i.blockedBy || i.blockedBy.length === 0
|
|
9079
|
-
);
|
|
9080
|
-
return filtered;
|
|
9081
|
-
}
|
|
9082
|
-
|
|
9083
|
-
// prd-run/local-queue-loop.ts
|
|
9084
|
-
var CHILD_CLEANUP_LABELS = ["agent-in-progress", "pr-open-awaiting-merge"];
|
|
9085
|
-
var LOCAL_ISSUE_ID_REGEX = /^I-(\d+)$/i;
|
|
9086
|
-
function getIssueArtifactPath2(repoRoot2, prdId, issueId) {
|
|
9087
|
-
return join19(
|
|
9088
|
-
repoRoot2,
|
|
9089
|
-
".pourkit",
|
|
9090
|
-
"local-prd-runs",
|
|
9091
|
-
prdId,
|
|
9092
|
-
"issues",
|
|
9093
|
-
`${issueId}.json`
|
|
9094
|
-
);
|
|
9095
|
-
}
|
|
9096
|
-
function readIssueArtifact(repoRoot2, prdId, issueId) {
|
|
9097
|
-
try {
|
|
9098
|
-
const content = readFileSync17(
|
|
9099
|
-
getIssueArtifactPath2(repoRoot2, prdId, issueId),
|
|
9100
|
-
"utf-8"
|
|
9101
|
-
);
|
|
9102
|
-
return JSON.parse(content);
|
|
9103
|
-
} catch {
|
|
9104
|
-
return null;
|
|
9105
|
-
}
|
|
9106
|
-
}
|
|
9107
|
-
function writeIssueArtifact(repoRoot2, prdId, issue) {
|
|
9108
|
-
writeFileSync6(
|
|
9109
|
-
getIssueArtifactPath2(repoRoot2, prdId, issue.id),
|
|
9110
|
-
JSON.stringify(issue, null, 2),
|
|
9111
|
-
"utf-8"
|
|
9112
|
-
);
|
|
9113
|
-
}
|
|
9114
|
-
function compareLocalIssueIds(a, b) {
|
|
9115
|
-
const aMatch = a.match(LOCAL_ISSUE_ID_REGEX);
|
|
9116
|
-
const bMatch = b.match(LOCAL_ISSUE_ID_REGEX);
|
|
9117
|
-
if (aMatch && bMatch) {
|
|
9118
|
-
const aNumber = Number.parseInt(aMatch[1], 10);
|
|
9119
|
-
const bNumber = Number.parseInt(bMatch[1], 10);
|
|
9120
|
-
if (aNumber !== bNumber) return aNumber - bNumber;
|
|
9121
|
-
}
|
|
9122
|
-
return a.localeCompare(b);
|
|
9123
|
-
}
|
|
9124
|
-
async function reconcileLocalBlockedIssues(prdId, repoRoot2) {
|
|
9125
|
-
const root = repoRoot2 ?? process.cwd();
|
|
9126
|
-
const issuesResult = await resolveLocalIssueArtifacts(prdId, root);
|
|
9127
|
-
if (!issuesResult.ok) return;
|
|
9128
|
-
const issues = issuesResult.data;
|
|
9129
|
-
for (const issue of issues) {
|
|
9130
|
-
const isBlocked = issue.status === "blocked";
|
|
9131
|
-
if (!isBlocked) continue;
|
|
9132
|
-
if (issue.dependencyIssueIds.length === 0) continue;
|
|
9133
|
-
const depResults = await Promise.all(
|
|
9134
|
-
issue.dependencyIssueIds.map(
|
|
9135
|
-
(depId) => hasLocalIssueMergeReceipt(prdId, depId, root)
|
|
9136
|
-
)
|
|
9137
|
-
);
|
|
9138
|
-
if (!depResults.every(Boolean)) continue;
|
|
9139
|
-
const updated = readIssueArtifact(root, prdId, issue.id);
|
|
9140
|
-
if (!updated) continue;
|
|
9141
|
-
updated.blockedReason = null;
|
|
9142
|
-
updated.labels = updated.labels.filter((l) => l !== "blocked");
|
|
9143
|
-
if (!updated.labels.includes("ready-for-agent")) {
|
|
9144
|
-
updated.labels.push("ready-for-agent");
|
|
9145
|
-
}
|
|
9146
|
-
if (hasValidLabels(updated.labels)) {
|
|
9147
|
-
updated.status = "ready-for-agent";
|
|
9148
|
-
} else {
|
|
9149
|
-
updated.status = "needs-triage";
|
|
9150
|
-
updated.labels = updated.labels.filter((l) => l !== "ready-for-agent");
|
|
9151
|
-
if (!updated.labels.includes("needs-triage")) {
|
|
9152
|
-
updated.labels.push("needs-triage");
|
|
9153
|
-
}
|
|
9154
|
-
}
|
|
9155
|
-
updated.receipts.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9156
|
-
writeIssueArtifact(root, prdId, updated);
|
|
9157
|
-
}
|
|
9158
|
-
}
|
|
9159
|
-
async function cleanupChildGitHubIssue(issue, issueProvider) {
|
|
9160
|
-
const issueNumber = issue.githubProjection.issueNumber;
|
|
9161
|
-
if (!issueNumber) return { ok: true };
|
|
9162
|
-
if (!issueProvider) {
|
|
9163
|
-
return {
|
|
9164
|
-
ok: false,
|
|
9165
|
-
failureCode: "missing_issue_provider",
|
|
9166
|
-
repairGuidance: "Issue provider is not available. Cannot close child GitHub Issue or remove labels. Configure a GitHub Issue provider to enable cleanup parity."
|
|
9167
|
-
};
|
|
9168
|
-
}
|
|
9169
|
-
for (const label of CHILD_CLEANUP_LABELS) {
|
|
9170
|
-
try {
|
|
9171
|
-
await issueProvider.removeLabel(issueNumber, label);
|
|
9172
|
-
} catch {
|
|
9173
|
-
}
|
|
9174
|
-
}
|
|
9175
|
-
try {
|
|
9176
|
-
await issueProvider.closeIssue(issueNumber);
|
|
9177
|
-
} catch {
|
|
9178
|
-
}
|
|
9179
|
-
return { ok: true };
|
|
9180
|
-
}
|
|
9181
|
-
async function runLocalQueueLoop(prdId, repoRoot2, issueProvider, memory) {
|
|
9182
|
-
const root = repoRoot2 ?? process.cwd();
|
|
9183
|
-
const completedIssues = [];
|
|
9184
|
-
const blockedIssues = [];
|
|
9185
|
-
await reconcileLocalBlockedIssues(prdId, root);
|
|
9186
|
-
for (; ; ) {
|
|
9187
|
-
const runnable = await getRunnableLocalIssues(prdId, root);
|
|
9188
|
-
if (runnable.length === 0) {
|
|
9189
|
-
const issuesResult = await resolveLocalIssueArtifacts(prdId, root);
|
|
9190
|
-
if (issuesResult.ok) {
|
|
9191
|
-
for (const issue2 of issuesResult.data) {
|
|
9192
|
-
if (issue2.status === "blocked") {
|
|
9193
|
-
blockedIssues.push(issue2.id);
|
|
9194
|
-
}
|
|
9195
|
-
}
|
|
9196
|
-
}
|
|
9197
|
-
break;
|
|
9198
|
-
}
|
|
9199
|
-
const issue = [...runnable].sort(
|
|
9200
|
-
(a, b) => compareLocalIssueIds(a.id, b.id)
|
|
9201
|
-
)[0];
|
|
9202
|
-
const runResult = memory ? await runLocalIssue(prdId, issue.id, void 0, root, memory) : await runLocalIssue(prdId, issue.id, void 0, root);
|
|
9203
|
-
if (!runResult.ok) {
|
|
9204
|
-
return {
|
|
9205
|
-
ok: false,
|
|
9206
|
-
completedIssues,
|
|
9207
|
-
blockedIssues,
|
|
9208
|
-
failureCode: runResult.failureCode ?? `Issue ${issue.id} failed`,
|
|
9209
|
-
repairGuidance: `Local issue run failed for ${issue.id}. Check the issue artifact and branch state.`,
|
|
9210
|
-
blockedGate: "queue"
|
|
9211
|
-
};
|
|
9212
|
-
}
|
|
9213
|
-
if (!runResult.issueFinalReviewPassedAt || !runResult.finalizerTitle || !runResult.finalizerBody) {
|
|
9214
|
-
return {
|
|
9215
|
-
ok: false,
|
|
9216
|
-
completedIssues,
|
|
9217
|
-
blockedIssues,
|
|
9218
|
-
failureCode: runResult.failureCode ?? "issue_final_review_not_passed",
|
|
9219
|
-
repairGuidance: "Local issue run must complete Issue Final Review and finalizer before local squash merge.",
|
|
9220
|
-
blockedGate: "queue"
|
|
9221
|
-
};
|
|
9222
|
-
}
|
|
9223
|
-
const runArtifact = readIssueArtifact(root, prdId, issue.id);
|
|
9224
|
-
if (!runArtifact) {
|
|
9225
|
-
return {
|
|
9226
|
-
ok: false,
|
|
9227
|
-
completedIssues,
|
|
9228
|
-
blockedIssues,
|
|
9229
|
-
failureCode: "missing_issue_artifact",
|
|
9230
|
-
repairGuidance: `Issue artifact not found for ${issue.id} under PRD ${prdId}. Ensure the issue exists before running the queue loop.`,
|
|
9231
|
-
blockedGate: "queue"
|
|
9232
|
-
};
|
|
9233
|
-
}
|
|
9234
|
-
runArtifact.issueFinalReviewPassedAt = runResult.issueFinalReviewPassedAt;
|
|
9235
|
-
runArtifact.receipts.reviewedAt = runResult.issueFinalReviewPassedAt;
|
|
9236
|
-
runArtifact.receipts.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9237
|
-
writeIssueArtifact(root, prdId, runArtifact);
|
|
9238
|
-
const mergeResult = await squashMergeLocalIssue(
|
|
9239
|
-
prdId,
|
|
9240
|
-
issue.id,
|
|
9241
|
-
{
|
|
9242
|
-
finalizerTitle: runResult.finalizerTitle,
|
|
9243
|
-
finalizerBody: runResult.finalizerBody,
|
|
9244
|
-
finalizerArtifactPath: runResult.finalizerArtifactPath ?? "",
|
|
9245
|
-
sourceBranch: runResult.branch
|
|
9246
|
-
},
|
|
9247
|
-
root
|
|
9248
|
-
);
|
|
9249
|
-
if (!mergeResult.ok) {
|
|
9250
|
-
if (mergeResult.failureCode === "already_merged") {
|
|
9251
|
-
const alreadyMergedArtifact = readIssueArtifact(root, prdId, issue.id);
|
|
9252
|
-
if (alreadyMergedArtifact) {
|
|
9253
|
-
alreadyMergedArtifact.status = "complete";
|
|
9254
|
-
alreadyMergedArtifact.receipts.completedAt = mergeResult.receipt?.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9255
|
-
alreadyMergedArtifact.receipts.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9256
|
-
writeIssueArtifact(root, prdId, alreadyMergedArtifact);
|
|
9257
|
-
const cleanupResult = await cleanupChildGitHubIssue(
|
|
9258
|
-
alreadyMergedArtifact,
|
|
9259
|
-
issueProvider
|
|
9260
|
-
);
|
|
9261
|
-
if (!cleanupResult.ok) {
|
|
9262
|
-
return {
|
|
9263
|
-
ok: false,
|
|
9264
|
-
completedIssues,
|
|
9265
|
-
blockedIssues,
|
|
9266
|
-
failureCode: cleanupResult.failureCode ?? "child_cleanup_failed",
|
|
9267
|
-
repairGuidance: cleanupResult.repairGuidance ?? `Child GitHub Issue cleanup failed for ${issue.id}.`,
|
|
9268
|
-
blockedGate: "queue"
|
|
9269
|
-
};
|
|
9270
|
-
}
|
|
9271
|
-
}
|
|
9272
|
-
completedIssues.push(issue.id);
|
|
9273
|
-
await reconcileLocalBlockedIssues(prdId, root);
|
|
9274
|
-
continue;
|
|
9275
|
-
}
|
|
9276
|
-
return {
|
|
9277
|
-
ok: false,
|
|
9278
|
-
completedIssues,
|
|
9279
|
-
blockedIssues,
|
|
9280
|
-
failureCode: mergeResult.failureCode ?? `Merge of ${issue.id} failed`,
|
|
9281
|
-
repairGuidance: mergeResult.repairGuidance,
|
|
9282
|
-
blockedGate: "queue"
|
|
9283
|
-
};
|
|
9284
|
-
}
|
|
9285
|
-
const completedArtifact = readIssueArtifact(root, prdId, issue.id);
|
|
9286
|
-
if (completedArtifact) {
|
|
9287
|
-
completedArtifact.status = "complete";
|
|
9288
|
-
completedArtifact.receipts.completedAt = mergeResult.receipt?.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9289
|
-
completedArtifact.receipts.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9290
|
-
writeIssueArtifact(root, prdId, completedArtifact);
|
|
9291
|
-
const cleanupResult = await cleanupChildGitHubIssue(
|
|
9292
|
-
completedArtifact,
|
|
9293
|
-
issueProvider
|
|
9294
|
-
);
|
|
9295
|
-
if (!cleanupResult.ok) {
|
|
9296
|
-
return {
|
|
9297
|
-
ok: false,
|
|
9298
|
-
completedIssues,
|
|
9299
|
-
blockedIssues,
|
|
9300
|
-
failureCode: cleanupResult.failureCode ?? "child_cleanup_failed",
|
|
9301
|
-
repairGuidance: cleanupResult.repairGuidance ?? `Child GitHub Issue cleanup failed for ${issue.id}.`,
|
|
9302
|
-
blockedGate: "queue"
|
|
9303
|
-
};
|
|
9304
|
-
}
|
|
9305
|
-
}
|
|
9306
|
-
completedIssues.push(issue.id);
|
|
9307
|
-
await reconcileLocalBlockedIssues(prdId, root);
|
|
9308
|
-
}
|
|
9309
|
-
return {
|
|
9310
|
-
ok: true,
|
|
9311
|
-
completedIssues,
|
|
9312
|
-
blockedIssues
|
|
9313
|
-
};
|
|
9314
|
-
}
|
|
9315
|
-
|
|
9316
7510
|
// commands/queue.ts
|
|
9317
7511
|
init_common();
|
|
9318
7512
|
import { Effect as Effect8 } from "effect";
|
|
@@ -9670,7 +7864,6 @@ function runOneQueueIssueEffect(options) {
|
|
|
9670
7864
|
}
|
|
9671
7865
|
const { issue: selected } = outcome;
|
|
9672
7866
|
const baseBranchOverride = options.queueRunContext?.prdBranch;
|
|
9673
|
-
const prdRunMode = options.prdRunMode ?? options.queueRunContext?.prdRunMode;
|
|
9674
7867
|
const runResult = yield* Effect8.tryPromise({
|
|
9675
7868
|
try: () => runIssueCommand({
|
|
9676
7869
|
issueNumber: selected.number,
|
|
@@ -9682,8 +7875,7 @@ function runOneQueueIssueEffect(options) {
|
|
|
9682
7875
|
force,
|
|
9683
7876
|
logger,
|
|
9684
7877
|
repoRoot: repoRoot2,
|
|
9685
|
-
...baseBranchOverride ? { baseBranchOverride } : {}
|
|
9686
|
-
...prdRunMode ? { prdRunMode } : {}
|
|
7878
|
+
...baseBranchOverride ? { baseBranchOverride } : {}
|
|
9687
7879
|
}),
|
|
9688
7880
|
catch: (e) => {
|
|
9689
7881
|
if (e instanceof Error && e.name === "HumanHandoffStop") {
|
|
@@ -9699,10 +7891,6 @@ function runOneQueueIssueEffect(options) {
|
|
|
9699
7891
|
logger.raw(` Branch: ${runResult.branchName}`);
|
|
9700
7892
|
if (runResult.noOp) {
|
|
9701
7893
|
logger.raw(" Status: no-op (closed without PR)");
|
|
9702
|
-
} else if (runResult.mode === "local") {
|
|
9703
|
-
logger.raw(` Local PRD Branch: ${runResult.localPrdBranch}`);
|
|
9704
|
-
logger.raw(` Merge Commit: ${runResult.mergeCommit}`);
|
|
9705
|
-
logger.raw(` Receipt: ${runResult.receiptPath}`);
|
|
9706
7894
|
} else {
|
|
9707
7895
|
logger.raw(` PR Title: ${runResult.prTitle}`);
|
|
9708
7896
|
logger.raw(` PR Number: ${runResult.prNumber}`);
|
|
@@ -9762,8 +7950,7 @@ async function runQueueCommand(options) {
|
|
|
9762
7950
|
logger: options.logger,
|
|
9763
7951
|
repoRoot: options.repoRoot,
|
|
9764
7952
|
prdRef: options.prdRef,
|
|
9765
|
-
queueRunContext: options.queueRunContext
|
|
9766
|
-
prdRunMode: options.queueRunContext?.prdRunMode
|
|
7953
|
+
queueRunContext: options.queueRunContext
|
|
9767
7954
|
};
|
|
9768
7955
|
if (!options.loop) {
|
|
9769
7956
|
return runEffectAndMapExit(runQueue(queueOptions));
|
|
@@ -9793,11 +7980,6 @@ function planPrdRunLaunchResume(record) {
|
|
|
9793
7980
|
skipped: ["start", "queue"],
|
|
9794
7981
|
resumed: []
|
|
9795
7982
|
})),
|
|
9796
|
-
Match.when({ status: "completed_local_branch" }, () => ({
|
|
9797
|
-
attempted: [],
|
|
9798
|
-
skipped: ["start", "queue"],
|
|
9799
|
-
resumed: []
|
|
9800
|
-
})),
|
|
9801
7983
|
Match.when({ status: "complete" }, () => ({
|
|
9802
7984
|
attempted: [],
|
|
9803
7985
|
skipped: ["start", "queue"],
|
|
@@ -9945,42 +8127,16 @@ function resolvePrdRunBaseBranch(options) {
|
|
|
9945
8127
|
return {
|
|
9946
8128
|
ok: false,
|
|
9947
8129
|
gate: "branch-state",
|
|
9948
|
-
reason: `Invalid PRD Run base branch: "${trimmed}" must not end with ".lock" suffix.`,
|
|
9949
|
-
diagnostics: [`Provided branch value: "${trimmed}"`],
|
|
9950
|
-
offendingPaths: []
|
|
9951
|
-
};
|
|
9952
|
-
}
|
|
9953
|
-
const source = options.baseBranchOverride !== void 0 ? "override" : "target";
|
|
9954
|
-
return { ok: true, baseBranch: trimmed, source };
|
|
9955
|
-
}
|
|
9956
|
-
function validateLocalStartStore(repoRoot2, prdRef) {
|
|
9957
|
-
const localStoreDir = join20(repoRoot2, ".pourkit", "local-prd-runs", prdRef);
|
|
9958
|
-
const localStorePath = join20(localStoreDir, "prd.json");
|
|
9959
|
-
let localStoreReady = false;
|
|
9960
|
-
if (existsSync16(localStorePath)) {
|
|
9961
|
-
try {
|
|
9962
|
-
const content = JSON.parse(readFileSync18(localStorePath, "utf8"));
|
|
9963
|
-
localStoreReady = content?.id === prdRef && content?.kind === "prd";
|
|
9964
|
-
} catch {
|
|
9965
|
-
localStoreReady = false;
|
|
9966
|
-
}
|
|
9967
|
-
}
|
|
9968
|
-
if (!localStoreReady) {
|
|
9969
|
-
return {
|
|
9970
|
-
ok: false,
|
|
9971
|
-
gate: "branch-state",
|
|
9972
|
-
reason: `Local PRD Run Store not ready for ${prdRef}. Expected valid prd.json at .pourkit/local-prd-runs/${prdRef}/prd.json with matching PRD ID. Ensure Local PRD Run Store is initialized before starting.`,
|
|
9973
|
-
diagnostics: [
|
|
9974
|
-
`Expected store path: .pourkit/local-prd-runs/${prdRef}/prd.json`,
|
|
9975
|
-
`Expected PRD ID: ${prdRef}`
|
|
9976
|
-
],
|
|
8130
|
+
reason: `Invalid PRD Run base branch: "${trimmed}" must not end with ".lock" suffix.`,
|
|
8131
|
+
diagnostics: [`Provided branch value: "${trimmed}"`],
|
|
9977
8132
|
offendingPaths: []
|
|
9978
8133
|
};
|
|
9979
8134
|
}
|
|
9980
|
-
|
|
8135
|
+
const source = options.baseBranchOverride !== void 0 ? "override" : "target";
|
|
8136
|
+
return { ok: true, baseBranch: trimmed, source };
|
|
9981
8137
|
}
|
|
9982
8138
|
function fetchOriginBranch(repoRoot2, baseBranch) {
|
|
9983
|
-
const fetchResult =
|
|
8139
|
+
const fetchResult = spawnSync2("git", ["fetch", "origin", baseBranch], {
|
|
9984
8140
|
cwd: repoRoot2,
|
|
9985
8141
|
encoding: "utf8"
|
|
9986
8142
|
});
|
|
@@ -9997,7 +8153,7 @@ function fetchOriginBranch(repoRoot2, baseBranch) {
|
|
|
9997
8153
|
offendingPaths: []
|
|
9998
8154
|
};
|
|
9999
8155
|
}
|
|
10000
|
-
const revParseResult =
|
|
8156
|
+
const revParseResult = spawnSync2(
|
|
10001
8157
|
"git",
|
|
10002
8158
|
["rev-parse", `origin/${baseBranch}`],
|
|
10003
8159
|
{
|
|
@@ -10021,7 +8177,7 @@ function fetchOriginBranch(repoRoot2, baseBranch) {
|
|
|
10021
8177
|
return { ok: true, startBaseCommit: revParseResult.stdout.trim() };
|
|
10022
8178
|
}
|
|
10023
8179
|
function inspectRemotePrdBranch(repoRoot2, prdRef) {
|
|
10024
|
-
const result =
|
|
8180
|
+
const result = spawnSync2("git", ["ls-remote", "--heads", "origin", prdRef], {
|
|
10025
8181
|
cwd: repoRoot2,
|
|
10026
8182
|
encoding: "utf8"
|
|
10027
8183
|
});
|
|
@@ -10045,7 +8201,7 @@ function inspectRemotePrdBranch(repoRoot2, prdRef) {
|
|
|
10045
8201
|
return { ok: true, exists: true, headSha };
|
|
10046
8202
|
}
|
|
10047
8203
|
function fetchPrdBranch(repoRoot2, prdRef) {
|
|
10048
|
-
const result =
|
|
8204
|
+
const result = spawnSync2("git", ["fetch", "origin", prdRef], {
|
|
10049
8205
|
cwd: repoRoot2,
|
|
10050
8206
|
encoding: "utf8"
|
|
10051
8207
|
});
|
|
@@ -10065,7 +8221,7 @@ function fetchPrdBranch(repoRoot2, prdRef) {
|
|
|
10065
8221
|
return { ok: true };
|
|
10066
8222
|
}
|
|
10067
8223
|
async function ensurePrdBranchPublished(repoRoot2, prdRef, startBaseCommit) {
|
|
10068
|
-
const pushResult =
|
|
8224
|
+
const pushResult = spawnSync2(
|
|
10069
8225
|
"git",
|
|
10070
8226
|
["push", "origin", `${startBaseCommit}:refs/heads/${prdRef}`],
|
|
10071
8227
|
{
|
|
@@ -10124,8 +8280,7 @@ function persistStartingPrdRunRecord(repoRoot2, prdRef, existingRecord, start, c
|
|
|
10124
8280
|
status: "starting",
|
|
10125
8281
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10126
8282
|
targetName: context.targetName,
|
|
10127
|
-
start
|
|
10128
|
-
mode: context.mode
|
|
8283
|
+
start
|
|
10129
8284
|
});
|
|
10130
8285
|
}
|
|
10131
8286
|
function blocked(repoRoot2, prdRef, gate, failureCode, reason, diagnostics, targetName) {
|
|
@@ -10155,28 +8310,6 @@ async function startPrdRun(options) {
|
|
|
10155
8310
|
);
|
|
10156
8311
|
}
|
|
10157
8312
|
const existingRecord = readPrdRun(options.repoRoot, prdRef);
|
|
10158
|
-
const resolvedMode = options.config ? (() => {
|
|
10159
|
-
try {
|
|
10160
|
-
const target = resolveTarget(options.config, targetName);
|
|
10161
|
-
return resolvePrdRunMode(target).mode;
|
|
10162
|
-
} catch {
|
|
10163
|
-
return void 0;
|
|
10164
|
-
}
|
|
10165
|
-
})() : void 0;
|
|
10166
|
-
if (resolvedMode && existingRecord.record?.mode && existingRecord.record.mode !== resolvedMode) {
|
|
10167
|
-
return blocked(
|
|
10168
|
-
options.repoRoot,
|
|
10169
|
-
prdRef,
|
|
10170
|
-
"branch-state",
|
|
10171
|
-
"mode_mismatch",
|
|
10172
|
-
`PRD Run ${prdRef} mode mismatch: recorded "${existingRecord.record.mode}" but resolved "${resolvedMode}". Resolve by using the correct target.`,
|
|
10173
|
-
[
|
|
10174
|
-
`Recorded mode: ${existingRecord.record.mode}`,
|
|
10175
|
-
`Resolved mode: ${resolvedMode}`
|
|
10176
|
-
],
|
|
10177
|
-
targetName
|
|
10178
|
-
);
|
|
10179
|
-
}
|
|
10180
8313
|
const resolvedTarget = options.config ? resolveTarget(options.config, targetName) : null;
|
|
10181
8314
|
const targetBaseBranch = resolvedTarget?.baseBranch ?? "main";
|
|
10182
8315
|
const baseResolution = resolvePrdRunBaseBranch({
|
|
@@ -10222,11 +8355,10 @@ async function startPrdRun(options) {
|
|
|
10222
8355
|
);
|
|
10223
8356
|
}
|
|
10224
8357
|
const currentStatus = existingRecord.record.status;
|
|
10225
|
-
if (currentStatus === "running" || currentStatus === "drained" || currentStatus === "completed_prd_branch" || currentStatus === "
|
|
8358
|
+
if (currentStatus === "running" || currentStatus === "drained" || currentStatus === "completed_prd_branch" || currentStatus === "complete") {
|
|
10226
8359
|
return {
|
|
10227
8360
|
kind: "started",
|
|
10228
8361
|
prdRef,
|
|
10229
|
-
mode: resolvedMode,
|
|
10230
8362
|
start: existingRecord.record.start,
|
|
10231
8363
|
diagnostics: [],
|
|
10232
8364
|
preservedStatus: currentStatus
|
|
@@ -10267,12 +8399,11 @@ async function startPrdRun(options) {
|
|
|
10267
8399
|
prdRef,
|
|
10268
8400
|
existingRecord,
|
|
10269
8401
|
reusedStart,
|
|
10270
|
-
{ targetName
|
|
8402
|
+
{ targetName }
|
|
10271
8403
|
);
|
|
10272
8404
|
return {
|
|
10273
8405
|
kind: "started",
|
|
10274
8406
|
prdRef,
|
|
10275
|
-
mode: resolvedMode,
|
|
10276
8407
|
start: reusedStart,
|
|
10277
8408
|
diagnostics: []
|
|
10278
8409
|
};
|
|
@@ -10303,7 +8434,7 @@ async function startPrdRun(options) {
|
|
|
10303
8434
|
fetchPrdResult.diagnostics
|
|
10304
8435
|
);
|
|
10305
8436
|
}
|
|
10306
|
-
const ancestryResult =
|
|
8437
|
+
const ancestryResult = spawnSync2(
|
|
10307
8438
|
"git",
|
|
10308
8439
|
[
|
|
10309
8440
|
"merge-base",
|
|
@@ -10340,12 +8471,11 @@ async function startPrdRun(options) {
|
|
|
10340
8471
|
prdRef,
|
|
10341
8472
|
existingRecord,
|
|
10342
8473
|
adoptedStart,
|
|
10343
|
-
{ targetName
|
|
8474
|
+
{ targetName }
|
|
10344
8475
|
);
|
|
10345
8476
|
return {
|
|
10346
8477
|
kind: "started",
|
|
10347
8478
|
prdRef,
|
|
10348
|
-
mode: resolvedMode,
|
|
10349
8479
|
start: adoptedStart,
|
|
10350
8480
|
diagnostics: []
|
|
10351
8481
|
};
|
|
@@ -10365,59 +8495,6 @@ async function startPrdRun(options) {
|
|
|
10365
8495
|
fetchResult.diagnostics
|
|
10366
8496
|
);
|
|
10367
8497
|
}
|
|
10368
|
-
if (resolvedMode === "local") {
|
|
10369
|
-
const localStoreResult = validateLocalStartStore(options.repoRoot, prdRef);
|
|
10370
|
-
if (!localStoreResult.ok) {
|
|
10371
|
-
return blocked(
|
|
10372
|
-
options.repoRoot,
|
|
10373
|
-
prdRef,
|
|
10374
|
-
localStoreResult.gate,
|
|
10375
|
-
"local_store_invalid",
|
|
10376
|
-
localStoreResult.reason,
|
|
10377
|
-
localStoreResult.diagnostics
|
|
10378
|
-
);
|
|
10379
|
-
}
|
|
10380
|
-
const localBranchName = getLocalPrdBranchName(prdRef);
|
|
10381
|
-
const localBranchResult = materializeLocalPrdBranch(
|
|
10382
|
-
prdRef,
|
|
10383
|
-
fetchResult.startBaseCommit,
|
|
10384
|
-
options.repoRoot
|
|
10385
|
-
);
|
|
10386
|
-
if (!localBranchResult.ok) {
|
|
10387
|
-
return blocked(
|
|
10388
|
-
options.repoRoot,
|
|
10389
|
-
prdRef,
|
|
10390
|
-
"branch-state",
|
|
10391
|
-
"local_branch_failed",
|
|
10392
|
-
localBranchResult.message,
|
|
10393
|
-
[localBranchResult.message]
|
|
10394
|
-
);
|
|
10395
|
-
}
|
|
10396
|
-
const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
10397
|
-
const start2 = buildStartReceipt({
|
|
10398
|
-
status: "started",
|
|
10399
|
-
targetName,
|
|
10400
|
-
prdBranch: localBranchName,
|
|
10401
|
-
startBaseBranch: baseResolution.baseBranch,
|
|
10402
|
-
startBaseCommit: fetchResult.startBaseCommit,
|
|
10403
|
-
branchAction: localBranchResult.created ? "created" : "reused",
|
|
10404
|
-
startedAt: updatedAt2
|
|
10405
|
-
});
|
|
10406
|
-
persistStartingPrdRunRecord(
|
|
10407
|
-
options.repoRoot,
|
|
10408
|
-
prdRef,
|
|
10409
|
-
existingRecord,
|
|
10410
|
-
start2,
|
|
10411
|
-
{ targetName, mode: resolvedMode }
|
|
10412
|
-
);
|
|
10413
|
-
return {
|
|
10414
|
-
kind: "started",
|
|
10415
|
-
prdRef,
|
|
10416
|
-
mode: resolvedMode,
|
|
10417
|
-
start: start2,
|
|
10418
|
-
diagnostics: []
|
|
10419
|
-
};
|
|
10420
|
-
}
|
|
10421
8498
|
const branchResult = inspectRemotePrdBranch(options.repoRoot, prdRef);
|
|
10422
8499
|
if (!branchResult.ok) {
|
|
10423
8500
|
return blocked(
|
|
@@ -10456,7 +8533,7 @@ async function startPrdRun(options) {
|
|
|
10456
8533
|
fetchPrdResult.diagnostics
|
|
10457
8534
|
);
|
|
10458
8535
|
}
|
|
10459
|
-
const ancestryResult =
|
|
8536
|
+
const ancestryResult = spawnSync2(
|
|
10460
8537
|
"git",
|
|
10461
8538
|
[
|
|
10462
8539
|
"merge-base",
|
|
@@ -10493,12 +8570,11 @@ async function startPrdRun(options) {
|
|
|
10493
8570
|
prdRef,
|
|
10494
8571
|
existingRecord,
|
|
10495
8572
|
adoptedStart,
|
|
10496
|
-
{ targetName
|
|
8573
|
+
{ targetName }
|
|
10497
8574
|
);
|
|
10498
8575
|
return {
|
|
10499
8576
|
kind: "started",
|
|
10500
8577
|
prdRef,
|
|
10501
|
-
mode: resolvedMode,
|
|
10502
8578
|
start: adoptedStart,
|
|
10503
8579
|
diagnostics: []
|
|
10504
8580
|
};
|
|
@@ -10532,13 +8608,11 @@ async function startPrdRun(options) {
|
|
|
10532
8608
|
startedAt: updatedAt
|
|
10533
8609
|
});
|
|
10534
8610
|
persistStartingPrdRunRecord(options.repoRoot, prdRef, existingRecord, start, {
|
|
10535
|
-
targetName
|
|
10536
|
-
mode: resolvedMode
|
|
8611
|
+
targetName
|
|
10537
8612
|
});
|
|
10538
8613
|
return {
|
|
10539
8614
|
kind: "started",
|
|
10540
8615
|
prdRef,
|
|
10541
|
-
mode: resolvedMode,
|
|
10542
8616
|
start,
|
|
10543
8617
|
diagnostics: []
|
|
10544
8618
|
};
|
|
@@ -10552,25 +8626,23 @@ function buildLaunchBlockedOutcome(prdRef, guidance, resume, start) {
|
|
|
10552
8626
|
resume
|
|
10553
8627
|
};
|
|
10554
8628
|
}
|
|
10555
|
-
function writeDrainedRecord(repoRoot2, prdRef, startReceipt, targetName
|
|
8629
|
+
function writeDrainedRecord(repoRoot2, prdRef, startReceipt, targetName) {
|
|
10556
8630
|
writePrdRunRecord(repoRoot2, {
|
|
10557
8631
|
prdRef,
|
|
10558
8632
|
status: "drained",
|
|
10559
8633
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10560
8634
|
targetName,
|
|
10561
|
-
start: startReceipt
|
|
10562
|
-
mode
|
|
8635
|
+
start: startReceipt
|
|
10563
8636
|
});
|
|
10564
8637
|
}
|
|
10565
|
-
function writeTerminalRecord(repoRoot2, prdRef, status, targetName, startReceipt, prdBranch
|
|
8638
|
+
function writeTerminalRecord(repoRoot2, prdRef, status, targetName, startReceipt, prdBranch) {
|
|
10566
8639
|
writePrdRunRecord(repoRoot2, {
|
|
10567
8640
|
prdRef,
|
|
10568
8641
|
status,
|
|
10569
8642
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10570
8643
|
targetName,
|
|
10571
8644
|
prdBranch,
|
|
10572
|
-
start: startReceipt
|
|
10573
|
-
mode
|
|
8645
|
+
start: startReceipt
|
|
10574
8646
|
});
|
|
10575
8647
|
}
|
|
10576
8648
|
function validatePrdRunStartEvidence(record, context) {
|
|
@@ -10629,22 +8701,6 @@ function validatePrdRunStartEvidence(record, context) {
|
|
|
10629
8701
|
}
|
|
10630
8702
|
};
|
|
10631
8703
|
}
|
|
10632
|
-
if (record.mode && record.mode !== context.mode) {
|
|
10633
|
-
return {
|
|
10634
|
-
ok: false,
|
|
10635
|
-
guidance: {
|
|
10636
|
-
blockedGate: "branch-state",
|
|
10637
|
-
failureCode: "mode_mismatch",
|
|
10638
|
-
reason: `PRD Run ${context.prdRef} mode mismatch: recorded "${record.mode}" but expected "${context.mode}".`,
|
|
10639
|
-
repairability: "operator-action",
|
|
10640
|
-
diagnostics: [
|
|
10641
|
-
`Recorded mode: ${record.mode}`,
|
|
10642
|
-
`Expected mode: ${context.mode}`
|
|
10643
|
-
],
|
|
10644
|
-
offendingPaths: []
|
|
10645
|
-
}
|
|
10646
|
-
};
|
|
10647
|
-
}
|
|
10648
8704
|
if (record.prdRef !== context.prdRef) {
|
|
10649
8705
|
return {
|
|
10650
8706
|
ok: false,
|
|
@@ -10764,31 +8820,6 @@ function validatePrdRunDrainEvidence(record, context) {
|
|
|
10764
8820
|
async function launchPrdRun(options) {
|
|
10765
8821
|
const prdRef = normalizePrdRunRef(options.prdRef);
|
|
10766
8822
|
const existingRecord = readPrdRun(options.repoRoot, prdRef);
|
|
10767
|
-
const resolvedLaunchMode = options.config ? (() => {
|
|
10768
|
-
try {
|
|
10769
|
-
const target = resolveTarget(options.config, options.targetName);
|
|
10770
|
-
return resolvePrdRunMode(target).mode;
|
|
10771
|
-
} catch {
|
|
10772
|
-
return void 0;
|
|
10773
|
-
}
|
|
10774
|
-
})() : void 0;
|
|
10775
|
-
if (resolvedLaunchMode && existingRecord.record?.mode && existingRecord.record.mode !== resolvedLaunchMode) {
|
|
10776
|
-
return buildLaunchBlockedOutcome(
|
|
10777
|
-
prdRef,
|
|
10778
|
-
{
|
|
10779
|
-
blockedGate: "branch-state",
|
|
10780
|
-
failureCode: "mode_mismatch",
|
|
10781
|
-
reason: `PRD Run ${prdRef} mode mismatch: recorded "${existingRecord.record.mode}" but resolved "${resolvedLaunchMode}". Resolve by using the correct target.`,
|
|
10782
|
-
repairability: "operator-action",
|
|
10783
|
-
diagnostics: [
|
|
10784
|
-
`Recorded mode: ${existingRecord.record.mode}`,
|
|
10785
|
-
`Resolved mode: ${resolvedLaunchMode}`
|
|
10786
|
-
],
|
|
10787
|
-
offendingPaths: []
|
|
10788
|
-
},
|
|
10789
|
-
{ attempted: [], skipped: ["start", "queue"], resumed: [] }
|
|
10790
|
-
);
|
|
10791
|
-
}
|
|
10792
8823
|
const plan = planPrdRunLaunchResume(existingRecord.record);
|
|
10793
8824
|
if (plan.blocked === "final_reviewed-incompatible") {
|
|
10794
8825
|
return buildLaunchBlockedOutcome(
|
|
@@ -10806,7 +8837,7 @@ async function launchPrdRun(options) {
|
|
|
10806
8837
|
plan
|
|
10807
8838
|
);
|
|
10808
8839
|
}
|
|
10809
|
-
if (existingRecord.record?.status === "completed_prd_branch" || existingRecord.record?.status === "complete"
|
|
8840
|
+
if (existingRecord.record?.status === "completed_prd_branch" || existingRecord.record?.status === "complete") {
|
|
10810
8841
|
return {
|
|
10811
8842
|
kind: "already-terminal",
|
|
10812
8843
|
prdRef,
|
|
@@ -10834,7 +8865,6 @@ async function launchPrdRun(options) {
|
|
|
10834
8865
|
plan
|
|
10835
8866
|
);
|
|
10836
8867
|
}
|
|
10837
|
-
const drainMode = resolvedLaunchMode ?? record.mode ?? "github";
|
|
10838
8868
|
const resolvedTarget = options.config ? resolveTarget(options.config, options.targetName) : null;
|
|
10839
8869
|
const targetBaseBranch = resolvedTarget?.baseBranch ?? "main";
|
|
10840
8870
|
const baseResolution = resolvePrdRunBaseBranch({
|
|
@@ -10857,7 +8887,6 @@ async function launchPrdRun(options) {
|
|
|
10857
8887
|
}
|
|
10858
8888
|
const drainContext = {
|
|
10859
8889
|
prdRef,
|
|
10860
|
-
mode: drainMode,
|
|
10861
8890
|
baseBranch: baseResolution.baseBranch,
|
|
10862
8891
|
prdBranch: record.start.prdBranch
|
|
10863
8892
|
};
|
|
@@ -10869,20 +8898,18 @@ async function launchPrdRun(options) {
|
|
|
10869
8898
|
if (!drainEvidence.ok) {
|
|
10870
8899
|
return buildLaunchBlockedOutcome(prdRef, drainEvidence.guidance, plan);
|
|
10871
8900
|
}
|
|
10872
|
-
const terminalStatus = drainMode === "local" ? "completed_local_branch" : "completed_prd_branch";
|
|
10873
8901
|
writeTerminalRecord(
|
|
10874
8902
|
options.repoRoot,
|
|
10875
8903
|
prdRef,
|
|
10876
|
-
|
|
8904
|
+
"completed_prd_branch",
|
|
10877
8905
|
record.targetName ?? options.targetName,
|
|
10878
8906
|
record.start,
|
|
10879
|
-
record.start.prdBranch
|
|
10880
|
-
drainMode
|
|
8907
|
+
record.start.prdBranch
|
|
10881
8908
|
);
|
|
10882
8909
|
return {
|
|
10883
8910
|
kind: "completed",
|
|
10884
8911
|
prdRef,
|
|
10885
|
-
status:
|
|
8912
|
+
status: "completed_prd_branch",
|
|
10886
8913
|
prdBranch: record.start.prdBranch,
|
|
10887
8914
|
diagnostics: [
|
|
10888
8915
|
`PRD Run ${prdRef} completed from drained state. Branch: ${record.start.prdBranch}.`
|
|
@@ -10933,7 +8960,6 @@ async function launchPrdRun(options) {
|
|
|
10933
8960
|
const currentRecord = readPrdRun(options.repoRoot, prdRef).record;
|
|
10934
8961
|
const startReceipt = currentRecord?.start;
|
|
10935
8962
|
const targetName = currentRecord?.targetName ?? options.targetName;
|
|
10936
|
-
const explicitMode = resolvedLaunchMode ?? currentRecord?.mode;
|
|
10937
8963
|
if (startReceipt) {
|
|
10938
8964
|
startReceipt.queueStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10939
8965
|
startReceipt.queueCommand = "queue-run";
|
|
@@ -10943,236 +8969,18 @@ async function launchPrdRun(options) {
|
|
|
10943
8969
|
status: "running",
|
|
10944
8970
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10945
8971
|
targetName,
|
|
10946
|
-
start: startReceipt
|
|
10947
|
-
mode: explicitMode
|
|
8972
|
+
start: startReceipt
|
|
10948
8973
|
});
|
|
10949
|
-
if (explicitMode === "local") {
|
|
10950
|
-
return await launchLocalQueueDrain(
|
|
10951
|
-
options,
|
|
10952
|
-
prdRef,
|
|
10953
|
-
startReceipt,
|
|
10954
|
-
targetName,
|
|
10955
|
-
explicitMode,
|
|
10956
|
-
plan,
|
|
10957
|
-
startOutcome
|
|
10958
|
-
);
|
|
10959
|
-
}
|
|
10960
8974
|
return await launchGithubQueueDrain(
|
|
10961
8975
|
options,
|
|
10962
8976
|
prdRef,
|
|
10963
8977
|
startReceipt,
|
|
10964
8978
|
targetName,
|
|
10965
|
-
explicitMode,
|
|
10966
8979
|
plan,
|
|
10967
8980
|
startOutcome
|
|
10968
8981
|
);
|
|
10969
8982
|
}
|
|
10970
|
-
async function
|
|
10971
|
-
if (!startReceipt) {
|
|
10972
|
-
writePrdRunRecord(options.repoRoot, {
|
|
10973
|
-
prdRef,
|
|
10974
|
-
status: "blocked",
|
|
10975
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10976
|
-
targetName,
|
|
10977
|
-
blockedGate: "branch-state",
|
|
10978
|
-
blockedReason: `PRD Run ${prdRef} is missing start receipt before local queue drain.`,
|
|
10979
|
-
diagnostics: [],
|
|
10980
|
-
offendingPaths: []
|
|
10981
|
-
});
|
|
10982
|
-
return buildLaunchBlockedOutcome(
|
|
10983
|
-
prdRef,
|
|
10984
|
-
{
|
|
10985
|
-
blockedGate: "branch-state",
|
|
10986
|
-
failureCode: "missing_start_receipt",
|
|
10987
|
-
reason: `PRD Run ${prdRef} is missing start receipt before local queue drain.`,
|
|
10988
|
-
repairability: "automatic-retry-safe",
|
|
10989
|
-
diagnostics: [],
|
|
10990
|
-
offendingPaths: []
|
|
10991
|
-
},
|
|
10992
|
-
plan,
|
|
10993
|
-
startOutcome
|
|
10994
|
-
);
|
|
10995
|
-
}
|
|
10996
|
-
const localStoreDir = join20(
|
|
10997
|
-
options.repoRoot,
|
|
10998
|
-
".pourkit",
|
|
10999
|
-
"local-prd-runs",
|
|
11000
|
-
prdRef
|
|
11001
|
-
);
|
|
11002
|
-
let localStoreReady = false;
|
|
11003
|
-
if (existsSync16(localStoreDir)) {
|
|
11004
|
-
const localStorePath = join20(localStoreDir, "prd.json");
|
|
11005
|
-
try {
|
|
11006
|
-
const content = JSON.parse(readFileSync18(localStorePath, "utf8"));
|
|
11007
|
-
localStoreReady = content?.id === prdRef && content?.kind === "prd";
|
|
11008
|
-
} catch {
|
|
11009
|
-
localStoreReady = false;
|
|
11010
|
-
}
|
|
11011
|
-
}
|
|
11012
|
-
if (!localStoreReady) {
|
|
11013
|
-
const reason = `Local PRD Run Store not ready for ${prdRef}. Expected valid prd.json at .pourkit/local-prd-runs/${prdRef}/prd.json with matching PRD ID.`;
|
|
11014
|
-
writePrdRunRecord(options.repoRoot, {
|
|
11015
|
-
prdRef,
|
|
11016
|
-
status: "blocked",
|
|
11017
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11018
|
-
targetName,
|
|
11019
|
-
start: startReceipt,
|
|
11020
|
-
mode,
|
|
11021
|
-
blockedGate: "branch-state",
|
|
11022
|
-
blockedReason: reason,
|
|
11023
|
-
diagnostics: [
|
|
11024
|
-
`Expected store path: .pourkit/local-prd-runs/${prdRef}/prd.json`,
|
|
11025
|
-
`Expected PRD ID: ${prdRef}`
|
|
11026
|
-
],
|
|
11027
|
-
offendingPaths: []
|
|
11028
|
-
});
|
|
11029
|
-
return buildLaunchBlockedOutcome(
|
|
11030
|
-
prdRef,
|
|
11031
|
-
{
|
|
11032
|
-
blockedGate: "branch-state",
|
|
11033
|
-
failureCode: "local_store_invalid",
|
|
11034
|
-
reason,
|
|
11035
|
-
repairability: "operator-action",
|
|
11036
|
-
diagnostics: [
|
|
11037
|
-
`Expected store path: .pourkit/local-prd-runs/${prdRef}/prd.json`,
|
|
11038
|
-
`Expected PRD ID: ${prdRef}`
|
|
11039
|
-
],
|
|
11040
|
-
offendingPaths: []
|
|
11041
|
-
},
|
|
11042
|
-
plan,
|
|
11043
|
-
startOutcome
|
|
11044
|
-
);
|
|
11045
|
-
}
|
|
11046
|
-
const memoryExecutionContext = options.config?.memory?.enabled ? { available: true, sandboxDbPath: "/home/agent/.pourkit/icm/memories.db" } : void 0;
|
|
11047
|
-
const queueResult = await runLocalQueueLoop(
|
|
11048
|
-
prdRef,
|
|
11049
|
-
options.repoRoot,
|
|
11050
|
-
options.issueProvider,
|
|
11051
|
-
memoryExecutionContext
|
|
11052
|
-
);
|
|
11053
|
-
if (!queueResult.ok) {
|
|
11054
|
-
const failureCode = queueResult.failureCode ?? "queue_error";
|
|
11055
|
-
const reason = queueResult.repairGuidance ?? `Local queue loop blocked: ${failureCode}`;
|
|
11056
|
-
const diagnostics = [
|
|
11057
|
-
`Local queue loop failed with failureCode "${failureCode}".`,
|
|
11058
|
-
...queueResult.repairGuidance ? [`Repair guidance: ${queueResult.repairGuidance}`] : []
|
|
11059
|
-
];
|
|
11060
|
-
writePrdRunRecord(options.repoRoot, {
|
|
11061
|
-
prdRef,
|
|
11062
|
-
status: "blocked",
|
|
11063
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11064
|
-
targetName,
|
|
11065
|
-
start: startReceipt,
|
|
11066
|
-
mode,
|
|
11067
|
-
blockedGate: "queue",
|
|
11068
|
-
blockedReason: reason,
|
|
11069
|
-
diagnostics,
|
|
11070
|
-
offendingPaths: []
|
|
11071
|
-
});
|
|
11072
|
-
return buildLaunchBlockedOutcome(
|
|
11073
|
-
prdRef,
|
|
11074
|
-
{
|
|
11075
|
-
blockedGate: "queue",
|
|
11076
|
-
failureCode,
|
|
11077
|
-
reason,
|
|
11078
|
-
repairability: "automatic-retry-safe",
|
|
11079
|
-
diagnostics,
|
|
11080
|
-
offendingPaths: []
|
|
11081
|
-
},
|
|
11082
|
-
plan,
|
|
11083
|
-
startOutcome
|
|
11084
|
-
);
|
|
11085
|
-
}
|
|
11086
|
-
if (queueResult.blockedIssues.length > 0) {
|
|
11087
|
-
const reason = `Queue processed complete but ${queueResult.blockedIssues.length} blocked child issue(s) remain.`;
|
|
11088
|
-
const diagnostics = [
|
|
11089
|
-
reason,
|
|
11090
|
-
`Blocked issues: ${queueResult.blockedIssues.join(", ")}`,
|
|
11091
|
-
"Resolve blocked children before parent close."
|
|
11092
|
-
];
|
|
11093
|
-
writePrdRunRecord(options.repoRoot, {
|
|
11094
|
-
prdRef,
|
|
11095
|
-
status: "blocked",
|
|
11096
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11097
|
-
targetName,
|
|
11098
|
-
start: startReceipt,
|
|
11099
|
-
mode,
|
|
11100
|
-
blockedGate: "queue",
|
|
11101
|
-
blockedReason: reason,
|
|
11102
|
-
diagnostics,
|
|
11103
|
-
offendingPaths: []
|
|
11104
|
-
});
|
|
11105
|
-
return buildLaunchBlockedOutcome(
|
|
11106
|
-
prdRef,
|
|
11107
|
-
{
|
|
11108
|
-
blockedGate: "queue",
|
|
11109
|
-
failureCode: "blocked_child_issues",
|
|
11110
|
-
reason,
|
|
11111
|
-
repairability: "operator-action",
|
|
11112
|
-
diagnostics,
|
|
11113
|
-
offendingPaths: []
|
|
11114
|
-
},
|
|
11115
|
-
plan,
|
|
11116
|
-
startOutcome
|
|
11117
|
-
);
|
|
11118
|
-
}
|
|
11119
|
-
startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11120
|
-
startReceipt.queueProcessedCount = queueResult.completedIssues.length + queueResult.blockedIssues.length;
|
|
11121
|
-
const prdBranch = startReceipt.prdBranch ?? `local/${prdRef}`;
|
|
11122
|
-
writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName, mode);
|
|
11123
|
-
const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
|
|
11124
|
-
if (drainRecord) {
|
|
11125
|
-
const drainContext = {
|
|
11126
|
-
prdRef,
|
|
11127
|
-
mode,
|
|
11128
|
-
baseBranch: startReceipt.startBaseBranch,
|
|
11129
|
-
prdBranch
|
|
11130
|
-
};
|
|
11131
|
-
const startEvidence = validatePrdRunStartEvidence(
|
|
11132
|
-
drainRecord,
|
|
11133
|
-
drainContext
|
|
11134
|
-
);
|
|
11135
|
-
if (!startEvidence.ok) {
|
|
11136
|
-
return buildLaunchBlockedOutcome(
|
|
11137
|
-
prdRef,
|
|
11138
|
-
startEvidence.guidance,
|
|
11139
|
-
plan,
|
|
11140
|
-
startOutcome
|
|
11141
|
-
);
|
|
11142
|
-
}
|
|
11143
|
-
const drainEvidence = validatePrdRunDrainEvidence(
|
|
11144
|
-
drainRecord,
|
|
11145
|
-
drainContext
|
|
11146
|
-
);
|
|
11147
|
-
if (!drainEvidence.ok) {
|
|
11148
|
-
return buildLaunchBlockedOutcome(
|
|
11149
|
-
prdRef,
|
|
11150
|
-
drainEvidence.guidance,
|
|
11151
|
-
plan,
|
|
11152
|
-
startOutcome
|
|
11153
|
-
);
|
|
11154
|
-
}
|
|
11155
|
-
}
|
|
11156
|
-
writeTerminalRecord(
|
|
11157
|
-
options.repoRoot,
|
|
11158
|
-
prdRef,
|
|
11159
|
-
"completed_local_branch",
|
|
11160
|
-
targetName,
|
|
11161
|
-
startReceipt,
|
|
11162
|
-
prdBranch,
|
|
11163
|
-
mode
|
|
11164
|
-
);
|
|
11165
|
-
return {
|
|
11166
|
-
kind: "completed",
|
|
11167
|
-
prdRef,
|
|
11168
|
-
status: "completed_local_branch",
|
|
11169
|
-
prdBranch,
|
|
11170
|
-
diagnostics: [`Local PRD Run ${prdRef} completed. Branch: ${prdBranch}.`],
|
|
11171
|
-
start: startOutcome,
|
|
11172
|
-
resume: plan
|
|
11173
|
-
};
|
|
11174
|
-
}
|
|
11175
|
-
async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName, mode, plan, startOutcome) {
|
|
8983
|
+
async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName, plan, startOutcome) {
|
|
11176
8984
|
if (!options.issueProvider || !options.prProvider || !options.executionProvider || !options.logger) {
|
|
11177
8985
|
const reason = `PRD Run ${prdRef} requires issueProvider, prProvider, executionProvider, and logger for GitHub-backed queue drain.`;
|
|
11178
8986
|
writePrdRunRecord(options.repoRoot, {
|
|
@@ -11181,7 +8989,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11181
8989
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11182
8990
|
targetName,
|
|
11183
8991
|
start: startReceipt,
|
|
11184
|
-
mode,
|
|
11185
8992
|
blockedGate: "queue",
|
|
11186
8993
|
blockedReason: reason,
|
|
11187
8994
|
diagnostics: [
|
|
@@ -11212,7 +9019,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11212
9019
|
status: "blocked",
|
|
11213
9020
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11214
9021
|
targetName,
|
|
11215
|
-
mode,
|
|
11216
9022
|
blockedGate: "branch-state",
|
|
11217
9023
|
blockedReason: `PRD Run ${prdRef} is missing start receipt before GitHub queue drain.`,
|
|
11218
9024
|
diagnostics: [],
|
|
@@ -11240,7 +9046,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11240
9046
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11241
9047
|
targetName,
|
|
11242
9048
|
start: startReceipt,
|
|
11243
|
-
mode,
|
|
11244
9049
|
blockedGate: "queue",
|
|
11245
9050
|
blockedReason: reason,
|
|
11246
9051
|
diagnostics: ["Missing config for GitHub-backed mode."],
|
|
@@ -11264,13 +9069,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11264
9069
|
const prProvider = options.prProvider;
|
|
11265
9070
|
const executionProvider = options.executionProvider;
|
|
11266
9071
|
const logger = options.logger;
|
|
11267
|
-
let resolvedModeForQueue;
|
|
11268
|
-
try {
|
|
11269
|
-
const target = resolveTarget(options.config, options.targetName);
|
|
11270
|
-
resolvedModeForQueue = resolvePrdRunMode(target);
|
|
11271
|
-
} catch {
|
|
11272
|
-
resolvedModeForQueue = void 0;
|
|
11273
|
-
}
|
|
11274
9072
|
try {
|
|
11275
9073
|
const outcome = await runQueueCommand({
|
|
11276
9074
|
targetName,
|
|
@@ -11285,8 +9083,7 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11285
9083
|
prdRef,
|
|
11286
9084
|
queueRunContext: {
|
|
11287
9085
|
prdRef,
|
|
11288
|
-
prdBranch: startReceipt.prdBranch
|
|
11289
|
-
prdRunMode: resolvedModeForQueue
|
|
9086
|
+
prdBranch: startReceipt.prdBranch
|
|
11290
9087
|
}
|
|
11291
9088
|
});
|
|
11292
9089
|
if (outcome.selected === null && outcome.code === "drained") {
|
|
@@ -11300,7 +9097,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11300
9097
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11301
9098
|
targetName,
|
|
11302
9099
|
start: startReceipt,
|
|
11303
|
-
mode,
|
|
11304
9100
|
blockedGate: "queue",
|
|
11305
9101
|
blockedReason: diagnostics[0],
|
|
11306
9102
|
diagnostics,
|
|
@@ -11323,19 +9119,11 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11323
9119
|
startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11324
9120
|
startReceipt.queueProcessedCount = outcome.processedCount;
|
|
11325
9121
|
const prdBranch = startReceipt.prdBranch ?? prdRef;
|
|
11326
|
-
writeDrainedRecord(
|
|
11327
|
-
options.repoRoot,
|
|
11328
|
-
prdRef,
|
|
11329
|
-
startReceipt,
|
|
11330
|
-
targetName,
|
|
11331
|
-
mode
|
|
11332
|
-
);
|
|
9122
|
+
writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
|
|
11333
9123
|
const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
|
|
11334
9124
|
if (drainRecord) {
|
|
11335
|
-
const drainMode = mode ?? "github";
|
|
11336
9125
|
const drainContext = {
|
|
11337
9126
|
prdRef,
|
|
11338
|
-
mode: drainMode,
|
|
11339
9127
|
baseBranch: startReceipt.startBaseBranch,
|
|
11340
9128
|
prdBranch
|
|
11341
9129
|
};
|
|
@@ -11370,8 +9158,7 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11370
9158
|
"completed_prd_branch",
|
|
11371
9159
|
targetName,
|
|
11372
9160
|
startReceipt,
|
|
11373
|
-
prdBranch
|
|
11374
|
-
mode
|
|
9161
|
+
prdBranch
|
|
11375
9162
|
);
|
|
11376
9163
|
return {
|
|
11377
9164
|
kind: "completed",
|
|
@@ -11393,7 +9180,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11393
9180
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11394
9181
|
targetName,
|
|
11395
9182
|
start: startReceipt,
|
|
11396
|
-
mode,
|
|
11397
9183
|
blockedGate: "queue",
|
|
11398
9184
|
blockedReason: outcome.reason,
|
|
11399
9185
|
diagnostics,
|
|
@@ -11421,7 +9207,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11421
9207
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11422
9208
|
targetName,
|
|
11423
9209
|
start: startReceipt,
|
|
11424
|
-
mode,
|
|
11425
9210
|
blockedGate: "queue",
|
|
11426
9211
|
blockedReason: outcomeReason,
|
|
11427
9212
|
diagnostics: outcomeDiagnostics,
|
|
@@ -11449,7 +9234,6 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11449
9234
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11450
9235
|
targetName,
|
|
11451
9236
|
start: startReceipt,
|
|
11452
|
-
mode,
|
|
11453
9237
|
blockedGate: "queue",
|
|
11454
9238
|
blockedReason: msg,
|
|
11455
9239
|
diagnostics,
|
|
@@ -11472,13 +9256,13 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
|
|
|
11472
9256
|
}
|
|
11473
9257
|
|
|
11474
9258
|
// prd-run/final-review-validation.ts
|
|
11475
|
-
import { existsSync as
|
|
9259
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
|
|
11476
9260
|
|
|
11477
9261
|
// commands/prd-run.ts
|
|
11478
9262
|
async function runPrdRunLaunchCommand(options) {
|
|
11479
9263
|
const outcome = await launchPrdRun(options);
|
|
11480
9264
|
const result = mapLaunchPrdRunOutcomeToCommandResult(outcome, options);
|
|
11481
|
-
if (options.config && options.logger &&
|
|
9265
|
+
if (options.config && options.logger && result.status === "completed_prd_branch") {
|
|
11482
9266
|
try {
|
|
11483
9267
|
const target = resolveTarget(options.config, options.targetName);
|
|
11484
9268
|
if (shouldCleanupRepositoryAfterPrdDrain(target)) {
|
|
@@ -11505,10 +9289,7 @@ function mapLaunchPrdRunOutcomeToCommandResult(outcome, options) {
|
|
|
11505
9289
|
if (outcome.kind === "already-terminal") {
|
|
11506
9290
|
const diagnostics = [
|
|
11507
9291
|
`PRD Run ${prdRef} is already in status "${outcome.status}".`,
|
|
11508
|
-
...outcome.status === "completed_prd_branch" ? [buildCompletedPrdBranchHint(outcome.prdBranch ?? prdRef)] : []
|
|
11509
|
-
...outcome.status === "completed_local_branch" ? [
|
|
11510
|
-
`Local PRD Run ${prdRef} completed. Branch: ${outcome.prdBranch ?? `local/${prdRef}`}.`
|
|
11511
|
-
] : []
|
|
9292
|
+
...outcome.status === "completed_prd_branch" ? [buildCompletedPrdBranchHint(outcome.prdBranch ?? prdRef)] : []
|
|
11512
9293
|
];
|
|
11513
9294
|
return {
|
|
11514
9295
|
prdRef,
|
|
@@ -11985,16 +9766,16 @@ async function runPrMergeCommand(args, logger, prProvider, config) {
|
|
|
11985
9766
|
}
|
|
11986
9767
|
|
|
11987
9768
|
// commands/init.ts
|
|
11988
|
-
import { existsSync as
|
|
9769
|
+
import { existsSync as existsSync15, statSync } from "fs";
|
|
11989
9770
|
import {
|
|
11990
9771
|
copyFile,
|
|
11991
|
-
mkdir as
|
|
11992
|
-
readFile as
|
|
9772
|
+
mkdir as mkdir4,
|
|
9773
|
+
readFile as readFile4,
|
|
11993
9774
|
readdir,
|
|
11994
9775
|
rename,
|
|
11995
|
-
writeFile
|
|
9776
|
+
writeFile
|
|
11996
9777
|
} from "fs/promises";
|
|
11997
|
-
import { createHash
|
|
9778
|
+
import { createHash, randomUUID } from "crypto";
|
|
11998
9779
|
import path5 from "path";
|
|
11999
9780
|
import { execFile as execFile2 } from "child_process";
|
|
12000
9781
|
import { promisify as promisify2 } from "util";
|
|
@@ -12251,7 +10032,40 @@ Database path: use \`$POURKIT_ICM_DB\` when set, otherwise use \`.pourkit/icm/me
|
|
|
12251
10032
|
|
|
12252
10033
|
CLI fallback: when MCP memory tools are unavailable, recall with \`icm --db "$POURKIT_ICM_DB" recall "<query>"\` or \`icm --db .pourkit/icm/memories.db recall "<query>"\`, and store durable observations with \`icm --db "$POURKIT_ICM_DB" store -c "<concise reusable observation>"\` or \`icm --db .pourkit/icm/memories.db store -c "<concise reusable observation>"\`.
|
|
12253
10034
|
|
|
12254
|
-
Memory is advisory only. Never treat memory as a replacement for reading current repository files, Run Context, artifacts, or verification output. When memory conflicts with current source or canonical state, current source and canonical state win
|
|
10035
|
+
Memory is advisory only. Never treat memory as a replacement for reading current repository files, Run Context, artifacts, or verification output. When memory conflicts with current source or canonical state, current source and canonical state win.
|
|
10036
|
+
|
|
10037
|
+
<!-- icm:start -->
|
|
10038
|
+
## Persistent memory (ICM) \u2014 MANDATORY
|
|
10039
|
+
|
|
10040
|
+
This project uses [ICM](https://github.com/rtk-ai/icm) for persistent memory across sessions.
|
|
10041
|
+
You MUST use it actively. Not optional.
|
|
10042
|
+
|
|
10043
|
+
### Recall (before starting work)
|
|
10044
|
+
\`\`\`bash
|
|
10045
|
+
icm recall "query" # search memories
|
|
10046
|
+
icm recall "query" -t "topic-name" # filter by topic
|
|
10047
|
+
icm recall-context "query" --limit 5 # formatted for prompt injection
|
|
10048
|
+
\`\`\`
|
|
10049
|
+
|
|
10050
|
+
### Store \u2014 MANDATORY triggers
|
|
10051
|
+
You MUST call \`icm store\` when ANY of the following happens:
|
|
10052
|
+
1. **Error resolved** \u2192 \`icm store -t errors-resolved -c "description" -i high -k "keyword1,keyword2"\`
|
|
10053
|
+
2. **Architecture/design decision** \u2192 \`icm store -t decisions-{project} -c "description" -i high\`
|
|
10054
|
+
3. **User preference discovered** \u2192 \`icm store -t preferences -c "description" -i critical\`
|
|
10055
|
+
4. **Significant task completed** \u2192 \`icm store -t context-{project} -c "summary of work done" -i high\`
|
|
10056
|
+
5. **Conversation exceeds ~20 tool calls without a store** \u2192 store a progress summary
|
|
10057
|
+
|
|
10058
|
+
Do this BEFORE responding to the user. Not after. Not later. Immediately.
|
|
10059
|
+
|
|
10060
|
+
Do NOT store: trivial details, info already in CLAUDE.md, ephemeral state (build logs, git status).
|
|
10061
|
+
|
|
10062
|
+
### Other commands
|
|
10063
|
+
\`\`\`bash
|
|
10064
|
+
icm update <id> -c "updated content" # edit memory in-place
|
|
10065
|
+
icm health # topic hygiene audit
|
|
10066
|
+
icm topics # list all topics
|
|
10067
|
+
\`\`\`
|
|
10068
|
+
<!-- icm:end -->`;
|
|
12255
10069
|
}
|
|
12256
10070
|
|
|
12257
10071
|
// commands/init.ts
|
|
@@ -12268,7 +10082,7 @@ function resolveSourceAssetPath(sourceRoot, ...segments) {
|
|
|
12268
10082
|
path5.join(sourceRoot, "dist", ...segments)
|
|
12269
10083
|
];
|
|
12270
10084
|
for (const candidatePath of candidatePaths) {
|
|
12271
|
-
if (
|
|
10085
|
+
if (existsSync15(candidatePath)) return candidatePath;
|
|
12272
10086
|
}
|
|
12273
10087
|
return path5.join(sourceRoot, "pourkit", ...segments);
|
|
12274
10088
|
}
|
|
@@ -12485,7 +10299,7 @@ function generateConfigTemplate(options) {
|
|
|
12485
10299
|
}
|
|
12486
10300
|
const config = {
|
|
12487
10301
|
$schema: "./schema/pourkit.schema.json",
|
|
12488
|
-
schemaVersion:
|
|
10302
|
+
schemaVersion: 2,
|
|
12489
10303
|
targets: [target],
|
|
12490
10304
|
workflowPack: {
|
|
12491
10305
|
version: "1.0.0",
|
|
@@ -12674,11 +10488,11 @@ async function walkDir(dir) {
|
|
|
12674
10488
|
return files;
|
|
12675
10489
|
}
|
|
12676
10490
|
async function computeFileChecksum(filePath) {
|
|
12677
|
-
const content = await
|
|
12678
|
-
return
|
|
10491
|
+
const content = await readFile4(filePath);
|
|
10492
|
+
return createHash("sha256").update(content).digest("hex");
|
|
12679
10493
|
}
|
|
12680
10494
|
function lockfileExists(root, name) {
|
|
12681
|
-
return
|
|
10495
|
+
return existsSync15(path5.join(root, name));
|
|
12682
10496
|
}
|
|
12683
10497
|
function detectPackageManager(root) {
|
|
12684
10498
|
if (lockfileExists(root, "pnpm-lock.yaml")) return "pnpm";
|
|
@@ -12722,7 +10536,7 @@ async function discoverLocalSource(sourcePath) {
|
|
|
12722
10536
|
async function discoverReadme(root) {
|
|
12723
10537
|
for (const name of ["README.md", "readme.md"]) {
|
|
12724
10538
|
const p = path5.join(root, name);
|
|
12725
|
-
if (
|
|
10539
|
+
if (existsSync15(p)) {
|
|
12726
10540
|
return p;
|
|
12727
10541
|
}
|
|
12728
10542
|
}
|
|
@@ -12732,7 +10546,7 @@ async function discoverAgentFiles(root) {
|
|
|
12732
10546
|
const files = [];
|
|
12733
10547
|
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
12734
10548
|
const p = path5.join(root, name);
|
|
12735
|
-
if (
|
|
10549
|
+
if (existsSync15(p)) {
|
|
12736
10550
|
files.push(p);
|
|
12737
10551
|
}
|
|
12738
10552
|
}
|
|
@@ -12740,7 +10554,7 @@ async function discoverAgentFiles(root) {
|
|
|
12740
10554
|
}
|
|
12741
10555
|
async function discoverMerlleState(root) {
|
|
12742
10556
|
const p = path5.join(root, ".pourkit", "state.json");
|
|
12743
|
-
return
|
|
10557
|
+
return existsSync15(p) ? p : null;
|
|
12744
10558
|
}
|
|
12745
10559
|
async function discoverAgentSkills(root) {
|
|
12746
10560
|
const dirs = [
|
|
@@ -12749,7 +10563,7 @@ async function discoverAgentSkills(root) {
|
|
|
12749
10563
|
];
|
|
12750
10564
|
const found = [];
|
|
12751
10565
|
for (const d of dirs) {
|
|
12752
|
-
if (
|
|
10566
|
+
if (existsSync15(d)) {
|
|
12753
10567
|
found.push(d);
|
|
12754
10568
|
}
|
|
12755
10569
|
}
|
|
@@ -12759,12 +10573,12 @@ async function discoverRootDomainDocs(root) {
|
|
|
12759
10573
|
const docs = [];
|
|
12760
10574
|
for (const name of ["CONTEXT.md", "CONTEXT-MAP.md"]) {
|
|
12761
10575
|
const p = path5.join(root, name);
|
|
12762
|
-
if (
|
|
10576
|
+
if (existsSync15(p)) {
|
|
12763
10577
|
docs.push(p);
|
|
12764
10578
|
}
|
|
12765
10579
|
}
|
|
12766
10580
|
const adrDir = path5.join(root, "docs", "adr");
|
|
12767
|
-
if (
|
|
10581
|
+
if (existsSync15(adrDir)) {
|
|
12768
10582
|
const entries = await readdir(adrDir, { withFileTypes: true });
|
|
12769
10583
|
for (const entry of entries) {
|
|
12770
10584
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -12906,7 +10720,7 @@ async function planInit(options) {
|
|
|
12906
10720
|
for (const file of skillFiles) {
|
|
12907
10721
|
const relPath = path5.relative(s, file);
|
|
12908
10722
|
const destPath = path5.join(targetRoot, ".agents", "skills", relPath);
|
|
12909
|
-
if (!
|
|
10723
|
+
if (!existsSync15(destPath)) {
|
|
12910
10724
|
operations.push({
|
|
12911
10725
|
kind: "copy",
|
|
12912
10726
|
sourcePath: file,
|
|
@@ -12969,7 +10783,7 @@ async function planInit(options) {
|
|
|
12969
10783
|
});
|
|
12970
10784
|
}
|
|
12971
10785
|
if (sourceRoot) {
|
|
12972
|
-
if (!
|
|
10786
|
+
if (!existsSync15(sourceRoot) || !statSync(sourceRoot).isDirectory()) {
|
|
12973
10787
|
warnings.push(
|
|
12974
10788
|
`--from-local path does not exist or is not a directory: ${sourceRoot}`
|
|
12975
10789
|
);
|
|
@@ -13034,9 +10848,9 @@ async function planInit(options) {
|
|
|
13034
10848
|
"release",
|
|
13035
10849
|
"skills"
|
|
13036
10850
|
);
|
|
13037
|
-
const srcSkills =
|
|
10851
|
+
const srcSkills = existsSync15(packagedManagedSkills) ? [
|
|
13038
10852
|
packagedManagedSkills,
|
|
13039
|
-
...
|
|
10853
|
+
...existsSync15(packagedReleaseAddonSkills) ? [packagedReleaseAddonSkills] : []
|
|
13040
10854
|
] : await discoverAgentSkills(sourceRoot);
|
|
13041
10855
|
for (const s of srcSkills) {
|
|
13042
10856
|
const isOpenCode = s.includes(".opencode");
|
|
@@ -13059,7 +10873,7 @@ async function planInit(options) {
|
|
|
13059
10873
|
"skills",
|
|
13060
10874
|
relPath
|
|
13061
10875
|
);
|
|
13062
|
-
if (!
|
|
10876
|
+
if (!existsSync15(destPath)) {
|
|
13063
10877
|
operations.push({
|
|
13064
10878
|
kind: "copy",
|
|
13065
10879
|
sourcePath: file,
|
|
@@ -13146,7 +10960,7 @@ async function planInit(options) {
|
|
|
13146
10960
|
});
|
|
13147
10961
|
continue;
|
|
13148
10962
|
}
|
|
13149
|
-
const sourceContent = await
|
|
10963
|
+
const sourceContent = await readFile4(file, "utf-8");
|
|
13150
10964
|
const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
|
|
13151
10965
|
operations.push({
|
|
13152
10966
|
kind: "create",
|
|
@@ -13191,7 +11005,7 @@ async function planInit(options) {
|
|
|
13191
11005
|
requiresConfirmation: false,
|
|
13192
11006
|
destructive: false
|
|
13193
11007
|
});
|
|
13194
|
-
} else if (
|
|
11008
|
+
} else if (existsSync15(destPath)) {
|
|
13195
11009
|
operations.push({
|
|
13196
11010
|
kind: "skip",
|
|
13197
11011
|
path: destPath,
|
|
@@ -13226,7 +11040,7 @@ async function planInit(options) {
|
|
|
13226
11040
|
let packageScripts = {};
|
|
13227
11041
|
let hasPackageJson = true;
|
|
13228
11042
|
try {
|
|
13229
|
-
const pkgContent = await
|
|
11043
|
+
const pkgContent = await readFile4(
|
|
13230
11044
|
path5.join(targetRoot, "package.json"),
|
|
13231
11045
|
"utf-8"
|
|
13232
11046
|
);
|
|
@@ -13249,7 +11063,7 @@ async function planInit(options) {
|
|
|
13249
11063
|
}
|
|
13250
11064
|
}
|
|
13251
11065
|
const contextPath = path5.join(targetRoot, ".pourkit", "CONTEXT.md");
|
|
13252
|
-
if (!
|
|
11066
|
+
if (!existsSync15(contextPath) && !merleDestPaths.has(contextPath)) {
|
|
13253
11067
|
operations.push({
|
|
13254
11068
|
kind: "create",
|
|
13255
11069
|
path: contextPath,
|
|
@@ -13267,7 +11081,7 @@ async function planInit(options) {
|
|
|
13267
11081
|
"adr",
|
|
13268
11082
|
".gitkeep"
|
|
13269
11083
|
);
|
|
13270
|
-
if (!
|
|
11084
|
+
if (!existsSync15(adrGitkeep)) {
|
|
13271
11085
|
operations.push({
|
|
13272
11086
|
kind: "create",
|
|
13273
11087
|
path: adrGitkeep,
|
|
@@ -13289,7 +11103,7 @@ async function planInit(options) {
|
|
|
13289
11103
|
"docs",
|
|
13290
11104
|
"agents"
|
|
13291
11105
|
);
|
|
13292
|
-
const srcDocAgents =
|
|
11106
|
+
const srcDocAgents = existsSync15(packagedManagedDocAgents) ? packagedManagedDocAgents : legacyDocAgents;
|
|
13293
11107
|
const tgtManagedDocAgents = path5.join(
|
|
13294
11108
|
targetRoot,
|
|
13295
11109
|
".pourkit",
|
|
@@ -13303,12 +11117,12 @@ async function planInit(options) {
|
|
|
13303
11117
|
"docs",
|
|
13304
11118
|
"agents"
|
|
13305
11119
|
);
|
|
13306
|
-
if (
|
|
11120
|
+
if (existsSync15(srcDocAgents)) {
|
|
13307
11121
|
const docFiles = await walkDir(srcDocAgents);
|
|
13308
11122
|
for (const file of docFiles) {
|
|
13309
11123
|
const relPath = path5.relative(srcDocAgents, file);
|
|
13310
11124
|
const managedDocDest = path5.join(tgtManagedDocAgents, relPath);
|
|
13311
|
-
if (!
|
|
11125
|
+
if (!existsSync15(managedDocDest) && relPath === "triage-labels.md") {
|
|
13312
11126
|
operations.push({
|
|
13313
11127
|
kind: "create",
|
|
13314
11128
|
path: managedDocDest,
|
|
@@ -13320,7 +11134,7 @@ async function planInit(options) {
|
|
|
13320
11134
|
options.labels ?? DEFAULT_RUNNER_LABELS
|
|
13321
11135
|
)
|
|
13322
11136
|
});
|
|
13323
|
-
} else if (!
|
|
11137
|
+
} else if (!existsSync15(managedDocDest)) {
|
|
13324
11138
|
const checksum = await computeFileChecksum(file);
|
|
13325
11139
|
operations.push({
|
|
13326
11140
|
kind: "copy",
|
|
@@ -13342,7 +11156,7 @@ async function planInit(options) {
|
|
|
13342
11156
|
relPath
|
|
13343
11157
|
);
|
|
13344
11158
|
const stubDocDest = path5.join(tgtStubDocAgents, relPath);
|
|
13345
|
-
if (
|
|
11159
|
+
if (existsSync15(stubDocDest)) continue;
|
|
13346
11160
|
operations.push({
|
|
13347
11161
|
kind: "create",
|
|
13348
11162
|
path: stubDocDest,
|
|
@@ -13376,7 +11190,7 @@ Do not edit this file.
|
|
|
13376
11190
|
"docs",
|
|
13377
11191
|
"agents"
|
|
13378
11192
|
);
|
|
13379
|
-
if (
|
|
11193
|
+
if (existsSync15(packagedReleaseAddonDocs)) {
|
|
13380
11194
|
const docFiles = await walkDir(packagedReleaseAddonDocs);
|
|
13381
11195
|
for (const file of docFiles) {
|
|
13382
11196
|
const relPath = path5.relative(packagedReleaseAddonDocs, file);
|
|
@@ -13400,19 +11214,19 @@ Do not edit this file.
|
|
|
13400
11214
|
"prompts"
|
|
13401
11215
|
);
|
|
13402
11216
|
const legacyPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
|
|
13403
|
-
const srcPrompts =
|
|
11217
|
+
const srcPrompts = existsSync15(packagedManagedPrompts) ? packagedManagedPrompts : legacyPrompts;
|
|
13404
11218
|
const tgtManagedPrompts = path5.join(
|
|
13405
11219
|
targetRoot,
|
|
13406
11220
|
".pourkit",
|
|
13407
11221
|
"managed",
|
|
13408
11222
|
"prompts"
|
|
13409
11223
|
);
|
|
13410
|
-
if (
|
|
11224
|
+
if (existsSync15(srcPrompts)) {
|
|
13411
11225
|
const promptFiles = await walkDir(srcPrompts);
|
|
13412
11226
|
for (const file of promptFiles) {
|
|
13413
11227
|
const relPath = path5.relative(srcPrompts, file);
|
|
13414
11228
|
const promptDest = path5.join(tgtManagedPrompts, relPath);
|
|
13415
|
-
if (
|
|
11229
|
+
if (existsSync15(promptDest)) continue;
|
|
13416
11230
|
const checksum = await computeFileChecksum(file);
|
|
13417
11231
|
operations.push({
|
|
13418
11232
|
kind: "copy",
|
|
@@ -13436,7 +11250,7 @@ Do not edit this file.
|
|
|
13436
11250
|
".sandcastle",
|
|
13437
11251
|
"Dockerfile"
|
|
13438
11252
|
);
|
|
13439
|
-
if (
|
|
11253
|
+
if (existsSync15(tgtSandboxDockerfile)) {
|
|
13440
11254
|
operations.push({
|
|
13441
11255
|
kind: "skip",
|
|
13442
11256
|
path: tgtSandboxDockerfile,
|
|
@@ -13445,7 +11259,7 @@ Do not edit this file.
|
|
|
13445
11259
|
requiresConfirmation: false,
|
|
13446
11260
|
destructive: false
|
|
13447
11261
|
});
|
|
13448
|
-
} else if (
|
|
11262
|
+
} else if (existsSync15(srcSandboxDockerfile)) {
|
|
13449
11263
|
const checksum = await computeFileChecksum(srcSandboxDockerfile);
|
|
13450
11264
|
operations.push({
|
|
13451
11265
|
kind: "copy",
|
|
@@ -13459,7 +11273,7 @@ Do not edit this file.
|
|
|
13459
11273
|
});
|
|
13460
11274
|
}
|
|
13461
11275
|
const configJsonPath = path5.join(targetRoot, ".pourkit", "config.json");
|
|
13462
|
-
if (!
|
|
11276
|
+
if (!existsSync15(configJsonPath)) {
|
|
13463
11277
|
const verifyCommands = inferVerificationCommands(
|
|
13464
11278
|
packageScripts,
|
|
13465
11279
|
pm || "npm"
|
|
@@ -13503,9 +11317,9 @@ Do not edit this file.
|
|
|
13503
11317
|
"schema",
|
|
13504
11318
|
"pourkit.schema.json"
|
|
13505
11319
|
);
|
|
13506
|
-
if (
|
|
11320
|
+
if (existsSync15(srcSchemaJson)) {
|
|
13507
11321
|
const checksum = await computeFileChecksum(srcSchemaJson);
|
|
13508
|
-
if (!
|
|
11322
|
+
if (!existsSync15(schemaJsonPath)) {
|
|
13509
11323
|
operations.push({
|
|
13510
11324
|
kind: "copy",
|
|
13511
11325
|
sourcePath: srcSchemaJson,
|
|
@@ -13538,9 +11352,9 @@ Do not edit this file.
|
|
|
13538
11352
|
"schema",
|
|
13539
11353
|
"pourkit.schema.hash"
|
|
13540
11354
|
);
|
|
13541
|
-
if (
|
|
11355
|
+
if (existsSync15(srcSchemaHash)) {
|
|
13542
11356
|
const checksum = await computeFileChecksum(srcSchemaHash);
|
|
13543
|
-
if (!
|
|
11357
|
+
if (!existsSync15(schemaHashPath)) {
|
|
13544
11358
|
operations.push({
|
|
13545
11359
|
kind: "copy",
|
|
13546
11360
|
sourcePath: srcSchemaHash,
|
|
@@ -13566,7 +11380,7 @@ Do not edit this file.
|
|
|
13566
11380
|
const hasExistingAgents = operations.some(
|
|
13567
11381
|
(op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("AGENTS.md")
|
|
13568
11382
|
);
|
|
13569
|
-
if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !
|
|
11383
|
+
if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !existsSync15(path5.join(targetRoot, "AGENTS.md"))) {
|
|
13570
11384
|
operations.push({
|
|
13571
11385
|
kind: "create",
|
|
13572
11386
|
path: path5.join(targetRoot, "AGENTS.md"),
|
|
@@ -13582,7 +11396,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
|
|
|
13582
11396
|
const hasExistingClaude = operations.some(
|
|
13583
11397
|
(op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("CLAUDE.md")
|
|
13584
11398
|
);
|
|
13585
|
-
if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !
|
|
11399
|
+
if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !existsSync15(path5.join(targetRoot, "CLAUDE.md"))) {
|
|
13586
11400
|
operations.push({
|
|
13587
11401
|
kind: "create",
|
|
13588
11402
|
path: path5.join(targetRoot, "CLAUDE.md"),
|
|
@@ -13597,7 +11411,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
|
|
|
13597
11411
|
}
|
|
13598
11412
|
const gitignoreTarget = path5.join(targetRoot, ".gitignore");
|
|
13599
11413
|
const gitignoreContent = generateGitignoreBlock();
|
|
13600
|
-
if (!
|
|
11414
|
+
if (!existsSync15(gitignoreTarget)) {
|
|
13601
11415
|
operations.push({
|
|
13602
11416
|
kind: "create",
|
|
13603
11417
|
path: gitignoreTarget,
|
|
@@ -13621,7 +11435,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13621
11435
|
});
|
|
13622
11436
|
}
|
|
13623
11437
|
const openCodePath = path5.join(targetRoot, "opencode.json");
|
|
13624
|
-
if (!
|
|
11438
|
+
if (!existsSync15(openCodePath)) {
|
|
13625
11439
|
operations.push({
|
|
13626
11440
|
kind: "create",
|
|
13627
11441
|
path: openCodePath,
|
|
@@ -13633,7 +11447,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13633
11447
|
});
|
|
13634
11448
|
} else {
|
|
13635
11449
|
try {
|
|
13636
|
-
const existingContent = await
|
|
11450
|
+
const existingContent = await readFile4(openCodePath, "utf-8");
|
|
13637
11451
|
const existingConfig = JSON.parse(existingContent);
|
|
13638
11452
|
if (typeof existingConfig !== "object" || existingConfig === null || Array.isArray(existingConfig)) {
|
|
13639
11453
|
warnings.push(
|
|
@@ -13678,7 +11492,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13678
11492
|
}
|
|
13679
11493
|
}
|
|
13680
11494
|
const manifestPath = path5.join(targetRoot, ".pourkit", "manifest.json");
|
|
13681
|
-
if (
|
|
11495
|
+
if (existsSync15(manifestPath)) {
|
|
13682
11496
|
operations.push({
|
|
13683
11497
|
kind: "skip",
|
|
13684
11498
|
path: manifestPath,
|
|
@@ -14059,7 +11873,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
14059
11873
|
}
|
|
14060
11874
|
async function writeFileAtomic(filePath, content) {
|
|
14061
11875
|
const tmpPath = `${filePath}.tmp.${randomUUID()}`;
|
|
14062
|
-
await
|
|
11876
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
14063
11877
|
await rename(tmpPath, filePath);
|
|
14064
11878
|
}
|
|
14065
11879
|
var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
|
|
@@ -14068,13 +11882,13 @@ async function updateManagedBlock(filePath, content) {
|
|
|
14068
11882
|
const blockContent = `${MANAGED_BLOCK_BEGIN}
|
|
14069
11883
|
${content}${MANAGED_BLOCK_END}
|
|
14070
11884
|
`;
|
|
14071
|
-
if (!
|
|
11885
|
+
if (!existsSync15(filePath)) {
|
|
14072
11886
|
const dir = path5.dirname(filePath);
|
|
14073
|
-
await
|
|
11887
|
+
await mkdir4(dir, { recursive: true });
|
|
14074
11888
|
await writeFileAtomic(filePath, blockContent);
|
|
14075
11889
|
return;
|
|
14076
11890
|
}
|
|
14077
|
-
const existing = await
|
|
11891
|
+
const existing = await readFile4(filePath, "utf-8");
|
|
14078
11892
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
|
|
14079
11893
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
14080
11894
|
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
@@ -14097,7 +11911,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14097
11911
|
if (op.requiresConfirmation) continue;
|
|
14098
11912
|
const relPath = path5.relative(plan.targetRoot, op.path);
|
|
14099
11913
|
if (relPath === ".pourkit/manifest.json") continue;
|
|
14100
|
-
if (
|
|
11914
|
+
if (existsSync15(op.path)) {
|
|
14101
11915
|
const sha256 = await computeFileChecksum(op.path);
|
|
14102
11916
|
assets[relPath] = {
|
|
14103
11917
|
ownership: op.ownership || "managed",
|
|
@@ -14106,7 +11920,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14106
11920
|
}
|
|
14107
11921
|
}
|
|
14108
11922
|
const manifest = {
|
|
14109
|
-
schemaVersion:
|
|
11923
|
+
schemaVersion: 2,
|
|
14110
11924
|
initializedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14111
11925
|
pourkit: {
|
|
14112
11926
|
versionSource: sourceMeta.versionSource,
|
|
@@ -14122,7 +11936,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14122
11936
|
workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
|
|
14123
11937
|
assets
|
|
14124
11938
|
};
|
|
14125
|
-
await
|
|
11939
|
+
await mkdir4(manifestDir, { recursive: true });
|
|
14126
11940
|
await writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
14127
11941
|
return manifest;
|
|
14128
11942
|
}
|
|
@@ -14143,12 +11957,12 @@ async function applyInitPlan(plan, options) {
|
|
|
14143
11957
|
skipped++;
|
|
14144
11958
|
continue;
|
|
14145
11959
|
}
|
|
14146
|
-
if (
|
|
11960
|
+
if (existsSync15(op.path) && !op.destructive) {
|
|
14147
11961
|
skipped++;
|
|
14148
11962
|
continue;
|
|
14149
11963
|
}
|
|
14150
11964
|
const dir = path5.dirname(op.path);
|
|
14151
|
-
await
|
|
11965
|
+
await mkdir4(dir, { recursive: true });
|
|
14152
11966
|
await writeFileAtomic(op.path, op.content ?? "");
|
|
14153
11967
|
applied++;
|
|
14154
11968
|
break;
|
|
@@ -14158,7 +11972,7 @@ async function applyInitPlan(plan, options) {
|
|
|
14158
11972
|
skipped++;
|
|
14159
11973
|
continue;
|
|
14160
11974
|
}
|
|
14161
|
-
if (
|
|
11975
|
+
if (existsSync15(op.path)) {
|
|
14162
11976
|
skipped++;
|
|
14163
11977
|
continue;
|
|
14164
11978
|
}
|
|
@@ -14167,7 +11981,7 @@ async function applyInitPlan(plan, options) {
|
|
|
14167
11981
|
skipped++;
|
|
14168
11982
|
} else {
|
|
14169
11983
|
const dir = path5.dirname(op.path);
|
|
14170
|
-
await
|
|
11984
|
+
await mkdir4(dir, { recursive: true });
|
|
14171
11985
|
await copyFile(op.sourcePath, op.path);
|
|
14172
11986
|
applied++;
|
|
14173
11987
|
}
|
|
@@ -14187,12 +12001,12 @@ async function applyInitPlan(plan, options) {
|
|
|
14187
12001
|
skipped++;
|
|
14188
12002
|
continue;
|
|
14189
12003
|
}
|
|
14190
|
-
if (
|
|
12004
|
+
if (existsSync15(op.path)) {
|
|
14191
12005
|
skipped++;
|
|
14192
12006
|
continue;
|
|
14193
12007
|
}
|
|
14194
12008
|
const dir = path5.dirname(op.path);
|
|
14195
|
-
await
|
|
12009
|
+
await mkdir4(dir, { recursive: true });
|
|
14196
12010
|
await rename(op.sourcePath, op.path);
|
|
14197
12011
|
applied++;
|
|
14198
12012
|
break;
|
|
@@ -14331,7 +12145,7 @@ async function applyInitFromSource(options) {
|
|
|
14331
12145
|
if (!manifestSkipped) {
|
|
14332
12146
|
const agentFiles = [];
|
|
14333
12147
|
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
14334
|
-
if (
|
|
12148
|
+
if (existsSync15(path5.join(targetRoot, name))) {
|
|
14335
12149
|
agentFiles.push(path5.join(targetRoot, name));
|
|
14336
12150
|
}
|
|
14337
12151
|
}
|
|
@@ -14507,18 +12321,18 @@ Init applied: ${result.applied} operations applied, ${result.skipped} skipped.`
|
|
|
14507
12321
|
}
|
|
14508
12322
|
|
|
14509
12323
|
// commands/memory-init.ts
|
|
14510
|
-
import { existsSync as
|
|
14511
|
-
import { join as
|
|
12324
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync5 } from "fs";
|
|
12325
|
+
import { join as join17 } from "path";
|
|
14512
12326
|
async function runMemoryInitCommand(options) {
|
|
14513
12327
|
const cwd = options.cwd ?? process.cwd();
|
|
14514
|
-
const configPath =
|
|
14515
|
-
if (!
|
|
12328
|
+
const configPath = join17(cwd, ".pourkit", "config.json");
|
|
12329
|
+
if (!existsSync16(configPath)) {
|
|
14516
12330
|
return {
|
|
14517
12331
|
ok: false,
|
|
14518
12332
|
message: "No .pourkit/config.json found. Run `pourkit init` first to initialize Pourkit, then run `pourkit memory init`."
|
|
14519
12333
|
};
|
|
14520
12334
|
}
|
|
14521
|
-
const raw = JSON.parse(
|
|
12335
|
+
const raw = JSON.parse(readFileSync17(configPath, "utf8"));
|
|
14522
12336
|
let alreadyEnabled = false;
|
|
14523
12337
|
if (Object.prototype.hasOwnProperty.call(raw, "memory")) {
|
|
14524
12338
|
const m = raw.memory;
|
|
@@ -14541,10 +12355,10 @@ async function runMemoryInitCommand(options) {
|
|
|
14541
12355
|
const memory = { enabled: true, provider: "icm" };
|
|
14542
12356
|
if (!alreadyEnabled) {
|
|
14543
12357
|
const next = { ...raw, memory };
|
|
14544
|
-
|
|
12358
|
+
writeFileSync5(configPath, JSON.stringify(next, null, 2) + "\n");
|
|
14545
12359
|
}
|
|
14546
|
-
|
|
14547
|
-
const gitignorePath =
|
|
12360
|
+
mkdirSync8(join17(cwd, ".pourkit", "icm"), { recursive: true });
|
|
12361
|
+
const gitignorePath = join17(cwd, ".gitignore");
|
|
14548
12362
|
updateManagedBlockSync(gitignorePath, generateGitignoreBlock());
|
|
14549
12363
|
const managed = await generateManagedAgentInstructions({
|
|
14550
12364
|
sourceRoot: cwd,
|
|
@@ -14552,14 +12366,14 @@ async function runMemoryInitCommand(options) {
|
|
|
14552
12366
|
});
|
|
14553
12367
|
let updatedAgentFile = false;
|
|
14554
12368
|
for (const fileName of ["AGENTS.md", "CLAUDE.md"]) {
|
|
14555
|
-
const agentPath =
|
|
14556
|
-
if (
|
|
12369
|
+
const agentPath = join17(cwd, fileName);
|
|
12370
|
+
if (existsSync16(agentPath)) {
|
|
14557
12371
|
updateManagedBlockSync(agentPath, managed);
|
|
14558
12372
|
updatedAgentFile = true;
|
|
14559
12373
|
}
|
|
14560
12374
|
}
|
|
14561
12375
|
if (!updatedAgentFile) {
|
|
14562
|
-
updateManagedBlockSync(
|
|
12376
|
+
updateManagedBlockSync(join17(cwd, "AGENTS.md"), managed);
|
|
14563
12377
|
}
|
|
14564
12378
|
return {
|
|
14565
12379
|
ok: true,
|
|
@@ -14571,21 +12385,21 @@ function updateManagedBlockSync(filePath, content) {
|
|
|
14571
12385
|
const blockContent = `${MANAGED_BLOCK_BEGIN}
|
|
14572
12386
|
${content}${MANAGED_BLOCK_END}
|
|
14573
12387
|
`;
|
|
14574
|
-
if (!
|
|
14575
|
-
|
|
12388
|
+
if (!existsSync16(filePath)) {
|
|
12389
|
+
writeFileSync5(filePath, blockContent);
|
|
14576
12390
|
return;
|
|
14577
12391
|
}
|
|
14578
|
-
const existing =
|
|
12392
|
+
const existing = readFileSync17(filePath, "utf8");
|
|
14579
12393
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
|
|
14580
12394
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
14581
12395
|
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
14582
12396
|
const before = existing.slice(0, beginIdx);
|
|
14583
12397
|
const after = existing.slice(endIdx + MANAGED_BLOCK_END.length);
|
|
14584
|
-
|
|
12398
|
+
writeFileSync5(filePath, before + blockContent + after);
|
|
14585
12399
|
return;
|
|
14586
12400
|
}
|
|
14587
12401
|
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
14588
|
-
|
|
12402
|
+
writeFileSync5(filePath, `${existing}${suffix}${blockContent}`);
|
|
14589
12403
|
}
|
|
14590
12404
|
|
|
14591
12405
|
// commands/serena.ts
|
|
@@ -14704,8 +12518,8 @@ async function runSerenaStatusCommand(options) {
|
|
|
14704
12518
|
}
|
|
14705
12519
|
|
|
14706
12520
|
// commands/config-schema.ts
|
|
14707
|
-
import { readFileSync as
|
|
14708
|
-
import { mkdir as
|
|
12521
|
+
import { readFileSync as readFileSync18, existsSync as existsSync17 } from "fs";
|
|
12522
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile2, readdir as readdir2 } from "fs/promises";
|
|
14709
12523
|
import { resolve as resolve4, dirname as dirname5, relative as relative2 } from "path";
|
|
14710
12524
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14711
12525
|
import Ajv2 from "ajv";
|
|
@@ -14717,7 +12531,7 @@ function resolvePackagedAssetPath2(fileName) {
|
|
|
14717
12531
|
resolve4(__dirname3, "schema", fileName),
|
|
14718
12532
|
resolve4(__dirname3, "../schema", fileName)
|
|
14719
12533
|
];
|
|
14720
|
-
return candidates.find((candidate) =>
|
|
12534
|
+
return candidates.find((candidate) => existsSync17(candidate)) ?? candidates[0];
|
|
14721
12535
|
}
|
|
14722
12536
|
var PACKAGED_SCHEMA_PATH = resolvePackagedAssetPath2("pourkit.schema.json");
|
|
14723
12537
|
var PACKAGED_HASH_PATH = resolvePackagedAssetPath2("pourkit.schema.hash");
|
|
@@ -14725,7 +12539,7 @@ var _schemaValidator = null;
|
|
|
14725
12539
|
var _schemaErrors = null;
|
|
14726
12540
|
function getSchemaValidator() {
|
|
14727
12541
|
if (!_schemaValidator) {
|
|
14728
|
-
const schema = JSON.parse(
|
|
12542
|
+
const schema = JSON.parse(readFileSync18(PACKAGED_SCHEMA_PATH, "utf-8"));
|
|
14729
12543
|
const ajv = new Ajv2({ strict: true });
|
|
14730
12544
|
ajv.addKeyword("x-pourkit-schema-version");
|
|
14731
12545
|
const validate = ajv.compile(schema);
|
|
@@ -14739,7 +12553,7 @@ function getSchemaValidator() {
|
|
|
14739
12553
|
return _schemaValidator;
|
|
14740
12554
|
}
|
|
14741
12555
|
function readPackagedHash() {
|
|
14742
|
-
return
|
|
12556
|
+
return readFileSync18(PACKAGED_HASH_PATH, "utf-8");
|
|
14743
12557
|
}
|
|
14744
12558
|
function localSchemaDir(repoRoot2) {
|
|
14745
12559
|
return resolve4(repoRoot2, ".pourkit/schema");
|
|
@@ -14763,7 +12577,7 @@ function resolvePackagedReleaseAddonDocPath(docName) {
|
|
|
14763
12577
|
}
|
|
14764
12578
|
async function readPackagedTextFile(filePath) {
|
|
14765
12579
|
try {
|
|
14766
|
-
return await
|
|
12580
|
+
return await readFile5(filePath, "utf-8");
|
|
14767
12581
|
} catch {
|
|
14768
12582
|
return null;
|
|
14769
12583
|
}
|
|
@@ -14860,7 +12674,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14860
12674
|
const baselineSkills = getBaselineManagedSkillNames();
|
|
14861
12675
|
for (const docName of catalog.docs) {
|
|
14862
12676
|
const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
|
|
14863
|
-
if (!
|
|
12677
|
+
if (!existsSync17(localPath)) {
|
|
14864
12678
|
failures.push({
|
|
14865
12679
|
severity: "failure",
|
|
14866
12680
|
kind: "missing_managed_asset",
|
|
@@ -14872,13 +12686,13 @@ async function validateWorkflowPack(cwd) {
|
|
|
14872
12686
|
const packagedContent = await readPackagedTextFile(packagedPath);
|
|
14873
12687
|
if (packagedContent !== null) {
|
|
14874
12688
|
try {
|
|
14875
|
-
const localContent = await
|
|
12689
|
+
const localContent = await readFile5(localPath, "utf-8");
|
|
14876
12690
|
if (localContent !== packagedContent) {
|
|
14877
12691
|
const overridePath = resolve4(
|
|
14878
12692
|
cwd,
|
|
14879
12693
|
`.pourkit/overrides/docs/agents/${docName}`
|
|
14880
12694
|
);
|
|
14881
|
-
if (
|
|
12695
|
+
if (existsSync17(overridePath)) {
|
|
14882
12696
|
warnings.push({
|
|
14883
12697
|
severity: "warning",
|
|
14884
12698
|
kind: "overridden",
|
|
@@ -14901,7 +12715,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14901
12715
|
}
|
|
14902
12716
|
for (const skillName of baselineSkills) {
|
|
14903
12717
|
const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
|
|
14904
|
-
if (!
|
|
12718
|
+
if (!existsSync17(skillDir)) {
|
|
14905
12719
|
failures.push({
|
|
14906
12720
|
severity: "failure",
|
|
14907
12721
|
kind: "missing_managed_asset",
|
|
@@ -14910,7 +12724,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14910
12724
|
});
|
|
14911
12725
|
} else {
|
|
14912
12726
|
const skillSourcePath = resolve4(skillDir, "SKILL.md");
|
|
14913
|
-
if (
|
|
12727
|
+
if (existsSync17(skillSourcePath)) {
|
|
14914
12728
|
const packagedSkillPath = resolvePackagedManagedPath(
|
|
14915
12729
|
"skills",
|
|
14916
12730
|
skillName,
|
|
@@ -14919,13 +12733,13 @@ async function validateWorkflowPack(cwd) {
|
|
|
14919
12733
|
const packagedContent = await readPackagedTextFile(packagedSkillPath);
|
|
14920
12734
|
if (packagedContent !== null) {
|
|
14921
12735
|
try {
|
|
14922
|
-
const localContent = await
|
|
12736
|
+
const localContent = await readFile5(skillSourcePath, "utf-8");
|
|
14923
12737
|
if (localContent !== packagedContent) {
|
|
14924
12738
|
const overridePath = resolve4(
|
|
14925
12739
|
cwd,
|
|
14926
12740
|
`.pourkit/overrides/skills/${skillName}/SKILL.md`
|
|
14927
12741
|
);
|
|
14928
|
-
if (
|
|
12742
|
+
if (existsSync17(overridePath)) {
|
|
14929
12743
|
warnings.push({
|
|
14930
12744
|
severity: "warning",
|
|
14931
12745
|
kind: "overridden",
|
|
@@ -14949,7 +12763,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14949
12763
|
}
|
|
14950
12764
|
for (const promptName of catalog.prompts) {
|
|
14951
12765
|
const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
|
|
14952
|
-
if (!
|
|
12766
|
+
if (!existsSync17(promptPath)) {
|
|
14953
12767
|
failures.push({
|
|
14954
12768
|
severity: "failure",
|
|
14955
12769
|
kind: "missing_managed_asset",
|
|
@@ -14960,7 +12774,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14960
12774
|
}
|
|
14961
12775
|
for (const skillName of baselineSkills) {
|
|
14962
12776
|
const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
|
|
14963
|
-
if (!
|
|
12777
|
+
if (!existsSync17(projectionDir)) {
|
|
14964
12778
|
warnings.push({
|
|
14965
12779
|
severity: "warning",
|
|
14966
12780
|
kind: "projection_stale",
|
|
@@ -14969,7 +12783,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14969
12783
|
});
|
|
14970
12784
|
} else {
|
|
14971
12785
|
const projectionPath = resolve4(projectionDir, "SKILL.md");
|
|
14972
|
-
if (
|
|
12786
|
+
if (existsSync17(projectionPath)) {
|
|
14973
12787
|
const managedSourcePath = resolvePackagedManagedPath(
|
|
14974
12788
|
"skills",
|
|
14975
12789
|
skillName,
|
|
@@ -14978,7 +12792,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14978
12792
|
const packagedContent = await readPackagedTextFile(managedSourcePath);
|
|
14979
12793
|
if (packagedContent !== null) {
|
|
14980
12794
|
try {
|
|
14981
|
-
const projectionContent = await
|
|
12795
|
+
const projectionContent = await readFile5(projectionPath, "utf-8");
|
|
14982
12796
|
const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
|
|
14983
12797
|
if (projectionContent !== buildOpenCodeSkillProjectionContent(
|
|
14984
12798
|
managedSourceRelativePath,
|
|
@@ -14998,9 +12812,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
14998
12812
|
}
|
|
14999
12813
|
}
|
|
15000
12814
|
const agentsPath = resolve4(cwd, "AGENTS.md");
|
|
15001
|
-
if (
|
|
12815
|
+
if (existsSync17(agentsPath)) {
|
|
15002
12816
|
try {
|
|
15003
|
-
const agentsContent = await
|
|
12817
|
+
const agentsContent = await readFile5(agentsPath, "utf-8");
|
|
15004
12818
|
if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
|
|
15005
12819
|
failures.push({
|
|
15006
12820
|
severity: "failure",
|
|
@@ -15021,7 +12835,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15021
12835
|
}
|
|
15022
12836
|
}
|
|
15023
12837
|
const overridesDir = resolve4(cwd, ".pourkit/overrides");
|
|
15024
|
-
if (
|
|
12838
|
+
if (existsSync17(overridesDir)) {
|
|
15025
12839
|
const overrideFiles = await walkDir2(overridesDir);
|
|
15026
12840
|
for (const file of overrideFiles) {
|
|
15027
12841
|
const relPath = relative2(cwd, file);
|
|
@@ -15036,9 +12850,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15036
12850
|
}
|
|
15037
12851
|
}
|
|
15038
12852
|
const configPath = resolve4(cwd, ".pourkit/config.json");
|
|
15039
|
-
if (
|
|
12853
|
+
if (existsSync17(configPath)) {
|
|
15040
12854
|
try {
|
|
15041
|
-
const configContent = await
|
|
12855
|
+
const configContent = await readFile5(configPath, "utf-8");
|
|
15042
12856
|
const parsed = JSON.parse(configContent);
|
|
15043
12857
|
const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
|
|
15044
12858
|
if (releaseEnabled) {
|
|
@@ -15047,7 +12861,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15047
12861
|
cwd,
|
|
15048
12862
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
15049
12863
|
);
|
|
15050
|
-
if (!
|
|
12864
|
+
if (!existsSync17(addonDir)) {
|
|
15051
12865
|
failures.push({
|
|
15052
12866
|
severity: "failure",
|
|
15053
12867
|
kind: "release_config_conflict",
|
|
@@ -15061,7 +12875,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15061
12875
|
cwd,
|
|
15062
12876
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15063
12877
|
);
|
|
15064
|
-
if (!
|
|
12878
|
+
if (!existsSync17(addonDocPath)) {
|
|
15065
12879
|
failures.push({
|
|
15066
12880
|
severity: "failure",
|
|
15067
12881
|
kind: "release_config_conflict",
|
|
@@ -15073,7 +12887,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15073
12887
|
resolvePackagedReleaseAddonDocPath(docName)
|
|
15074
12888
|
);
|
|
15075
12889
|
if (packagedContent !== null) {
|
|
15076
|
-
const localContent = await
|
|
12890
|
+
const localContent = await readFile5(addonDocPath, "utf-8");
|
|
15077
12891
|
if (localContent !== packagedContent) {
|
|
15078
12892
|
failures.push({
|
|
15079
12893
|
severity: "failure",
|
|
@@ -15106,7 +12920,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15106
12920
|
cwd,
|
|
15107
12921
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
15108
12922
|
);
|
|
15109
|
-
if (
|
|
12923
|
+
if (existsSync17(addonDir)) {
|
|
15110
12924
|
failures.push({
|
|
15111
12925
|
severity: "failure",
|
|
15112
12926
|
kind: "release_config_conflict",
|
|
@@ -15120,7 +12934,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15120
12934
|
cwd,
|
|
15121
12935
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15122
12936
|
);
|
|
15123
|
-
if (
|
|
12937
|
+
if (existsSync17(addonDocPath)) {
|
|
15124
12938
|
failures.push({
|
|
15125
12939
|
severity: "failure",
|
|
15126
12940
|
kind: "release_config_conflict",
|
|
@@ -15134,9 +12948,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15134
12948
|
}
|
|
15135
12949
|
}
|
|
15136
12950
|
const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
|
|
15137
|
-
if (
|
|
12951
|
+
if (existsSync17(manifestPath)) {
|
|
15138
12952
|
try {
|
|
15139
|
-
const manifestContent = await
|
|
12953
|
+
const manifestContent = await readFile5(manifestPath, "utf-8");
|
|
15140
12954
|
const manifest = JSON.parse(manifestContent);
|
|
15141
12955
|
const wp = manifest.workflowPack;
|
|
15142
12956
|
if (wp?.projections) {
|
|
@@ -15157,7 +12971,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15157
12971
|
}
|
|
15158
12972
|
}
|
|
15159
12973
|
const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
|
|
15160
|
-
if (
|
|
12974
|
+
if (existsSync17(pathEscapeOverrideDir)) {
|
|
15161
12975
|
const overrideFiles = await walkDir2(pathEscapeOverrideDir);
|
|
15162
12976
|
for (const file of overrideFiles) {
|
|
15163
12977
|
if (!isPathContained(cwd, file)) {
|
|
@@ -15171,12 +12985,12 @@ async function validateWorkflowPack(cwd) {
|
|
|
15171
12985
|
}
|
|
15172
12986
|
}
|
|
15173
12987
|
const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
|
|
15174
|
-
if (
|
|
12988
|
+
if (existsSync17(managedDocsDir)) {
|
|
15175
12989
|
const docFiles = await walkDir2(managedDocsDir);
|
|
15176
12990
|
for (const file of docFiles) {
|
|
15177
12991
|
if (!file.endsWith(".md")) continue;
|
|
15178
12992
|
try {
|
|
15179
|
-
const content = await
|
|
12993
|
+
const content = await readFile5(file, "utf-8");
|
|
15180
12994
|
const relPath = relative2(cwd, file);
|
|
15181
12995
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15182
12996
|
for (const v of violations) {
|
|
@@ -15192,14 +13006,14 @@ async function validateWorkflowPack(cwd) {
|
|
|
15192
13006
|
}
|
|
15193
13007
|
}
|
|
15194
13008
|
const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
|
|
15195
|
-
if (
|
|
13009
|
+
if (existsSync17(managedSkillsDir)) {
|
|
15196
13010
|
const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
|
|
15197
13011
|
for (const entry of skillDirs) {
|
|
15198
13012
|
if (!entry.isDirectory()) continue;
|
|
15199
13013
|
const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
|
|
15200
|
-
if (!
|
|
13014
|
+
if (!existsSync17(skillFile)) continue;
|
|
15201
13015
|
try {
|
|
15202
|
-
const content = await
|
|
13016
|
+
const content = await readFile5(skillFile, "utf-8");
|
|
15203
13017
|
const relPath = relative2(cwd, skillFile);
|
|
15204
13018
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15205
13019
|
for (const v of violations) {
|
|
@@ -15215,12 +13029,12 @@ async function validateWorkflowPack(cwd) {
|
|
|
15215
13029
|
}
|
|
15216
13030
|
}
|
|
15217
13031
|
const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
|
|
15218
|
-
if (
|
|
13032
|
+
if (existsSync17(managedPromptsDir)) {
|
|
15219
13033
|
for (const promptName of catalog.prompts) {
|
|
15220
13034
|
const promptFile = resolve4(managedPromptsDir, promptName);
|
|
15221
|
-
if (!
|
|
13035
|
+
if (!existsSync17(promptFile)) continue;
|
|
15222
13036
|
try {
|
|
15223
|
-
const content = await
|
|
13037
|
+
const content = await readFile5(promptFile, "utf-8");
|
|
15224
13038
|
const relPath = relative2(cwd, promptFile);
|
|
15225
13039
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15226
13040
|
for (const v of violations) {
|
|
@@ -15240,9 +13054,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15240
13054
|
cwd,
|
|
15241
13055
|
`.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
|
|
15242
13056
|
);
|
|
15243
|
-
if (!
|
|
13057
|
+
if (!existsSync17(addonSkillFile)) continue;
|
|
15244
13058
|
try {
|
|
15245
|
-
const content = await
|
|
13059
|
+
const content = await readFile5(addonSkillFile, "utf-8");
|
|
15246
13060
|
const relPath = relative2(cwd, addonSkillFile);
|
|
15247
13061
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15248
13062
|
for (const v of violations) {
|
|
@@ -15261,9 +13075,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15261
13075
|
cwd,
|
|
15262
13076
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15263
13077
|
);
|
|
15264
|
-
if (!
|
|
13078
|
+
if (!existsSync17(addonDocFile)) continue;
|
|
15265
13079
|
try {
|
|
15266
|
-
const content = await
|
|
13080
|
+
const content = await readFile5(addonDocFile, "utf-8");
|
|
15267
13081
|
const relPath = relative2(cwd, addonDocFile);
|
|
15268
13082
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15269
13083
|
for (const v of violations) {
|
|
@@ -15295,7 +13109,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
|
|
|
15295
13109
|
detail: icmPath !== null ? icmPath : "icm not found on PATH"
|
|
15296
13110
|
});
|
|
15297
13111
|
const gitignorePath = resolve4(cwd, ".gitignore");
|
|
15298
|
-
const gitignoreContent =
|
|
13112
|
+
const gitignoreContent = existsSync17(gitignorePath) ? readFileSync18(gitignorePath, "utf-8") : "";
|
|
15299
13113
|
const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
|
|
15300
13114
|
checks.push({
|
|
15301
13115
|
name: "icm-gitignore",
|
|
@@ -15322,8 +13136,8 @@ async function runDoctorCommand(options) {
|
|
|
15322
13136
|
const schemaDir = localSchemaDir(repoRootPath);
|
|
15323
13137
|
const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
|
|
15324
13138
|
const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
|
|
15325
|
-
const localSchemaExists =
|
|
15326
|
-
const localHashExists =
|
|
13139
|
+
const localSchemaExists = existsSync17(localSchemaPath);
|
|
13140
|
+
const localHashExists = existsSync17(localHashPath);
|
|
15327
13141
|
let packagedHash = null;
|
|
15328
13142
|
try {
|
|
15329
13143
|
packagedHash = readPackagedHash();
|
|
@@ -15332,7 +13146,7 @@ async function runDoctorCommand(options) {
|
|
|
15332
13146
|
let localHashContent = null;
|
|
15333
13147
|
if (localHashExists) {
|
|
15334
13148
|
try {
|
|
15335
|
-
localHashContent = await
|
|
13149
|
+
localHashContent = await readFile5(localHashPath, "utf-8");
|
|
15336
13150
|
} catch {
|
|
15337
13151
|
}
|
|
15338
13152
|
}
|
|
@@ -15342,9 +13156,9 @@ async function runDoctorCommand(options) {
|
|
|
15342
13156
|
const configPath = resolve4(repoRootPath, ".pourkit/config.json");
|
|
15343
13157
|
let configValidation;
|
|
15344
13158
|
let rawMemoryConfig;
|
|
15345
|
-
if (
|
|
13159
|
+
if (existsSync17(configPath)) {
|
|
15346
13160
|
try {
|
|
15347
|
-
const raw = JSON.parse(
|
|
13161
|
+
const raw = JSON.parse(readFileSync18(configPath, "utf-8"));
|
|
15348
13162
|
rawMemoryConfig = raw.memory;
|
|
15349
13163
|
const validate = getSchemaValidator();
|
|
15350
13164
|
const valid2 = validate(raw);
|
|
@@ -15377,7 +13191,7 @@ async function runDoctorCommand(options) {
|
|
|
15377
13191
|
"pourkit.config.mjs",
|
|
15378
13192
|
"pourkit.config.js",
|
|
15379
13193
|
"pourkit.json"
|
|
15380
|
-
].filter((p) =>
|
|
13194
|
+
].filter((p) => existsSync17(resolve4(repoRootPath, p)));
|
|
15381
13195
|
const workflowPack = await validateWorkflowPack(repoRootPath);
|
|
15382
13196
|
const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
|
|
15383
13197
|
let recommendation = null;
|
|
@@ -15420,31 +13234,31 @@ async function runDoctorCommand(options) {
|
|
|
15420
13234
|
async function runConfigSyncSchemaCommand(options) {
|
|
15421
13235
|
const repoRootPath = options.cwd ?? process.cwd();
|
|
15422
13236
|
const schemaDir = localSchemaDir(repoRootPath);
|
|
15423
|
-
await
|
|
15424
|
-
const packagedSchema = await
|
|
15425
|
-
const packagedHash = await
|
|
13237
|
+
await mkdir5(schemaDir, { recursive: true });
|
|
13238
|
+
const packagedSchema = await readFile5(PACKAGED_SCHEMA_PATH, "utf-8");
|
|
13239
|
+
const packagedHash = await readFile5(PACKAGED_HASH_PATH, "utf-8");
|
|
15426
13240
|
const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
|
|
15427
13241
|
const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
|
|
15428
13242
|
let schemaWritten = false;
|
|
15429
13243
|
let hashWritten = false;
|
|
15430
|
-
if (
|
|
15431
|
-
const existing = await
|
|
13244
|
+
if (existsSync17(localSchemaPath)) {
|
|
13245
|
+
const existing = await readFile5(localSchemaPath, "utf-8");
|
|
15432
13246
|
if (existing !== packagedSchema) {
|
|
15433
|
-
await
|
|
13247
|
+
await writeFile2(localSchemaPath, packagedSchema, "utf-8");
|
|
15434
13248
|
schemaWritten = true;
|
|
15435
13249
|
}
|
|
15436
13250
|
} else {
|
|
15437
|
-
await
|
|
13251
|
+
await writeFile2(localSchemaPath, packagedSchema, "utf-8");
|
|
15438
13252
|
schemaWritten = true;
|
|
15439
13253
|
}
|
|
15440
|
-
if (
|
|
15441
|
-
const existing = await
|
|
13254
|
+
if (existsSync17(localHashPath)) {
|
|
13255
|
+
const existing = await readFile5(localHashPath, "utf-8");
|
|
15442
13256
|
if (existing !== packagedHash) {
|
|
15443
|
-
await
|
|
13257
|
+
await writeFile2(localHashPath, packagedHash, "utf-8");
|
|
15444
13258
|
hashWritten = true;
|
|
15445
13259
|
}
|
|
15446
13260
|
} else {
|
|
15447
|
-
await
|
|
13261
|
+
await writeFile2(localHashPath, packagedHash, "utf-8");
|
|
15448
13262
|
hashWritten = true;
|
|
15449
13263
|
}
|
|
15450
13264
|
return {
|
|
@@ -15455,8 +13269,8 @@ async function runConfigSyncSchemaCommand(options) {
|
|
|
15455
13269
|
}
|
|
15456
13270
|
|
|
15457
13271
|
// commands/workflow-pack-sync.ts
|
|
15458
|
-
import { existsSync as
|
|
15459
|
-
import { mkdir as
|
|
13272
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
13273
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir3 } from "fs/promises";
|
|
15460
13274
|
import { resolve as resolve5, dirname as dirname6, relative as relative3, normalize as normalize2 } from "path";
|
|
15461
13275
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15462
13276
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
@@ -15494,7 +13308,7 @@ function resolvePackagedManagedPromptPath(promptName) {
|
|
|
15494
13308
|
}
|
|
15495
13309
|
async function readPackagedTextFile2(filePath) {
|
|
15496
13310
|
try {
|
|
15497
|
-
return await
|
|
13311
|
+
return await readFile6(filePath, "utf-8");
|
|
15498
13312
|
} catch {
|
|
15499
13313
|
return null;
|
|
15500
13314
|
}
|
|
@@ -15577,20 +13391,20 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
|
|
|
15577
13391
|
errors.push(escapeError);
|
|
15578
13392
|
return false;
|
|
15579
13393
|
}
|
|
15580
|
-
await
|
|
15581
|
-
if (
|
|
15582
|
-
const existing = await
|
|
13394
|
+
await mkdir6(resolve5(targetPath, ".."), { recursive: true });
|
|
13395
|
+
if (existsSync18(targetPath)) {
|
|
13396
|
+
const existing = await readFile6(targetPath, "utf-8");
|
|
15583
13397
|
if (existing === sourceContent) {
|
|
15584
13398
|
return false;
|
|
15585
13399
|
}
|
|
15586
13400
|
}
|
|
15587
|
-
await
|
|
13401
|
+
await writeFile3(targetPath, sourceContent, "utf-8");
|
|
15588
13402
|
return true;
|
|
15589
13403
|
}
|
|
15590
13404
|
function walkDirSync(dir) {
|
|
15591
13405
|
const result = [];
|
|
15592
13406
|
try {
|
|
15593
|
-
const entries =
|
|
13407
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
15594
13408
|
for (const entry of entries) {
|
|
15595
13409
|
const full = resolve5(dir, entry.name);
|
|
15596
13410
|
if (entry.isDirectory()) {
|
|
@@ -15605,7 +13419,7 @@ function walkDirSync(dir) {
|
|
|
15605
13419
|
}
|
|
15606
13420
|
async function detectOverrides(cwd) {
|
|
15607
13421
|
const overridesDir = resolve5(cwd, OVERRIDES_DIR);
|
|
15608
|
-
if (!
|
|
13422
|
+
if (!existsSync18(overridesDir)) {
|
|
15609
13423
|
return [];
|
|
15610
13424
|
}
|
|
15611
13425
|
const files = await walkDir3(overridesDir);
|
|
@@ -15615,13 +13429,13 @@ async function detectProjectOwnedFiles(cwd) {
|
|
|
15615
13429
|
const found = [];
|
|
15616
13430
|
for (const ownedPath of PROJECT_OWNED_PATHS) {
|
|
15617
13431
|
const fullPath = resolve5(cwd, ownedPath);
|
|
15618
|
-
if (
|
|
13432
|
+
if (existsSync18(fullPath)) {
|
|
15619
13433
|
found.push(ownedPath);
|
|
15620
13434
|
}
|
|
15621
13435
|
}
|
|
15622
13436
|
for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
|
|
15623
13437
|
const fullDir = resolve5(cwd, ownedDir);
|
|
15624
|
-
if (
|
|
13438
|
+
if (existsSync18(fullDir)) {
|
|
15625
13439
|
const files = await walkDir3(fullDir);
|
|
15626
13440
|
for (const file of files) {
|
|
15627
13441
|
found.push(relative3(cwd, file));
|
|
@@ -15633,7 +13447,7 @@ async function detectProjectOwnedFiles(cwd) {
|
|
|
15633
13447
|
async function isReleaseWorkflowEnabled(cwd) {
|
|
15634
13448
|
try {
|
|
15635
13449
|
const configPath = resolve5(cwd, ".pourkit", "config.json");
|
|
15636
|
-
const content = await
|
|
13450
|
+
const content = await readFile6(configPath, "utf-8");
|
|
15637
13451
|
const parsed = JSON.parse(content);
|
|
15638
13452
|
return parsed.releaseWorkflow?.enabled === true;
|
|
15639
13453
|
} catch {
|
|
@@ -15773,12 +13587,12 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15773
13587
|
const fullManagedBlock = `${MANAGED_BLOCK_BEGIN3}${managedBlockContent}${MANAGED_BLOCK_END2}`;
|
|
15774
13588
|
async function syncManagedAgentFile(fileName, options2) {
|
|
15775
13589
|
const filePath = resolve5(cwd, fileName);
|
|
15776
|
-
if (!
|
|
13590
|
+
if (!existsSync18(filePath)) {
|
|
15777
13591
|
if (!options2.createIfMissing) return false;
|
|
15778
|
-
await
|
|
13592
|
+
await writeFile3(filePath, fullManagedBlock + "\n", "utf-8");
|
|
15779
13593
|
return true;
|
|
15780
13594
|
}
|
|
15781
|
-
const existing = await
|
|
13595
|
+
const existing = await readFile6(filePath, "utf-8");
|
|
15782
13596
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN3);
|
|
15783
13597
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END2);
|
|
15784
13598
|
if (beginIdx !== -1 && endIdx !== -1) {
|
|
@@ -15786,7 +13600,7 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15786
13600
|
const afterBlock = existing.slice(endIdx + MANAGED_BLOCK_END2.length);
|
|
15787
13601
|
const newContent = `${beforeBlock}${fullManagedBlock}${afterBlock}`;
|
|
15788
13602
|
if (newContent !== existing) {
|
|
15789
|
-
await
|
|
13603
|
+
await writeFile3(filePath, newContent, "utf-8");
|
|
15790
13604
|
return true;
|
|
15791
13605
|
}
|
|
15792
13606
|
} else {
|
|
@@ -15795,7 +13609,7 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15795
13609
|
${fullManagedBlock}
|
|
15796
13610
|
`;
|
|
15797
13611
|
if (newContent !== existing) {
|
|
15798
|
-
await
|
|
13612
|
+
await writeFile3(filePath, newContent, "utf-8");
|
|
15799
13613
|
return true;
|
|
15800
13614
|
}
|
|
15801
13615
|
}
|
|
@@ -15813,7 +13627,7 @@ ${fullManagedBlock}
|
|
|
15813
13627
|
let manifestWritten = false;
|
|
15814
13628
|
let previousManifestWorkflowPack = void 0;
|
|
15815
13629
|
try {
|
|
15816
|
-
const existingRaw = await
|
|
13630
|
+
const existingRaw = await readFile6(manifestPath, "utf-8").catch(() => null);
|
|
15817
13631
|
if (existingRaw) {
|
|
15818
13632
|
const existing = JSON.parse(existingRaw);
|
|
15819
13633
|
previousManifestWorkflowPack = existing.workflowPack;
|
|
@@ -15822,13 +13636,13 @@ ${fullManagedBlock}
|
|
|
15822
13636
|
}
|
|
15823
13637
|
if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
|
|
15824
13638
|
try {
|
|
15825
|
-
const existingRaw = await
|
|
13639
|
+
const existingRaw = await readFile6(manifestPath, "utf-8").catch(
|
|
15826
13640
|
() => null
|
|
15827
13641
|
);
|
|
15828
13642
|
let manifest = existingRaw ? JSON.parse(existingRaw) : {};
|
|
15829
13643
|
manifest.workflowPack = manifestMeta;
|
|
15830
|
-
await
|
|
15831
|
-
await
|
|
13644
|
+
await mkdir6(resolve5(manifestPath, ".."), { recursive: true });
|
|
13645
|
+
await writeFile3(
|
|
15832
13646
|
manifestPath,
|
|
15833
13647
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
15834
13648
|
"utf-8"
|
|
@@ -16548,22 +14362,22 @@ function formatChecks2(checks) {
|
|
|
16548
14362
|
init_common();
|
|
16549
14363
|
|
|
16550
14364
|
// execution/sandcastle-execution.ts
|
|
16551
|
-
import { existsSync as
|
|
16552
|
-
import { join as
|
|
14365
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
14366
|
+
import { join as join20 } from "path";
|
|
16553
14367
|
import { createWorktree, opencode } from "@ai-hero/sandcastle";
|
|
16554
14368
|
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
16555
14369
|
|
|
16556
14370
|
// execution/execution-provider.ts
|
|
16557
14371
|
init_common();
|
|
16558
14372
|
import { mkdtempSync } from "fs";
|
|
16559
|
-
import { writeFile as
|
|
14373
|
+
import { writeFile as writeFile4 } from "fs/promises";
|
|
16560
14374
|
import { tmpdir } from "os";
|
|
16561
|
-
import { dirname as dirname7, join as
|
|
14375
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
16562
14376
|
async function writeExecutionArtifacts(worktreePath, artifacts) {
|
|
16563
14377
|
for (const artifact of artifacts) {
|
|
16564
|
-
const filePath =
|
|
14378
|
+
const filePath = join18(worktreePath, artifact.path);
|
|
16565
14379
|
await ensureDir(dirname7(filePath));
|
|
16566
|
-
await
|
|
14380
|
+
await writeFile4(filePath, artifact.content, "utf-8");
|
|
16567
14381
|
}
|
|
16568
14382
|
}
|
|
16569
14383
|
|
|
@@ -16575,19 +14389,19 @@ init_common();
|
|
|
16575
14389
|
import path7 from "path";
|
|
16576
14390
|
|
|
16577
14391
|
// execution/sandbox-image.ts
|
|
16578
|
-
import { createHash as
|
|
16579
|
-
import { existsSync as
|
|
14392
|
+
import { createHash as createHash2 } from "crypto";
|
|
14393
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "fs";
|
|
16580
14394
|
import path6 from "path";
|
|
16581
14395
|
function sandboxImageName(repoRoot2, installIcm) {
|
|
16582
14396
|
const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
|
|
16583
14397
|
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
16584
14398
|
const baseName = sanitized || "local";
|
|
16585
14399
|
const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
|
|
16586
|
-
if (!
|
|
14400
|
+
if (!existsSync19(dockerfilePath)) {
|
|
16587
14401
|
const base2 = `sandcastle:${baseName}`;
|
|
16588
14402
|
return installIcm ? `${base2}-icm` : base2;
|
|
16589
14403
|
}
|
|
16590
|
-
const fingerprint =
|
|
14404
|
+
const fingerprint = createHash2("sha256").update(readFileSync19(dockerfilePath)).digest("hex").slice(0, 8);
|
|
16591
14405
|
const base = `sandcastle:${baseName}-${fingerprint}`;
|
|
16592
14406
|
return installIcm ? `${base}-icm` : base;
|
|
16593
14407
|
}
|
|
@@ -16623,7 +14437,7 @@ async function createSandboxFromExistingWorktree(options) {
|
|
|
16623
14437
|
}
|
|
16624
14438
|
|
|
16625
14439
|
// execution/sandbox-options.ts
|
|
16626
|
-
import { join as
|
|
14440
|
+
import { join as join19 } from "path";
|
|
16627
14441
|
var POURKIT_ICM_CONTAINER_DIR = "/home/agent/.pourkit/icm";
|
|
16628
14442
|
function buildSandboxOptions(repoRoot2, sandbox, memory) {
|
|
16629
14443
|
const mounts = [];
|
|
@@ -16632,12 +14446,12 @@ function buildSandboxOptions(repoRoot2, sandbox, memory) {
|
|
|
16632
14446
|
}
|
|
16633
14447
|
if (memory?.available) {
|
|
16634
14448
|
mounts.push({
|
|
16635
|
-
hostPath:
|
|
14449
|
+
hostPath: join19(repoRoot2, ".pourkit", "icm"),
|
|
16636
14450
|
sandboxPath: POURKIT_ICM_CONTAINER_DIR,
|
|
16637
14451
|
readonly: false
|
|
16638
14452
|
});
|
|
16639
14453
|
mounts.push({
|
|
16640
|
-
hostPath:
|
|
14454
|
+
hostPath: join19(repoRoot2, ".pourkit", "icm", "cache"),
|
|
16641
14455
|
sandboxPath: "/home/agent/.cache/icm",
|
|
16642
14456
|
readonly: false
|
|
16643
14457
|
});
|
|
@@ -16771,13 +14585,13 @@ var SandcastleExecutionSession = class {
|
|
|
16771
14585
|
"Memory enabled but memory context is invalid. Run `pourkit memory init` or install ICM for host planning use."
|
|
16772
14586
|
);
|
|
16773
14587
|
}
|
|
16774
|
-
const hostMemoryDir =
|
|
16775
|
-
if (!
|
|
14588
|
+
const hostMemoryDir = join20(repoRoot2, ".pourkit", "icm");
|
|
14589
|
+
if (!existsSync20(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
|
|
16776
14590
|
throw new MissingHostMemoryDirError(
|
|
16777
14591
|
"Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
|
|
16778
14592
|
);
|
|
16779
14593
|
}
|
|
16780
|
-
|
|
14594
|
+
mkdirSync9(join20(hostMemoryDir, "cache"), { recursive: true });
|
|
16781
14595
|
}
|
|
16782
14596
|
const sandboxOptions = buildSandboxOptions(
|
|
16783
14597
|
repoRoot2,
|
|
@@ -16936,8 +14750,8 @@ function sanitizeBranch(branchName) {
|
|
|
16936
14750
|
}
|
|
16937
14751
|
function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
16938
14752
|
return paths.filter((relativePath) => {
|
|
16939
|
-
const source =
|
|
16940
|
-
if (!
|
|
14753
|
+
const source = join20(repoRoot2, relativePath);
|
|
14754
|
+
if (!existsSync20(source)) {
|
|
16941
14755
|
return true;
|
|
16942
14756
|
}
|
|
16943
14757
|
try {
|
|
@@ -16947,8 +14761,8 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
|
16947
14761
|
} catch {
|
|
16948
14762
|
return true;
|
|
16949
14763
|
}
|
|
16950
|
-
const destination =
|
|
16951
|
-
return !
|
|
14764
|
+
const destination = join20(worktreePath, relativePath);
|
|
14765
|
+
return !existsSync20(destination);
|
|
16952
14766
|
});
|
|
16953
14767
|
}
|
|
16954
14768
|
function formatAgentStreamEvent(event) {
|
|
@@ -17012,13 +14826,13 @@ function isPlainObject(value) {
|
|
|
17012
14826
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17013
14827
|
}
|
|
17014
14828
|
function savePromptToFile(repoRoot2, stage, iteration, prompt) {
|
|
17015
|
-
const promptsDir =
|
|
17016
|
-
|
|
14829
|
+
const promptsDir = join20(repoRoot2, ".pourkit", ".tmp", "prompts");
|
|
14830
|
+
mkdirSync9(promptsDir, { recursive: true });
|
|
17017
14831
|
const timestamp2 = Date.now();
|
|
17018
14832
|
const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
|
|
17019
14833
|
const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
|
|
17020
|
-
const filePath =
|
|
17021
|
-
|
|
14834
|
+
const filePath = join20(promptsDir, filename);
|
|
14835
|
+
writeFileSync6(filePath, prompt, "utf-8");
|
|
17022
14836
|
}
|
|
17023
14837
|
|
|
17024
14838
|
// cli.ts
|
|
@@ -17118,8 +14932,8 @@ function createCliProgram(version) {
|
|
|
17118
14932
|
});
|
|
17119
14933
|
program.command("validate-artifact").description("Validate an agent handoff artifact").argument(
|
|
17120
14934
|
"<kind>",
|
|
17121
|
-
"artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution,
|
|
17122
|
-
).argument("<artifactPath>", "artifact path to validate").argument("[extraPaths...]", "
|
|
14935
|
+
"artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution, or issue-final-review"
|
|
14936
|
+
).argument("<artifactPath>", "artifact path to validate").argument("[extraPaths...]", "unused compatibility argument").option("--iteration <number>", "review/refactor iteration", (value) => {
|
|
17123
14937
|
const parsed = Number.parseInt(value, 10);
|
|
17124
14938
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
17125
14939
|
throw new CommanderError(
|
|
@@ -17172,16 +14986,13 @@ function createCliProgram(version) {
|
|
|
17172
14986
|
"--base-ref <ref>",
|
|
17173
14987
|
"canonical base ref for issue-final-review changedFiles coverage validation"
|
|
17174
14988
|
).option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
|
|
17175
|
-
(kind, artifactPath,
|
|
14989
|
+
(kind, artifactPath, _extraPaths, options) => {
|
|
17176
14990
|
const allowedKinds = /* @__PURE__ */ new Set([
|
|
17177
14991
|
"reviewer",
|
|
17178
14992
|
"refactor",
|
|
17179
14993
|
"finalizer",
|
|
17180
14994
|
"conflict-resolution",
|
|
17181
14995
|
"failure-resolution",
|
|
17182
|
-
"local-prd",
|
|
17183
|
-
"local-issue",
|
|
17184
|
-
"local-triage",
|
|
17185
14996
|
"issue-final-review"
|
|
17186
14997
|
]);
|
|
17187
14998
|
if (!allowedKinds.has(kind)) {
|
|
@@ -17191,18 +15002,8 @@ function createCliProgram(version) {
|
|
|
17191
15002
|
`Unsupported artifact kind "${kind}"`
|
|
17192
15003
|
);
|
|
17193
15004
|
}
|
|
17194
|
-
const LOCAL_KINDS = /* @__PURE__ */ new Set([
|
|
17195
|
-
"local-prd",
|
|
17196
|
-
"local-issue",
|
|
17197
|
-
"local-triage"
|
|
17198
|
-
]);
|
|
17199
15005
|
const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
|
|
17200
|
-
const result =
|
|
17201
|
-
repoRoot: targetRepoRoot,
|
|
17202
|
-
kind,
|
|
17203
|
-
artifactPath,
|
|
17204
|
-
extraArgs: extraPaths
|
|
17205
|
-
}) : runValidateArtifactCommand({
|
|
15006
|
+
const result = runValidateArtifactCommand({
|
|
17206
15007
|
repoRoot: targetRepoRoot,
|
|
17207
15008
|
kind,
|
|
17208
15009
|
artifactPath,
|
|
@@ -17485,15 +15286,14 @@ function createCliProgram(version) {
|
|
|
17485
15286
|
const logger = createLogger("pourkit", logPath);
|
|
17486
15287
|
try {
|
|
17487
15288
|
const config = await loadRepoConfig(targetRepoRoot);
|
|
17488
|
-
|
|
17489
|
-
const
|
|
17490
|
-
const
|
|
17491
|
-
const issueProvider = client ? new GitHubIssueProvider(client, {
|
|
15289
|
+
resolveTarget(config, options.target);
|
|
15290
|
+
const client = await requireGitHubClient({ cwd: targetRepoRoot });
|
|
15291
|
+
const issueProvider = new GitHubIssueProvider(client, {
|
|
17492
15292
|
readyForAgentLabel: config.labels.readyForAgent,
|
|
17493
15293
|
blockedLabel: config.labels.blocked,
|
|
17494
15294
|
issueListLimit: config.checks.issueListLimit
|
|
17495
|
-
})
|
|
17496
|
-
const prProvider =
|
|
15295
|
+
});
|
|
15296
|
+
const prProvider = new GitHubPRProvider(client, logger);
|
|
17497
15297
|
const executionProvider = new SandcastleExecutionProvider();
|
|
17498
15298
|
const result = await runPrdRunLaunchCommand({
|
|
17499
15299
|
repoRoot: targetRepoRoot,
|
|
@@ -17752,11 +15552,11 @@ function createCliProgram(version) {
|
|
|
17752
15552
|
return program;
|
|
17753
15553
|
}
|
|
17754
15554
|
async function resolveCliVersion() {
|
|
17755
|
-
if (isPackageVersion("0.0.0-next-
|
|
17756
|
-
return "0.0.0-next-
|
|
15555
|
+
if (isPackageVersion("0.0.0-next-20260619094345")) {
|
|
15556
|
+
return "0.0.0-next-20260619094345";
|
|
17757
15557
|
}
|
|
17758
|
-
if (isReleaseVersion("0.0.0-next-
|
|
17759
|
-
return "0.0.0-next-
|
|
15558
|
+
if (isReleaseVersion("0.0.0-next-20260619094345")) {
|
|
15559
|
+
return "0.0.0-next-20260619094345";
|
|
17760
15560
|
}
|
|
17761
15561
|
try {
|
|
17762
15562
|
const root = repoRoot();
|