@codedrifters/configulator 0.0.328 → 0.0.330
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.mts +254 -44
- package/lib/index.d.ts +255 -45
- package/lib/index.js +1735 -734
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1731 -733
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -237,6 +237,7 @@ __export(index_exports, {
|
|
|
237
237
|
DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE: () => DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
|
|
238
238
|
DEFAULT_OFF_PEAK_CRON_EXAMPLE: () => DEFAULT_OFF_PEAK_CRON_EXAMPLE,
|
|
239
239
|
DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE: () => DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
|
|
240
|
+
DEFAULT_PATHS_EXEMPT_FROM_SIZE: () => DEFAULT_PATHS_EXEMPT_FROM_SIZE,
|
|
240
241
|
DEFAULT_PRIORITY_LABELS: () => DEFAULT_PRIORITY_LABELS,
|
|
241
242
|
DEFAULT_PRODUCT_CONTEXT_PATH: () => DEFAULT_PRODUCT_CONTEXT_PATH,
|
|
242
243
|
DEFAULT_PROGRESS_FILES_ENABLED: () => DEFAULT_PROGRESS_FILES_ENABLED,
|
|
@@ -339,6 +340,7 @@ __export(index_exports, {
|
|
|
339
340
|
buildMeetingAnalysisBundle: () => buildMeetingAnalysisBundle,
|
|
340
341
|
buildOrchestratorConventionsContent: () => buildOrchestratorConventionsContent,
|
|
341
342
|
buildPeopleProfileBundle: () => buildPeopleProfileBundle,
|
|
343
|
+
buildPrReviewBundle: () => buildPrReviewBundle,
|
|
342
344
|
buildRegulatoryResearchBundle: () => buildRegulatoryResearchBundle,
|
|
343
345
|
buildReport: () => buildReport,
|
|
344
346
|
buildRequirementsAnalystBundle: () => buildRequirementsAnalystBundle,
|
|
@@ -448,7 +450,6 @@ __export(index_exports, {
|
|
|
448
450
|
renderSkillEvalsRuleContent: () => renderSkillEvalsRuleContent,
|
|
449
451
|
renderSkillEvalsRunnerScript: () => renderSkillEvalsRunnerScript,
|
|
450
452
|
renderSourceTierExamples: () => renderSourceTierExamples,
|
|
451
|
-
renderStubIndexConventionRuleContent: () => renderStubIndexConventionRuleContent,
|
|
452
453
|
renderTemporalFramingCheckerScript: () => renderTemporalFramingCheckerScript,
|
|
453
454
|
renderTemporalFramingRuleContent: () => renderTemporalFramingRuleContent,
|
|
454
455
|
renderUnblockDependentsScript: () => renderUnblockDependentsScript,
|
|
@@ -469,6 +470,7 @@ __export(index_exports, {
|
|
|
469
470
|
resolveOrchestratorAssets: () => resolveOrchestratorAssets,
|
|
470
471
|
resolveOutdirFromPackageName: () => resolveOutdirFromPackageName,
|
|
471
472
|
resolveOverrideForLabels: () => resolveOverrideForLabels,
|
|
473
|
+
resolvePrReviewPolicy: () => resolvePrReviewPolicy,
|
|
472
474
|
resolveProgressFiles: () => resolveProgressFiles,
|
|
473
475
|
resolveReactViteSiteProjectOutdir: () => resolveReactViteSiteProjectOutdir,
|
|
474
476
|
resolveRunRatio: () => resolveRunRatio,
|
|
@@ -492,6 +494,7 @@ __export(index_exports, {
|
|
|
492
494
|
validateIssueDefaultsConfig: () => validateIssueDefaultsConfig,
|
|
493
495
|
validateIssueTemplatesConfig: () => validateIssueTemplatesConfig,
|
|
494
496
|
validateMonorepoLayout: () => validateMonorepoLayout,
|
|
497
|
+
validatePrReviewPolicyConfig: () => validatePrReviewPolicyConfig,
|
|
495
498
|
validateProgressFilesConfig: () => validateProgressFilesConfig,
|
|
496
499
|
validateRunRatioConfig: () => validateRunRatioConfig,
|
|
497
500
|
validateScheduledTasksConfig: () => validateScheduledTasksConfig,
|
|
@@ -919,7 +922,8 @@ var agendaAnalystSubAgent = {
|
|
|
919
922
|
" or cut content \u2014 do not ship a math-broken agenda.",
|
|
920
923
|
"",
|
|
921
924
|
"7. **Create the folder `index.md` if missing.** Populate it",
|
|
922
|
-
" following the
|
|
925
|
+
" following the section-index-page contract in the",
|
|
926
|
+
" `shared-editing-safety` rule:",
|
|
923
927
|
" - Frontmatter `title` (short sidebar label) and `description`",
|
|
924
928
|
" (one line).",
|
|
925
929
|
" - A 2\u20134 sentence summary of the meeting's objective and desired",
|
|
@@ -1492,11 +1496,7 @@ var awsCdkBundle = {
|
|
|
1492
1496
|
// src/agent/bundles/issue-templates.ts
|
|
1493
1497
|
var DEFAULT_ISSUE_TEMPLATES_ENABLED = true;
|
|
1494
1498
|
var DEFAULT_ISSUE_TEMPLATES_PATH = "docs/src/content/docs/agents/issue-templates.md";
|
|
1495
|
-
var DEFAULT_ISSUE_TEMPLATES_BUNDLE_PATH_PATTERNS = [
|
|
1496
|
-
"packages/@codedrifters/configulator/src/agent/bundles/**/*.ts",
|
|
1497
|
-
".claude/agents/**/*.md",
|
|
1498
|
-
".claude/skills/**/*.md"
|
|
1499
|
-
];
|
|
1499
|
+
var DEFAULT_ISSUE_TEMPLATES_BUNDLE_PATH_PATTERNS = [".claude/agents/**/*.md", ".claude/skills/**/*.md"];
|
|
1500
1500
|
var DEFAULT_ISSUE_TEMPLATES_EMIT_CHECKER = false;
|
|
1501
1501
|
var DEFAULT_ISSUE_TEMPLATES_EMIT_STARTER = false;
|
|
1502
1502
|
var DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE = true;
|
|
@@ -2585,6 +2585,55 @@ function renderSharedEditingRuleContent(se) {
|
|
|
2585
2585
|
}
|
|
2586
2586
|
}
|
|
2587
2587
|
lines.push(
|
|
2588
|
+
"## Defer Shared-Index Commit to Final Pre-Push Step",
|
|
2589
|
+
"",
|
|
2590
|
+
"Even with the single-entry, deterministic-sort discipline above,",
|
|
2591
|
+
"two sessions that **prepare** their row inserts at the same time",
|
|
2592
|
+
"still race on push: whichever session pushes second sees the",
|
|
2593
|
+
"first session's commit on the remote and has to rebase its own",
|
|
2594
|
+
"commit on top, regenerating the same insert position calculation",
|
|
2595
|
+
"against a now-changed file. Repeated rebases multiply the chance",
|
|
2596
|
+
"of a mis-merge that silently drops a row.",
|
|
2597
|
+
"",
|
|
2598
|
+
"Sessions that produce both content and a shared-index row insert",
|
|
2599
|
+
"shrink this race window by deferring the index commit to the",
|
|
2600
|
+
"**final pre-push step** \u2014 after every content commit has landed",
|
|
2601
|
+
"locally, and immediately before `git push`:",
|
|
2602
|
+
"",
|
|
2603
|
+
"1. **Commit content first.** Write and commit every non-index",
|
|
2604
|
+
" change that the session produces (profile body, transcript",
|
|
2605
|
+
" extraction, requirement document, etc.) in its own focused",
|
|
2606
|
+
" commit or commits.",
|
|
2607
|
+
"2. **Rebase against the remote default branch** before touching",
|
|
2608
|
+
" the shared index:",
|
|
2609
|
+
"",
|
|
2610
|
+
" ```bash",
|
|
2611
|
+
" git fetch origin",
|
|
2612
|
+
` git pull --${se.conflictStrategy} origin <default-branch>`,
|
|
2613
|
+
" ```",
|
|
2614
|
+
"",
|
|
2615
|
+
"3. **Re-read the shared index** from the now-up-to-date working",
|
|
2616
|
+
" tree. Another session may have appended a row while this",
|
|
2617
|
+
" session's content commits were in flight.",
|
|
2618
|
+
"4. **Re-compute the insert position** in declared sort order",
|
|
2619
|
+
" against the freshly-read rows. Do not assume the position",
|
|
2620
|
+
" computed earlier in the session is still correct.",
|
|
2621
|
+
"5. **Insert the row and commit the index edit on its own** \u2014",
|
|
2622
|
+
" one focused commit whose only file is the shared index. Run",
|
|
2623
|
+
" the commit-path verification (above) against that commit",
|
|
2624
|
+
" before continuing.",
|
|
2625
|
+
"6. **Push immediately.** The shorter the wall-clock gap between",
|
|
2626
|
+
" the rebase / re-read in step 2 and the push, the smaller the",
|
|
2627
|
+
" window in which another session can land a competing row.",
|
|
2628
|
+
"",
|
|
2629
|
+
"Per-agent workflows that touch a shared index file should call",
|
|
2630
|
+
"out this defer-to-final-commit sequence explicitly \u2014 see the",
|
|
2631
|
+
"`meeting-analyst` and `software-profile-analyst` sub-agent",
|
|
2632
|
+
"prompts for the canonical wording. The pattern is mechanical",
|
|
2633
|
+
"(rebase, re-read, re-compute, focused commit, push) rather than",
|
|
2634
|
+
"editorial, so the same recipe applies to every shared-index",
|
|
2635
|
+
"row-producing agent regardless of what content surrounds it.",
|
|
2636
|
+
"",
|
|
2588
2637
|
"## Merge-Conflict Resolution",
|
|
2589
2638
|
"",
|
|
2590
2639
|
"When `git push` reports a conflict on a shared index file (two",
|
|
@@ -2616,7 +2665,66 @@ function renderSharedEditingRuleContent(se) {
|
|
|
2616
2665
|
"conflicts; rewriting or reordering existing rows almost always",
|
|
2617
2666
|
"does. If a shared index needs a structural change (column",
|
|
2618
2667
|
"added, sort key changed), file a dedicated issue for the change",
|
|
2619
|
-
"rather than bundling it into a content-contributing PR."
|
|
2668
|
+
"rather than bundling it into a content-contributing PR.",
|
|
2669
|
+
"",
|
|
2670
|
+
"## Section Index Page Shape",
|
|
2671
|
+
"",
|
|
2672
|
+
"When any agent creates or updates an `index.md` (or `README.md`)",
|
|
2673
|
+
"in a docs subdirectory under a Starlight content root, the file's",
|
|
2674
|
+
"body must include:",
|
|
2675
|
+
"",
|
|
2676
|
+
"1. **A 1\u20132 paragraph summary** of the section's purpose and how",
|
|
2677
|
+
" it fits into the larger research, requirements, or capability",
|
|
2678
|
+
" area. Readers landing on the page should understand what's in",
|
|
2679
|
+
" the section without falling back to the sidebar.",
|
|
2680
|
+
"",
|
|
2681
|
+
"2. **A grouped, linked listing of the directory's children**",
|
|
2682
|
+
" (table or bullet list). When a natural taxonomy exists (e.g.",
|
|
2683
|
+
" organizations grouped by sub-segment, regulations grouped by",
|
|
2684
|
+
" jurisdiction, capabilities grouped by tier), use it. Otherwise",
|
|
2685
|
+
" sort alphabetically by title. Every listing entry must link",
|
|
2686
|
+
" to the child page.",
|
|
2687
|
+
"",
|
|
2688
|
+
"3. **No body `# Heading`.** Starlight renders the frontmatter",
|
|
2689
|
+
" `title:` as the page H1 automatically \u2014 a body H1 produces a",
|
|
2690
|
+
" duplicate. The same no-body-H1 contract that applies to skill",
|
|
2691
|
+
" templates and inline agent templates also applies to every",
|
|
2692
|
+
" index page emitted by an agent.",
|
|
2693
|
+
"",
|
|
2694
|
+
"### When this applies",
|
|
2695
|
+
"",
|
|
2696
|
+
"The convention applies to every `index.md` / `README.md` file",
|
|
2697
|
+
"covered by the shared-index path globs listed above:",
|
|
2698
|
+
"",
|
|
2699
|
+
"- `docs/src/content/docs/**/index.md`",
|
|
2700
|
+
"- `docs/src/content/docs/**/README.md`",
|
|
2701
|
+
"",
|
|
2702
|
+
"Any agent that scaffolds a new directory under a Starlight",
|
|
2703
|
+
"content root MUST populate the directory's index page following",
|
|
2704
|
+
"this contract \u2014 a one-sentence stub is not acceptable.",
|
|
2705
|
+
"",
|
|
2706
|
+
"### Reference shapes",
|
|
2707
|
+
"",
|
|
2708
|
+
"Existing rich indexes are the canonical reference shape. Examples:",
|
|
2709
|
+
"",
|
|
2710
|
+
"- A `<MEETINGS_ROOT>/<YYYY-MM-DD>-<slug>/index.md` populated by",
|
|
2711
|
+
" the `agenda-analyst` bundle: frontmatter `title` /",
|
|
2712
|
+
" `description`, a 2\u20134 sentence summary, and a `## Documents`",
|
|
2713
|
+
" section listing every page in the folder.",
|
|
2714
|
+
"- A regulations scope index that opens with two paragraphs of",
|
|
2715
|
+
" context and groups every linked regulation under a",
|
|
2716
|
+
" `## Regulations` table by jurisdiction.",
|
|
2717
|
+
"- A standards-organizations index that groups every linked",
|
|
2718
|
+
" organization under a `## Organizations` heading by role",
|
|
2719
|
+
" (governance, working group, implementer).",
|
|
2720
|
+
"",
|
|
2721
|
+
"### Out of scope",
|
|
2722
|
+
"",
|
|
2723
|
+
"- Auto-generated child listings via Astro/Starlight components.",
|
|
2724
|
+
" Agents emit hand-curated tables or bullet lists; the rule",
|
|
2725
|
+
" defines the shape, not the rendering technology.",
|
|
2726
|
+
"- Backfilling pre-existing stub indexes is a downstream-consumer",
|
|
2727
|
+
" cleanup task, not a configulator change."
|
|
2620
2728
|
);
|
|
2621
2729
|
return lines.join("\n");
|
|
2622
2730
|
}
|
|
@@ -2632,9 +2740,14 @@ function renderSharedEditingBundleHook(se, bundleLabel) {
|
|
|
2632
2740
|
"the latest default branch before editing, insert exactly one row",
|
|
2633
2741
|
"in deterministic sort position, commit the index edit in its own",
|
|
2634
2742
|
"focused commit, and verify the row is present in the commit",
|
|
2635
|
-
"before pushing.
|
|
2636
|
-
"
|
|
2637
|
-
"
|
|
2743
|
+
"before pushing. **Defer the index commit to the final pre-push",
|
|
2744
|
+
"step** \u2014 land every content commit first, then rebase against",
|
|
2745
|
+
"the remote default branch, re-read the index, re-compute the",
|
|
2746
|
+
"insert position, write the row, commit the index on its own,",
|
|
2747
|
+
"and push immediately. See the `shared-editing-safety` rule for",
|
|
2748
|
+
"the full protocol, the list of files covered, the",
|
|
2749
|
+
"defer-to-final-commit sequence, and the merge-conflict",
|
|
2750
|
+
"resolution recipe."
|
|
2638
2751
|
].join("\n");
|
|
2639
2752
|
}
|
|
2640
2753
|
function renderSharedEditingHelperScript(_se) {
|
|
@@ -3166,71 +3279,6 @@ function assertValidProductContextPath(value) {
|
|
|
3166
3279
|
}
|
|
3167
3280
|
}
|
|
3168
3281
|
|
|
3169
|
-
// src/agent/bundles/stub-index-convention.ts
|
|
3170
|
-
function renderStubIndexConventionRuleContent() {
|
|
3171
|
-
return [
|
|
3172
|
-
"# Section Index Pages",
|
|
3173
|
-
"",
|
|
3174
|
-
"When any agent creates or updates an `index.md` (or `README.md`)",
|
|
3175
|
-
"in a docs subdirectory under a Starlight content root, the file's",
|
|
3176
|
-
"body must include:",
|
|
3177
|
-
"",
|
|
3178
|
-
"1. **A 1\u20132 paragraph summary** of the section's purpose and how",
|
|
3179
|
-
" it fits into the larger research, requirements, or capability",
|
|
3180
|
-
" area. Readers landing on the page should understand what's in",
|
|
3181
|
-
" the section without falling back to the sidebar.",
|
|
3182
|
-
"",
|
|
3183
|
-
"2. **A grouped, linked listing of the directory's children**",
|
|
3184
|
-
" (table or bullet list). When a natural taxonomy exists (e.g.",
|
|
3185
|
-
" organizations grouped by sub-segment, regulations grouped by",
|
|
3186
|
-
" jurisdiction, capabilities grouped by tier), use it. Otherwise",
|
|
3187
|
-
" sort alphabetically by title. Every listing entry must link",
|
|
3188
|
-
" to the child page.",
|
|
3189
|
-
"",
|
|
3190
|
-
"3. **No body `# Heading`.** Starlight renders the frontmatter",
|
|
3191
|
-
" `title:` as the page H1 automatically \u2014 a body H1 produces a",
|
|
3192
|
-
" duplicate. The same no-body-H1 contract that applies to skill",
|
|
3193
|
-
" templates and inline agent templates also applies to every",
|
|
3194
|
-
" index page emitted by an agent.",
|
|
3195
|
-
"",
|
|
3196
|
-
"## When this applies",
|
|
3197
|
-
"",
|
|
3198
|
-
"The convention applies to every `index.md` / `README.md` file",
|
|
3199
|
-
"covered by the shared-index path globs documented in the",
|
|
3200
|
-
"`shared-editing-safety` rule:",
|
|
3201
|
-
"",
|
|
3202
|
-
"- `docs/src/content/docs/**/index.md`",
|
|
3203
|
-
"- `docs/src/content/docs/**/README.md`",
|
|
3204
|
-
"",
|
|
3205
|
-
"Any agent that scaffolds a new directory under a Starlight",
|
|
3206
|
-
"content root MUST populate the directory's index page following",
|
|
3207
|
-
"this contract \u2014 a one-sentence stub is not acceptable.",
|
|
3208
|
-
"",
|
|
3209
|
-
"## Reference shapes",
|
|
3210
|
-
"",
|
|
3211
|
-
"Existing rich indexes are the canonical reference shape. Examples:",
|
|
3212
|
-
"",
|
|
3213
|
-
"- A `<MEETINGS_ROOT>/<YYYY-MM-DD>-<slug>/index.md` populated by",
|
|
3214
|
-
" the `agenda-analyst` bundle: frontmatter `title` /",
|
|
3215
|
-
" `description`, a 2\u20134 sentence summary, and a `## Documents`",
|
|
3216
|
-
" section listing every page in the folder.",
|
|
3217
|
-
"- A regulations scope index that opens with two paragraphs of",
|
|
3218
|
-
" context and groups every linked regulation under a",
|
|
3219
|
-
" `## Regulations` table by jurisdiction.",
|
|
3220
|
-
"- A standards-organizations index that groups every linked",
|
|
3221
|
-
" organization under a `## Organizations` heading by role",
|
|
3222
|
-
" (governance, working group, implementer).",
|
|
3223
|
-
"",
|
|
3224
|
-
"## Out of scope",
|
|
3225
|
-
"",
|
|
3226
|
-
"- Auto-generated child listings via Astro/Starlight components.",
|
|
3227
|
-
" Agents emit hand-curated tables or bullet lists; the rule",
|
|
3228
|
-
" defines the shape, not the rendering technology.",
|
|
3229
|
-
"- Backfilling pre-existing stub indexes is a downstream-consumer",
|
|
3230
|
-
" cleanup task, not a configulator change."
|
|
3231
|
-
].join("\n");
|
|
3232
|
-
}
|
|
3233
|
-
|
|
3234
3282
|
// src/agent/bundles/temporal-framing.ts
|
|
3235
3283
|
var DEFAULT_TEMPORAL_FRAMING_ENABLED = true;
|
|
3236
3284
|
var DEFAULT_TEMPORAL_FRAMING_PATHS = [
|
|
@@ -4399,6 +4447,12 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4399
4447
|
{
|
|
4400
4448
|
name: "issue-label-conventions",
|
|
4401
4449
|
description: "Priority and status label taxonomy, defaults, inference rules, and blocking rules for agent-created or updated issues",
|
|
4450
|
+
// ALWAYS scope: every agent (and the user) needs the label
|
|
4451
|
+
// taxonomy whenever an issue gets created or updated, which
|
|
4452
|
+
// happens from any context — not only when editing agent /
|
|
4453
|
+
// skill / bundle source. Consumers that want to narrow the
|
|
4454
|
+
// load can override via `agentConfig.additionalRulePaths`
|
|
4455
|
+
// or `excludeRules`.
|
|
4402
4456
|
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
4403
4457
|
content: [
|
|
4404
4458
|
"# Issue Label Conventions",
|
|
@@ -4764,7 +4818,13 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4764
4818
|
{
|
|
4765
4819
|
name: "progress-file-convention",
|
|
4766
4820
|
description: "Progress-file schema and write rules, partial-resume protocol, stale-branch decision tree, and [BLOCKED] comment format that let phased agents survive crashes without losing work.",
|
|
4767
|
-
scope: AGENT_RULE_SCOPE.
|
|
4821
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
4822
|
+
// Bundle defaults exclude paths into configulator's own source
|
|
4823
|
+
// (only meaningful when configulator is a workspace package,
|
|
4824
|
+
// i.e. in codedrifters/packages itself). That repo restores
|
|
4825
|
+
// them via `agentConfig.additionalRulePaths`; other consumers
|
|
4826
|
+
// get a clean default.
|
|
4827
|
+
filePatterns: [".claude/agents/*.md", ".claude/procedures/**"],
|
|
4768
4828
|
content: renderProgressFilesRuleContent(resolveProgressFiles()),
|
|
4769
4829
|
platforms: {
|
|
4770
4830
|
cursor: { exclude: true }
|
|
@@ -4774,27 +4834,26 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4774
4834
|
{
|
|
4775
4835
|
name: "shared-editing-safety",
|
|
4776
4836
|
description: "Shared-editing safety: single-entry deterministic-sort inserts on index files, commit-path verification, and the merge-conflict resolution recipe that keeps concurrent agent sessions from dropping each other's rows on shared registries and feature matrices.",
|
|
4777
|
-
scope: AGENT_RULE_SCOPE.
|
|
4837
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
4838
|
+
filePatterns: DEFAULT_SHARED_INDEX_PATHS,
|
|
4778
4839
|
content: renderSharedEditingRuleContent(resolveSharedEditing()),
|
|
4779
4840
|
platforms: {
|
|
4780
4841
|
cursor: { exclude: true }
|
|
4781
4842
|
},
|
|
4782
4843
|
tags: ["workflow"]
|
|
4783
4844
|
},
|
|
4784
|
-
{
|
|
4785
|
-
name: "stub-index-convention",
|
|
4786
|
-
description: "Section-index convention: every agent-emitted `index.md` / `README.md` under a Starlight docs root carries a contextual summary plus a grouped, linked listing of the directory's children \u2014 and no body `# Heading` (Starlight renders the frontmatter `title:` as the page H1).",
|
|
4787
|
-
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
4788
|
-
content: renderStubIndexConventionRuleContent(),
|
|
4789
|
-
platforms: {
|
|
4790
|
-
cursor: { exclude: true }
|
|
4791
|
-
},
|
|
4792
|
-
tags: ["workflow"]
|
|
4793
|
-
},
|
|
4794
4845
|
{
|
|
4795
4846
|
name: "temporal-framing-convention",
|
|
4796
4847
|
description: "Temporal-framing convention: every agent-authored time-sensitive factual claim (ownership, leadership tenure, regulatory status, litigation, dated metrics) carries an inline `as of [YYYY-MM-DD]` or `as of [Month YYYY]` qualifier on first occurrence so refresh agents have a mechanical signal for which claims to re-verify.",
|
|
4797
|
-
scope: AGENT_RULE_SCOPE.
|
|
4848
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
4849
|
+
filePatterns: [
|
|
4850
|
+
"docs/src/content/docs/profiles/**/*.md",
|
|
4851
|
+
"docs/src/content/docs/industry-research/**/*.md",
|
|
4852
|
+
"docs/src/content/docs/software-research/**/*.md",
|
|
4853
|
+
"docs/src/content/docs/regulatory-research/**/*.md",
|
|
4854
|
+
"docs/src/content/docs/standards-research/**/*.md",
|
|
4855
|
+
"docs/src/content/docs/customer-research/**/*.md"
|
|
4856
|
+
],
|
|
4798
4857
|
content: renderTemporalFramingRuleContent(resolveTemporalFraming()),
|
|
4799
4858
|
platforms: {
|
|
4800
4859
|
cursor: { exclude: true }
|
|
@@ -4804,7 +4863,10 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4804
4863
|
{
|
|
4805
4864
|
name: "skill-evals",
|
|
4806
4865
|
description: "Skill eval harness contract: declarative prompt/expected-output regression suites per skill at `<skillsRoot>/<skill-name>/evals/evals.json`, parameterised by a shared product-context fixture so the same eval shape works across every configulator-consuming project without forking fixtures.",
|
|
4807
|
-
scope: AGENT_RULE_SCOPE.
|
|
4866
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
4867
|
+
// Bundle defaults exclude paths into configulator's own
|
|
4868
|
+
// source — see comment on progress-file-convention above.
|
|
4869
|
+
filePatterns: [".claude/skills/**"],
|
|
4808
4870
|
content: renderSkillEvalsRuleContent(resolveSkillEvals()),
|
|
4809
4871
|
platforms: {
|
|
4810
4872
|
cursor: { exclude: true }
|
|
@@ -4814,7 +4876,14 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4814
4876
|
{
|
|
4815
4877
|
name: "issue-templates-convention",
|
|
4816
4878
|
description: `Issue-templates convention: a single hand-authored reference page under \`${paths.docsRoot}/agents/issue-templates.md\` that carries one canonical \`gh issue create\` recipe per downstream phase label, and the reference-don't-inline rule that keeps bundle rules and agent prompts citing that page instead of duplicating full template invocations.`,
|
|
4817
|
-
scope: AGENT_RULE_SCOPE.
|
|
4879
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
4880
|
+
// Bundle defaults exclude paths into configulator's own
|
|
4881
|
+
// source — see comment on progress-file-convention above.
|
|
4882
|
+
filePatterns: [
|
|
4883
|
+
".claude/skills/**/SKILL.md",
|
|
4884
|
+
".claude/agents/*.md",
|
|
4885
|
+
`${paths.docsRoot}/agents/issue-templates.md`
|
|
4886
|
+
],
|
|
4818
4887
|
content: renderIssueTemplatesRuleContent(resolveIssueTemplates()),
|
|
4819
4888
|
platforms: {
|
|
4820
4889
|
cursor: { exclude: true }
|
|
@@ -6137,8 +6206,8 @@ function buildBusinessModelsAnalystSubAgent(paths) {
|
|
|
6137
6206
|
"",
|
|
6138
6207
|
"7. **Create the segment index** at",
|
|
6139
6208
|
" `<BUSINESS_MODELS_ROOT>/<industry>/segments/<SEGMENT_SLUG>/index.md`",
|
|
6140
|
-
" if it does not already exist. Follow the",
|
|
6141
|
-
" `
|
|
6209
|
+
" if it does not already exist. Follow the section-index-page",
|
|
6210
|
+
" contract in the `shared-editing-safety` rule: a 1\u20132 paragraph summary of the",
|
|
6142
6211
|
" segment plus a linked listing of every page in the folder",
|
|
6143
6212
|
" (`./business-model.md` and any value-stream / capability pages",
|
|
6144
6213
|
" that land later). No body `# Heading` \u2014 the frontmatter",
|
|
@@ -10095,6 +10164,395 @@ var setIssueTypeProcedure = {
|
|
|
10095
10164
|
`echo "$result" | jq -c '.data.updateIssueIssueType.issue'`
|
|
10096
10165
|
].join("\n")
|
|
10097
10166
|
};
|
|
10167
|
+
var cleanMergedBranchesProcedure = {
|
|
10168
|
+
name: "clean-merged-branches.sh",
|
|
10169
|
+
description: "Analyse local branches and classify each as MERGED, UNMERGED, EMPTY, or SKIP_WORKTREE against a base branch (default: main). Analysis-only \u2014 never deletes branches. Handles squash merges via content equality.",
|
|
10170
|
+
content: [
|
|
10171
|
+
"#!/usr/bin/env bash",
|
|
10172
|
+
"# clean-merged-branches.sh \u2014 Analyse local branches against a base.",
|
|
10173
|
+
"#",
|
|
10174
|
+
"# Reports each local branch (other than HEAD and the base branch) as",
|
|
10175
|
+
"# MERGED, UNMERGED, EMPTY, or SKIP_WORKTREE. MERGED means every file the",
|
|
10176
|
+
"# branch added or modified now matches the base \u2014 so the branch content",
|
|
10177
|
+
"# is fully on the base even when the merge was a squash.",
|
|
10178
|
+
"#",
|
|
10179
|
+
"# This procedure is analysis-only. It never runs `git branch -D`. The",
|
|
10180
|
+
"# /clean-merged-branches slash-command skill wraps it with the deletion",
|
|
10181
|
+
"# prompt; the procedure itself is safe to invoke non-interactively from",
|
|
10182
|
+
"# any agent.",
|
|
10183
|
+
"#",
|
|
10184
|
+
"# Usage:",
|
|
10185
|
+
"# .claude/procedures/clean-merged-branches.sh # default base: main",
|
|
10186
|
+
"# .claude/procedures/clean-merged-branches.sh --base develop",
|
|
10187
|
+
"#",
|
|
10188
|
+
"# Output (one line per branch, stable format for grepping):",
|
|
10189
|
+
"# MERGED <branch> files=<n>",
|
|
10190
|
+
"# UNMERGED <branch> files=<n> differs=<n>",
|
|
10191
|
+
"# EMPTY <branch>",
|
|
10192
|
+
"# SKIP_WORKTREE <branch> worktree=<path>",
|
|
10193
|
+
"#",
|
|
10194
|
+
"# Exit codes:",
|
|
10195
|
+
"# 0 \u2014 analysis completed (whether or not any MERGED branches were found)",
|
|
10196
|
+
"# 2 \u2014 argument error",
|
|
10197
|
+
"# 3 \u2014 required command not on PATH",
|
|
10198
|
+
"# 4 \u2014 not a git repository",
|
|
10199
|
+
"",
|
|
10200
|
+
"set -uo pipefail",
|
|
10201
|
+
"",
|
|
10202
|
+
"# \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
10203
|
+
"",
|
|
10204
|
+
"err() {",
|
|
10205
|
+
' printf "clean-merged-branches.sh: %s\\n" "$*" >&2',
|
|
10206
|
+
"}",
|
|
10207
|
+
"",
|
|
10208
|
+
"usage() {",
|
|
10209
|
+
" cat >&2 <<'USAGE'",
|
|
10210
|
+
"Usage: clean-merged-branches.sh [--base <name>]",
|
|
10211
|
+
"",
|
|
10212
|
+
" --base <name> Base branch to compare against (default: main; falls",
|
|
10213
|
+
" back to whatever origin/HEAD points at if main is",
|
|
10214
|
+
" not present locally).",
|
|
10215
|
+
"",
|
|
10216
|
+
"Reports MERGED / UNMERGED / EMPTY / SKIP_WORKTREE for every local",
|
|
10217
|
+
"branch other than HEAD and the base branch. Does not delete anything.",
|
|
10218
|
+
"USAGE",
|
|
10219
|
+
"}",
|
|
10220
|
+
"",
|
|
10221
|
+
"# \u2500\u2500 argument parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
10222
|
+
"",
|
|
10223
|
+
'base=""',
|
|
10224
|
+
"while [[ $# -gt 0 ]]; do",
|
|
10225
|
+
' case "$1" in',
|
|
10226
|
+
" --base)",
|
|
10227
|
+
' if [[ $# -lt 2 || -z "${2:-}" ]]; then',
|
|
10228
|
+
' err "--base requires a value"',
|
|
10229
|
+
" usage",
|
|
10230
|
+
" exit 2",
|
|
10231
|
+
" fi",
|
|
10232
|
+
' base="$2"',
|
|
10233
|
+
" shift 2",
|
|
10234
|
+
" ;;",
|
|
10235
|
+
" -h|--help)",
|
|
10236
|
+
" usage",
|
|
10237
|
+
" exit 0",
|
|
10238
|
+
" ;;",
|
|
10239
|
+
" *)",
|
|
10240
|
+
' err "unknown argument: $1"',
|
|
10241
|
+
" usage",
|
|
10242
|
+
" exit 2",
|
|
10243
|
+
" ;;",
|
|
10244
|
+
" esac",
|
|
10245
|
+
"done",
|
|
10246
|
+
"",
|
|
10247
|
+
"# \u2500\u2500 dependency checks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
10248
|
+
"",
|
|
10249
|
+
"for cmd in git; do",
|
|
10250
|
+
' if ! command -v "$cmd" >/dev/null 2>&1; then',
|
|
10251
|
+
' err "required command not found on PATH: $cmd"',
|
|
10252
|
+
" exit 3",
|
|
10253
|
+
" fi",
|
|
10254
|
+
"done",
|
|
10255
|
+
"",
|
|
10256
|
+
"# \u2500\u2500 repo + base resolution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
10257
|
+
"",
|
|
10258
|
+
"if ! git rev-parse --git-dir >/dev/null 2>&1; then",
|
|
10259
|
+
' err "not inside a git repository"',
|
|
10260
|
+
" exit 4",
|
|
10261
|
+
"fi",
|
|
10262
|
+
"",
|
|
10263
|
+
"# Default base = main if it exists locally, otherwise whatever",
|
|
10264
|
+
"# origin/HEAD points at (e.g. master, trunk, develop).",
|
|
10265
|
+
'if [[ -z "$base" ]]; then',
|
|
10266
|
+
' if git show-ref --verify --quiet "refs/heads/main"; then',
|
|
10267
|
+
' base="main"',
|
|
10268
|
+
" else",
|
|
10269
|
+
" origin_head=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || true)",
|
|
10270
|
+
' if [[ -n "$origin_head" ]]; then',
|
|
10271
|
+
' base="${origin_head#origin/}"',
|
|
10272
|
+
" else",
|
|
10273
|
+
' base="main"',
|
|
10274
|
+
" fi",
|
|
10275
|
+
" fi",
|
|
10276
|
+
"fi",
|
|
10277
|
+
"",
|
|
10278
|
+
"# Verify the chosen base actually exists as a local branch.",
|
|
10279
|
+
'if ! git show-ref --verify --quiet "refs/heads/$base"; then',
|
|
10280
|
+
` err "base branch '$base' does not exist locally"`,
|
|
10281
|
+
" exit 4",
|
|
10282
|
+
"fi",
|
|
10283
|
+
"",
|
|
10284
|
+
"# Refresh remote-tracking refs and prune deleted upstream branches",
|
|
10285
|
+
"# so the gone-upstream signal is fresh (best effort \u2014 never fatal).",
|
|
10286
|
+
"git fetch --prune origin >/dev/null 2>&1 || true",
|
|
10287
|
+
"",
|
|
10288
|
+
'current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")',
|
|
10289
|
+
"",
|
|
10290
|
+
"# \u2500\u2500 worktree map (branch -> worktree path, excluding the current) \u2500\u2500\u2500",
|
|
10291
|
+
"",
|
|
10292
|
+
"# Use git worktree list --porcelain to detect any branch checked out in",
|
|
10293
|
+
"# a separate worktree. We must skip those because `git branch -D` would",
|
|
10294
|
+
"# refuse to delete a branch that is checked out elsewhere.",
|
|
10295
|
+
"#",
|
|
10296
|
+
"# We use parallel arrays (not an associative array) to stay compatible",
|
|
10297
|
+
"# with bash 3 \u2014 macOS still ships bash 3.2 as /bin/bash, and the script",
|
|
10298
|
+
"# is invoked via #!/usr/bin/env bash which picks whichever bash is",
|
|
10299
|
+
"# first on PATH. Lookups are O(n) but n is tiny (worktree count).",
|
|
10300
|
+
"worktree_branches=()",
|
|
10301
|
+
"worktree_paths=()",
|
|
10302
|
+
'cwd_top=$(git rev-parse --show-toplevel 2>/dev/null || echo "")',
|
|
10303
|
+
'wt_path=""',
|
|
10304
|
+
"while IFS= read -r line; do",
|
|
10305
|
+
' case "$line" in',
|
|
10306
|
+
" worktree\\ *)",
|
|
10307
|
+
' wt_path="${line#worktree }"',
|
|
10308
|
+
" ;;",
|
|
10309
|
+
" branch\\ *)",
|
|
10310
|
+
' wt_branch="${line#branch refs/heads/}"',
|
|
10311
|
+
" # Skip the worktree that matches the current top-level \u2014 that one",
|
|
10312
|
+
" # is the *current* checkout; skipping HEAD is handled separately.",
|
|
10313
|
+
' if [[ -n "$wt_path" && "$wt_path" != "$cwd_top" ]]; then',
|
|
10314
|
+
' worktree_branches+=("$wt_branch")',
|
|
10315
|
+
' worktree_paths+=("$wt_path")',
|
|
10316
|
+
" fi",
|
|
10317
|
+
' wt_path=""',
|
|
10318
|
+
" ;;",
|
|
10319
|
+
' "")',
|
|
10320
|
+
' wt_path=""',
|
|
10321
|
+
" ;;",
|
|
10322
|
+
" esac",
|
|
10323
|
+
"done < <(git worktree list --porcelain 2>/dev/null)",
|
|
10324
|
+
"",
|
|
10325
|
+
"# Echo the worktree path for the given branch, or empty string if the",
|
|
10326
|
+
"# branch is not checked out in another worktree.",
|
|
10327
|
+
"lookup_worktree() {",
|
|
10328
|
+
' local needle="$1"',
|
|
10329
|
+
" local i",
|
|
10330
|
+
' for i in "${!worktree_branches[@]}"; do',
|
|
10331
|
+
' if [[ "${worktree_branches[$i]}" == "$needle" ]]; then',
|
|
10332
|
+
` printf '%s\\n' "\${worktree_paths[$i]}"`,
|
|
10333
|
+
" return 0",
|
|
10334
|
+
" fi",
|
|
10335
|
+
" done",
|
|
10336
|
+
" return 0",
|
|
10337
|
+
"}",
|
|
10338
|
+
"",
|
|
10339
|
+
"# \u2500\u2500 walk local branches \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
10340
|
+
"",
|
|
10341
|
+
'echo "Analysing local branches against ${base}\u2026"',
|
|
10342
|
+
"echo",
|
|
10343
|
+
"",
|
|
10344
|
+
"while IFS= read -r branch; do",
|
|
10345
|
+
' [[ -z "$branch" ]] && continue',
|
|
10346
|
+
' [[ "$branch" == "$base" ]] && continue',
|
|
10347
|
+
' [[ "$branch" == "$current" ]] && continue',
|
|
10348
|
+
"",
|
|
10349
|
+
' wt=$(lookup_worktree "$branch")',
|
|
10350
|
+
' if [[ -n "$wt" ]]; then',
|
|
10351
|
+
` printf 'SKIP_WORKTREE %s worktree=%s\\n' "$branch" "$wt"`,
|
|
10352
|
+
" continue",
|
|
10353
|
+
" fi",
|
|
10354
|
+
"",
|
|
10355
|
+
' mb=$(git merge-base "$base" "$branch" 2>/dev/null || true)',
|
|
10356
|
+
' if [[ -z "$mb" ]]; then',
|
|
10357
|
+
" # No common ancestor with base \u2014 treat as UNMERGED (don't risk it).",
|
|
10358
|
+
` printf 'UNMERGED %s files=? differs=?\\n' "$branch"`,
|
|
10359
|
+
" continue",
|
|
10360
|
+
" fi",
|
|
10361
|
+
"",
|
|
10362
|
+
' files=$(git diff --name-only "$mb" "$branch" 2>/dev/null || true)',
|
|
10363
|
+
' if [[ -z "$files" ]]; then',
|
|
10364
|
+
` printf 'EMPTY %s\\n' "$branch"`,
|
|
10365
|
+
" continue",
|
|
10366
|
+
" fi",
|
|
10367
|
+
"",
|
|
10368
|
+
" # Count how many of those files still differ from the base today.",
|
|
10369
|
+
" # We use NUL-delimited git output to survive filenames with spaces.",
|
|
10370
|
+
" diff_count=0",
|
|
10371
|
+
" while IFS= read -r -d '' f; do",
|
|
10372
|
+
" diff_count=$((diff_count + 1))",
|
|
10373
|
+
' done < <(git diff -z --name-only "$branch" "$base" -- $files 2>/dev/null)',
|
|
10374
|
+
"",
|
|
10375
|
+
` total=$(echo "$files" | wc -l | tr -d ' ')`,
|
|
10376
|
+
"",
|
|
10377
|
+
' if [[ "$diff_count" -eq 0 ]]; then',
|
|
10378
|
+
` printf 'MERGED %s files=%s\\n' "$branch" "$total"`,
|
|
10379
|
+
" else",
|
|
10380
|
+
` printf 'UNMERGED %s files=%s differs=%s\\n' "$branch" "$total" "$diff_count"`,
|
|
10381
|
+
" fi",
|
|
10382
|
+
"done < <(git branch --format='%(refname:short)')",
|
|
10383
|
+
"",
|
|
10384
|
+
"exit 0"
|
|
10385
|
+
].join("\n")
|
|
10386
|
+
};
|
|
10387
|
+
var cleanMergedBranchesSkill = {
|
|
10388
|
+
name: "clean-merged-branches",
|
|
10389
|
+
description: "Report every local branch as MERGED or UNMERGED against the base (handles squash merges via content equality), then prompt the user to force-delete the MERGED list. Defaults base to main; --base overrides. Skips current branch, base branch, and any branch checked out in another worktree.",
|
|
10390
|
+
disableModelInvocation: true,
|
|
10391
|
+
userInvocable: true,
|
|
10392
|
+
platforms: { cursor: { exclude: true } },
|
|
10393
|
+
instructions: [
|
|
10394
|
+
"# Clean Merged Branches",
|
|
10395
|
+
"",
|
|
10396
|
+
"Identify local branches whose content is fully on the base branch \u2014",
|
|
10397
|
+
"including branches that were squash-merged (where `git branch -d`",
|
|
10398
|
+
"refuses because the branch commits are not ancestors of the base) \u2014",
|
|
10399
|
+
"report them to the user, and force-delete them after explicit",
|
|
10400
|
+
"confirmation.",
|
|
10401
|
+
"",
|
|
10402
|
+
"## Usage",
|
|
10403
|
+
"",
|
|
10404
|
+
"```",
|
|
10405
|
+
"/clean-merged-branches # default base: main",
|
|
10406
|
+
"/clean-merged-branches --base develop # custom base branch",
|
|
10407
|
+
"```",
|
|
10408
|
+
"",
|
|
10409
|
+
"### Flags",
|
|
10410
|
+
"",
|
|
10411
|
+
"- **`--base <name>`** \u2014 base branch to compare against. Defaults to",
|
|
10412
|
+
" `main`; falls back to whatever `origin/HEAD` points at if `main`",
|
|
10413
|
+
" does not exist locally.",
|
|
10414
|
+
"",
|
|
10415
|
+
"## What This Skill Does",
|
|
10416
|
+
"",
|
|
10417
|
+
"1. **Analyse.** Runs `.claude/procedures/clean-merged-branches.sh`",
|
|
10418
|
+
" (passing through any `--base` flag). The procedure walks every",
|
|
10419
|
+
" local branch and prints one stable log line per branch:",
|
|
10420
|
+
" - `MERGED <branch> files=<n>` \u2014 every file the branch added",
|
|
10421
|
+
" or modified now matches the base. Safe to delete.",
|
|
10422
|
+
" - `UNMERGED <branch> files=<n> differs=<n>` \u2014 branch content",
|
|
10423
|
+
" still differs from the base. Do NOT delete.",
|
|
10424
|
+
" - `EMPTY <branch>` \u2014 branch has no file changes vs. its",
|
|
10425
|
+
" merge-base. Safe to delete.",
|
|
10426
|
+
" - `SKIP_WORKTREE <branch> worktree=<path>` \u2014 branch is checked",
|
|
10427
|
+
" out in another worktree. Skipped (cannot be deleted while it's",
|
|
10428
|
+
" in use).",
|
|
10429
|
+
"",
|
|
10430
|
+
"2. **Confirm.** If the report turns up at least one `MERGED` (or",
|
|
10431
|
+
" `EMPTY`) branch, print the exact deletion list and a single",
|
|
10432
|
+
" `[y/N]` prompt. Empty input defaults to **no** and aborts.",
|
|
10433
|
+
"",
|
|
10434
|
+
"3. **Delete.** On explicit `y` / `Y`, run `git branch -D <branch>`",
|
|
10435
|
+
" for each confirmed branch and emit one `DELETED <branch>` line",
|
|
10436
|
+
" per success. On any other answer (including empty input),",
|
|
10437
|
+
" abort with `Aborted. No branches deleted.` and exit cleanly.",
|
|
10438
|
+
"",
|
|
10439
|
+
"If the report finds zero deletable branches, exit cleanly without",
|
|
10440
|
+
"prompting \u2014 there is nothing to confirm.",
|
|
10441
|
+
"",
|
|
10442
|
+
"## Behaviour Guarantees",
|
|
10443
|
+
"",
|
|
10444
|
+
"- **Always skip the current branch and the base branch.** The",
|
|
10445
|
+
" procedure enforces this; the skill never needs to filter again.",
|
|
10446
|
+
"- **Never delete without an explicit `y`.** The default on empty",
|
|
10447
|
+
" input is no. The skill never runs `git branch -D` until after",
|
|
10448
|
+
" the prompt returns `y` or `Y`.",
|
|
10449
|
+
"- **Content equality is the proof.** A branch classifies as",
|
|
10450
|
+
" `MERGED` when every file it added or modified matches the base",
|
|
10451
|
+
" today \u2014 handles squash merges, file deletions, and renames.",
|
|
10452
|
+
" The `[origin/<branch>: gone]` indicator is a useful secondary",
|
|
10453
|
+
" signal but is NOT the deletion gate.",
|
|
10454
|
+
"- **Safe across worktrees.** Branches checked out in another",
|
|
10455
|
+
" worktree are logged as `SKIP_WORKTREE` and excluded from the",
|
|
10456
|
+
" deletion list.",
|
|
10457
|
+
"",
|
|
10458
|
+
"## Output",
|
|
10459
|
+
"",
|
|
10460
|
+
"The procedure's per-branch log lines first, then (if any deletable",
|
|
10461
|
+
"branches exist) the confirmation prompt, then one `DELETED <branch>`",
|
|
10462
|
+
"line per successful deletion or `Aborted. No branches deleted.`",
|
|
10463
|
+
"on abort.",
|
|
10464
|
+
"",
|
|
10465
|
+
"## Implementation Recipe",
|
|
10466
|
+
"",
|
|
10467
|
+
"```bash",
|
|
10468
|
+
"# 1. Run the analysis-only procedure and capture its output.",
|
|
10469
|
+
'report=$(.claude/procedures/clean-merged-branches.sh "$@")',
|
|
10470
|
+
'printf "%s\\n" "$report"',
|
|
10471
|
+
"",
|
|
10472
|
+
"# 2. Extract the MERGED + EMPTY branch names from the report.",
|
|
10473
|
+
"mergeable=()",
|
|
10474
|
+
"while IFS= read -r line; do",
|
|
10475
|
+
' case "$line" in',
|
|
10476
|
+
' "MERGED "*|"EMPTY "*)',
|
|
10477
|
+
" # The branch name is the second whitespace-delimited token.",
|
|
10478
|
+
` branch=$(echo "$line" | awk '{print $2}')`,
|
|
10479
|
+
' [[ -n "$branch" ]] && mergeable+=("$branch")',
|
|
10480
|
+
" ;;",
|
|
10481
|
+
" esac",
|
|
10482
|
+
'done <<< "$report"',
|
|
10483
|
+
"",
|
|
10484
|
+
"# 3. If nothing to delete, exit cleanly without prompting.",
|
|
10485
|
+
"if [[ ${#mergeable[@]} -eq 0 ]]; then",
|
|
10486
|
+
" echo",
|
|
10487
|
+
' echo "No merged branches to delete."',
|
|
10488
|
+
" exit 0",
|
|
10489
|
+
"fi",
|
|
10490
|
+
"",
|
|
10491
|
+
"# 4. Show the list and prompt the user once.",
|
|
10492
|
+
"echo",
|
|
10493
|
+
'echo "The following ${#mergeable[@]} branches are safe to delete:"',
|
|
10494
|
+
`printf ' %s\\n' "\${mergeable[@]}"`,
|
|
10495
|
+
"echo",
|
|
10496
|
+
'read -r -p "Force-delete all ${#mergeable[@]} with git branch -D? [y/N] " answer',
|
|
10497
|
+
"",
|
|
10498
|
+
"# 5. On explicit y/Y only, delete each branch.",
|
|
10499
|
+
'if [[ "$answer" == "y" || "$answer" == "Y" ]]; then',
|
|
10500
|
+
' for b in "${mergeable[@]}"; do',
|
|
10501
|
+
' if git branch -D "$b" >/dev/null 2>&1; then',
|
|
10502
|
+
' echo "DELETED $b"',
|
|
10503
|
+
" else",
|
|
10504
|
+
' echo "DELETE_FAILED $b" >&2',
|
|
10505
|
+
" fi",
|
|
10506
|
+
" done",
|
|
10507
|
+
"else",
|
|
10508
|
+
' echo "Aborted. No branches deleted."',
|
|
10509
|
+
"fi",
|
|
10510
|
+
"```",
|
|
10511
|
+
"",
|
|
10512
|
+
"## Composability",
|
|
10513
|
+
"",
|
|
10514
|
+
"The procedure (`.claude/procedures/clean-merged-branches.sh`) is",
|
|
10515
|
+
"analysis-only and safe to invoke from any agent. The skill is the",
|
|
10516
|
+
"interactive entry point \u2014 use it when a human is at the keyboard.",
|
|
10517
|
+
"Background workers (orchestrator, maintenance-audit) should call",
|
|
10518
|
+
"the procedure directly and report its output without acting on it."
|
|
10519
|
+
].join("\n"),
|
|
10520
|
+
referenceFiles: [
|
|
10521
|
+
{
|
|
10522
|
+
path: "evals/evals.json",
|
|
10523
|
+
content: JSON.stringify(
|
|
10524
|
+
{
|
|
10525
|
+
skill_name: "clean-merged-branches",
|
|
10526
|
+
evals: [
|
|
10527
|
+
{
|
|
10528
|
+
id: 1,
|
|
10529
|
+
prompt: "/clean-merged-branches",
|
|
10530
|
+
expected_output: "The skill runs the analysis-only `clean-merged-branches.sh` procedure against the current checkout's base branch (default: `main`). Each local branch other than HEAD and `main` is reported on one stable line as `MERGED`, `UNMERGED`, `EMPTY`, or `SKIP_WORKTREE` with a file count. When at least one branch classifies as `MERGED` or `EMPTY`, the skill prints the exact deletion list and prompts `Force-delete all <n> with git branch -D? [y/N]`. On explicit `y` / `Y` it force-deletes each branch with `git branch -D` and emits one `DELETED <branch>` line per success. Empty input or any other answer aborts with `Aborted. No branches deleted.` and no branches are deleted. When zero branches are deletable, the skill exits cleanly without prompting.",
|
|
10531
|
+
files: [],
|
|
10532
|
+
product_context_refs: []
|
|
10533
|
+
},
|
|
10534
|
+
{
|
|
10535
|
+
id: 2,
|
|
10536
|
+
prompt: "/clean-merged-branches --base develop",
|
|
10537
|
+
expected_output: "The skill forwards `--base develop` to the procedure, which compares every local branch (other than HEAD and `develop`) against `develop` instead of `main`. Branches whose content matches `develop` classify as `MERGED`; others as `UNMERGED`. The base-branch override is also reflected in the deletion prompt (`...with git branch -D?`), and the skill never deletes `develop` itself or the current HEAD. If `develop` does not exist as a local branch, the procedure exits non-zero with a clear diagnostic and the skill aborts without prompting.",
|
|
10538
|
+
files: [],
|
|
10539
|
+
product_context_refs: []
|
|
10540
|
+
},
|
|
10541
|
+
{
|
|
10542
|
+
id: 3,
|
|
10543
|
+
prompt: "/clean-merged-branches \u2014 I have a branch called feat/old-feature that I checked out in a sibling worktree under /tmp/work. Confirm it's skipped.",
|
|
10544
|
+
expected_output: "The procedure detects `feat/old-feature` via `git worktree list --porcelain` and reports `SKIP_WORKTREE feat/old-feature worktree=/tmp/work` instead of classifying it. The branch is excluded from the deletion list shown at the confirmation prompt. Even if the user answers `y`, the skill never runs `git branch -D feat/old-feature` because the branch is not in the mergeable list.",
|
|
10545
|
+
files: [],
|
|
10546
|
+
product_context_refs: []
|
|
10547
|
+
}
|
|
10548
|
+
]
|
|
10549
|
+
},
|
|
10550
|
+
null,
|
|
10551
|
+
2
|
|
10552
|
+
)
|
|
10553
|
+
}
|
|
10554
|
+
]
|
|
10555
|
+
};
|
|
10098
10556
|
var githubWorkflowBundle = {
|
|
10099
10557
|
name: "github-workflow",
|
|
10100
10558
|
description: "GitHub issue and PR workflow automation patterns",
|
|
@@ -10116,6 +10574,10 @@ var githubWorkflowBundle = {
|
|
|
10116
10574
|
{
|
|
10117
10575
|
name: "create-issue-workflow",
|
|
10118
10576
|
description: "Automated workflow for creating a new GitHub issue",
|
|
10577
|
+
// ALWAYS scope: users invoke "create an issue" from any
|
|
10578
|
+
// context, not only when editing agent / skill / bundle source.
|
|
10579
|
+
// Consumers that want to narrow the load can override via
|
|
10580
|
+
// `agentConfig.additionalRulePaths` or `excludeRules`.
|
|
10119
10581
|
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
10120
10582
|
content: [
|
|
10121
10583
|
"# Create Issue Workflow",
|
|
@@ -10242,9 +10704,36 @@ var githubWorkflowBundle = {
|
|
|
10242
10704
|
"- Delegate merge to the `pr-reviewer` sub-agent \u2014 do not merge manually and do not enable auto-merge directly"
|
|
10243
10705
|
].join("\n"),
|
|
10244
10706
|
tags: ["workflow"]
|
|
10707
|
+
},
|
|
10708
|
+
{
|
|
10709
|
+
name: "branch-cleanup",
|
|
10710
|
+
description: "Local-branch hygiene helpers shipped with the github-workflow bundle, including the /clean-merged-branches skill for safely force-deleting branches whose content has already merged into the base (handles squash merges).",
|
|
10711
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
10712
|
+
content: [
|
|
10713
|
+
"# Branch Cleanup",
|
|
10714
|
+
"",
|
|
10715
|
+
"Local branches accumulate after every merged PR. In squash-merge",
|
|
10716
|
+
"repositories `git branch -d` refuses to delete them because the",
|
|
10717
|
+
"commit hash on the base differs, even when the branch content is",
|
|
10718
|
+
"fully merged. The `github-workflow` bundle ships two affordances",
|
|
10719
|
+
"that use content-equality (not commit-graph reachability) to",
|
|
10720
|
+
"identify branches safe to force-delete:",
|
|
10721
|
+
"",
|
|
10722
|
+
"- `/clean-merged-branches` \u2014 interactive slash-command skill that",
|
|
10723
|
+
" classifies every local branch, prompts for confirmation, then",
|
|
10724
|
+
" runs `git branch -D` on the confirmed list. See",
|
|
10725
|
+
" `.claude/skills/clean-merged-branches/SKILL.md` for usage,",
|
|
10726
|
+
" output format, and the squash-merge verification algorithm.",
|
|
10727
|
+
"- `.claude/procedures/clean-merged-branches.sh` \u2014 analysis-only",
|
|
10728
|
+
" procedure for non-interactive agent use (orchestrator,",
|
|
10729
|
+
" maintenance-audit). NEVER deletes \u2014 only reports `MERGED` /",
|
|
10730
|
+
" `UNMERGED` / `EMPTY` / `SKIP_WORKTREE` lines."
|
|
10731
|
+
].join("\n"),
|
|
10732
|
+
tags: ["workflow"]
|
|
10245
10733
|
}
|
|
10246
10734
|
],
|
|
10247
|
-
|
|
10735
|
+
skills: [cleanMergedBranchesSkill],
|
|
10736
|
+
procedures: [setIssueTypeProcedure, cleanMergedBranchesProcedure]
|
|
10248
10737
|
};
|
|
10249
10738
|
|
|
10250
10739
|
// src/agent/bundles/industry-discovery.ts
|
|
@@ -11881,12 +12370,14 @@ function buildMeetingAnalystSubAgent(tier) {
|
|
|
11881
12370
|
" Interest (signal threshold still applies); capture customer",
|
|
11882
12371
|
" pain points as candidate BR (not FR).",
|
|
11883
12372
|
"",
|
|
11884
|
-
"5. **
|
|
11885
|
-
" `<meetingsRoot>/insights/index.md` and",
|
|
12373
|
+
"5. **Plan the `insights/` tree index update \u2014 do not commit it",
|
|
12374
|
+
" yet.** Ensure `<meetingsRoot>/insights/index.md` and",
|
|
11886
12375
|
" `<meetingsRoot>/insights/{type}/index.md` exist (create them",
|
|
11887
|
-
" following the `section-index` convention if missing)
|
|
11888
|
-
"
|
|
11889
|
-
"
|
|
12376
|
+
" following the `section-index` convention if missing). Note",
|
|
12377
|
+
" the basename row this phase will eventually append, but",
|
|
12378
|
+
" **defer** the row insert and its commit to step 8 below so",
|
|
12379
|
+
" the shared-index commit can land in the smallest possible",
|
|
12380
|
+
" window before push.",
|
|
11890
12381
|
"",
|
|
11891
12382
|
"6. **Create downstream phase issues** using `gh issue create`:",
|
|
11892
12383
|
" - Always create a `meeting:notes` issue (blocked on this extract issue)",
|
|
@@ -11897,7 +12388,35 @@ function buildMeetingAnalystSubAgent(tier) {
|
|
|
11897
12388
|
" - Always create a `meeting:link` issue \u2014 blocked on the draft issue if one",
|
|
11898
12389
|
" was created, otherwise blocked on the notes issue",
|
|
11899
12390
|
"",
|
|
11900
|
-
"7. Commit
|
|
12391
|
+
"7. **Commit the extraction content first.** Stage and commit",
|
|
12392
|
+
" the `insights/{type}/<basename>.md` file (and any newly",
|
|
12393
|
+
" created `insights/index.md` / `insights/{type}/index.md`",
|
|
12394
|
+
" stub pages from step 5 that do not yet exist on the remote)",
|
|
12395
|
+
" in a single focused commit. **Do not push yet.**",
|
|
12396
|
+
"",
|
|
12397
|
+
"8. **Defer the shared-index row insert to a final pre-push",
|
|
12398
|
+
" commit.** Per the `shared-editing-safety` rule's",
|
|
12399
|
+
" **Defer Shared-Index Commit to Final Pre-Push Step**",
|
|
12400
|
+
" subsection, the index update for an existing",
|
|
12401
|
+
" `insights/{type}/index.md` is a **shared-index row insert**",
|
|
12402
|
+
" that races other parallel meeting sessions writing rows into",
|
|
12403
|
+
" the same partition file. Apply the deferred sequence:",
|
|
12404
|
+
"",
|
|
12405
|
+
" ```bash",
|
|
12406
|
+
" git fetch origin",
|
|
12407
|
+
" git pull --rebase origin <default-branch>",
|
|
12408
|
+
" ```",
|
|
12409
|
+
"",
|
|
12410
|
+
" Re-read `insights/{type}/index.md` from the now-up-to-date",
|
|
12411
|
+
" working tree, re-compute the alphabetical insert position",
|
|
12412
|
+
" for the current meeting's basename row (another session may",
|
|
12413
|
+
" have appended a sibling row in the meantime), insert exactly",
|
|
12414
|
+
" one row, and commit the index edit in its **own focused",
|
|
12415
|
+
" commit** whose only file is the shared index. Run the",
|
|
12416
|
+
" commit-path verification step (`git show HEAD:<index-path>`",
|
|
12417
|
+
" + grep count) against that commit before pushing.",
|
|
12418
|
+
"",
|
|
12419
|
+
"9. **Push and close.** Push the branch and close the extract issue.",
|
|
11901
12420
|
"",
|
|
11902
12421
|
"---",
|
|
11903
12422
|
"",
|
|
@@ -11957,13 +12476,41 @@ function buildMeetingAnalystSubAgent(tier) {
|
|
|
11957
12476
|
" - Action Items (table: who, what, when)",
|
|
11958
12477
|
" - Open Questions",
|
|
11959
12478
|
" - Follow-up items",
|
|
11960
|
-
"4. **
|
|
11961
|
-
" `<meetingsRoot>/notes/index.md` and",
|
|
12479
|
+
"4. **Plan the `notes/` tree index update \u2014 do not commit it",
|
|
12480
|
+
" yet.** Ensure `<meetingsRoot>/notes/index.md` and",
|
|
11962
12481
|
" `<meetingsRoot>/notes/{type}/index.md` exist (create them",
|
|
11963
|
-
" following the `section-index` convention if missing)
|
|
11964
|
-
"
|
|
11965
|
-
"
|
|
11966
|
-
"
|
|
12482
|
+
" following the `section-index` convention if missing). Note",
|
|
12483
|
+
" the basename row this phase will eventually append, but",
|
|
12484
|
+
" **defer** the row insert and its commit to step 6 below so",
|
|
12485
|
+
" the shared-index commit can land in the smallest possible",
|
|
12486
|
+
" window before push.",
|
|
12487
|
+
"5. **Commit the notes content first.** Stage and commit the",
|
|
12488
|
+
" `notes/{type}/<basename>.md` file (and any newly created",
|
|
12489
|
+
" `notes/index.md` / `notes/{type}/index.md` stub pages from",
|
|
12490
|
+
" step 4 that do not yet exist on the remote) in a single",
|
|
12491
|
+
" focused commit. **Do not push yet.**",
|
|
12492
|
+
"6. **Defer the shared-index row insert to a final pre-push",
|
|
12493
|
+
" commit.** Per the `shared-editing-safety` rule's",
|
|
12494
|
+
" **Defer Shared-Index Commit to Final Pre-Push Step**",
|
|
12495
|
+
" subsection, the index update for an existing",
|
|
12496
|
+
" `notes/{type}/index.md` is a **shared-index row insert**",
|
|
12497
|
+
" that races other parallel meeting sessions writing rows",
|
|
12498
|
+
" into the same partition file. Apply the deferred sequence:",
|
|
12499
|
+
"",
|
|
12500
|
+
" ```bash",
|
|
12501
|
+
" git fetch origin",
|
|
12502
|
+
" git pull --rebase origin <default-branch>",
|
|
12503
|
+
" ```",
|
|
12504
|
+
"",
|
|
12505
|
+
" Re-read `notes/{type}/index.md` from the now-up-to-date",
|
|
12506
|
+
" working tree, re-compute the alphabetical insert position",
|
|
12507
|
+
" for the current meeting's basename row (another session may",
|
|
12508
|
+
" have appended a sibling row in the meantime), insert exactly",
|
|
12509
|
+
" one row, and commit the index edit in its **own focused",
|
|
12510
|
+
" commit** whose only file is the shared index. Run the",
|
|
12511
|
+
" commit-path verification step against that commit before",
|
|
12512
|
+
" pushing.",
|
|
12513
|
+
"7. Push and close the notes issue.",
|
|
11967
12514
|
"",
|
|
11968
12515
|
"---",
|
|
11969
12516
|
"",
|
|
@@ -15605,12 +16152,25 @@ var issueWorkerSubAgent = {
|
|
|
15605
16152
|
" `file` (and optional `line`). Track `comment_id` per item so you can",
|
|
15606
16153
|
" report which items were handled and which (if any) failed.",
|
|
15607
16154
|
"",
|
|
15608
|
-
" **Synthetic rebase items.**
|
|
15609
|
-
"
|
|
15610
|
-
"
|
|
15611
|
-
"
|
|
15612
|
-
"
|
|
15613
|
-
"
|
|
16155
|
+
" **Synthetic rebase items.** Two `comment_id` values flag the",
|
|
16156
|
+
" reviewer's signal that the PR's head branch is BEHIND the default",
|
|
16157
|
+
" branch with merge conflicts that `gh pr update-branch` could not",
|
|
16158
|
+
" resolve. For either item the work is **not** an editorial change",
|
|
16159
|
+
" \u2014 it is a rebase plus conflict resolution. The two ids select",
|
|
16160
|
+
" different recipes:",
|
|
16161
|
+
"",
|
|
16162
|
+
" - `synthetic:rebase-behind-main` \u2014 generic conflict. Resolve",
|
|
16163
|
+
" each conflicting file by hand (read both sides, reconcile,",
|
|
16164
|
+
" stage), then `git rebase --continue` until the rebase",
|
|
16165
|
+
" completes.",
|
|
16166
|
+
" - `synthetic:rebase-shared-index` \u2014 every conflicting file is a",
|
|
16167
|
+
" row-insert race on a shared-index file (registry / index /",
|
|
16168
|
+
" feature-matrix under a Starlight content root). Apply the",
|
|
16169
|
+
" explicit re-insert recipe carried in the item's `instruction`",
|
|
16170
|
+
" field \u2014 do **not** hand-merge.",
|
|
16171
|
+
"",
|
|
16172
|
+
" **For `synthetic:rebase-behind-main`.** Run the following",
|
|
16173
|
+
" sequence:",
|
|
15614
16174
|
"",
|
|
15615
16175
|
" ```bash",
|
|
15616
16176
|
" git fetch origin",
|
|
@@ -15634,6 +16194,75 @@ var issueWorkerSubAgent = {
|
|
|
15634
16194
|
" history. Push with a regular non-force `git push origin <branch>`",
|
|
15635
16195
|
" and report the rebased head SHA as the worker's commit.",
|
|
15636
16196
|
"",
|
|
16197
|
+
" **For `synthetic:rebase-shared-index`.** Apply the typed",
|
|
16198
|
+
" re-insert recipe step-by-step. Read the recipe verbatim from the",
|
|
16199
|
+
" item's `instruction` field; the steps below summarise the",
|
|
16200
|
+
" contract the reviewer encodes and the precondition guards you",
|
|
16201
|
+
" must enforce:",
|
|
16202
|
+
"",
|
|
16203
|
+
" 1. **Pull and rebase** onto the default branch:",
|
|
16204
|
+
"",
|
|
16205
|
+
" ```bash",
|
|
16206
|
+
" git fetch origin",
|
|
16207
|
+
" git pull --rebase origin {{repository.defaultBranch}}",
|
|
16208
|
+
" ```",
|
|
16209
|
+
"",
|
|
16210
|
+
" 2. **For each conflicting file**, inspect the conflict markers",
|
|
16211
|
+
" against the shared-index glob set and the precondition",
|
|
16212
|
+
" guards. The shared-index globs (sourced from the",
|
|
16213
|
+
" `shared-editing-safety` rule) are:",
|
|
16214
|
+
"",
|
|
16215
|
+
...DEFAULT_SHARED_INDEX_PATHS.map((p) => ` - \`${p}\``),
|
|
16216
|
+
"",
|
|
16217
|
+
" **Precondition guards.** Before re-inserting, confirm every",
|
|
16218
|
+
" `<<<<<<<` / `=======` / `>>>>>>>` hunk in the file touches",
|
|
16219
|
+
" only data rows (lines starting with `| ` that are not the",
|
|
16220
|
+
" table header or `|---|---|` separator). If any hunk touches",
|
|
16221
|
+
" the frontmatter (lines between the opening / closing `---`",
|
|
16222
|
+
" fences), the page H1, surrounding prose paragraphs, the",
|
|
16223
|
+
" table header row, or the separator row, **stop**, run",
|
|
16224
|
+
" `git rebase --abort`, and record the item as `failed` with",
|
|
16225
|
+
" the structured marker `BLOCKED <reason>` (e.g.",
|
|
16226
|
+
" `BLOCKED conflict touches table header in <path>`). Do not",
|
|
16227
|
+
" hand-merge \u2014 the typed recipe applies only to pure",
|
|
16228
|
+
" row-insert races.",
|
|
16229
|
+
"",
|
|
16230
|
+
" When the guards pass, read the rebased version of the file",
|
|
16231
|
+
" (it now contains the other PR's row), extract this PR's row",
|
|
16232
|
+
" from the `<<<<<<<` side of the conflict markers, re-insert",
|
|
16233
|
+
" that row in declared sort order using the file's documented",
|
|
16234
|
+
" sort key (alphabetical on the first column by default), and",
|
|
16235
|
+
" stage the file:",
|
|
16236
|
+
"",
|
|
16237
|
+
" ```bash",
|
|
16238
|
+
" git add <path>",
|
|
16239
|
+
" ```",
|
|
16240
|
+
"",
|
|
16241
|
+
" 3. **Run the commit-path verification** for each re-inserted",
|
|
16242
|
+
" row. The marker must appear exactly once in the staged file",
|
|
16243
|
+
" (zero means missing, more than one means duplicated by a",
|
|
16244
|
+
" mis-merge):",
|
|
16245
|
+
"",
|
|
16246
|
+
" ```bash",
|
|
16247
|
+
' count=$(git show :<path> | grep -Fc "<row-unique-marker>")',
|
|
16248
|
+
' [ "$count" = "1" ] || { echo "BLOCKED verification failed for <path>"; exit 1; }',
|
|
16249
|
+
" ```",
|
|
16250
|
+
"",
|
|
16251
|
+
" 4. **Continue the rebase and push** with a non-force push once",
|
|
16252
|
+
" every file is staged and verified:",
|
|
16253
|
+
"",
|
|
16254
|
+
" ```bash",
|
|
16255
|
+
" git rebase --continue",
|
|
16256
|
+
" git push origin <branch>",
|
|
16257
|
+
" ```",
|
|
16258
|
+
"",
|
|
16259
|
+
" On any `BLOCKED` precondition failure, exit non-zero with the",
|
|
16260
|
+
" `BLOCKED <reason>` line, run `git rebase --abort`, record the",
|
|
16261
|
+
" item as `failed`, and proceed to the report step. The reviewer",
|
|
16262
|
+
" will see the `failed` outcome on the next pass and fall through",
|
|
16263
|
+
" to the human-required hand-off via `review:awaiting-human`. Do",
|
|
16264
|
+
" not force-push under any circumstance.",
|
|
16265
|
+
"",
|
|
15637
16266
|
"4. When complete, prepare a short structured report (PR number, commit",
|
|
15638
16267
|
" SHAs you will push, items handled by `comment_id`, items that failed",
|
|
15639
16268
|
" to apply) \u2014 you will return this after Phase 6.",
|
|
@@ -15697,11 +16326,11 @@ var issueWorkerSubAgent = {
|
|
|
15697
16326
|
"",
|
|
15698
16327
|
"**Synthetic-rebase items skip the `fix(review)` commit.** When the",
|
|
15699
16328
|
"fix-list contained a `comment_id` of `synthetic:rebase-behind-main`",
|
|
15700
|
-
"and you completed the rebase in
|
|
15701
|
-
"
|
|
15702
|
-
"wrap in a `fix(review)`
|
|
15703
|
-
"`git
|
|
15704
|
-
"directly:",
|
|
16329
|
+
"or `synthetic:rebase-shared-index` and you completed the rebase in",
|
|
16330
|
+
"Phase 4, the rebased commit history is itself the deliverable \u2014",
|
|
16331
|
+
"there is no per-item editorial change to wrap in a `fix(review)`",
|
|
16332
|
+
"commit. Skip `git add` / `git commit` / `git pull --rebase` for",
|
|
16333
|
+
"that item and push the rebased branch directly:",
|
|
15705
16334
|
"",
|
|
15706
16335
|
"```bash",
|
|
15707
16336
|
"git push origin <branch-name>",
|
|
@@ -15979,7 +16608,15 @@ var orchestratorBundle = {
|
|
|
15979
16608
|
{
|
|
15980
16609
|
name: "orchestrator-conventions",
|
|
15981
16610
|
description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
|
|
15982
|
-
scope: AGENT_RULE_SCOPE.
|
|
16611
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
16612
|
+
// Bundle defaults exclude paths into configulator's own source
|
|
16613
|
+
// (only meaningful when configulator is a workspace package).
|
|
16614
|
+
// codedrifters/packages restores them via
|
|
16615
|
+
// `agentConfig.additionalRulePaths`.
|
|
16616
|
+
filePatterns: [
|
|
16617
|
+
".claude/agents/orchestrator.md",
|
|
16618
|
+
".claude/scheduled-tasks/**"
|
|
16619
|
+
],
|
|
15983
16620
|
content: buildOrchestratorConventionsContent(
|
|
15984
16621
|
DEFAULT_AGENT_TIERS,
|
|
15985
16622
|
resolveScopeGate(),
|
|
@@ -15988,7 +16625,6 @@ var orchestratorBundle = {
|
|
|
15988
16625
|
resolveUnblockDependents()
|
|
15989
16626
|
),
|
|
15990
16627
|
platforms: {
|
|
15991
|
-
claude: { target: "claude-md" },
|
|
15992
16628
|
cursor: { exclude: true }
|
|
15993
16629
|
},
|
|
15994
16630
|
tags: ["workflow"]
|
|
@@ -17112,6 +17748,32 @@ var pnpmBundle = {
|
|
|
17112
17748
|
]
|
|
17113
17749
|
};
|
|
17114
17750
|
|
|
17751
|
+
// src/agent/bundles/pr-review-policy.ts
|
|
17752
|
+
var DEFAULT_PATHS_EXEMPT_FROM_SIZE = [
|
|
17753
|
+
"docs/**"
|
|
17754
|
+
];
|
|
17755
|
+
function resolvePrReviewPolicy(config) {
|
|
17756
|
+
const pathsExemptFromSize = config?.autoMerge?.pathsExemptFromSize ?? DEFAULT_PATHS_EXEMPT_FROM_SIZE;
|
|
17757
|
+
assertValidPathsExemptFromSize(pathsExemptFromSize);
|
|
17758
|
+
return {
|
|
17759
|
+
autoMerge: {
|
|
17760
|
+
pathsExemptFromSize: [...pathsExemptFromSize]
|
|
17761
|
+
}
|
|
17762
|
+
};
|
|
17763
|
+
}
|
|
17764
|
+
function validatePrReviewPolicyConfig(config) {
|
|
17765
|
+
return resolvePrReviewPolicy(config);
|
|
17766
|
+
}
|
|
17767
|
+
function assertValidPathsExemptFromSize(paths) {
|
|
17768
|
+
for (const path8 of paths) {
|
|
17769
|
+
if (typeof path8 !== "string" || path8.trim().length === 0) {
|
|
17770
|
+
throw new Error(
|
|
17771
|
+
"prReviewPolicy.autoMerge.pathsExemptFromSize entries must be non-empty strings"
|
|
17772
|
+
);
|
|
17773
|
+
}
|
|
17774
|
+
}
|
|
17775
|
+
}
|
|
17776
|
+
|
|
17115
17777
|
// src/agent/bundles/pr-review.ts
|
|
17116
17778
|
var prReviewerSubAgent = {
|
|
17117
17779
|
name: "pr-reviewer",
|
|
@@ -17302,11 +17964,16 @@ var prReviewerSubAgent = {
|
|
|
17302
17964
|
"### Step 2: Evaluate in precedence order",
|
|
17303
17965
|
"",
|
|
17304
17966
|
"Walk the following checks in order. The **first match wins** and fixes",
|
|
17305
|
-
"the mode; record the triggering condition as the `reason
|
|
17306
|
-
"
|
|
17307
|
-
"`human-required
|
|
17308
|
-
"
|
|
17309
|
-
"
|
|
17967
|
+
"the mode; record the triggering condition as the `reason` **and**",
|
|
17968
|
+
"record the numeric **`matched_rule`** index (one of `2`, `3`, `4`,",
|
|
17969
|
+
"`5`, or `6` for any human-required match \u2014 see the rule numbers",
|
|
17970
|
+
"below) so later phases can branch on which precedence rule fired.",
|
|
17971
|
+
"When rule 1 (force-auto label) or rule 7 (default) fixes the mode,",
|
|
17972
|
+
"record `matched_rule: null` \u2014 only the human-required rules carry an",
|
|
17973
|
+
"actionable index. Mixed-match PRs (signals from both sides) resolve",
|
|
17974
|
+
"conservatively to `human-required` \u2014 force-auto only wins when it is",
|
|
17975
|
+
"the single highest match and no later human-required signal changes",
|
|
17976
|
+
"the outcome under step 2c.",
|
|
17310
17977
|
"",
|
|
17311
17978
|
"1. **Force-auto label** \u2014 if the PR carries any label listed under",
|
|
17312
17979
|
" `auto-merge.labels-that-force-auto` (e.g. `review:auto-ok`), set",
|
|
@@ -17323,9 +17990,18 @@ var prReviewerSubAgent = {
|
|
|
17323
17990
|
" (fetched in Phase 2) matches any entry in",
|
|
17324
17991
|
" `human-required.issue-types` (case-insensitive), set",
|
|
17325
17992
|
" `mode = human-required`.",
|
|
17326
|
-
"6. **Size thresholds** \u2014
|
|
17327
|
-
" `
|
|
17328
|
-
"
|
|
17993
|
+
"6. **Size thresholds** \u2014 first read",
|
|
17994
|
+
" `auto-merge.paths-exempt-from-size` from the policy (defaults to",
|
|
17995
|
+
" the documented carve-out list). For every file in the PR diff,",
|
|
17996
|
+
" evaluate whether the file path matches at least one glob in the",
|
|
17997
|
+
" carve-out list. If **every** changed path matches the carve-out,",
|
|
17998
|
+
" **skip rule #6** entirely and continue to rule #7 \u2014 the PR is",
|
|
17999
|
+
" exempt from the size threshold regardless of its `files` or",
|
|
18000
|
+
" `insertions` count. Otherwise (any changed path falls outside",
|
|
18001
|
+
" the carve-out list), apply the size check: if the PR exceeds",
|
|
18002
|
+
" either threshold under `human-required.size` (`files` count or",
|
|
18003
|
+
" `insertions` count), set `mode = human-required` and record the",
|
|
18004
|
+
" triggered axis (files vs. insertions) as the reason.",
|
|
17329
18005
|
"7. **Default** \u2014 if no rule above matched, apply the `default` field",
|
|
17330
18006
|
" from the policy (typically `auto-merge`).",
|
|
17331
18007
|
"",
|
|
@@ -17339,14 +18015,23 @@ var prReviewerSubAgent = {
|
|
|
17339
18015
|
"",
|
|
17340
18016
|
"### Step 3: Record the decision",
|
|
17341
18017
|
"",
|
|
17342
|
-
"Persist the evaluated mode
|
|
17343
|
-
"any downstream summary writer can
|
|
18018
|
+
"Persist the evaluated mode, the reason, and the matched-rule index",
|
|
18019
|
+
"for later phases so Phase 4 and any downstream summary writer can",
|
|
18020
|
+
"cite them:",
|
|
17344
18021
|
"",
|
|
17345
18022
|
"```",
|
|
17346
18023
|
"Review mode: <auto-merge | human-required>",
|
|
17347
18024
|
"Reason: <short explanation \u2014 label name, path+glob, issue type, size threshold, default>",
|
|
18025
|
+
"Matched rule: <2 | 3 | 4 | 5 | 6 | null>",
|
|
17348
18026
|
"```",
|
|
17349
18027
|
"",
|
|
18028
|
+
"`Matched rule` is the numeric index of the precedence rule that",
|
|
18029
|
+
"fixed the mode in Step 2. It is populated only for human-required",
|
|
18030
|
+
"matches (rules 2\u20136) \u2014 force-auto (rule 1) and default (rule 7)",
|
|
18031
|
+
"record `null`. Phase 4's `gh pr update-branch` gate consults this",
|
|
18032
|
+
"field to decide whether the size-only carve-out (rule 6) allows the",
|
|
18033
|
+
"bot to keep a human-required branch fresh against the default branch.",
|
|
18034
|
+
"",
|
|
17350
18035
|
"Phases 3 (acceptance-criteria comparison) and CI verification run",
|
|
17351
18036
|
"unchanged regardless of mode. Only the terminal action in Phase 4",
|
|
17352
18037
|
"branches on the decided mode.",
|
|
@@ -17798,9 +18483,42 @@ var prReviewerSubAgent = {
|
|
|
17798
18483
|
"gh pr view <pr-number> --json mergeStateStatus --jq '.mergeStateStatus'",
|
|
17799
18484
|
"```",
|
|
17800
18485
|
"",
|
|
17801
|
-
"
|
|
17802
|
-
"
|
|
17803
|
-
"
|
|
18486
|
+
"Before running `gh pr update-branch`, evaluate the **eligibility",
|
|
18487
|
+
"gate** below. The step runs when `mergeStateStatus == BEHIND` **AND**",
|
|
18488
|
+
"either of the following holds:",
|
|
18489
|
+
"",
|
|
18490
|
+
"- The review mode decided in Phase 2.75 is `auto-merge`, **or**",
|
|
18491
|
+
"- The review mode is `human-required` **and** `matched_rule == 6`",
|
|
18492
|
+
" (size threshold is the sole trigger that fixed the mode).",
|
|
18493
|
+
"",
|
|
18494
|
+
"All other `human-required` matches (rules 2\u20135) continue to block",
|
|
18495
|
+
"`update-branch`. Concretely, **never** run `gh pr update-branch`",
|
|
18496
|
+
"when the mode is `human-required` and any of the following fired:",
|
|
18497
|
+
"",
|
|
18498
|
+
"- rule 2 (`review:human-required` label),",
|
|
18499
|
+
"- rule 3 (any `labels-that-force-human` label such as `priority:critical`),",
|
|
18500
|
+
"- rule 4 (`human-required.paths` glob match), or",
|
|
18501
|
+
"- rule 5 (`human-required.issue-types` match).",
|
|
18502
|
+
"",
|
|
18503
|
+
"The rationale: rule 6 fires on the **volume** of the diff alone \u2014",
|
|
18504
|
+
"there is nothing about the changed paths or labels that suggests a",
|
|
18505
|
+
"human reviewer has explicit ownership of the branch's lifecycle.",
|
|
18506
|
+
"Pushing the default branch into a size-tripped human-required PR",
|
|
18507
|
+
"keeps it from sitting stale while the human is still drafting",
|
|
18508
|
+
"their review. By contrast, each of rules 2\u20135 signals a human",
|
|
18509
|
+
"reviewer who owns the branch's lifecycle; silently pushing main",
|
|
18510
|
+
"into those PRs expands the diff under review without their consent.",
|
|
18511
|
+
"",
|
|
18512
|
+
"When the gate **denies** `update-branch` (`human-required` mode and",
|
|
18513
|
+
"`matched_rule` in 2\u20135), record",
|
|
18514
|
+
"`Branch updated: not eligible (human-required by rule <N>)` and skip",
|
|
18515
|
+
"the rest of this sub-section. The human reviewer keeps branch-",
|
|
18516
|
+
"lifecycle ownership.",
|
|
18517
|
+
"",
|
|
18518
|
+
"When the gate **permits** `update-branch`, attempt to bring the head",
|
|
18519
|
+
"branch current with the default branch via `gh pr update-branch`",
|
|
18520
|
+
"(default merge strategy \u2014 **never** `--rebase`, which would rewrite",
|
|
18521
|
+
"commits on a published branch):",
|
|
17804
18522
|
"",
|
|
17805
18523
|
"```bash",
|
|
17806
18524
|
"gh pr update-branch <pr-number>",
|
|
@@ -17809,8 +18527,9 @@ var prReviewerSubAgent = {
|
|
|
17809
18527
|
"Branch on the outcome:",
|
|
17810
18528
|
"",
|
|
17811
18529
|
"- **Success** \u2014 record `Branch updated: yes` for the per-PR report and",
|
|
17812
|
-
" stop. Auto-merge will fire when required checks pass
|
|
17813
|
-
" SHA. Do **not** poll for the merge here \u2014 Phase 5
|
|
18530
|
+
" stop. Auto-merge (when enabled) will fire when required checks pass",
|
|
18531
|
+
" on the new head SHA. Do **not** poll for the merge here \u2014 Phase 5",
|
|
18532
|
+
" owns polling.",
|
|
17814
18533
|
"- **Failure for reasons other than a merge conflict** (permission",
|
|
17815
18534
|
" denied, branch protection refusing the merge commit, transient",
|
|
17816
18535
|
" network error) \u2014 record `Branch updated: failed (<reason>)`, post a",
|
|
@@ -17824,13 +18543,6 @@ var prReviewerSubAgent = {
|
|
|
17824
18543
|
"an `update-branch` attempt \u2014 every other state either has nothing to do",
|
|
17825
18544
|
"or is already gated on a different signal that Phase 5 picks up.",
|
|
17826
18545
|
"",
|
|
17827
|
-
"Never run `gh pr update-branch` on a PR whose review mode is",
|
|
17828
|
-
"`human-required`. Pushing main into a human-required PR expands the",
|
|
17829
|
-
"scope of the diff the human is reviewing without their consent. The",
|
|
17830
|
-
"`update-branch` step lives **only** under the `Mode auto-merge` branch",
|
|
17831
|
-
"of this phase \u2014 `human-required` skips straight to its hand-off block",
|
|
17832
|
-
"below.",
|
|
17833
|
-
"",
|
|
17834
18546
|
"##### Conflict-resolution delegation (BEHIND + conflicts)",
|
|
17835
18547
|
"",
|
|
17836
18548
|
"When `gh pr update-branch <pr-number>` fails because the merge would",
|
|
@@ -17844,9 +18556,13 @@ var prReviewerSubAgent = {
|
|
|
17844
18556
|
"fall through to the fallback at the end of this sub-section instead.",
|
|
17845
18557
|
"",
|
|
17846
18558
|
"1. **Review mode is `auto-merge`.** Never delegate conflict",
|
|
17847
|
-
" resolution on `human-required` PRs \u2014 pushing
|
|
17848
|
-
" them expands the diff
|
|
17849
|
-
"
|
|
18559
|
+
" resolution on `human-required` PRs \u2014 pushing worker-resolved",
|
|
18560
|
+
" merge content into them expands the diff under review without",
|
|
18561
|
+
" the human reviewer's consent. (Unlike the `update-branch` step",
|
|
18562
|
+
" above, which permits a size-only `human-required` carve-out, the",
|
|
18563
|
+
" conflict-resolution delegation flow is auto-merge-only across",
|
|
18564
|
+
" the board: a worker rebase push is a stronger branch mutation",
|
|
18565
|
+
" than the merge-commit `gh pr update-branch` performs.)",
|
|
17850
18566
|
"2. **Delegation invocation guard permits the hand-off** \u2014 the PR",
|
|
17851
18567
|
" carries the `origin:issue-worker` label, **or** the reviewer was",
|
|
17852
18568
|
" invoked with `--allow-human-author`. The same guard used for the",
|
|
@@ -17871,8 +18587,53 @@ var prReviewerSubAgent = {
|
|
|
17871
18587
|
" and conflicts there should be resolved by re-running synth, not by",
|
|
17872
18588
|
" merging the conflict markers.",
|
|
17873
18589
|
"",
|
|
17874
|
-
"When every guard above passes,
|
|
17875
|
-
"
|
|
18590
|
+
"When every guard above passes, **classify each conflicting file**",
|
|
18591
|
+
"before composing the fix-list. The classification picks one of two",
|
|
18592
|
+
"typed recipes \u2014 a precise `shared-index` resolver when every",
|
|
18593
|
+
"conflict is a row-insert race on a registry / index / feature-matrix",
|
|
18594
|
+
"file, or the generic rebase recipe in every other case.",
|
|
18595
|
+
"",
|
|
18596
|
+
"**Classification step.** For each conflicting path reported by the",
|
|
18597
|
+
"failed `update-branch`, decide whether the file is `shared-index` or",
|
|
18598
|
+
"`generic` against these criteria (the shared-index glob set comes",
|
|
18599
|
+
"from the `shared-editing-safety` rule \u2014 see the bundle's",
|
|
18600
|
+
"`shared-editing.ts` for the canonical constant):",
|
|
18601
|
+
"",
|
|
18602
|
+
"- `shared-index` \u2014 the path matches one of the shared-editing glob",
|
|
18603
|
+
" patterns:",
|
|
18604
|
+
...DEFAULT_SHARED_INDEX_PATHS.map((p) => ` - \`${p}\``),
|
|
18605
|
+
" **AND** the conflict diff is bounded to row insertions only.",
|
|
18606
|
+
" Inspect the merge conflict diff for the file: every `<<<<<<<` /",
|
|
18607
|
+
" `=======` / `>>>>>>>` hunk must touch only data rows (lines that",
|
|
18608
|
+
" start with `| ` and are not the table header or the `|---|---|`",
|
|
18609
|
+
" separator). The conflict must **not** touch:",
|
|
18610
|
+
" - The frontmatter block (lines between the opening and",
|
|
18611
|
+
" closing `---` fences at the top of the file).",
|
|
18612
|
+
" - The page H1 or any surrounding prose paragraphs.",
|
|
18613
|
+
" - The table header row (the first `| Column | ... |` row of",
|
|
18614
|
+
" any table).",
|
|
18615
|
+
" - The separator row (`|---|---|...|`).",
|
|
18616
|
+
" If the conflict hunks touch any of the above, classify the file",
|
|
18617
|
+
" as `generic` \u2014 the mechanical row-insert recipe cannot safely",
|
|
18618
|
+
" reconcile structural edits.",
|
|
18619
|
+
"- `generic` \u2014 anything else (path outside the shared-editing glob",
|
|
18620
|
+
" set, **or** conflict hunks touch the frontmatter / header /",
|
|
18621
|
+
" separator / surrounding prose).",
|
|
18622
|
+
"",
|
|
18623
|
+
"**Branching emit step.**",
|
|
18624
|
+
"",
|
|
18625
|
+
"- **Every conflicting file is `shared-index`** \u2014 emit the typed",
|
|
18626
|
+
' fix-list item with `comment_id: "synthetic:rebase-shared-index"`',
|
|
18627
|
+
" carrying the explicit re-insert recipe (see step 3 below). The",
|
|
18628
|
+
" worker mechanically re-inserts each row in declared sort order",
|
|
18629
|
+
" against the rebased file.",
|
|
18630
|
+
"- **Any conflicting file is `generic`** \u2014 emit the existing generic",
|
|
18631
|
+
' fix-list item with `comment_id: "synthetic:rebase-behind-main"`',
|
|
18632
|
+
" (see step 3 below). The worker performs a hand-merge of the",
|
|
18633
|
+
" conflict markers.",
|
|
18634
|
+
"",
|
|
18635
|
+
"Hand off to `issue-worker` with the chosen single synthetic",
|
|
18636
|
+
"fix-list item:",
|
|
17876
18637
|
"",
|
|
17877
18638
|
"1. **Disable auto-merge** so a fast CI pass cannot land the PR",
|
|
17878
18639
|
" mid-delegation (idempotent \u2014 safe no-op when auto-merge was never",
|
|
@@ -17891,9 +18652,18 @@ var prReviewerSubAgent = {
|
|
|
17891
18652
|
" ```",
|
|
17892
18653
|
"",
|
|
17893
18654
|
"3. **Post a fix-list comment** containing exactly one synthetic item",
|
|
17894
|
-
" describing the rebase. The `comment_id`
|
|
17895
|
-
"
|
|
17896
|
-
"
|
|
18655
|
+
" describing the rebase. The `comment_id` field selects the typed",
|
|
18656
|
+
" recipe:",
|
|
18657
|
+
"",
|
|
18658
|
+
" - `synthetic:rebase-behind-main` \u2014 generic conflict; the worker",
|
|
18659
|
+
" resolves conflicting hunks by hand.",
|
|
18660
|
+
" - `synthetic:rebase-shared-index` \u2014 every conflict is a pure",
|
|
18661
|
+
" row-insert race on a shared-index file; the worker re-inserts",
|
|
18662
|
+
" each row in declared sort order against the rebased file.",
|
|
18663
|
+
"",
|
|
18664
|
+
" The next reviewer pass identifies the item by its `comment_id`.",
|
|
18665
|
+
"",
|
|
18666
|
+
" **Generic shape (`synthetic:rebase-behind-main`):**",
|
|
17897
18667
|
"",
|
|
17898
18668
|
" ```markdown",
|
|
17899
18669
|
" ## Reviewer: fix list for @issue-worker",
|
|
@@ -17912,6 +18682,27 @@ var prReviewerSubAgent = {
|
|
|
17912
18682
|
" ```",
|
|
17913
18683
|
" ```",
|
|
17914
18684
|
"",
|
|
18685
|
+
" **Typed shape (`synthetic:rebase-shared-index`).** The",
|
|
18686
|
+
" `instruction` field carries the full re-insert recipe \u2014 the",
|
|
18687
|
+
" worker reads it imperatively, so spell every step out:",
|
|
18688
|
+
"",
|
|
18689
|
+
" ```markdown",
|
|
18690
|
+
" ## Reviewer: fix list for @issue-worker",
|
|
18691
|
+
"",
|
|
18692
|
+
" - [ ] @reviewer \u2014 rebase onto origin/<default-branch> and re-insert shared-index rows in: <space-separated list of conflicting files>",
|
|
18693
|
+
"",
|
|
18694
|
+
" ```json fix-list",
|
|
18695
|
+
" {",
|
|
18696
|
+
' "pr": <pr-number>,',
|
|
18697
|
+
' "branch": "<head-ref-name>",',
|
|
18698
|
+
' "generated_at": "<ISO-8601 timestamp>",',
|
|
18699
|
+
' "items": [',
|
|
18700
|
+
' {"comment_id": "synthetic:rebase-shared-index", "author": "reviewer", "file": "<first-conflicting-file>", "instruction": "Branch is BEHIND default-branch with shared-index row-insert conflicts only. Apply this recipe: (1) Fetch and rebase: `git fetch origin && git pull --rebase origin <default-branch>`. (2) For each conflicting shared-index file: read the rebased version (it now contains the other PR\'s row), extract this PR\'s row from the `<<<<<<<` side of the conflict markers, re-insert that row in declared sort order using the file\'s documented sort key (alphabetical on the first column by default), stage the file with `git add <path>`. (3) Run the commit-path verification for each row: `count=$(git show :<path> | grep -Fc <row-unique-marker>) && [ \\"$count\\" = \\"1\\" ] || exit 1`. (4) `git rebase --continue` and push with non-force `git push origin <branch>`. (5) If any conflict marker touches the frontmatter, table header row, `|---|---|` separator, or surrounding prose, abort the recipe and emit `BLOCKED <reason>` \u2014 the precondition guards from the reviewer\'s classification step were violated."}',
|
|
18701
|
+
" ]",
|
|
18702
|
+
" }",
|
|
18703
|
+
" ```",
|
|
18704
|
+
" ```",
|
|
18705
|
+
"",
|
|
17915
18706
|
"4. **Invoke `issue-worker` in feedback mode** with the same prompt",
|
|
17916
18707
|
" shape used by the in-scope-fix flow: include the literal phrase",
|
|
17917
18708
|
" `feedback mode: PR #<n>` plus the repository identifier",
|
|
@@ -17956,7 +18747,16 @@ var prReviewerSubAgent = {
|
|
|
17956
18747
|
" gh pr edit <pr-number> --add-label 'review:awaiting-human'",
|
|
17957
18748
|
" ```",
|
|
17958
18749
|
"",
|
|
17959
|
-
"2.
|
|
18750
|
+
"2. **If `matched_rule == 6`** (size threshold was the sole trigger),",
|
|
18751
|
+
" run the `Update the branch when `mergeStateStatus` is `BEHIND``",
|
|
18752
|
+
" step from the `Mode auto-merge` branch above before exiting. The",
|
|
18753
|
+
" eligibility gate documented in that sub-section explicitly permits",
|
|
18754
|
+
" `gh pr update-branch` on size-only human-required PRs so the bot",
|
|
18755
|
+
" keeps the branch fresh against the default branch while the human",
|
|
18756
|
+
" reviews. Skip this sub-step for `matched_rule` in 2\u20135 \u2014 the gate",
|
|
18757
|
+
" denies `update-branch` there and the human owns branch-lifecycle.",
|
|
18758
|
+
"",
|
|
18759
|
+
"3. Exit cleanly after the acceptance-criteria check completes and any",
|
|
17960
18760
|
" summary comment the reviewer posts. Proceed to Phase 5 only if a",
|
|
17961
18761
|
" merge occurred \u2014 in `human-required` mode the reviewer stops at",
|
|
17962
18762
|
" the hand-off and does not poll for merge.",
|
|
@@ -18201,13 +19001,20 @@ var prReviewerSubAgent = {
|
|
|
18201
19001
|
" AC-drift pushback, any failed-fix pushback, and any human-required",
|
|
18202
19002
|
" hand-off all keep auto-merge disabled until the human resolves",
|
|
18203
19003
|
" the underlying state.",
|
|
18204
|
-
"15. **
|
|
18205
|
-
"
|
|
18206
|
-
"
|
|
18207
|
-
"
|
|
18208
|
-
"
|
|
19004
|
+
"15. **Restrict `gh pr update-branch` on `human-required` PRs.** The",
|
|
19005
|
+
" `update-branch` step is permitted when the review mode is",
|
|
19006
|
+
" `auto-merge`, **or** when the mode is `human-required` **and**",
|
|
19007
|
+
" Phase 2.75's `matched_rule == 6` (size threshold was the sole",
|
|
19008
|
+
" trigger). All other human-required matches (rule 2 force-human",
|
|
19009
|
+
" label, rule 3 listed force-human label, rule 4 path glob, rule 5",
|
|
19010
|
+
" issue type) continue to block `update-branch`: pushing main into",
|
|
19011
|
+
" those PRs expands the diff under review without the human",
|
|
19012
|
+
" reviewer's consent. The same restriction applies to delegating",
|
|
18209
19013
|
" conflict resolution to `issue-worker`: never delegate a rebase",
|
|
18210
|
-
" on a `human-required` PR
|
|
19014
|
+
" on a `human-required` PR regardless of `matched_rule` \u2014 the",
|
|
19015
|
+
" typed-recipe delegation flow stays auto-merge-only because a",
|
|
19016
|
+
" worker push to a human-required branch is a stronger mutation",
|
|
19017
|
+
" than the merge-commit `gh pr update-branch` performs.",
|
|
18211
19018
|
"16. **Never delegate conflict resolution involving generated or",
|
|
18212
19019
|
" projen-managed files.** When `gh pr update-branch` fails on a",
|
|
18213
19020
|
" BEHIND PR with conflicts and any conflicting path is a lockfile,",
|
|
@@ -18375,364 +19182,442 @@ var reviewPrsSkill = {
|
|
|
18375
19182
|
"comment on that PR and continue with the next."
|
|
18376
19183
|
].join("\n")
|
|
18377
19184
|
};
|
|
18378
|
-
|
|
18379
|
-
|
|
18380
|
-
|
|
18381
|
-
|
|
18382
|
-
|
|
18383
|
-
|
|
18384
|
-
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18411
|
-
|
|
18412
|
-
|
|
18413
|
-
|
|
18414
|
-
|
|
18415
|
-
|
|
18416
|
-
|
|
18417
|
-
|
|
18418
|
-
|
|
18419
|
-
|
|
18420
|
-
|
|
18421
|
-
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
|
|
18489
|
-
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
|
|
18498
|
-
|
|
18499
|
-
|
|
18500
|
-
|
|
18501
|
-
|
|
18502
|
-
|
|
18503
|
-
|
|
18504
|
-
|
|
18505
|
-
|
|
18506
|
-
|
|
18507
|
-
|
|
18508
|
-
|
|
18509
|
-
|
|
18510
|
-
|
|
18511
|
-
|
|
18512
|
-
|
|
19185
|
+
function buildPrReviewBundle(policy = resolvePrReviewPolicy()) {
|
|
19186
|
+
return {
|
|
19187
|
+
name: "pr-review",
|
|
19188
|
+
description: "Pull request review workflow: verifies PRs against their linked issues' acceptance criteria and orchestrates squash-merge, single or looped over all eligible PRs",
|
|
19189
|
+
// Default-apply: the PR review workflow is safe to include everywhere,
|
|
19190
|
+
// and keeping review/merge policy centralised in the pr-reviewer agent
|
|
19191
|
+
// means consumers get consistent behaviour out of the box. Consumers can
|
|
19192
|
+
// still exclude it explicitly via `excludeBundles` if desired.
|
|
19193
|
+
appliesWhen: () => true,
|
|
19194
|
+
rules: [
|
|
19195
|
+
{
|
|
19196
|
+
name: "pr-review-policy",
|
|
19197
|
+
description: "Declarative policy that tells the pr-reviewer which PRs may auto-merge and which must wait for a human reviewer",
|
|
19198
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
19199
|
+
// Bundle defaults exclude paths into configulator's own source
|
|
19200
|
+
// (only meaningful when configulator is a workspace package).
|
|
19201
|
+
// codedrifters/packages restores them via
|
|
19202
|
+
// `agentConfig.additionalRulePaths`.
|
|
19203
|
+
filePatterns: [
|
|
19204
|
+
".claude/agents/pr-reviewer.md",
|
|
19205
|
+
".claude/skills/review-pr/**"
|
|
19206
|
+
],
|
|
19207
|
+
content: [
|
|
19208
|
+
"# PR Review Policy",
|
|
19209
|
+
"",
|
|
19210
|
+
"The `pr-reviewer` sub-agent evaluates every PR it reviews against the",
|
|
19211
|
+
"policy below and routes the PR into one of two modes:",
|
|
19212
|
+
"",
|
|
19213
|
+
"- **`auto-merge`** \u2014 the reviewer may enable squash auto-merge once",
|
|
19214
|
+
" all acceptance criteria are met and CI is green.",
|
|
19215
|
+
"- **`human-required`** \u2014 the reviewer runs the full AC/CI check but",
|
|
19216
|
+
" never calls `gh pr merge --auto`. It applies the",
|
|
19217
|
+
" `review:awaiting-human` label and hands off to a human reviewer.",
|
|
19218
|
+
"",
|
|
19219
|
+
"## Policy",
|
|
19220
|
+
"",
|
|
19221
|
+
"```yaml",
|
|
19222
|
+
"version: 1",
|
|
19223
|
+
"default: auto-merge",
|
|
19224
|
+
"",
|
|
19225
|
+
"human-required:",
|
|
19226
|
+
" paths:",
|
|
19227
|
+
' - "docs/src/content/docs/requirements/architectural-decisions/**"',
|
|
19228
|
+
' - "docs/src/content/docs/project-context.md"',
|
|
19229
|
+
' - ".github/workflows/**"',
|
|
19230
|
+
' - ".github/CODEOWNERS"',
|
|
19231
|
+
' - ".projenrc.ts"',
|
|
19232
|
+
' - "projenrc/**"',
|
|
19233
|
+
' - "CLAUDE.md"',
|
|
19234
|
+
' - ".claude/**"',
|
|
19235
|
+
' - "packages/**/package.json"',
|
|
19236
|
+
" issue-types:",
|
|
19237
|
+
" - release",
|
|
19238
|
+
" - hotfix",
|
|
19239
|
+
" size:",
|
|
19240
|
+
" files: 10",
|
|
19241
|
+
" insertions: 500",
|
|
19242
|
+
" labels-that-force-human:",
|
|
19243
|
+
' - "review:human-required"',
|
|
19244
|
+
' - "priority:critical"',
|
|
19245
|
+
"",
|
|
19246
|
+
"auto-merge:",
|
|
19247
|
+
" labels-that-force-auto:",
|
|
19248
|
+
' - "review:auto-ok"',
|
|
19249
|
+
" paths-exempt-from-size:",
|
|
19250
|
+
...renderPathsExemptFromSizeYaml(
|
|
19251
|
+
policy.autoMerge.pathsExemptFromSize
|
|
19252
|
+
),
|
|
19253
|
+
"```",
|
|
19254
|
+
"",
|
|
19255
|
+
"## Precedence",
|
|
19256
|
+
"",
|
|
19257
|
+
"The reviewer walks the following checks in order. The **first match**",
|
|
19258
|
+
"fixes the mode; any mixed-match PR (signals from both sides) resolves",
|
|
19259
|
+
"conservatively to `human-required` \u2014 `auto-merge` only wins when the",
|
|
19260
|
+
"force-auto label is the single top-priority match.",
|
|
19261
|
+
"",
|
|
19262
|
+
"1. **`auto-merge.labels-that-force-auto`** \u2014 if the PR carries any of",
|
|
19263
|
+
" these labels (e.g. `review:auto-ok`), the mode is `auto-merge`",
|
|
19264
|
+
" outright. This is the only escape hatch from the conservative",
|
|
19265
|
+
" default; it requires a maintainer to apply the label explicitly.",
|
|
19266
|
+
"2. **`review:human-required` label** \u2014 reserved force-human label;",
|
|
19267
|
+
" if present (and no force-auto label beat it in step 1), the mode",
|
|
19268
|
+
" is `human-required`.",
|
|
19269
|
+
"3. **`human-required.labels-that-force-human`** \u2014 any listed label on",
|
|
19270
|
+
" the PR (e.g. `priority:critical`) forces `human-required`.",
|
|
19271
|
+
"4. **`human-required.paths`** \u2014 if any file in the PR diff matches",
|
|
19272
|
+
" any glob here, the mode is `human-required`. Matching uses",
|
|
19273
|
+
" standard glob semantics (`**` for recursive directories,",
|
|
19274
|
+
" `*` for a single path segment).",
|
|
19275
|
+
"5. **`human-required.issue-types`** \u2014 if the linked issue's GitHub",
|
|
19276
|
+
" issue type matches any entry (case-insensitive), the mode is",
|
|
19277
|
+
" `human-required`.",
|
|
19278
|
+
"6. **`human-required.size`** \u2014 first read",
|
|
19279
|
+
" `auto-merge.paths-exempt-from-size` from the policy block above.",
|
|
19280
|
+
" For every file in the PR diff, evaluate whether the file path",
|
|
19281
|
+
" matches at least one glob in that carve-out list. If **every**",
|
|
19282
|
+
" changed path matches the carve-out, **skip rule #6** entirely",
|
|
19283
|
+
" and continue to rule #7 \u2014 the PR is exempt from the size",
|
|
19284
|
+
" threshold regardless of its `files` or `insertions` count.",
|
|
19285
|
+
" Otherwise (any changed path falls outside the carve-out list),",
|
|
19286
|
+
" apply the size check: if the PR exceeds either the `files`",
|
|
19287
|
+
" count or the `insertions` count, the mode is `human-required`.",
|
|
19288
|
+
"7. **`default`** \u2014 applied only when no rule above matched",
|
|
19289
|
+
" (normally `auto-merge`).",
|
|
19290
|
+
"",
|
|
19291
|
+
"The `auto-merge.paths-exempt-from-size` carve-out exists so",
|
|
19292
|
+
"**doc-only PRs** that routinely exceed the 500-insertion size",
|
|
19293
|
+
"threshold (large migrations, bulk additions, refresh passes)",
|
|
19294
|
+
"are not forced into `human-required` mode for a reason that does",
|
|
19295
|
+
"not reflect production risk. The default carve-out exempts the",
|
|
19296
|
+
"entire `docs/**` tree \u2014 every consumer of configulator places its",
|
|
19297
|
+
"Starlight docs site there. A PR mixing docs and code still falls",
|
|
19298
|
+
"into `human-required` at rule #6 because the non-docs path fails",
|
|
19299
|
+
"the carve-out check, so the rule only relaxes the threshold for",
|
|
19300
|
+
"PRs whose **every** changed path is doc-only.",
|
|
19301
|
+
"",
|
|
19302
|
+
"The `pr-reviewer` sub-agent records the decided mode, the",
|
|
19303
|
+
"triggering reason, and the numeric **matched-rule index** (2\u20136",
|
|
19304
|
+
"for human-required matches; `null` for rule 1 force-auto or",
|
|
19305
|
+
"rule 7 default) in its Phase 2.75 output. Downstream phases and",
|
|
19306
|
+
"the sticky summary cite the specific rule that applied.",
|
|
19307
|
+
"",
|
|
19308
|
+
"### Rule-#6 carve-out for `gh pr update-branch`",
|
|
19309
|
+
"",
|
|
19310
|
+
"The reviewer's BEHIND-branch refresh step (`gh pr update-branch`)",
|
|
19311
|
+
"is normally restricted to `auto-merge` PRs because pushing the",
|
|
19312
|
+
"default branch into a `human-required` PR expands the diff the",
|
|
19313
|
+
"human is reviewing without their consent. A narrow exception",
|
|
19314
|
+
"applies when rule #6 (size threshold) is the **sole** trigger",
|
|
19315
|
+
"for `human-required` mode: the bot may still run `gh pr",
|
|
19316
|
+
"update-branch` so a code-heavy size-tripped PR does not sit",
|
|
19317
|
+
"BEHIND while the human drafts their review.",
|
|
19318
|
+
"",
|
|
19319
|
+
"The exception is keyed on the matched-rule index recorded in",
|
|
19320
|
+
"Phase 2.75. All other `human-required` triggers \u2014 rule 2",
|
|
19321
|
+
"(`review:human-required` label), rule 3 (any",
|
|
19322
|
+
"`labels-that-force-human` label such as `priority:critical`),",
|
|
19323
|
+
"rule 4 (`human-required.paths` glob match), and rule 5",
|
|
19324
|
+
"(`human-required.issue-types` match) \u2014 continue to block",
|
|
19325
|
+
"`update-branch` because each one signals a human reviewer who",
|
|
19326
|
+
"has explicit ownership of the branch's lifecycle.",
|
|
19327
|
+
"",
|
|
19328
|
+
"This carve-out is largely belt-and-suspenders given the doc-only",
|
|
19329
|
+
"size carve-out above. Doc-only PRs that trip rule #6 now route",
|
|
19330
|
+
"directly to `auto-merge`, so the rule-#6 `update-branch` carve-",
|
|
19331
|
+
"out only kicks in for **code-heavy** PRs that legitimately trip",
|
|
19332
|
+
"rule #6 (mixed-content diffs whose non-doc paths fail the",
|
|
19333
|
+
"`paths-exempt-from-size` check, or consumers that disable the",
|
|
19334
|
+
"doc-only carve-out entirely)."
|
|
19335
|
+
].join("\n"),
|
|
19336
|
+
tags: ["policy", "review"]
|
|
18513
19337
|
},
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18519
|
-
|
|
18520
|
-
|
|
18521
|
-
|
|
18522
|
-
|
|
18523
|
-
|
|
18524
|
-
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18541
|
-
|
|
18542
|
-
|
|
18543
|
-
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
|
|
18549
|
-
|
|
18550
|
-
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
"
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
18560
|
-
"| `-1` | Declined as out-of-scope. A separate tracking issue was created; the reviewer's reply links to it. | **Yes** |",
|
|
18561
|
-
"",
|
|
18562
|
-
"Terminal reactions (`+1`, `rocket`, `-1`) are applied **only after**",
|
|
18563
|
-
"the corresponding action has truly completed \u2014 the fix accepted,",
|
|
18564
|
-
"the commit landed on the branch, or the out-of-scope tracking",
|
|
18565
|
-
"issue created and linked in a reply. The reviewer never applies a",
|
|
18566
|
-
"terminal reaction pre-emptively.",
|
|
18567
|
-
"",
|
|
18568
|
-
"A comment carrying only `eyes` or `thinking_face` from the",
|
|
18569
|
-
"reviewer is **non-terminal** and will be re-evaluated on the next",
|
|
18570
|
-
"pass. A comment carrying any terminal reaction authored by the",
|
|
18571
|
-
"reviewer is dropped from future classification.",
|
|
18572
|
-
"",
|
|
18573
|
-
"GitHub's reactions API uses `confused` as the content string for",
|
|
18574
|
-
"the `thinking_face` reaction (`content=confused` when POSTing).",
|
|
18575
|
-
"",
|
|
18576
|
-
"### Resolving a Pushback",
|
|
18577
|
-
"",
|
|
18578
|
-
"When the reviewer pushes back on a comment with `thinking_face`,",
|
|
18579
|
-
"auto-merge is blocked until the dispute is resolved. Humans have",
|
|
18580
|
-
"three ways to clear a pushback:",
|
|
18581
|
-
"",
|
|
18582
|
-
"1. **Withdraw the comment.** Delete the comment, or edit out the",
|
|
18583
|
-
" disputed request, then re-invoke `/review-pr <n>`. The reviewer",
|
|
18584
|
-
" drops the withdrawn item from its queue on the next pass.",
|
|
18585
|
-
"2. **Reply with clarification.** Post a reply on the same thread",
|
|
18586
|
-
" that addresses the reviewer's objection (cite the acceptance",
|
|
18587
|
-
" criterion you meant, supply the missing context, or concede the",
|
|
18588
|
-
" point). Re-invoke `/review-pr <n>` \u2014 the reviewer re-classifies",
|
|
18589
|
-
" the thread and may promote `thinking_face` to `+1` if the",
|
|
18590
|
-
" clarification satisfies it.",
|
|
18591
|
-
"3. **Force through with `review:auto-ok`.** Apply the",
|
|
18592
|
-
" `review:auto-ok` label to the PR as an explicit maintainer",
|
|
18593
|
-
" override. The reviewer will log the override in the sticky",
|
|
18594
|
-
" `## Reviewer notes` comment and proceed with auto-merge even",
|
|
18595
|
-
" though the dispute was never resolved by reply or withdrawal.",
|
|
18596
|
-
"",
|
|
18597
|
-
"### Fix-List Comment Format",
|
|
18598
|
-
"",
|
|
18599
|
-
"When Phase 4 delegates in-scope fixes to `issue-worker`, it posts",
|
|
18600
|
-
"a single PR-level comment whose body carries both a human-readable",
|
|
18601
|
-
"checkbox summary and a fenced ```json fix-list``` block. The JSON",
|
|
18602
|
-
"block is the authoritative payload the worker parses; the",
|
|
18603
|
-
"checkbox list is for humans reading the PR.",
|
|
18604
|
-
"",
|
|
18605
|
-
"```markdown",
|
|
18606
|
-
"## Reviewer: fix list for @issue-worker",
|
|
18607
|
-
"",
|
|
18608
|
-
"- [ ] @<author> \u2014 <instruction summary> (<file>:<line>)",
|
|
18609
|
-
"",
|
|
18610
|
-
"```json fix-list",
|
|
18611
|
-
"{",
|
|
18612
|
-
' "pr": <pr-number>,',
|
|
18613
|
-
' "branch": "<head-ref-name>",',
|
|
18614
|
-
' "generated_at": "<ISO-8601 timestamp>",',
|
|
18615
|
-
' "items": [',
|
|
18616
|
-
' {"comment_id": "<id>", "author": "<login>", "file": "<path>", "line": <n>, "instruction": "<imperative instruction>"}',
|
|
18617
|
-
" ]",
|
|
18618
|
-
"}",
|
|
18619
|
-
"```",
|
|
18620
|
-
"```",
|
|
18621
|
-
"",
|
|
18622
|
-
"Each `items[]` entry corresponds to one in-scope comment the",
|
|
18623
|
-
"reviewer queued on this pass. The `comment_id` is preserved",
|
|
18624
|
-
"exactly as returned by the GitHub API so that `issue-worker` can",
|
|
18625
|
-
"report per-item outcomes and the reviewer can apply `rocket` or",
|
|
18626
|
-
"`thinking_face` to the correct source comment on the next pass.",
|
|
18627
|
-
"",
|
|
18628
|
-
"### Sticky `## Reviewer notes` Comment",
|
|
18629
|
-
"",
|
|
18630
|
-
"Every PR has **one** canonical reviewer-notes comment. The",
|
|
18631
|
-
"reviewer creates it on the first pass, then **edits it in place**",
|
|
18632
|
-
"on every subsequent pass via",
|
|
18633
|
-
"`gh api .../issues/comments/<id> -X PATCH`. It is never",
|
|
18634
|
-
"duplicated and never replaced by a fresh per-pass summary.",
|
|
18635
|
-
"",
|
|
18636
|
-
"This sticky comment is the **single human-facing source of truth**",
|
|
18637
|
-
"for the PR's current state. Humans scanning the PR should read",
|
|
18638
|
-
"the sticky first, before scrolling back through individual threads.",
|
|
18639
|
-
"It carries, at a minimum:",
|
|
18640
|
-
"",
|
|
18641
|
-
"- **Mode** \u2014 `auto-merge` or `human-required`, with the Phase 2.75",
|
|
18642
|
-
" reason that chose that mode.",
|
|
18643
|
-
"- **AC status** \u2014 met, partial, or missing, with evidence links",
|
|
18644
|
-
" to files or tests.",
|
|
18645
|
-
"- **CI status** \u2014 green, pending, or red.",
|
|
18646
|
-
"- **Outstanding** \u2014 comments still carrying a non-terminal",
|
|
18647
|
-
" reviewer reaction (`eyes`, open `thinking_face`).",
|
|
18648
|
-
"- **Pushbacks** \u2014 every unresolved `thinking_face` the reviewer",
|
|
18649
|
-
" has left, with the reason captured in its pushback reply.",
|
|
18650
|
-
"- **Last pass** \u2014 the ISO 8601 timestamp of the most recent run.",
|
|
18651
|
-
"",
|
|
18652
|
-
"The sticky is updated on every pass \u2014 including passes that ended",
|
|
18653
|
-
"in a pushback-gated skip, a `NEEDS_CHANGES` findings comment, or",
|
|
18654
|
-
"a `human-required` hand-off \u2014 so it never goes stale while the",
|
|
18655
|
-
"reviewer is actively processing the PR.",
|
|
18656
|
-
"",
|
|
18657
|
-
"### Label Glossary",
|
|
18658
|
-
"",
|
|
18659
|
-
"Five review-workflow labels drive the feedback loop. Consumers",
|
|
18660
|
-
"that adopt this workflow are responsible for creating them in",
|
|
18661
|
-
"their own repos (the same way they create `priority:*` and",
|
|
18662
|
-
"`status:*` labels).",
|
|
18663
|
-
"",
|
|
18664
|
-
"| Label | Purpose |",
|
|
18665
|
-
"|-------|---------|",
|
|
18666
|
-
"| `origin:issue-worker` | PR was opened by the `issue-worker` agent. Eligible for auto-delegation of in-scope fixes. Human-authored PRs lack this label and will not trigger delegation unless the reviewer is invoked with `--allow-human-author`. |",
|
|
18667
|
-
"| `review:human-required` | Force human review regardless of what the policy would otherwise decide. The reviewer never enables auto-merge on a PR carrying this label. |",
|
|
18668
|
-
"| `review:auto-ok` | Force auto-merge regardless of what the policy would otherwise decide. **Also resolves outstanding `thinking_face` pushbacks** as an explicit maintainer override; the reviewer logs the override in the sticky summary. |",
|
|
18669
|
-
"| `review:awaiting-human` | Set by the reviewer when it completes its work on a `human-required` PR and is handing off the final merge decision. Cleared by a human (or by `review:auto-ok` flipping the PR back to `auto-merge` mode). |",
|
|
18670
|
-
"| `review:fixing` | Short-lived lease held by the reviewer while an `issue-worker` feedback-mode delegation is mid-run. Released automatically at the end of Phase 4 step (g). Contention on this label means a prior delegation crashed without releasing it and needs human investigation. |",
|
|
18671
|
-
"",
|
|
18672
|
-
"### Reviewing Human-Authored PRs: the `--allow-human-author` Flag",
|
|
18673
|
-
"",
|
|
18674
|
-
"By default the reviewer only **delegates** in-scope fixes on",
|
|
18675
|
-
"bot-authored PRs \u2014 those carrying the `origin:issue-worker`",
|
|
18676
|
-
"label. Running `/review-pr <n>` or `/review-prs` on a",
|
|
18677
|
-
"human-authored PR still produces a full review (reactions,",
|
|
18678
|
-
"replies, sticky summary, and auto-merge when the policy allows",
|
|
18679
|
-
"it) but skips the delegation hand-off to `issue-worker` \u2014 the",
|
|
18680
|
-
"human author is expected to apply the fixes themselves.",
|
|
18681
|
-
"",
|
|
18682
|
-
"Pass `--allow-human-author` to opt into delegation on",
|
|
18683
|
-
"human-authored PRs for a single invocation:",
|
|
18684
|
-
"",
|
|
18685
|
-
"```",
|
|
18686
|
-
"/review-pr <pr-number> --allow-human-author",
|
|
18687
|
-
"/review-prs --allow-human-author",
|
|
18688
|
-
"```",
|
|
18689
|
-
"",
|
|
18690
|
-
"The flag does **not** persist across invocations. Subsequent",
|
|
18691
|
-
"invocations return to the bot-only default and require the flag",
|
|
18692
|
-
"to be re-supplied if delegation on a human-authored PR is desired",
|
|
18693
|
-
"again."
|
|
18694
|
-
].join("\n"),
|
|
18695
|
-
platforms: {
|
|
18696
|
-
cursor: { exclude: true }
|
|
19338
|
+
{
|
|
19339
|
+
name: "pr-review-workflow",
|
|
19340
|
+
description: "Describes the /review-pr and /review-prs skills and their delegation to the pr-reviewer sub-agent",
|
|
19341
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
19342
|
+
content: [
|
|
19343
|
+
"# PR Review Workflow",
|
|
19344
|
+
"",
|
|
19345
|
+
"Two skills are available, both backed by the same `pr-reviewer`",
|
|
19346
|
+
"sub-agent:",
|
|
19347
|
+
"",
|
|
19348
|
+
"- **`/review-pr <pr-number>`** \u2014 review a single targeted PR.",
|
|
19349
|
+
"- **`/review-prs`** \u2014 loop over every eligible open PR in the",
|
|
19350
|
+
" repository and review each one in turn.",
|
|
19351
|
+
"",
|
|
19352
|
+
"The `pr-reviewer` sub-agent:",
|
|
19353
|
+
"",
|
|
19354
|
+
"1. Runs a pre-flight eligibility filter (mergeable, CI not failing,",
|
|
19355
|
+
" has a linked issue). Ineligible PRs get a short comment and are",
|
|
19356
|
+
" skipped.",
|
|
19357
|
+
"2. Fetches the PR, its diff, CI status, and the linked issue",
|
|
19358
|
+
"3. **Evaluates the PR Review Policy** (see the `PR Review Policy`",
|
|
19359
|
+
" section above) to decide whether the PR is `auto-merge` or",
|
|
19360
|
+
" `human-required`, and records the triggering reason",
|
|
19361
|
+
"4. Builds a checklist from the issue's acceptance criteria",
|
|
19362
|
+
"5. Verifies the diff satisfies each criterion and that CI is green",
|
|
19363
|
+
"6. **Enables squash auto-merge** (with `--delete-branch`) when all",
|
|
19364
|
+
" checks pass **and** the review mode is `auto-merge`",
|
|
19365
|
+
"7. **Applies `review:awaiting-human`** and hands off to a human",
|
|
19366
|
+
" reviewer when the review mode is `human-required` (no auto-merge,",
|
|
19367
|
+
" even if every acceptance criterion is met)",
|
|
19368
|
+
"8. **Comments with grouped findings** when any check fails (plain",
|
|
19369
|
+
" `gh pr comment`, not a formal `--request-changes` review)",
|
|
19370
|
+
"9. After a successful merge, verifies the linked issue is closed",
|
|
19371
|
+
" and closes it explicitly if the merge commit did not",
|
|
19372
|
+
"10. Cleans up the local branch after merge",
|
|
19373
|
+
"",
|
|
19374
|
+
"The reviewer **never** implements code and **never** pushes commits",
|
|
19375
|
+
"to a PR's branch \u2014 it only reviews, decides, and orchestrates merge",
|
|
19376
|
+
"or comment. In loop mode, a failed review for one PR never stops",
|
|
19377
|
+
"the loop; the reviewer comments and moves on. See the `pr-reviewer`",
|
|
19378
|
+
"agent definition for the full phase-by-phase contract."
|
|
19379
|
+
].join("\n"),
|
|
19380
|
+
platforms: {
|
|
19381
|
+
cursor: { exclude: true }
|
|
19382
|
+
},
|
|
19383
|
+
tags: ["workflow"]
|
|
18697
19384
|
},
|
|
18698
|
-
|
|
18699
|
-
|
|
18700
|
-
|
|
18701
|
-
|
|
18702
|
-
|
|
18703
|
-
|
|
18704
|
-
|
|
18705
|
-
|
|
18706
|
-
|
|
18707
|
-
|
|
18708
|
-
|
|
18709
|
-
|
|
18710
|
-
|
|
18711
|
-
|
|
18712
|
-
|
|
18713
|
-
|
|
18714
|
-
|
|
18715
|
-
|
|
18716
|
-
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
|
|
18720
|
-
|
|
18721
|
-
|
|
18722
|
-
|
|
18723
|
-
|
|
18724
|
-
|
|
18725
|
-
|
|
18726
|
-
|
|
18727
|
-
|
|
18728
|
-
|
|
18729
|
-
|
|
18730
|
-
|
|
18731
|
-
|
|
18732
|
-
|
|
18733
|
-
|
|
18734
|
-
|
|
18735
|
-
|
|
19385
|
+
{
|
|
19386
|
+
name: "pr-review-feedback-protocol",
|
|
19387
|
+
description: "Documents the human-in-the-loop feedback loop on PR review: reaction state machine, pushback resolution, fix-list comment format, sticky reviewer-notes comment, label glossary, and human-author opt-in flag.",
|
|
19388
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
19389
|
+
// Bundle defaults exclude paths into configulator's own source
|
|
19390
|
+
// (only meaningful when configulator is a workspace package).
|
|
19391
|
+
// codedrifters/packages restores them via
|
|
19392
|
+
// `agentConfig.additionalRulePaths`.
|
|
19393
|
+
filePatterns: [
|
|
19394
|
+
".claude/agents/pr-reviewer.md",
|
|
19395
|
+
".claude/agents/issue-worker.md"
|
|
19396
|
+
],
|
|
19397
|
+
content: [
|
|
19398
|
+
"# PR Review Feedback Protocol",
|
|
19399
|
+
"",
|
|
19400
|
+
"## Human-in-the-Loop Feedback Protocol",
|
|
19401
|
+
"",
|
|
19402
|
+
"The PR review pipeline is a **human-in-the-loop feedback loop**.",
|
|
19403
|
+
"Reviewers (human or agent) leave comments on the PR; the",
|
|
19404
|
+
"`pr-reviewer` sub-agent classifies each comment, reacts to it,",
|
|
19405
|
+
"delegates in-scope fixes to `issue-worker`, and updates a single",
|
|
19406
|
+
"sticky `## Reviewer notes` comment that is the canonical record of",
|
|
19407
|
+
"PR state. The sections below document the conventions humans need",
|
|
19408
|
+
"to read and drive that loop.",
|
|
19409
|
+
"",
|
|
19410
|
+
"### Trigger Model: Human-Triggered, Single-Pass",
|
|
19411
|
+
"",
|
|
19412
|
+
"Each reviewer pass runs exactly once and does not self-chain. A",
|
|
19413
|
+
"human re-invokes `/review-pr <n>` (or `/review-prs`) whenever the",
|
|
19414
|
+
"PR state changes enough to warrant another look \u2014 a new comment,",
|
|
19415
|
+
"a new commit, a resolved pushback, a label flip. The reviewer",
|
|
19416
|
+
"never reschedules itself and never loops back after handing off to",
|
|
19417
|
+
"`issue-worker`: the worker's run is the terminal step of that",
|
|
19418
|
+
"pass, and a human must re-invoke the reviewer to see the follow-up",
|
|
19419
|
+
"reactions and the auto-merge re-enablement decision.",
|
|
19420
|
+
"",
|
|
19421
|
+
"This keeps the loop cheap to reason about: every agent action is",
|
|
19422
|
+
"traceable to a specific human invocation, and there is no",
|
|
19423
|
+
"background automation to pause or cancel.",
|
|
19424
|
+
"",
|
|
19425
|
+
"### Reaction State Machine",
|
|
19426
|
+
"",
|
|
19427
|
+
"The reviewer signals its disposition toward each human comment via",
|
|
19428
|
+
"GitHub reactions on that comment. Five reactions carry meaning in",
|
|
19429
|
+
"this workflow; every other reaction is ignored.",
|
|
19430
|
+
"",
|
|
19431
|
+
"| Reaction | Meaning | Terminal? |",
|
|
19432
|
+
"|----------|---------|-----------|",
|
|
19433
|
+
"| `eyes` | Seen by reviewer; no terminal decision yet. Queued for processing on this or a later pass. | No |",
|
|
19434
|
+
"| `+1` | Reviewer accepted the comment's request; a fix has been queued or has already landed. | **Yes** |",
|
|
19435
|
+
"| `rocket` | The accepted fix has landed on the branch. The reviewer's reply cites the commit SHA that applied it. | **Yes** |",
|
|
19436
|
+
"| `thinking_face` | Reviewer pushback \u2014 the comment conflicts with an acceptance criterion, a CLAUDE.md convention, the project-context doc, or is ambiguous. **Blocks auto-merge** until resolved. | No |",
|
|
19437
|
+
"| `-1` | Declined as out-of-scope. A separate tracking issue was created; the reviewer's reply links to it. | **Yes** |",
|
|
19438
|
+
"",
|
|
19439
|
+
"Terminal reactions (`+1`, `rocket`, `-1`) are applied **only after**",
|
|
19440
|
+
"the corresponding action has truly completed \u2014 the fix accepted,",
|
|
19441
|
+
"the commit landed on the branch, or the out-of-scope tracking",
|
|
19442
|
+
"issue created and linked in a reply. The reviewer never applies a",
|
|
19443
|
+
"terminal reaction pre-emptively.",
|
|
19444
|
+
"",
|
|
19445
|
+
"A comment carrying only `eyes` or `thinking_face` from the",
|
|
19446
|
+
"reviewer is **non-terminal** and will be re-evaluated on the next",
|
|
19447
|
+
"pass. A comment carrying any terminal reaction authored by the",
|
|
19448
|
+
"reviewer is dropped from future classification.",
|
|
19449
|
+
"",
|
|
19450
|
+
"GitHub's reactions API uses `confused` as the content string for",
|
|
19451
|
+
"the `thinking_face` reaction (`content=confused` when POSTing).",
|
|
19452
|
+
"",
|
|
19453
|
+
"### Resolving a Pushback",
|
|
19454
|
+
"",
|
|
19455
|
+
"When the reviewer pushes back on a comment with `thinking_face`,",
|
|
19456
|
+
"auto-merge is blocked until the dispute is resolved. Humans have",
|
|
19457
|
+
"three ways to clear a pushback:",
|
|
19458
|
+
"",
|
|
19459
|
+
"1. **Withdraw the comment.** Delete the comment, or edit out the",
|
|
19460
|
+
" disputed request, then re-invoke `/review-pr <n>`. The reviewer",
|
|
19461
|
+
" drops the withdrawn item from its queue on the next pass.",
|
|
19462
|
+
"2. **Reply with clarification.** Post a reply on the same thread",
|
|
19463
|
+
" that addresses the reviewer's objection (cite the acceptance",
|
|
19464
|
+
" criterion you meant, supply the missing context, or concede the",
|
|
19465
|
+
" point). Re-invoke `/review-pr <n>` \u2014 the reviewer re-classifies",
|
|
19466
|
+
" the thread and may promote `thinking_face` to `+1` if the",
|
|
19467
|
+
" clarification satisfies it.",
|
|
19468
|
+
"3. **Force through with `review:auto-ok`.** Apply the",
|
|
19469
|
+
" `review:auto-ok` label to the PR as an explicit maintainer",
|
|
19470
|
+
" override. The reviewer will log the override in the sticky",
|
|
19471
|
+
" `## Reviewer notes` comment and proceed with auto-merge even",
|
|
19472
|
+
" though the dispute was never resolved by reply or withdrawal.",
|
|
19473
|
+
"",
|
|
19474
|
+
"### Fix-List Comment Format",
|
|
19475
|
+
"",
|
|
19476
|
+
"When Phase 4 delegates in-scope fixes to `issue-worker`, it posts",
|
|
19477
|
+
"a single PR-level comment whose body carries both a human-readable",
|
|
19478
|
+
"checkbox summary and a fenced ```json fix-list``` block. The JSON",
|
|
19479
|
+
"block is the authoritative payload the worker parses; the",
|
|
19480
|
+
"checkbox list is for humans reading the PR.",
|
|
19481
|
+
"",
|
|
19482
|
+
"```markdown",
|
|
19483
|
+
"## Reviewer: fix list for @issue-worker",
|
|
19484
|
+
"",
|
|
19485
|
+
"- [ ] @<author> \u2014 <instruction summary> (<file>:<line>)",
|
|
19486
|
+
"",
|
|
19487
|
+
"```json fix-list",
|
|
19488
|
+
"{",
|
|
19489
|
+
' "pr": <pr-number>,',
|
|
19490
|
+
' "branch": "<head-ref-name>",',
|
|
19491
|
+
' "generated_at": "<ISO-8601 timestamp>",',
|
|
19492
|
+
' "items": [',
|
|
19493
|
+
' {"comment_id": "<id>", "author": "<login>", "file": "<path>", "line": <n>, "instruction": "<imperative instruction>"}',
|
|
19494
|
+
" ]",
|
|
19495
|
+
"}",
|
|
19496
|
+
"```",
|
|
19497
|
+
"```",
|
|
19498
|
+
"",
|
|
19499
|
+
"Each `items[]` entry corresponds to one in-scope comment the",
|
|
19500
|
+
"reviewer queued on this pass. The `comment_id` is preserved",
|
|
19501
|
+
"exactly as returned by the GitHub API so that `issue-worker` can",
|
|
19502
|
+
"report per-item outcomes and the reviewer can apply `rocket` or",
|
|
19503
|
+
"`thinking_face` to the correct source comment on the next pass.",
|
|
19504
|
+
"",
|
|
19505
|
+
"### Sticky `## Reviewer notes` Comment",
|
|
19506
|
+
"",
|
|
19507
|
+
"Every PR has **one** canonical reviewer-notes comment. The",
|
|
19508
|
+
"reviewer creates it on the first pass, then **edits it in place**",
|
|
19509
|
+
"on every subsequent pass via",
|
|
19510
|
+
"`gh api .../issues/comments/<id> -X PATCH`. It is never",
|
|
19511
|
+
"duplicated and never replaced by a fresh per-pass summary.",
|
|
19512
|
+
"",
|
|
19513
|
+
"This sticky comment is the **single human-facing source of truth**",
|
|
19514
|
+
"for the PR's current state. Humans scanning the PR should read",
|
|
19515
|
+
"the sticky first, before scrolling back through individual threads.",
|
|
19516
|
+
"It carries, at a minimum:",
|
|
19517
|
+
"",
|
|
19518
|
+
"- **Mode** \u2014 `auto-merge` or `human-required`, with the Phase 2.75",
|
|
19519
|
+
" reason that chose that mode.",
|
|
19520
|
+
"- **AC status** \u2014 met, partial, or missing, with evidence links",
|
|
19521
|
+
" to files or tests.",
|
|
19522
|
+
"- **CI status** \u2014 green, pending, or red.",
|
|
19523
|
+
"- **Outstanding** \u2014 comments still carrying a non-terminal",
|
|
19524
|
+
" reviewer reaction (`eyes`, open `thinking_face`).",
|
|
19525
|
+
"- **Pushbacks** \u2014 every unresolved `thinking_face` the reviewer",
|
|
19526
|
+
" has left, with the reason captured in its pushback reply.",
|
|
19527
|
+
"- **Last pass** \u2014 the ISO 8601 timestamp of the most recent run.",
|
|
19528
|
+
"",
|
|
19529
|
+
"The sticky is updated on every pass \u2014 including passes that ended",
|
|
19530
|
+
"in a pushback-gated skip, a `NEEDS_CHANGES` findings comment, or",
|
|
19531
|
+
"a `human-required` hand-off \u2014 so it never goes stale while the",
|
|
19532
|
+
"reviewer is actively processing the PR.",
|
|
19533
|
+
"",
|
|
19534
|
+
"### Label Glossary",
|
|
19535
|
+
"",
|
|
19536
|
+
"Five review-workflow labels drive the feedback loop. Consumers",
|
|
19537
|
+
"that adopt this workflow are responsible for creating them in",
|
|
19538
|
+
"their own repos (the same way they create `priority:*` and",
|
|
19539
|
+
"`status:*` labels).",
|
|
19540
|
+
"",
|
|
19541
|
+
"| Label | Purpose |",
|
|
19542
|
+
"|-------|---------|",
|
|
19543
|
+
"| `origin:issue-worker` | PR was opened by the `issue-worker` agent. Eligible for auto-delegation of in-scope fixes. Human-authored PRs lack this label and will not trigger delegation unless the reviewer is invoked with `--allow-human-author`. |",
|
|
19544
|
+
"| `review:human-required` | Force human review regardless of what the policy would otherwise decide. The reviewer never enables auto-merge on a PR carrying this label. |",
|
|
19545
|
+
"| `review:auto-ok` | Force auto-merge regardless of what the policy would otherwise decide. **Also resolves outstanding `thinking_face` pushbacks** as an explicit maintainer override; the reviewer logs the override in the sticky summary. |",
|
|
19546
|
+
"| `review:awaiting-human` | Set by the reviewer when it completes its work on a `human-required` PR and is handing off the final merge decision. Cleared by a human (or by `review:auto-ok` flipping the PR back to `auto-merge` mode). |",
|
|
19547
|
+
"| `review:fixing` | Short-lived lease held by the reviewer while an `issue-worker` feedback-mode delegation is mid-run. Released automatically at the end of Phase 4 step (g). Contention on this label means a prior delegation crashed without releasing it and needs human investigation. |",
|
|
19548
|
+
"",
|
|
19549
|
+
"### Reviewing Human-Authored PRs: the `--allow-human-author` Flag",
|
|
19550
|
+
"",
|
|
19551
|
+
"By default the reviewer only **delegates** in-scope fixes on",
|
|
19552
|
+
"bot-authored PRs \u2014 those carrying the `origin:issue-worker`",
|
|
19553
|
+
"label. Running `/review-pr <n>` or `/review-prs` on a",
|
|
19554
|
+
"human-authored PR still produces a full review (reactions,",
|
|
19555
|
+
"replies, sticky summary, and auto-merge when the policy allows",
|
|
19556
|
+
"it) but skips the delegation hand-off to `issue-worker` \u2014 the",
|
|
19557
|
+
"human author is expected to apply the fixes themselves.",
|
|
19558
|
+
"",
|
|
19559
|
+
"Pass `--allow-human-author` to opt into delegation on",
|
|
19560
|
+
"human-authored PRs for a single invocation:",
|
|
19561
|
+
"",
|
|
19562
|
+
"```",
|
|
19563
|
+
"/review-pr <pr-number> --allow-human-author",
|
|
19564
|
+
"/review-prs --allow-human-author",
|
|
19565
|
+
"```",
|
|
19566
|
+
"",
|
|
19567
|
+
"The flag does **not** persist across invocations. Subsequent",
|
|
19568
|
+
"invocations return to the bot-only default and require the flag",
|
|
19569
|
+
"to be re-supplied if delegation on a human-authored PR is desired",
|
|
19570
|
+
"again."
|
|
19571
|
+
].join("\n"),
|
|
19572
|
+
platforms: {
|
|
19573
|
+
cursor: { exclude: true }
|
|
19574
|
+
},
|
|
19575
|
+
tags: ["workflow"]
|
|
19576
|
+
}
|
|
19577
|
+
],
|
|
19578
|
+
skills: [reviewPrSkill, reviewPrsSkill],
|
|
19579
|
+
subAgents: [prReviewerSubAgent],
|
|
19580
|
+
labels: [
|
|
19581
|
+
{
|
|
19582
|
+
name: "type:pr-review",
|
|
19583
|
+
color: "5319E7",
|
|
19584
|
+
description: "PR review tasks"
|
|
19585
|
+
},
|
|
19586
|
+
{
|
|
19587
|
+
name: "origin:issue-worker",
|
|
19588
|
+
color: "5319E7",
|
|
19589
|
+
description: "PR opened by the issue-worker agent"
|
|
19590
|
+
},
|
|
19591
|
+
{
|
|
19592
|
+
name: "review:auto-ok",
|
|
19593
|
+
color: "0E8A16",
|
|
19594
|
+
description: "Force auto-merge regardless of policy"
|
|
19595
|
+
},
|
|
19596
|
+
{
|
|
19597
|
+
name: "review:human-required",
|
|
19598
|
+
color: "D93F0B",
|
|
19599
|
+
description: "Force human review regardless of policy"
|
|
19600
|
+
},
|
|
19601
|
+
{
|
|
19602
|
+
name: "review:awaiting-human",
|
|
19603
|
+
color: "FBCA04",
|
|
19604
|
+
description: "Reviewer handed off; awaiting human merge decision"
|
|
19605
|
+
},
|
|
19606
|
+
{
|
|
19607
|
+
name: "review:fixing",
|
|
19608
|
+
color: "D4C5F9",
|
|
19609
|
+
description: "Short-lived lease while issue-worker applies feedback fixes"
|
|
19610
|
+
}
|
|
19611
|
+
]
|
|
19612
|
+
};
|
|
19613
|
+
}
|
|
19614
|
+
function renderPathsExemptFromSizeYaml(paths) {
|
|
19615
|
+
if (paths.length === 0) {
|
|
19616
|
+
return [" []"];
|
|
19617
|
+
}
|
|
19618
|
+
return paths.map((path8) => ` - "${path8}"`);
|
|
19619
|
+
}
|
|
19620
|
+
var prReviewBundle = buildPrReviewBundle();
|
|
18736
19621
|
|
|
18737
19622
|
// src/agent/bundles/projen.ts
|
|
18738
19623
|
var projenBundle = {
|
|
@@ -18743,6 +19628,10 @@ var projenBundle = {
|
|
|
18743
19628
|
{
|
|
18744
19629
|
name: "development-commands",
|
|
18745
19630
|
description: "Projen development commands for building, testing, linting, and validating changes",
|
|
19631
|
+
// ALWAYS scope: developers ask "how do I build/test/lint?" from
|
|
19632
|
+
// anywhere in the repo, not only when editing projen config.
|
|
19633
|
+
// Consumers that want to narrow the load can override via
|
|
19634
|
+
// `agentConfig.additionalRulePaths` or `excludeRules`.
|
|
18746
19635
|
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
18747
19636
|
content: [
|
|
18748
19637
|
"# Development Commands",
|
|
@@ -19451,7 +20340,8 @@ function buildRegulatoryResearchAnalystSubAgent(paths, issueDefaults) {
|
|
|
19451
20340
|
"7. **Create or update the scope index page** at",
|
|
19452
20341
|
" `<SCOPE_INDEX_PAGE>` so downstream regulations link back to a",
|
|
19453
20342
|
" coherent landing page for the scope. Follow the",
|
|
19454
|
-
"
|
|
20343
|
+
" section-index-page contract in the `shared-editing-safety` rule:",
|
|
20344
|
+
" the body must carry a 1\u20132",
|
|
19455
20345
|
" paragraph contextual summary plus a grouped, linked listing of",
|
|
19456
20346
|
" every regulation in the scope (e.g. by jurisdiction). No body",
|
|
19457
20347
|
" `# Heading` \u2014 the frontmatter `title:` already renders as the",
|
|
@@ -25795,8 +26685,39 @@ function buildSoftwareProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
|
25795
26685
|
" every profile is mapped back to the capability model regardless",
|
|
25796
26686
|
" of whether any adjacent products were surfaced.",
|
|
25797
26687
|
"",
|
|
25798
|
-
"7. **Commit
|
|
25799
|
-
"
|
|
26688
|
+
"7. **Commit any profile updates first \u2014 do not touch the",
|
|
26689
|
+
" matrix yet.** If step 4 surfaced edits to the per-product",
|
|
26690
|
+
" profile file (e.g. notes added to `## Risks / Open",
|
|
26691
|
+
" Questions` about column drift), stage and commit those",
|
|
26692
|
+
" profile changes in a focused commit. **Do not push yet,**",
|
|
26693
|
+
" and **do not** include the matrix file in this commit.",
|
|
26694
|
+
"",
|
|
26695
|
+
"8. **Defer the feature-matrix row insert to a final pre-push",
|
|
26696
|
+
" commit.** Per the `shared-editing-safety` rule's",
|
|
26697
|
+
" **Defer Shared-Index Commit to Final Pre-Push Step**",
|
|
26698
|
+
" subsection, the feature matrix is the canonical example of",
|
|
26699
|
+
" a **shared index file** \u2014 multiple software-profile",
|
|
26700
|
+
" sessions racing to append rows for different products is",
|
|
26701
|
+
" the exact contention this rule exists to mitigate. Apply",
|
|
26702
|
+
" the deferred sequence:",
|
|
26703
|
+
"",
|
|
26704
|
+
" ```bash",
|
|
26705
|
+
" git fetch origin",
|
|
26706
|
+
" git pull --rebase origin <default-branch>",
|
|
26707
|
+
" ```",
|
|
26708
|
+
"",
|
|
26709
|
+
" Re-read `<MATRIX_FILE>` from the now-up-to-date working",
|
|
26710
|
+
" tree, re-compute the deterministic insert position for the",
|
|
26711
|
+
" current product's rows (another session may have appended",
|
|
26712
|
+
" rows for a different product, or extended the segment",
|
|
26713
|
+
" columns, in the meantime), insert the rows in sort order,",
|
|
26714
|
+
" and commit the matrix edit in its **own focused commit**",
|
|
26715
|
+
" whose only file is `<MATRIX_FILE>`. Run the commit-path",
|
|
26716
|
+
" verification step (`git show HEAD:<MATRIX_FILE>` + grep",
|
|
26717
|
+
" count on the product slug as the unique marker) against",
|
|
26718
|
+
" that commit before pushing.",
|
|
26719
|
+
"",
|
|
26720
|
+
"9. **Push and close.** Push the branch and close the matrix issue.",
|
|
25800
26721
|
"",
|
|
25801
26722
|
"---",
|
|
25802
26723
|
"",
|
|
@@ -26905,7 +27826,8 @@ function buildStandardsResearchAnalystSubAgent(paths, issueDefaults) {
|
|
|
26905
27826
|
" pages.",
|
|
26906
27827
|
"",
|
|
26907
27828
|
"4. **Write the overview page** at `<OVERVIEW_PAGE>`. Follow",
|
|
26908
|
-
" the
|
|
27829
|
+
" the section-index-page contract in the",
|
|
27830
|
+
" `shared-editing-safety` rule: the body opens with a",
|
|
26909
27831
|
" 1\u20132 paragraph contextual summary of the standard and its",
|
|
26910
27832
|
" versions, and the version pages, modules, and extensions",
|
|
26911
27833
|
" sections below double as the grouped, linked listing of",
|
|
@@ -28738,6 +29660,62 @@ function bundleNameForWorkflowRule(ruleName) {
|
|
|
28738
29660
|
);
|
|
28739
29661
|
return entry?.bundle;
|
|
28740
29662
|
}
|
|
29663
|
+
function buildConventionsRegistryRule(rules) {
|
|
29664
|
+
const entries = [];
|
|
29665
|
+
for (const rule of rules) {
|
|
29666
|
+
if (rule.scope !== AGENT_RULE_SCOPE.FILE_PATTERN) {
|
|
29667
|
+
continue;
|
|
29668
|
+
}
|
|
29669
|
+
if (rule.platforms?.claude?.exclude === true) {
|
|
29670
|
+
continue;
|
|
29671
|
+
}
|
|
29672
|
+
if (!rule.filePatterns || rule.filePatterns.length === 0) {
|
|
29673
|
+
continue;
|
|
29674
|
+
}
|
|
29675
|
+
entries.push(rule);
|
|
29676
|
+
}
|
|
29677
|
+
if (entries.length === 0) {
|
|
29678
|
+
return void 0;
|
|
29679
|
+
}
|
|
29680
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
29681
|
+
const lines = [
|
|
29682
|
+
"# Conventions Registry",
|
|
29683
|
+
"",
|
|
29684
|
+
"Each row below is a `FILE_PATTERN`-scoped convention rule that",
|
|
29685
|
+
"auto-loads on demand when the agent's current file matches one",
|
|
29686
|
+
"of the listed paths. Full rule bodies live under",
|
|
29687
|
+
"`.claude/rules/<slug>.md` so they only consume context when",
|
|
29688
|
+
"they apply. Consult the matching rule before doing work in any",
|
|
29689
|
+
"of the listed surfaces.",
|
|
29690
|
+
"",
|
|
29691
|
+
"| Rule | Auto-loads when editing | Purpose |",
|
|
29692
|
+
"| --- | --- | --- |"
|
|
29693
|
+
];
|
|
29694
|
+
const MAX_PATTERNS_SHOWN = 3;
|
|
29695
|
+
for (const rule of entries) {
|
|
29696
|
+
const ruleLink = `[\`${rule.name}\`](.claude/rules/${rule.name}.md)`;
|
|
29697
|
+
const patterns = rule.filePatterns ?? [];
|
|
29698
|
+
let patternCell;
|
|
29699
|
+
if (patterns.length <= MAX_PATTERNS_SHOWN) {
|
|
29700
|
+
patternCell = patterns.map((p) => `\`${p}\``).join(", ");
|
|
29701
|
+
} else {
|
|
29702
|
+
const shown = patterns.slice(0, MAX_PATTERNS_SHOWN).map((p) => `\`${p}\``).join(", ");
|
|
29703
|
+
patternCell = `${shown}, \u2026 ([full list](.claude/rules/${rule.name}.md))`;
|
|
29704
|
+
}
|
|
29705
|
+
const purposeCell = rule.description.replace(/\s+/g, " ").replace(/\|/g, "\\|").trim();
|
|
29706
|
+
lines.push(`| ${ruleLink} | ${patternCell} | ${purposeCell} |`);
|
|
29707
|
+
}
|
|
29708
|
+
return {
|
|
29709
|
+
name: "conventions-registry",
|
|
29710
|
+
description: "Routing table for every FILE_PATTERN-scoped convention rule. Lists each rule's slug, the file patterns that auto-trigger it, and a one-line purpose so agents whose current file does not match any rule's paths: can still discover the full convention set.",
|
|
29711
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
29712
|
+
content: lines.join("\n"),
|
|
29713
|
+
platforms: {
|
|
29714
|
+
cursor: { exclude: true }
|
|
29715
|
+
},
|
|
29716
|
+
tags: ["workflow"]
|
|
29717
|
+
};
|
|
29718
|
+
}
|
|
28741
29719
|
function buildAgentRegistryRule(bundles, paths) {
|
|
28742
29720
|
const activeNames = new Set(bundles.map((b) => b.name));
|
|
28743
29721
|
const rows = AGENT_REGISTRY_ENTRIES.filter(
|
|
@@ -29153,7 +30131,7 @@ function renderPriorityRulesSection(rules) {
|
|
|
29153
30131
|
}
|
|
29154
30132
|
|
|
29155
30133
|
// src/agent/bundles/index.ts
|
|
29156
|
-
function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS, defaultAgentTier = AGENT_MODEL.BALANCED, bundleAgentTiers = /* @__PURE__ */ new Map()) {
|
|
30134
|
+
function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS, defaultAgentTier = AGENT_MODEL.BALANCED, bundleAgentTiers = /* @__PURE__ */ new Map(), prReviewPolicy = resolvePrReviewPolicy()) {
|
|
29157
30135
|
const tierFor = (bundle) => bundleAgentTiers.get(bundle) ?? defaultAgentTier;
|
|
29158
30136
|
return [
|
|
29159
30137
|
buildBaseBundle(paths),
|
|
@@ -29170,7 +30148,7 @@ function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAUL
|
|
|
29170
30148
|
buildMeetingAnalysisBundle(tierFor("meeting-analysis")),
|
|
29171
30149
|
agendaBundle,
|
|
29172
30150
|
orchestratorBundle,
|
|
29173
|
-
|
|
30151
|
+
buildPrReviewBundle(prReviewPolicy),
|
|
29174
30152
|
buildRequirementsAnalystBundle(paths, issueDefaults),
|
|
29175
30153
|
buildRequirementsWriterBundle(paths, issueDefaults),
|
|
29176
30154
|
buildRequirementsReviewerBundle(paths, issueDefaults),
|
|
@@ -29234,210 +30212,10 @@ function prefix(rel, entry) {
|
|
|
29234
30212
|
return path.posix.join(rel, entry);
|
|
29235
30213
|
}
|
|
29236
30214
|
|
|
29237
|
-
// src/agent/renderers/
|
|
30215
|
+
// src/agent/renderers/claude-renderer.ts
|
|
29238
30216
|
var import_projen6 = require("projen");
|
|
29239
30217
|
var import_textfile2 = require("projen/lib/textfile");
|
|
29240
|
-
var GENERATED_MARKER = "
|
|
29241
|
-
var CursorRenderer = class _CursorRenderer {
|
|
29242
|
-
/**
|
|
29243
|
-
* Render all Cursor configuration files.
|
|
29244
|
-
*/
|
|
29245
|
-
static render(component, rules, skills, subAgents, mcpServers, settings) {
|
|
29246
|
-
_CursorRenderer.renderRules(component, rules);
|
|
29247
|
-
_CursorRenderer.renderSkills(component, skills);
|
|
29248
|
-
_CursorRenderer.renderSubAgents(component, subAgents);
|
|
29249
|
-
_CursorRenderer.renderMcpServers(component, mcpServers);
|
|
29250
|
-
_CursorRenderer.renderHooks(component, settings);
|
|
29251
|
-
_CursorRenderer.renderIgnoreFiles(component, settings);
|
|
29252
|
-
}
|
|
29253
|
-
static renderRules(component, rules) {
|
|
29254
|
-
for (const rule of rules) {
|
|
29255
|
-
if (rule.platforms?.cursor?.exclude) continue;
|
|
29256
|
-
const lines = [];
|
|
29257
|
-
const description = rule.platforms?.cursor?.description ?? rule.description;
|
|
29258
|
-
const isAlways = rule.scope === AGENT_RULE_SCOPE.ALWAYS;
|
|
29259
|
-
lines.push("---");
|
|
29260
|
-
lines.push(`description: "${description}"`);
|
|
29261
|
-
lines.push(`alwaysApply: ${isAlways}`);
|
|
29262
|
-
if (!isAlways && rule.filePatterns && rule.filePatterns.length > 0) {
|
|
29263
|
-
lines.push(`path: ${JSON.stringify([...rule.filePatterns])}`);
|
|
29264
|
-
}
|
|
29265
|
-
lines.push("---");
|
|
29266
|
-
lines.push("");
|
|
29267
|
-
lines.push(...rule.content.split("\n"));
|
|
29268
|
-
new import_textfile2.TextFile(component, `.cursor/rules/${rule.name}.mdc`, { lines });
|
|
29269
|
-
}
|
|
29270
|
-
}
|
|
29271
|
-
static renderSkills(component, skills) {
|
|
29272
|
-
for (const skill of skills) {
|
|
29273
|
-
if (skill.platforms?.cursor?.exclude) continue;
|
|
29274
|
-
const lines = [];
|
|
29275
|
-
lines.push("---");
|
|
29276
|
-
lines.push(`name: "${skill.name}"`);
|
|
29277
|
-
lines.push(`description: "${skill.description}"`);
|
|
29278
|
-
if (skill.disableModelInvocation) {
|
|
29279
|
-
lines.push(`disable-model-invocation: true`);
|
|
29280
|
-
}
|
|
29281
|
-
if (skill.userInvocable === false) {
|
|
29282
|
-
lines.push(`user-invocable: false`);
|
|
29283
|
-
}
|
|
29284
|
-
if (skill.context) {
|
|
29285
|
-
lines.push(`context: "${skill.context}"`);
|
|
29286
|
-
}
|
|
29287
|
-
if (skill.agent) {
|
|
29288
|
-
lines.push(`agent: "${skill.agent}"`);
|
|
29289
|
-
}
|
|
29290
|
-
if (skill.shell) {
|
|
29291
|
-
lines.push(`shell: "${skill.shell}"`);
|
|
29292
|
-
}
|
|
29293
|
-
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
29294
|
-
lines.push(`allowed-tools:`);
|
|
29295
|
-
for (const tool of skill.allowedTools) {
|
|
29296
|
-
lines.push(` - "${tool}"`);
|
|
29297
|
-
}
|
|
29298
|
-
}
|
|
29299
|
-
lines.push("---");
|
|
29300
|
-
lines.push("");
|
|
29301
|
-
lines.push(...skill.instructions.split("\n"));
|
|
29302
|
-
new import_textfile2.TextFile(component, `.cursor/skills/${skill.name}/SKILL.md`, {
|
|
29303
|
-
lines
|
|
29304
|
-
});
|
|
29305
|
-
if (skill.referenceFiles && skill.referenceFiles.length > 0) {
|
|
29306
|
-
for (const file of skill.referenceFiles) {
|
|
29307
|
-
new import_textfile2.TextFile(component, `.cursor/skills/${skill.name}/${file.path}`, {
|
|
29308
|
-
lines: file.content.split("\n")
|
|
29309
|
-
});
|
|
29310
|
-
}
|
|
29311
|
-
}
|
|
29312
|
-
}
|
|
29313
|
-
}
|
|
29314
|
-
static renderSubAgents(component, subAgents) {
|
|
29315
|
-
for (const agent of subAgents) {
|
|
29316
|
-
if (agent.platforms?.cursor?.exclude) continue;
|
|
29317
|
-
const lines = [];
|
|
29318
|
-
lines.push("---");
|
|
29319
|
-
lines.push(`name: ${agent.name}`);
|
|
29320
|
-
lines.push(`description: >-`);
|
|
29321
|
-
lines.push(` ${agent.description}`);
|
|
29322
|
-
if (agent.platforms?.cursor?.readonly) {
|
|
29323
|
-
lines.push(`readonly: true`);
|
|
29324
|
-
}
|
|
29325
|
-
if (agent.platforms?.cursor?.isBackground) {
|
|
29326
|
-
lines.push(`is_background: true`);
|
|
29327
|
-
}
|
|
29328
|
-
lines.push("---");
|
|
29329
|
-
lines.push("");
|
|
29330
|
-
lines.push(...agent.prompt.split("\n"));
|
|
29331
|
-
new import_textfile2.TextFile(component, `.cursor/agents/${agent.name}.md`, { lines });
|
|
29332
|
-
}
|
|
29333
|
-
}
|
|
29334
|
-
static renderMcpServers(component, mcpServers) {
|
|
29335
|
-
const serverNames = Object.keys(mcpServers);
|
|
29336
|
-
if (serverNames.length === 0) return;
|
|
29337
|
-
const obj = { mcpServers: {} };
|
|
29338
|
-
const servers = obj.mcpServers;
|
|
29339
|
-
for (const [name, config] of Object.entries(mcpServers)) {
|
|
29340
|
-
const server = {};
|
|
29341
|
-
if (config.transport) server.transport = config.transport;
|
|
29342
|
-
if (config.command) server.command = config.command;
|
|
29343
|
-
if (config.args) server.args = [...config.args];
|
|
29344
|
-
if (config.url) server.url = config.url;
|
|
29345
|
-
if (config.headers && Object.keys(config.headers).length > 0) {
|
|
29346
|
-
server.headers = { ...config.headers };
|
|
29347
|
-
}
|
|
29348
|
-
if (config.env) server.env = { ...config.env };
|
|
29349
|
-
servers[name] = server;
|
|
29350
|
-
}
|
|
29351
|
-
new import_projen6.JsonFile(component, ".cursor/mcp.json", { obj });
|
|
29352
|
-
}
|
|
29353
|
-
static renderHooks(component, settings) {
|
|
29354
|
-
if (!settings?.hooks) return;
|
|
29355
|
-
const hooks = {};
|
|
29356
|
-
const hookEntries = settings.hooks;
|
|
29357
|
-
for (const [event, actions] of Object.entries(hookEntries)) {
|
|
29358
|
-
if (actions && actions.length > 0) {
|
|
29359
|
-
hooks[event] = actions.map((h) => ({
|
|
29360
|
-
command: h.command
|
|
29361
|
-
}));
|
|
29362
|
-
}
|
|
29363
|
-
}
|
|
29364
|
-
if (Object.keys(hooks).length === 0) return;
|
|
29365
|
-
new import_projen6.JsonFile(component, ".cursor/hooks.json", {
|
|
29366
|
-
obj: { version: 1, hooks }
|
|
29367
|
-
});
|
|
29368
|
-
}
|
|
29369
|
-
static renderIgnoreFiles(component, settings) {
|
|
29370
|
-
if (settings?.ignorePatterns && settings.ignorePatterns.length > 0) {
|
|
29371
|
-
new import_textfile2.TextFile(component, ".cursorignore", {
|
|
29372
|
-
lines: [GENERATED_MARKER, "", ...settings.ignorePatterns]
|
|
29373
|
-
});
|
|
29374
|
-
}
|
|
29375
|
-
if (settings?.indexingIgnorePatterns && settings.indexingIgnorePatterns.length > 0) {
|
|
29376
|
-
new import_textfile2.TextFile(component, ".cursorindexingignore", {
|
|
29377
|
-
lines: [GENERATED_MARKER, "", ...settings.indexingIgnorePatterns]
|
|
29378
|
-
});
|
|
29379
|
-
}
|
|
29380
|
-
}
|
|
29381
|
-
};
|
|
29382
|
-
|
|
29383
|
-
// src/agent/template-resolver.ts
|
|
29384
|
-
var FALLBACKS = {
|
|
29385
|
-
"repository.owner": "<owner>",
|
|
29386
|
-
"repository.name": "<repo>",
|
|
29387
|
-
"repository.defaultBranch": "main",
|
|
29388
|
-
"organization.name": "<organization>",
|
|
29389
|
-
"organization.githubOrg": "<org>",
|
|
29390
|
-
"githubProject.name": "<project-name>",
|
|
29391
|
-
"githubProject.number": "<project-number>",
|
|
29392
|
-
"githubProject.nodeId": "<project-node-id>",
|
|
29393
|
-
docsPath: "<docs-path>",
|
|
29394
|
-
// The monorepo-layout seed block is additive: when absent, the
|
|
29395
|
-
// seeded `project-context.md` template reads cleanly without it.
|
|
29396
|
-
// Fall back to an empty string so no placeholder text leaks into
|
|
29397
|
-
// rendered prompts for repos that predate the layout contract.
|
|
29398
|
-
monorepoLayoutSeedBlock: ""
|
|
29399
|
-
};
|
|
29400
|
-
var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
|
|
29401
|
-
function getNestedValue(obj, path8) {
|
|
29402
|
-
const parts = path8.split(".");
|
|
29403
|
-
let current = obj;
|
|
29404
|
-
for (const part of parts) {
|
|
29405
|
-
if (current == null || typeof current !== "object") {
|
|
29406
|
-
return void 0;
|
|
29407
|
-
}
|
|
29408
|
-
current = current[part];
|
|
29409
|
-
}
|
|
29410
|
-
if (current == null) {
|
|
29411
|
-
return void 0;
|
|
29412
|
-
}
|
|
29413
|
-
return String(current);
|
|
29414
|
-
}
|
|
29415
|
-
function resolveTemplateVariables(template, metadata) {
|
|
29416
|
-
if (!TEMPLATE_RE.test(template)) {
|
|
29417
|
-
return { resolved: template, unresolvedKeys: [] };
|
|
29418
|
-
}
|
|
29419
|
-
const unresolvedKeys = [];
|
|
29420
|
-
TEMPLATE_RE.lastIndex = 0;
|
|
29421
|
-
const resolved = template.replace(TEMPLATE_RE, (_match, key) => {
|
|
29422
|
-
if (metadata) {
|
|
29423
|
-
const value = getNestedValue(
|
|
29424
|
-
metadata,
|
|
29425
|
-
key
|
|
29426
|
-
);
|
|
29427
|
-
if (value !== void 0) {
|
|
29428
|
-
return value;
|
|
29429
|
-
}
|
|
29430
|
-
}
|
|
29431
|
-
unresolvedKeys.push(key);
|
|
29432
|
-
return FALLBACKS[key] ?? `<${key}>`;
|
|
29433
|
-
});
|
|
29434
|
-
return { resolved, unresolvedKeys };
|
|
29435
|
-
}
|
|
29436
|
-
|
|
29437
|
-
// src/agent/renderers/claude-renderer.ts
|
|
29438
|
-
var import_projen7 = require("projen");
|
|
29439
|
-
var import_textfile3 = require("projen/lib/textfile");
|
|
29440
|
-
var GENERATED_MARKER2 = "<!-- ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~ -->";
|
|
30218
|
+
var GENERATED_MARKER = "<!-- ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~ -->";
|
|
29441
30219
|
var ClaudeRenderer = class _ClaudeRenderer {
|
|
29442
30220
|
/**
|
|
29443
30221
|
* Render all Claude Code configuration files.
|
|
@@ -29462,12 +30240,12 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29462
30240
|
return target === CLAUDE_RULE_TARGET.CLAUDE_MD;
|
|
29463
30241
|
});
|
|
29464
30242
|
if (claudeMdRules.length === 0) return;
|
|
29465
|
-
const lines = [
|
|
30243
|
+
const lines = [GENERATED_MARKER, ""];
|
|
29466
30244
|
for (let i = 0; i < claudeMdRules.length; i++) {
|
|
29467
30245
|
if (i > 0) lines.push("", "---", "");
|
|
29468
30246
|
lines.push(...claudeMdRules[i].content.split("\n"));
|
|
29469
30247
|
}
|
|
29470
|
-
new
|
|
30248
|
+
new import_textfile2.TextFile(component, "CLAUDE.md", { lines });
|
|
29471
30249
|
}
|
|
29472
30250
|
static renderScopedRules(component, rules) {
|
|
29473
30251
|
const scopedRules = rules.filter((r) => {
|
|
@@ -29487,7 +30265,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29487
30265
|
lines.push("");
|
|
29488
30266
|
}
|
|
29489
30267
|
lines.push(...rule.content.split("\n"));
|
|
29490
|
-
new
|
|
30268
|
+
new import_textfile2.TextFile(component, `.claude/rules/${rule.name}.md`, { lines });
|
|
29491
30269
|
}
|
|
29492
30270
|
}
|
|
29493
30271
|
static renderSettings(component, mcpServers, settings) {
|
|
@@ -29612,7 +30390,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29612
30390
|
hasContent = true;
|
|
29613
30391
|
}
|
|
29614
30392
|
if (!hasContent) return;
|
|
29615
|
-
new
|
|
30393
|
+
new import_projen6.JsonFile(component, ".claude/settings.json", { obj });
|
|
29616
30394
|
}
|
|
29617
30395
|
static buildSandboxObj(sandbox) {
|
|
29618
30396
|
const obj = {};
|
|
@@ -29699,12 +30477,12 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29699
30477
|
lines.push("---");
|
|
29700
30478
|
lines.push("");
|
|
29701
30479
|
lines.push(...skill.instructions.split("\n"));
|
|
29702
|
-
new
|
|
30480
|
+
new import_textfile2.TextFile(component, `.claude/skills/${skill.name}/SKILL.md`, {
|
|
29703
30481
|
lines
|
|
29704
30482
|
});
|
|
29705
30483
|
if (skill.referenceFiles && skill.referenceFiles.length > 0) {
|
|
29706
30484
|
for (const file of skill.referenceFiles) {
|
|
29707
|
-
new
|
|
30485
|
+
new import_textfile2.TextFile(component, `.claude/skills/${skill.name}/${file.path}`, {
|
|
29708
30486
|
lines: file.content.split("\n")
|
|
29709
30487
|
});
|
|
29710
30488
|
}
|
|
@@ -29762,7 +30540,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29762
30540
|
lines.push("---");
|
|
29763
30541
|
lines.push("");
|
|
29764
30542
|
lines.push(...agent.prompt.split("\n"));
|
|
29765
|
-
new
|
|
30543
|
+
new import_textfile2.TextFile(component, `.claude/agents/${agent.name}.md`, { lines });
|
|
29766
30544
|
}
|
|
29767
30545
|
}
|
|
29768
30546
|
static buildMcpServerObj(config) {
|
|
@@ -29779,7 +30557,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29779
30557
|
}
|
|
29780
30558
|
static renderProcedures(component, procedures) {
|
|
29781
30559
|
for (const proc of procedures) {
|
|
29782
|
-
new
|
|
30560
|
+
new import_textfile2.TextFile(component, `.claude/procedures/${proc.name}`, {
|
|
29783
30561
|
lines: proc.content.split("\n"),
|
|
29784
30562
|
executable: true
|
|
29785
30563
|
});
|
|
@@ -29806,7 +30584,7 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29806
30584
|
lines.push("---");
|
|
29807
30585
|
lines.push("");
|
|
29808
30586
|
lines.push(...command.content.split("\n"));
|
|
29809
|
-
new
|
|
30587
|
+
new import_textfile2.TextFile(component, `.claude/commands/${command.name}.md`, { lines });
|
|
29810
30588
|
}
|
|
29811
30589
|
}
|
|
29812
30590
|
/**
|
|
@@ -29818,6 +30596,206 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
29818
30596
|
}
|
|
29819
30597
|
};
|
|
29820
30598
|
|
|
30599
|
+
// src/agent/renderers/cursor-renderer.ts
|
|
30600
|
+
var import_projen7 = require("projen");
|
|
30601
|
+
var import_textfile3 = require("projen/lib/textfile");
|
|
30602
|
+
var GENERATED_MARKER2 = "# ~~ Generated by @codedrifters/configulator. Edits welcome \u2014 please contribute improvements back. ~~";
|
|
30603
|
+
var CursorRenderer = class _CursorRenderer {
|
|
30604
|
+
/**
|
|
30605
|
+
* Render all Cursor configuration files.
|
|
30606
|
+
*/
|
|
30607
|
+
static render(component, rules, skills, subAgents, mcpServers, settings) {
|
|
30608
|
+
_CursorRenderer.renderRules(component, rules);
|
|
30609
|
+
_CursorRenderer.renderSkills(component, skills);
|
|
30610
|
+
_CursorRenderer.renderSubAgents(component, subAgents);
|
|
30611
|
+
_CursorRenderer.renderMcpServers(component, mcpServers);
|
|
30612
|
+
_CursorRenderer.renderHooks(component, settings);
|
|
30613
|
+
_CursorRenderer.renderIgnoreFiles(component, settings);
|
|
30614
|
+
}
|
|
30615
|
+
static renderRules(component, rules) {
|
|
30616
|
+
for (const rule of rules) {
|
|
30617
|
+
if (rule.platforms?.cursor?.exclude) continue;
|
|
30618
|
+
const lines = [];
|
|
30619
|
+
const description = rule.platforms?.cursor?.description ?? rule.description;
|
|
30620
|
+
const isAlways = rule.scope === AGENT_RULE_SCOPE.ALWAYS;
|
|
30621
|
+
lines.push("---");
|
|
30622
|
+
lines.push(`description: "${description}"`);
|
|
30623
|
+
lines.push(`alwaysApply: ${isAlways}`);
|
|
30624
|
+
if (!isAlways && rule.filePatterns && rule.filePatterns.length > 0) {
|
|
30625
|
+
lines.push(`path: ${JSON.stringify([...rule.filePatterns])}`);
|
|
30626
|
+
}
|
|
30627
|
+
lines.push("---");
|
|
30628
|
+
lines.push("");
|
|
30629
|
+
lines.push(...rule.content.split("\n"));
|
|
30630
|
+
new import_textfile3.TextFile(component, `.cursor/rules/${rule.name}.mdc`, { lines });
|
|
30631
|
+
}
|
|
30632
|
+
}
|
|
30633
|
+
static renderSkills(component, skills) {
|
|
30634
|
+
for (const skill of skills) {
|
|
30635
|
+
if (skill.platforms?.cursor?.exclude) continue;
|
|
30636
|
+
const lines = [];
|
|
30637
|
+
lines.push("---");
|
|
30638
|
+
lines.push(`name: "${skill.name}"`);
|
|
30639
|
+
lines.push(`description: "${skill.description}"`);
|
|
30640
|
+
if (skill.disableModelInvocation) {
|
|
30641
|
+
lines.push(`disable-model-invocation: true`);
|
|
30642
|
+
}
|
|
30643
|
+
if (skill.userInvocable === false) {
|
|
30644
|
+
lines.push(`user-invocable: false`);
|
|
30645
|
+
}
|
|
30646
|
+
if (skill.context) {
|
|
30647
|
+
lines.push(`context: "${skill.context}"`);
|
|
30648
|
+
}
|
|
30649
|
+
if (skill.agent) {
|
|
30650
|
+
lines.push(`agent: "${skill.agent}"`);
|
|
30651
|
+
}
|
|
30652
|
+
if (skill.shell) {
|
|
30653
|
+
lines.push(`shell: "${skill.shell}"`);
|
|
30654
|
+
}
|
|
30655
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
30656
|
+
lines.push(`allowed-tools:`);
|
|
30657
|
+
for (const tool of skill.allowedTools) {
|
|
30658
|
+
lines.push(` - "${tool}"`);
|
|
30659
|
+
}
|
|
30660
|
+
}
|
|
30661
|
+
lines.push("---");
|
|
30662
|
+
lines.push("");
|
|
30663
|
+
lines.push(...skill.instructions.split("\n"));
|
|
30664
|
+
new import_textfile3.TextFile(component, `.cursor/skills/${skill.name}/SKILL.md`, {
|
|
30665
|
+
lines
|
|
30666
|
+
});
|
|
30667
|
+
if (skill.referenceFiles && skill.referenceFiles.length > 0) {
|
|
30668
|
+
for (const file of skill.referenceFiles) {
|
|
30669
|
+
new import_textfile3.TextFile(component, `.cursor/skills/${skill.name}/${file.path}`, {
|
|
30670
|
+
lines: file.content.split("\n")
|
|
30671
|
+
});
|
|
30672
|
+
}
|
|
30673
|
+
}
|
|
30674
|
+
}
|
|
30675
|
+
}
|
|
30676
|
+
static renderSubAgents(component, subAgents) {
|
|
30677
|
+
for (const agent of subAgents) {
|
|
30678
|
+
if (agent.platforms?.cursor?.exclude) continue;
|
|
30679
|
+
const lines = [];
|
|
30680
|
+
lines.push("---");
|
|
30681
|
+
lines.push(`name: ${agent.name}`);
|
|
30682
|
+
lines.push(`description: >-`);
|
|
30683
|
+
lines.push(` ${agent.description}`);
|
|
30684
|
+
if (agent.platforms?.cursor?.readonly) {
|
|
30685
|
+
lines.push(`readonly: true`);
|
|
30686
|
+
}
|
|
30687
|
+
if (agent.platforms?.cursor?.isBackground) {
|
|
30688
|
+
lines.push(`is_background: true`);
|
|
30689
|
+
}
|
|
30690
|
+
lines.push("---");
|
|
30691
|
+
lines.push("");
|
|
30692
|
+
lines.push(...agent.prompt.split("\n"));
|
|
30693
|
+
new import_textfile3.TextFile(component, `.cursor/agents/${agent.name}.md`, { lines });
|
|
30694
|
+
}
|
|
30695
|
+
}
|
|
30696
|
+
static renderMcpServers(component, mcpServers) {
|
|
30697
|
+
const serverNames = Object.keys(mcpServers);
|
|
30698
|
+
if (serverNames.length === 0) return;
|
|
30699
|
+
const obj = { mcpServers: {} };
|
|
30700
|
+
const servers = obj.mcpServers;
|
|
30701
|
+
for (const [name, config] of Object.entries(mcpServers)) {
|
|
30702
|
+
const server = {};
|
|
30703
|
+
if (config.transport) server.transport = config.transport;
|
|
30704
|
+
if (config.command) server.command = config.command;
|
|
30705
|
+
if (config.args) server.args = [...config.args];
|
|
30706
|
+
if (config.url) server.url = config.url;
|
|
30707
|
+
if (config.headers && Object.keys(config.headers).length > 0) {
|
|
30708
|
+
server.headers = { ...config.headers };
|
|
30709
|
+
}
|
|
30710
|
+
if (config.env) server.env = { ...config.env };
|
|
30711
|
+
servers[name] = server;
|
|
30712
|
+
}
|
|
30713
|
+
new import_projen7.JsonFile(component, ".cursor/mcp.json", { obj });
|
|
30714
|
+
}
|
|
30715
|
+
static renderHooks(component, settings) {
|
|
30716
|
+
if (!settings?.hooks) return;
|
|
30717
|
+
const hooks = {};
|
|
30718
|
+
const hookEntries = settings.hooks;
|
|
30719
|
+
for (const [event, actions] of Object.entries(hookEntries)) {
|
|
30720
|
+
if (actions && actions.length > 0) {
|
|
30721
|
+
hooks[event] = actions.map((h) => ({
|
|
30722
|
+
command: h.command
|
|
30723
|
+
}));
|
|
30724
|
+
}
|
|
30725
|
+
}
|
|
30726
|
+
if (Object.keys(hooks).length === 0) return;
|
|
30727
|
+
new import_projen7.JsonFile(component, ".cursor/hooks.json", {
|
|
30728
|
+
obj: { version: 1, hooks }
|
|
30729
|
+
});
|
|
30730
|
+
}
|
|
30731
|
+
static renderIgnoreFiles(component, settings) {
|
|
30732
|
+
if (settings?.ignorePatterns && settings.ignorePatterns.length > 0) {
|
|
30733
|
+
new import_textfile3.TextFile(component, ".cursorignore", {
|
|
30734
|
+
lines: [GENERATED_MARKER2, "", ...settings.ignorePatterns]
|
|
30735
|
+
});
|
|
30736
|
+
}
|
|
30737
|
+
if (settings?.indexingIgnorePatterns && settings.indexingIgnorePatterns.length > 0) {
|
|
30738
|
+
new import_textfile3.TextFile(component, ".cursorindexingignore", {
|
|
30739
|
+
lines: [GENERATED_MARKER2, "", ...settings.indexingIgnorePatterns]
|
|
30740
|
+
});
|
|
30741
|
+
}
|
|
30742
|
+
}
|
|
30743
|
+
};
|
|
30744
|
+
|
|
30745
|
+
// src/agent/template-resolver.ts
|
|
30746
|
+
var FALLBACKS = {
|
|
30747
|
+
"repository.owner": "<owner>",
|
|
30748
|
+
"repository.name": "<repo>",
|
|
30749
|
+
"repository.defaultBranch": "main",
|
|
30750
|
+
"organization.name": "<organization>",
|
|
30751
|
+
"organization.githubOrg": "<org>",
|
|
30752
|
+
"githubProject.name": "<project-name>",
|
|
30753
|
+
"githubProject.number": "<project-number>",
|
|
30754
|
+
"githubProject.nodeId": "<project-node-id>",
|
|
30755
|
+
docsPath: "<docs-path>",
|
|
30756
|
+
// The monorepo-layout seed block is additive: when absent, the
|
|
30757
|
+
// seeded `project-context.md` template reads cleanly without it.
|
|
30758
|
+
// Fall back to an empty string so no placeholder text leaks into
|
|
30759
|
+
// rendered prompts for repos that predate the layout contract.
|
|
30760
|
+
monorepoLayoutSeedBlock: ""
|
|
30761
|
+
};
|
|
30762
|
+
var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
|
|
30763
|
+
function getNestedValue(obj, path8) {
|
|
30764
|
+
const parts = path8.split(".");
|
|
30765
|
+
let current = obj;
|
|
30766
|
+
for (const part of parts) {
|
|
30767
|
+
if (current == null || typeof current !== "object") {
|
|
30768
|
+
return void 0;
|
|
30769
|
+
}
|
|
30770
|
+
current = current[part];
|
|
30771
|
+
}
|
|
30772
|
+
if (current == null) {
|
|
30773
|
+
return void 0;
|
|
30774
|
+
}
|
|
30775
|
+
return String(current);
|
|
30776
|
+
}
|
|
30777
|
+
function resolveTemplateVariables(template, metadata) {
|
|
30778
|
+
if (!TEMPLATE_RE.test(template)) {
|
|
30779
|
+
return { resolved: template, unresolvedKeys: [] };
|
|
30780
|
+
}
|
|
30781
|
+
const unresolvedKeys = [];
|
|
30782
|
+
TEMPLATE_RE.lastIndex = 0;
|
|
30783
|
+
const resolved = template.replace(TEMPLATE_RE, (_match, key) => {
|
|
30784
|
+
if (metadata) {
|
|
30785
|
+
const value = getNestedValue(
|
|
30786
|
+
metadata,
|
|
30787
|
+
key
|
|
30788
|
+
);
|
|
30789
|
+
if (value !== void 0) {
|
|
30790
|
+
return value;
|
|
30791
|
+
}
|
|
30792
|
+
}
|
|
30793
|
+
unresolvedKeys.push(key);
|
|
30794
|
+
return FALLBACKS[key] ?? `<${key}>`;
|
|
30795
|
+
});
|
|
30796
|
+
return { resolved, unresolvedKeys };
|
|
30797
|
+
}
|
|
30798
|
+
|
|
29821
30799
|
// src/agent/renderers/codex-renderer.ts
|
|
29822
30800
|
var CodexRenderer = class {
|
|
29823
30801
|
static render(_component, _rules, _skills, _subAgents) {
|
|
@@ -30034,6 +31012,7 @@ var SHARED_EDITING_BUNDLE_HOOKS = [
|
|
|
30034
31012
|
["customer-profile-workflow", "customer-profile"],
|
|
30035
31013
|
["industry-discovery-workflow", "industry-discovery"],
|
|
30036
31014
|
["meeting-agenda-workflow", "agenda"],
|
|
31015
|
+
["meeting-processing-workflow", "meeting-analyst"],
|
|
30037
31016
|
["people-profile-workflow", "people-profile"],
|
|
30038
31017
|
["regulatory-research-workflow", "regulatory-research"],
|
|
30039
31018
|
["requirements-reviewer-workflow", "requirements-reviewer"],
|
|
@@ -30223,7 +31202,8 @@ var AgentConfig = class _AgentConfig extends import_projen8.Component {
|
|
|
30223
31202
|
this.resolvedPaths,
|
|
30224
31203
|
resolveIssueDefaults(this.options.issueDefaults),
|
|
30225
31204
|
resolveDefaultAgentTier(this.options),
|
|
30226
|
-
resolveBundleAgentTiers(this.options)
|
|
31205
|
+
resolveBundleAgentTiers(this.options),
|
|
31206
|
+
resolvePrReviewPolicy(this.options.prReviewPolicy)
|
|
30227
31207
|
);
|
|
30228
31208
|
}
|
|
30229
31209
|
return this.cachedBundles;
|
|
@@ -30265,6 +31245,7 @@ var AgentConfig = class _AgentConfig extends import_projen8.Component {
|
|
|
30265
31245
|
super.preSynthesize();
|
|
30266
31246
|
validateAgentTierConfig(this.options.tiers);
|
|
30267
31247
|
validateScopeGateConfig(this.options.scopeGate);
|
|
31248
|
+
validatePrReviewPolicyConfig(this.options.prReviewPolicy);
|
|
30268
31249
|
const resolvedRunRatio = resolveRunRatio(this.options.runRatio);
|
|
30269
31250
|
if (resolvedRunRatio.enabled) {
|
|
30270
31251
|
this.project.gitignore.addPatterns(`/${resolvedRunRatio.stateFilePath}`);
|
|
@@ -30457,6 +31438,20 @@ ${extra}`
|
|
|
30457
31438
|
}
|
|
30458
31439
|
}
|
|
30459
31440
|
}
|
|
31441
|
+
if (this.options.additionalRulePaths) {
|
|
31442
|
+
for (const [name, extraPaths] of Object.entries(
|
|
31443
|
+
this.options.additionalRulePaths
|
|
31444
|
+
)) {
|
|
31445
|
+
if (extraPaths.length === 0) continue;
|
|
31446
|
+
const existing = ruleMap.get(name);
|
|
31447
|
+
if (!existing) continue;
|
|
31448
|
+
if (existing.scope !== AGENT_RULE_SCOPE.FILE_PATTERN) continue;
|
|
31449
|
+
ruleMap.set(name, {
|
|
31450
|
+
...existing,
|
|
31451
|
+
filePatterns: [...existing.filePatterns ?? [], ...extraPaths]
|
|
31452
|
+
});
|
|
31453
|
+
}
|
|
31454
|
+
}
|
|
30460
31455
|
if (this.options.priorityRules && this.options.priorityRules.length > 0) {
|
|
30461
31456
|
const issueLabelRule = ruleMap.get("issue-label-conventions");
|
|
30462
31457
|
if (issueLabelRule) {
|
|
@@ -30663,9 +31658,6 @@ ${hook}`
|
|
|
30663
31658
|
}
|
|
30664
31659
|
}
|
|
30665
31660
|
}
|
|
30666
|
-
if (!hasAnyDocsEmittingBundle(excludedBundleNames)) {
|
|
30667
|
-
ruleMap.delete("stub-index-convention");
|
|
30668
|
-
}
|
|
30669
31661
|
if (injectBundleHooks && resolvedIssueTemplatesForRules.enabled && hasDownstreamBundles) {
|
|
30670
31662
|
for (const [ruleName, label] of ISSUE_TEMPLATES_BUNDLE_HOOKS) {
|
|
30671
31663
|
const existing = ruleMap.get(ruleName);
|
|
@@ -30782,6 +31774,12 @@ ${meetingsSection}`;
|
|
|
30782
31774
|
});
|
|
30783
31775
|
}
|
|
30784
31776
|
}
|
|
31777
|
+
const conventionsRegistryRule = buildConventionsRegistryRule(
|
|
31778
|
+
ruleMap.values()
|
|
31779
|
+
);
|
|
31780
|
+
if (conventionsRegistryRule) {
|
|
31781
|
+
ruleMap.set(conventionsRegistryRule.name, conventionsRegistryRule);
|
|
31782
|
+
}
|
|
30785
31783
|
return [...ruleMap.values()].sort((a, b) => {
|
|
30786
31784
|
if (a.name === "project-overview") return -1;
|
|
30787
31785
|
if (b.name === "project-overview") return 1;
|
|
@@ -36791,6 +37789,7 @@ export const collections = {
|
|
|
36791
37789
|
DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
|
|
36792
37790
|
DEFAULT_OFF_PEAK_CRON_EXAMPLE,
|
|
36793
37791
|
DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
|
|
37792
|
+
DEFAULT_PATHS_EXEMPT_FROM_SIZE,
|
|
36794
37793
|
DEFAULT_PRIORITY_LABELS,
|
|
36795
37794
|
DEFAULT_PRODUCT_CONTEXT_PATH,
|
|
36796
37795
|
DEFAULT_PROGRESS_FILES_ENABLED,
|
|
@@ -36893,6 +37892,7 @@ export const collections = {
|
|
|
36893
37892
|
buildMeetingAnalysisBundle,
|
|
36894
37893
|
buildOrchestratorConventionsContent,
|
|
36895
37894
|
buildPeopleProfileBundle,
|
|
37895
|
+
buildPrReviewBundle,
|
|
36896
37896
|
buildRegulatoryResearchBundle,
|
|
36897
37897
|
buildReport,
|
|
36898
37898
|
buildRequirementsAnalystBundle,
|
|
@@ -37002,7 +38002,6 @@ export const collections = {
|
|
|
37002
38002
|
renderSkillEvalsRuleContent,
|
|
37003
38003
|
renderSkillEvalsRunnerScript,
|
|
37004
38004
|
renderSourceTierExamples,
|
|
37005
|
-
renderStubIndexConventionRuleContent,
|
|
37006
38005
|
renderTemporalFramingCheckerScript,
|
|
37007
38006
|
renderTemporalFramingRuleContent,
|
|
37008
38007
|
renderUnblockDependentsScript,
|
|
@@ -37023,6 +38022,7 @@ export const collections = {
|
|
|
37023
38022
|
resolveOrchestratorAssets,
|
|
37024
38023
|
resolveOutdirFromPackageName,
|
|
37025
38024
|
resolveOverrideForLabels,
|
|
38025
|
+
resolvePrReviewPolicy,
|
|
37026
38026
|
resolveProgressFiles,
|
|
37027
38027
|
resolveReactViteSiteProjectOutdir,
|
|
37028
38028
|
resolveRunRatio,
|
|
@@ -37046,6 +38046,7 @@ export const collections = {
|
|
|
37046
38046
|
validateIssueDefaultsConfig,
|
|
37047
38047
|
validateIssueTemplatesConfig,
|
|
37048
38048
|
validateMonorepoLayout,
|
|
38049
|
+
validatePrReviewPolicyConfig,
|
|
37049
38050
|
validateProgressFilesConfig,
|
|
37050
38051
|
validateRunRatioConfig,
|
|
37051
38052
|
validateScheduledTasksConfig,
|