@pourkit/cli 0.0.0-next-20260619074943 → 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 +420 -2653
- 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,300 +4882,12 @@ 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(
|
|
4887
|
+
yield* fs.writeFile(join11(dir, "generated.md"), output).pipe(Effect6.catchAll(() => Effect6.void));
|
|
5662
4888
|
});
|
|
5663
4889
|
}
|
|
5664
4890
|
|
|
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"
|
|
5711
|
-
});
|
|
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
|
-
}
|
|
5952
|
-
|
|
5953
4891
|
// pr/pr-body.ts
|
|
5954
4892
|
import { readFile as readFile2 } from "fs/promises";
|
|
5955
4893
|
var DEFAULT_MANUAL_PR_BODY = `## Summary
|
|
@@ -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;
|
|
@@ -7498,150 +6413,25 @@ async function completeIssueRun(options) {
|
|
|
7498
6413
|
const finalCommitFromState = worktreeState?.finalCommit?.completed;
|
|
7499
6414
|
if (!finalCommitFromState) {
|
|
7500
6415
|
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
|
-
);
|
|
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"],
|
|
@@ -9936,51 +8118,25 @@ function resolvePrdRunBaseBranch(options) {
|
|
|
9936
8118
|
return {
|
|
9937
8119
|
ok: false,
|
|
9938
8120
|
gate: "branch-state",
|
|
9939
|
-
reason: `Invalid PRD Run base branch: "${trimmed}" must not end with a trailing slash.`,
|
|
9940
|
-
diagnostics: [`Provided branch value: "${trimmed}"`],
|
|
9941
|
-
offendingPaths: []
|
|
9942
|
-
};
|
|
9943
|
-
}
|
|
9944
|
-
if (trimmed.endsWith(".lock")) {
|
|
9945
|
-
return {
|
|
9946
|
-
ok: false,
|
|
9947
|
-
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
|
-
}
|
|
8121
|
+
reason: `Invalid PRD Run base branch: "${trimmed}" must not end with a trailing slash.`,
|
|
8122
|
+
diagnostics: [`Provided branch value: "${trimmed}"`],
|
|
8123
|
+
offendingPaths: []
|
|
8124
|
+
};
|
|
9967
8125
|
}
|
|
9968
|
-
if (
|
|
8126
|
+
if (trimmed.endsWith(".lock")) {
|
|
9969
8127
|
return {
|
|
9970
8128
|
ok: false,
|
|
9971
8129
|
gate: "branch-state",
|
|
9972
|
-
reason: `
|
|
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";
|
|
@@ -12301,7 +10082,7 @@ function resolveSourceAssetPath(sourceRoot, ...segments) {
|
|
|
12301
10082
|
path5.join(sourceRoot, "dist", ...segments)
|
|
12302
10083
|
];
|
|
12303
10084
|
for (const candidatePath of candidatePaths) {
|
|
12304
|
-
if (
|
|
10085
|
+
if (existsSync15(candidatePath)) return candidatePath;
|
|
12305
10086
|
}
|
|
12306
10087
|
return path5.join(sourceRoot, "pourkit", ...segments);
|
|
12307
10088
|
}
|
|
@@ -12518,7 +10299,7 @@ function generateConfigTemplate(options) {
|
|
|
12518
10299
|
}
|
|
12519
10300
|
const config = {
|
|
12520
10301
|
$schema: "./schema/pourkit.schema.json",
|
|
12521
|
-
schemaVersion:
|
|
10302
|
+
schemaVersion: 2,
|
|
12522
10303
|
targets: [target],
|
|
12523
10304
|
workflowPack: {
|
|
12524
10305
|
version: "1.0.0",
|
|
@@ -12707,11 +10488,11 @@ async function walkDir(dir) {
|
|
|
12707
10488
|
return files;
|
|
12708
10489
|
}
|
|
12709
10490
|
async function computeFileChecksum(filePath) {
|
|
12710
|
-
const content = await
|
|
12711
|
-
return
|
|
10491
|
+
const content = await readFile4(filePath);
|
|
10492
|
+
return createHash("sha256").update(content).digest("hex");
|
|
12712
10493
|
}
|
|
12713
10494
|
function lockfileExists(root, name) {
|
|
12714
|
-
return
|
|
10495
|
+
return existsSync15(path5.join(root, name));
|
|
12715
10496
|
}
|
|
12716
10497
|
function detectPackageManager(root) {
|
|
12717
10498
|
if (lockfileExists(root, "pnpm-lock.yaml")) return "pnpm";
|
|
@@ -12755,7 +10536,7 @@ async function discoverLocalSource(sourcePath) {
|
|
|
12755
10536
|
async function discoverReadme(root) {
|
|
12756
10537
|
for (const name of ["README.md", "readme.md"]) {
|
|
12757
10538
|
const p = path5.join(root, name);
|
|
12758
|
-
if (
|
|
10539
|
+
if (existsSync15(p)) {
|
|
12759
10540
|
return p;
|
|
12760
10541
|
}
|
|
12761
10542
|
}
|
|
@@ -12765,7 +10546,7 @@ async function discoverAgentFiles(root) {
|
|
|
12765
10546
|
const files = [];
|
|
12766
10547
|
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
12767
10548
|
const p = path5.join(root, name);
|
|
12768
|
-
if (
|
|
10549
|
+
if (existsSync15(p)) {
|
|
12769
10550
|
files.push(p);
|
|
12770
10551
|
}
|
|
12771
10552
|
}
|
|
@@ -12773,7 +10554,7 @@ async function discoverAgentFiles(root) {
|
|
|
12773
10554
|
}
|
|
12774
10555
|
async function discoverMerlleState(root) {
|
|
12775
10556
|
const p = path5.join(root, ".pourkit", "state.json");
|
|
12776
|
-
return
|
|
10557
|
+
return existsSync15(p) ? p : null;
|
|
12777
10558
|
}
|
|
12778
10559
|
async function discoverAgentSkills(root) {
|
|
12779
10560
|
const dirs = [
|
|
@@ -12782,7 +10563,7 @@ async function discoverAgentSkills(root) {
|
|
|
12782
10563
|
];
|
|
12783
10564
|
const found = [];
|
|
12784
10565
|
for (const d of dirs) {
|
|
12785
|
-
if (
|
|
10566
|
+
if (existsSync15(d)) {
|
|
12786
10567
|
found.push(d);
|
|
12787
10568
|
}
|
|
12788
10569
|
}
|
|
@@ -12792,12 +10573,12 @@ async function discoverRootDomainDocs(root) {
|
|
|
12792
10573
|
const docs = [];
|
|
12793
10574
|
for (const name of ["CONTEXT.md", "CONTEXT-MAP.md"]) {
|
|
12794
10575
|
const p = path5.join(root, name);
|
|
12795
|
-
if (
|
|
10576
|
+
if (existsSync15(p)) {
|
|
12796
10577
|
docs.push(p);
|
|
12797
10578
|
}
|
|
12798
10579
|
}
|
|
12799
10580
|
const adrDir = path5.join(root, "docs", "adr");
|
|
12800
|
-
if (
|
|
10581
|
+
if (existsSync15(adrDir)) {
|
|
12801
10582
|
const entries = await readdir(adrDir, { withFileTypes: true });
|
|
12802
10583
|
for (const entry of entries) {
|
|
12803
10584
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -12939,7 +10720,7 @@ async function planInit(options) {
|
|
|
12939
10720
|
for (const file of skillFiles) {
|
|
12940
10721
|
const relPath = path5.relative(s, file);
|
|
12941
10722
|
const destPath = path5.join(targetRoot, ".agents", "skills", relPath);
|
|
12942
|
-
if (!
|
|
10723
|
+
if (!existsSync15(destPath)) {
|
|
12943
10724
|
operations.push({
|
|
12944
10725
|
kind: "copy",
|
|
12945
10726
|
sourcePath: file,
|
|
@@ -13002,7 +10783,7 @@ async function planInit(options) {
|
|
|
13002
10783
|
});
|
|
13003
10784
|
}
|
|
13004
10785
|
if (sourceRoot) {
|
|
13005
|
-
if (!
|
|
10786
|
+
if (!existsSync15(sourceRoot) || !statSync(sourceRoot).isDirectory()) {
|
|
13006
10787
|
warnings.push(
|
|
13007
10788
|
`--from-local path does not exist or is not a directory: ${sourceRoot}`
|
|
13008
10789
|
);
|
|
@@ -13067,9 +10848,9 @@ async function planInit(options) {
|
|
|
13067
10848
|
"release",
|
|
13068
10849
|
"skills"
|
|
13069
10850
|
);
|
|
13070
|
-
const srcSkills =
|
|
10851
|
+
const srcSkills = existsSync15(packagedManagedSkills) ? [
|
|
13071
10852
|
packagedManagedSkills,
|
|
13072
|
-
...
|
|
10853
|
+
...existsSync15(packagedReleaseAddonSkills) ? [packagedReleaseAddonSkills] : []
|
|
13073
10854
|
] : await discoverAgentSkills(sourceRoot);
|
|
13074
10855
|
for (const s of srcSkills) {
|
|
13075
10856
|
const isOpenCode = s.includes(".opencode");
|
|
@@ -13092,7 +10873,7 @@ async function planInit(options) {
|
|
|
13092
10873
|
"skills",
|
|
13093
10874
|
relPath
|
|
13094
10875
|
);
|
|
13095
|
-
if (!
|
|
10876
|
+
if (!existsSync15(destPath)) {
|
|
13096
10877
|
operations.push({
|
|
13097
10878
|
kind: "copy",
|
|
13098
10879
|
sourcePath: file,
|
|
@@ -13179,7 +10960,7 @@ async function planInit(options) {
|
|
|
13179
10960
|
});
|
|
13180
10961
|
continue;
|
|
13181
10962
|
}
|
|
13182
|
-
const sourceContent = await
|
|
10963
|
+
const sourceContent = await readFile4(file, "utf-8");
|
|
13183
10964
|
const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
|
|
13184
10965
|
operations.push({
|
|
13185
10966
|
kind: "create",
|
|
@@ -13224,7 +11005,7 @@ async function planInit(options) {
|
|
|
13224
11005
|
requiresConfirmation: false,
|
|
13225
11006
|
destructive: false
|
|
13226
11007
|
});
|
|
13227
|
-
} else if (
|
|
11008
|
+
} else if (existsSync15(destPath)) {
|
|
13228
11009
|
operations.push({
|
|
13229
11010
|
kind: "skip",
|
|
13230
11011
|
path: destPath,
|
|
@@ -13259,7 +11040,7 @@ async function planInit(options) {
|
|
|
13259
11040
|
let packageScripts = {};
|
|
13260
11041
|
let hasPackageJson = true;
|
|
13261
11042
|
try {
|
|
13262
|
-
const pkgContent = await
|
|
11043
|
+
const pkgContent = await readFile4(
|
|
13263
11044
|
path5.join(targetRoot, "package.json"),
|
|
13264
11045
|
"utf-8"
|
|
13265
11046
|
);
|
|
@@ -13282,7 +11063,7 @@ async function planInit(options) {
|
|
|
13282
11063
|
}
|
|
13283
11064
|
}
|
|
13284
11065
|
const contextPath = path5.join(targetRoot, ".pourkit", "CONTEXT.md");
|
|
13285
|
-
if (!
|
|
11066
|
+
if (!existsSync15(contextPath) && !merleDestPaths.has(contextPath)) {
|
|
13286
11067
|
operations.push({
|
|
13287
11068
|
kind: "create",
|
|
13288
11069
|
path: contextPath,
|
|
@@ -13300,7 +11081,7 @@ async function planInit(options) {
|
|
|
13300
11081
|
"adr",
|
|
13301
11082
|
".gitkeep"
|
|
13302
11083
|
);
|
|
13303
|
-
if (!
|
|
11084
|
+
if (!existsSync15(adrGitkeep)) {
|
|
13304
11085
|
operations.push({
|
|
13305
11086
|
kind: "create",
|
|
13306
11087
|
path: adrGitkeep,
|
|
@@ -13322,7 +11103,7 @@ async function planInit(options) {
|
|
|
13322
11103
|
"docs",
|
|
13323
11104
|
"agents"
|
|
13324
11105
|
);
|
|
13325
|
-
const srcDocAgents =
|
|
11106
|
+
const srcDocAgents = existsSync15(packagedManagedDocAgents) ? packagedManagedDocAgents : legacyDocAgents;
|
|
13326
11107
|
const tgtManagedDocAgents = path5.join(
|
|
13327
11108
|
targetRoot,
|
|
13328
11109
|
".pourkit",
|
|
@@ -13336,12 +11117,12 @@ async function planInit(options) {
|
|
|
13336
11117
|
"docs",
|
|
13337
11118
|
"agents"
|
|
13338
11119
|
);
|
|
13339
|
-
if (
|
|
11120
|
+
if (existsSync15(srcDocAgents)) {
|
|
13340
11121
|
const docFiles = await walkDir(srcDocAgents);
|
|
13341
11122
|
for (const file of docFiles) {
|
|
13342
11123
|
const relPath = path5.relative(srcDocAgents, file);
|
|
13343
11124
|
const managedDocDest = path5.join(tgtManagedDocAgents, relPath);
|
|
13344
|
-
if (!
|
|
11125
|
+
if (!existsSync15(managedDocDest) && relPath === "triage-labels.md") {
|
|
13345
11126
|
operations.push({
|
|
13346
11127
|
kind: "create",
|
|
13347
11128
|
path: managedDocDest,
|
|
@@ -13353,7 +11134,7 @@ async function planInit(options) {
|
|
|
13353
11134
|
options.labels ?? DEFAULT_RUNNER_LABELS
|
|
13354
11135
|
)
|
|
13355
11136
|
});
|
|
13356
|
-
} else if (!
|
|
11137
|
+
} else if (!existsSync15(managedDocDest)) {
|
|
13357
11138
|
const checksum = await computeFileChecksum(file);
|
|
13358
11139
|
operations.push({
|
|
13359
11140
|
kind: "copy",
|
|
@@ -13375,7 +11156,7 @@ async function planInit(options) {
|
|
|
13375
11156
|
relPath
|
|
13376
11157
|
);
|
|
13377
11158
|
const stubDocDest = path5.join(tgtStubDocAgents, relPath);
|
|
13378
|
-
if (
|
|
11159
|
+
if (existsSync15(stubDocDest)) continue;
|
|
13379
11160
|
operations.push({
|
|
13380
11161
|
kind: "create",
|
|
13381
11162
|
path: stubDocDest,
|
|
@@ -13409,7 +11190,7 @@ Do not edit this file.
|
|
|
13409
11190
|
"docs",
|
|
13410
11191
|
"agents"
|
|
13411
11192
|
);
|
|
13412
|
-
if (
|
|
11193
|
+
if (existsSync15(packagedReleaseAddonDocs)) {
|
|
13413
11194
|
const docFiles = await walkDir(packagedReleaseAddonDocs);
|
|
13414
11195
|
for (const file of docFiles) {
|
|
13415
11196
|
const relPath = path5.relative(packagedReleaseAddonDocs, file);
|
|
@@ -13433,19 +11214,19 @@ Do not edit this file.
|
|
|
13433
11214
|
"prompts"
|
|
13434
11215
|
);
|
|
13435
11216
|
const legacyPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
|
|
13436
|
-
const srcPrompts =
|
|
11217
|
+
const srcPrompts = existsSync15(packagedManagedPrompts) ? packagedManagedPrompts : legacyPrompts;
|
|
13437
11218
|
const tgtManagedPrompts = path5.join(
|
|
13438
11219
|
targetRoot,
|
|
13439
11220
|
".pourkit",
|
|
13440
11221
|
"managed",
|
|
13441
11222
|
"prompts"
|
|
13442
11223
|
);
|
|
13443
|
-
if (
|
|
11224
|
+
if (existsSync15(srcPrompts)) {
|
|
13444
11225
|
const promptFiles = await walkDir(srcPrompts);
|
|
13445
11226
|
for (const file of promptFiles) {
|
|
13446
11227
|
const relPath = path5.relative(srcPrompts, file);
|
|
13447
11228
|
const promptDest = path5.join(tgtManagedPrompts, relPath);
|
|
13448
|
-
if (
|
|
11229
|
+
if (existsSync15(promptDest)) continue;
|
|
13449
11230
|
const checksum = await computeFileChecksum(file);
|
|
13450
11231
|
operations.push({
|
|
13451
11232
|
kind: "copy",
|
|
@@ -13469,7 +11250,7 @@ Do not edit this file.
|
|
|
13469
11250
|
".sandcastle",
|
|
13470
11251
|
"Dockerfile"
|
|
13471
11252
|
);
|
|
13472
|
-
if (
|
|
11253
|
+
if (existsSync15(tgtSandboxDockerfile)) {
|
|
13473
11254
|
operations.push({
|
|
13474
11255
|
kind: "skip",
|
|
13475
11256
|
path: tgtSandboxDockerfile,
|
|
@@ -13478,7 +11259,7 @@ Do not edit this file.
|
|
|
13478
11259
|
requiresConfirmation: false,
|
|
13479
11260
|
destructive: false
|
|
13480
11261
|
});
|
|
13481
|
-
} else if (
|
|
11262
|
+
} else if (existsSync15(srcSandboxDockerfile)) {
|
|
13482
11263
|
const checksum = await computeFileChecksum(srcSandboxDockerfile);
|
|
13483
11264
|
operations.push({
|
|
13484
11265
|
kind: "copy",
|
|
@@ -13492,7 +11273,7 @@ Do not edit this file.
|
|
|
13492
11273
|
});
|
|
13493
11274
|
}
|
|
13494
11275
|
const configJsonPath = path5.join(targetRoot, ".pourkit", "config.json");
|
|
13495
|
-
if (!
|
|
11276
|
+
if (!existsSync15(configJsonPath)) {
|
|
13496
11277
|
const verifyCommands = inferVerificationCommands(
|
|
13497
11278
|
packageScripts,
|
|
13498
11279
|
pm || "npm"
|
|
@@ -13536,9 +11317,9 @@ Do not edit this file.
|
|
|
13536
11317
|
"schema",
|
|
13537
11318
|
"pourkit.schema.json"
|
|
13538
11319
|
);
|
|
13539
|
-
if (
|
|
11320
|
+
if (existsSync15(srcSchemaJson)) {
|
|
13540
11321
|
const checksum = await computeFileChecksum(srcSchemaJson);
|
|
13541
|
-
if (!
|
|
11322
|
+
if (!existsSync15(schemaJsonPath)) {
|
|
13542
11323
|
operations.push({
|
|
13543
11324
|
kind: "copy",
|
|
13544
11325
|
sourcePath: srcSchemaJson,
|
|
@@ -13571,9 +11352,9 @@ Do not edit this file.
|
|
|
13571
11352
|
"schema",
|
|
13572
11353
|
"pourkit.schema.hash"
|
|
13573
11354
|
);
|
|
13574
|
-
if (
|
|
11355
|
+
if (existsSync15(srcSchemaHash)) {
|
|
13575
11356
|
const checksum = await computeFileChecksum(srcSchemaHash);
|
|
13576
|
-
if (!
|
|
11357
|
+
if (!existsSync15(schemaHashPath)) {
|
|
13577
11358
|
operations.push({
|
|
13578
11359
|
kind: "copy",
|
|
13579
11360
|
sourcePath: srcSchemaHash,
|
|
@@ -13599,7 +11380,7 @@ Do not edit this file.
|
|
|
13599
11380
|
const hasExistingAgents = operations.some(
|
|
13600
11381
|
(op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("AGENTS.md")
|
|
13601
11382
|
);
|
|
13602
|
-
if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !
|
|
11383
|
+
if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !existsSync15(path5.join(targetRoot, "AGENTS.md"))) {
|
|
13603
11384
|
operations.push({
|
|
13604
11385
|
kind: "create",
|
|
13605
11386
|
path: path5.join(targetRoot, "AGENTS.md"),
|
|
@@ -13615,7 +11396,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
|
|
|
13615
11396
|
const hasExistingClaude = operations.some(
|
|
13616
11397
|
(op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("CLAUDE.md")
|
|
13617
11398
|
);
|
|
13618
|
-
if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !
|
|
11399
|
+
if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !existsSync15(path5.join(targetRoot, "CLAUDE.md"))) {
|
|
13619
11400
|
operations.push({
|
|
13620
11401
|
kind: "create",
|
|
13621
11402
|
path: path5.join(targetRoot, "CLAUDE.md"),
|
|
@@ -13630,7 +11411,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
|
|
|
13630
11411
|
}
|
|
13631
11412
|
const gitignoreTarget = path5.join(targetRoot, ".gitignore");
|
|
13632
11413
|
const gitignoreContent = generateGitignoreBlock();
|
|
13633
|
-
if (!
|
|
11414
|
+
if (!existsSync15(gitignoreTarget)) {
|
|
13634
11415
|
operations.push({
|
|
13635
11416
|
kind: "create",
|
|
13636
11417
|
path: gitignoreTarget,
|
|
@@ -13654,7 +11435,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13654
11435
|
});
|
|
13655
11436
|
}
|
|
13656
11437
|
const openCodePath = path5.join(targetRoot, "opencode.json");
|
|
13657
|
-
if (!
|
|
11438
|
+
if (!existsSync15(openCodePath)) {
|
|
13658
11439
|
operations.push({
|
|
13659
11440
|
kind: "create",
|
|
13660
11441
|
path: openCodePath,
|
|
@@ -13666,7 +11447,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13666
11447
|
});
|
|
13667
11448
|
} else {
|
|
13668
11449
|
try {
|
|
13669
|
-
const existingContent = await
|
|
11450
|
+
const existingContent = await readFile4(openCodePath, "utf-8");
|
|
13670
11451
|
const existingConfig = JSON.parse(existingContent);
|
|
13671
11452
|
if (typeof existingConfig !== "object" || existingConfig === null || Array.isArray(existingConfig)) {
|
|
13672
11453
|
warnings.push(
|
|
@@ -13711,7 +11492,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13711
11492
|
}
|
|
13712
11493
|
}
|
|
13713
11494
|
const manifestPath = path5.join(targetRoot, ".pourkit", "manifest.json");
|
|
13714
|
-
if (
|
|
11495
|
+
if (existsSync15(manifestPath)) {
|
|
13715
11496
|
operations.push({
|
|
13716
11497
|
kind: "skip",
|
|
13717
11498
|
path: manifestPath,
|
|
@@ -14092,7 +11873,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
14092
11873
|
}
|
|
14093
11874
|
async function writeFileAtomic(filePath, content) {
|
|
14094
11875
|
const tmpPath = `${filePath}.tmp.${randomUUID()}`;
|
|
14095
|
-
await
|
|
11876
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
14096
11877
|
await rename(tmpPath, filePath);
|
|
14097
11878
|
}
|
|
14098
11879
|
var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
|
|
@@ -14101,13 +11882,13 @@ async function updateManagedBlock(filePath, content) {
|
|
|
14101
11882
|
const blockContent = `${MANAGED_BLOCK_BEGIN}
|
|
14102
11883
|
${content}${MANAGED_BLOCK_END}
|
|
14103
11884
|
`;
|
|
14104
|
-
if (!
|
|
11885
|
+
if (!existsSync15(filePath)) {
|
|
14105
11886
|
const dir = path5.dirname(filePath);
|
|
14106
|
-
await
|
|
11887
|
+
await mkdir4(dir, { recursive: true });
|
|
14107
11888
|
await writeFileAtomic(filePath, blockContent);
|
|
14108
11889
|
return;
|
|
14109
11890
|
}
|
|
14110
|
-
const existing = await
|
|
11891
|
+
const existing = await readFile4(filePath, "utf-8");
|
|
14111
11892
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
|
|
14112
11893
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
14113
11894
|
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
@@ -14130,7 +11911,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14130
11911
|
if (op.requiresConfirmation) continue;
|
|
14131
11912
|
const relPath = path5.relative(plan.targetRoot, op.path);
|
|
14132
11913
|
if (relPath === ".pourkit/manifest.json") continue;
|
|
14133
|
-
if (
|
|
11914
|
+
if (existsSync15(op.path)) {
|
|
14134
11915
|
const sha256 = await computeFileChecksum(op.path);
|
|
14135
11916
|
assets[relPath] = {
|
|
14136
11917
|
ownership: op.ownership || "managed",
|
|
@@ -14139,7 +11920,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14139
11920
|
}
|
|
14140
11921
|
}
|
|
14141
11922
|
const manifest = {
|
|
14142
|
-
schemaVersion:
|
|
11923
|
+
schemaVersion: 2,
|
|
14143
11924
|
initializedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14144
11925
|
pourkit: {
|
|
14145
11926
|
versionSource: sourceMeta.versionSource,
|
|
@@ -14155,7 +11936,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
14155
11936
|
workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
|
|
14156
11937
|
assets
|
|
14157
11938
|
};
|
|
14158
|
-
await
|
|
11939
|
+
await mkdir4(manifestDir, { recursive: true });
|
|
14159
11940
|
await writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
14160
11941
|
return manifest;
|
|
14161
11942
|
}
|
|
@@ -14176,12 +11957,12 @@ async function applyInitPlan(plan, options) {
|
|
|
14176
11957
|
skipped++;
|
|
14177
11958
|
continue;
|
|
14178
11959
|
}
|
|
14179
|
-
if (
|
|
11960
|
+
if (existsSync15(op.path) && !op.destructive) {
|
|
14180
11961
|
skipped++;
|
|
14181
11962
|
continue;
|
|
14182
11963
|
}
|
|
14183
11964
|
const dir = path5.dirname(op.path);
|
|
14184
|
-
await
|
|
11965
|
+
await mkdir4(dir, { recursive: true });
|
|
14185
11966
|
await writeFileAtomic(op.path, op.content ?? "");
|
|
14186
11967
|
applied++;
|
|
14187
11968
|
break;
|
|
@@ -14191,7 +11972,7 @@ async function applyInitPlan(plan, options) {
|
|
|
14191
11972
|
skipped++;
|
|
14192
11973
|
continue;
|
|
14193
11974
|
}
|
|
14194
|
-
if (
|
|
11975
|
+
if (existsSync15(op.path)) {
|
|
14195
11976
|
skipped++;
|
|
14196
11977
|
continue;
|
|
14197
11978
|
}
|
|
@@ -14200,7 +11981,7 @@ async function applyInitPlan(plan, options) {
|
|
|
14200
11981
|
skipped++;
|
|
14201
11982
|
} else {
|
|
14202
11983
|
const dir = path5.dirname(op.path);
|
|
14203
|
-
await
|
|
11984
|
+
await mkdir4(dir, { recursive: true });
|
|
14204
11985
|
await copyFile(op.sourcePath, op.path);
|
|
14205
11986
|
applied++;
|
|
14206
11987
|
}
|
|
@@ -14220,12 +12001,12 @@ async function applyInitPlan(plan, options) {
|
|
|
14220
12001
|
skipped++;
|
|
14221
12002
|
continue;
|
|
14222
12003
|
}
|
|
14223
|
-
if (
|
|
12004
|
+
if (existsSync15(op.path)) {
|
|
14224
12005
|
skipped++;
|
|
14225
12006
|
continue;
|
|
14226
12007
|
}
|
|
14227
12008
|
const dir = path5.dirname(op.path);
|
|
14228
|
-
await
|
|
12009
|
+
await mkdir4(dir, { recursive: true });
|
|
14229
12010
|
await rename(op.sourcePath, op.path);
|
|
14230
12011
|
applied++;
|
|
14231
12012
|
break;
|
|
@@ -14364,7 +12145,7 @@ async function applyInitFromSource(options) {
|
|
|
14364
12145
|
if (!manifestSkipped) {
|
|
14365
12146
|
const agentFiles = [];
|
|
14366
12147
|
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
14367
|
-
if (
|
|
12148
|
+
if (existsSync15(path5.join(targetRoot, name))) {
|
|
14368
12149
|
agentFiles.push(path5.join(targetRoot, name));
|
|
14369
12150
|
}
|
|
14370
12151
|
}
|
|
@@ -14540,18 +12321,18 @@ Init applied: ${result.applied} operations applied, ${result.skipped} skipped.`
|
|
|
14540
12321
|
}
|
|
14541
12322
|
|
|
14542
12323
|
// commands/memory-init.ts
|
|
14543
|
-
import { existsSync as
|
|
14544
|
-
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";
|
|
14545
12326
|
async function runMemoryInitCommand(options) {
|
|
14546
12327
|
const cwd = options.cwd ?? process.cwd();
|
|
14547
|
-
const configPath =
|
|
14548
|
-
if (!
|
|
12328
|
+
const configPath = join17(cwd, ".pourkit", "config.json");
|
|
12329
|
+
if (!existsSync16(configPath)) {
|
|
14549
12330
|
return {
|
|
14550
12331
|
ok: false,
|
|
14551
12332
|
message: "No .pourkit/config.json found. Run `pourkit init` first to initialize Pourkit, then run `pourkit memory init`."
|
|
14552
12333
|
};
|
|
14553
12334
|
}
|
|
14554
|
-
const raw = JSON.parse(
|
|
12335
|
+
const raw = JSON.parse(readFileSync17(configPath, "utf8"));
|
|
14555
12336
|
let alreadyEnabled = false;
|
|
14556
12337
|
if (Object.prototype.hasOwnProperty.call(raw, "memory")) {
|
|
14557
12338
|
const m = raw.memory;
|
|
@@ -14574,10 +12355,10 @@ async function runMemoryInitCommand(options) {
|
|
|
14574
12355
|
const memory = { enabled: true, provider: "icm" };
|
|
14575
12356
|
if (!alreadyEnabled) {
|
|
14576
12357
|
const next = { ...raw, memory };
|
|
14577
|
-
|
|
12358
|
+
writeFileSync5(configPath, JSON.stringify(next, null, 2) + "\n");
|
|
14578
12359
|
}
|
|
14579
|
-
|
|
14580
|
-
const gitignorePath =
|
|
12360
|
+
mkdirSync8(join17(cwd, ".pourkit", "icm"), { recursive: true });
|
|
12361
|
+
const gitignorePath = join17(cwd, ".gitignore");
|
|
14581
12362
|
updateManagedBlockSync(gitignorePath, generateGitignoreBlock());
|
|
14582
12363
|
const managed = await generateManagedAgentInstructions({
|
|
14583
12364
|
sourceRoot: cwd,
|
|
@@ -14585,14 +12366,14 @@ async function runMemoryInitCommand(options) {
|
|
|
14585
12366
|
});
|
|
14586
12367
|
let updatedAgentFile = false;
|
|
14587
12368
|
for (const fileName of ["AGENTS.md", "CLAUDE.md"]) {
|
|
14588
|
-
const agentPath =
|
|
14589
|
-
if (
|
|
12369
|
+
const agentPath = join17(cwd, fileName);
|
|
12370
|
+
if (existsSync16(agentPath)) {
|
|
14590
12371
|
updateManagedBlockSync(agentPath, managed);
|
|
14591
12372
|
updatedAgentFile = true;
|
|
14592
12373
|
}
|
|
14593
12374
|
}
|
|
14594
12375
|
if (!updatedAgentFile) {
|
|
14595
|
-
updateManagedBlockSync(
|
|
12376
|
+
updateManagedBlockSync(join17(cwd, "AGENTS.md"), managed);
|
|
14596
12377
|
}
|
|
14597
12378
|
return {
|
|
14598
12379
|
ok: true,
|
|
@@ -14604,21 +12385,21 @@ function updateManagedBlockSync(filePath, content) {
|
|
|
14604
12385
|
const blockContent = `${MANAGED_BLOCK_BEGIN}
|
|
14605
12386
|
${content}${MANAGED_BLOCK_END}
|
|
14606
12387
|
`;
|
|
14607
|
-
if (!
|
|
14608
|
-
|
|
12388
|
+
if (!existsSync16(filePath)) {
|
|
12389
|
+
writeFileSync5(filePath, blockContent);
|
|
14609
12390
|
return;
|
|
14610
12391
|
}
|
|
14611
|
-
const existing =
|
|
12392
|
+
const existing = readFileSync17(filePath, "utf8");
|
|
14612
12393
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
|
|
14613
12394
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
14614
12395
|
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
14615
12396
|
const before = existing.slice(0, beginIdx);
|
|
14616
12397
|
const after = existing.slice(endIdx + MANAGED_BLOCK_END.length);
|
|
14617
|
-
|
|
12398
|
+
writeFileSync5(filePath, before + blockContent + after);
|
|
14618
12399
|
return;
|
|
14619
12400
|
}
|
|
14620
12401
|
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
14621
|
-
|
|
12402
|
+
writeFileSync5(filePath, `${existing}${suffix}${blockContent}`);
|
|
14622
12403
|
}
|
|
14623
12404
|
|
|
14624
12405
|
// commands/serena.ts
|
|
@@ -14737,8 +12518,8 @@ async function runSerenaStatusCommand(options) {
|
|
|
14737
12518
|
}
|
|
14738
12519
|
|
|
14739
12520
|
// commands/config-schema.ts
|
|
14740
|
-
import { readFileSync as
|
|
14741
|
-
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";
|
|
14742
12523
|
import { resolve as resolve4, dirname as dirname5, relative as relative2 } from "path";
|
|
14743
12524
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14744
12525
|
import Ajv2 from "ajv";
|
|
@@ -14750,7 +12531,7 @@ function resolvePackagedAssetPath2(fileName) {
|
|
|
14750
12531
|
resolve4(__dirname3, "schema", fileName),
|
|
14751
12532
|
resolve4(__dirname3, "../schema", fileName)
|
|
14752
12533
|
];
|
|
14753
|
-
return candidates.find((candidate) =>
|
|
12534
|
+
return candidates.find((candidate) => existsSync17(candidate)) ?? candidates[0];
|
|
14754
12535
|
}
|
|
14755
12536
|
var PACKAGED_SCHEMA_PATH = resolvePackagedAssetPath2("pourkit.schema.json");
|
|
14756
12537
|
var PACKAGED_HASH_PATH = resolvePackagedAssetPath2("pourkit.schema.hash");
|
|
@@ -14758,7 +12539,7 @@ var _schemaValidator = null;
|
|
|
14758
12539
|
var _schemaErrors = null;
|
|
14759
12540
|
function getSchemaValidator() {
|
|
14760
12541
|
if (!_schemaValidator) {
|
|
14761
|
-
const schema = JSON.parse(
|
|
12542
|
+
const schema = JSON.parse(readFileSync18(PACKAGED_SCHEMA_PATH, "utf-8"));
|
|
14762
12543
|
const ajv = new Ajv2({ strict: true });
|
|
14763
12544
|
ajv.addKeyword("x-pourkit-schema-version");
|
|
14764
12545
|
const validate = ajv.compile(schema);
|
|
@@ -14772,7 +12553,7 @@ function getSchemaValidator() {
|
|
|
14772
12553
|
return _schemaValidator;
|
|
14773
12554
|
}
|
|
14774
12555
|
function readPackagedHash() {
|
|
14775
|
-
return
|
|
12556
|
+
return readFileSync18(PACKAGED_HASH_PATH, "utf-8");
|
|
14776
12557
|
}
|
|
14777
12558
|
function localSchemaDir(repoRoot2) {
|
|
14778
12559
|
return resolve4(repoRoot2, ".pourkit/schema");
|
|
@@ -14796,7 +12577,7 @@ function resolvePackagedReleaseAddonDocPath(docName) {
|
|
|
14796
12577
|
}
|
|
14797
12578
|
async function readPackagedTextFile(filePath) {
|
|
14798
12579
|
try {
|
|
14799
|
-
return await
|
|
12580
|
+
return await readFile5(filePath, "utf-8");
|
|
14800
12581
|
} catch {
|
|
14801
12582
|
return null;
|
|
14802
12583
|
}
|
|
@@ -14893,7 +12674,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14893
12674
|
const baselineSkills = getBaselineManagedSkillNames();
|
|
14894
12675
|
for (const docName of catalog.docs) {
|
|
14895
12676
|
const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
|
|
14896
|
-
if (!
|
|
12677
|
+
if (!existsSync17(localPath)) {
|
|
14897
12678
|
failures.push({
|
|
14898
12679
|
severity: "failure",
|
|
14899
12680
|
kind: "missing_managed_asset",
|
|
@@ -14905,13 +12686,13 @@ async function validateWorkflowPack(cwd) {
|
|
|
14905
12686
|
const packagedContent = await readPackagedTextFile(packagedPath);
|
|
14906
12687
|
if (packagedContent !== null) {
|
|
14907
12688
|
try {
|
|
14908
|
-
const localContent = await
|
|
12689
|
+
const localContent = await readFile5(localPath, "utf-8");
|
|
14909
12690
|
if (localContent !== packagedContent) {
|
|
14910
12691
|
const overridePath = resolve4(
|
|
14911
12692
|
cwd,
|
|
14912
12693
|
`.pourkit/overrides/docs/agents/${docName}`
|
|
14913
12694
|
);
|
|
14914
|
-
if (
|
|
12695
|
+
if (existsSync17(overridePath)) {
|
|
14915
12696
|
warnings.push({
|
|
14916
12697
|
severity: "warning",
|
|
14917
12698
|
kind: "overridden",
|
|
@@ -14934,7 +12715,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14934
12715
|
}
|
|
14935
12716
|
for (const skillName of baselineSkills) {
|
|
14936
12717
|
const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
|
|
14937
|
-
if (!
|
|
12718
|
+
if (!existsSync17(skillDir)) {
|
|
14938
12719
|
failures.push({
|
|
14939
12720
|
severity: "failure",
|
|
14940
12721
|
kind: "missing_managed_asset",
|
|
@@ -14943,7 +12724,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14943
12724
|
});
|
|
14944
12725
|
} else {
|
|
14945
12726
|
const skillSourcePath = resolve4(skillDir, "SKILL.md");
|
|
14946
|
-
if (
|
|
12727
|
+
if (existsSync17(skillSourcePath)) {
|
|
14947
12728
|
const packagedSkillPath = resolvePackagedManagedPath(
|
|
14948
12729
|
"skills",
|
|
14949
12730
|
skillName,
|
|
@@ -14952,13 +12733,13 @@ async function validateWorkflowPack(cwd) {
|
|
|
14952
12733
|
const packagedContent = await readPackagedTextFile(packagedSkillPath);
|
|
14953
12734
|
if (packagedContent !== null) {
|
|
14954
12735
|
try {
|
|
14955
|
-
const localContent = await
|
|
12736
|
+
const localContent = await readFile5(skillSourcePath, "utf-8");
|
|
14956
12737
|
if (localContent !== packagedContent) {
|
|
14957
12738
|
const overridePath = resolve4(
|
|
14958
12739
|
cwd,
|
|
14959
12740
|
`.pourkit/overrides/skills/${skillName}/SKILL.md`
|
|
14960
12741
|
);
|
|
14961
|
-
if (
|
|
12742
|
+
if (existsSync17(overridePath)) {
|
|
14962
12743
|
warnings.push({
|
|
14963
12744
|
severity: "warning",
|
|
14964
12745
|
kind: "overridden",
|
|
@@ -14982,7 +12763,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14982
12763
|
}
|
|
14983
12764
|
for (const promptName of catalog.prompts) {
|
|
14984
12765
|
const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
|
|
14985
|
-
if (!
|
|
12766
|
+
if (!existsSync17(promptPath)) {
|
|
14986
12767
|
failures.push({
|
|
14987
12768
|
severity: "failure",
|
|
14988
12769
|
kind: "missing_managed_asset",
|
|
@@ -14993,7 +12774,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
14993
12774
|
}
|
|
14994
12775
|
for (const skillName of baselineSkills) {
|
|
14995
12776
|
const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
|
|
14996
|
-
if (!
|
|
12777
|
+
if (!existsSync17(projectionDir)) {
|
|
14997
12778
|
warnings.push({
|
|
14998
12779
|
severity: "warning",
|
|
14999
12780
|
kind: "projection_stale",
|
|
@@ -15002,7 +12783,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15002
12783
|
});
|
|
15003
12784
|
} else {
|
|
15004
12785
|
const projectionPath = resolve4(projectionDir, "SKILL.md");
|
|
15005
|
-
if (
|
|
12786
|
+
if (existsSync17(projectionPath)) {
|
|
15006
12787
|
const managedSourcePath = resolvePackagedManagedPath(
|
|
15007
12788
|
"skills",
|
|
15008
12789
|
skillName,
|
|
@@ -15011,7 +12792,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15011
12792
|
const packagedContent = await readPackagedTextFile(managedSourcePath);
|
|
15012
12793
|
if (packagedContent !== null) {
|
|
15013
12794
|
try {
|
|
15014
|
-
const projectionContent = await
|
|
12795
|
+
const projectionContent = await readFile5(projectionPath, "utf-8");
|
|
15015
12796
|
const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
|
|
15016
12797
|
if (projectionContent !== buildOpenCodeSkillProjectionContent(
|
|
15017
12798
|
managedSourceRelativePath,
|
|
@@ -15031,9 +12812,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15031
12812
|
}
|
|
15032
12813
|
}
|
|
15033
12814
|
const agentsPath = resolve4(cwd, "AGENTS.md");
|
|
15034
|
-
if (
|
|
12815
|
+
if (existsSync17(agentsPath)) {
|
|
15035
12816
|
try {
|
|
15036
|
-
const agentsContent = await
|
|
12817
|
+
const agentsContent = await readFile5(agentsPath, "utf-8");
|
|
15037
12818
|
if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
|
|
15038
12819
|
failures.push({
|
|
15039
12820
|
severity: "failure",
|
|
@@ -15054,7 +12835,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15054
12835
|
}
|
|
15055
12836
|
}
|
|
15056
12837
|
const overridesDir = resolve4(cwd, ".pourkit/overrides");
|
|
15057
|
-
if (
|
|
12838
|
+
if (existsSync17(overridesDir)) {
|
|
15058
12839
|
const overrideFiles = await walkDir2(overridesDir);
|
|
15059
12840
|
for (const file of overrideFiles) {
|
|
15060
12841
|
const relPath = relative2(cwd, file);
|
|
@@ -15069,9 +12850,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15069
12850
|
}
|
|
15070
12851
|
}
|
|
15071
12852
|
const configPath = resolve4(cwd, ".pourkit/config.json");
|
|
15072
|
-
if (
|
|
12853
|
+
if (existsSync17(configPath)) {
|
|
15073
12854
|
try {
|
|
15074
|
-
const configContent = await
|
|
12855
|
+
const configContent = await readFile5(configPath, "utf-8");
|
|
15075
12856
|
const parsed = JSON.parse(configContent);
|
|
15076
12857
|
const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
|
|
15077
12858
|
if (releaseEnabled) {
|
|
@@ -15080,7 +12861,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15080
12861
|
cwd,
|
|
15081
12862
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
15082
12863
|
);
|
|
15083
|
-
if (!
|
|
12864
|
+
if (!existsSync17(addonDir)) {
|
|
15084
12865
|
failures.push({
|
|
15085
12866
|
severity: "failure",
|
|
15086
12867
|
kind: "release_config_conflict",
|
|
@@ -15094,7 +12875,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15094
12875
|
cwd,
|
|
15095
12876
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15096
12877
|
);
|
|
15097
|
-
if (!
|
|
12878
|
+
if (!existsSync17(addonDocPath)) {
|
|
15098
12879
|
failures.push({
|
|
15099
12880
|
severity: "failure",
|
|
15100
12881
|
kind: "release_config_conflict",
|
|
@@ -15106,7 +12887,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15106
12887
|
resolvePackagedReleaseAddonDocPath(docName)
|
|
15107
12888
|
);
|
|
15108
12889
|
if (packagedContent !== null) {
|
|
15109
|
-
const localContent = await
|
|
12890
|
+
const localContent = await readFile5(addonDocPath, "utf-8");
|
|
15110
12891
|
if (localContent !== packagedContent) {
|
|
15111
12892
|
failures.push({
|
|
15112
12893
|
severity: "failure",
|
|
@@ -15139,7 +12920,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15139
12920
|
cwd,
|
|
15140
12921
|
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
15141
12922
|
);
|
|
15142
|
-
if (
|
|
12923
|
+
if (existsSync17(addonDir)) {
|
|
15143
12924
|
failures.push({
|
|
15144
12925
|
severity: "failure",
|
|
15145
12926
|
kind: "release_config_conflict",
|
|
@@ -15153,7 +12934,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15153
12934
|
cwd,
|
|
15154
12935
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15155
12936
|
);
|
|
15156
|
-
if (
|
|
12937
|
+
if (existsSync17(addonDocPath)) {
|
|
15157
12938
|
failures.push({
|
|
15158
12939
|
severity: "failure",
|
|
15159
12940
|
kind: "release_config_conflict",
|
|
@@ -15167,9 +12948,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15167
12948
|
}
|
|
15168
12949
|
}
|
|
15169
12950
|
const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
|
|
15170
|
-
if (
|
|
12951
|
+
if (existsSync17(manifestPath)) {
|
|
15171
12952
|
try {
|
|
15172
|
-
const manifestContent = await
|
|
12953
|
+
const manifestContent = await readFile5(manifestPath, "utf-8");
|
|
15173
12954
|
const manifest = JSON.parse(manifestContent);
|
|
15174
12955
|
const wp = manifest.workflowPack;
|
|
15175
12956
|
if (wp?.projections) {
|
|
@@ -15190,7 +12971,7 @@ async function validateWorkflowPack(cwd) {
|
|
|
15190
12971
|
}
|
|
15191
12972
|
}
|
|
15192
12973
|
const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
|
|
15193
|
-
if (
|
|
12974
|
+
if (existsSync17(pathEscapeOverrideDir)) {
|
|
15194
12975
|
const overrideFiles = await walkDir2(pathEscapeOverrideDir);
|
|
15195
12976
|
for (const file of overrideFiles) {
|
|
15196
12977
|
if (!isPathContained(cwd, file)) {
|
|
@@ -15204,12 +12985,12 @@ async function validateWorkflowPack(cwd) {
|
|
|
15204
12985
|
}
|
|
15205
12986
|
}
|
|
15206
12987
|
const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
|
|
15207
|
-
if (
|
|
12988
|
+
if (existsSync17(managedDocsDir)) {
|
|
15208
12989
|
const docFiles = await walkDir2(managedDocsDir);
|
|
15209
12990
|
for (const file of docFiles) {
|
|
15210
12991
|
if (!file.endsWith(".md")) continue;
|
|
15211
12992
|
try {
|
|
15212
|
-
const content = await
|
|
12993
|
+
const content = await readFile5(file, "utf-8");
|
|
15213
12994
|
const relPath = relative2(cwd, file);
|
|
15214
12995
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15215
12996
|
for (const v of violations) {
|
|
@@ -15225,14 +13006,14 @@ async function validateWorkflowPack(cwd) {
|
|
|
15225
13006
|
}
|
|
15226
13007
|
}
|
|
15227
13008
|
const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
|
|
15228
|
-
if (
|
|
13009
|
+
if (existsSync17(managedSkillsDir)) {
|
|
15229
13010
|
const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
|
|
15230
13011
|
for (const entry of skillDirs) {
|
|
15231
13012
|
if (!entry.isDirectory()) continue;
|
|
15232
13013
|
const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
|
|
15233
|
-
if (!
|
|
13014
|
+
if (!existsSync17(skillFile)) continue;
|
|
15234
13015
|
try {
|
|
15235
|
-
const content = await
|
|
13016
|
+
const content = await readFile5(skillFile, "utf-8");
|
|
15236
13017
|
const relPath = relative2(cwd, skillFile);
|
|
15237
13018
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15238
13019
|
for (const v of violations) {
|
|
@@ -15248,12 +13029,12 @@ async function validateWorkflowPack(cwd) {
|
|
|
15248
13029
|
}
|
|
15249
13030
|
}
|
|
15250
13031
|
const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
|
|
15251
|
-
if (
|
|
13032
|
+
if (existsSync17(managedPromptsDir)) {
|
|
15252
13033
|
for (const promptName of catalog.prompts) {
|
|
15253
13034
|
const promptFile = resolve4(managedPromptsDir, promptName);
|
|
15254
|
-
if (!
|
|
13035
|
+
if (!existsSync17(promptFile)) continue;
|
|
15255
13036
|
try {
|
|
15256
|
-
const content = await
|
|
13037
|
+
const content = await readFile5(promptFile, "utf-8");
|
|
15257
13038
|
const relPath = relative2(cwd, promptFile);
|
|
15258
13039
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15259
13040
|
for (const v of violations) {
|
|
@@ -15273,9 +13054,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15273
13054
|
cwd,
|
|
15274
13055
|
`.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
|
|
15275
13056
|
);
|
|
15276
|
-
if (!
|
|
13057
|
+
if (!existsSync17(addonSkillFile)) continue;
|
|
15277
13058
|
try {
|
|
15278
|
-
const content = await
|
|
13059
|
+
const content = await readFile5(addonSkillFile, "utf-8");
|
|
15279
13060
|
const relPath = relative2(cwd, addonSkillFile);
|
|
15280
13061
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15281
13062
|
for (const v of violations) {
|
|
@@ -15294,9 +13075,9 @@ async function validateWorkflowPack(cwd) {
|
|
|
15294
13075
|
cwd,
|
|
15295
13076
|
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15296
13077
|
);
|
|
15297
|
-
if (!
|
|
13078
|
+
if (!existsSync17(addonDocFile)) continue;
|
|
15298
13079
|
try {
|
|
15299
|
-
const content = await
|
|
13080
|
+
const content = await readFile5(addonDocFile, "utf-8");
|
|
15300
13081
|
const relPath = relative2(cwd, addonDocFile);
|
|
15301
13082
|
const violations = scanManagedPolicyText(content, relPath);
|
|
15302
13083
|
for (const v of violations) {
|
|
@@ -15328,7 +13109,7 @@ async function validateMemoryHealth(cwd, memoryConfig) {
|
|
|
15328
13109
|
detail: icmPath !== null ? icmPath : "icm not found on PATH"
|
|
15329
13110
|
});
|
|
15330
13111
|
const gitignorePath = resolve4(cwd, ".gitignore");
|
|
15331
|
-
const gitignoreContent =
|
|
13112
|
+
const gitignoreContent = existsSync17(gitignorePath) ? readFileSync18(gitignorePath, "utf-8") : "";
|
|
15332
13113
|
const hasGitignoreEntry = gitignoreContent.includes(".pourkit/icm/");
|
|
15333
13114
|
checks.push({
|
|
15334
13115
|
name: "icm-gitignore",
|
|
@@ -15355,8 +13136,8 @@ async function runDoctorCommand(options) {
|
|
|
15355
13136
|
const schemaDir = localSchemaDir(repoRootPath);
|
|
15356
13137
|
const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
|
|
15357
13138
|
const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
|
|
15358
|
-
const localSchemaExists =
|
|
15359
|
-
const localHashExists =
|
|
13139
|
+
const localSchemaExists = existsSync17(localSchemaPath);
|
|
13140
|
+
const localHashExists = existsSync17(localHashPath);
|
|
15360
13141
|
let packagedHash = null;
|
|
15361
13142
|
try {
|
|
15362
13143
|
packagedHash = readPackagedHash();
|
|
@@ -15365,7 +13146,7 @@ async function runDoctorCommand(options) {
|
|
|
15365
13146
|
let localHashContent = null;
|
|
15366
13147
|
if (localHashExists) {
|
|
15367
13148
|
try {
|
|
15368
|
-
localHashContent = await
|
|
13149
|
+
localHashContent = await readFile5(localHashPath, "utf-8");
|
|
15369
13150
|
} catch {
|
|
15370
13151
|
}
|
|
15371
13152
|
}
|
|
@@ -15375,9 +13156,9 @@ async function runDoctorCommand(options) {
|
|
|
15375
13156
|
const configPath = resolve4(repoRootPath, ".pourkit/config.json");
|
|
15376
13157
|
let configValidation;
|
|
15377
13158
|
let rawMemoryConfig;
|
|
15378
|
-
if (
|
|
13159
|
+
if (existsSync17(configPath)) {
|
|
15379
13160
|
try {
|
|
15380
|
-
const raw = JSON.parse(
|
|
13161
|
+
const raw = JSON.parse(readFileSync18(configPath, "utf-8"));
|
|
15381
13162
|
rawMemoryConfig = raw.memory;
|
|
15382
13163
|
const validate = getSchemaValidator();
|
|
15383
13164
|
const valid2 = validate(raw);
|
|
@@ -15410,7 +13191,7 @@ async function runDoctorCommand(options) {
|
|
|
15410
13191
|
"pourkit.config.mjs",
|
|
15411
13192
|
"pourkit.config.js",
|
|
15412
13193
|
"pourkit.json"
|
|
15413
|
-
].filter((p) =>
|
|
13194
|
+
].filter((p) => existsSync17(resolve4(repoRootPath, p)));
|
|
15414
13195
|
const workflowPack = await validateWorkflowPack(repoRootPath);
|
|
15415
13196
|
const memory = await validateMemoryHealth(repoRootPath, rawMemoryConfig);
|
|
15416
13197
|
let recommendation = null;
|
|
@@ -15453,31 +13234,31 @@ async function runDoctorCommand(options) {
|
|
|
15453
13234
|
async function runConfigSyncSchemaCommand(options) {
|
|
15454
13235
|
const repoRootPath = options.cwd ?? process.cwd();
|
|
15455
13236
|
const schemaDir = localSchemaDir(repoRootPath);
|
|
15456
|
-
await
|
|
15457
|
-
const packagedSchema = await
|
|
15458
|
-
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");
|
|
15459
13240
|
const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
|
|
15460
13241
|
const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
|
|
15461
13242
|
let schemaWritten = false;
|
|
15462
13243
|
let hashWritten = false;
|
|
15463
|
-
if (
|
|
15464
|
-
const existing = await
|
|
13244
|
+
if (existsSync17(localSchemaPath)) {
|
|
13245
|
+
const existing = await readFile5(localSchemaPath, "utf-8");
|
|
15465
13246
|
if (existing !== packagedSchema) {
|
|
15466
|
-
await
|
|
13247
|
+
await writeFile2(localSchemaPath, packagedSchema, "utf-8");
|
|
15467
13248
|
schemaWritten = true;
|
|
15468
13249
|
}
|
|
15469
13250
|
} else {
|
|
15470
|
-
await
|
|
13251
|
+
await writeFile2(localSchemaPath, packagedSchema, "utf-8");
|
|
15471
13252
|
schemaWritten = true;
|
|
15472
13253
|
}
|
|
15473
|
-
if (
|
|
15474
|
-
const existing = await
|
|
13254
|
+
if (existsSync17(localHashPath)) {
|
|
13255
|
+
const existing = await readFile5(localHashPath, "utf-8");
|
|
15475
13256
|
if (existing !== packagedHash) {
|
|
15476
|
-
await
|
|
13257
|
+
await writeFile2(localHashPath, packagedHash, "utf-8");
|
|
15477
13258
|
hashWritten = true;
|
|
15478
13259
|
}
|
|
15479
13260
|
} else {
|
|
15480
|
-
await
|
|
13261
|
+
await writeFile2(localHashPath, packagedHash, "utf-8");
|
|
15481
13262
|
hashWritten = true;
|
|
15482
13263
|
}
|
|
15483
13264
|
return {
|
|
@@ -15488,8 +13269,8 @@ async function runConfigSyncSchemaCommand(options) {
|
|
|
15488
13269
|
}
|
|
15489
13270
|
|
|
15490
13271
|
// commands/workflow-pack-sync.ts
|
|
15491
|
-
import { existsSync as
|
|
15492
|
-
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";
|
|
15493
13274
|
import { resolve as resolve5, dirname as dirname6, relative as relative3, normalize as normalize2 } from "path";
|
|
15494
13275
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15495
13276
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
@@ -15527,7 +13308,7 @@ function resolvePackagedManagedPromptPath(promptName) {
|
|
|
15527
13308
|
}
|
|
15528
13309
|
async function readPackagedTextFile2(filePath) {
|
|
15529
13310
|
try {
|
|
15530
|
-
return await
|
|
13311
|
+
return await readFile6(filePath, "utf-8");
|
|
15531
13312
|
} catch {
|
|
15532
13313
|
return null;
|
|
15533
13314
|
}
|
|
@@ -15610,20 +13391,20 @@ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
|
|
|
15610
13391
|
errors.push(escapeError);
|
|
15611
13392
|
return false;
|
|
15612
13393
|
}
|
|
15613
|
-
await
|
|
15614
|
-
if (
|
|
15615
|
-
const existing = await
|
|
13394
|
+
await mkdir6(resolve5(targetPath, ".."), { recursive: true });
|
|
13395
|
+
if (existsSync18(targetPath)) {
|
|
13396
|
+
const existing = await readFile6(targetPath, "utf-8");
|
|
15616
13397
|
if (existing === sourceContent) {
|
|
15617
13398
|
return false;
|
|
15618
13399
|
}
|
|
15619
13400
|
}
|
|
15620
|
-
await
|
|
13401
|
+
await writeFile3(targetPath, sourceContent, "utf-8");
|
|
15621
13402
|
return true;
|
|
15622
13403
|
}
|
|
15623
13404
|
function walkDirSync(dir) {
|
|
15624
13405
|
const result = [];
|
|
15625
13406
|
try {
|
|
15626
|
-
const entries =
|
|
13407
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
15627
13408
|
for (const entry of entries) {
|
|
15628
13409
|
const full = resolve5(dir, entry.name);
|
|
15629
13410
|
if (entry.isDirectory()) {
|
|
@@ -15638,7 +13419,7 @@ function walkDirSync(dir) {
|
|
|
15638
13419
|
}
|
|
15639
13420
|
async function detectOverrides(cwd) {
|
|
15640
13421
|
const overridesDir = resolve5(cwd, OVERRIDES_DIR);
|
|
15641
|
-
if (!
|
|
13422
|
+
if (!existsSync18(overridesDir)) {
|
|
15642
13423
|
return [];
|
|
15643
13424
|
}
|
|
15644
13425
|
const files = await walkDir3(overridesDir);
|
|
@@ -15648,13 +13429,13 @@ async function detectProjectOwnedFiles(cwd) {
|
|
|
15648
13429
|
const found = [];
|
|
15649
13430
|
for (const ownedPath of PROJECT_OWNED_PATHS) {
|
|
15650
13431
|
const fullPath = resolve5(cwd, ownedPath);
|
|
15651
|
-
if (
|
|
13432
|
+
if (existsSync18(fullPath)) {
|
|
15652
13433
|
found.push(ownedPath);
|
|
15653
13434
|
}
|
|
15654
13435
|
}
|
|
15655
13436
|
for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
|
|
15656
13437
|
const fullDir = resolve5(cwd, ownedDir);
|
|
15657
|
-
if (
|
|
13438
|
+
if (existsSync18(fullDir)) {
|
|
15658
13439
|
const files = await walkDir3(fullDir);
|
|
15659
13440
|
for (const file of files) {
|
|
15660
13441
|
found.push(relative3(cwd, file));
|
|
@@ -15666,7 +13447,7 @@ async function detectProjectOwnedFiles(cwd) {
|
|
|
15666
13447
|
async function isReleaseWorkflowEnabled(cwd) {
|
|
15667
13448
|
try {
|
|
15668
13449
|
const configPath = resolve5(cwd, ".pourkit", "config.json");
|
|
15669
|
-
const content = await
|
|
13450
|
+
const content = await readFile6(configPath, "utf-8");
|
|
15670
13451
|
const parsed = JSON.parse(content);
|
|
15671
13452
|
return parsed.releaseWorkflow?.enabled === true;
|
|
15672
13453
|
} catch {
|
|
@@ -15806,12 +13587,12 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15806
13587
|
const fullManagedBlock = `${MANAGED_BLOCK_BEGIN3}${managedBlockContent}${MANAGED_BLOCK_END2}`;
|
|
15807
13588
|
async function syncManagedAgentFile(fileName, options2) {
|
|
15808
13589
|
const filePath = resolve5(cwd, fileName);
|
|
15809
|
-
if (!
|
|
13590
|
+
if (!existsSync18(filePath)) {
|
|
15810
13591
|
if (!options2.createIfMissing) return false;
|
|
15811
|
-
await
|
|
13592
|
+
await writeFile3(filePath, fullManagedBlock + "\n", "utf-8");
|
|
15812
13593
|
return true;
|
|
15813
13594
|
}
|
|
15814
|
-
const existing = await
|
|
13595
|
+
const existing = await readFile6(filePath, "utf-8");
|
|
15815
13596
|
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN3);
|
|
15816
13597
|
const endIdx = existing.indexOf(MANAGED_BLOCK_END2);
|
|
15817
13598
|
if (beginIdx !== -1 && endIdx !== -1) {
|
|
@@ -15819,7 +13600,7 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15819
13600
|
const afterBlock = existing.slice(endIdx + MANAGED_BLOCK_END2.length);
|
|
15820
13601
|
const newContent = `${beforeBlock}${fullManagedBlock}${afterBlock}`;
|
|
15821
13602
|
if (newContent !== existing) {
|
|
15822
|
-
await
|
|
13603
|
+
await writeFile3(filePath, newContent, "utf-8");
|
|
15823
13604
|
return true;
|
|
15824
13605
|
}
|
|
15825
13606
|
} else {
|
|
@@ -15828,7 +13609,7 @@ async function runWorkflowPackSyncCommand(options) {
|
|
|
15828
13609
|
${fullManagedBlock}
|
|
15829
13610
|
`;
|
|
15830
13611
|
if (newContent !== existing) {
|
|
15831
|
-
await
|
|
13612
|
+
await writeFile3(filePath, newContent, "utf-8");
|
|
15832
13613
|
return true;
|
|
15833
13614
|
}
|
|
15834
13615
|
}
|
|
@@ -15846,7 +13627,7 @@ ${fullManagedBlock}
|
|
|
15846
13627
|
let manifestWritten = false;
|
|
15847
13628
|
let previousManifestWorkflowPack = void 0;
|
|
15848
13629
|
try {
|
|
15849
|
-
const existingRaw = await
|
|
13630
|
+
const existingRaw = await readFile6(manifestPath, "utf-8").catch(() => null);
|
|
15850
13631
|
if (existingRaw) {
|
|
15851
13632
|
const existing = JSON.parse(existingRaw);
|
|
15852
13633
|
previousManifestWorkflowPack = existing.workflowPack;
|
|
@@ -15855,13 +13636,13 @@ ${fullManagedBlock}
|
|
|
15855
13636
|
}
|
|
15856
13637
|
if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
|
|
15857
13638
|
try {
|
|
15858
|
-
const existingRaw = await
|
|
13639
|
+
const existingRaw = await readFile6(manifestPath, "utf-8").catch(
|
|
15859
13640
|
() => null
|
|
15860
13641
|
);
|
|
15861
13642
|
let manifest = existingRaw ? JSON.parse(existingRaw) : {};
|
|
15862
13643
|
manifest.workflowPack = manifestMeta;
|
|
15863
|
-
await
|
|
15864
|
-
await
|
|
13644
|
+
await mkdir6(resolve5(manifestPath, ".."), { recursive: true });
|
|
13645
|
+
await writeFile3(
|
|
15865
13646
|
manifestPath,
|
|
15866
13647
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
15867
13648
|
"utf-8"
|
|
@@ -16581,22 +14362,22 @@ function formatChecks2(checks) {
|
|
|
16581
14362
|
init_common();
|
|
16582
14363
|
|
|
16583
14364
|
// execution/sandcastle-execution.ts
|
|
16584
|
-
import { existsSync as
|
|
16585
|
-
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";
|
|
16586
14367
|
import { createWorktree, opencode } from "@ai-hero/sandcastle";
|
|
16587
14368
|
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
16588
14369
|
|
|
16589
14370
|
// execution/execution-provider.ts
|
|
16590
14371
|
init_common();
|
|
16591
14372
|
import { mkdtempSync } from "fs";
|
|
16592
|
-
import { writeFile as
|
|
14373
|
+
import { writeFile as writeFile4 } from "fs/promises";
|
|
16593
14374
|
import { tmpdir } from "os";
|
|
16594
|
-
import { dirname as dirname7, join as
|
|
14375
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
16595
14376
|
async function writeExecutionArtifacts(worktreePath, artifacts) {
|
|
16596
14377
|
for (const artifact of artifacts) {
|
|
16597
|
-
const filePath =
|
|
14378
|
+
const filePath = join18(worktreePath, artifact.path);
|
|
16598
14379
|
await ensureDir(dirname7(filePath));
|
|
16599
|
-
await
|
|
14380
|
+
await writeFile4(filePath, artifact.content, "utf-8");
|
|
16600
14381
|
}
|
|
16601
14382
|
}
|
|
16602
14383
|
|
|
@@ -16608,19 +14389,19 @@ init_common();
|
|
|
16608
14389
|
import path7 from "path";
|
|
16609
14390
|
|
|
16610
14391
|
// execution/sandbox-image.ts
|
|
16611
|
-
import { createHash as
|
|
16612
|
-
import { existsSync as
|
|
14392
|
+
import { createHash as createHash2 } from "crypto";
|
|
14393
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "fs";
|
|
16613
14394
|
import path6 from "path";
|
|
16614
14395
|
function sandboxImageName(repoRoot2, installIcm) {
|
|
16615
14396
|
const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
|
|
16616
14397
|
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
16617
14398
|
const baseName = sanitized || "local";
|
|
16618
14399
|
const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
|
|
16619
|
-
if (!
|
|
14400
|
+
if (!existsSync19(dockerfilePath)) {
|
|
16620
14401
|
const base2 = `sandcastle:${baseName}`;
|
|
16621
14402
|
return installIcm ? `${base2}-icm` : base2;
|
|
16622
14403
|
}
|
|
16623
|
-
const fingerprint =
|
|
14404
|
+
const fingerprint = createHash2("sha256").update(readFileSync19(dockerfilePath)).digest("hex").slice(0, 8);
|
|
16624
14405
|
const base = `sandcastle:${baseName}-${fingerprint}`;
|
|
16625
14406
|
return installIcm ? `${base}-icm` : base;
|
|
16626
14407
|
}
|
|
@@ -16656,7 +14437,7 @@ async function createSandboxFromExistingWorktree(options) {
|
|
|
16656
14437
|
}
|
|
16657
14438
|
|
|
16658
14439
|
// execution/sandbox-options.ts
|
|
16659
|
-
import { join as
|
|
14440
|
+
import { join as join19 } from "path";
|
|
16660
14441
|
var POURKIT_ICM_CONTAINER_DIR = "/home/agent/.pourkit/icm";
|
|
16661
14442
|
function buildSandboxOptions(repoRoot2, sandbox, memory) {
|
|
16662
14443
|
const mounts = [];
|
|
@@ -16665,12 +14446,12 @@ function buildSandboxOptions(repoRoot2, sandbox, memory) {
|
|
|
16665
14446
|
}
|
|
16666
14447
|
if (memory?.available) {
|
|
16667
14448
|
mounts.push({
|
|
16668
|
-
hostPath:
|
|
14449
|
+
hostPath: join19(repoRoot2, ".pourkit", "icm"),
|
|
16669
14450
|
sandboxPath: POURKIT_ICM_CONTAINER_DIR,
|
|
16670
14451
|
readonly: false
|
|
16671
14452
|
});
|
|
16672
14453
|
mounts.push({
|
|
16673
|
-
hostPath:
|
|
14454
|
+
hostPath: join19(repoRoot2, ".pourkit", "icm", "cache"),
|
|
16674
14455
|
sandboxPath: "/home/agent/.cache/icm",
|
|
16675
14456
|
readonly: false
|
|
16676
14457
|
});
|
|
@@ -16804,13 +14585,13 @@ var SandcastleExecutionSession = class {
|
|
|
16804
14585
|
"Memory enabled but memory context is invalid. Run `pourkit memory init` or install ICM for host planning use."
|
|
16805
14586
|
);
|
|
16806
14587
|
}
|
|
16807
|
-
const hostMemoryDir =
|
|
16808
|
-
if (!
|
|
14588
|
+
const hostMemoryDir = join20(repoRoot2, ".pourkit", "icm");
|
|
14589
|
+
if (!existsSync20(hostMemoryDir) || !statSync2(hostMemoryDir).isDirectory()) {
|
|
16809
14590
|
throw new MissingHostMemoryDirError(
|
|
16810
14591
|
"Memory enabled but host .pourkit/icm/ directory is missing. Run `pourkit memory init` to create the repo-scoped memory store."
|
|
16811
14592
|
);
|
|
16812
14593
|
}
|
|
16813
|
-
|
|
14594
|
+
mkdirSync9(join20(hostMemoryDir, "cache"), { recursive: true });
|
|
16814
14595
|
}
|
|
16815
14596
|
const sandboxOptions = buildSandboxOptions(
|
|
16816
14597
|
repoRoot2,
|
|
@@ -16969,8 +14750,8 @@ function sanitizeBranch(branchName) {
|
|
|
16969
14750
|
}
|
|
16970
14751
|
function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
16971
14752
|
return paths.filter((relativePath) => {
|
|
16972
|
-
const source =
|
|
16973
|
-
if (!
|
|
14753
|
+
const source = join20(repoRoot2, relativePath);
|
|
14754
|
+
if (!existsSync20(source)) {
|
|
16974
14755
|
return true;
|
|
16975
14756
|
}
|
|
16976
14757
|
try {
|
|
@@ -16980,8 +14761,8 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
|
16980
14761
|
} catch {
|
|
16981
14762
|
return true;
|
|
16982
14763
|
}
|
|
16983
|
-
const destination =
|
|
16984
|
-
return !
|
|
14764
|
+
const destination = join20(worktreePath, relativePath);
|
|
14765
|
+
return !existsSync20(destination);
|
|
16985
14766
|
});
|
|
16986
14767
|
}
|
|
16987
14768
|
function formatAgentStreamEvent(event) {
|
|
@@ -17045,13 +14826,13 @@ function isPlainObject(value) {
|
|
|
17045
14826
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17046
14827
|
}
|
|
17047
14828
|
function savePromptToFile(repoRoot2, stage, iteration, prompt) {
|
|
17048
|
-
const promptsDir =
|
|
17049
|
-
|
|
14829
|
+
const promptsDir = join20(repoRoot2, ".pourkit", ".tmp", "prompts");
|
|
14830
|
+
mkdirSync9(promptsDir, { recursive: true });
|
|
17050
14831
|
const timestamp2 = Date.now();
|
|
17051
14832
|
const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
|
|
17052
14833
|
const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
|
|
17053
|
-
const filePath =
|
|
17054
|
-
|
|
14834
|
+
const filePath = join20(promptsDir, filename);
|
|
14835
|
+
writeFileSync6(filePath, prompt, "utf-8");
|
|
17055
14836
|
}
|
|
17056
14837
|
|
|
17057
14838
|
// cli.ts
|
|
@@ -17151,8 +14932,8 @@ function createCliProgram(version) {
|
|
|
17151
14932
|
});
|
|
17152
14933
|
program.command("validate-artifact").description("Validate an agent handoff artifact").argument(
|
|
17153
14934
|
"<kind>",
|
|
17154
|
-
"artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution,
|
|
17155
|
-
).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) => {
|
|
17156
14937
|
const parsed = Number.parseInt(value, 10);
|
|
17157
14938
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
17158
14939
|
throw new CommanderError(
|
|
@@ -17205,16 +14986,13 @@ function createCliProgram(version) {
|
|
|
17205
14986
|
"--base-ref <ref>",
|
|
17206
14987
|
"canonical base ref for issue-final-review changedFiles coverage validation"
|
|
17207
14988
|
).option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
|
|
17208
|
-
(kind, artifactPath,
|
|
14989
|
+
(kind, artifactPath, _extraPaths, options) => {
|
|
17209
14990
|
const allowedKinds = /* @__PURE__ */ new Set([
|
|
17210
14991
|
"reviewer",
|
|
17211
14992
|
"refactor",
|
|
17212
14993
|
"finalizer",
|
|
17213
14994
|
"conflict-resolution",
|
|
17214
14995
|
"failure-resolution",
|
|
17215
|
-
"local-prd",
|
|
17216
|
-
"local-issue",
|
|
17217
|
-
"local-triage",
|
|
17218
14996
|
"issue-final-review"
|
|
17219
14997
|
]);
|
|
17220
14998
|
if (!allowedKinds.has(kind)) {
|
|
@@ -17224,18 +15002,8 @@ function createCliProgram(version) {
|
|
|
17224
15002
|
`Unsupported artifact kind "${kind}"`
|
|
17225
15003
|
);
|
|
17226
15004
|
}
|
|
17227
|
-
const LOCAL_KINDS = /* @__PURE__ */ new Set([
|
|
17228
|
-
"local-prd",
|
|
17229
|
-
"local-issue",
|
|
17230
|
-
"local-triage"
|
|
17231
|
-
]);
|
|
17232
15005
|
const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
|
|
17233
|
-
const result =
|
|
17234
|
-
repoRoot: targetRepoRoot,
|
|
17235
|
-
kind,
|
|
17236
|
-
artifactPath,
|
|
17237
|
-
extraArgs: extraPaths
|
|
17238
|
-
}) : runValidateArtifactCommand({
|
|
15006
|
+
const result = runValidateArtifactCommand({
|
|
17239
15007
|
repoRoot: targetRepoRoot,
|
|
17240
15008
|
kind,
|
|
17241
15009
|
artifactPath,
|
|
@@ -17518,15 +15286,14 @@ function createCliProgram(version) {
|
|
|
17518
15286
|
const logger = createLogger("pourkit", logPath);
|
|
17519
15287
|
try {
|
|
17520
15288
|
const config = await loadRepoConfig(targetRepoRoot);
|
|
17521
|
-
|
|
17522
|
-
const
|
|
17523
|
-
const
|
|
17524
|
-
const issueProvider = client ? new GitHubIssueProvider(client, {
|
|
15289
|
+
resolveTarget(config, options.target);
|
|
15290
|
+
const client = await requireGitHubClient({ cwd: targetRepoRoot });
|
|
15291
|
+
const issueProvider = new GitHubIssueProvider(client, {
|
|
17525
15292
|
readyForAgentLabel: config.labels.readyForAgent,
|
|
17526
15293
|
blockedLabel: config.labels.blocked,
|
|
17527
15294
|
issueListLimit: config.checks.issueListLimit
|
|
17528
|
-
})
|
|
17529
|
-
const prProvider =
|
|
15295
|
+
});
|
|
15296
|
+
const prProvider = new GitHubPRProvider(client, logger);
|
|
17530
15297
|
const executionProvider = new SandcastleExecutionProvider();
|
|
17531
15298
|
const result = await runPrdRunLaunchCommand({
|
|
17532
15299
|
repoRoot: targetRepoRoot,
|
|
@@ -17785,11 +15552,11 @@ function createCliProgram(version) {
|
|
|
17785
15552
|
return program;
|
|
17786
15553
|
}
|
|
17787
15554
|
async function resolveCliVersion() {
|
|
17788
|
-
if (isPackageVersion("0.0.0-next-
|
|
17789
|
-
return "0.0.0-next-
|
|
15555
|
+
if (isPackageVersion("0.0.0-next-20260619094345")) {
|
|
15556
|
+
return "0.0.0-next-20260619094345";
|
|
17790
15557
|
}
|
|
17791
|
-
if (isReleaseVersion("0.0.0-next-
|
|
17792
|
-
return "0.0.0-next-
|
|
15558
|
+
if (isReleaseVersion("0.0.0-next-20260619094345")) {
|
|
15559
|
+
return "0.0.0-next-20260619094345";
|
|
17793
15560
|
}
|
|
17794
15561
|
try {
|
|
17795
15562
|
const root = repoRoot();
|