@pourkit/cli 0.0.0-next-20260615184943 → 0.0.0-next-20260617195445
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 +1873 -231
- package/dist/cli.js.map +1 -1
- package/dist/e2e/run-live-e2e.js +112 -20
- package/dist/e2e/run-live-e2e.js.map +1 -1
- package/dist/managed/addons/release/docs/agents/release-workflow.md +33 -0
- package/dist/managed/addons/release/skills/changeset/SKILL.md +76 -0
- package/dist/managed/addons/release/skills/promote-release/SKILL.md +94 -0
- package/dist/managed/docs/agents/commit-style.md +75 -0
- package/dist/managed/docs/agents/domain.md +57 -0
- package/dist/managed/docs/agents/git-workflow.md +197 -0
- package/dist/managed/docs/agents/issue-tracker.md +33 -0
- package/dist/managed/docs/agents/naming.md +58 -0
- package/dist/managed/docs/agents/triage-labels.md +28 -0
- package/dist/managed/prompts/builder.prompt.md +89 -0
- package/dist/managed/prompts/conflict-resolution.prompt.md +70 -0
- package/dist/managed/prompts/failure-resolution.prompt.md +68 -0
- package/dist/managed/prompts/finalizer.prompt.md +77 -0
- package/dist/managed/prompts/issue-final-review.prompt.md +122 -0
- package/dist/managed/prompts/pr-description.prompt.md +77 -0
- package/dist/managed/prompts/refactor.prompt.md +114 -0
- package/dist/managed/prompts/reviewer-correctness.snippet.md +5 -0
- package/dist/managed/prompts/reviewer-quality.snippet.md +3 -0
- package/dist/managed/prompts/reviewer-scope.snippet.md +5 -0
- package/dist/managed/prompts/reviewer-tests.snippet.md +5 -0
- package/dist/managed/prompts/reviewer.prompt.md +169 -0
- package/dist/managed/skills/code-review/SKILL.md +152 -0
- package/dist/managed/skills/code-review/references/thermo-nuclear-code-quality-review.md +147 -0
- package/dist/managed/skills/diagnose/SKILL.md +119 -0
- package/dist/managed/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
- package/dist/managed/skills/explain/EXAMPLES.md +227 -0
- package/dist/managed/skills/explain/SKILL.md +106 -0
- package/dist/managed/skills/explain/references/mermaid.md +136 -0
- package/dist/managed/skills/grill-with-docs/ADR-FORMAT.md +47 -0
- package/dist/managed/skills/grill-with-docs/CONTEXT-FORMAT.md +77 -0
- package/dist/managed/skills/grill-with-docs/SKILL.md +88 -0
- package/dist/managed/skills/handoff/SKILL.md +13 -0
- package/dist/managed/skills/improve-codebase-architecture/DEEPENING.md +37 -0
- package/dist/managed/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
- package/dist/managed/skills/improve-codebase-architecture/LANGUAGE.md +53 -0
- package/dist/managed/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/dist/managed/skills/publish-prd/SKILL.md +130 -0
- package/dist/managed/skills/secret-commit-guard/SKILL.md +54 -0
- package/dist/managed/skills/security-review/SKILL.md +495 -0
- package/dist/managed/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/dist/managed/skills/ship-current-changes/SKILL.md +199 -0
- package/dist/managed/skills/tdd/SKILL.md +110 -0
- package/dist/managed/skills/tdd/deep-modules.md +33 -0
- package/dist/managed/skills/tdd/interface-design.md +31 -0
- package/dist/managed/skills/tdd/mocking.md +59 -0
- package/dist/managed/skills/tdd/refactoring.md +10 -0
- package/dist/managed/skills/tdd/tests.md +61 -0
- package/dist/managed/skills/to-issues/SKILL.md +442 -0
- package/dist/managed/skills/to-plan/SKILL.md +20 -0
- package/dist/managed/skills/to-prd/SKILL.md +129 -0
- package/dist/managed/skills/triage/AGENT-BRIEF.md +168 -0
- package/dist/managed/skills/triage/OUT-OF-SCOPE.md +101 -0
- package/dist/managed/skills/triage/SKILL.md +162 -0
- package/dist/managed/skills/write-a-skill/SKILL.md +117 -0
- package/dist/managed/skills/zoom-out/SKILL.md +7 -0
- package/dist/schema/pourkit.schema.hash +1 -1
- package/dist/schema/pourkit.schema.json +32 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -58,20 +58,20 @@ function createLogger(name, filePath) {
|
|
|
58
58
|
);
|
|
59
59
|
},
|
|
60
60
|
async close() {
|
|
61
|
-
await new Promise((
|
|
61
|
+
await new Promise((resolve6) => {
|
|
62
62
|
if (!fileStream) {
|
|
63
|
-
|
|
63
|
+
resolve6();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
const timer = setTimeout(() => {
|
|
67
67
|
if (!fileStream.destroyed) {
|
|
68
68
|
fileStream.destroy();
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
resolve6();
|
|
71
71
|
}, 2e3);
|
|
72
72
|
fileStream.end(() => {
|
|
73
73
|
clearTimeout(timer);
|
|
74
|
-
|
|
74
|
+
resolve6();
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -266,7 +266,7 @@ async function execJson(command, args, options = {}) {
|
|
|
266
266
|
return JSON.parse(result.stdout);
|
|
267
267
|
}
|
|
268
268
|
function sleep(ms) {
|
|
269
|
-
return new Promise((
|
|
269
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
270
270
|
}
|
|
271
271
|
async function execCaptureWithRetry(command, args, options = {}) {
|
|
272
272
|
const retries = options.retries ?? 3;
|
|
@@ -892,11 +892,11 @@ var OBSOLETE_CONFIG_PATHS = [
|
|
|
892
892
|
];
|
|
893
893
|
var CANONICAL_CONFIG_PATH = ".pourkit/config.json";
|
|
894
894
|
async function loadRepoConfig(repoRoot2, _configFileName) {
|
|
895
|
-
const { existsSync:
|
|
895
|
+
const { existsSync: existsSync23 } = await import("fs");
|
|
896
896
|
const { join: pjoin } = await import("path");
|
|
897
897
|
for (const obPath of OBSOLETE_CONFIG_PATHS) {
|
|
898
898
|
const fullPath = pjoin(repoRoot2, obPath);
|
|
899
|
-
if (
|
|
899
|
+
if (existsSync23(fullPath)) {
|
|
900
900
|
const isRootJson = obPath === "pourkit.json";
|
|
901
901
|
throw new Error(
|
|
902
902
|
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".`
|
|
@@ -904,13 +904,13 @@ async function loadRepoConfig(repoRoot2, _configFileName) {
|
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
const configPath = pjoin(repoRoot2, CANONICAL_CONFIG_PATH);
|
|
907
|
-
if (!
|
|
907
|
+
if (!existsSync23(configPath)) {
|
|
908
908
|
throw new Error(
|
|
909
909
|
`No Pourkit config found at ${CANONICAL_CONFIG_PATH}. Run pourkit init or create ${CANONICAL_CONFIG_PATH} with "$schema": "./schema/pourkit.schema.json".`
|
|
910
910
|
);
|
|
911
911
|
}
|
|
912
|
-
const { readFile:
|
|
913
|
-
const raw = await
|
|
912
|
+
const { readFile: readFile8 } = await import("fs/promises");
|
|
913
|
+
const raw = await readFile8(configPath, "utf-8");
|
|
914
914
|
let parsed;
|
|
915
915
|
try {
|
|
916
916
|
parsed = JSON.parse(raw);
|
|
@@ -924,7 +924,7 @@ function resolvePromptTemplatePath(repoRoot2, promptTemplate) {
|
|
|
924
924
|
if (promptTemplate.includes("/")) {
|
|
925
925
|
return join(repoRoot2, promptTemplate);
|
|
926
926
|
}
|
|
927
|
-
return join(repoRoot2, ".pourkit", "prompts", promptTemplate);
|
|
927
|
+
return join(repoRoot2, ".pourkit", "managed", "prompts", promptTemplate);
|
|
928
928
|
}
|
|
929
929
|
function resolveTarget(config, explicitTarget) {
|
|
930
930
|
if (config.targets.length === 0) {
|
|
@@ -1067,10 +1067,10 @@ async function removeStaleWorktree(candidate, repoRoot2, logger) {
|
|
|
1067
1067
|
}
|
|
1068
1068
|
}
|
|
1069
1069
|
async function removeExpiredFiles(dirPath, retentionDays) {
|
|
1070
|
-
const { readdir:
|
|
1070
|
+
const { readdir: readdir4, stat, unlink, access: access2 } = await import("fs/promises");
|
|
1071
1071
|
let entries;
|
|
1072
1072
|
try {
|
|
1073
|
-
entries = await
|
|
1073
|
+
entries = await readdir4(dirPath);
|
|
1074
1074
|
} catch {
|
|
1075
1075
|
return;
|
|
1076
1076
|
}
|
|
@@ -2505,6 +2505,7 @@ function renderReviewCriteriaEffect(repoRoot2, criteria, fs) {
|
|
|
2505
2505
|
const snippetPath = join6(
|
|
2506
2506
|
repoRoot2,
|
|
2507
2507
|
".pourkit",
|
|
2508
|
+
"managed",
|
|
2508
2509
|
"prompts",
|
|
2509
2510
|
`reviewer-${criterion}.snippet.md`
|
|
2510
2511
|
);
|
|
@@ -3345,6 +3346,34 @@ function validateIssueFinalReviewArtifact(parsed, options) {
|
|
|
3345
3346
|
diagnostics.push(
|
|
3346
3347
|
`changedFiles: ${parsed.changedFiles.length} files`
|
|
3347
3348
|
);
|
|
3349
|
+
if (options.baseRef && parsed.verdict === "pass") {
|
|
3350
|
+
const expectedChangedFiles = listIssueFinalReviewChangedPaths({
|
|
3351
|
+
worktreePath: options.worktreePath ?? process.cwd(),
|
|
3352
|
+
baseRef: options.baseRef
|
|
3353
|
+
});
|
|
3354
|
+
const declaredChangedFiles = new Set(
|
|
3355
|
+
parsed.changedFiles.map(
|
|
3356
|
+
(file) => normalizeGitPath(String(file.path))
|
|
3357
|
+
)
|
|
3358
|
+
);
|
|
3359
|
+
const missing = expectedChangedFiles.filter(
|
|
3360
|
+
(path9) => !declaredChangedFiles.has(path9)
|
|
3361
|
+
);
|
|
3362
|
+
if (missing.length > 0) {
|
|
3363
|
+
return {
|
|
3364
|
+
ok: false,
|
|
3365
|
+
reason: `pass verdict changedFiles is missing ${missing.length} changed file(s): ${missing.join(", ")}`,
|
|
3366
|
+
diagnostics: [
|
|
3367
|
+
...diagnostics,
|
|
3368
|
+
`baseRef: ${options.baseRef}`,
|
|
3369
|
+
`missingChangedFiles: ${missing.join(", ")}`
|
|
3370
|
+
]
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
diagnostics.push(
|
|
3374
|
+
`changedFiles coverage: ${expectedChangedFiles.length} files from ${options.baseRef}`
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3348
3377
|
if (!Array.isArray(parsed.outOfScopeFiles) || !parsed.outOfScopeFiles.every((p) => typeof p === "string")) {
|
|
3349
3378
|
return {
|
|
3350
3379
|
ok: false,
|
|
@@ -3627,9 +3656,44 @@ function runValidateArtifactCommand(options) {
|
|
|
3627
3656
|
...options,
|
|
3628
3657
|
artifactPath,
|
|
3629
3658
|
latestReviewArtifactPath,
|
|
3630
|
-
worktreePath: options.repoRoot
|
|
3659
|
+
worktreePath: options.repoRoot,
|
|
3660
|
+
baseRef: options.baseRef
|
|
3631
3661
|
});
|
|
3632
3662
|
}
|
|
3663
|
+
function listIssueFinalReviewChangedPaths(options) {
|
|
3664
|
+
const trackedDiff = String(
|
|
3665
|
+
execSync(`git diff-index --name-only ${shellQuote(options.baseRef)} --`, {
|
|
3666
|
+
cwd: options.worktreePath,
|
|
3667
|
+
encoding: "utf-8",
|
|
3668
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3669
|
+
})
|
|
3670
|
+
);
|
|
3671
|
+
const untrackedFiles = String(
|
|
3672
|
+
execSync("git ls-files --others --exclude-standard", {
|
|
3673
|
+
cwd: options.worktreePath,
|
|
3674
|
+
encoding: "utf-8",
|
|
3675
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3676
|
+
})
|
|
3677
|
+
);
|
|
3678
|
+
return Array.from(
|
|
3679
|
+
/* @__PURE__ */ new Set([
|
|
3680
|
+
...parseGitPathList(trackedDiff),
|
|
3681
|
+
...parseGitPathList(untrackedFiles)
|
|
3682
|
+
])
|
|
3683
|
+
).filter(isIssueFinalReviewAuditedPath);
|
|
3684
|
+
}
|
|
3685
|
+
function parseGitPathList(output) {
|
|
3686
|
+
return output.split(/\r?\n/).map(normalizeGitPath).filter((path9) => path9.length > 0);
|
|
3687
|
+
}
|
|
3688
|
+
function normalizeGitPath(path9) {
|
|
3689
|
+
return path9.replace(/^"|"$/g, "").replace(/\\/g, "/").replace(/^\.\/+/g, "").replace(/\/+$/g, "");
|
|
3690
|
+
}
|
|
3691
|
+
function isIssueFinalReviewAuditedPath(path9) {
|
|
3692
|
+
return path9 !== "" && path9 !== ".pourkit/state.json" && !path9.startsWith(".pourkit/.tmp/") && !path9.startsWith(".pourkit/logs/");
|
|
3693
|
+
}
|
|
3694
|
+
function shellQuote(value) {
|
|
3695
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
3696
|
+
}
|
|
3633
3697
|
function runLocalValidateArtifactCommand(options) {
|
|
3634
3698
|
const resolvedPath = resolve3(options.repoRoot, options.artifactPath);
|
|
3635
3699
|
const resolvedExtra = (options.extraArgs ?? []).map(
|
|
@@ -4935,7 +4999,7 @@ async function canReachMcp(url) {
|
|
|
4935
4999
|
return true;
|
|
4936
5000
|
} catch {
|
|
4937
5001
|
if (attempt < 9) {
|
|
4938
|
-
await new Promise((
|
|
5002
|
+
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
4939
5003
|
}
|
|
4940
5004
|
}
|
|
4941
5005
|
}
|
|
@@ -6671,7 +6735,11 @@ async function runIssueFinalReviewAgent(options) {
|
|
|
6671
6735
|
const strategy = target.strategy;
|
|
6672
6736
|
const issueFinalReview = strategy.issueFinalReview;
|
|
6673
6737
|
const agent = issueFinalReview;
|
|
6674
|
-
const prompt = loadIssueFinalReviewPrompt(
|
|
6738
|
+
const prompt = loadIssueFinalReviewPrompt({
|
|
6739
|
+
repoRoot: repoRoot2,
|
|
6740
|
+
promptTemplate: agent.promptTemplate,
|
|
6741
|
+
validationCommand: `pourkit validate-artifact issue-final-review ${artifactPathInWorktree} --issue-number ${issue.number} --branch-name ${builderBranch} --base-ref origin/${target.baseBranch}`
|
|
6742
|
+
});
|
|
6675
6743
|
const entry = await executeWithMissingOrEmptyArtifactRetry({
|
|
6676
6744
|
executionProvider,
|
|
6677
6745
|
missingOrEmptyRetries: resolveMissingOrEmptyOutputRetries(agent),
|
|
@@ -6764,7 +6832,8 @@ function extractChangedPaths(parsed) {
|
|
|
6764
6832
|
(file) => file && typeof file === "object" ? file.path : void 0
|
|
6765
6833
|
).filter((path9) => typeof path9 === "string") : [];
|
|
6766
6834
|
}
|
|
6767
|
-
function loadIssueFinalReviewPrompt(
|
|
6835
|
+
function loadIssueFinalReviewPrompt(options) {
|
|
6836
|
+
const { repoRoot: repoRoot2, promptTemplate, validationCommand } = options;
|
|
6768
6837
|
const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
|
|
6769
6838
|
const promptBody = existsSync12(promptPath) ? readFileSync14(promptPath, "utf-8") : promptTemplate;
|
|
6770
6839
|
return appendProtectedWorkGuidance(`${promptBody}
|
|
@@ -6780,7 +6849,24 @@ Read the selected issue requirements, PRD context, comments, branch context, ver
|
|
|
6780
6849
|
- Run the commands exactly as configured. Do not substitute narrower commands unless the configured command cannot run.
|
|
6781
6850
|
- If a configured command fails, keep reviewing after recording the failure details; use the failure output as review evidence.
|
|
6782
6851
|
- If a command cannot run because the environment is missing required setup, dependencies, or scripts outside agent control, treat it as a human handoff blocker.
|
|
6783
|
-
- If no verification commands are configured, note that and proceed with normal review
|
|
6852
|
+
- If no verification commands are configured, note that and proceed with normal review.
|
|
6853
|
+
|
|
6854
|
+
## Changed Files Audit
|
|
6855
|
+
|
|
6856
|
+
- Read the Branch section of ${RUN_CONTEXT_PATH_IN_WORKTREE} and use its Canonical Base Ref for the changed-file inventory.
|
|
6857
|
+
- Before writing \`changedFiles\`, run \`git diff-index --name-only <canonical-base-ref> --\` from the Worktree to enumerate tracked files changed by the Issue.
|
|
6858
|
+
- Also run \`git ls-files --others --exclude-standard\` from the Worktree to enumerate untracked files changed by the Issue.
|
|
6859
|
+
- Treat the union of those command outputs, excluding only \`.pourkit/.tmp/**\`, \`.pourkit/logs/**\`, and \`.pourkit/state.json\`, as the required \`changedFiles\` inventory.
|
|
6860
|
+
- Include every file from that inventory in \`changedFiles\`, including generated files, managed operating assets, docs, prompts, schema files, and large batches.
|
|
6861
|
+
- If any file from the inventory is out-of-scope, mark it \`out-of-scope\` and either self-retouch it away or return \`needs_human_review\`; do not omit it.
|
|
6862
|
+
- Do not return \`pass\` until \`changedFiles\` covers the full inventory.
|
|
6863
|
+
|
|
6864
|
+
## Required Artifact Validation
|
|
6865
|
+
|
|
6866
|
+
- Before handoff, run exactly: \`${validationCommand}\`
|
|
6867
|
+
- This validation checks both the artifact shape and \`changedFiles\` coverage against the canonical Git diff.
|
|
6868
|
+
- If validation fails, fix the artifact or safely retouch the worktree as needed, then rerun the same command.
|
|
6869
|
+
- Do not finish until the validation command passes.`);
|
|
6784
6870
|
}
|
|
6785
6871
|
|
|
6786
6872
|
// commands/issue-run.ts
|
|
@@ -6899,7 +6985,7 @@ async function advanceIssueFinalReview(options) {
|
|
|
6899
6985
|
logger
|
|
6900
6986
|
});
|
|
6901
6987
|
if (result.verdict === "pass") {
|
|
6902
|
-
await
|
|
6988
|
+
const changedPaths = await completeIssueFinalReviewChangedPaths({
|
|
6903
6989
|
worktreePath,
|
|
6904
6990
|
baseRef: `origin/${target.baseBranch}`,
|
|
6905
6991
|
declaredChangedPaths: result.changedPaths,
|
|
@@ -6911,10 +6997,14 @@ async function advanceIssueFinalReview(options) {
|
|
|
6911
6997
|
verdict: "pass",
|
|
6912
6998
|
artifactPath: result.artifactPath,
|
|
6913
6999
|
selfRetouched: result.selfRetouched,
|
|
6914
|
-
changedPaths
|
|
7000
|
+
changedPaths,
|
|
6915
7001
|
verificationPassed: result.verificationPassed
|
|
6916
7002
|
}
|
|
6917
7003
|
});
|
|
7004
|
+
return {
|
|
7005
|
+
...result,
|
|
7006
|
+
changedPaths
|
|
7007
|
+
};
|
|
6918
7008
|
}
|
|
6919
7009
|
return result;
|
|
6920
7010
|
}
|
|
@@ -7629,18 +7719,21 @@ async function assertCanonicalBaseAncestor(options) {
|
|
|
7629
7719
|
);
|
|
7630
7720
|
}
|
|
7631
7721
|
}
|
|
7632
|
-
async function
|
|
7633
|
-
const actualChangedPaths = await
|
|
7634
|
-
const declared = new Set(options.declaredChangedPaths.map(
|
|
7722
|
+
async function completeIssueFinalReviewChangedPaths(options) {
|
|
7723
|
+
const actualChangedPaths = await listIssueFinalReviewChangedPaths2(options);
|
|
7724
|
+
const declared = new Set(options.declaredChangedPaths.map(normalizeGitPath2));
|
|
7635
7725
|
const missing = actualChangedPaths.filter((path9) => !declared.has(path9));
|
|
7636
7726
|
if (missing.length === 0) {
|
|
7637
|
-
return;
|
|
7727
|
+
return actualChangedPaths;
|
|
7638
7728
|
}
|
|
7639
|
-
|
|
7640
|
-
|
|
7729
|
+
options.logger.step(
|
|
7730
|
+
"warn",
|
|
7731
|
+
`Issue Final Review pass omitted ${missing.length} changed file(s) from changedFiles; using git inventory for finalization state.`
|
|
7641
7732
|
);
|
|
7733
|
+
options.logger.kv("MISSING_CHANGED_FILES", missing.join(", "));
|
|
7734
|
+
return actualChangedPaths;
|
|
7642
7735
|
}
|
|
7643
|
-
async function
|
|
7736
|
+
async function listIssueFinalReviewChangedPaths2(options) {
|
|
7644
7737
|
const trackedDiff = await execCapture(
|
|
7645
7738
|
"git",
|
|
7646
7739
|
["diff-index", "--name-only", options.baseRef, "--"],
|
|
@@ -7661,18 +7754,18 @@ async function listIssueFinalReviewChangedPaths(options) {
|
|
|
7661
7754
|
);
|
|
7662
7755
|
return Array.from(
|
|
7663
7756
|
/* @__PURE__ */ new Set([
|
|
7664
|
-
...
|
|
7665
|
-
...
|
|
7757
|
+
...parseGitPathList2(trackedDiff.stdout),
|
|
7758
|
+
...parseGitPathList2(untrackedFiles.stdout)
|
|
7666
7759
|
])
|
|
7667
|
-
).filter(
|
|
7760
|
+
).filter(isIssueFinalReviewAuditedPath2);
|
|
7668
7761
|
}
|
|
7669
|
-
function
|
|
7670
|
-
return output.split(/\r?\n/).map(
|
|
7762
|
+
function parseGitPathList2(output) {
|
|
7763
|
+
return output.split(/\r?\n/).map(normalizeGitPath2).filter((path9) => path9.length > 0);
|
|
7671
7764
|
}
|
|
7672
|
-
function
|
|
7765
|
+
function normalizeGitPath2(path9) {
|
|
7673
7766
|
return path9.replace(/^"|"$/g, "").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
7674
7767
|
}
|
|
7675
|
-
function
|
|
7768
|
+
function isIssueFinalReviewAuditedPath2(path9) {
|
|
7676
7769
|
return path9 !== "" && path9 !== WORKTREE_RUN_STATE_PATH && !path9.startsWith(".pourkit/.tmp/") && !path9.startsWith(".pourkit/logs/");
|
|
7677
7770
|
}
|
|
7678
7771
|
async function syncRemoteBaseRef(worktreePath, baseRef, logger) {
|
|
@@ -11848,6 +11941,7 @@ import { createHash as createHash2, randomUUID } from "crypto";
|
|
|
11848
11941
|
import path5 from "path";
|
|
11849
11942
|
import { execFile as execFile2 } from "child_process";
|
|
11850
11943
|
import { promisify as promisify2 } from "util";
|
|
11944
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11851
11945
|
import { confirm, isCancel, log, select, text } from "@clack/prompts";
|
|
11852
11946
|
|
|
11853
11947
|
// providers/github-client.ts
|
|
@@ -11933,8 +12027,134 @@ async function tryCreateGitHubClient(options) {
|
|
|
11933
12027
|
return { ok: true, client: { octokit, ...repo } };
|
|
11934
12028
|
}
|
|
11935
12029
|
|
|
12030
|
+
// workflow-pack.ts
|
|
12031
|
+
var BASELINE_MANAGED_SKILLS = [
|
|
12032
|
+
"code-review",
|
|
12033
|
+
"diagnose",
|
|
12034
|
+
"explain",
|
|
12035
|
+
"grill-with-docs",
|
|
12036
|
+
"handoff",
|
|
12037
|
+
"improve-codebase-architecture",
|
|
12038
|
+
"publish-prd",
|
|
12039
|
+
"secret-commit-guard",
|
|
12040
|
+
"security-review",
|
|
12041
|
+
"ship-current-changes",
|
|
12042
|
+
"tdd",
|
|
12043
|
+
"to-issues",
|
|
12044
|
+
"to-plan",
|
|
12045
|
+
"to-prd",
|
|
12046
|
+
"triage",
|
|
12047
|
+
"write-a-skill",
|
|
12048
|
+
"zoom-out"
|
|
12049
|
+
];
|
|
12050
|
+
var RELEASE_ADDON_SKILLS = ["changeset", "promote-release"];
|
|
12051
|
+
var RELEASE_ADDON_DOCS = ["release-workflow.md"];
|
|
12052
|
+
var MANAGED_DOCS = [
|
|
12053
|
+
"git-workflow.md",
|
|
12054
|
+
"issue-tracker.md",
|
|
12055
|
+
"naming.md",
|
|
12056
|
+
"triage-labels.md",
|
|
12057
|
+
"commit-style.md",
|
|
12058
|
+
"domain.md"
|
|
12059
|
+
];
|
|
12060
|
+
var MANAGED_PROMPTS = [
|
|
12061
|
+
"builder.prompt.md",
|
|
12062
|
+
"conflict-resolution.prompt.md",
|
|
12063
|
+
"failure-resolution.prompt.md",
|
|
12064
|
+
"finalizer.prompt.md",
|
|
12065
|
+
"issue-final-review.prompt.md",
|
|
12066
|
+
"pr-description.prompt.md",
|
|
12067
|
+
"refactor.prompt.md",
|
|
12068
|
+
"reviewer.prompt.md",
|
|
12069
|
+
"reviewer-correctness.snippet.md",
|
|
12070
|
+
"reviewer-quality.snippet.md",
|
|
12071
|
+
"reviewer-scope.snippet.md",
|
|
12072
|
+
"reviewer-tests.snippet.md"
|
|
12073
|
+
];
|
|
12074
|
+
var OPENCODE_PROJECTION = {
|
|
12075
|
+
harness: "opencode",
|
|
12076
|
+
path: ".agents/skills"
|
|
12077
|
+
};
|
|
12078
|
+
var CURRENT_VERSION = "0.0.0-next";
|
|
12079
|
+
function getWorkflowPackCatalog() {
|
|
12080
|
+
return {
|
|
12081
|
+
docs: [...MANAGED_DOCS],
|
|
12082
|
+
prompts: [...MANAGED_PROMPTS],
|
|
12083
|
+
baselineSkills: [...BASELINE_MANAGED_SKILLS],
|
|
12084
|
+
releaseAddonDocs: [...RELEASE_ADDON_DOCS],
|
|
12085
|
+
releaseAddonSkills: [...RELEASE_ADDON_SKILLS],
|
|
12086
|
+
projections: [OPENCODE_PROJECTION]
|
|
12087
|
+
};
|
|
12088
|
+
}
|
|
12089
|
+
function buildOpenCodeSkillProjectionContent(managedSourcePath, sourceContent) {
|
|
12090
|
+
const generatedNotice = `Generated from ${managedSourcePath}. Do not edit directly.`;
|
|
12091
|
+
if (managedSourcePath.endsWith(".md") || managedSourcePath.endsWith(".mdx")) {
|
|
12092
|
+
return `<!-- ${generatedNotice} -->
|
|
12093
|
+
${sourceContent}`;
|
|
12094
|
+
}
|
|
12095
|
+
if (managedSourcePath.endsWith(".sh") || managedSourcePath.endsWith(".bash") || managedSourcePath.endsWith(".zsh") || managedSourcePath.endsWith(".py") || managedSourcePath.endsWith(".rb") || managedSourcePath.endsWith(".yml") || managedSourcePath.endsWith(".yaml") || managedSourcePath.endsWith(".toml")) {
|
|
12096
|
+
const comment = `# ${generatedNotice}`;
|
|
12097
|
+
if (sourceContent.startsWith("#!")) {
|
|
12098
|
+
const lineBreakIndex = sourceContent.indexOf("\n");
|
|
12099
|
+
if (lineBreakIndex === -1) {
|
|
12100
|
+
return `${sourceContent}
|
|
12101
|
+
${comment}
|
|
12102
|
+
`;
|
|
12103
|
+
}
|
|
12104
|
+
return `${sourceContent.slice(0, lineBreakIndex + 1)}${comment}
|
|
12105
|
+
${sourceContent.slice(lineBreakIndex + 1)}`;
|
|
12106
|
+
}
|
|
12107
|
+
return `${comment}
|
|
12108
|
+
${sourceContent}`;
|
|
12109
|
+
}
|
|
12110
|
+
return sourceContent;
|
|
12111
|
+
}
|
|
12112
|
+
function getBaselineManagedSkillNames() {
|
|
12113
|
+
return BASELINE_MANAGED_SKILLS;
|
|
12114
|
+
}
|
|
12115
|
+
function getWorkflowPackVersion() {
|
|
12116
|
+
return CURRENT_VERSION;
|
|
12117
|
+
}
|
|
12118
|
+
function buildWorkflowPackMetadata(enabledAddons) {
|
|
12119
|
+
return {
|
|
12120
|
+
version: getWorkflowPackVersion(),
|
|
12121
|
+
enabledAddons,
|
|
12122
|
+
projections: [OPENCODE_PROJECTION]
|
|
12123
|
+
};
|
|
12124
|
+
}
|
|
12125
|
+
|
|
11936
12126
|
// commands/init.ts
|
|
11937
12127
|
var execFileAsync2 = promisify2(execFile2);
|
|
12128
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
12129
|
+
var __dirname2 = path5.dirname(__filename2);
|
|
12130
|
+
var RELEASE_ADDON_SKILL_NAMES = ["changeset", "promote-release"];
|
|
12131
|
+
function resolvePackagedSourceRoot() {
|
|
12132
|
+
return path5.resolve(__dirname2, "..");
|
|
12133
|
+
}
|
|
12134
|
+
function resolveSourceAssetPath(sourceRoot, ...segments) {
|
|
12135
|
+
const packagedPath = path5.join(sourceRoot, ...segments);
|
|
12136
|
+
if (existsSync18(packagedPath)) return packagedPath;
|
|
12137
|
+
return path5.join(sourceRoot, "pourkit", ...segments);
|
|
12138
|
+
}
|
|
12139
|
+
var BASELINE_SKILL_NAMES = [
|
|
12140
|
+
"code-review",
|
|
12141
|
+
"diagnose",
|
|
12142
|
+
"explain",
|
|
12143
|
+
"grill-with-docs",
|
|
12144
|
+
"handoff",
|
|
12145
|
+
"improve-codebase-architecture",
|
|
12146
|
+
"publish-prd",
|
|
12147
|
+
"secret-commit-guard",
|
|
12148
|
+
"security-review",
|
|
12149
|
+
"ship-current-changes",
|
|
12150
|
+
"tdd",
|
|
12151
|
+
"to-issues",
|
|
12152
|
+
"to-plan",
|
|
12153
|
+
"to-prd",
|
|
12154
|
+
"triage",
|
|
12155
|
+
"write-a-skill",
|
|
12156
|
+
"zoom-out"
|
|
12157
|
+
];
|
|
11938
12158
|
var NO_TOKEN_LABEL_PROVISIONING_WARNING = "Skipped GitHub label provisioning because no GitHub token was provided.";
|
|
11939
12159
|
var ALLOWED_VERIFICATION_SCRIPTS = [
|
|
11940
12160
|
"typecheck",
|
|
@@ -12077,26 +12297,26 @@ function generateConfigTemplate(options) {
|
|
|
12077
12297
|
builder: {
|
|
12078
12298
|
agent: "pourkit-builder",
|
|
12079
12299
|
model: "opencode-go/deepseek-v4-flash",
|
|
12080
|
-
promptTemplate: ".pourkit/prompts/builder.prompt.md"
|
|
12300
|
+
promptTemplate: ".pourkit/managed/prompts/builder.prompt.md"
|
|
12081
12301
|
}
|
|
12082
12302
|
},
|
|
12083
12303
|
failureResolution: {
|
|
12084
12304
|
agent: "pourkit-failure-resolution-agent",
|
|
12085
12305
|
model: "opencode-go/mimo-v2.5",
|
|
12086
|
-
promptTemplate: ".pourkit/prompts/failure-resolution.prompt.md",
|
|
12306
|
+
promptTemplate: ".pourkit/managed/prompts/failure-resolution.prompt.md",
|
|
12087
12307
|
maxAttemptsPerFailure: 1
|
|
12088
12308
|
},
|
|
12089
12309
|
review: {
|
|
12090
12310
|
reviewer: {
|
|
12091
12311
|
agent: "pourkit-reviewer",
|
|
12092
12312
|
model: "opencode-go/deepseek-v4-pro",
|
|
12093
|
-
promptTemplate: ".pourkit/prompts/reviewer.prompt.md",
|
|
12313
|
+
promptTemplate: ".pourkit/managed/prompts/reviewer.prompt.md",
|
|
12094
12314
|
criteria: ["correctness", "scope", "tests", "quality"]
|
|
12095
12315
|
},
|
|
12096
12316
|
refactor: {
|
|
12097
12317
|
agent: "pourkit-refactor",
|
|
12098
12318
|
model: "opencode-go/qwen3.6-plus",
|
|
12099
|
-
promptTemplate: ".pourkit/prompts/refactor.prompt.md"
|
|
12319
|
+
promptTemplate: ".pourkit/managed/prompts/refactor.prompt.md"
|
|
12100
12320
|
},
|
|
12101
12321
|
maxIterations: 3,
|
|
12102
12322
|
passWithNotesRefactorAttempts: 2
|
|
@@ -12104,14 +12324,14 @@ function generateConfigTemplate(options) {
|
|
|
12104
12324
|
issueFinalReview: {
|
|
12105
12325
|
agent: "pourkit-reviewer",
|
|
12106
12326
|
model: "opencode-go/deepseek-v4-pro",
|
|
12107
|
-
promptTemplate: ".pourkit/prompts/issue-final-review.prompt.md",
|
|
12327
|
+
promptTemplate: ".pourkit/managed/prompts/issue-final-review.prompt.md",
|
|
12108
12328
|
maxAttempts: 3
|
|
12109
12329
|
},
|
|
12110
12330
|
finalize: {
|
|
12111
12331
|
prDescriptionAgent: {
|
|
12112
12332
|
agent: "pourkit-pr-description",
|
|
12113
12333
|
model: "opencode-go/deepseek-v4-flash",
|
|
12114
|
-
promptTemplate: ".pourkit/prompts/pr-description.prompt.md"
|
|
12334
|
+
promptTemplate: ".pourkit/managed/prompts/pr-description.prompt.md"
|
|
12115
12335
|
},
|
|
12116
12336
|
maxAttempts: 2
|
|
12117
12337
|
}
|
|
@@ -12131,6 +12351,17 @@ function generateConfigTemplate(options) {
|
|
|
12131
12351
|
$schema: "./schema/pourkit.schema.json",
|
|
12132
12352
|
schemaVersion: 1,
|
|
12133
12353
|
targets: [target],
|
|
12354
|
+
workflowPack: {
|
|
12355
|
+
version: "1.0.0",
|
|
12356
|
+
skillProjections: [{ harness: "opencode", path: ".agents/skills" }]
|
|
12357
|
+
},
|
|
12358
|
+
releaseWorkflow: options.releaseWorkflow?.enabled ? {
|
|
12359
|
+
enabled: true,
|
|
12360
|
+
integrationBranch: options.releaseWorkflow.integrationBranch ?? "integration",
|
|
12361
|
+
developmentReleaseBranch: options.releaseWorkflow.developmentReleaseBranch ?? "preview",
|
|
12362
|
+
stableReleaseBranch: options.releaseWorkflow.stableReleaseBranch ?? "stable",
|
|
12363
|
+
changesets: options.releaseWorkflow.changesets ?? true
|
|
12364
|
+
} : { enabled: false },
|
|
12134
12365
|
labels: {
|
|
12135
12366
|
readyForAgent: labels.readyForAgent,
|
|
12136
12367
|
agentInProgress: labels.agentInProgress,
|
|
@@ -12278,18 +12509,33 @@ _Avoid_: Alternative terms
|
|
|
12278
12509
|
- <Document ambiguous terms and their resolution here.>
|
|
12279
12510
|
`;
|
|
12280
12511
|
}
|
|
12281
|
-
async function generateManagedAgentInstructions(
|
|
12282
|
-
|
|
12283
|
-
try {
|
|
12284
|
-
return await readFile5(sourcePath, "utf-8");
|
|
12285
|
-
} catch {
|
|
12286
|
-
return `## Codebase exploration
|
|
12512
|
+
async function generateManagedAgentInstructions(_options) {
|
|
12513
|
+
return `## Codebase exploration
|
|
12287
12514
|
|
|
12288
12515
|
Use \`fd\` for file discovery, \`rg\` for text search, and direct file reads for focused context.
|
|
12289
12516
|
|
|
12290
|
-
Follow the project's domain docs and conventions documented in \`.pourkit/docs/agents/*\`.
|
|
12517
|
+
Follow the project's domain docs and conventions documented in \`.pourkit/managed/docs/agents/*\`.
|
|
12518
|
+
|
|
12519
|
+
### Managed operating docs
|
|
12520
|
+
|
|
12521
|
+
- \`.pourkit/managed/docs/agents/issue-tracker.md\`
|
|
12522
|
+
- \`.pourkit/managed/docs/agents/triage-labels.md\`
|
|
12523
|
+
- \`.pourkit/managed/docs/agents/naming.md\`
|
|
12524
|
+
- \`.pourkit/managed/docs/agents/git-workflow.md\`
|
|
12525
|
+
- \`.pourkit/managed/docs/agents/commit-style.md\`
|
|
12526
|
+
- \`.pourkit/managed/docs/agents/domain.md\`
|
|
12527
|
+
|
|
12528
|
+
### Project-owned context
|
|
12529
|
+
|
|
12530
|
+
- \`.pourkit/CONTEXT.md\`
|
|
12531
|
+
- \`.pourkit/CONTEXT-MAP.md\`
|
|
12532
|
+
- \`.pourkit/docs/adr/*\`
|
|
12533
|
+
|
|
12534
|
+
### Important
|
|
12535
|
+
|
|
12536
|
+
- Do not edit \`.pourkit/managed/*\` unless explicitly updating Pourkit-managed assets.
|
|
12537
|
+
- Do not edit generated harness projections directly.
|
|
12291
12538
|
`;
|
|
12292
|
-
}
|
|
12293
12539
|
}
|
|
12294
12540
|
function generateGitignoreBlock() {
|
|
12295
12541
|
const lines = [
|
|
@@ -12455,16 +12701,24 @@ async function checkExistingLabelConflicts(client, canonicalLabels) {
|
|
|
12455
12701
|
}
|
|
12456
12702
|
}
|
|
12457
12703
|
async function planInit(options) {
|
|
12458
|
-
const { targetRoot
|
|
12704
|
+
const { targetRoot } = options;
|
|
12705
|
+
let sourceRoot = options.sourceRoot ?? resolvePackagedSourceRoot();
|
|
12706
|
+
let usingExplicitSource = Boolean(options.sourceRoot);
|
|
12459
12707
|
const operations = [];
|
|
12460
12708
|
const warnings = [];
|
|
12461
12709
|
let hasLabelConflicts = false;
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12710
|
+
if (options.sourceRoot) {
|
|
12711
|
+
warnings.push("--from-local is deprecated; omit it to use packaged assets");
|
|
12712
|
+
operations.push({
|
|
12713
|
+
kind: "warn",
|
|
12714
|
+
reason: "--from-local is deprecated; omit it to use packaged assets",
|
|
12715
|
+
destructive: false,
|
|
12716
|
+
requiresConfirmation: false
|
|
12466
12717
|
});
|
|
12467
12718
|
}
|
|
12719
|
+
const managedAgentContent = await generateManagedAgentInstructions({
|
|
12720
|
+
sourceRoot
|
|
12721
|
+
});
|
|
12468
12722
|
const pm = options.packageManager ?? detectPackageManager(targetRoot);
|
|
12469
12723
|
if (pm) {
|
|
12470
12724
|
operations.push({
|
|
@@ -12495,39 +12749,28 @@ async function planInit(options) {
|
|
|
12495
12749
|
const agentFiles = await discoverAgentFiles(targetRoot);
|
|
12496
12750
|
if (agentFiles.length > 0) {
|
|
12497
12751
|
for (const f of agentFiles) {
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
if (agentFileMode === "skip" || agentFileMode === "agents" && basename !== "AGENTS.md" || agentFileMode === "claude" && basename !== "CLAUDE.md") {
|
|
12502
|
-
operations.push({
|
|
12503
|
-
kind: "skip",
|
|
12504
|
-
path: f,
|
|
12505
|
-
ownership: "project-owned",
|
|
12506
|
-
reason: `Existing agent file skipped per policy: ${basename}`,
|
|
12507
|
-
requiresConfirmation: false,
|
|
12508
|
-
destructive: false
|
|
12509
|
-
});
|
|
12510
|
-
continue;
|
|
12511
|
-
}
|
|
12512
|
-
operations.push({
|
|
12513
|
-
kind: "update",
|
|
12514
|
-
path: f,
|
|
12515
|
-
ownership: "managed",
|
|
12516
|
-
reason: `Update existing ${basename} with Pourkit managed block`,
|
|
12517
|
-
requiresConfirmation: false,
|
|
12518
|
-
destructive: false,
|
|
12519
|
-
content: managedAgentContent
|
|
12520
|
-
});
|
|
12521
|
-
} else {
|
|
12752
|
+
const agentFileMode = options.conflictPolicy?.agentFile ?? "both";
|
|
12753
|
+
const basename = path5.basename(f);
|
|
12754
|
+
if (agentFileMode === "skip" || agentFileMode === "agents" && basename !== "AGENTS.md" || agentFileMode === "claude" && basename !== "CLAUDE.md") {
|
|
12522
12755
|
operations.push({
|
|
12523
12756
|
kind: "skip",
|
|
12524
12757
|
path: f,
|
|
12525
12758
|
ownership: "project-owned",
|
|
12526
|
-
reason: `Existing agent file: ${
|
|
12759
|
+
reason: `Existing agent file skipped per policy: ${basename}`,
|
|
12527
12760
|
requiresConfirmation: false,
|
|
12528
12761
|
destructive: false
|
|
12529
12762
|
});
|
|
12763
|
+
continue;
|
|
12530
12764
|
}
|
|
12765
|
+
operations.push({
|
|
12766
|
+
kind: "update",
|
|
12767
|
+
path: f,
|
|
12768
|
+
ownership: "managed",
|
|
12769
|
+
reason: `Update existing ${basename} with Pourkit managed block`,
|
|
12770
|
+
requiresConfirmation: false,
|
|
12771
|
+
destructive: false,
|
|
12772
|
+
content: managedAgentContent
|
|
12773
|
+
});
|
|
12531
12774
|
}
|
|
12532
12775
|
}
|
|
12533
12776
|
const merlleState = await discoverMerlleState(targetRoot);
|
|
@@ -12621,7 +12864,10 @@ async function planInit(options) {
|
|
|
12621
12864
|
destructive: false,
|
|
12622
12865
|
requiresConfirmation: false
|
|
12623
12866
|
});
|
|
12624
|
-
|
|
12867
|
+
sourceRoot = resolvePackagedSourceRoot();
|
|
12868
|
+
usingExplicitSource = false;
|
|
12869
|
+
}
|
|
12870
|
+
{
|
|
12625
12871
|
let sourceMeta;
|
|
12626
12872
|
try {
|
|
12627
12873
|
sourceMeta = await discoverLocalSource(sourceRoot);
|
|
@@ -12634,17 +12880,19 @@ async function planInit(options) {
|
|
|
12634
12880
|
releaseChannel: "unknown",
|
|
12635
12881
|
latestTag: null
|
|
12636
12882
|
};
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12883
|
+
if (usingExplicitSource) {
|
|
12884
|
+
warnings.push(
|
|
12885
|
+
"Source is not a Git repository; version metadata unavailable"
|
|
12886
|
+
);
|
|
12887
|
+
operations.push({
|
|
12888
|
+
kind: "warn",
|
|
12889
|
+
reason: "Source is not a Git repository; version metadata unavailable",
|
|
12890
|
+
destructive: false,
|
|
12891
|
+
requiresConfirmation: false
|
|
12892
|
+
});
|
|
12893
|
+
}
|
|
12646
12894
|
}
|
|
12647
|
-
if (sourceMeta.sourceDirty) {
|
|
12895
|
+
if (usingExplicitSource && sourceMeta.sourceDirty) {
|
|
12648
12896
|
warnings.push("Local source has uncommitted changes");
|
|
12649
12897
|
operations.push({
|
|
12650
12898
|
kind: "warn",
|
|
@@ -12655,11 +12903,26 @@ async function planInit(options) {
|
|
|
12655
12903
|
}
|
|
12656
12904
|
const plannedSkillDests = /* @__PURE__ */ new Set();
|
|
12657
12905
|
for (const op of operations) {
|
|
12658
|
-
if (op.
|
|
12906
|
+
if (op.path?.includes(".agents/skills")) {
|
|
12659
12907
|
plannedSkillDests.add(op.path);
|
|
12660
12908
|
}
|
|
12661
12909
|
}
|
|
12662
|
-
const
|
|
12910
|
+
const packagedManagedSkills = resolveSourceAssetPath(
|
|
12911
|
+
sourceRoot,
|
|
12912
|
+
"managed",
|
|
12913
|
+
"skills"
|
|
12914
|
+
);
|
|
12915
|
+
const packagedReleaseAddonSkills = resolveSourceAssetPath(
|
|
12916
|
+
sourceRoot,
|
|
12917
|
+
"managed",
|
|
12918
|
+
"addons",
|
|
12919
|
+
"release",
|
|
12920
|
+
"skills"
|
|
12921
|
+
);
|
|
12922
|
+
const srcSkills = existsSync18(packagedManagedSkills) ? [
|
|
12923
|
+
packagedManagedSkills,
|
|
12924
|
+
...existsSync18(packagedReleaseAddonSkills) ? [packagedReleaseAddonSkills] : []
|
|
12925
|
+
] : await discoverAgentSkills(sourceRoot);
|
|
12663
12926
|
for (const s of srcSkills) {
|
|
12664
12927
|
const isOpenCode = s.includes(".opencode");
|
|
12665
12928
|
if (isOpenCode && !options.legacySkills) {
|
|
@@ -12671,35 +12934,118 @@ async function planInit(options) {
|
|
|
12671
12934
|
});
|
|
12672
12935
|
continue;
|
|
12673
12936
|
}
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12937
|
+
if (isOpenCode) {
|
|
12938
|
+
const skillFiles = await walkDir(s);
|
|
12939
|
+
for (const file of skillFiles) {
|
|
12940
|
+
const relPath = path5.relative(s, file);
|
|
12941
|
+
const destPath = path5.join(
|
|
12942
|
+
targetRoot,
|
|
12943
|
+
".agents",
|
|
12944
|
+
"skills",
|
|
12945
|
+
relPath
|
|
12946
|
+
);
|
|
12947
|
+
if (!existsSync18(destPath)) {
|
|
12948
|
+
operations.push({
|
|
12949
|
+
kind: "copy",
|
|
12950
|
+
sourcePath: file,
|
|
12951
|
+
path: destPath,
|
|
12952
|
+
ownership: "project-owned",
|
|
12953
|
+
reason: `Migrate legacy skill: ${path5.join(".opencode/skills", relPath)}`,
|
|
12954
|
+
requiresConfirmation: false,
|
|
12955
|
+
destructive: false
|
|
12956
|
+
});
|
|
12957
|
+
}
|
|
12958
|
+
}
|
|
12959
|
+
continue;
|
|
12960
|
+
}
|
|
12961
|
+
const managedSkillsDir = path5.join(
|
|
12962
|
+
targetRoot,
|
|
12963
|
+
".pourkit",
|
|
12964
|
+
"managed",
|
|
12965
|
+
"skills"
|
|
12966
|
+
);
|
|
12967
|
+
const projectionSkillsDir = path5.join(targetRoot, ".agents", "skills");
|
|
12968
|
+
const entries = await readdir(s, { withFileTypes: true });
|
|
12969
|
+
const skillNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
12970
|
+
const addonManagedDir = path5.join(
|
|
12971
|
+
targetRoot,
|
|
12972
|
+
".pourkit",
|
|
12973
|
+
"managed",
|
|
12974
|
+
"addons",
|
|
12975
|
+
"release",
|
|
12976
|
+
"skills"
|
|
12977
|
+
);
|
|
12978
|
+
for (const skillName of skillNames) {
|
|
12979
|
+
const isReleaseAddon = RELEASE_ADDON_SKILL_NAMES.includes(skillName);
|
|
12980
|
+
const isBaseline = BASELINE_SKILL_NAMES.includes(skillName);
|
|
12981
|
+
if (isReleaseAddon) {
|
|
12982
|
+
if (!options.releaseWorkflow?.enabled) {
|
|
12983
|
+
operations.push({
|
|
12984
|
+
kind: "skip",
|
|
12985
|
+
reason: `Release add-on skill not enabled: ${skillName}`,
|
|
12986
|
+
requiresConfirmation: false,
|
|
12987
|
+
destructive: false
|
|
12988
|
+
});
|
|
12989
|
+
continue;
|
|
12990
|
+
}
|
|
12991
|
+
} else if (!isBaseline) {
|
|
12681
12992
|
operations.push({
|
|
12682
12993
|
kind: "skip",
|
|
12683
|
-
|
|
12684
|
-
ownership: "project-owned",
|
|
12685
|
-
reason: `Skill destination conflict, skipping source copy: ${path5.join(targetDirName, relPath)}`,
|
|
12994
|
+
reason: `Non-baseline skill not installed: ${skillName}`,
|
|
12686
12995
|
requiresConfirmation: false,
|
|
12687
|
-
destructive: false
|
|
12688
|
-
conflict: "destination already planned"
|
|
12996
|
+
destructive: false
|
|
12689
12997
|
});
|
|
12690
12998
|
continue;
|
|
12691
12999
|
}
|
|
12692
|
-
const
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
13000
|
+
const skillSourceDir = path5.join(s, skillName);
|
|
13001
|
+
const skillFiles = await walkDir(skillSourceDir);
|
|
13002
|
+
for (const file of skillFiles) {
|
|
13003
|
+
const relPath = path5.relative(skillSourceDir, file);
|
|
13004
|
+
const managedDest = isReleaseAddon ? path5.join(addonManagedDir, skillName, relPath) : path5.join(managedSkillsDir, skillName, relPath);
|
|
13005
|
+
const checksum = await computeFileChecksum(file);
|
|
13006
|
+
const installLabel = isReleaseAddon ? `Install release add-on skill: ${skillName}/${relPath}` : `Install managed skill: ${skillName}/${relPath}`;
|
|
13007
|
+
operations.push({
|
|
13008
|
+
kind: "copy",
|
|
13009
|
+
sourcePath: file,
|
|
13010
|
+
path: managedDest,
|
|
13011
|
+
ownership: "managed",
|
|
13012
|
+
reason: installLabel,
|
|
13013
|
+
requiresConfirmation: false,
|
|
13014
|
+
destructive: false,
|
|
13015
|
+
checksum
|
|
13016
|
+
});
|
|
13017
|
+
const projectionDest = path5.join(
|
|
13018
|
+
projectionSkillsDir,
|
|
13019
|
+
skillName,
|
|
13020
|
+
relPath
|
|
13021
|
+
);
|
|
13022
|
+
if (plannedSkillDests.has(projectionDest)) {
|
|
13023
|
+
operations.push({
|
|
13024
|
+
kind: "skip",
|
|
13025
|
+
path: projectionDest,
|
|
13026
|
+
ownership: "project-owned",
|
|
13027
|
+
reason: `Skill destination conflict, skipping projection: ${skillName}/${relPath}`,
|
|
13028
|
+
requiresConfirmation: false,
|
|
13029
|
+
destructive: false,
|
|
13030
|
+
conflict: "destination already planned"
|
|
13031
|
+
});
|
|
13032
|
+
continue;
|
|
13033
|
+
}
|
|
13034
|
+
const sourceContent = await readFile5(file, "utf-8");
|
|
13035
|
+
const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
|
|
13036
|
+
operations.push({
|
|
13037
|
+
kind: "create",
|
|
13038
|
+
path: projectionDest,
|
|
13039
|
+
ownership: "managed",
|
|
13040
|
+
reason: `Project skill: ${skillName}/${relPath}`,
|
|
13041
|
+
requiresConfirmation: false,
|
|
13042
|
+
destructive: false,
|
|
13043
|
+
content: buildOpenCodeSkillProjectionContent(
|
|
13044
|
+
managedSourcePath,
|
|
13045
|
+
sourceContent
|
|
13046
|
+
)
|
|
13047
|
+
});
|
|
13048
|
+
}
|
|
12703
13049
|
}
|
|
12704
13050
|
}
|
|
12705
13051
|
const srcReadme = await discoverReadme(sourceRoot);
|
|
@@ -12792,7 +13138,7 @@ async function planInit(options) {
|
|
|
12792
13138
|
operations.push({
|
|
12793
13139
|
kind: "create",
|
|
12794
13140
|
path: contextPath,
|
|
12795
|
-
ownership: "
|
|
13141
|
+
ownership: "project-owned",
|
|
12796
13142
|
reason: "Init CONTEXT.md scaffold",
|
|
12797
13143
|
requiresConfirmation: false,
|
|
12798
13144
|
destructive: false,
|
|
@@ -12816,16 +13162,40 @@ async function planInit(options) {
|
|
|
12816
13162
|
destructive: false
|
|
12817
13163
|
});
|
|
12818
13164
|
}
|
|
12819
|
-
const
|
|
12820
|
-
|
|
12821
|
-
|
|
13165
|
+
const packagedManagedDocAgents = resolveSourceAssetPath(
|
|
13166
|
+
sourceRoot,
|
|
13167
|
+
"managed",
|
|
13168
|
+
"docs",
|
|
13169
|
+
"agents"
|
|
13170
|
+
);
|
|
13171
|
+
const legacyDocAgents = path5.join(
|
|
13172
|
+
sourceRoot,
|
|
13173
|
+
".pourkit",
|
|
13174
|
+
"docs",
|
|
13175
|
+
"agents"
|
|
13176
|
+
);
|
|
13177
|
+
const srcDocAgents = existsSync18(packagedManagedDocAgents) ? packagedManagedDocAgents : legacyDocAgents;
|
|
13178
|
+
const tgtManagedDocAgents = path5.join(
|
|
13179
|
+
targetRoot,
|
|
13180
|
+
".pourkit",
|
|
13181
|
+
"managed",
|
|
13182
|
+
"docs",
|
|
13183
|
+
"agents"
|
|
13184
|
+
);
|
|
13185
|
+
const tgtStubDocAgents = path5.join(
|
|
13186
|
+
targetRoot,
|
|
13187
|
+
".pourkit",
|
|
13188
|
+
"docs",
|
|
13189
|
+
"agents"
|
|
13190
|
+
);
|
|
13191
|
+
if (existsSync18(srcDocAgents) && !existsSync18(tgtManagedDocAgents)) {
|
|
12822
13192
|
const docFiles = await walkDir(srcDocAgents);
|
|
12823
13193
|
for (const file of docFiles) {
|
|
12824
13194
|
const relPath = path5.relative(srcDocAgents, file);
|
|
12825
13195
|
if (relPath === "triage-labels.md") {
|
|
12826
13196
|
operations.push({
|
|
12827
13197
|
kind: "create",
|
|
12828
|
-
path: path5.join(
|
|
13198
|
+
path: path5.join(tgtManagedDocAgents, relPath),
|
|
12829
13199
|
ownership: "managed",
|
|
12830
13200
|
reason: "Init triage labels doc",
|
|
12831
13201
|
requiresConfirmation: false,
|
|
@@ -12834,24 +13204,92 @@ async function planInit(options) {
|
|
|
12834
13204
|
options.labels ?? DEFAULT_RUNNER_LABELS
|
|
12835
13205
|
)
|
|
12836
13206
|
});
|
|
12837
|
-
|
|
13207
|
+
} else {
|
|
13208
|
+
const checksum = await computeFileChecksum(file);
|
|
13209
|
+
operations.push({
|
|
13210
|
+
kind: "copy",
|
|
13211
|
+
sourcePath: file,
|
|
13212
|
+
path: path5.join(tgtManagedDocAgents, relPath),
|
|
13213
|
+
ownership: "managed",
|
|
13214
|
+
reason: `Copy agent doc: ${relPath}`,
|
|
13215
|
+
requiresConfirmation: false,
|
|
13216
|
+
destructive: false,
|
|
13217
|
+
checksum
|
|
13218
|
+
});
|
|
12838
13219
|
}
|
|
12839
|
-
|
|
13220
|
+
if (relPath === "triage-labels.md") continue;
|
|
13221
|
+
const managedRelPath = path5.join(
|
|
13222
|
+
".pourkit",
|
|
13223
|
+
"managed",
|
|
13224
|
+
"docs",
|
|
13225
|
+
"agents",
|
|
13226
|
+
relPath
|
|
13227
|
+
);
|
|
12840
13228
|
operations.push({
|
|
12841
|
-
kind: "
|
|
12842
|
-
|
|
12843
|
-
path: path5.join(tgtDocAgents, relPath),
|
|
13229
|
+
kind: "create",
|
|
13230
|
+
path: path5.join(tgtStubDocAgents, relPath),
|
|
12844
13231
|
ownership: "managed",
|
|
12845
|
-
reason: `
|
|
13232
|
+
reason: `Create moved stub for: ${relPath}`,
|
|
12846
13233
|
requiresConfirmation: false,
|
|
12847
13234
|
destructive: false,
|
|
12848
|
-
|
|
13235
|
+
content: `# Moved
|
|
13236
|
+
|
|
13237
|
+
This Pourkit operating doc moved to \`${managedRelPath}\`.
|
|
13238
|
+
Do not edit this file.
|
|
13239
|
+
`
|
|
12849
13240
|
});
|
|
12850
13241
|
}
|
|
12851
13242
|
}
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
13243
|
+
if (options.releaseWorkflow?.enabled) {
|
|
13244
|
+
const packagedReleaseAddonDocs = resolveSourceAssetPath(
|
|
13245
|
+
sourceRoot,
|
|
13246
|
+
"managed",
|
|
13247
|
+
"addons",
|
|
13248
|
+
"release",
|
|
13249
|
+
"docs",
|
|
13250
|
+
"agents"
|
|
13251
|
+
);
|
|
13252
|
+
const tgtReleaseAddonDocs = path5.join(
|
|
13253
|
+
targetRoot,
|
|
13254
|
+
".pourkit",
|
|
13255
|
+
"managed",
|
|
13256
|
+
"addons",
|
|
13257
|
+
"release",
|
|
13258
|
+
"docs",
|
|
13259
|
+
"agents"
|
|
13260
|
+
);
|
|
13261
|
+
if (existsSync18(packagedReleaseAddonDocs)) {
|
|
13262
|
+
const docFiles = await walkDir(packagedReleaseAddonDocs);
|
|
13263
|
+
for (const file of docFiles) {
|
|
13264
|
+
const relPath = path5.relative(packagedReleaseAddonDocs, file);
|
|
13265
|
+
const checksum = await computeFileChecksum(file);
|
|
13266
|
+
operations.push({
|
|
13267
|
+
kind: "copy",
|
|
13268
|
+
sourcePath: file,
|
|
13269
|
+
path: path5.join(tgtReleaseAddonDocs, relPath),
|
|
13270
|
+
ownership: "managed",
|
|
13271
|
+
reason: `Install release add-on doc: ${relPath}`,
|
|
13272
|
+
requiresConfirmation: false,
|
|
13273
|
+
destructive: false,
|
|
13274
|
+
checksum
|
|
13275
|
+
});
|
|
13276
|
+
}
|
|
13277
|
+
}
|
|
13278
|
+
}
|
|
13279
|
+
const packagedManagedPrompts = resolveSourceAssetPath(
|
|
13280
|
+
sourceRoot,
|
|
13281
|
+
"managed",
|
|
13282
|
+
"prompts"
|
|
13283
|
+
);
|
|
13284
|
+
const legacyPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
|
|
13285
|
+
const srcPrompts = existsSync18(packagedManagedPrompts) ? packagedManagedPrompts : legacyPrompts;
|
|
13286
|
+
const tgtManagedPrompts = path5.join(
|
|
13287
|
+
targetRoot,
|
|
13288
|
+
".pourkit",
|
|
13289
|
+
"managed",
|
|
13290
|
+
"prompts"
|
|
13291
|
+
);
|
|
13292
|
+
if (existsSync18(srcPrompts) && !existsSync18(tgtManagedPrompts)) {
|
|
12855
13293
|
const promptFiles = await walkDir(srcPrompts);
|
|
12856
13294
|
for (const file of promptFiles) {
|
|
12857
13295
|
const relPath = path5.relative(srcPrompts, file);
|
|
@@ -12859,7 +13297,7 @@ async function planInit(options) {
|
|
|
12859
13297
|
operations.push({
|
|
12860
13298
|
kind: "copy",
|
|
12861
13299
|
sourcePath: file,
|
|
12862
|
-
path: path5.join(
|
|
13300
|
+
path: path5.join(tgtManagedPrompts, relPath),
|
|
12863
13301
|
ownership: "managed",
|
|
12864
13302
|
reason: `Copy prompt: ${relPath}`,
|
|
12865
13303
|
requiresConfirmation: false,
|
|
@@ -12911,7 +13349,8 @@ async function planInit(options) {
|
|
|
12911
13349
|
baseBranch,
|
|
12912
13350
|
verificationCommands: verifyCommands,
|
|
12913
13351
|
hasPackageJson,
|
|
12914
|
-
labels: options.labels
|
|
13352
|
+
labels: options.labels,
|
|
13353
|
+
releaseWorkflow: options.releaseWorkflow
|
|
12915
13354
|
});
|
|
12916
13355
|
operations.push({
|
|
12917
13356
|
kind: "create",
|
|
@@ -12938,9 +13377,8 @@ async function planInit(options) {
|
|
|
12938
13377
|
"schema",
|
|
12939
13378
|
"pourkit.schema.json"
|
|
12940
13379
|
);
|
|
12941
|
-
const srcSchemaJson =
|
|
13380
|
+
const srcSchemaJson = resolveSourceAssetPath(
|
|
12942
13381
|
sourceRoot,
|
|
12943
|
-
"pourkit",
|
|
12944
13382
|
"schema",
|
|
12945
13383
|
"pourkit.schema.json"
|
|
12946
13384
|
);
|
|
@@ -12974,9 +13412,8 @@ async function planInit(options) {
|
|
|
12974
13412
|
"schema",
|
|
12975
13413
|
"pourkit.schema.hash"
|
|
12976
13414
|
);
|
|
12977
|
-
const srcSchemaHash =
|
|
13415
|
+
const srcSchemaHash = resolveSourceAssetPath(
|
|
12978
13416
|
sourceRoot,
|
|
12979
|
-
"pourkit",
|
|
12980
13417
|
"schema",
|
|
12981
13418
|
"pourkit.schema.hash"
|
|
12982
13419
|
);
|
|
@@ -13131,7 +13568,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13131
13568
|
conflict: "already exists"
|
|
13132
13569
|
});
|
|
13133
13570
|
}
|
|
13134
|
-
const sourceLabel = sourceMeta.versionSource === "local-git" ? `Init from local source (${sourceMeta.branch}@${sourceMeta.sha.slice(0, 7)})` : "Init from local source (non-Git)";
|
|
13571
|
+
const sourceLabel = usingExplicitSource ? sourceMeta.versionSource === "local-git" ? `Init from local source (${sourceMeta.branch}@${sourceMeta.sha.slice(0, 7)})` : "Init from local source (non-Git)" : "Init from packaged assets";
|
|
13135
13572
|
operations.push({
|
|
13136
13573
|
kind: "copy",
|
|
13137
13574
|
sourcePath: sourceRoot,
|
|
@@ -13142,21 +13579,14 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
|
|
|
13142
13579
|
destructive: false
|
|
13143
13580
|
});
|
|
13144
13581
|
}
|
|
13145
|
-
} else {
|
|
13146
|
-
warnings.push("No --from-local source provided");
|
|
13147
|
-
operations.push({
|
|
13148
|
-
kind: "warn",
|
|
13149
|
-
reason: "No --from-local source provided",
|
|
13150
|
-
destructive: false,
|
|
13151
|
-
requiresConfirmation: false
|
|
13152
|
-
});
|
|
13153
13582
|
}
|
|
13154
13583
|
return {
|
|
13155
13584
|
targetRoot,
|
|
13156
|
-
sourceRoot
|
|
13585
|
+
sourceRoot,
|
|
13157
13586
|
operations,
|
|
13158
13587
|
warnings,
|
|
13159
|
-
hasLabelConflicts
|
|
13588
|
+
hasLabelConflicts,
|
|
13589
|
+
enabledAddons: options.releaseWorkflow?.enabled ? ["release"] : []
|
|
13160
13590
|
};
|
|
13161
13591
|
}
|
|
13162
13592
|
var GROUP_LABELS = {
|
|
@@ -13228,6 +13658,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
13228
13658
|
let packageManager = current.packageManager;
|
|
13229
13659
|
let legacySkills = current.legacySkills ?? false;
|
|
13230
13660
|
let noGitCheck = current.noGitCheck ?? false;
|
|
13661
|
+
let releaseWorkflow;
|
|
13231
13662
|
const labels = current.labels ? { ...current.labels } : { ...DEFAULT_RUNNER_LABELS };
|
|
13232
13663
|
if (!packageManager && plan.warnings.some((w) => w.toLowerCase().includes("lockfile"))) {
|
|
13233
13664
|
const result = await select({
|
|
@@ -13247,6 +13678,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
13247
13678
|
legacySkills,
|
|
13248
13679
|
noGitCheck,
|
|
13249
13680
|
labels,
|
|
13681
|
+
releaseWorkflow,
|
|
13250
13682
|
cancelled: true
|
|
13251
13683
|
};
|
|
13252
13684
|
}
|
|
@@ -13270,6 +13702,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
13270
13702
|
legacySkills,
|
|
13271
13703
|
noGitCheck,
|
|
13272
13704
|
labels,
|
|
13705
|
+
releaseWorkflow,
|
|
13273
13706
|
cancelled: true
|
|
13274
13707
|
};
|
|
13275
13708
|
}
|
|
@@ -13295,6 +13728,7 @@ async function promptForInitChoices(plan, current) {
|
|
|
13295
13728
|
legacySkills,
|
|
13296
13729
|
noGitCheck,
|
|
13297
13730
|
labels,
|
|
13731
|
+
releaseWorkflow,
|
|
13298
13732
|
cancelled: true
|
|
13299
13733
|
};
|
|
13300
13734
|
}
|
|
@@ -13413,45 +13847,119 @@ async function promptForInitChoices(plan, current) {
|
|
|
13413
13847
|
legacySkills,
|
|
13414
13848
|
noGitCheck,
|
|
13415
13849
|
labels,
|
|
13850
|
+
releaseWorkflow,
|
|
13416
13851
|
cancelled: true
|
|
13417
13852
|
};
|
|
13418
13853
|
}
|
|
13419
13854
|
noGitCheck = true;
|
|
13420
13855
|
}
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
}
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13856
|
+
const releaseEnabledResult = await confirm({
|
|
13857
|
+
message: "Enable the Release Workflow Add-On for Changesets and Promotion PR orchestration?",
|
|
13858
|
+
initialValue: false
|
|
13859
|
+
});
|
|
13860
|
+
if (isCancel(releaseEnabledResult)) {
|
|
13861
|
+
return {
|
|
13862
|
+
docsMigration: docsMigration ?? "copy",
|
|
13863
|
+
agentFile: agentFile ?? "both",
|
|
13864
|
+
packageManager,
|
|
13865
|
+
legacySkills,
|
|
13866
|
+
noGitCheck,
|
|
13867
|
+
labels,
|
|
13868
|
+
releaseWorkflow: void 0,
|
|
13869
|
+
cancelled: true
|
|
13870
|
+
};
|
|
13871
|
+
}
|
|
13872
|
+
if (releaseEnabledResult) {
|
|
13873
|
+
const integrationBranchResult = await text({
|
|
13874
|
+
message: "Integration Branch name (e.g. dev)?",
|
|
13875
|
+
placeholder: "integration"
|
|
13876
|
+
});
|
|
13877
|
+
if (isCancel(integrationBranchResult)) {
|
|
13878
|
+
return {
|
|
13879
|
+
docsMigration: docsMigration ?? "copy",
|
|
13880
|
+
agentFile: agentFile ?? "both",
|
|
13881
|
+
packageManager,
|
|
13882
|
+
legacySkills,
|
|
13883
|
+
noGitCheck,
|
|
13884
|
+
labels,
|
|
13885
|
+
releaseWorkflow: void 0,
|
|
13886
|
+
cancelled: true
|
|
13887
|
+
};
|
|
13888
|
+
}
|
|
13889
|
+
const devReleaseBranchResult = await text({
|
|
13890
|
+
message: "Development Release Branch name (e.g. next)?",
|
|
13891
|
+
placeholder: "preview"
|
|
13892
|
+
});
|
|
13893
|
+
if (isCancel(devReleaseBranchResult)) {
|
|
13894
|
+
return {
|
|
13895
|
+
docsMigration: docsMigration ?? "copy",
|
|
13896
|
+
agentFile: agentFile ?? "both",
|
|
13897
|
+
packageManager,
|
|
13898
|
+
legacySkills,
|
|
13899
|
+
noGitCheck,
|
|
13900
|
+
labels,
|
|
13901
|
+
releaseWorkflow: void 0,
|
|
13902
|
+
cancelled: true
|
|
13903
|
+
};
|
|
13904
|
+
}
|
|
13905
|
+
const stableReleaseBranchResult = await text({
|
|
13906
|
+
message: "Stable Release Branch name (e.g. main)?",
|
|
13907
|
+
placeholder: "stable"
|
|
13908
|
+
});
|
|
13909
|
+
if (isCancel(stableReleaseBranchResult)) {
|
|
13910
|
+
return {
|
|
13911
|
+
docsMigration: docsMigration ?? "copy",
|
|
13912
|
+
agentFile: agentFile ?? "both",
|
|
13913
|
+
packageManager,
|
|
13914
|
+
legacySkills,
|
|
13915
|
+
noGitCheck,
|
|
13916
|
+
labels,
|
|
13917
|
+
releaseWorkflow: void 0,
|
|
13918
|
+
cancelled: true
|
|
13919
|
+
};
|
|
13920
|
+
}
|
|
13921
|
+
releaseWorkflow = {
|
|
13922
|
+
enabled: true,
|
|
13923
|
+
integrationBranch: typeof integrationBranchResult === "string" && integrationBranchResult.trim().length > 0 ? integrationBranchResult.trim() : "integration",
|
|
13924
|
+
developmentReleaseBranch: typeof devReleaseBranchResult === "string" && devReleaseBranchResult.trim().length > 0 ? devReleaseBranchResult.trim() : "preview",
|
|
13925
|
+
stableReleaseBranch: typeof stableReleaseBranchResult === "string" && stableReleaseBranchResult.trim().length > 0 ? stableReleaseBranchResult.trim() : "stable"
|
|
13926
|
+
};
|
|
13927
|
+
}
|
|
13928
|
+
return {
|
|
13929
|
+
docsMigration: docsMigration ?? "copy",
|
|
13930
|
+
agentFile: agentFile ?? "both",
|
|
13931
|
+
packageManager,
|
|
13932
|
+
legacySkills,
|
|
13933
|
+
noGitCheck,
|
|
13934
|
+
labels,
|
|
13935
|
+
releaseWorkflow,
|
|
13936
|
+
cancelled: false
|
|
13937
|
+
};
|
|
13938
|
+
}
|
|
13939
|
+
async function writeFileAtomic(filePath, content) {
|
|
13940
|
+
const tmpPath = `${filePath}.tmp.${randomUUID()}`;
|
|
13941
|
+
await writeFile2(tmpPath, content, "utf-8");
|
|
13942
|
+
await rename(tmpPath, filePath);
|
|
13943
|
+
}
|
|
13944
|
+
var MANAGED_BLOCK_BEGIN = "<!-- BEGIN POURKIT MANAGED BLOCK -->";
|
|
13945
|
+
var MANAGED_BLOCK_END = "<!-- END POURKIT MANAGED BLOCK -->";
|
|
13946
|
+
async function updateManagedBlock(filePath, content) {
|
|
13947
|
+
const blockContent = `${MANAGED_BLOCK_BEGIN}
|
|
13948
|
+
${content}${MANAGED_BLOCK_END}
|
|
13949
|
+
`;
|
|
13950
|
+
if (!existsSync18(filePath)) {
|
|
13951
|
+
const dir = path5.dirname(filePath);
|
|
13952
|
+
await mkdir5(dir, { recursive: true });
|
|
13953
|
+
await writeFileAtomic(filePath, blockContent);
|
|
13954
|
+
return;
|
|
13955
|
+
}
|
|
13956
|
+
const existing = await readFile5(filePath, "utf-8");
|
|
13957
|
+
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN);
|
|
13958
|
+
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
13959
|
+
if (beginIdx !== -1 && endIdx !== -1 && endIdx > beginIdx) {
|
|
13960
|
+
const before = existing.slice(0, beginIdx);
|
|
13961
|
+
const after = existing.slice(endIdx + MANAGED_BLOCK_END.length);
|
|
13962
|
+
await writeFileAtomic(filePath, before + blockContent + after);
|
|
13455
13963
|
} else {
|
|
13456
13964
|
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
13457
13965
|
await writeFileAtomic(filePath, existing + suffix + blockContent + "\n");
|
|
@@ -13480,7 +13988,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
13480
13988
|
schemaVersion: 1,
|
|
13481
13989
|
initializedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13482
13990
|
pourkit: {
|
|
13483
|
-
versionSource:
|
|
13991
|
+
versionSource: sourceMeta.versionSource,
|
|
13484
13992
|
releaseVersion: sourceMeta.latestTag,
|
|
13485
13993
|
releaseChannel: sourceMeta.releaseChannel,
|
|
13486
13994
|
sourceBranch: sourceMeta.branch,
|
|
@@ -13490,6 +13998,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
|
|
|
13490
13998
|
},
|
|
13491
13999
|
agentFiles,
|
|
13492
14000
|
packageManager,
|
|
14001
|
+
workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
|
|
13493
14002
|
assets
|
|
13494
14003
|
};
|
|
13495
14004
|
await mkdir5(manifestDir, { recursive: true });
|
|
@@ -13674,7 +14183,7 @@ async function applyInitFromSource(options) {
|
|
|
13674
14183
|
}
|
|
13675
14184
|
let sourceMeta;
|
|
13676
14185
|
try {
|
|
13677
|
-
sourceMeta = await discoverLocalSource(
|
|
14186
|
+
sourceMeta = await discoverLocalSource(plan.sourceRoot);
|
|
13678
14187
|
} catch {
|
|
13679
14188
|
sourceMeta = {
|
|
13680
14189
|
versionSource: "local-path",
|
|
@@ -13720,7 +14229,6 @@ async function runInitCommand(options) {
|
|
|
13720
14229
|
}
|
|
13721
14230
|
if (!isInteractive && !options.yes && !options.dryRun) {
|
|
13722
14231
|
const missing = [];
|
|
13723
|
-
if (!options.fromLocal) missing.push("--from-local");
|
|
13724
14232
|
if (!options.docsMigration) missing.push("--docs-migration");
|
|
13725
14233
|
if (!options.agentFile) missing.push("--agent-file");
|
|
13726
14234
|
if (!options.packageManager) {
|
|
@@ -13766,7 +14274,8 @@ async function runInitCommand(options) {
|
|
|
13766
14274
|
agentFile: options.agentFile ?? promptResult.agentFile,
|
|
13767
14275
|
packageManager: options.packageManager ?? promptResult.packageManager,
|
|
13768
14276
|
legacySkills: options.legacySkills ?? promptResult.legacySkills,
|
|
13769
|
-
noGitCheck: options.noGitCheck ?? promptResult.noGitCheck
|
|
14277
|
+
noGitCheck: options.noGitCheck ?? promptResult.noGitCheck,
|
|
14278
|
+
releaseWorkflow: options.releaseWorkflow ?? promptResult.releaseWorkflow
|
|
13770
14279
|
};
|
|
13771
14280
|
promptLabels = promptResult.labels;
|
|
13772
14281
|
}
|
|
@@ -13784,7 +14293,8 @@ async function runInitCommand(options) {
|
|
|
13784
14293
|
packageManager: options.packageManager,
|
|
13785
14294
|
noGitCheck: options.noGitCheck,
|
|
13786
14295
|
skipInstall: options.skipInstall,
|
|
13787
|
-
labels: planLabels
|
|
14296
|
+
labels: planLabels,
|
|
14297
|
+
releaseWorkflow: options.releaseWorkflow
|
|
13788
14298
|
});
|
|
13789
14299
|
let planLabelConflictPolicy = "skip-metadata-changes";
|
|
13790
14300
|
if (isInteractive && plan.hasLabelConflicts) {
|
|
@@ -13828,12 +14338,6 @@ ${plan.warnings.length} warning(s) \u2014 review above before applying.`
|
|
|
13828
14338
|
}
|
|
13829
14339
|
return;
|
|
13830
14340
|
}
|
|
13831
|
-
if (!options.fromLocal) {
|
|
13832
|
-
console.error(
|
|
13833
|
-
"\nError: --from-local <path> is required to apply the init plan."
|
|
13834
|
-
);
|
|
13835
|
-
process.exit(1);
|
|
13836
|
-
}
|
|
13837
14341
|
const result = await applyInitFromSource({
|
|
13838
14342
|
targetRoot,
|
|
13839
14343
|
fromLocal: options.fromLocal,
|
|
@@ -13990,16 +14494,16 @@ async function runSerenaStatusCommand(options) {
|
|
|
13990
14494
|
|
|
13991
14495
|
// commands/config-schema.ts
|
|
13992
14496
|
import { readFileSync as readFileSync20, existsSync as existsSync19 } from "fs";
|
|
13993
|
-
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
13994
|
-
import { resolve as resolve4, dirname as dirname5 } from "path";
|
|
13995
|
-
import { fileURLToPath as
|
|
14497
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
14498
|
+
import { resolve as resolve4, dirname as dirname5, relative as relative2 } from "path";
|
|
14499
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13996
14500
|
import Ajv2 from "ajv";
|
|
13997
|
-
var
|
|
13998
|
-
var
|
|
14501
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
14502
|
+
var __dirname3 = dirname5(__filename3);
|
|
13999
14503
|
function resolvePackagedAssetPath2(fileName) {
|
|
14000
14504
|
const candidates = [
|
|
14001
|
-
resolve4(
|
|
14002
|
-
resolve4(
|
|
14505
|
+
resolve4(__dirname3, "schema", fileName),
|
|
14506
|
+
resolve4(__dirname3, "../schema", fileName)
|
|
14003
14507
|
];
|
|
14004
14508
|
return candidates.find((candidate) => existsSync19(candidate)) ?? candidates[0];
|
|
14005
14509
|
}
|
|
@@ -14028,6 +14532,542 @@ function readPackagedHash() {
|
|
|
14028
14532
|
function localSchemaDir(repoRoot2) {
|
|
14029
14533
|
return resolve4(repoRoot2, ".pourkit/schema");
|
|
14030
14534
|
}
|
|
14535
|
+
var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
|
|
14536
|
+
var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
|
|
14537
|
+
function resolvePackagedManagedPath(...segments) {
|
|
14538
|
+
return resolve4(__dirname3, "..", "managed", ...segments);
|
|
14539
|
+
}
|
|
14540
|
+
function resolvePackagedManagedDocPath(docName) {
|
|
14541
|
+
return resolvePackagedManagedPath("docs", "agents", docName);
|
|
14542
|
+
}
|
|
14543
|
+
function resolvePackagedReleaseAddonDocPath(docName) {
|
|
14544
|
+
return resolvePackagedManagedPath(
|
|
14545
|
+
"addons",
|
|
14546
|
+
"release",
|
|
14547
|
+
"docs",
|
|
14548
|
+
"agents",
|
|
14549
|
+
docName
|
|
14550
|
+
);
|
|
14551
|
+
}
|
|
14552
|
+
async function readPackagedTextFile(filePath) {
|
|
14553
|
+
try {
|
|
14554
|
+
return await readFile6(filePath, "utf-8");
|
|
14555
|
+
} catch {
|
|
14556
|
+
return null;
|
|
14557
|
+
}
|
|
14558
|
+
}
|
|
14559
|
+
function isPathContained(parent, child) {
|
|
14560
|
+
const relativePath = relative2(parent, child);
|
|
14561
|
+
return !relativePath.startsWith("..") && !relativePath.startsWith("../");
|
|
14562
|
+
}
|
|
14563
|
+
async function walkDir2(dir) {
|
|
14564
|
+
const files = [];
|
|
14565
|
+
try {
|
|
14566
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
14567
|
+
for (const entry of entries) {
|
|
14568
|
+
const full = resolve4(dir, entry.name);
|
|
14569
|
+
if (entry.isDirectory()) {
|
|
14570
|
+
files.push(...await walkDir2(full));
|
|
14571
|
+
} else {
|
|
14572
|
+
files.push(full);
|
|
14573
|
+
}
|
|
14574
|
+
}
|
|
14575
|
+
} catch {
|
|
14576
|
+
return [];
|
|
14577
|
+
}
|
|
14578
|
+
return files;
|
|
14579
|
+
}
|
|
14580
|
+
function removeCodeFences(content) {
|
|
14581
|
+
return content.replace(/```[\s\S]*?```/g, "");
|
|
14582
|
+
}
|
|
14583
|
+
function isInsideBacktick(content, index) {
|
|
14584
|
+
const before = content.slice(0, index);
|
|
14585
|
+
const backtickCount = (before.match(/`/g) || []).length;
|
|
14586
|
+
return backtickCount % 2 === 1;
|
|
14587
|
+
}
|
|
14588
|
+
var BRANCH_NAME_PATTERN = /\b(dev|next|main)\b/g;
|
|
14589
|
+
var OBSOLETE_PRD_PATTERN = /PRD-\d{3}(?![\dNn])/g;
|
|
14590
|
+
var OVERWIDE_PRD_PATTERN = /PRD-\d{5,}/g;
|
|
14591
|
+
function isExampleLine(content, matchIndex) {
|
|
14592
|
+
return /^(?:[-*]\s+)?Example:/i.test(getLine(content, matchIndex).trim());
|
|
14593
|
+
}
|
|
14594
|
+
function getLine(content, matchIndex) {
|
|
14595
|
+
const lineStart = content.lastIndexOf("\n", matchIndex - 1) + 1;
|
|
14596
|
+
const lineEnd = content.indexOf("\n", matchIndex);
|
|
14597
|
+
return content.slice(lineStart, lineEnd === -1 ? content.length : lineEnd);
|
|
14598
|
+
}
|
|
14599
|
+
function isBranchPolicyLine(content, matchIndex) {
|
|
14600
|
+
const line = getLine(content, matchIndex).toLowerCase();
|
|
14601
|
+
return /->|\b(branch|branches|lane|release|promotion|promote|target|base|merge|push|hotfix|changeset|origin)\b/.test(
|
|
14602
|
+
line
|
|
14603
|
+
);
|
|
14604
|
+
}
|
|
14605
|
+
function scanManagedPolicyText(content, filePath, allowlist) {
|
|
14606
|
+
const violations = [];
|
|
14607
|
+
const noFences = removeCodeFences(content);
|
|
14608
|
+
let match;
|
|
14609
|
+
BRANCH_NAME_PATTERN.lastIndex = 0;
|
|
14610
|
+
while ((match = BRANCH_NAME_PATTERN.exec(noFences)) !== null) {
|
|
14611
|
+
if (isExampleLine(noFences, match.index)) continue;
|
|
14612
|
+
if (!isBranchPolicyLine(noFences, match.index)) continue;
|
|
14613
|
+
if (allowlist?.includes(match[0])) continue;
|
|
14614
|
+
violations.push({
|
|
14615
|
+
path: filePath,
|
|
14616
|
+
phrase: match[0],
|
|
14617
|
+
reason: "hardcoded_branch_policy"
|
|
14618
|
+
});
|
|
14619
|
+
}
|
|
14620
|
+
OBSOLETE_PRD_PATTERN.lastIndex = 0;
|
|
14621
|
+
while ((match = OBSOLETE_PRD_PATTERN.exec(noFences)) !== null) {
|
|
14622
|
+
if (allowlist?.includes(match[0])) continue;
|
|
14623
|
+
if (!isInsideBacktick(noFences, match.index)) {
|
|
14624
|
+
violations.push({
|
|
14625
|
+
path: filePath,
|
|
14626
|
+
phrase: match[0],
|
|
14627
|
+
reason: "obsolete_prd_placeholder"
|
|
14628
|
+
});
|
|
14629
|
+
}
|
|
14630
|
+
}
|
|
14631
|
+
OVERWIDE_PRD_PATTERN.lastIndex = 0;
|
|
14632
|
+
while ((match = OVERWIDE_PRD_PATTERN.exec(noFences)) !== null) {
|
|
14633
|
+
if (allowlist?.includes(match[0])) continue;
|
|
14634
|
+
if (!isInsideBacktick(noFences, match.index)) {
|
|
14635
|
+
violations.push({
|
|
14636
|
+
path: filePath,
|
|
14637
|
+
phrase: match[0],
|
|
14638
|
+
reason: "obsolete_prd_placeholder"
|
|
14639
|
+
});
|
|
14640
|
+
}
|
|
14641
|
+
}
|
|
14642
|
+
return violations;
|
|
14643
|
+
}
|
|
14644
|
+
async function validateWorkflowPack(cwd) {
|
|
14645
|
+
const failures = [];
|
|
14646
|
+
const warnings = [];
|
|
14647
|
+
const catalog = getWorkflowPackCatalog();
|
|
14648
|
+
const baselineSkills = getBaselineManagedSkillNames();
|
|
14649
|
+
for (const docName of catalog.docs) {
|
|
14650
|
+
const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
|
|
14651
|
+
if (!existsSync19(localPath)) {
|
|
14652
|
+
failures.push({
|
|
14653
|
+
severity: "failure",
|
|
14654
|
+
kind: "missing_managed_asset",
|
|
14655
|
+
path: `.pourkit/managed/docs/agents/${docName}`,
|
|
14656
|
+
message: `Missing managed doc: ${docName}`
|
|
14657
|
+
});
|
|
14658
|
+
} else {
|
|
14659
|
+
const packagedPath = resolvePackagedManagedDocPath(docName);
|
|
14660
|
+
const packagedContent = await readPackagedTextFile(packagedPath);
|
|
14661
|
+
if (packagedContent !== null) {
|
|
14662
|
+
try {
|
|
14663
|
+
const localContent = await readFile6(localPath, "utf-8");
|
|
14664
|
+
if (localContent !== packagedContent) {
|
|
14665
|
+
const overridePath = resolve4(
|
|
14666
|
+
cwd,
|
|
14667
|
+
`.pourkit/overrides/docs/agents/${docName}`
|
|
14668
|
+
);
|
|
14669
|
+
if (existsSync19(overridePath)) {
|
|
14670
|
+
warnings.push({
|
|
14671
|
+
severity: "warning",
|
|
14672
|
+
kind: "overridden",
|
|
14673
|
+
path: `.pourkit/overrides/docs/agents/${docName}`,
|
|
14674
|
+
message: `Managed doc ${docName} is overridden by explicit override.`
|
|
14675
|
+
});
|
|
14676
|
+
} else {
|
|
14677
|
+
failures.push({
|
|
14678
|
+
severity: "failure",
|
|
14679
|
+
kind: "managed_asset_drift",
|
|
14680
|
+
path: `.pourkit/managed/docs/agents/${docName}`,
|
|
14681
|
+
message: `Managed doc ${docName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
|
|
14682
|
+
});
|
|
14683
|
+
}
|
|
14684
|
+
}
|
|
14685
|
+
} catch {
|
|
14686
|
+
}
|
|
14687
|
+
}
|
|
14688
|
+
}
|
|
14689
|
+
}
|
|
14690
|
+
for (const skillName of baselineSkills) {
|
|
14691
|
+
const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
|
|
14692
|
+
if (!existsSync19(skillDir)) {
|
|
14693
|
+
failures.push({
|
|
14694
|
+
severity: "failure",
|
|
14695
|
+
kind: "missing_managed_asset",
|
|
14696
|
+
path: `.pourkit/managed/skills/${skillName}`,
|
|
14697
|
+
message: `Missing managed skill: ${skillName}`
|
|
14698
|
+
});
|
|
14699
|
+
} else {
|
|
14700
|
+
const skillSourcePath = resolve4(skillDir, "SKILL.md");
|
|
14701
|
+
if (existsSync19(skillSourcePath)) {
|
|
14702
|
+
const packagedSkillPath = resolvePackagedManagedPath(
|
|
14703
|
+
"skills",
|
|
14704
|
+
skillName,
|
|
14705
|
+
"SKILL.md"
|
|
14706
|
+
);
|
|
14707
|
+
const packagedContent = await readPackagedTextFile(packagedSkillPath);
|
|
14708
|
+
if (packagedContent !== null) {
|
|
14709
|
+
try {
|
|
14710
|
+
const localContent = await readFile6(skillSourcePath, "utf-8");
|
|
14711
|
+
if (localContent !== packagedContent) {
|
|
14712
|
+
const overridePath = resolve4(
|
|
14713
|
+
cwd,
|
|
14714
|
+
`.pourkit/overrides/skills/${skillName}/SKILL.md`
|
|
14715
|
+
);
|
|
14716
|
+
if (existsSync19(overridePath)) {
|
|
14717
|
+
warnings.push({
|
|
14718
|
+
severity: "warning",
|
|
14719
|
+
kind: "overridden",
|
|
14720
|
+
path: `.pourkit/overrides/skills/${skillName}/SKILL.md`,
|
|
14721
|
+
message: `Managed skill ${skillName} is overridden by explicit override.`
|
|
14722
|
+
});
|
|
14723
|
+
} else {
|
|
14724
|
+
failures.push({
|
|
14725
|
+
severity: "failure",
|
|
14726
|
+
kind: "managed_asset_drift",
|
|
14727
|
+
path: `.pourkit/managed/skills/${skillName}/SKILL.md`,
|
|
14728
|
+
message: `Managed skill ${skillName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
|
|
14729
|
+
});
|
|
14730
|
+
}
|
|
14731
|
+
}
|
|
14732
|
+
} catch {
|
|
14733
|
+
}
|
|
14734
|
+
}
|
|
14735
|
+
}
|
|
14736
|
+
}
|
|
14737
|
+
}
|
|
14738
|
+
for (const promptName of catalog.prompts) {
|
|
14739
|
+
const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
|
|
14740
|
+
if (!existsSync19(promptPath)) {
|
|
14741
|
+
failures.push({
|
|
14742
|
+
severity: "failure",
|
|
14743
|
+
kind: "missing_managed_asset",
|
|
14744
|
+
path: `.pourkit/managed/prompts/${promptName}`,
|
|
14745
|
+
message: `Missing managed prompt: ${promptName}`
|
|
14746
|
+
});
|
|
14747
|
+
}
|
|
14748
|
+
}
|
|
14749
|
+
for (const skillName of baselineSkills) {
|
|
14750
|
+
const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
|
|
14751
|
+
if (!existsSync19(projectionDir)) {
|
|
14752
|
+
warnings.push({
|
|
14753
|
+
severity: "warning",
|
|
14754
|
+
kind: "projection_stale",
|
|
14755
|
+
path: `.agents/skills/${skillName}`,
|
|
14756
|
+
message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
|
|
14757
|
+
});
|
|
14758
|
+
} else {
|
|
14759
|
+
const projectionPath = resolve4(projectionDir, "SKILL.md");
|
|
14760
|
+
if (existsSync19(projectionPath)) {
|
|
14761
|
+
const managedSourcePath = resolvePackagedManagedPath(
|
|
14762
|
+
"skills",
|
|
14763
|
+
skillName,
|
|
14764
|
+
"SKILL.md"
|
|
14765
|
+
);
|
|
14766
|
+
const packagedContent = await readPackagedTextFile(managedSourcePath);
|
|
14767
|
+
if (packagedContent !== null) {
|
|
14768
|
+
try {
|
|
14769
|
+
const projectionContent = await readFile6(projectionPath, "utf-8");
|
|
14770
|
+
const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
|
|
14771
|
+
if (projectionContent !== buildOpenCodeSkillProjectionContent(
|
|
14772
|
+
managedSourceRelativePath,
|
|
14773
|
+
packagedContent
|
|
14774
|
+
)) {
|
|
14775
|
+
warnings.push({
|
|
14776
|
+
severity: "warning",
|
|
14777
|
+
kind: "projection_stale",
|
|
14778
|
+
path: `.agents/skills/${skillName}/SKILL.md`,
|
|
14779
|
+
message: `Projection for ${skillName} differs from managed source. Run 'pourkit sync workflow-pack' to regenerate.`
|
|
14780
|
+
});
|
|
14781
|
+
}
|
|
14782
|
+
} catch {
|
|
14783
|
+
}
|
|
14784
|
+
}
|
|
14785
|
+
}
|
|
14786
|
+
}
|
|
14787
|
+
}
|
|
14788
|
+
const agentsPath = resolve4(cwd, "AGENTS.md");
|
|
14789
|
+
if (existsSync19(agentsPath)) {
|
|
14790
|
+
try {
|
|
14791
|
+
const agentsContent = await readFile6(agentsPath, "utf-8");
|
|
14792
|
+
if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
|
|
14793
|
+
failures.push({
|
|
14794
|
+
severity: "failure",
|
|
14795
|
+
kind: "router_drift",
|
|
14796
|
+
path: "AGENTS.md",
|
|
14797
|
+
message: "AGENTS.md references old .pourkit/docs/agents/ paths. Update to .pourkit/managed/docs/agents/."
|
|
14798
|
+
});
|
|
14799
|
+
}
|
|
14800
|
+
if (!agentsContent.includes(MANAGED_BLOCK_BEGIN2.trim())) {
|
|
14801
|
+
failures.push({
|
|
14802
|
+
severity: "failure",
|
|
14803
|
+
kind: "router_drift",
|
|
14804
|
+
path: "AGENTS.md",
|
|
14805
|
+
message: "AGENTS.md is missing the Pourkit managed block. Run 'pourkit sync workflow-pack' to add it."
|
|
14806
|
+
});
|
|
14807
|
+
}
|
|
14808
|
+
} catch {
|
|
14809
|
+
}
|
|
14810
|
+
}
|
|
14811
|
+
const overridesDir = resolve4(cwd, ".pourkit/overrides");
|
|
14812
|
+
if (existsSync19(overridesDir)) {
|
|
14813
|
+
const overrideFiles = await walkDir2(overridesDir);
|
|
14814
|
+
for (const file of overrideFiles) {
|
|
14815
|
+
const relPath = relative2(cwd, file);
|
|
14816
|
+
if (!warnings.some((w) => w.kind === "overridden" && w.path === relPath)) {
|
|
14817
|
+
warnings.push({
|
|
14818
|
+
severity: "warning",
|
|
14819
|
+
kind: "overridden",
|
|
14820
|
+
path: relPath,
|
|
14821
|
+
message: `Explicit override present: ${relPath}`
|
|
14822
|
+
});
|
|
14823
|
+
}
|
|
14824
|
+
}
|
|
14825
|
+
}
|
|
14826
|
+
const configPath = resolve4(cwd, ".pourkit/config.json");
|
|
14827
|
+
if (existsSync19(configPath)) {
|
|
14828
|
+
try {
|
|
14829
|
+
const configContent = await readFile6(configPath, "utf-8");
|
|
14830
|
+
const parsed = JSON.parse(configContent);
|
|
14831
|
+
const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
|
|
14832
|
+
if (releaseEnabled) {
|
|
14833
|
+
for (const skillName of catalog.releaseAddonSkills) {
|
|
14834
|
+
const addonDir = resolve4(
|
|
14835
|
+
cwd,
|
|
14836
|
+
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
14837
|
+
);
|
|
14838
|
+
if (!existsSync19(addonDir)) {
|
|
14839
|
+
failures.push({
|
|
14840
|
+
severity: "failure",
|
|
14841
|
+
kind: "release_config_conflict",
|
|
14842
|
+
path: `.pourkit/managed/addons/release/skills/${skillName}`,
|
|
14843
|
+
message: `Release workflow is enabled but release addon skill ${skillName} is not installed. Run 'pourkit sync workflow-pack' to install.`
|
|
14844
|
+
});
|
|
14845
|
+
}
|
|
14846
|
+
}
|
|
14847
|
+
for (const docName of catalog.releaseAddonDocs) {
|
|
14848
|
+
const addonDocPath = resolve4(
|
|
14849
|
+
cwd,
|
|
14850
|
+
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
14851
|
+
);
|
|
14852
|
+
if (!existsSync19(addonDocPath)) {
|
|
14853
|
+
failures.push({
|
|
14854
|
+
severity: "failure",
|
|
14855
|
+
kind: "release_config_conflict",
|
|
14856
|
+
path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
|
|
14857
|
+
message: `Release workflow is enabled but release addon doc ${docName} is not installed. Run 'pourkit sync workflow-pack' to install.`
|
|
14858
|
+
});
|
|
14859
|
+
} else {
|
|
14860
|
+
const packagedContent = await readPackagedTextFile(
|
|
14861
|
+
resolvePackagedReleaseAddonDocPath(docName)
|
|
14862
|
+
);
|
|
14863
|
+
if (packagedContent !== null) {
|
|
14864
|
+
const localContent = await readFile6(addonDocPath, "utf-8");
|
|
14865
|
+
if (localContent !== packagedContent) {
|
|
14866
|
+
failures.push({
|
|
14867
|
+
severity: "failure",
|
|
14868
|
+
kind: "managed_asset_drift",
|
|
14869
|
+
path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
|
|
14870
|
+
message: `Release addon doc ${docName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
|
|
14871
|
+
});
|
|
14872
|
+
}
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
}
|
|
14876
|
+
const rw = parsed.releaseWorkflow;
|
|
14877
|
+
const requiredFields = [
|
|
14878
|
+
["integrationBranch", rw?.integrationBranch],
|
|
14879
|
+
["developmentReleaseBranch", rw?.developmentReleaseBranch],
|
|
14880
|
+
["stableReleaseBranch", rw?.stableReleaseBranch]
|
|
14881
|
+
];
|
|
14882
|
+
for (const [fieldName, value] of requiredFields) {
|
|
14883
|
+
if (!value || typeof value === "string" && value.trim() === "") {
|
|
14884
|
+
failures.push({
|
|
14885
|
+
severity: "failure",
|
|
14886
|
+
kind: "release_config_conflict",
|
|
14887
|
+
message: `Release workflow is enabled but required field releaseWorkflow.${fieldName} is missing or empty.`
|
|
14888
|
+
});
|
|
14889
|
+
}
|
|
14890
|
+
}
|
|
14891
|
+
} else {
|
|
14892
|
+
for (const skillName of catalog.releaseAddonSkills) {
|
|
14893
|
+
const addonDir = resolve4(
|
|
14894
|
+
cwd,
|
|
14895
|
+
`.pourkit/managed/addons/release/skills/${skillName}`
|
|
14896
|
+
);
|
|
14897
|
+
if (existsSync19(addonDir)) {
|
|
14898
|
+
failures.push({
|
|
14899
|
+
severity: "failure",
|
|
14900
|
+
kind: "release_config_conflict",
|
|
14901
|
+
path: `.pourkit/managed/addons/release/skills/${skillName}`,
|
|
14902
|
+
message: `Release workflow is disabled but release addon skill ${skillName} is present as active baseline policy. Disable or remove the addon skill.`
|
|
14903
|
+
});
|
|
14904
|
+
}
|
|
14905
|
+
}
|
|
14906
|
+
for (const docName of catalog.releaseAddonDocs) {
|
|
14907
|
+
const addonDocPath = resolve4(
|
|
14908
|
+
cwd,
|
|
14909
|
+
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
14910
|
+
);
|
|
14911
|
+
if (existsSync19(addonDocPath)) {
|
|
14912
|
+
failures.push({
|
|
14913
|
+
severity: "failure",
|
|
14914
|
+
kind: "release_config_conflict",
|
|
14915
|
+
path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
|
|
14916
|
+
message: `Release workflow is disabled but release addon doc ${docName} is present as active baseline policy. Disable or remove the addon doc.`
|
|
14917
|
+
});
|
|
14918
|
+
}
|
|
14919
|
+
}
|
|
14920
|
+
}
|
|
14921
|
+
} catch {
|
|
14922
|
+
}
|
|
14923
|
+
}
|
|
14924
|
+
const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
|
|
14925
|
+
if (existsSync19(manifestPath)) {
|
|
14926
|
+
try {
|
|
14927
|
+
const manifestContent = await readFile6(manifestPath, "utf-8");
|
|
14928
|
+
const manifest = JSON.parse(manifestContent);
|
|
14929
|
+
const wp = manifest.workflowPack;
|
|
14930
|
+
if (wp?.projections) {
|
|
14931
|
+
for (const proj of wp.projections) {
|
|
14932
|
+
if (proj.path) {
|
|
14933
|
+
const resolved = resolve4(cwd, proj.path);
|
|
14934
|
+
if (!isPathContained(cwd, resolved)) {
|
|
14935
|
+
failures.push({
|
|
14936
|
+
severity: "failure",
|
|
14937
|
+
kind: "path_escape",
|
|
14938
|
+
message: `Manifest projection path escapes repository: ${proj.path}`
|
|
14939
|
+
});
|
|
14940
|
+
}
|
|
14941
|
+
}
|
|
14942
|
+
}
|
|
14943
|
+
}
|
|
14944
|
+
} catch {
|
|
14945
|
+
}
|
|
14946
|
+
}
|
|
14947
|
+
const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
|
|
14948
|
+
if (existsSync19(pathEscapeOverrideDir)) {
|
|
14949
|
+
const overrideFiles = await walkDir2(pathEscapeOverrideDir);
|
|
14950
|
+
for (const file of overrideFiles) {
|
|
14951
|
+
if (!isPathContained(cwd, file)) {
|
|
14952
|
+
failures.push({
|
|
14953
|
+
severity: "failure",
|
|
14954
|
+
kind: "path_escape",
|
|
14955
|
+
path: relative2(cwd, file),
|
|
14956
|
+
message: `Override file path escapes repository: ${relative2(cwd, file)}`
|
|
14957
|
+
});
|
|
14958
|
+
}
|
|
14959
|
+
}
|
|
14960
|
+
}
|
|
14961
|
+
const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
|
|
14962
|
+
if (existsSync19(managedDocsDir)) {
|
|
14963
|
+
const docFiles = await walkDir2(managedDocsDir);
|
|
14964
|
+
for (const file of docFiles) {
|
|
14965
|
+
if (!file.endsWith(".md")) continue;
|
|
14966
|
+
try {
|
|
14967
|
+
const content = await readFile6(file, "utf-8");
|
|
14968
|
+
const relPath = relative2(cwd, file);
|
|
14969
|
+
const violations = scanManagedPolicyText(content, relPath);
|
|
14970
|
+
for (const v of violations) {
|
|
14971
|
+
failures.push({
|
|
14972
|
+
severity: "failure",
|
|
14973
|
+
kind: "terminology_policy_violation",
|
|
14974
|
+
path: v.path,
|
|
14975
|
+
message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
|
|
14976
|
+
});
|
|
14977
|
+
}
|
|
14978
|
+
} catch {
|
|
14979
|
+
}
|
|
14980
|
+
}
|
|
14981
|
+
}
|
|
14982
|
+
const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
|
|
14983
|
+
if (existsSync19(managedSkillsDir)) {
|
|
14984
|
+
const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
|
|
14985
|
+
for (const entry of skillDirs) {
|
|
14986
|
+
if (!entry.isDirectory()) continue;
|
|
14987
|
+
const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
|
|
14988
|
+
if (!existsSync19(skillFile)) continue;
|
|
14989
|
+
try {
|
|
14990
|
+
const content = await readFile6(skillFile, "utf-8");
|
|
14991
|
+
const relPath = relative2(cwd, skillFile);
|
|
14992
|
+
const violations = scanManagedPolicyText(content, relPath);
|
|
14993
|
+
for (const v of violations) {
|
|
14994
|
+
failures.push({
|
|
14995
|
+
severity: "failure",
|
|
14996
|
+
kind: "terminology_policy_violation",
|
|
14997
|
+
path: v.path,
|
|
14998
|
+
message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
|
|
14999
|
+
});
|
|
15000
|
+
}
|
|
15001
|
+
} catch {
|
|
15002
|
+
}
|
|
15003
|
+
}
|
|
15004
|
+
}
|
|
15005
|
+
const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
|
|
15006
|
+
if (existsSync19(managedPromptsDir)) {
|
|
15007
|
+
for (const promptName of catalog.prompts) {
|
|
15008
|
+
const promptFile = resolve4(managedPromptsDir, promptName);
|
|
15009
|
+
if (!existsSync19(promptFile)) continue;
|
|
15010
|
+
try {
|
|
15011
|
+
const content = await readFile6(promptFile, "utf-8");
|
|
15012
|
+
const relPath = relative2(cwd, promptFile);
|
|
15013
|
+
const violations = scanManagedPolicyText(content, relPath);
|
|
15014
|
+
for (const v of violations) {
|
|
15015
|
+
failures.push({
|
|
15016
|
+
severity: "failure",
|
|
15017
|
+
kind: "terminology_policy_violation",
|
|
15018
|
+
path: v.path,
|
|
15019
|
+
message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
|
|
15020
|
+
});
|
|
15021
|
+
}
|
|
15022
|
+
} catch {
|
|
15023
|
+
}
|
|
15024
|
+
}
|
|
15025
|
+
}
|
|
15026
|
+
for (const skillName of catalog.releaseAddonSkills) {
|
|
15027
|
+
const addonSkillFile = resolve4(
|
|
15028
|
+
cwd,
|
|
15029
|
+
`.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
|
|
15030
|
+
);
|
|
15031
|
+
if (!existsSync19(addonSkillFile)) continue;
|
|
15032
|
+
try {
|
|
15033
|
+
const content = await readFile6(addonSkillFile, "utf-8");
|
|
15034
|
+
const relPath = relative2(cwd, addonSkillFile);
|
|
15035
|
+
const violations = scanManagedPolicyText(content, relPath);
|
|
15036
|
+
for (const v of violations) {
|
|
15037
|
+
failures.push({
|
|
15038
|
+
severity: "failure",
|
|
15039
|
+
kind: "terminology_policy_violation",
|
|
15040
|
+
path: v.path,
|
|
15041
|
+
message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
|
|
15042
|
+
});
|
|
15043
|
+
}
|
|
15044
|
+
} catch {
|
|
15045
|
+
}
|
|
15046
|
+
}
|
|
15047
|
+
for (const docName of catalog.releaseAddonDocs) {
|
|
15048
|
+
const addonDocFile = resolve4(
|
|
15049
|
+
cwd,
|
|
15050
|
+
`.pourkit/managed/addons/release/docs/agents/${docName}`
|
|
15051
|
+
);
|
|
15052
|
+
if (!existsSync19(addonDocFile)) continue;
|
|
15053
|
+
try {
|
|
15054
|
+
const content = await readFile6(addonDocFile, "utf-8");
|
|
15055
|
+
const relPath = relative2(cwd, addonDocFile);
|
|
15056
|
+
const violations = scanManagedPolicyText(content, relPath);
|
|
15057
|
+
for (const v of violations) {
|
|
15058
|
+
failures.push({
|
|
15059
|
+
severity: "failure",
|
|
15060
|
+
kind: "terminology_policy_violation",
|
|
15061
|
+
path: v.path,
|
|
15062
|
+
message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
|
|
15063
|
+
});
|
|
15064
|
+
}
|
|
15065
|
+
} catch {
|
|
15066
|
+
}
|
|
15067
|
+
}
|
|
15068
|
+
const status = failures.length > 0 ? "failed" : warnings.length > 0 ? "warning" : "fresh";
|
|
15069
|
+
return { status, failures, warnings };
|
|
15070
|
+
}
|
|
14031
15071
|
async function runDoctorCommand(options) {
|
|
14032
15072
|
const repoRootPath = options.cwd ?? process.cwd();
|
|
14033
15073
|
const schemaDir = localSchemaDir(repoRootPath);
|
|
@@ -14087,30 +15127,37 @@ async function runDoctorCommand(options) {
|
|
|
14087
15127
|
"pourkit.config.js",
|
|
14088
15128
|
"pourkit.json"
|
|
14089
15129
|
].filter((p) => existsSync19(resolve4(repoRootPath, p)));
|
|
15130
|
+
const workflowPack = await validateWorkflowPack(repoRootPath);
|
|
14090
15131
|
let recommendation = null;
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
|
|
14107
|
-
|
|
14108
|
-
|
|
15132
|
+
const recParts = [];
|
|
15133
|
+
if (obsoleteConfigs.length > 0) {
|
|
15134
|
+
recParts.push(
|
|
15135
|
+
`Found obsolete config files: ${obsoleteConfigs.join(", ")}. Move configuration to .pourkit/config.json with "$schema": "./schema/pourkit.schema.json".`
|
|
15136
|
+
);
|
|
15137
|
+
}
|
|
15138
|
+
if (!configValidation.ok) {
|
|
15139
|
+
recParts.push(
|
|
15140
|
+
"Config validation failed; fix errors in .pourkit/config.json."
|
|
15141
|
+
);
|
|
15142
|
+
}
|
|
15143
|
+
if (overall !== "fresh") {
|
|
15144
|
+
recParts.push(
|
|
15145
|
+
'Run "pourkit config sync-schema" to update local schema assets.'
|
|
15146
|
+
);
|
|
15147
|
+
}
|
|
15148
|
+
if (workflowPack.status === "failed" || workflowPack.status === "warning") {
|
|
15149
|
+
recParts.push(
|
|
15150
|
+
'Run "pourkit sync workflow-pack" to repair workflow pack issues.'
|
|
15151
|
+
);
|
|
15152
|
+
}
|
|
15153
|
+
if (recParts.length > 0) {
|
|
15154
|
+
recommendation = recParts.join(" ");
|
|
14109
15155
|
}
|
|
14110
15156
|
return {
|
|
14111
15157
|
configValidation,
|
|
14112
15158
|
obsoleteConfigs,
|
|
14113
15159
|
schemaAssets: { schema, hash, overall },
|
|
15160
|
+
workflowPack,
|
|
14114
15161
|
recommendation
|
|
14115
15162
|
};
|
|
14116
15163
|
}
|
|
@@ -14151,6 +15198,576 @@ async function runConfigSyncSchemaCommand(options) {
|
|
|
14151
15198
|
};
|
|
14152
15199
|
}
|
|
14153
15200
|
|
|
15201
|
+
// commands/workflow-pack-sync.ts
|
|
15202
|
+
import { existsSync as existsSync20, readdirSync as readdirSync5 } from "fs";
|
|
15203
|
+
import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile4, readdir as readdir3 } from "fs/promises";
|
|
15204
|
+
import { resolve as resolve5, dirname as dirname6, relative as relative3, normalize as normalize2 } from "path";
|
|
15205
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15206
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
15207
|
+
var __dirname4 = dirname6(__filename4);
|
|
15208
|
+
var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
|
|
15209
|
+
var PROJECT_OWNED_DIRECTORIES = [
|
|
15210
|
+
".pourkit/docs/adr",
|
|
15211
|
+
".pourkit/plans",
|
|
15212
|
+
".pourkit/handoffs"
|
|
15213
|
+
];
|
|
15214
|
+
var OVERRIDES_DIR = ".pourkit/overrides";
|
|
15215
|
+
var MANAGED_BLOCK_BEGIN3 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
|
|
15216
|
+
var MANAGED_BLOCK_END2 = "\n<!-- END POURKIT MANAGED BLOCK -->";
|
|
15217
|
+
function resolvePackagedManagedPath2(...segments) {
|
|
15218
|
+
const candidates = [resolve5(__dirname4, "..", "managed", ...segments)];
|
|
15219
|
+
return candidates[0];
|
|
15220
|
+
}
|
|
15221
|
+
function resolvePackagedManagedDocPath2(docName) {
|
|
15222
|
+
return resolvePackagedManagedPath2("docs", "agents", docName);
|
|
15223
|
+
}
|
|
15224
|
+
function resolvePackagedReleaseAddonDocPath2(docName) {
|
|
15225
|
+
return resolvePackagedManagedPath2(
|
|
15226
|
+
"addons",
|
|
15227
|
+
"release",
|
|
15228
|
+
"docs",
|
|
15229
|
+
"agents",
|
|
15230
|
+
docName
|
|
15231
|
+
);
|
|
15232
|
+
}
|
|
15233
|
+
function resolvePackagedManagedSkillDir(skillName) {
|
|
15234
|
+
return resolvePackagedManagedPath2("skills", skillName);
|
|
15235
|
+
}
|
|
15236
|
+
function resolvePackagedManagedPromptPath(promptName) {
|
|
15237
|
+
return resolvePackagedManagedPath2("prompts", promptName);
|
|
15238
|
+
}
|
|
15239
|
+
async function readPackagedTextFile2(filePath) {
|
|
15240
|
+
try {
|
|
15241
|
+
return await readFile7(filePath, "utf-8");
|
|
15242
|
+
} catch {
|
|
15243
|
+
return null;
|
|
15244
|
+
}
|
|
15245
|
+
}
|
|
15246
|
+
function isPathContained2(parent, child) {
|
|
15247
|
+
const relativePath = relative3(parent, child);
|
|
15248
|
+
return !relativePath.startsWith("..") && !relativePath.startsWith("../");
|
|
15249
|
+
}
|
|
15250
|
+
function isProjectOwnedPath(relativePath) {
|
|
15251
|
+
for (const owned of PROJECT_OWNED_PATHS) {
|
|
15252
|
+
if (relativePath === owned || relativePath.startsWith(owned + "/")) {
|
|
15253
|
+
return true;
|
|
15254
|
+
}
|
|
15255
|
+
}
|
|
15256
|
+
for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
|
|
15257
|
+
if (relativePath.startsWith(ownedDir + "/") || relativePath === ownedDir + "/") {
|
|
15258
|
+
return true;
|
|
15259
|
+
}
|
|
15260
|
+
}
|
|
15261
|
+
return false;
|
|
15262
|
+
}
|
|
15263
|
+
function getManagedRelativePath(overrideRelativePath) {
|
|
15264
|
+
if (!overrideRelativePath.startsWith(OVERRIDES_DIR + "/")) {
|
|
15265
|
+
return null;
|
|
15266
|
+
}
|
|
15267
|
+
const managedPath = overrideRelativePath.slice(OVERRIDES_DIR.length + 1);
|
|
15268
|
+
if (managedPath.startsWith("skills/") || managedPath.startsWith("docs/") || managedPath.startsWith("prompts/")) {
|
|
15269
|
+
return `.pourkit/managed/${managedPath}`;
|
|
15270
|
+
}
|
|
15271
|
+
return null;
|
|
15272
|
+
}
|
|
15273
|
+
function findOverrideForManagedPath(existingOverrides, targetRelativePath, overridePath) {
|
|
15274
|
+
if (existingOverrides.some(
|
|
15275
|
+
(o) => o === overridePath || o.startsWith(overridePath + "/")
|
|
15276
|
+
)) {
|
|
15277
|
+
return overridePath;
|
|
15278
|
+
}
|
|
15279
|
+
if (existingOverrides.some((o) => {
|
|
15280
|
+
const managedPath = getManagedRelativePath(o);
|
|
15281
|
+
return managedPath === targetRelativePath;
|
|
15282
|
+
})) {
|
|
15283
|
+
return overridePath;
|
|
15284
|
+
}
|
|
15285
|
+
return null;
|
|
15286
|
+
}
|
|
15287
|
+
function checkForPathEscape(cwd, resolvedPath) {
|
|
15288
|
+
const normalizedCwd = normalize2(cwd);
|
|
15289
|
+
const normalizedPath = normalize2(resolvedPath);
|
|
15290
|
+
if (!isPathContained2(normalizedCwd, normalizedPath)) {
|
|
15291
|
+
return `Path escape detected: ${resolvedPath} is outside ${cwd}`;
|
|
15292
|
+
}
|
|
15293
|
+
return null;
|
|
15294
|
+
}
|
|
15295
|
+
async function walkDir3(dir) {
|
|
15296
|
+
const files = [];
|
|
15297
|
+
try {
|
|
15298
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
15299
|
+
for (const entry of entries) {
|
|
15300
|
+
const full = resolve5(dir, entry.name);
|
|
15301
|
+
if (entry.isDirectory()) {
|
|
15302
|
+
files.push(...await walkDir3(full));
|
|
15303
|
+
} else {
|
|
15304
|
+
files.push(full);
|
|
15305
|
+
}
|
|
15306
|
+
}
|
|
15307
|
+
} catch {
|
|
15308
|
+
}
|
|
15309
|
+
return files;
|
|
15310
|
+
}
|
|
15311
|
+
function generateManagedBlockContent() {
|
|
15312
|
+
return [
|
|
15313
|
+
"This repository uses Pourkit.",
|
|
15314
|
+
"",
|
|
15315
|
+
"## Managed operating docs",
|
|
15316
|
+
"",
|
|
15317
|
+
"Follow the managed operating docs under `.pourkit/managed/docs/agents/*`:",
|
|
15318
|
+
"",
|
|
15319
|
+
"- `.pourkit/managed/docs/agents/issue-tracker.md`",
|
|
15320
|
+
"- `.pourkit/managed/docs/agents/triage-labels.md`",
|
|
15321
|
+
"- `.pourkit/managed/docs/agents/naming.md`",
|
|
15322
|
+
"- `.pourkit/managed/docs/agents/git-workflow.md`",
|
|
15323
|
+
"- `.pourkit/managed/docs/agents/commit-style.md`",
|
|
15324
|
+
"- `.pourkit/managed/docs/agents/domain.md`",
|
|
15325
|
+
"",
|
|
15326
|
+
"### Project-owned context",
|
|
15327
|
+
"",
|
|
15328
|
+
"- `.pourkit/CONTEXT.md`",
|
|
15329
|
+
"- `.pourkit/CONTEXT-MAP.md`",
|
|
15330
|
+
"- `.pourkit/docs/adr/*`",
|
|
15331
|
+
"- `.pourkit/plans/*`",
|
|
15332
|
+
"- `.pourkit/handoffs/*`",
|
|
15333
|
+
"",
|
|
15334
|
+
"### Important",
|
|
15335
|
+
"",
|
|
15336
|
+
"- Do not edit `.pourkit/managed/*` unless explicitly updating Pourkit-managed assets.",
|
|
15337
|
+
"- Do not edit generated harness projections directly.",
|
|
15338
|
+
""
|
|
15339
|
+
].join("\n");
|
|
15340
|
+
}
|
|
15341
|
+
async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
|
|
15342
|
+
if (sourceContent === null) {
|
|
15343
|
+
return false;
|
|
15344
|
+
}
|
|
15345
|
+
const targetPath = resolve5(cwd, targetRelativePath);
|
|
15346
|
+
const escapeError = checkForPathEscape(cwd, targetPath);
|
|
15347
|
+
if (escapeError) {
|
|
15348
|
+
errors.push(escapeError);
|
|
15349
|
+
return false;
|
|
15350
|
+
}
|
|
15351
|
+
await mkdir7(resolve5(targetPath, ".."), { recursive: true });
|
|
15352
|
+
if (existsSync20(targetPath)) {
|
|
15353
|
+
const existing = await readFile7(targetPath, "utf-8");
|
|
15354
|
+
if (existing === sourceContent) {
|
|
15355
|
+
return false;
|
|
15356
|
+
}
|
|
15357
|
+
}
|
|
15358
|
+
await writeFile4(targetPath, sourceContent, "utf-8");
|
|
15359
|
+
return true;
|
|
15360
|
+
}
|
|
15361
|
+
function walkDirSync(dir) {
|
|
15362
|
+
const result = [];
|
|
15363
|
+
try {
|
|
15364
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
15365
|
+
for (const entry of entries) {
|
|
15366
|
+
const full = resolve5(dir, entry.name);
|
|
15367
|
+
if (entry.isDirectory()) {
|
|
15368
|
+
result.push(...walkDirSync(full));
|
|
15369
|
+
} else {
|
|
15370
|
+
result.push(full);
|
|
15371
|
+
}
|
|
15372
|
+
}
|
|
15373
|
+
} catch {
|
|
15374
|
+
}
|
|
15375
|
+
return result;
|
|
15376
|
+
}
|
|
15377
|
+
async function detectOverrides(cwd) {
|
|
15378
|
+
const overridesDir = resolve5(cwd, OVERRIDES_DIR);
|
|
15379
|
+
if (!existsSync20(overridesDir)) {
|
|
15380
|
+
return [];
|
|
15381
|
+
}
|
|
15382
|
+
const files = await walkDir3(overridesDir);
|
|
15383
|
+
return files.map((f) => relative3(cwd, f));
|
|
15384
|
+
}
|
|
15385
|
+
async function detectProjectOwnedFiles(cwd) {
|
|
15386
|
+
const found = [];
|
|
15387
|
+
for (const ownedPath of PROJECT_OWNED_PATHS) {
|
|
15388
|
+
const fullPath = resolve5(cwd, ownedPath);
|
|
15389
|
+
if (existsSync20(fullPath)) {
|
|
15390
|
+
found.push(ownedPath);
|
|
15391
|
+
}
|
|
15392
|
+
}
|
|
15393
|
+
for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
|
|
15394
|
+
const fullDir = resolve5(cwd, ownedDir);
|
|
15395
|
+
if (existsSync20(fullDir)) {
|
|
15396
|
+
const files = await walkDir3(fullDir);
|
|
15397
|
+
for (const file of files) {
|
|
15398
|
+
found.push(relative3(cwd, file));
|
|
15399
|
+
}
|
|
15400
|
+
}
|
|
15401
|
+
}
|
|
15402
|
+
return found;
|
|
15403
|
+
}
|
|
15404
|
+
async function isReleaseWorkflowEnabled(cwd) {
|
|
15405
|
+
try {
|
|
15406
|
+
const configPath = resolve5(cwd, ".pourkit", "config.json");
|
|
15407
|
+
const content = await readFile7(configPath, "utf-8");
|
|
15408
|
+
const parsed = JSON.parse(content);
|
|
15409
|
+
return parsed.releaseWorkflow?.enabled === true;
|
|
15410
|
+
} catch {
|
|
15411
|
+
return false;
|
|
15412
|
+
}
|
|
15413
|
+
}
|
|
15414
|
+
async function runWorkflowPackSyncCommand(options) {
|
|
15415
|
+
const cwd = options.cwd ?? process.cwd();
|
|
15416
|
+
const catalog = getWorkflowPackCatalog();
|
|
15417
|
+
const updated = [];
|
|
15418
|
+
const regeneratedProjections = [];
|
|
15419
|
+
const skippedProjectOwned = [];
|
|
15420
|
+
const overrides = [];
|
|
15421
|
+
const errors = [];
|
|
15422
|
+
const existingOverrides = await detectOverrides(cwd);
|
|
15423
|
+
const existingProjectOwned = await detectProjectOwnedFiles(cwd);
|
|
15424
|
+
for (const docName of catalog.docs) {
|
|
15425
|
+
const targetRelativePath = `.pourkit/managed/docs/agents/${docName}`;
|
|
15426
|
+
if (isProjectOwnedPath(targetRelativePath)) {
|
|
15427
|
+
skippedProjectOwned.push(targetRelativePath);
|
|
15428
|
+
continue;
|
|
15429
|
+
}
|
|
15430
|
+
const overridePath = `.pourkit/overrides/docs/agents/${docName}`;
|
|
15431
|
+
if (findOverrideForManagedPath(
|
|
15432
|
+
existingOverrides,
|
|
15433
|
+
targetRelativePath,
|
|
15434
|
+
overridePath
|
|
15435
|
+
)) {
|
|
15436
|
+
overrides.push(overridePath);
|
|
15437
|
+
continue;
|
|
15438
|
+
}
|
|
15439
|
+
const sourcePath = resolvePackagedManagedDocPath2(docName);
|
|
15440
|
+
const sourceContent = await readPackagedTextFile2(sourcePath);
|
|
15441
|
+
if (sourceContent === null) {
|
|
15442
|
+
errors.push(`Missing packaged managed doc: ${docName}`);
|
|
15443
|
+
continue;
|
|
15444
|
+
}
|
|
15445
|
+
const written = await syncFile(
|
|
15446
|
+
cwd,
|
|
15447
|
+
targetRelativePath,
|
|
15448
|
+
sourceContent,
|
|
15449
|
+
errors
|
|
15450
|
+
);
|
|
15451
|
+
if (written) {
|
|
15452
|
+
updated.push(targetRelativePath);
|
|
15453
|
+
}
|
|
15454
|
+
}
|
|
15455
|
+
const baselineSkills = getBaselineManagedSkillNames();
|
|
15456
|
+
const syncAcc = {
|
|
15457
|
+
updated,
|
|
15458
|
+
regeneratedProjections,
|
|
15459
|
+
skippedProjectOwned,
|
|
15460
|
+
overrides,
|
|
15461
|
+
errors
|
|
15462
|
+
};
|
|
15463
|
+
const syncCtx = { cwd, existingOverrides };
|
|
15464
|
+
for (const skillName of baselineSkills) {
|
|
15465
|
+
await syncSkill(skillName, catalog, syncAcc, syncCtx);
|
|
15466
|
+
}
|
|
15467
|
+
const releaseEnabled = await isReleaseWorkflowEnabled(cwd);
|
|
15468
|
+
if (releaseEnabled) {
|
|
15469
|
+
for (const skillName of catalog.releaseAddonSkills) {
|
|
15470
|
+
await syncAddonSkill(skillName, syncAcc, syncCtx);
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
if (releaseEnabled) {
|
|
15474
|
+
for (const docName of catalog.releaseAddonDocs) {
|
|
15475
|
+
const targetRelativePath = `.pourkit/managed/addons/release/docs/agents/${docName}`;
|
|
15476
|
+
const overridePath = `.pourkit/overrides/addons/release/docs/agents/${docName}`;
|
|
15477
|
+
if (findOverrideForManagedPath(
|
|
15478
|
+
existingOverrides,
|
|
15479
|
+
targetRelativePath,
|
|
15480
|
+
overridePath
|
|
15481
|
+
)) {
|
|
15482
|
+
overrides.push(overridePath);
|
|
15483
|
+
continue;
|
|
15484
|
+
}
|
|
15485
|
+
const sourcePath = resolvePackagedReleaseAddonDocPath2(docName);
|
|
15486
|
+
const sourceContent = await readPackagedTextFile2(sourcePath);
|
|
15487
|
+
if (sourceContent === null) {
|
|
15488
|
+
errors.push(`Missing packaged release addon doc: ${docName}`);
|
|
15489
|
+
continue;
|
|
15490
|
+
}
|
|
15491
|
+
const written = await syncFile(
|
|
15492
|
+
cwd,
|
|
15493
|
+
targetRelativePath,
|
|
15494
|
+
sourceContent,
|
|
15495
|
+
errors
|
|
15496
|
+
);
|
|
15497
|
+
if (written) {
|
|
15498
|
+
updated.push(targetRelativePath);
|
|
15499
|
+
}
|
|
15500
|
+
}
|
|
15501
|
+
}
|
|
15502
|
+
for (const promptName of catalog.prompts) {
|
|
15503
|
+
const targetRelativePath = `.pourkit/managed/prompts/${promptName}`;
|
|
15504
|
+
if (isProjectOwnedPath(targetRelativePath)) {
|
|
15505
|
+
skippedProjectOwned.push(targetRelativePath);
|
|
15506
|
+
continue;
|
|
15507
|
+
}
|
|
15508
|
+
const overridePath = `.pourkit/overrides/prompts/${promptName}`;
|
|
15509
|
+
if (findOverrideForManagedPath(
|
|
15510
|
+
existingOverrides,
|
|
15511
|
+
targetRelativePath,
|
|
15512
|
+
overridePath
|
|
15513
|
+
)) {
|
|
15514
|
+
overrides.push(overridePath);
|
|
15515
|
+
continue;
|
|
15516
|
+
}
|
|
15517
|
+
const sourcePath = resolvePackagedManagedPromptPath(promptName);
|
|
15518
|
+
const sourceContent = await readPackagedTextFile2(sourcePath);
|
|
15519
|
+
if (sourceContent === null) {
|
|
15520
|
+
errors.push(`Missing packaged managed prompt: ${promptName}`);
|
|
15521
|
+
continue;
|
|
15522
|
+
}
|
|
15523
|
+
const written = await syncFile(
|
|
15524
|
+
cwd,
|
|
15525
|
+
targetRelativePath,
|
|
15526
|
+
sourceContent,
|
|
15527
|
+
errors
|
|
15528
|
+
);
|
|
15529
|
+
if (written) {
|
|
15530
|
+
updated.push(targetRelativePath);
|
|
15531
|
+
}
|
|
15532
|
+
}
|
|
15533
|
+
const schemaResult = await runConfigSyncSchemaCommand({ cwd });
|
|
15534
|
+
const agentsPath = resolve5(cwd, "AGENTS.md");
|
|
15535
|
+
const managedBlockContent = generateManagedBlockContent();
|
|
15536
|
+
const fullManagedBlock = `${MANAGED_BLOCK_BEGIN3}${managedBlockContent}${MANAGED_BLOCK_END2}`;
|
|
15537
|
+
let agentsUpdated = false;
|
|
15538
|
+
if (existsSync20(agentsPath)) {
|
|
15539
|
+
const existing = await readFile7(agentsPath, "utf-8");
|
|
15540
|
+
const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN3);
|
|
15541
|
+
const endIdx = existing.indexOf(MANAGED_BLOCK_END2);
|
|
15542
|
+
if (beginIdx !== -1 && endIdx !== -1) {
|
|
15543
|
+
const beforeBlock = existing.slice(0, beginIdx);
|
|
15544
|
+
const afterBlock = existing.slice(endIdx + MANAGED_BLOCK_END2.length);
|
|
15545
|
+
const newContent = `${beforeBlock}${fullManagedBlock}${afterBlock}`;
|
|
15546
|
+
if (newContent !== existing) {
|
|
15547
|
+
await writeFile4(agentsPath, newContent, "utf-8");
|
|
15548
|
+
agentsUpdated = true;
|
|
15549
|
+
}
|
|
15550
|
+
} else {
|
|
15551
|
+
const newContent = `${existing}
|
|
15552
|
+
|
|
15553
|
+
${fullManagedBlock}
|
|
15554
|
+
`;
|
|
15555
|
+
if (newContent !== existing) {
|
|
15556
|
+
await writeFile4(agentsPath, newContent, "utf-8");
|
|
15557
|
+
agentsUpdated = true;
|
|
15558
|
+
}
|
|
15559
|
+
}
|
|
15560
|
+
} else {
|
|
15561
|
+
await writeFile4(agentsPath, fullManagedBlock + "\n", "utf-8");
|
|
15562
|
+
agentsUpdated = true;
|
|
15563
|
+
}
|
|
15564
|
+
if (agentsUpdated) {
|
|
15565
|
+
updated.push("AGENTS.md");
|
|
15566
|
+
}
|
|
15567
|
+
const enabledAddons = releaseEnabled ? ["release"] : [];
|
|
15568
|
+
const manifestMeta = buildWorkflowPackMetadata(enabledAddons);
|
|
15569
|
+
const manifestPath = resolve5(cwd, ".pourkit", "manifest.json");
|
|
15570
|
+
let manifestWritten = false;
|
|
15571
|
+
let previousManifestWorkflowPack = void 0;
|
|
15572
|
+
try {
|
|
15573
|
+
const existingRaw = await readFile7(manifestPath, "utf-8").catch(() => null);
|
|
15574
|
+
if (existingRaw) {
|
|
15575
|
+
const existing = JSON.parse(existingRaw);
|
|
15576
|
+
previousManifestWorkflowPack = existing.workflowPack;
|
|
15577
|
+
}
|
|
15578
|
+
} catch {
|
|
15579
|
+
}
|
|
15580
|
+
if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
|
|
15581
|
+
try {
|
|
15582
|
+
const existingRaw = await readFile7(manifestPath, "utf-8").catch(
|
|
15583
|
+
() => null
|
|
15584
|
+
);
|
|
15585
|
+
let manifest = existingRaw ? JSON.parse(existingRaw) : {};
|
|
15586
|
+
manifest.workflowPack = manifestMeta;
|
|
15587
|
+
await mkdir7(resolve5(manifestPath, ".."), { recursive: true });
|
|
15588
|
+
await writeFile4(
|
|
15589
|
+
manifestPath,
|
|
15590
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
15591
|
+
"utf-8"
|
|
15592
|
+
);
|
|
15593
|
+
manifestWritten = true;
|
|
15594
|
+
} catch {
|
|
15595
|
+
errors.push("Failed to write manifest metadata");
|
|
15596
|
+
}
|
|
15597
|
+
}
|
|
15598
|
+
for (const owned of existingProjectOwned) {
|
|
15599
|
+
if (!skippedProjectOwned.includes(owned)) {
|
|
15600
|
+
skippedProjectOwned.push(owned);
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15603
|
+
for (const overridePath of existingOverrides) {
|
|
15604
|
+
const displayPath = overridePath;
|
|
15605
|
+
if (!overrides.includes(displayPath)) {
|
|
15606
|
+
overrides.push(displayPath);
|
|
15607
|
+
}
|
|
15608
|
+
}
|
|
15609
|
+
const changed = updated.length > 0 || regeneratedProjections.length > 0 || schemaResult.changed || agentsUpdated || manifestWritten;
|
|
15610
|
+
return {
|
|
15611
|
+
changed,
|
|
15612
|
+
updated,
|
|
15613
|
+
regeneratedProjections,
|
|
15614
|
+
skippedProjectOwned,
|
|
15615
|
+
overrides,
|
|
15616
|
+
errors,
|
|
15617
|
+
schema: schemaResult,
|
|
15618
|
+
manifestWritten
|
|
15619
|
+
};
|
|
15620
|
+
}
|
|
15621
|
+
async function syncSkill(skillName, catalog, acc, ctx) {
|
|
15622
|
+
const { cwd, existingOverrides } = ctx;
|
|
15623
|
+
const {
|
|
15624
|
+
updated,
|
|
15625
|
+
regeneratedProjections,
|
|
15626
|
+
skippedProjectOwned,
|
|
15627
|
+
overrides,
|
|
15628
|
+
errors
|
|
15629
|
+
} = acc;
|
|
15630
|
+
const skillDir = resolvePackagedManagedSkillDir(skillName);
|
|
15631
|
+
const skillFiles = walkDirSync(skillDir);
|
|
15632
|
+
if (skillFiles.length === 0) {
|
|
15633
|
+
errors.push(`Missing packaged managed skill: ${skillName}`);
|
|
15634
|
+
return;
|
|
15635
|
+
}
|
|
15636
|
+
for (const sourceFile of skillFiles) {
|
|
15637
|
+
const relPath = relative3(skillDir, sourceFile);
|
|
15638
|
+
const targetRelativePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
|
|
15639
|
+
if (isProjectOwnedPath(targetRelativePath)) {
|
|
15640
|
+
skippedProjectOwned.push(targetRelativePath);
|
|
15641
|
+
continue;
|
|
15642
|
+
}
|
|
15643
|
+
const overridePath = `.pourkit/overrides/skills/${skillName}/${relPath}`;
|
|
15644
|
+
if (findOverrideForManagedPath(
|
|
15645
|
+
existingOverrides,
|
|
15646
|
+
targetRelativePath,
|
|
15647
|
+
overridePath
|
|
15648
|
+
)) {
|
|
15649
|
+
if (!overrides.includes(overridePath)) {
|
|
15650
|
+
overrides.push(overridePath);
|
|
15651
|
+
}
|
|
15652
|
+
continue;
|
|
15653
|
+
}
|
|
15654
|
+
const sourceContent = await readPackagedTextFile2(sourceFile);
|
|
15655
|
+
if (sourceContent === null) {
|
|
15656
|
+
errors.push(`Cannot read packaged skill file: ${skillName}/${relPath}`);
|
|
15657
|
+
continue;
|
|
15658
|
+
}
|
|
15659
|
+
const written = await syncFile(
|
|
15660
|
+
cwd,
|
|
15661
|
+
targetRelativePath,
|
|
15662
|
+
sourceContent,
|
|
15663
|
+
errors
|
|
15664
|
+
);
|
|
15665
|
+
if (written) {
|
|
15666
|
+
updated.push(targetRelativePath);
|
|
15667
|
+
}
|
|
15668
|
+
const projectionRelativePath = `.agents/skills/${skillName}/${relPath}`;
|
|
15669
|
+
const projectionOverridePath = `.pourkit/overrides/${projectionRelativePath}`;
|
|
15670
|
+
if (existingOverrides.some((o) => o === projectionOverridePath)) {
|
|
15671
|
+
if (!overrides.includes(projectionOverridePath)) {
|
|
15672
|
+
overrides.push(projectionOverridePath);
|
|
15673
|
+
}
|
|
15674
|
+
continue;
|
|
15675
|
+
}
|
|
15676
|
+
const managedSourcePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
|
|
15677
|
+
const projectionContent = buildOpenCodeSkillProjectionContent(
|
|
15678
|
+
managedSourcePath,
|
|
15679
|
+
sourceContent
|
|
15680
|
+
);
|
|
15681
|
+
const projectionWritten = await syncFile(
|
|
15682
|
+
cwd,
|
|
15683
|
+
projectionRelativePath,
|
|
15684
|
+
projectionContent,
|
|
15685
|
+
errors
|
|
15686
|
+
);
|
|
15687
|
+
if (projectionWritten) {
|
|
15688
|
+
regeneratedProjections.push(projectionRelativePath);
|
|
15689
|
+
}
|
|
15690
|
+
}
|
|
15691
|
+
}
|
|
15692
|
+
async function syncAddonSkill(skillName, acc, ctx) {
|
|
15693
|
+
const { cwd, existingOverrides } = ctx;
|
|
15694
|
+
const {
|
|
15695
|
+
updated,
|
|
15696
|
+
regeneratedProjections,
|
|
15697
|
+
skippedProjectOwned,
|
|
15698
|
+
overrides,
|
|
15699
|
+
errors
|
|
15700
|
+
} = acc;
|
|
15701
|
+
const addonSkillDir = resolvePackagedManagedPath2(
|
|
15702
|
+
"addons",
|
|
15703
|
+
"release",
|
|
15704
|
+
"skills",
|
|
15705
|
+
skillName
|
|
15706
|
+
);
|
|
15707
|
+
const skillFiles = walkDirSync(addonSkillDir);
|
|
15708
|
+
if (skillFiles.length === 0) {
|
|
15709
|
+
errors.push(`Missing packaged release addon skill: ${skillName}`);
|
|
15710
|
+
return;
|
|
15711
|
+
}
|
|
15712
|
+
for (const sourceFile of skillFiles) {
|
|
15713
|
+
const relPath = relative3(addonSkillDir, sourceFile);
|
|
15714
|
+
const targetRelativePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
|
|
15715
|
+
if (isProjectOwnedPath(targetRelativePath)) {
|
|
15716
|
+
skippedProjectOwned.push(targetRelativePath);
|
|
15717
|
+
continue;
|
|
15718
|
+
}
|
|
15719
|
+
const overridePath = `.pourkit/overrides/addons/release/skills/${skillName}/${relPath}`;
|
|
15720
|
+
if (findOverrideForManagedPath(
|
|
15721
|
+
existingOverrides,
|
|
15722
|
+
targetRelativePath,
|
|
15723
|
+
overridePath
|
|
15724
|
+
)) {
|
|
15725
|
+
if (!overrides.includes(overridePath)) {
|
|
15726
|
+
overrides.push(overridePath);
|
|
15727
|
+
}
|
|
15728
|
+
continue;
|
|
15729
|
+
}
|
|
15730
|
+
const sourceContent = await readPackagedTextFile2(sourceFile);
|
|
15731
|
+
if (sourceContent === null) {
|
|
15732
|
+
errors.push(
|
|
15733
|
+
`Cannot read packaged addon skill file: ${skillName}/${relPath}`
|
|
15734
|
+
);
|
|
15735
|
+
continue;
|
|
15736
|
+
}
|
|
15737
|
+
const written = await syncFile(
|
|
15738
|
+
cwd,
|
|
15739
|
+
targetRelativePath,
|
|
15740
|
+
sourceContent,
|
|
15741
|
+
errors
|
|
15742
|
+
);
|
|
15743
|
+
if (written) {
|
|
15744
|
+
updated.push(targetRelativePath);
|
|
15745
|
+
}
|
|
15746
|
+
const projectionRelativePath = `.agents/skills/${skillName}/${relPath}`;
|
|
15747
|
+
const projectionOverridePath = `.pourkit/overrides/${projectionRelativePath}`;
|
|
15748
|
+
if (existingOverrides.some((o) => o === projectionOverridePath)) {
|
|
15749
|
+
if (!overrides.includes(projectionOverridePath)) {
|
|
15750
|
+
overrides.push(projectionOverridePath);
|
|
15751
|
+
}
|
|
15752
|
+
continue;
|
|
15753
|
+
}
|
|
15754
|
+
const managedSourcePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
|
|
15755
|
+
const projectionContent = buildOpenCodeSkillProjectionContent(
|
|
15756
|
+
managedSourcePath,
|
|
15757
|
+
sourceContent
|
|
15758
|
+
);
|
|
15759
|
+
const projectionWritten = await syncFile(
|
|
15760
|
+
cwd,
|
|
15761
|
+
projectionRelativePath,
|
|
15762
|
+
projectionContent,
|
|
15763
|
+
errors
|
|
15764
|
+
);
|
|
15765
|
+
if (projectionWritten) {
|
|
15766
|
+
regeneratedProjections.push(projectionRelativePath);
|
|
15767
|
+
}
|
|
15768
|
+
}
|
|
15769
|
+
}
|
|
15770
|
+
|
|
14154
15771
|
// providers/github-provider.ts
|
|
14155
15772
|
var GitHubIssueProvider = class {
|
|
14156
15773
|
client;
|
|
@@ -14688,7 +16305,7 @@ function formatChecks2(checks) {
|
|
|
14688
16305
|
init_common();
|
|
14689
16306
|
|
|
14690
16307
|
// execution/sandcastle-execution.ts
|
|
14691
|
-
import { existsSync as
|
|
16308
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
14692
16309
|
import { join as join23 } from "path";
|
|
14693
16310
|
import { createWorktree, opencode } from "@ai-hero/sandcastle";
|
|
14694
16311
|
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
@@ -14696,14 +16313,14 @@ import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
|
14696
16313
|
// execution/execution-provider.ts
|
|
14697
16314
|
init_common();
|
|
14698
16315
|
import { mkdtempSync } from "fs";
|
|
14699
|
-
import { writeFile as
|
|
16316
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
14700
16317
|
import { tmpdir } from "os";
|
|
14701
|
-
import { dirname as
|
|
16318
|
+
import { dirname as dirname7, join as join22 } from "path";
|
|
14702
16319
|
async function writeExecutionArtifacts(worktreePath, artifacts) {
|
|
14703
16320
|
for (const artifact of artifacts) {
|
|
14704
16321
|
const filePath = join22(worktreePath, artifact.path);
|
|
14705
|
-
await ensureDir(
|
|
14706
|
-
await
|
|
16322
|
+
await ensureDir(dirname7(filePath));
|
|
16323
|
+
await writeFile5(filePath, artifact.content, "utf-8");
|
|
14707
16324
|
}
|
|
14708
16325
|
}
|
|
14709
16326
|
|
|
@@ -14713,14 +16330,14 @@ import path7 from "path";
|
|
|
14713
16330
|
|
|
14714
16331
|
// execution/sandbox-image.ts
|
|
14715
16332
|
import { createHash as createHash3 } from "crypto";
|
|
14716
|
-
import { existsSync as
|
|
16333
|
+
import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
|
|
14717
16334
|
import path6 from "path";
|
|
14718
16335
|
function sandboxImageName(repoRoot2) {
|
|
14719
16336
|
const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
|
|
14720
16337
|
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
14721
16338
|
const baseName = sanitized || "local";
|
|
14722
16339
|
const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
|
|
14723
|
-
if (!
|
|
16340
|
+
if (!existsSync21(dockerfilePath)) {
|
|
14724
16341
|
return `sandcastle:${baseName}`;
|
|
14725
16342
|
}
|
|
14726
16343
|
const fingerprint = createHash3("sha256").update(readFileSync21(dockerfilePath)).digest("hex").slice(0, 8);
|
|
@@ -14970,7 +16587,7 @@ function sanitizeBranch(branchName) {
|
|
|
14970
16587
|
function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
14971
16588
|
return paths.filter((relativePath) => {
|
|
14972
16589
|
const source = join23(repoRoot2, relativePath);
|
|
14973
|
-
if (!
|
|
16590
|
+
if (!existsSync22(source)) {
|
|
14974
16591
|
return true;
|
|
14975
16592
|
}
|
|
14976
16593
|
try {
|
|
@@ -14981,7 +16598,7 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
|
|
|
14981
16598
|
return true;
|
|
14982
16599
|
}
|
|
14983
16600
|
const destination = join23(worktreePath, relativePath);
|
|
14984
|
-
return !
|
|
16601
|
+
return !existsSync22(destination);
|
|
14985
16602
|
});
|
|
14986
16603
|
}
|
|
14987
16604
|
function formatAgentStreamEvent(event) {
|
|
@@ -15201,6 +16818,9 @@ function createCliProgram(version) {
|
|
|
15201
16818
|
).option(
|
|
15202
16819
|
"--branch-name <name>",
|
|
15203
16820
|
"branch name for issue-final-review artifacts"
|
|
16821
|
+
).option(
|
|
16822
|
+
"--base-ref <ref>",
|
|
16823
|
+
"canonical base ref for issue-final-review changedFiles coverage validation"
|
|
15204
16824
|
).option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
|
|
15205
16825
|
(kind, artifactPath, extraPaths, options) => {
|
|
15206
16826
|
const allowedKinds = /* @__PURE__ */ new Set([
|
|
@@ -15243,6 +16863,7 @@ function createCliProgram(version) {
|
|
|
15243
16863
|
allowedDecisions: options.allowedDecision,
|
|
15244
16864
|
issueNumber: options.issueNumber,
|
|
15245
16865
|
branchName: options.branchName,
|
|
16866
|
+
baseRef: options.baseRef,
|
|
15246
16867
|
checkConflictMarkers: options.checkConflictMarkers
|
|
15247
16868
|
});
|
|
15248
16869
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -15563,7 +17184,10 @@ function createCliProgram(version) {
|
|
|
15563
17184
|
const result = runPrdRunListCommand({ repoRoot: targetRepoRoot });
|
|
15564
17185
|
console.log(JSON.stringify(result, null, 2));
|
|
15565
17186
|
});
|
|
15566
|
-
program.command("init").description("Initialize .pourkit layout in a target repo").option("--dry-run", "print the init plan without applying changes").option("--json", "output machine-readable JSON plan").option(
|
|
17187
|
+
program.command("init").description("Initialize .pourkit layout in a target repo").option("--dry-run", "print the init plan without applying changes").option("--json", "output machine-readable JSON plan").option(
|
|
17188
|
+
"--from-local <path>",
|
|
17189
|
+
"deprecated: local source repo to copy artifacts from"
|
|
17190
|
+
).option("--cwd <path>", "target repository directory").addOption(
|
|
15567
17191
|
new Option(
|
|
15568
17192
|
"--docs-migration <mode>",
|
|
15569
17193
|
"docs migration mode: copy, move, skip"
|
|
@@ -15603,7 +17227,7 @@ function createCliProgram(version) {
|
|
|
15603
17227
|
const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
|
|
15604
17228
|
const report = await runDoctorCommand({ cwd: targetRepoRoot });
|
|
15605
17229
|
console.log(JSON.stringify(report, null, 2));
|
|
15606
|
-
if (!report.configValidation.ok || report.obsoleteConfigs.length > 0 || report.schemaAssets.overall !== "fresh") {
|
|
17230
|
+
if (!report.configValidation.ok || report.obsoleteConfigs.length > 0 || report.schemaAssets.overall !== "fresh" || report.workflowPack.status === "failed") {
|
|
15607
17231
|
process.exitCode = 1;
|
|
15608
17232
|
}
|
|
15609
17233
|
});
|
|
@@ -15619,6 +17243,24 @@ function createCliProgram(version) {
|
|
|
15619
17243
|
console.log("Schema assets are up to date.");
|
|
15620
17244
|
}
|
|
15621
17245
|
});
|
|
17246
|
+
const syncCommand = program.command("sync").description("Sync commands for refreshing managed assets");
|
|
17247
|
+
syncCommand.command("workflow-pack").description("Refresh managed workflow pack assets from packaged sources").option("--cwd <path>", "target repository directory").action(async (options) => {
|
|
17248
|
+
const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
|
|
17249
|
+
const result = await runWorkflowPackSyncCommand({ cwd: targetRepoRoot });
|
|
17250
|
+
if (result.changed) {
|
|
17251
|
+
console.log(
|
|
17252
|
+
`Workflow pack sync complete: ${result.updated.length} updated, ${result.regeneratedProjections.length} projections regenerated, ${result.overrides.length} overrides, ${result.skippedProjectOwned.length} project-owned files skipped.`
|
|
17253
|
+
);
|
|
17254
|
+
} else {
|
|
17255
|
+
console.log("Workflow pack is up to date.");
|
|
17256
|
+
}
|
|
17257
|
+
if (result.errors.length > 0) {
|
|
17258
|
+
for (const err of result.errors) {
|
|
17259
|
+
console.error(` Error: ${err}`);
|
|
17260
|
+
}
|
|
17261
|
+
process.exitCode = 1;
|
|
17262
|
+
}
|
|
17263
|
+
});
|
|
15622
17264
|
const serena = program.command("serena").description("Serena baseline and sidecar commands");
|
|
15623
17265
|
serena.command("init").requiredOption("--target <name>", "target name").option("--cwd <path>", "target repository directory").action(async (options) => {
|
|
15624
17266
|
await runSerenaInitCommand({
|
|
@@ -15744,11 +17386,11 @@ function createCliProgram(version) {
|
|
|
15744
17386
|
return program;
|
|
15745
17387
|
}
|
|
15746
17388
|
async function resolveCliVersion() {
|
|
15747
|
-
if (isPackageVersion("0.0.0-next-
|
|
15748
|
-
return "0.0.0-next-
|
|
17389
|
+
if (isPackageVersion("0.0.0-next-20260617195445")) {
|
|
17390
|
+
return "0.0.0-next-20260617195445";
|
|
15749
17391
|
}
|
|
15750
|
-
if (isReleaseVersion("0.0.0-next-
|
|
15751
|
-
return "0.0.0-next-
|
|
17392
|
+
if (isReleaseVersion("0.0.0-next-20260617195445")) {
|
|
17393
|
+
return "0.0.0-next-20260617195445";
|
|
15752
17394
|
}
|
|
15753
17395
|
try {
|
|
15754
17396
|
const root = repoRoot();
|