@pourkit/cli 0.0.0-next-20260615184943 → 0.0.0-next-20260617183635

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.
Files changed (62) hide show
  1. package/dist/cli.js +1797 -149
  2. package/dist/cli.js.map +1 -1
  3. package/dist/e2e/run-live-e2e.js +112 -20
  4. package/dist/e2e/run-live-e2e.js.map +1 -1
  5. package/dist/managed/addons/release/docs/agents/release-workflow.md +33 -0
  6. package/dist/managed/addons/release/skills/changeset/SKILL.md +76 -0
  7. package/dist/managed/addons/release/skills/promote-release/SKILL.md +94 -0
  8. package/dist/managed/docs/agents/commit-style.md +75 -0
  9. package/dist/managed/docs/agents/domain.md +57 -0
  10. package/dist/managed/docs/agents/git-workflow.md +197 -0
  11. package/dist/managed/docs/agents/issue-tracker.md +33 -0
  12. package/dist/managed/docs/agents/naming.md +58 -0
  13. package/dist/managed/docs/agents/triage-labels.md +28 -0
  14. package/dist/managed/prompts/builder.prompt.md +89 -0
  15. package/dist/managed/prompts/conflict-resolution.prompt.md +70 -0
  16. package/dist/managed/prompts/failure-resolution.prompt.md +68 -0
  17. package/dist/managed/prompts/finalizer.prompt.md +77 -0
  18. package/dist/managed/prompts/issue-final-review.prompt.md +122 -0
  19. package/dist/managed/prompts/pr-description.prompt.md +77 -0
  20. package/dist/managed/prompts/refactor.prompt.md +114 -0
  21. package/dist/managed/prompts/reviewer-correctness.snippet.md +5 -0
  22. package/dist/managed/prompts/reviewer-quality.snippet.md +3 -0
  23. package/dist/managed/prompts/reviewer-scope.snippet.md +5 -0
  24. package/dist/managed/prompts/reviewer-tests.snippet.md +5 -0
  25. package/dist/managed/prompts/reviewer.prompt.md +169 -0
  26. package/dist/managed/skills/code-review/SKILL.md +152 -0
  27. package/dist/managed/skills/code-review/references/thermo-nuclear-code-quality-review.md +147 -0
  28. package/dist/managed/skills/diagnose/SKILL.md +119 -0
  29. package/dist/managed/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
  30. package/dist/managed/skills/explain/EXAMPLES.md +227 -0
  31. package/dist/managed/skills/explain/SKILL.md +106 -0
  32. package/dist/managed/skills/explain/references/mermaid.md +136 -0
  33. package/dist/managed/skills/grill-with-docs/ADR-FORMAT.md +47 -0
  34. package/dist/managed/skills/grill-with-docs/CONTEXT-FORMAT.md +77 -0
  35. package/dist/managed/skills/grill-with-docs/SKILL.md +88 -0
  36. package/dist/managed/skills/handoff/SKILL.md +13 -0
  37. package/dist/managed/skills/improve-codebase-architecture/DEEPENING.md +37 -0
  38. package/dist/managed/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
  39. package/dist/managed/skills/improve-codebase-architecture/LANGUAGE.md +53 -0
  40. package/dist/managed/skills/improve-codebase-architecture/SKILL.md +71 -0
  41. package/dist/managed/skills/publish-prd/SKILL.md +130 -0
  42. package/dist/managed/skills/secret-commit-guard/SKILL.md +54 -0
  43. package/dist/managed/skills/security-review/SKILL.md +495 -0
  44. package/dist/managed/skills/security-review/cloud-infrastructure-security.md +361 -0
  45. package/dist/managed/skills/ship-current-changes/SKILL.md +199 -0
  46. package/dist/managed/skills/tdd/SKILL.md +110 -0
  47. package/dist/managed/skills/tdd/deep-modules.md +33 -0
  48. package/dist/managed/skills/tdd/interface-design.md +31 -0
  49. package/dist/managed/skills/tdd/mocking.md +59 -0
  50. package/dist/managed/skills/tdd/refactoring.md +10 -0
  51. package/dist/managed/skills/tdd/tests.md +61 -0
  52. package/dist/managed/skills/to-issues/SKILL.md +442 -0
  53. package/dist/managed/skills/to-plan/SKILL.md +20 -0
  54. package/dist/managed/skills/to-prd/SKILL.md +129 -0
  55. package/dist/managed/skills/triage/AGENT-BRIEF.md +168 -0
  56. package/dist/managed/skills/triage/OUT-OF-SCOPE.md +101 -0
  57. package/dist/managed/skills/triage/SKILL.md +162 -0
  58. package/dist/managed/skills/write-a-skill/SKILL.md +117 -0
  59. package/dist/managed/skills/zoom-out/SKILL.md +7 -0
  60. package/dist/schema/pourkit.schema.hash +1 -1
  61. package/dist/schema/pourkit.schema.json +32 -1
  62. 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((resolve5) => {
61
+ await new Promise((resolve6) => {
62
62
  if (!fileStream) {
63
- resolve5();
63
+ resolve6();
64
64
  return;
65
65
  }
66
66
  const timer = setTimeout(() => {
67
67
  if (!fileStream.destroyed) {
68
68
  fileStream.destroy();
69
69
  }
70
- resolve5();
70
+ resolve6();
71
71
  }, 2e3);
72
72
  fileStream.end(() => {
73
73
  clearTimeout(timer);
74
- resolve5();
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((resolve5) => setTimeout(resolve5, ms));
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: existsSync22 } = await import("fs");
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 (existsSync22(fullPath)) {
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 (!existsSync22(configPath)) {
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: readFile7 } = await import("fs/promises");
913
- const raw = await readFile7(configPath, "utf-8");
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: readdir2, stat, unlink, access: access2 } = await import("fs/promises");
1070
+ const { readdir: readdir4, stat, unlink, access: access2 } = await import("fs/promises");
1071
1071
  let entries;
1072
1072
  try {
1073
- entries = await readdir2(dirPath);
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((resolve5) => setTimeout(resolve5, 100));
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(repoRoot2, agent.promptTemplate);
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(repoRoot2, promptTemplate) {
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 assertIssueFinalReviewCoversActualDiff({
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: result.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 assertIssueFinalReviewCoversActualDiff(options) {
7633
- const actualChangedPaths = await listIssueFinalReviewChangedPaths(options);
7634
- const declared = new Set(options.declaredChangedPaths.map(normalizeGitPath));
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
- throw new Error(
7640
- `Issue Final Review pass did not enumerate all changed files. Missing from changedFiles: ${missing.join(", ")}. Re-run Issue Final Review or remove out-of-scope files before finalization.`
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 listIssueFinalReviewChangedPaths(options) {
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
- ...parseGitPathList(trackedDiff.stdout),
7665
- ...parseGitPathList(untrackedFiles.stdout)
7757
+ ...parseGitPathList2(trackedDiff.stdout),
7758
+ ...parseGitPathList2(untrackedFiles.stdout)
7666
7759
  ])
7667
- ).filter(isIssueFinalReviewAuditedPath);
7760
+ ).filter(isIssueFinalReviewAuditedPath2);
7668
7761
  }
7669
- function parseGitPathList(output) {
7670
- return output.split(/\r?\n/).map(normalizeGitPath).filter((path9) => path9.length > 0);
7762
+ function parseGitPathList2(output) {
7763
+ return output.split(/\r?\n/).map(normalizeGitPath2).filter((path9) => path9.length > 0);
7671
7764
  }
7672
- function normalizeGitPath(path9) {
7765
+ function normalizeGitPath2(path9) {
7673
7766
  return path9.replace(/^"|"$/g, "").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
7674
7767
  }
7675
- function isIssueFinalReviewAuditedPath(path9) {
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) {
@@ -11933,8 +12026,124 @@ async function tryCreateGitHubClient(options) {
11933
12026
  return { ok: true, client: { octokit, ...repo } };
11934
12027
  }
11935
12028
 
12029
+ // workflow-pack.ts
12030
+ var BASELINE_MANAGED_SKILLS = [
12031
+ "code-review",
12032
+ "diagnose",
12033
+ "explain",
12034
+ "grill-with-docs",
12035
+ "handoff",
12036
+ "improve-codebase-architecture",
12037
+ "publish-prd",
12038
+ "secret-commit-guard",
12039
+ "security-review",
12040
+ "ship-current-changes",
12041
+ "tdd",
12042
+ "to-issues",
12043
+ "to-plan",
12044
+ "to-prd",
12045
+ "triage",
12046
+ "write-a-skill",
12047
+ "zoom-out"
12048
+ ];
12049
+ var RELEASE_ADDON_SKILLS = ["changeset", "promote-release"];
12050
+ var RELEASE_ADDON_DOCS = ["release-workflow.md"];
12051
+ var MANAGED_DOCS = [
12052
+ "git-workflow.md",
12053
+ "issue-tracker.md",
12054
+ "naming.md",
12055
+ "triage-labels.md",
12056
+ "commit-style.md",
12057
+ "domain.md"
12058
+ ];
12059
+ var MANAGED_PROMPTS = [
12060
+ "builder.prompt.md",
12061
+ "conflict-resolution.prompt.md",
12062
+ "failure-resolution.prompt.md",
12063
+ "finalizer.prompt.md",
12064
+ "issue-final-review.prompt.md",
12065
+ "pr-description.prompt.md",
12066
+ "refactor.prompt.md",
12067
+ "reviewer.prompt.md",
12068
+ "reviewer-correctness.snippet.md",
12069
+ "reviewer-quality.snippet.md",
12070
+ "reviewer-scope.snippet.md",
12071
+ "reviewer-tests.snippet.md"
12072
+ ];
12073
+ var OPENCODE_PROJECTION = {
12074
+ harness: "opencode",
12075
+ path: ".agents/skills"
12076
+ };
12077
+ var CURRENT_VERSION = "0.0.0-next";
12078
+ function getWorkflowPackCatalog() {
12079
+ return {
12080
+ docs: [...MANAGED_DOCS],
12081
+ prompts: [...MANAGED_PROMPTS],
12082
+ baselineSkills: [...BASELINE_MANAGED_SKILLS],
12083
+ releaseAddonDocs: [...RELEASE_ADDON_DOCS],
12084
+ releaseAddonSkills: [...RELEASE_ADDON_SKILLS],
12085
+ projections: [OPENCODE_PROJECTION]
12086
+ };
12087
+ }
12088
+ function buildOpenCodeSkillProjectionContent(managedSourcePath, sourceContent) {
12089
+ const generatedNotice = `Generated from ${managedSourcePath}. Do not edit directly.`;
12090
+ if (managedSourcePath.endsWith(".md") || managedSourcePath.endsWith(".mdx")) {
12091
+ return `<!-- ${generatedNotice} -->
12092
+ ${sourceContent}`;
12093
+ }
12094
+ 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")) {
12095
+ const comment = `# ${generatedNotice}`;
12096
+ if (sourceContent.startsWith("#!")) {
12097
+ const lineBreakIndex = sourceContent.indexOf("\n");
12098
+ if (lineBreakIndex === -1) {
12099
+ return `${sourceContent}
12100
+ ${comment}
12101
+ `;
12102
+ }
12103
+ return `${sourceContent.slice(0, lineBreakIndex + 1)}${comment}
12104
+ ${sourceContent.slice(lineBreakIndex + 1)}`;
12105
+ }
12106
+ return `${comment}
12107
+ ${sourceContent}`;
12108
+ }
12109
+ return sourceContent;
12110
+ }
12111
+ function getBaselineManagedSkillNames() {
12112
+ return BASELINE_MANAGED_SKILLS;
12113
+ }
12114
+ function getWorkflowPackVersion() {
12115
+ return CURRENT_VERSION;
12116
+ }
12117
+ function buildWorkflowPackMetadata(enabledAddons) {
12118
+ return {
12119
+ version: getWorkflowPackVersion(),
12120
+ enabledAddons,
12121
+ projections: [OPENCODE_PROJECTION]
12122
+ };
12123
+ }
12124
+
11936
12125
  // commands/init.ts
11937
12126
  var execFileAsync2 = promisify2(execFile2);
12127
+ var RELEASE_ADDON_SKILL_NAMES = ["changeset", "promote-release"];
12128
+ var BASELINE_SKILL_NAMES = [
12129
+ "code-review",
12130
+ "diagnose",
12131
+ "explain",
12132
+ "grill-with-docs",
12133
+ "handoff",
12134
+ "improve-codebase-architecture",
12135
+ "publish-prd",
12136
+ "secret-commit-guard",
12137
+ "security-review",
12138
+ "ship-current-changes",
12139
+ "tdd",
12140
+ "to-issues",
12141
+ "to-plan",
12142
+ "to-prd",
12143
+ "triage",
12144
+ "write-a-skill",
12145
+ "zoom-out"
12146
+ ];
11938
12147
  var NO_TOKEN_LABEL_PROVISIONING_WARNING = "Skipped GitHub label provisioning because no GitHub token was provided.";
11939
12148
  var ALLOWED_VERIFICATION_SCRIPTS = [
11940
12149
  "typecheck",
@@ -12077,26 +12286,26 @@ function generateConfigTemplate(options) {
12077
12286
  builder: {
12078
12287
  agent: "pourkit-builder",
12079
12288
  model: "opencode-go/deepseek-v4-flash",
12080
- promptTemplate: ".pourkit/prompts/builder.prompt.md"
12289
+ promptTemplate: ".pourkit/managed/prompts/builder.prompt.md"
12081
12290
  }
12082
12291
  },
12083
12292
  failureResolution: {
12084
12293
  agent: "pourkit-failure-resolution-agent",
12085
12294
  model: "opencode-go/mimo-v2.5",
12086
- promptTemplate: ".pourkit/prompts/failure-resolution.prompt.md",
12295
+ promptTemplate: ".pourkit/managed/prompts/failure-resolution.prompt.md",
12087
12296
  maxAttemptsPerFailure: 1
12088
12297
  },
12089
12298
  review: {
12090
12299
  reviewer: {
12091
12300
  agent: "pourkit-reviewer",
12092
12301
  model: "opencode-go/deepseek-v4-pro",
12093
- promptTemplate: ".pourkit/prompts/reviewer.prompt.md",
12302
+ promptTemplate: ".pourkit/managed/prompts/reviewer.prompt.md",
12094
12303
  criteria: ["correctness", "scope", "tests", "quality"]
12095
12304
  },
12096
12305
  refactor: {
12097
12306
  agent: "pourkit-refactor",
12098
12307
  model: "opencode-go/qwen3.6-plus",
12099
- promptTemplate: ".pourkit/prompts/refactor.prompt.md"
12308
+ promptTemplate: ".pourkit/managed/prompts/refactor.prompt.md"
12100
12309
  },
12101
12310
  maxIterations: 3,
12102
12311
  passWithNotesRefactorAttempts: 2
@@ -12104,14 +12313,14 @@ function generateConfigTemplate(options) {
12104
12313
  issueFinalReview: {
12105
12314
  agent: "pourkit-reviewer",
12106
12315
  model: "opencode-go/deepseek-v4-pro",
12107
- promptTemplate: ".pourkit/prompts/issue-final-review.prompt.md",
12316
+ promptTemplate: ".pourkit/managed/prompts/issue-final-review.prompt.md",
12108
12317
  maxAttempts: 3
12109
12318
  },
12110
12319
  finalize: {
12111
12320
  prDescriptionAgent: {
12112
12321
  agent: "pourkit-pr-description",
12113
12322
  model: "opencode-go/deepseek-v4-flash",
12114
- promptTemplate: ".pourkit/prompts/pr-description.prompt.md"
12323
+ promptTemplate: ".pourkit/managed/prompts/pr-description.prompt.md"
12115
12324
  },
12116
12325
  maxAttempts: 2
12117
12326
  }
@@ -12131,6 +12340,17 @@ function generateConfigTemplate(options) {
12131
12340
  $schema: "./schema/pourkit.schema.json",
12132
12341
  schemaVersion: 1,
12133
12342
  targets: [target],
12343
+ workflowPack: {
12344
+ version: "1.0.0",
12345
+ skillProjections: [{ harness: "opencode", path: ".agents/skills" }]
12346
+ },
12347
+ releaseWorkflow: options.releaseWorkflow?.enabled ? {
12348
+ enabled: true,
12349
+ integrationBranch: options.releaseWorkflow.integrationBranch ?? "integration",
12350
+ developmentReleaseBranch: options.releaseWorkflow.developmentReleaseBranch ?? "preview",
12351
+ stableReleaseBranch: options.releaseWorkflow.stableReleaseBranch ?? "stable",
12352
+ changesets: options.releaseWorkflow.changesets ?? true
12353
+ } : { enabled: false },
12134
12354
  labels: {
12135
12355
  readyForAgent: labels.readyForAgent,
12136
12356
  agentInProgress: labels.agentInProgress,
@@ -12278,18 +12498,33 @@ _Avoid_: Alternative terms
12278
12498
  - <Document ambiguous terms and their resolution here.>
12279
12499
  `;
12280
12500
  }
12281
- async function generateManagedAgentInstructions(options) {
12282
- const sourcePath = path5.join(options.sourceRoot, "AGENTS.md");
12283
- try {
12284
- return await readFile5(sourcePath, "utf-8");
12285
- } catch {
12286
- return `## Codebase exploration
12501
+ async function generateManagedAgentInstructions(_options) {
12502
+ return `## Codebase exploration
12287
12503
 
12288
12504
  Use \`fd\` for file discovery, \`rg\` for text search, and direct file reads for focused context.
12289
12505
 
12290
- Follow the project's domain docs and conventions documented in \`.pourkit/docs/agents/*\`.
12506
+ Follow the project's domain docs and conventions documented in \`.pourkit/managed/docs/agents/*\`.
12507
+
12508
+ ### Managed operating docs
12509
+
12510
+ - \`.pourkit/managed/docs/agents/issue-tracker.md\`
12511
+ - \`.pourkit/managed/docs/agents/triage-labels.md\`
12512
+ - \`.pourkit/managed/docs/agents/naming.md\`
12513
+ - \`.pourkit/managed/docs/agents/git-workflow.md\`
12514
+ - \`.pourkit/managed/docs/agents/commit-style.md\`
12515
+ - \`.pourkit/managed/docs/agents/domain.md\`
12516
+
12517
+ ### Project-owned context
12518
+
12519
+ - \`.pourkit/CONTEXT.md\`
12520
+ - \`.pourkit/CONTEXT-MAP.md\`
12521
+ - \`.pourkit/docs/adr/*\`
12522
+
12523
+ ### Important
12524
+
12525
+ - Do not edit \`.pourkit/managed/*\` unless explicitly updating Pourkit-managed assets.
12526
+ - Do not edit generated harness projections directly.
12291
12527
  `;
12292
- }
12293
12528
  }
12294
12529
  function generateGitignoreBlock() {
12295
12530
  const lines = [
@@ -12655,11 +12890,28 @@ async function planInit(options) {
12655
12890
  }
12656
12891
  const plannedSkillDests = /* @__PURE__ */ new Set();
12657
12892
  for (const op of operations) {
12658
- if (op.kind === "copy" && op.path?.includes(".agents/skills")) {
12893
+ if (op.path?.includes(".agents/skills")) {
12659
12894
  plannedSkillDests.add(op.path);
12660
12895
  }
12661
12896
  }
12662
- const srcSkills = await discoverAgentSkills(sourceRoot);
12897
+ const packagedManagedSkills = path5.join(
12898
+ sourceRoot,
12899
+ "pourkit",
12900
+ "managed",
12901
+ "skills"
12902
+ );
12903
+ const packagedReleaseAddonSkills = path5.join(
12904
+ sourceRoot,
12905
+ "pourkit",
12906
+ "managed",
12907
+ "addons",
12908
+ "release",
12909
+ "skills"
12910
+ );
12911
+ const srcSkills = existsSync18(packagedManagedSkills) ? [
12912
+ packagedManagedSkills,
12913
+ ...existsSync18(packagedReleaseAddonSkills) ? [packagedReleaseAddonSkills] : []
12914
+ ] : await discoverAgentSkills(sourceRoot);
12663
12915
  for (const s of srcSkills) {
12664
12916
  const isOpenCode = s.includes(".opencode");
12665
12917
  if (isOpenCode && !options.legacySkills) {
@@ -12671,35 +12923,118 @@ async function planInit(options) {
12671
12923
  });
12672
12924
  continue;
12673
12925
  }
12674
- const targetDirName = ".agents/skills";
12675
- const targetPath = path5.join(targetRoot, targetDirName);
12676
- const skillFiles = await walkDir(s);
12677
- for (const file of skillFiles) {
12678
- const relPath = path5.relative(s, file);
12679
- const destPath = path5.join(targetPath, relPath);
12680
- if (plannedSkillDests.has(destPath)) {
12926
+ if (isOpenCode) {
12927
+ const skillFiles = await walkDir(s);
12928
+ for (const file of skillFiles) {
12929
+ const relPath = path5.relative(s, file);
12930
+ const destPath = path5.join(
12931
+ targetRoot,
12932
+ ".agents",
12933
+ "skills",
12934
+ relPath
12935
+ );
12936
+ if (!existsSync18(destPath)) {
12937
+ operations.push({
12938
+ kind: "copy",
12939
+ sourcePath: file,
12940
+ path: destPath,
12941
+ ownership: "project-owned",
12942
+ reason: `Migrate legacy skill: ${path5.join(".opencode/skills", relPath)}`,
12943
+ requiresConfirmation: false,
12944
+ destructive: false
12945
+ });
12946
+ }
12947
+ }
12948
+ continue;
12949
+ }
12950
+ const managedSkillsDir = path5.join(
12951
+ targetRoot,
12952
+ ".pourkit",
12953
+ "managed",
12954
+ "skills"
12955
+ );
12956
+ const projectionSkillsDir = path5.join(targetRoot, ".agents", "skills");
12957
+ const entries = await readdir(s, { withFileTypes: true });
12958
+ const skillNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
12959
+ const addonManagedDir = path5.join(
12960
+ targetRoot,
12961
+ ".pourkit",
12962
+ "managed",
12963
+ "addons",
12964
+ "release",
12965
+ "skills"
12966
+ );
12967
+ for (const skillName of skillNames) {
12968
+ const isReleaseAddon = RELEASE_ADDON_SKILL_NAMES.includes(skillName);
12969
+ const isBaseline = BASELINE_SKILL_NAMES.includes(skillName);
12970
+ if (isReleaseAddon) {
12971
+ if (!options.releaseWorkflow?.enabled) {
12972
+ operations.push({
12973
+ kind: "skip",
12974
+ reason: `Release add-on skill not enabled: ${skillName}`,
12975
+ requiresConfirmation: false,
12976
+ destructive: false
12977
+ });
12978
+ continue;
12979
+ }
12980
+ } else if (!isBaseline) {
12681
12981
  operations.push({
12682
12982
  kind: "skip",
12683
- path: destPath,
12684
- ownership: "project-owned",
12685
- reason: `Skill destination conflict, skipping source copy: ${path5.join(targetDirName, relPath)}`,
12983
+ reason: `Non-baseline skill not installed: ${skillName}`,
12686
12984
  requiresConfirmation: false,
12687
- destructive: false,
12688
- conflict: "destination already planned"
12985
+ destructive: false
12689
12986
  });
12690
12987
  continue;
12691
12988
  }
12692
- const checksum = await computeFileChecksum(file);
12693
- operations.push({
12694
- kind: "copy",
12695
- sourcePath: file,
12696
- path: destPath,
12697
- ownership: "copied-customizable",
12698
- reason: `Copy skill: ${path5.join(targetDirName, relPath)}`,
12699
- requiresConfirmation: false,
12700
- destructive: false,
12701
- checksum
12702
- });
12989
+ const skillSourceDir = path5.join(s, skillName);
12990
+ const skillFiles = await walkDir(skillSourceDir);
12991
+ for (const file of skillFiles) {
12992
+ const relPath = path5.relative(skillSourceDir, file);
12993
+ const managedDest = isReleaseAddon ? path5.join(addonManagedDir, skillName, relPath) : path5.join(managedSkillsDir, skillName, relPath);
12994
+ const checksum = await computeFileChecksum(file);
12995
+ const installLabel = isReleaseAddon ? `Install release add-on skill: ${skillName}/${relPath}` : `Install managed skill: ${skillName}/${relPath}`;
12996
+ operations.push({
12997
+ kind: "copy",
12998
+ sourcePath: file,
12999
+ path: managedDest,
13000
+ ownership: "managed",
13001
+ reason: installLabel,
13002
+ requiresConfirmation: false,
13003
+ destructive: false,
13004
+ checksum
13005
+ });
13006
+ const projectionDest = path5.join(
13007
+ projectionSkillsDir,
13008
+ skillName,
13009
+ relPath
13010
+ );
13011
+ if (plannedSkillDests.has(projectionDest)) {
13012
+ operations.push({
13013
+ kind: "skip",
13014
+ path: projectionDest,
13015
+ ownership: "project-owned",
13016
+ reason: `Skill destination conflict, skipping projection: ${skillName}/${relPath}`,
13017
+ requiresConfirmation: false,
13018
+ destructive: false,
13019
+ conflict: "destination already planned"
13020
+ });
13021
+ continue;
13022
+ }
13023
+ const sourceContent = await readFile5(file, "utf-8");
13024
+ const managedSourcePath = isReleaseAddon ? `.pourkit/managed/addons/release/skills/${skillName}/${relPath}` : `.pourkit/managed/skills/${skillName}/${relPath}`;
13025
+ operations.push({
13026
+ kind: "create",
13027
+ path: projectionDest,
13028
+ ownership: "managed",
13029
+ reason: `Project skill: ${skillName}/${relPath}`,
13030
+ requiresConfirmation: false,
13031
+ destructive: false,
13032
+ content: buildOpenCodeSkillProjectionContent(
13033
+ managedSourcePath,
13034
+ sourceContent
13035
+ )
13036
+ });
13037
+ }
12703
13038
  }
12704
13039
  }
12705
13040
  const srcReadme = await discoverReadme(sourceRoot);
@@ -12792,7 +13127,7 @@ async function planInit(options) {
12792
13127
  operations.push({
12793
13128
  kind: "create",
12794
13129
  path: contextPath,
12795
- ownership: "managed",
13130
+ ownership: "project-owned",
12796
13131
  reason: "Init CONTEXT.md scaffold",
12797
13132
  requiresConfirmation: false,
12798
13133
  destructive: false,
@@ -12816,16 +13151,41 @@ async function planInit(options) {
12816
13151
  destructive: false
12817
13152
  });
12818
13153
  }
12819
- const srcDocAgents = path5.join(sourceRoot, ".pourkit", "docs", "agents");
12820
- const tgtDocAgents = path5.join(targetRoot, ".pourkit", "docs", "agents");
12821
- if (existsSync18(srcDocAgents) && !existsSync18(tgtDocAgents)) {
13154
+ const packagedManagedDocAgents = path5.join(
13155
+ sourceRoot,
13156
+ "pourkit",
13157
+ "managed",
13158
+ "docs",
13159
+ "agents"
13160
+ );
13161
+ const legacyDocAgents = path5.join(
13162
+ sourceRoot,
13163
+ ".pourkit",
13164
+ "docs",
13165
+ "agents"
13166
+ );
13167
+ const srcDocAgents = existsSync18(packagedManagedDocAgents) ? packagedManagedDocAgents : legacyDocAgents;
13168
+ const tgtManagedDocAgents = path5.join(
13169
+ targetRoot,
13170
+ ".pourkit",
13171
+ "managed",
13172
+ "docs",
13173
+ "agents"
13174
+ );
13175
+ const tgtStubDocAgents = path5.join(
13176
+ targetRoot,
13177
+ ".pourkit",
13178
+ "docs",
13179
+ "agents"
13180
+ );
13181
+ if (existsSync18(srcDocAgents) && !existsSync18(tgtManagedDocAgents)) {
12822
13182
  const docFiles = await walkDir(srcDocAgents);
12823
13183
  for (const file of docFiles) {
12824
13184
  const relPath = path5.relative(srcDocAgents, file);
12825
13185
  if (relPath === "triage-labels.md") {
12826
13186
  operations.push({
12827
13187
  kind: "create",
12828
- path: path5.join(tgtDocAgents, relPath),
13188
+ path: path5.join(tgtManagedDocAgents, relPath),
12829
13189
  ownership: "managed",
12830
13190
  reason: "Init triage labels doc",
12831
13191
  requiresConfirmation: false,
@@ -12834,24 +13194,94 @@ async function planInit(options) {
12834
13194
  options.labels ?? DEFAULT_RUNNER_LABELS
12835
13195
  )
12836
13196
  });
12837
- continue;
13197
+ } else {
13198
+ const checksum = await computeFileChecksum(file);
13199
+ operations.push({
13200
+ kind: "copy",
13201
+ sourcePath: file,
13202
+ path: path5.join(tgtManagedDocAgents, relPath),
13203
+ ownership: "managed",
13204
+ reason: `Copy agent doc: ${relPath}`,
13205
+ requiresConfirmation: false,
13206
+ destructive: false,
13207
+ checksum
13208
+ });
12838
13209
  }
12839
- const checksum = await computeFileChecksum(file);
13210
+ if (relPath === "triage-labels.md") continue;
13211
+ const managedRelPath = path5.join(
13212
+ ".pourkit",
13213
+ "managed",
13214
+ "docs",
13215
+ "agents",
13216
+ relPath
13217
+ );
12840
13218
  operations.push({
12841
- kind: "copy",
12842
- sourcePath: file,
12843
- path: path5.join(tgtDocAgents, relPath),
13219
+ kind: "create",
13220
+ path: path5.join(tgtStubDocAgents, relPath),
12844
13221
  ownership: "managed",
12845
- reason: `Copy agent doc: ${relPath}`,
13222
+ reason: `Create moved stub for: ${relPath}`,
12846
13223
  requiresConfirmation: false,
12847
13224
  destructive: false,
12848
- checksum
13225
+ content: `# Moved
13226
+
13227
+ This Pourkit operating doc moved to \`${managedRelPath}\`.
13228
+ Do not edit this file.
13229
+ `
12849
13230
  });
12850
13231
  }
12851
13232
  }
12852
- const srcPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
12853
- const tgtPrompts = path5.join(targetRoot, ".pourkit", "prompts");
12854
- if (existsSync18(srcPrompts) && !existsSync18(tgtPrompts)) {
13233
+ if (options.releaseWorkflow?.enabled) {
13234
+ const packagedReleaseAddonDocs = path5.join(
13235
+ sourceRoot,
13236
+ "pourkit",
13237
+ "managed",
13238
+ "addons",
13239
+ "release",
13240
+ "docs",
13241
+ "agents"
13242
+ );
13243
+ const tgtReleaseAddonDocs = path5.join(
13244
+ targetRoot,
13245
+ ".pourkit",
13246
+ "managed",
13247
+ "addons",
13248
+ "release",
13249
+ "docs",
13250
+ "agents"
13251
+ );
13252
+ if (existsSync18(packagedReleaseAddonDocs)) {
13253
+ const docFiles = await walkDir(packagedReleaseAddonDocs);
13254
+ for (const file of docFiles) {
13255
+ const relPath = path5.relative(packagedReleaseAddonDocs, file);
13256
+ const checksum = await computeFileChecksum(file);
13257
+ operations.push({
13258
+ kind: "copy",
13259
+ sourcePath: file,
13260
+ path: path5.join(tgtReleaseAddonDocs, relPath),
13261
+ ownership: "managed",
13262
+ reason: `Install release add-on doc: ${relPath}`,
13263
+ requiresConfirmation: false,
13264
+ destructive: false,
13265
+ checksum
13266
+ });
13267
+ }
13268
+ }
13269
+ }
13270
+ const packagedManagedPrompts = path5.join(
13271
+ sourceRoot,
13272
+ "pourkit",
13273
+ "managed",
13274
+ "prompts"
13275
+ );
13276
+ const legacyPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
13277
+ const srcPrompts = existsSync18(packagedManagedPrompts) ? packagedManagedPrompts : legacyPrompts;
13278
+ const tgtManagedPrompts = path5.join(
13279
+ targetRoot,
13280
+ ".pourkit",
13281
+ "managed",
13282
+ "prompts"
13283
+ );
13284
+ if (existsSync18(srcPrompts) && !existsSync18(tgtManagedPrompts)) {
12855
13285
  const promptFiles = await walkDir(srcPrompts);
12856
13286
  for (const file of promptFiles) {
12857
13287
  const relPath = path5.relative(srcPrompts, file);
@@ -12859,7 +13289,7 @@ async function planInit(options) {
12859
13289
  operations.push({
12860
13290
  kind: "copy",
12861
13291
  sourcePath: file,
12862
- path: path5.join(tgtPrompts, relPath),
13292
+ path: path5.join(tgtManagedPrompts, relPath),
12863
13293
  ownership: "managed",
12864
13294
  reason: `Copy prompt: ${relPath}`,
12865
13295
  requiresConfirmation: false,
@@ -12911,7 +13341,8 @@ async function planInit(options) {
12911
13341
  baseBranch,
12912
13342
  verificationCommands: verifyCommands,
12913
13343
  hasPackageJson,
12914
- labels: options.labels
13344
+ labels: options.labels,
13345
+ releaseWorkflow: options.releaseWorkflow
12915
13346
  });
12916
13347
  operations.push({
12917
13348
  kind: "create",
@@ -13156,7 +13587,8 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
13156
13587
  sourceRoot: sourceRoot ?? "",
13157
13588
  operations,
13158
13589
  warnings,
13159
- hasLabelConflicts
13590
+ hasLabelConflicts,
13591
+ enabledAddons: options.releaseWorkflow?.enabled ? ["release"] : []
13160
13592
  };
13161
13593
  }
13162
13594
  var GROUP_LABELS = {
@@ -13228,6 +13660,7 @@ async function promptForInitChoices(plan, current) {
13228
13660
  let packageManager = current.packageManager;
13229
13661
  let legacySkills = current.legacySkills ?? false;
13230
13662
  let noGitCheck = current.noGitCheck ?? false;
13663
+ let releaseWorkflow;
13231
13664
  const labels = current.labels ? { ...current.labels } : { ...DEFAULT_RUNNER_LABELS };
13232
13665
  if (!packageManager && plan.warnings.some((w) => w.toLowerCase().includes("lockfile"))) {
13233
13666
  const result = await select({
@@ -13247,6 +13680,7 @@ async function promptForInitChoices(plan, current) {
13247
13680
  legacySkills,
13248
13681
  noGitCheck,
13249
13682
  labels,
13683
+ releaseWorkflow,
13250
13684
  cancelled: true
13251
13685
  };
13252
13686
  }
@@ -13270,6 +13704,7 @@ async function promptForInitChoices(plan, current) {
13270
13704
  legacySkills,
13271
13705
  noGitCheck,
13272
13706
  labels,
13707
+ releaseWorkflow,
13273
13708
  cancelled: true
13274
13709
  };
13275
13710
  }
@@ -13295,6 +13730,7 @@ async function promptForInitChoices(plan, current) {
13295
13730
  legacySkills,
13296
13731
  noGitCheck,
13297
13732
  labels,
13733
+ releaseWorkflow,
13298
13734
  cancelled: true
13299
13735
  };
13300
13736
  }
@@ -13413,11 +13849,84 @@ async function promptForInitChoices(plan, current) {
13413
13849
  legacySkills,
13414
13850
  noGitCheck,
13415
13851
  labels,
13852
+ releaseWorkflow,
13416
13853
  cancelled: true
13417
13854
  };
13418
13855
  }
13419
13856
  noGitCheck = true;
13420
13857
  }
13858
+ const releaseEnabledResult = await confirm({
13859
+ message: "Enable the Release Workflow Add-On for Changesets and Promotion PR orchestration?",
13860
+ initialValue: false
13861
+ });
13862
+ if (isCancel(releaseEnabledResult)) {
13863
+ return {
13864
+ docsMigration: docsMigration ?? "copy",
13865
+ agentFile: agentFile ?? "both",
13866
+ packageManager,
13867
+ legacySkills,
13868
+ noGitCheck,
13869
+ labels,
13870
+ releaseWorkflow: void 0,
13871
+ cancelled: true
13872
+ };
13873
+ }
13874
+ if (releaseEnabledResult) {
13875
+ const integrationBranchResult = await text({
13876
+ message: "Integration Branch name (e.g. dev)?",
13877
+ placeholder: "integration"
13878
+ });
13879
+ if (isCancel(integrationBranchResult)) {
13880
+ return {
13881
+ docsMigration: docsMigration ?? "copy",
13882
+ agentFile: agentFile ?? "both",
13883
+ packageManager,
13884
+ legacySkills,
13885
+ noGitCheck,
13886
+ labels,
13887
+ releaseWorkflow: void 0,
13888
+ cancelled: true
13889
+ };
13890
+ }
13891
+ const devReleaseBranchResult = await text({
13892
+ message: "Development Release Branch name (e.g. next)?",
13893
+ placeholder: "preview"
13894
+ });
13895
+ if (isCancel(devReleaseBranchResult)) {
13896
+ return {
13897
+ docsMigration: docsMigration ?? "copy",
13898
+ agentFile: agentFile ?? "both",
13899
+ packageManager,
13900
+ legacySkills,
13901
+ noGitCheck,
13902
+ labels,
13903
+ releaseWorkflow: void 0,
13904
+ cancelled: true
13905
+ };
13906
+ }
13907
+ const stableReleaseBranchResult = await text({
13908
+ message: "Stable Release Branch name (e.g. main)?",
13909
+ placeholder: "stable"
13910
+ });
13911
+ if (isCancel(stableReleaseBranchResult)) {
13912
+ return {
13913
+ docsMigration: docsMigration ?? "copy",
13914
+ agentFile: agentFile ?? "both",
13915
+ packageManager,
13916
+ legacySkills,
13917
+ noGitCheck,
13918
+ labels,
13919
+ releaseWorkflow: void 0,
13920
+ cancelled: true
13921
+ };
13922
+ }
13923
+ releaseWorkflow = {
13924
+ enabled: true,
13925
+ integrationBranch: typeof integrationBranchResult === "string" && integrationBranchResult.trim().length > 0 ? integrationBranchResult.trim() : "integration",
13926
+ developmentReleaseBranch: typeof devReleaseBranchResult === "string" && devReleaseBranchResult.trim().length > 0 ? devReleaseBranchResult.trim() : "preview",
13927
+ stableReleaseBranch: typeof stableReleaseBranchResult === "string" && stableReleaseBranchResult.trim().length > 0 ? stableReleaseBranchResult.trim() : "stable"
13928
+ };
13929
+ }
13421
13930
  return {
13422
13931
  docsMigration: docsMigration ?? "copy",
13423
13932
  agentFile: agentFile ?? "both",
@@ -13425,6 +13934,7 @@ async function promptForInitChoices(plan, current) {
13425
13934
  legacySkills,
13426
13935
  noGitCheck,
13427
13936
  labels,
13937
+ releaseWorkflow,
13428
13938
  cancelled: false
13429
13939
  };
13430
13940
  }
@@ -13490,6 +14000,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
13490
14000
  },
13491
14001
  agentFiles,
13492
14002
  packageManager,
14003
+ workflowPack: buildWorkflowPackMetadata(plan.enabledAddons ?? []),
13493
14004
  assets
13494
14005
  };
13495
14006
  await mkdir5(manifestDir, { recursive: true });
@@ -13766,7 +14277,8 @@ async function runInitCommand(options) {
13766
14277
  agentFile: options.agentFile ?? promptResult.agentFile,
13767
14278
  packageManager: options.packageManager ?? promptResult.packageManager,
13768
14279
  legacySkills: options.legacySkills ?? promptResult.legacySkills,
13769
- noGitCheck: options.noGitCheck ?? promptResult.noGitCheck
14280
+ noGitCheck: options.noGitCheck ?? promptResult.noGitCheck,
14281
+ releaseWorkflow: options.releaseWorkflow ?? promptResult.releaseWorkflow
13770
14282
  };
13771
14283
  promptLabels = promptResult.labels;
13772
14284
  }
@@ -13784,7 +14296,8 @@ async function runInitCommand(options) {
13784
14296
  packageManager: options.packageManager,
13785
14297
  noGitCheck: options.noGitCheck,
13786
14298
  skipInstall: options.skipInstall,
13787
- labels: planLabels
14299
+ labels: planLabels,
14300
+ releaseWorkflow: options.releaseWorkflow
13788
14301
  });
13789
14302
  let planLabelConflictPolicy = "skip-metadata-changes";
13790
14303
  if (isInteractive && plan.hasLabelConflicts) {
@@ -13990,8 +14503,8 @@ async function runSerenaStatusCommand(options) {
13990
14503
 
13991
14504
  // commands/config-schema.ts
13992
14505
  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";
14506
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
14507
+ import { resolve as resolve4, dirname as dirname5, relative as relative2 } from "path";
13995
14508
  import { fileURLToPath as fileURLToPath2 } from "url";
13996
14509
  import Ajv2 from "ajv";
13997
14510
  var __filename2 = fileURLToPath2(import.meta.url);
@@ -14028,33 +14541,569 @@ function readPackagedHash() {
14028
14541
  function localSchemaDir(repoRoot2) {
14029
14542
  return resolve4(repoRoot2, ".pourkit/schema");
14030
14543
  }
14031
- async function runDoctorCommand(options) {
14032
- const repoRootPath = options.cwd ?? process.cwd();
14033
- const schemaDir = localSchemaDir(repoRootPath);
14034
- const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
14035
- const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
14036
- const localSchemaExists = existsSync19(localSchemaPath);
14037
- const localHashExists = existsSync19(localHashPath);
14038
- let packagedHash = null;
14544
+ var MANAGED_BLOCK_BEGIN2 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
14545
+ var OLD_DOCS_PATH_PATTERN = ".pourkit/docs/agents/";
14546
+ function resolvePackagedManagedPath(...segments) {
14547
+ return resolve4(__dirname2, "..", "managed", ...segments);
14548
+ }
14549
+ function resolvePackagedManagedDocPath(docName) {
14550
+ return resolvePackagedManagedPath("docs", "agents", docName);
14551
+ }
14552
+ function resolvePackagedReleaseAddonDocPath(docName) {
14553
+ return resolvePackagedManagedPath(
14554
+ "addons",
14555
+ "release",
14556
+ "docs",
14557
+ "agents",
14558
+ docName
14559
+ );
14560
+ }
14561
+ async function readPackagedTextFile(filePath) {
14039
14562
  try {
14040
- packagedHash = readPackagedHash();
14563
+ return await readFile6(filePath, "utf-8");
14041
14564
  } catch {
14565
+ return null;
14042
14566
  }
14043
- let localHashContent = null;
14044
- if (localHashExists) {
14045
- try {
14046
- localHashContent = await readFile6(localHashPath, "utf-8");
14047
- } catch {
14567
+ }
14568
+ function isPathContained(parent, child) {
14569
+ const relativePath = relative2(parent, child);
14570
+ return !relativePath.startsWith("..") && !relativePath.startsWith("../");
14571
+ }
14572
+ async function walkDir2(dir) {
14573
+ const files = [];
14574
+ try {
14575
+ const entries = await readdir2(dir, { withFileTypes: true });
14576
+ for (const entry of entries) {
14577
+ const full = resolve4(dir, entry.name);
14578
+ if (entry.isDirectory()) {
14579
+ files.push(...await walkDir2(full));
14580
+ } else {
14581
+ files.push(full);
14582
+ }
14048
14583
  }
14584
+ } catch {
14585
+ return [];
14049
14586
  }
14050
- const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
14051
- const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
14052
- const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
14053
- const configPath = resolve4(repoRootPath, ".pourkit/config.json");
14054
- let configValidation;
14055
- if (existsSync19(configPath)) {
14056
- try {
14057
- const raw = JSON.parse(readFileSync20(configPath, "utf-8"));
14587
+ return files;
14588
+ }
14589
+ function removeCodeFences(content) {
14590
+ return content.replace(/```[\s\S]*?```/g, "");
14591
+ }
14592
+ function isInsideBacktick(content, index) {
14593
+ const before = content.slice(0, index);
14594
+ const backtickCount = (before.match(/`/g) || []).length;
14595
+ return backtickCount % 2 === 1;
14596
+ }
14597
+ var BRANCH_NAME_PATTERN = /\b(dev|next|main)\b/g;
14598
+ var OBSOLETE_PRD_PATTERN = /PRD-\d{3}(?![\dNn])/g;
14599
+ var OVERWIDE_PRD_PATTERN = /PRD-\d{5,}/g;
14600
+ function isExampleLine(content, matchIndex) {
14601
+ return /^(?:[-*]\s+)?Example:/i.test(getLine(content, matchIndex).trim());
14602
+ }
14603
+ function getLine(content, matchIndex) {
14604
+ const lineStart = content.lastIndexOf("\n", matchIndex - 1) + 1;
14605
+ const lineEnd = content.indexOf("\n", matchIndex);
14606
+ return content.slice(lineStart, lineEnd === -1 ? content.length : lineEnd);
14607
+ }
14608
+ function isBranchPolicyLine(content, matchIndex) {
14609
+ const line = getLine(content, matchIndex).toLowerCase();
14610
+ return /->|\b(branch|branches|lane|release|promotion|promote|target|base|merge|push|hotfix|changeset|origin)\b/.test(
14611
+ line
14612
+ );
14613
+ }
14614
+ function scanManagedPolicyText(content, filePath, allowlist) {
14615
+ const violations = [];
14616
+ const noFences = removeCodeFences(content);
14617
+ let match;
14618
+ BRANCH_NAME_PATTERN.lastIndex = 0;
14619
+ while ((match = BRANCH_NAME_PATTERN.exec(noFences)) !== null) {
14620
+ if (isExampleLine(noFences, match.index)) continue;
14621
+ if (!isBranchPolicyLine(noFences, match.index)) continue;
14622
+ if (allowlist?.includes(match[0])) continue;
14623
+ violations.push({
14624
+ path: filePath,
14625
+ phrase: match[0],
14626
+ reason: "hardcoded_branch_policy"
14627
+ });
14628
+ }
14629
+ OBSOLETE_PRD_PATTERN.lastIndex = 0;
14630
+ while ((match = OBSOLETE_PRD_PATTERN.exec(noFences)) !== null) {
14631
+ if (allowlist?.includes(match[0])) continue;
14632
+ if (!isInsideBacktick(noFences, match.index)) {
14633
+ violations.push({
14634
+ path: filePath,
14635
+ phrase: match[0],
14636
+ reason: "obsolete_prd_placeholder"
14637
+ });
14638
+ }
14639
+ }
14640
+ OVERWIDE_PRD_PATTERN.lastIndex = 0;
14641
+ while ((match = OVERWIDE_PRD_PATTERN.exec(noFences)) !== null) {
14642
+ if (allowlist?.includes(match[0])) continue;
14643
+ if (!isInsideBacktick(noFences, match.index)) {
14644
+ violations.push({
14645
+ path: filePath,
14646
+ phrase: match[0],
14647
+ reason: "obsolete_prd_placeholder"
14648
+ });
14649
+ }
14650
+ }
14651
+ return violations;
14652
+ }
14653
+ async function validateWorkflowPack(cwd) {
14654
+ const failures = [];
14655
+ const warnings = [];
14656
+ const catalog = getWorkflowPackCatalog();
14657
+ const baselineSkills = getBaselineManagedSkillNames();
14658
+ for (const docName of catalog.docs) {
14659
+ const localPath = resolve4(cwd, `.pourkit/managed/docs/agents/${docName}`);
14660
+ if (!existsSync19(localPath)) {
14661
+ failures.push({
14662
+ severity: "failure",
14663
+ kind: "missing_managed_asset",
14664
+ path: `.pourkit/managed/docs/agents/${docName}`,
14665
+ message: `Missing managed doc: ${docName}`
14666
+ });
14667
+ } else {
14668
+ const packagedPath = resolvePackagedManagedDocPath(docName);
14669
+ const packagedContent = await readPackagedTextFile(packagedPath);
14670
+ if (packagedContent !== null) {
14671
+ try {
14672
+ const localContent = await readFile6(localPath, "utf-8");
14673
+ if (localContent !== packagedContent) {
14674
+ const overridePath = resolve4(
14675
+ cwd,
14676
+ `.pourkit/overrides/docs/agents/${docName}`
14677
+ );
14678
+ if (existsSync19(overridePath)) {
14679
+ warnings.push({
14680
+ severity: "warning",
14681
+ kind: "overridden",
14682
+ path: `.pourkit/overrides/docs/agents/${docName}`,
14683
+ message: `Managed doc ${docName} is overridden by explicit override.`
14684
+ });
14685
+ } else {
14686
+ failures.push({
14687
+ severity: "failure",
14688
+ kind: "managed_asset_drift",
14689
+ path: `.pourkit/managed/docs/agents/${docName}`,
14690
+ message: `Managed doc ${docName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
14691
+ });
14692
+ }
14693
+ }
14694
+ } catch {
14695
+ }
14696
+ }
14697
+ }
14698
+ }
14699
+ for (const skillName of baselineSkills) {
14700
+ const skillDir = resolve4(cwd, `.pourkit/managed/skills/${skillName}`);
14701
+ if (!existsSync19(skillDir)) {
14702
+ failures.push({
14703
+ severity: "failure",
14704
+ kind: "missing_managed_asset",
14705
+ path: `.pourkit/managed/skills/${skillName}`,
14706
+ message: `Missing managed skill: ${skillName}`
14707
+ });
14708
+ } else {
14709
+ const skillSourcePath = resolve4(skillDir, "SKILL.md");
14710
+ if (existsSync19(skillSourcePath)) {
14711
+ const packagedSkillPath = resolvePackagedManagedPath(
14712
+ "skills",
14713
+ skillName,
14714
+ "SKILL.md"
14715
+ );
14716
+ const packagedContent = await readPackagedTextFile(packagedSkillPath);
14717
+ if (packagedContent !== null) {
14718
+ try {
14719
+ const localContent = await readFile6(skillSourcePath, "utf-8");
14720
+ if (localContent !== packagedContent) {
14721
+ const overridePath = resolve4(
14722
+ cwd,
14723
+ `.pourkit/overrides/skills/${skillName}/SKILL.md`
14724
+ );
14725
+ if (existsSync19(overridePath)) {
14726
+ warnings.push({
14727
+ severity: "warning",
14728
+ kind: "overridden",
14729
+ path: `.pourkit/overrides/skills/${skillName}/SKILL.md`,
14730
+ message: `Managed skill ${skillName} is overridden by explicit override.`
14731
+ });
14732
+ } else {
14733
+ failures.push({
14734
+ severity: "failure",
14735
+ kind: "managed_asset_drift",
14736
+ path: `.pourkit/managed/skills/${skillName}/SKILL.md`,
14737
+ message: `Managed skill ${skillName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
14738
+ });
14739
+ }
14740
+ }
14741
+ } catch {
14742
+ }
14743
+ }
14744
+ }
14745
+ }
14746
+ }
14747
+ for (const promptName of catalog.prompts) {
14748
+ const promptPath = resolve4(cwd, `.pourkit/managed/prompts/${promptName}`);
14749
+ if (!existsSync19(promptPath)) {
14750
+ failures.push({
14751
+ severity: "failure",
14752
+ kind: "missing_managed_asset",
14753
+ path: `.pourkit/managed/prompts/${promptName}`,
14754
+ message: `Missing managed prompt: ${promptName}`
14755
+ });
14756
+ }
14757
+ }
14758
+ for (const skillName of baselineSkills) {
14759
+ const projectionDir = resolve4(cwd, `.agents/skills/${skillName}`);
14760
+ if (!existsSync19(projectionDir)) {
14761
+ warnings.push({
14762
+ severity: "warning",
14763
+ kind: "projection_stale",
14764
+ path: `.agents/skills/${skillName}`,
14765
+ message: `Missing skill projection for ${skillName}. Run 'pourkit sync workflow-pack' to regenerate.`
14766
+ });
14767
+ } else {
14768
+ const projectionPath = resolve4(projectionDir, "SKILL.md");
14769
+ if (existsSync19(projectionPath)) {
14770
+ const managedSourcePath = resolvePackagedManagedPath(
14771
+ "skills",
14772
+ skillName,
14773
+ "SKILL.md"
14774
+ );
14775
+ const packagedContent = await readPackagedTextFile(managedSourcePath);
14776
+ if (packagedContent !== null) {
14777
+ try {
14778
+ const projectionContent = await readFile6(projectionPath, "utf-8");
14779
+ const managedSourceRelativePath = `.pourkit/managed/skills/${skillName}/SKILL.md`;
14780
+ if (projectionContent !== buildOpenCodeSkillProjectionContent(
14781
+ managedSourceRelativePath,
14782
+ packagedContent
14783
+ )) {
14784
+ warnings.push({
14785
+ severity: "warning",
14786
+ kind: "projection_stale",
14787
+ path: `.agents/skills/${skillName}/SKILL.md`,
14788
+ message: `Projection for ${skillName} differs from managed source. Run 'pourkit sync workflow-pack' to regenerate.`
14789
+ });
14790
+ }
14791
+ } catch {
14792
+ }
14793
+ }
14794
+ }
14795
+ }
14796
+ }
14797
+ const agentsPath = resolve4(cwd, "AGENTS.md");
14798
+ if (existsSync19(agentsPath)) {
14799
+ try {
14800
+ const agentsContent = await readFile6(agentsPath, "utf-8");
14801
+ if (agentsContent.includes(OLD_DOCS_PATH_PATTERN)) {
14802
+ failures.push({
14803
+ severity: "failure",
14804
+ kind: "router_drift",
14805
+ path: "AGENTS.md",
14806
+ message: "AGENTS.md references old .pourkit/docs/agents/ paths. Update to .pourkit/managed/docs/agents/."
14807
+ });
14808
+ }
14809
+ if (!agentsContent.includes(MANAGED_BLOCK_BEGIN2.trim())) {
14810
+ failures.push({
14811
+ severity: "failure",
14812
+ kind: "router_drift",
14813
+ path: "AGENTS.md",
14814
+ message: "AGENTS.md is missing the Pourkit managed block. Run 'pourkit sync workflow-pack' to add it."
14815
+ });
14816
+ }
14817
+ } catch {
14818
+ }
14819
+ }
14820
+ const overridesDir = resolve4(cwd, ".pourkit/overrides");
14821
+ if (existsSync19(overridesDir)) {
14822
+ const overrideFiles = await walkDir2(overridesDir);
14823
+ for (const file of overrideFiles) {
14824
+ const relPath = relative2(cwd, file);
14825
+ if (!warnings.some((w) => w.kind === "overridden" && w.path === relPath)) {
14826
+ warnings.push({
14827
+ severity: "warning",
14828
+ kind: "overridden",
14829
+ path: relPath,
14830
+ message: `Explicit override present: ${relPath}`
14831
+ });
14832
+ }
14833
+ }
14834
+ }
14835
+ const configPath = resolve4(cwd, ".pourkit/config.json");
14836
+ if (existsSync19(configPath)) {
14837
+ try {
14838
+ const configContent = await readFile6(configPath, "utf-8");
14839
+ const parsed = JSON.parse(configContent);
14840
+ const releaseEnabled = parsed.releaseWorkflow?.enabled === true;
14841
+ if (releaseEnabled) {
14842
+ for (const skillName of catalog.releaseAddonSkills) {
14843
+ const addonDir = resolve4(
14844
+ cwd,
14845
+ `.pourkit/managed/addons/release/skills/${skillName}`
14846
+ );
14847
+ if (!existsSync19(addonDir)) {
14848
+ failures.push({
14849
+ severity: "failure",
14850
+ kind: "release_config_conflict",
14851
+ path: `.pourkit/managed/addons/release/skills/${skillName}`,
14852
+ message: `Release workflow is enabled but release addon skill ${skillName} is not installed. Run 'pourkit sync workflow-pack' to install.`
14853
+ });
14854
+ }
14855
+ }
14856
+ for (const docName of catalog.releaseAddonDocs) {
14857
+ const addonDocPath = resolve4(
14858
+ cwd,
14859
+ `.pourkit/managed/addons/release/docs/agents/${docName}`
14860
+ );
14861
+ if (!existsSync19(addonDocPath)) {
14862
+ failures.push({
14863
+ severity: "failure",
14864
+ kind: "release_config_conflict",
14865
+ path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
14866
+ message: `Release workflow is enabled but release addon doc ${docName} is not installed. Run 'pourkit sync workflow-pack' to install.`
14867
+ });
14868
+ } else {
14869
+ const packagedContent = await readPackagedTextFile(
14870
+ resolvePackagedReleaseAddonDocPath(docName)
14871
+ );
14872
+ if (packagedContent !== null) {
14873
+ const localContent = await readFile6(addonDocPath, "utf-8");
14874
+ if (localContent !== packagedContent) {
14875
+ failures.push({
14876
+ severity: "failure",
14877
+ kind: "managed_asset_drift",
14878
+ path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
14879
+ message: `Release addon doc ${docName} content differs from packaged source. Run 'pourkit sync workflow-pack' to repair.`
14880
+ });
14881
+ }
14882
+ }
14883
+ }
14884
+ }
14885
+ const rw = parsed.releaseWorkflow;
14886
+ const requiredFields = [
14887
+ ["integrationBranch", rw?.integrationBranch],
14888
+ ["developmentReleaseBranch", rw?.developmentReleaseBranch],
14889
+ ["stableReleaseBranch", rw?.stableReleaseBranch]
14890
+ ];
14891
+ for (const [fieldName, value] of requiredFields) {
14892
+ if (!value || typeof value === "string" && value.trim() === "") {
14893
+ failures.push({
14894
+ severity: "failure",
14895
+ kind: "release_config_conflict",
14896
+ message: `Release workflow is enabled but required field releaseWorkflow.${fieldName} is missing or empty.`
14897
+ });
14898
+ }
14899
+ }
14900
+ } else {
14901
+ for (const skillName of catalog.releaseAddonSkills) {
14902
+ const addonDir = resolve4(
14903
+ cwd,
14904
+ `.pourkit/managed/addons/release/skills/${skillName}`
14905
+ );
14906
+ if (existsSync19(addonDir)) {
14907
+ failures.push({
14908
+ severity: "failure",
14909
+ kind: "release_config_conflict",
14910
+ path: `.pourkit/managed/addons/release/skills/${skillName}`,
14911
+ message: `Release workflow is disabled but release addon skill ${skillName} is present as active baseline policy. Disable or remove the addon skill.`
14912
+ });
14913
+ }
14914
+ }
14915
+ for (const docName of catalog.releaseAddonDocs) {
14916
+ const addonDocPath = resolve4(
14917
+ cwd,
14918
+ `.pourkit/managed/addons/release/docs/agents/${docName}`
14919
+ );
14920
+ if (existsSync19(addonDocPath)) {
14921
+ failures.push({
14922
+ severity: "failure",
14923
+ kind: "release_config_conflict",
14924
+ path: `.pourkit/managed/addons/release/docs/agents/${docName}`,
14925
+ message: `Release workflow is disabled but release addon doc ${docName} is present as active baseline policy. Disable or remove the addon doc.`
14926
+ });
14927
+ }
14928
+ }
14929
+ }
14930
+ } catch {
14931
+ }
14932
+ }
14933
+ const manifestPath = resolve4(cwd, ".pourkit/manifest.json");
14934
+ if (existsSync19(manifestPath)) {
14935
+ try {
14936
+ const manifestContent = await readFile6(manifestPath, "utf-8");
14937
+ const manifest = JSON.parse(manifestContent);
14938
+ const wp = manifest.workflowPack;
14939
+ if (wp?.projections) {
14940
+ for (const proj of wp.projections) {
14941
+ if (proj.path) {
14942
+ const resolved = resolve4(cwd, proj.path);
14943
+ if (!isPathContained(cwd, resolved)) {
14944
+ failures.push({
14945
+ severity: "failure",
14946
+ kind: "path_escape",
14947
+ message: `Manifest projection path escapes repository: ${proj.path}`
14948
+ });
14949
+ }
14950
+ }
14951
+ }
14952
+ }
14953
+ } catch {
14954
+ }
14955
+ }
14956
+ const pathEscapeOverrideDir = resolve4(cwd, ".pourkit/overrides");
14957
+ if (existsSync19(pathEscapeOverrideDir)) {
14958
+ const overrideFiles = await walkDir2(pathEscapeOverrideDir);
14959
+ for (const file of overrideFiles) {
14960
+ if (!isPathContained(cwd, file)) {
14961
+ failures.push({
14962
+ severity: "failure",
14963
+ kind: "path_escape",
14964
+ path: relative2(cwd, file),
14965
+ message: `Override file path escapes repository: ${relative2(cwd, file)}`
14966
+ });
14967
+ }
14968
+ }
14969
+ }
14970
+ const managedDocsDir = resolve4(cwd, ".pourkit/managed/docs/agents");
14971
+ if (existsSync19(managedDocsDir)) {
14972
+ const docFiles = await walkDir2(managedDocsDir);
14973
+ for (const file of docFiles) {
14974
+ if (!file.endsWith(".md")) continue;
14975
+ try {
14976
+ const content = await readFile6(file, "utf-8");
14977
+ const relPath = relative2(cwd, file);
14978
+ const violations = scanManagedPolicyText(content, relPath);
14979
+ for (const v of violations) {
14980
+ failures.push({
14981
+ severity: "failure",
14982
+ kind: "terminology_policy_violation",
14983
+ path: v.path,
14984
+ message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
14985
+ });
14986
+ }
14987
+ } catch {
14988
+ }
14989
+ }
14990
+ }
14991
+ const managedSkillsDir = resolve4(cwd, ".pourkit/managed/skills");
14992
+ if (existsSync19(managedSkillsDir)) {
14993
+ const skillDirs = await readdir2(managedSkillsDir, { withFileTypes: true });
14994
+ for (const entry of skillDirs) {
14995
+ if (!entry.isDirectory()) continue;
14996
+ const skillFile = resolve4(managedSkillsDir, entry.name, "SKILL.md");
14997
+ if (!existsSync19(skillFile)) continue;
14998
+ try {
14999
+ const content = await readFile6(skillFile, "utf-8");
15000
+ const relPath = relative2(cwd, skillFile);
15001
+ const violations = scanManagedPolicyText(content, relPath);
15002
+ for (const v of violations) {
15003
+ failures.push({
15004
+ severity: "failure",
15005
+ kind: "terminology_policy_violation",
15006
+ path: v.path,
15007
+ message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
15008
+ });
15009
+ }
15010
+ } catch {
15011
+ }
15012
+ }
15013
+ }
15014
+ const managedPromptsDir = resolve4(cwd, ".pourkit/managed/prompts");
15015
+ if (existsSync19(managedPromptsDir)) {
15016
+ for (const promptName of catalog.prompts) {
15017
+ const promptFile = resolve4(managedPromptsDir, promptName);
15018
+ if (!existsSync19(promptFile)) continue;
15019
+ try {
15020
+ const content = await readFile6(promptFile, "utf-8");
15021
+ const relPath = relative2(cwd, promptFile);
15022
+ const violations = scanManagedPolicyText(content, relPath);
15023
+ for (const v of violations) {
15024
+ failures.push({
15025
+ severity: "failure",
15026
+ kind: "terminology_policy_violation",
15027
+ path: v.path,
15028
+ message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
15029
+ });
15030
+ }
15031
+ } catch {
15032
+ }
15033
+ }
15034
+ }
15035
+ for (const skillName of catalog.releaseAddonSkills) {
15036
+ const addonSkillFile = resolve4(
15037
+ cwd,
15038
+ `.pourkit/managed/addons/release/skills/${skillName}/SKILL.md`
15039
+ );
15040
+ if (!existsSync19(addonSkillFile)) continue;
15041
+ try {
15042
+ const content = await readFile6(addonSkillFile, "utf-8");
15043
+ const relPath = relative2(cwd, addonSkillFile);
15044
+ const violations = scanManagedPolicyText(content, relPath);
15045
+ for (const v of violations) {
15046
+ failures.push({
15047
+ severity: "failure",
15048
+ kind: "terminology_policy_violation",
15049
+ path: v.path,
15050
+ message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
15051
+ });
15052
+ }
15053
+ } catch {
15054
+ }
15055
+ }
15056
+ for (const docName of catalog.releaseAddonDocs) {
15057
+ const addonDocFile = resolve4(
15058
+ cwd,
15059
+ `.pourkit/managed/addons/release/docs/agents/${docName}`
15060
+ );
15061
+ if (!existsSync19(addonDocFile)) continue;
15062
+ try {
15063
+ const content = await readFile6(addonDocFile, "utf-8");
15064
+ const relPath = relative2(cwd, addonDocFile);
15065
+ const violations = scanManagedPolicyText(content, relPath);
15066
+ for (const v of violations) {
15067
+ failures.push({
15068
+ severity: "failure",
15069
+ kind: "terminology_policy_violation",
15070
+ path: v.path,
15071
+ message: `Policy violation: "${v.phrase}" is a ${v.reason === "hardcoded_branch_policy" ? "hardcoded branch name in policy context" : "obsolete or over-wide PRD placeholder"}.`
15072
+ });
15073
+ }
15074
+ } catch {
15075
+ }
15076
+ }
15077
+ const status = failures.length > 0 ? "failed" : warnings.length > 0 ? "warning" : "fresh";
15078
+ return { status, failures, warnings };
15079
+ }
15080
+ async function runDoctorCommand(options) {
15081
+ const repoRootPath = options.cwd ?? process.cwd();
15082
+ const schemaDir = localSchemaDir(repoRootPath);
15083
+ const localSchemaPath = resolve4(schemaDir, "pourkit.schema.json");
15084
+ const localHashPath = resolve4(schemaDir, "pourkit.schema.hash");
15085
+ const localSchemaExists = existsSync19(localSchemaPath);
15086
+ const localHashExists = existsSync19(localHashPath);
15087
+ let packagedHash = null;
15088
+ try {
15089
+ packagedHash = readPackagedHash();
15090
+ } catch {
15091
+ }
15092
+ let localHashContent = null;
15093
+ if (localHashExists) {
15094
+ try {
15095
+ localHashContent = await readFile6(localHashPath, "utf-8");
15096
+ } catch {
15097
+ }
15098
+ }
15099
+ const schema = !localSchemaExists ? "missing" : !packagedHash || !localHashContent ? "missing" : localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
15100
+ const hash = !localHashExists ? "missing" : !packagedHash ? "missing" : localHashContent && localHashContent.trim() === packagedHash.trim() ? "fresh" : "stale";
15101
+ const overall = schema === "fresh" && hash === "fresh" ? "fresh" : schema === "missing" || hash === "missing" ? "missing" : "stale";
15102
+ const configPath = resolve4(repoRootPath, ".pourkit/config.json");
15103
+ let configValidation;
15104
+ if (existsSync19(configPath)) {
15105
+ try {
15106
+ const raw = JSON.parse(readFileSync20(configPath, "utf-8"));
14058
15107
  const validate = getSchemaValidator();
14059
15108
  const valid2 = validate(raw);
14060
15109
  if (valid2) {
@@ -14087,30 +15136,37 @@ async function runDoctorCommand(options) {
14087
15136
  "pourkit.config.js",
14088
15137
  "pourkit.json"
14089
15138
  ].filter((p) => existsSync19(resolve4(repoRootPath, p)));
15139
+ const workflowPack = await validateWorkflowPack(repoRootPath);
14090
15140
  let recommendation = null;
14091
- if (!configValidation.ok || overall !== "fresh" || obsoleteConfigs.length > 0) {
14092
- const parts = [];
14093
- if (obsoleteConfigs.length > 0) {
14094
- parts.push(
14095
- `Found obsolete config files: ${obsoleteConfigs.join(", ")}. Move configuration to .pourkit/config.json with "$schema": "./schema/pourkit.schema.json".`
14096
- );
14097
- }
14098
- if (!configValidation.ok) {
14099
- parts.push(
14100
- "Config validation failed; fix errors in .pourkit/config.json."
14101
- );
14102
- }
14103
- if (overall !== "fresh") {
14104
- parts.push(
14105
- 'Run "pourkit config sync-schema" to update local schema assets.'
14106
- );
14107
- }
14108
- recommendation = parts.join(" ");
15141
+ const recParts = [];
15142
+ if (obsoleteConfigs.length > 0) {
15143
+ recParts.push(
15144
+ `Found obsolete config files: ${obsoleteConfigs.join(", ")}. Move configuration to .pourkit/config.json with "$schema": "./schema/pourkit.schema.json".`
15145
+ );
15146
+ }
15147
+ if (!configValidation.ok) {
15148
+ recParts.push(
15149
+ "Config validation failed; fix errors in .pourkit/config.json."
15150
+ );
15151
+ }
15152
+ if (overall !== "fresh") {
15153
+ recParts.push(
15154
+ 'Run "pourkit config sync-schema" to update local schema assets.'
15155
+ );
15156
+ }
15157
+ if (workflowPack.status === "failed" || workflowPack.status === "warning") {
15158
+ recParts.push(
15159
+ 'Run "pourkit sync workflow-pack" to repair workflow pack issues.'
15160
+ );
15161
+ }
15162
+ if (recParts.length > 0) {
15163
+ recommendation = recParts.join(" ");
14109
15164
  }
14110
15165
  return {
14111
15166
  configValidation,
14112
15167
  obsoleteConfigs,
14113
15168
  schemaAssets: { schema, hash, overall },
15169
+ workflowPack,
14114
15170
  recommendation
14115
15171
  };
14116
15172
  }
@@ -14151,6 +15207,576 @@ async function runConfigSyncSchemaCommand(options) {
14151
15207
  };
14152
15208
  }
14153
15209
 
15210
+ // commands/workflow-pack-sync.ts
15211
+ import { existsSync as existsSync20, readdirSync as readdirSync5 } from "fs";
15212
+ import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile4, readdir as readdir3 } from "fs/promises";
15213
+ import { resolve as resolve5, dirname as dirname6, relative as relative3, normalize as normalize2 } from "path";
15214
+ import { fileURLToPath as fileURLToPath3 } from "url";
15215
+ var __filename3 = fileURLToPath3(import.meta.url);
15216
+ var __dirname3 = dirname6(__filename3);
15217
+ var PROJECT_OWNED_PATHS = [".pourkit/CONTEXT.md", ".pourkit/CONTEXT-MAP.md"];
15218
+ var PROJECT_OWNED_DIRECTORIES = [
15219
+ ".pourkit/docs/adr",
15220
+ ".pourkit/plans",
15221
+ ".pourkit/handoffs"
15222
+ ];
15223
+ var OVERRIDES_DIR = ".pourkit/overrides";
15224
+ var MANAGED_BLOCK_BEGIN3 = "<!-- BEGIN POURKIT MANAGED BLOCK -->\n";
15225
+ var MANAGED_BLOCK_END2 = "\n<!-- END POURKIT MANAGED BLOCK -->";
15226
+ function resolvePackagedManagedPath2(...segments) {
15227
+ const candidates = [resolve5(__dirname3, "..", "managed", ...segments)];
15228
+ return candidates[0];
15229
+ }
15230
+ function resolvePackagedManagedDocPath2(docName) {
15231
+ return resolvePackagedManagedPath2("docs", "agents", docName);
15232
+ }
15233
+ function resolvePackagedReleaseAddonDocPath2(docName) {
15234
+ return resolvePackagedManagedPath2(
15235
+ "addons",
15236
+ "release",
15237
+ "docs",
15238
+ "agents",
15239
+ docName
15240
+ );
15241
+ }
15242
+ function resolvePackagedManagedSkillDir(skillName) {
15243
+ return resolvePackagedManagedPath2("skills", skillName);
15244
+ }
15245
+ function resolvePackagedManagedPromptPath(promptName) {
15246
+ return resolvePackagedManagedPath2("prompts", promptName);
15247
+ }
15248
+ async function readPackagedTextFile2(filePath) {
15249
+ try {
15250
+ return await readFile7(filePath, "utf-8");
15251
+ } catch {
15252
+ return null;
15253
+ }
15254
+ }
15255
+ function isPathContained2(parent, child) {
15256
+ const relativePath = relative3(parent, child);
15257
+ return !relativePath.startsWith("..") && !relativePath.startsWith("../");
15258
+ }
15259
+ function isProjectOwnedPath(relativePath) {
15260
+ for (const owned of PROJECT_OWNED_PATHS) {
15261
+ if (relativePath === owned || relativePath.startsWith(owned + "/")) {
15262
+ return true;
15263
+ }
15264
+ }
15265
+ for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
15266
+ if (relativePath.startsWith(ownedDir + "/") || relativePath === ownedDir + "/") {
15267
+ return true;
15268
+ }
15269
+ }
15270
+ return false;
15271
+ }
15272
+ function getManagedRelativePath(overrideRelativePath) {
15273
+ if (!overrideRelativePath.startsWith(OVERRIDES_DIR + "/")) {
15274
+ return null;
15275
+ }
15276
+ const managedPath = overrideRelativePath.slice(OVERRIDES_DIR.length + 1);
15277
+ if (managedPath.startsWith("skills/") || managedPath.startsWith("docs/") || managedPath.startsWith("prompts/")) {
15278
+ return `.pourkit/managed/${managedPath}`;
15279
+ }
15280
+ return null;
15281
+ }
15282
+ function findOverrideForManagedPath(existingOverrides, targetRelativePath, overridePath) {
15283
+ if (existingOverrides.some(
15284
+ (o) => o === overridePath || o.startsWith(overridePath + "/")
15285
+ )) {
15286
+ return overridePath;
15287
+ }
15288
+ if (existingOverrides.some((o) => {
15289
+ const managedPath = getManagedRelativePath(o);
15290
+ return managedPath === targetRelativePath;
15291
+ })) {
15292
+ return overridePath;
15293
+ }
15294
+ return null;
15295
+ }
15296
+ function checkForPathEscape(cwd, resolvedPath) {
15297
+ const normalizedCwd = normalize2(cwd);
15298
+ const normalizedPath = normalize2(resolvedPath);
15299
+ if (!isPathContained2(normalizedCwd, normalizedPath)) {
15300
+ return `Path escape detected: ${resolvedPath} is outside ${cwd}`;
15301
+ }
15302
+ return null;
15303
+ }
15304
+ async function walkDir3(dir) {
15305
+ const files = [];
15306
+ try {
15307
+ const entries = await readdir3(dir, { withFileTypes: true });
15308
+ for (const entry of entries) {
15309
+ const full = resolve5(dir, entry.name);
15310
+ if (entry.isDirectory()) {
15311
+ files.push(...await walkDir3(full));
15312
+ } else {
15313
+ files.push(full);
15314
+ }
15315
+ }
15316
+ } catch {
15317
+ }
15318
+ return files;
15319
+ }
15320
+ function generateManagedBlockContent() {
15321
+ return [
15322
+ "This repository uses Pourkit.",
15323
+ "",
15324
+ "## Managed operating docs",
15325
+ "",
15326
+ "Follow the managed operating docs under `.pourkit/managed/docs/agents/*`:",
15327
+ "",
15328
+ "- `.pourkit/managed/docs/agents/issue-tracker.md`",
15329
+ "- `.pourkit/managed/docs/agents/triage-labels.md`",
15330
+ "- `.pourkit/managed/docs/agents/naming.md`",
15331
+ "- `.pourkit/managed/docs/agents/git-workflow.md`",
15332
+ "- `.pourkit/managed/docs/agents/commit-style.md`",
15333
+ "- `.pourkit/managed/docs/agents/domain.md`",
15334
+ "",
15335
+ "### Project-owned context",
15336
+ "",
15337
+ "- `.pourkit/CONTEXT.md`",
15338
+ "- `.pourkit/CONTEXT-MAP.md`",
15339
+ "- `.pourkit/docs/adr/*`",
15340
+ "- `.pourkit/plans/*`",
15341
+ "- `.pourkit/handoffs/*`",
15342
+ "",
15343
+ "### Important",
15344
+ "",
15345
+ "- Do not edit `.pourkit/managed/*` unless explicitly updating Pourkit-managed assets.",
15346
+ "- Do not edit generated harness projections directly.",
15347
+ ""
15348
+ ].join("\n");
15349
+ }
15350
+ async function syncFile(cwd, targetRelativePath, sourceContent, errors) {
15351
+ if (sourceContent === null) {
15352
+ return false;
15353
+ }
15354
+ const targetPath = resolve5(cwd, targetRelativePath);
15355
+ const escapeError = checkForPathEscape(cwd, targetPath);
15356
+ if (escapeError) {
15357
+ errors.push(escapeError);
15358
+ return false;
15359
+ }
15360
+ await mkdir7(resolve5(targetPath, ".."), { recursive: true });
15361
+ if (existsSync20(targetPath)) {
15362
+ const existing = await readFile7(targetPath, "utf-8");
15363
+ if (existing === sourceContent) {
15364
+ return false;
15365
+ }
15366
+ }
15367
+ await writeFile4(targetPath, sourceContent, "utf-8");
15368
+ return true;
15369
+ }
15370
+ function walkDirSync(dir) {
15371
+ const result = [];
15372
+ try {
15373
+ const entries = readdirSync5(dir, { withFileTypes: true });
15374
+ for (const entry of entries) {
15375
+ const full = resolve5(dir, entry.name);
15376
+ if (entry.isDirectory()) {
15377
+ result.push(...walkDirSync(full));
15378
+ } else {
15379
+ result.push(full);
15380
+ }
15381
+ }
15382
+ } catch {
15383
+ }
15384
+ return result;
15385
+ }
15386
+ async function detectOverrides(cwd) {
15387
+ const overridesDir = resolve5(cwd, OVERRIDES_DIR);
15388
+ if (!existsSync20(overridesDir)) {
15389
+ return [];
15390
+ }
15391
+ const files = await walkDir3(overridesDir);
15392
+ return files.map((f) => relative3(cwd, f));
15393
+ }
15394
+ async function detectProjectOwnedFiles(cwd) {
15395
+ const found = [];
15396
+ for (const ownedPath of PROJECT_OWNED_PATHS) {
15397
+ const fullPath = resolve5(cwd, ownedPath);
15398
+ if (existsSync20(fullPath)) {
15399
+ found.push(ownedPath);
15400
+ }
15401
+ }
15402
+ for (const ownedDir of PROJECT_OWNED_DIRECTORIES) {
15403
+ const fullDir = resolve5(cwd, ownedDir);
15404
+ if (existsSync20(fullDir)) {
15405
+ const files = await walkDir3(fullDir);
15406
+ for (const file of files) {
15407
+ found.push(relative3(cwd, file));
15408
+ }
15409
+ }
15410
+ }
15411
+ return found;
15412
+ }
15413
+ async function isReleaseWorkflowEnabled(cwd) {
15414
+ try {
15415
+ const configPath = resolve5(cwd, ".pourkit", "config.json");
15416
+ const content = await readFile7(configPath, "utf-8");
15417
+ const parsed = JSON.parse(content);
15418
+ return parsed.releaseWorkflow?.enabled === true;
15419
+ } catch {
15420
+ return false;
15421
+ }
15422
+ }
15423
+ async function runWorkflowPackSyncCommand(options) {
15424
+ const cwd = options.cwd ?? process.cwd();
15425
+ const catalog = getWorkflowPackCatalog();
15426
+ const updated = [];
15427
+ const regeneratedProjections = [];
15428
+ const skippedProjectOwned = [];
15429
+ const overrides = [];
15430
+ const errors = [];
15431
+ const existingOverrides = await detectOverrides(cwd);
15432
+ const existingProjectOwned = await detectProjectOwnedFiles(cwd);
15433
+ for (const docName of catalog.docs) {
15434
+ const targetRelativePath = `.pourkit/managed/docs/agents/${docName}`;
15435
+ if (isProjectOwnedPath(targetRelativePath)) {
15436
+ skippedProjectOwned.push(targetRelativePath);
15437
+ continue;
15438
+ }
15439
+ const overridePath = `.pourkit/overrides/docs/agents/${docName}`;
15440
+ if (findOverrideForManagedPath(
15441
+ existingOverrides,
15442
+ targetRelativePath,
15443
+ overridePath
15444
+ )) {
15445
+ overrides.push(overridePath);
15446
+ continue;
15447
+ }
15448
+ const sourcePath = resolvePackagedManagedDocPath2(docName);
15449
+ const sourceContent = await readPackagedTextFile2(sourcePath);
15450
+ if (sourceContent === null) {
15451
+ errors.push(`Missing packaged managed doc: ${docName}`);
15452
+ continue;
15453
+ }
15454
+ const written = await syncFile(
15455
+ cwd,
15456
+ targetRelativePath,
15457
+ sourceContent,
15458
+ errors
15459
+ );
15460
+ if (written) {
15461
+ updated.push(targetRelativePath);
15462
+ }
15463
+ }
15464
+ const baselineSkills = getBaselineManagedSkillNames();
15465
+ const syncAcc = {
15466
+ updated,
15467
+ regeneratedProjections,
15468
+ skippedProjectOwned,
15469
+ overrides,
15470
+ errors
15471
+ };
15472
+ const syncCtx = { cwd, existingOverrides };
15473
+ for (const skillName of baselineSkills) {
15474
+ await syncSkill(skillName, catalog, syncAcc, syncCtx);
15475
+ }
15476
+ const releaseEnabled = await isReleaseWorkflowEnabled(cwd);
15477
+ if (releaseEnabled) {
15478
+ for (const skillName of catalog.releaseAddonSkills) {
15479
+ await syncAddonSkill(skillName, syncAcc, syncCtx);
15480
+ }
15481
+ }
15482
+ if (releaseEnabled) {
15483
+ for (const docName of catalog.releaseAddonDocs) {
15484
+ const targetRelativePath = `.pourkit/managed/addons/release/docs/agents/${docName}`;
15485
+ const overridePath = `.pourkit/overrides/addons/release/docs/agents/${docName}`;
15486
+ if (findOverrideForManagedPath(
15487
+ existingOverrides,
15488
+ targetRelativePath,
15489
+ overridePath
15490
+ )) {
15491
+ overrides.push(overridePath);
15492
+ continue;
15493
+ }
15494
+ const sourcePath = resolvePackagedReleaseAddonDocPath2(docName);
15495
+ const sourceContent = await readPackagedTextFile2(sourcePath);
15496
+ if (sourceContent === null) {
15497
+ errors.push(`Missing packaged release addon doc: ${docName}`);
15498
+ continue;
15499
+ }
15500
+ const written = await syncFile(
15501
+ cwd,
15502
+ targetRelativePath,
15503
+ sourceContent,
15504
+ errors
15505
+ );
15506
+ if (written) {
15507
+ updated.push(targetRelativePath);
15508
+ }
15509
+ }
15510
+ }
15511
+ for (const promptName of catalog.prompts) {
15512
+ const targetRelativePath = `.pourkit/managed/prompts/${promptName}`;
15513
+ if (isProjectOwnedPath(targetRelativePath)) {
15514
+ skippedProjectOwned.push(targetRelativePath);
15515
+ continue;
15516
+ }
15517
+ const overridePath = `.pourkit/overrides/prompts/${promptName}`;
15518
+ if (findOverrideForManagedPath(
15519
+ existingOverrides,
15520
+ targetRelativePath,
15521
+ overridePath
15522
+ )) {
15523
+ overrides.push(overridePath);
15524
+ continue;
15525
+ }
15526
+ const sourcePath = resolvePackagedManagedPromptPath(promptName);
15527
+ const sourceContent = await readPackagedTextFile2(sourcePath);
15528
+ if (sourceContent === null) {
15529
+ errors.push(`Missing packaged managed prompt: ${promptName}`);
15530
+ continue;
15531
+ }
15532
+ const written = await syncFile(
15533
+ cwd,
15534
+ targetRelativePath,
15535
+ sourceContent,
15536
+ errors
15537
+ );
15538
+ if (written) {
15539
+ updated.push(targetRelativePath);
15540
+ }
15541
+ }
15542
+ const schemaResult = await runConfigSyncSchemaCommand({ cwd });
15543
+ const agentsPath = resolve5(cwd, "AGENTS.md");
15544
+ const managedBlockContent = generateManagedBlockContent();
15545
+ const fullManagedBlock = `${MANAGED_BLOCK_BEGIN3}${managedBlockContent}${MANAGED_BLOCK_END2}`;
15546
+ let agentsUpdated = false;
15547
+ if (existsSync20(agentsPath)) {
15548
+ const existing = await readFile7(agentsPath, "utf-8");
15549
+ const beginIdx = existing.indexOf(MANAGED_BLOCK_BEGIN3);
15550
+ const endIdx = existing.indexOf(MANAGED_BLOCK_END2);
15551
+ if (beginIdx !== -1 && endIdx !== -1) {
15552
+ const beforeBlock = existing.slice(0, beginIdx);
15553
+ const afterBlock = existing.slice(endIdx + MANAGED_BLOCK_END2.length);
15554
+ const newContent = `${beforeBlock}${fullManagedBlock}${afterBlock}`;
15555
+ if (newContent !== existing) {
15556
+ await writeFile4(agentsPath, newContent, "utf-8");
15557
+ agentsUpdated = true;
15558
+ }
15559
+ } else {
15560
+ const newContent = `${existing}
15561
+
15562
+ ${fullManagedBlock}
15563
+ `;
15564
+ if (newContent !== existing) {
15565
+ await writeFile4(agentsPath, newContent, "utf-8");
15566
+ agentsUpdated = true;
15567
+ }
15568
+ }
15569
+ } else {
15570
+ await writeFile4(agentsPath, fullManagedBlock + "\n", "utf-8");
15571
+ agentsUpdated = true;
15572
+ }
15573
+ if (agentsUpdated) {
15574
+ updated.push("AGENTS.md");
15575
+ }
15576
+ const enabledAddons = releaseEnabled ? ["release"] : [];
15577
+ const manifestMeta = buildWorkflowPackMetadata(enabledAddons);
15578
+ const manifestPath = resolve5(cwd, ".pourkit", "manifest.json");
15579
+ let manifestWritten = false;
15580
+ let previousManifestWorkflowPack = void 0;
15581
+ try {
15582
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(() => null);
15583
+ if (existingRaw) {
15584
+ const existing = JSON.parse(existingRaw);
15585
+ previousManifestWorkflowPack = existing.workflowPack;
15586
+ }
15587
+ } catch {
15588
+ }
15589
+ if (previousManifestWorkflowPack === void 0 || JSON.stringify(previousManifestWorkflowPack) !== JSON.stringify(manifestMeta)) {
15590
+ try {
15591
+ const existingRaw = await readFile7(manifestPath, "utf-8").catch(
15592
+ () => null
15593
+ );
15594
+ let manifest = existingRaw ? JSON.parse(existingRaw) : {};
15595
+ manifest.workflowPack = manifestMeta;
15596
+ await mkdir7(resolve5(manifestPath, ".."), { recursive: true });
15597
+ await writeFile4(
15598
+ manifestPath,
15599
+ JSON.stringify(manifest, null, 2) + "\n",
15600
+ "utf-8"
15601
+ );
15602
+ manifestWritten = true;
15603
+ } catch {
15604
+ errors.push("Failed to write manifest metadata");
15605
+ }
15606
+ }
15607
+ for (const owned of existingProjectOwned) {
15608
+ if (!skippedProjectOwned.includes(owned)) {
15609
+ skippedProjectOwned.push(owned);
15610
+ }
15611
+ }
15612
+ for (const overridePath of existingOverrides) {
15613
+ const displayPath = overridePath;
15614
+ if (!overrides.includes(displayPath)) {
15615
+ overrides.push(displayPath);
15616
+ }
15617
+ }
15618
+ const changed = updated.length > 0 || regeneratedProjections.length > 0 || schemaResult.changed || agentsUpdated || manifestWritten;
15619
+ return {
15620
+ changed,
15621
+ updated,
15622
+ regeneratedProjections,
15623
+ skippedProjectOwned,
15624
+ overrides,
15625
+ errors,
15626
+ schema: schemaResult,
15627
+ manifestWritten
15628
+ };
15629
+ }
15630
+ async function syncSkill(skillName, catalog, acc, ctx) {
15631
+ const { cwd, existingOverrides } = ctx;
15632
+ const {
15633
+ updated,
15634
+ regeneratedProjections,
15635
+ skippedProjectOwned,
15636
+ overrides,
15637
+ errors
15638
+ } = acc;
15639
+ const skillDir = resolvePackagedManagedSkillDir(skillName);
15640
+ const skillFiles = walkDirSync(skillDir);
15641
+ if (skillFiles.length === 0) {
15642
+ errors.push(`Missing packaged managed skill: ${skillName}`);
15643
+ return;
15644
+ }
15645
+ for (const sourceFile of skillFiles) {
15646
+ const relPath = relative3(skillDir, sourceFile);
15647
+ const targetRelativePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
15648
+ if (isProjectOwnedPath(targetRelativePath)) {
15649
+ skippedProjectOwned.push(targetRelativePath);
15650
+ continue;
15651
+ }
15652
+ const overridePath = `.pourkit/overrides/skills/${skillName}/${relPath}`;
15653
+ if (findOverrideForManagedPath(
15654
+ existingOverrides,
15655
+ targetRelativePath,
15656
+ overridePath
15657
+ )) {
15658
+ if (!overrides.includes(overridePath)) {
15659
+ overrides.push(overridePath);
15660
+ }
15661
+ continue;
15662
+ }
15663
+ const sourceContent = await readPackagedTextFile2(sourceFile);
15664
+ if (sourceContent === null) {
15665
+ errors.push(`Cannot read packaged skill file: ${skillName}/${relPath}`);
15666
+ continue;
15667
+ }
15668
+ const written = await syncFile(
15669
+ cwd,
15670
+ targetRelativePath,
15671
+ sourceContent,
15672
+ errors
15673
+ );
15674
+ if (written) {
15675
+ updated.push(targetRelativePath);
15676
+ }
15677
+ const projectionRelativePath = `.agents/skills/${skillName}/${relPath}`;
15678
+ const projectionOverridePath = `.pourkit/overrides/${projectionRelativePath}`;
15679
+ if (existingOverrides.some((o) => o === projectionOverridePath)) {
15680
+ if (!overrides.includes(projectionOverridePath)) {
15681
+ overrides.push(projectionOverridePath);
15682
+ }
15683
+ continue;
15684
+ }
15685
+ const managedSourcePath = `.pourkit/managed/skills/${skillName}/${relPath}`;
15686
+ const projectionContent = buildOpenCodeSkillProjectionContent(
15687
+ managedSourcePath,
15688
+ sourceContent
15689
+ );
15690
+ const projectionWritten = await syncFile(
15691
+ cwd,
15692
+ projectionRelativePath,
15693
+ projectionContent,
15694
+ errors
15695
+ );
15696
+ if (projectionWritten) {
15697
+ regeneratedProjections.push(projectionRelativePath);
15698
+ }
15699
+ }
15700
+ }
15701
+ async function syncAddonSkill(skillName, acc, ctx) {
15702
+ const { cwd, existingOverrides } = ctx;
15703
+ const {
15704
+ updated,
15705
+ regeneratedProjections,
15706
+ skippedProjectOwned,
15707
+ overrides,
15708
+ errors
15709
+ } = acc;
15710
+ const addonSkillDir = resolvePackagedManagedPath2(
15711
+ "addons",
15712
+ "release",
15713
+ "skills",
15714
+ skillName
15715
+ );
15716
+ const skillFiles = walkDirSync(addonSkillDir);
15717
+ if (skillFiles.length === 0) {
15718
+ errors.push(`Missing packaged release addon skill: ${skillName}`);
15719
+ return;
15720
+ }
15721
+ for (const sourceFile of skillFiles) {
15722
+ const relPath = relative3(addonSkillDir, sourceFile);
15723
+ const targetRelativePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
15724
+ if (isProjectOwnedPath(targetRelativePath)) {
15725
+ skippedProjectOwned.push(targetRelativePath);
15726
+ continue;
15727
+ }
15728
+ const overridePath = `.pourkit/overrides/addons/release/skills/${skillName}/${relPath}`;
15729
+ if (findOverrideForManagedPath(
15730
+ existingOverrides,
15731
+ targetRelativePath,
15732
+ overridePath
15733
+ )) {
15734
+ if (!overrides.includes(overridePath)) {
15735
+ overrides.push(overridePath);
15736
+ }
15737
+ continue;
15738
+ }
15739
+ const sourceContent = await readPackagedTextFile2(sourceFile);
15740
+ if (sourceContent === null) {
15741
+ errors.push(
15742
+ `Cannot read packaged addon skill file: ${skillName}/${relPath}`
15743
+ );
15744
+ continue;
15745
+ }
15746
+ const written = await syncFile(
15747
+ cwd,
15748
+ targetRelativePath,
15749
+ sourceContent,
15750
+ errors
15751
+ );
15752
+ if (written) {
15753
+ updated.push(targetRelativePath);
15754
+ }
15755
+ const projectionRelativePath = `.agents/skills/${skillName}/${relPath}`;
15756
+ const projectionOverridePath = `.pourkit/overrides/${projectionRelativePath}`;
15757
+ if (existingOverrides.some((o) => o === projectionOverridePath)) {
15758
+ if (!overrides.includes(projectionOverridePath)) {
15759
+ overrides.push(projectionOverridePath);
15760
+ }
15761
+ continue;
15762
+ }
15763
+ const managedSourcePath = `.pourkit/managed/addons/release/skills/${skillName}/${relPath}`;
15764
+ const projectionContent = buildOpenCodeSkillProjectionContent(
15765
+ managedSourcePath,
15766
+ sourceContent
15767
+ );
15768
+ const projectionWritten = await syncFile(
15769
+ cwd,
15770
+ projectionRelativePath,
15771
+ projectionContent,
15772
+ errors
15773
+ );
15774
+ if (projectionWritten) {
15775
+ regeneratedProjections.push(projectionRelativePath);
15776
+ }
15777
+ }
15778
+ }
15779
+
14154
15780
  // providers/github-provider.ts
14155
15781
  var GitHubIssueProvider = class {
14156
15782
  client;
@@ -14688,7 +16314,7 @@ function formatChecks2(checks) {
14688
16314
  init_common();
14689
16315
 
14690
16316
  // execution/sandcastle-execution.ts
14691
- import { existsSync as existsSync21, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
16317
+ import { existsSync as existsSync22, mkdirSync as mkdirSync9, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
14692
16318
  import { join as join23 } from "path";
14693
16319
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
14694
16320
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
@@ -14696,14 +16322,14 @@ import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
14696
16322
  // execution/execution-provider.ts
14697
16323
  init_common();
14698
16324
  import { mkdtempSync } from "fs";
14699
- import { writeFile as writeFile4 } from "fs/promises";
16325
+ import { writeFile as writeFile5 } from "fs/promises";
14700
16326
  import { tmpdir } from "os";
14701
- import { dirname as dirname6, join as join22 } from "path";
16327
+ import { dirname as dirname7, join as join22 } from "path";
14702
16328
  async function writeExecutionArtifacts(worktreePath, artifacts) {
14703
16329
  for (const artifact of artifacts) {
14704
16330
  const filePath = join22(worktreePath, artifact.path);
14705
- await ensureDir(dirname6(filePath));
14706
- await writeFile4(filePath, artifact.content, "utf-8");
16331
+ await ensureDir(dirname7(filePath));
16332
+ await writeFile5(filePath, artifact.content, "utf-8");
14707
16333
  }
14708
16334
  }
14709
16335
 
@@ -14713,14 +16339,14 @@ import path7 from "path";
14713
16339
 
14714
16340
  // execution/sandbox-image.ts
14715
16341
  import { createHash as createHash3 } from "crypto";
14716
- import { existsSync as existsSync20, readFileSync as readFileSync21 } from "fs";
16342
+ import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
14717
16343
  import path6 from "path";
14718
16344
  function sandboxImageName(repoRoot2) {
14719
16345
  const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
14720
16346
  const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
14721
16347
  const baseName = sanitized || "local";
14722
16348
  const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
14723
- if (!existsSync20(dockerfilePath)) {
16349
+ if (!existsSync21(dockerfilePath)) {
14724
16350
  return `sandcastle:${baseName}`;
14725
16351
  }
14726
16352
  const fingerprint = createHash3("sha256").update(readFileSync21(dockerfilePath)).digest("hex").slice(0, 8);
@@ -14970,7 +16596,7 @@ function sanitizeBranch(branchName) {
14970
16596
  function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
14971
16597
  return paths.filter((relativePath) => {
14972
16598
  const source = join23(repoRoot2, relativePath);
14973
- if (!existsSync21(source)) {
16599
+ if (!existsSync22(source)) {
14974
16600
  return true;
14975
16601
  }
14976
16602
  try {
@@ -14981,7 +16607,7 @@ function safeCopyToWorktreePaths(paths, repoRoot2, worktreePath) {
14981
16607
  return true;
14982
16608
  }
14983
16609
  const destination = join23(worktreePath, relativePath);
14984
- return !existsSync21(destination);
16610
+ return !existsSync22(destination);
14985
16611
  });
14986
16612
  }
14987
16613
  function formatAgentStreamEvent(event) {
@@ -15201,6 +16827,9 @@ function createCliProgram(version) {
15201
16827
  ).option(
15202
16828
  "--branch-name <name>",
15203
16829
  "branch name for issue-final-review artifacts"
16830
+ ).option(
16831
+ "--base-ref <ref>",
16832
+ "canonical base ref for issue-final-review changedFiles coverage validation"
15204
16833
  ).option("--no-check-conflict-markers", "skip conflict marker scan").option("--cwd <path>", "target repository directory").action(
15205
16834
  (kind, artifactPath, extraPaths, options) => {
15206
16835
  const allowedKinds = /* @__PURE__ */ new Set([
@@ -15243,6 +16872,7 @@ function createCliProgram(version) {
15243
16872
  allowedDecisions: options.allowedDecision,
15244
16873
  issueNumber: options.issueNumber,
15245
16874
  branchName: options.branchName,
16875
+ baseRef: options.baseRef,
15246
16876
  checkConflictMarkers: options.checkConflictMarkers
15247
16877
  });
15248
16878
  console.log(JSON.stringify(result, null, 2));
@@ -15603,7 +17233,7 @@ function createCliProgram(version) {
15603
17233
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
15604
17234
  const report = await runDoctorCommand({ cwd: targetRepoRoot });
15605
17235
  console.log(JSON.stringify(report, null, 2));
15606
- if (!report.configValidation.ok || report.obsoleteConfigs.length > 0 || report.schemaAssets.overall !== "fresh") {
17236
+ if (!report.configValidation.ok || report.obsoleteConfigs.length > 0 || report.schemaAssets.overall !== "fresh" || report.workflowPack.status === "failed") {
15607
17237
  process.exitCode = 1;
15608
17238
  }
15609
17239
  });
@@ -15619,6 +17249,24 @@ function createCliProgram(version) {
15619
17249
  console.log("Schema assets are up to date.");
15620
17250
  }
15621
17251
  });
17252
+ const syncCommand = program.command("sync").description("Sync commands for refreshing managed assets");
17253
+ syncCommand.command("workflow-pack").description("Refresh managed workflow pack assets from packaged sources").option("--cwd <path>", "target repository directory").action(async (options) => {
17254
+ const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
17255
+ const result = await runWorkflowPackSyncCommand({ cwd: targetRepoRoot });
17256
+ if (result.changed) {
17257
+ console.log(
17258
+ `Workflow pack sync complete: ${result.updated.length} updated, ${result.regeneratedProjections.length} projections regenerated, ${result.overrides.length} overrides, ${result.skippedProjectOwned.length} project-owned files skipped.`
17259
+ );
17260
+ } else {
17261
+ console.log("Workflow pack is up to date.");
17262
+ }
17263
+ if (result.errors.length > 0) {
17264
+ for (const err of result.errors) {
17265
+ console.error(` Error: ${err}`);
17266
+ }
17267
+ process.exitCode = 1;
17268
+ }
17269
+ });
15622
17270
  const serena = program.command("serena").description("Serena baseline and sidecar commands");
15623
17271
  serena.command("init").requiredOption("--target <name>", "target name").option("--cwd <path>", "target repository directory").action(async (options) => {
15624
17272
  await runSerenaInitCommand({
@@ -15744,11 +17392,11 @@ function createCliProgram(version) {
15744
17392
  return program;
15745
17393
  }
15746
17394
  async function resolveCliVersion() {
15747
- if (isPackageVersion("0.0.0-next-20260615184943")) {
15748
- return "0.0.0-next-20260615184943";
17395
+ if (isPackageVersion("0.0.0-next-20260617183635")) {
17396
+ return "0.0.0-next-20260617183635";
15749
17397
  }
15750
- if (isReleaseVersion("0.0.0-next-20260615184943")) {
15751
- return "0.0.0-next-20260615184943";
17398
+ if (isReleaseVersion("0.0.0-next-20260617183635")) {
17399
+ return "0.0.0-next-20260617183635";
15752
17400
  }
15753
17401
  try {
15754
17402
  const root = repoRoot();