@codedrifters/configulator 0.0.406 → 0.0.408
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 +2141 -1891
- package/lib/index.d.ts +2142 -1892
- package/lib/index.js +609 -223
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +599 -223
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -1830,12 +1830,111 @@ var awsCdkBundle = {
|
|
|
1830
1830
|
]
|
|
1831
1831
|
};
|
|
1832
1832
|
|
|
1833
|
+
// src/agent/bundles/issue-defaults.ts
|
|
1834
|
+
var VALID_STATUS_VALUES = [
|
|
1835
|
+
"ready",
|
|
1836
|
+
"blocked",
|
|
1837
|
+
"in-progress",
|
|
1838
|
+
"ready-for-review",
|
|
1839
|
+
"needs-attention",
|
|
1840
|
+
"done",
|
|
1841
|
+
"deferred"
|
|
1842
|
+
];
|
|
1843
|
+
var VALID_PRIORITY_VALUES = [
|
|
1844
|
+
"critical",
|
|
1845
|
+
"high",
|
|
1846
|
+
"medium",
|
|
1847
|
+
"low",
|
|
1848
|
+
"trivial"
|
|
1849
|
+
];
|
|
1850
|
+
var DEFAULT_ISSUE_STATUS = "ready";
|
|
1851
|
+
var DEFAULT_ISSUE_PRIORITY = "medium";
|
|
1852
|
+
var DEFAULT_RESOLVED_ISSUE_DEFAULTS = {
|
|
1853
|
+
defaults: {
|
|
1854
|
+
status: DEFAULT_ISSUE_STATUS,
|
|
1855
|
+
priority: DEFAULT_ISSUE_PRIORITY
|
|
1856
|
+
},
|
|
1857
|
+
overrides: {}
|
|
1858
|
+
};
|
|
1859
|
+
function resolveIssueDefaults(config) {
|
|
1860
|
+
if (config === void 0) {
|
|
1861
|
+
return DEFAULT_RESOLVED_ISSUE_DEFAULTS;
|
|
1862
|
+
}
|
|
1863
|
+
const overrides = {};
|
|
1864
|
+
for (const [phaseLabel, override] of Object.entries(config)) {
|
|
1865
|
+
assertValidPhaseLabel(phaseLabel);
|
|
1866
|
+
assertValidOverride(phaseLabel, override);
|
|
1867
|
+
overrides[phaseLabel] = {
|
|
1868
|
+
status: override.status ?? DEFAULT_ISSUE_STATUS,
|
|
1869
|
+
priority: override.priority ?? DEFAULT_ISSUE_PRIORITY
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
defaults: {
|
|
1874
|
+
status: DEFAULT_ISSUE_STATUS,
|
|
1875
|
+
priority: DEFAULT_ISSUE_PRIORITY
|
|
1876
|
+
},
|
|
1877
|
+
overrides
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
function validateIssueDefaultsConfig(config) {
|
|
1881
|
+
return resolveIssueDefaults(config);
|
|
1882
|
+
}
|
|
1883
|
+
function labelsForPhase(resolved, phaseLabel) {
|
|
1884
|
+
return resolved.overrides[phaseLabel] ?? resolved.defaults;
|
|
1885
|
+
}
|
|
1886
|
+
function assertValidPhaseLabel(phaseLabel) {
|
|
1887
|
+
if (typeof phaseLabel !== "string" || phaseLabel.trim() === "") {
|
|
1888
|
+
throw new Error(
|
|
1889
|
+
"AgentConfigOptions.issueDefaults: phase-label keys must be non-empty strings (e.g. `people:research`)."
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function assertValidOverride(phaseLabel, override) {
|
|
1894
|
+
if (override === null || typeof override !== "object" || Array.isArray(override)) {
|
|
1895
|
+
throw new Error(
|
|
1896
|
+
`AgentConfigOptions.issueDefaults["${phaseLabel}"] must be an object with optional \`status\` and \`priority\` fields.`
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
const { status, priority } = override;
|
|
1900
|
+
if (status === void 0 && priority === void 0) {
|
|
1901
|
+
throw new Error(
|
|
1902
|
+
`AgentConfigOptions.issueDefaults["${phaseLabel}"] must declare at least one of \`status\` or \`priority\`. Empty entries are rejected because they are almost always a typo on the field name.`
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
if (status !== void 0 && !VALID_STATUS_VALUES.includes(status)) {
|
|
1906
|
+
throw new Error(
|
|
1907
|
+
`AgentConfigOptions.issueDefaults["${phaseLabel}"].status="${status}" is not a recognised status value. Allowed values: ${VALID_STATUS_VALUES.join(", ")}.`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
if (priority !== void 0 && !VALID_PRIORITY_VALUES.includes(priority)) {
|
|
1911
|
+
throw new Error(
|
|
1912
|
+
`AgentConfigOptions.issueDefaults["${phaseLabel}"].priority="${priority}" is not a recognised priority value. Allowed values: ${VALID_PRIORITY_VALUES.join(", ")}.`
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1833
1917
|
// src/agent/bundles/issue-templates.ts
|
|
1834
1918
|
var DEFAULT_ISSUE_TEMPLATES_ENABLED = true;
|
|
1835
1919
|
var DEFAULT_ISSUE_TEMPLATES_PATH = "docs/src/content/docs/agents/issue-templates.md";
|
|
1836
1920
|
var DEFAULT_ISSUE_TEMPLATES_BUNDLE_PATH_PATTERNS = [".claude/agents/**/*.md", ".claude/skills/**/*.md"];
|
|
1837
1921
|
var DEFAULT_ISSUE_TEMPLATES_EMIT_CHECKER = false;
|
|
1838
1922
|
var DEFAULT_ISSUE_TEMPLATES_EMIT_STARTER = false;
|
|
1923
|
+
var ISSUE_TEMPLATES_GENERATED_SUFFIX = "-generated";
|
|
1924
|
+
function issueTemplatesGeneratedPath(templatesPath) {
|
|
1925
|
+
const slash = templatesPath.lastIndexOf("/");
|
|
1926
|
+
const dot = templatesPath.lastIndexOf(".");
|
|
1927
|
+
if (dot > slash) {
|
|
1928
|
+
return `${templatesPath.slice(0, dot)}${ISSUE_TEMPLATES_GENERATED_SUFFIX}${templatesPath.slice(dot)}`;
|
|
1929
|
+
}
|
|
1930
|
+
return `${templatesPath}${ISSUE_TEMPLATES_GENERATED_SUFFIX}`;
|
|
1931
|
+
}
|
|
1932
|
+
function issueTemplatesChildGlob(templatesPath) {
|
|
1933
|
+
const slash = templatesPath.lastIndexOf("/");
|
|
1934
|
+
const dot = templatesPath.lastIndexOf(".");
|
|
1935
|
+
const stem = dot > slash ? templatesPath.slice(0, dot) : templatesPath;
|
|
1936
|
+
return `${stem}/*.md`;
|
|
1937
|
+
}
|
|
1839
1938
|
var DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE = true;
|
|
1840
1939
|
function resolveIssueTemplates(config) {
|
|
1841
1940
|
const templatesPath = config?.templatesPath ?? DEFAULT_ISSUE_TEMPLATES_PATH;
|
|
@@ -1919,7 +2018,40 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
|
|
|
1919
2018
|
"issue kind appends one section; retiring a kind removes one. The",
|
|
1920
2019
|
"label taxonomy, body template shape, and placeholder conventions",
|
|
1921
2020
|
"are shared across every section.",
|
|
1922
|
-
""
|
|
2021
|
+
""
|
|
2022
|
+
];
|
|
2023
|
+
if (it.emitStarterDoc) {
|
|
2024
|
+
lines.push(
|
|
2025
|
+
"## Scaffolded Starting Point",
|
|
2026
|
+
"",
|
|
2027
|
+
"The page is hand-authored, but it does not start empty. Two files",
|
|
2028
|
+
"ship together:",
|
|
2029
|
+
"",
|
|
2030
|
+
`1. \`${it.templatesPath}\` \u2014 **written once**, then yours. Titles,`,
|
|
2031
|
+
" bodies, and the callers list are hand-authored here and never",
|
|
2032
|
+
" overwritten.",
|
|
2033
|
+
`2. \`${issueTemplatesGeneratedPath(it.templatesPath)}\` \u2014`,
|
|
2034
|
+
" **regenerated on every synth**. One",
|
|
2035
|
+
" `## Template: <phase-label>` stub per phase label the active",
|
|
2036
|
+
" bundles contribute to the label registry, with the phase label,",
|
|
2037
|
+
" the `type:<bundle>` label its owning bundle declares, and the",
|
|
2038
|
+
" configured `status:*` / `priority:*` defaults pre-filled.",
|
|
2039
|
+
"",
|
|
2040
|
+
"Only the **label set** is generated. Title and body stay",
|
|
2041
|
+
"angle-bracket placeholders, so the scaffold answers *which labels*",
|
|
2042
|
+
"without dictating *what the issue says*. Copy a stub into the",
|
|
2043
|
+
"hand-authored page, flesh out its body, and leave the label block",
|
|
2044
|
+
"as generated.",
|
|
2045
|
+
"",
|
|
2046
|
+
"The split is load-bearing. A write-once scaffold alone freezes",
|
|
2047
|
+
"every consumer on whatever skeleton shipped the day they adopted",
|
|
2048
|
+
"the convention \u2014 a new phase label minted upstream never reaches",
|
|
2049
|
+
"them. Regenerating the label half fixes that without ever",
|
|
2050
|
+
"clobbering an authored body.",
|
|
2051
|
+
""
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
lines.push(
|
|
1923
2055
|
"## Reference-Don't-Inline Rule",
|
|
1924
2056
|
"",
|
|
1925
2057
|
`Every bundle rule, agent prompt, or skill instruction that files a`,
|
|
@@ -1990,8 +2122,12 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
|
|
|
1990
2122
|
" placeholders (`<Organization Name>`, `<slug>`) that match",
|
|
1991
2123
|
" existing templates. Consistency across sections lets callers",
|
|
1992
2124
|
" substitute without re-reading the body every time.",
|
|
2125
|
+
"5. **Does the recipe assign a GitHub issue type?** `gh issue",
|
|
2126
|
+
" create` cannot set it and the `type:*` label does not either, so",
|
|
2127
|
+
" every section ends with the follow-up call. An issue that skips",
|
|
2128
|
+
" it stays untyped forever.",
|
|
1993
2129
|
""
|
|
1994
|
-
|
|
2130
|
+
);
|
|
1995
2131
|
if (it.emitChecker) {
|
|
1996
2132
|
lines.push(
|
|
1997
2133
|
"## Automated Lint",
|
|
@@ -2022,6 +2158,33 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
|
|
|
2022
2158
|
"(which documents the conventional `gh issue create` call shape",
|
|
2023
2159
|
"as taxonomy, not a downstream template). All other bundle",
|
|
2024
2160
|
"files in the pattern set are in scope.",
|
|
2161
|
+
"",
|
|
2162
|
+
"### Recipe label correctness",
|
|
2163
|
+
"",
|
|
2164
|
+
"A companion lint,",
|
|
2165
|
+
"`.claude/procedures/check-issue-template-labels.sh`, checks the",
|
|
2166
|
+
"recipes themselves rather than where they live. It walks the",
|
|
2167
|
+
"templates page, its router-style child pages, and the generated",
|
|
2168
|
+
"label-set companion, and fails non-zero when a",
|
|
2169
|
+
"`## Template: <phase-label>` section:",
|
|
2170
|
+
"",
|
|
2171
|
+
"- never passes `--label <phase-label>` \u2014 the heading and the",
|
|
2172
|
+
" command disagree;",
|
|
2173
|
+
"- carries no `type:*` label, more than one, or one that is not",
|
|
2174
|
+
" the `type:<bundle>` label the phase label's owning bundle",
|
|
2175
|
+
" declares;",
|
|
2176
|
+
"- has no GitHub issue-type assignment step.",
|
|
2177
|
+
"",
|
|
2178
|
+
"```bash",
|
|
2179
|
+
".claude/procedures/check-issue-template-labels.sh",
|
|
2180
|
+
"```",
|
|
2181
|
+
"",
|
|
2182
|
+
"The phase-label \u2192 type-label pairing is generated from the same",
|
|
2183
|
+
"bundle-ownership map that drives `.github/labels.yml` and the",
|
|
2184
|
+
"orchestrator's invariant sweep, so a hand-edit can never",
|
|
2185
|
+
"reintroduce a mismatch the registry has already ruled out.",
|
|
2186
|
+
"Sections whose heading matches no bundle-owned phase label are",
|
|
2187
|
+
"skipped \u2014 consumer-specific labels are deliberately not policed.",
|
|
2025
2188
|
""
|
|
2026
2189
|
);
|
|
2027
2190
|
}
|
|
@@ -2030,8 +2193,10 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
|
|
|
2030
2193
|
"",
|
|
2031
2194
|
"The convention intentionally does not:",
|
|
2032
2195
|
"",
|
|
2033
|
-
"- Auto-generate
|
|
2034
|
-
"
|
|
2196
|
+
"- Auto-generate recipe **bodies**. Only the label set is",
|
|
2197
|
+
" scaffolded, because labels are derivable from the bundle",
|
|
2198
|
+
" registry and bodies are not \u2014 repo-specific objectives,",
|
|
2199
|
+
" acceptance criteria, and callers lists stay hand-authored.",
|
|
2035
2200
|
"- Prescribe a fixed body shape across templates. Each section",
|
|
2036
2201
|
" may carry its own `## Acceptance Criteria`, `## Scope Size`,",
|
|
2037
2202
|
" or `## Output Path` fields as the downstream agent requires;",
|
|
@@ -2062,7 +2227,8 @@ function renderIssueTemplatesBundleHook(it, bundleLabel) {
|
|
|
2062
2227
|
"as an inline template vs. a prose mention."
|
|
2063
2228
|
].join("\n");
|
|
2064
2229
|
}
|
|
2065
|
-
function renderIssueTemplatesStarterPage(
|
|
2230
|
+
function renderIssueTemplatesStarterPage(it) {
|
|
2231
|
+
const generatedLink = `./${basename(issueTemplatesGeneratedPath(it.templatesPath))}`;
|
|
2066
2232
|
return [
|
|
2067
2233
|
"---",
|
|
2068
2234
|
"title: Issue Templates",
|
|
@@ -2097,6 +2263,20 @@ function renderIssueTemplatesStarterPage(_it) {
|
|
|
2097
2263
|
"bundle's rule \u2014 the issue-templates page is a recipe catalogue,",
|
|
2098
2264
|
"not a duplicate-check layer.",
|
|
2099
2265
|
"",
|
|
2266
|
+
"## Generated label sets",
|
|
2267
|
+
"",
|
|
2268
|
+
`[Generated Issue-Template Label Sets](${generatedLink}) carries one`,
|
|
2269
|
+
"correct-by-construction stub per phase label the active bundles",
|
|
2270
|
+
"emit \u2014 the phase label paired with the `type:<bundle>` label its",
|
|
2271
|
+
"owning bundle declares, plus the configured `status:*` /",
|
|
2272
|
+
"`priority:*` defaults. That page regenerates on every `projen` run,",
|
|
2273
|
+
"so it never goes stale as phase labels come and go.",
|
|
2274
|
+
"",
|
|
2275
|
+
"This page is the opposite: written once, then owned by you. Copy a",
|
|
2276
|
+
"stub across, flesh out its title and body here, and leave the label",
|
|
2277
|
+
"block exactly as generated \u2014 the label-consistency lint holds both",
|
|
2278
|
+
"copies to the same pairing.",
|
|
2279
|
+
"",
|
|
2100
2280
|
"## Template: example-phase",
|
|
2101
2281
|
"",
|
|
2102
2282
|
"Replace this example with the real templates for this repo. Each",
|
|
@@ -2136,6 +2316,115 @@ function renderIssueTemplatesStarterPage(_it) {
|
|
|
2136
2316
|
"```"
|
|
2137
2317
|
].join("\n");
|
|
2138
2318
|
}
|
|
2319
|
+
function collectIssueTemplateRecipeStubs(bundles, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS) {
|
|
2320
|
+
const stubs = /* @__PURE__ */ new Map();
|
|
2321
|
+
for (const bundle of bundles) {
|
|
2322
|
+
for (const label of bundle.labels ?? []) {
|
|
2323
|
+
const typeLabel = typeLabelForPhaseLabel(label.name);
|
|
2324
|
+
if (typeLabel === void 0) {
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
if (stubs.has(label.name)) {
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
const defaults = labelsForPhase(issueDefaults, label.name);
|
|
2331
|
+
stubs.set(label.name, {
|
|
2332
|
+
phaseLabel: label.name,
|
|
2333
|
+
typeLabel,
|
|
2334
|
+
bundleName: bundle.name,
|
|
2335
|
+
description: label.description ?? "",
|
|
2336
|
+
status: defaults.status,
|
|
2337
|
+
priority: defaults.priority,
|
|
2338
|
+
issueType: githubIssueTypeForTitle(`${label.name}: placeholder`)
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return [...stubs.values()].sort(
|
|
2343
|
+
(a, b) => a.phaseLabel.localeCompare(b.phaseLabel)
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
function renderIssueTemplatesGeneratedPage(it, stubs) {
|
|
2347
|
+
const lines = [
|
|
2348
|
+
"---",
|
|
2349
|
+
"title: Generated Issue-Template Label Sets",
|
|
2350
|
+
"description: Auto-generated label-correct gh issue create stubs \u2014 one per phase label.",
|
|
2351
|
+
"---",
|
|
2352
|
+
"",
|
|
2353
|
+
"**Generated file \u2014 do not edit.** Every section below is",
|
|
2354
|
+
"regenerated from the active bundle set on each `projen` run; hand",
|
|
2355
|
+
"edits are overwritten. The hand-authored recipes live in",
|
|
2356
|
+
`[the issue-templates page](./${basename(it.templatesPath)}) \u2014 this`,
|
|
2357
|
+
"page exists so the **label half** of every recipe stays",
|
|
2358
|
+
"correct-by-construction as phase labels come and go.",
|
|
2359
|
+
"",
|
|
2360
|
+
"## How to use",
|
|
2361
|
+
"",
|
|
2362
|
+
"Each section carries the exact label set the phase-label invariant",
|
|
2363
|
+
"requires: the phase label itself, the `type:<bundle>` label its",
|
|
2364
|
+
"owning bundle declares, and the configured `status:*` / `priority:*`",
|
|
2365
|
+
"defaults for that phase. Title and body are placeholders \u2014 copy the",
|
|
2366
|
+
"stub into the hand-authored templates page and flesh out the body",
|
|
2367
|
+
"there, or run it as-is when no repo-specific body exists yet.",
|
|
2368
|
+
"",
|
|
2369
|
+
"Every stub ends with the **GitHub issue type** assignment. `gh issue",
|
|
2370
|
+
"create` cannot set the issue type and the `type:*` label does not",
|
|
2371
|
+
"set it either, so an issue that skips the follow-up call stays",
|
|
2372
|
+
"untyped forever. See **Assigning the GitHub issue type** in the",
|
|
2373
|
+
"`issue-conventions` rule for the no-helper fallback.",
|
|
2374
|
+
""
|
|
2375
|
+
];
|
|
2376
|
+
if (stubs.length === 0) {
|
|
2377
|
+
lines.push(
|
|
2378
|
+
"## No phase labels",
|
|
2379
|
+
"",
|
|
2380
|
+
"No active bundle contributes a phase label, so there is nothing to",
|
|
2381
|
+
"scaffold. Enable a phased-pipeline bundle (or drop it from",
|
|
2382
|
+
"`excludeBundles`) and re-run `projen`."
|
|
2383
|
+
);
|
|
2384
|
+
return lines.join("\n");
|
|
2385
|
+
}
|
|
2386
|
+
for (const stub of stubs) {
|
|
2387
|
+
lines.push(
|
|
2388
|
+
`## Template: ${stub.phaseLabel}`,
|
|
2389
|
+
"",
|
|
2390
|
+
...stub.description === "" ? [] : [stub.description, ""],
|
|
2391
|
+
`**Owning bundle:** \`${stub.bundleName}\` \u2014 **required type label:**`,
|
|
2392
|
+
`\`${stub.typeLabel}\` (paired by the bundle-ownership map).`,
|
|
2393
|
+
"",
|
|
2394
|
+
"**Callers:** <name the bundles and agents that file this issue>",
|
|
2395
|
+
"",
|
|
2396
|
+
"```bash",
|
|
2397
|
+
"gh issue create \\",
|
|
2398
|
+
` --title "${stub.phaseLabel}: <short description>" \\`,
|
|
2399
|
+
` --label "${stub.typeLabel}" \\`,
|
|
2400
|
+
` --label "${stub.phaseLabel}" \\`,
|
|
2401
|
+
` --label "priority:${stub.priority}" \\`,
|
|
2402
|
+
` --label "status:${stub.status}" \\`,
|
|
2403
|
+
' --body "## Objective',
|
|
2404
|
+
"",
|
|
2405
|
+
"<1-3 sentences describing the work>.",
|
|
2406
|
+
"",
|
|
2407
|
+
"## Context",
|
|
2408
|
+
"",
|
|
2409
|
+
"- **Discovered in:** <source description> (#<parent-issue>)",
|
|
2410
|
+
"",
|
|
2411
|
+
"## Acceptance Criteria",
|
|
2412
|
+
"",
|
|
2413
|
+
"- [ ] <criterion 1>",
|
|
2414
|
+
'"',
|
|
2415
|
+
"```",
|
|
2416
|
+
"",
|
|
2417
|
+
`Then assign the GitHub issue type (\`${stub.issueType}\`):`,
|
|
2418
|
+
"",
|
|
2419
|
+
"```bash",
|
|
2420
|
+
`${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> ${stub.issueType}`,
|
|
2421
|
+
"```",
|
|
2422
|
+
""
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
lines.pop();
|
|
2426
|
+
return lines.join("\n");
|
|
2427
|
+
}
|
|
2139
2428
|
function renderIssueTemplatesCheckerScript(it) {
|
|
2140
2429
|
const patternsLiteral = it.bundlePathPatterns.map((p) => ` ${JSON.stringify(p)}`).join("\n");
|
|
2141
2430
|
return [
|
|
@@ -2324,6 +2613,157 @@ function renderIssueTemplatesCheckerScript(it) {
|
|
|
2324
2613
|
"exit 0"
|
|
2325
2614
|
].join("\n");
|
|
2326
2615
|
}
|
|
2616
|
+
function renderIssueTemplateLabelsCheckerScript(it) {
|
|
2617
|
+
const generatedPath = issueTemplatesGeneratedPath(it.templatesPath);
|
|
2618
|
+
const childGlob = issueTemplatesChildGlob(it.templatesPath);
|
|
2619
|
+
return [
|
|
2620
|
+
"#!/usr/bin/env bash",
|
|
2621
|
+
"# check-issue-template-labels.sh \u2014 Enforce recipe label correctness.",
|
|
2622
|
+
"#",
|
|
2623
|
+
"# Usage:",
|
|
2624
|
+
"# .claude/procedures/check-issue-template-labels.sh [<file>...]",
|
|
2625
|
+
"#",
|
|
2626
|
+
"# With no arguments the lint walks the templates page, its",
|
|
2627
|
+
"# router-style child pages, and the generated label-set companion.",
|
|
2628
|
+
"# Positional arguments override that default set.",
|
|
2629
|
+
"#",
|
|
2630
|
+
"# For every `## Template: <phase-label>` section it asserts the",
|
|
2631
|
+
"# recipe passes the heading's phase label, carries exactly one",
|
|
2632
|
+
"# `type:*` label matching the phase-label invariant, and assigns a",
|
|
2633
|
+
"# GitHub issue type. Sections whose heading matches no bundle-owned",
|
|
2634
|
+
"# phase label are skipped \u2014 unrecognised labels are consumer-",
|
|
2635
|
+
"# specific and deliberately not policed.",
|
|
2636
|
+
"#",
|
|
2637
|
+
"# The resolver below is generated from the canonical bundle-",
|
|
2638
|
+
"# ownership map \u2014 do not edit by hand; regenerate via",
|
|
2639
|
+
"# `pnpm exec projen`.",
|
|
2640
|
+
"",
|
|
2641
|
+
"set -uo pipefail",
|
|
2642
|
+
"",
|
|
2643
|
+
"err() {",
|
|
2644
|
+
' printf "check-issue-template-labels.sh: %s\\n" "$*" >&2',
|
|
2645
|
+
"}",
|
|
2646
|
+
"",
|
|
2647
|
+
renderPhaseTypeInvariantShellHelpers(),
|
|
2648
|
+
"",
|
|
2649
|
+
`templates_path=${JSON.stringify(it.templatesPath)}`,
|
|
2650
|
+
`generated_path=${JSON.stringify(generatedPath)}`,
|
|
2651
|
+
`child_glob=${JSON.stringify(childGlob)}`,
|
|
2652
|
+
"",
|
|
2653
|
+
"# Emit one TAB-separated record per `## Template:` section:",
|
|
2654
|
+
"# <file>\\t<line>\\t<phase-label>\\t<comma-joined labels>\\t<0|1 typed>",
|
|
2655
|
+
"# Quote characters are folded to spaces up front so a recipe written",
|
|
2656
|
+
"# with single quotes, double quotes, or none at all parses the same.",
|
|
2657
|
+
"# Fenced blocks are tracked so the `## Objective` heading inside a",
|
|
2658
|
+
"# recipe's --body string never reads as the end of the section.",
|
|
2659
|
+
"scan_file() {",
|
|
2660
|
+
' local file="$1"',
|
|
2661
|
+
` tr '\\042\\047' ' ' < "$file" | awk -v FNAME="$file" '`,
|
|
2662
|
+
" function flush() {",
|
|
2663
|
+
' if (phase != "") {',
|
|
2664
|
+
' printf "%s\\t%d\\t%s\\t%s\\t%d\\n", FNAME, start, phase, labels, hastype;',
|
|
2665
|
+
" }",
|
|
2666
|
+
' phase = ""; labels = ""; hastype = 0;',
|
|
2667
|
+
" }",
|
|
2668
|
+
" {",
|
|
2669
|
+
" if ($0 ~ /^[ \\t]*```/) {",
|
|
2670
|
+
" fence = 1 - fence;",
|
|
2671
|
+
' } else if (fence == 0 && index($0, "## ") == 1) {',
|
|
2672
|
+
" flush();",
|
|
2673
|
+
' if (index($0, "## Template: ") == 1) {',
|
|
2674
|
+
" phase = substr($0, 14);",
|
|
2675
|
+
' gsub(/[ \\t`]/, "", phase);',
|
|
2676
|
+
" start = NR;",
|
|
2677
|
+
" }",
|
|
2678
|
+
" next;",
|
|
2679
|
+
" }",
|
|
2680
|
+
' if (phase == "") next;',
|
|
2681
|
+
" if ($0 ~ /set-issue-type\\.sh/ || $0 ~ /updateIssueIssueType/) {",
|
|
2682
|
+
" hastype = 1;",
|
|
2683
|
+
" }",
|
|
2684
|
+
" line = $0;",
|
|
2685
|
+
" while (match(line, /--label[ \\t]+[^ \\t]+/)) {",
|
|
2686
|
+
" tok = substr(line, RSTART, RLENGTH);",
|
|
2687
|
+
" line = substr(line, RSTART + RLENGTH);",
|
|
2688
|
+
' sub(/^--label[ \\t]+/, "", tok);',
|
|
2689
|
+
' gsub(/[\\\\`]/, "", tok);',
|
|
2690
|
+
' labels = labels tok ",";',
|
|
2691
|
+
" }",
|
|
2692
|
+
" }",
|
|
2693
|
+
" END { flush(); }",
|
|
2694
|
+
" '",
|
|
2695
|
+
"}",
|
|
2696
|
+
"",
|
|
2697
|
+
"# Collect the file list: positional args override the default set.",
|
|
2698
|
+
"files=()",
|
|
2699
|
+
"if [[ $# -gt 0 ]]; then",
|
|
2700
|
+
' files=("$@")',
|
|
2701
|
+
"else",
|
|
2702
|
+
' [[ -f "$templates_path" ]] && files+=("$templates_path")',
|
|
2703
|
+
' [[ -f "$generated_path" ]] && files+=("$generated_path")',
|
|
2704
|
+
" for child in $child_glob; do",
|
|
2705
|
+
' [[ -f "$child" ]] && files+=("$child")',
|
|
2706
|
+
" done",
|
|
2707
|
+
"fi",
|
|
2708
|
+
"",
|
|
2709
|
+
"if [[ ${#files[@]} -eq 0 ]]; then",
|
|
2710
|
+
" exit 0",
|
|
2711
|
+
"fi",
|
|
2712
|
+
"",
|
|
2713
|
+
"violations=()",
|
|
2714
|
+
"checked=0",
|
|
2715
|
+
"",
|
|
2716
|
+
'for file in "${files[@]}"; do',
|
|
2717
|
+
' [[ -f "$file" ]] || continue',
|
|
2718
|
+
" while IFS=$'\\t' read -r rec_file rec_line phase labels hastype; do",
|
|
2719
|
+
' [[ -z "$phase" ]] && continue',
|
|
2720
|
+
' required=$(phase_label_type_of "$phase")',
|
|
2721
|
+
' [[ -z "$required" ]] && continue',
|
|
2722
|
+
" checked=$(( checked + 1 ))",
|
|
2723
|
+
' case ",${labels}" in',
|
|
2724
|
+
' *",${phase},"*) ;;',
|
|
2725
|
+
` *) violations+=("$rec_file:$rec_line: recipe for '$phase' never passes --label $phase") ;;`,
|
|
2726
|
+
" esac",
|
|
2727
|
+
' type_labels=""',
|
|
2728
|
+
" type_count=0",
|
|
2729
|
+
' for entry in $(printf "%s" "$labels" | tr , " "); do',
|
|
2730
|
+
' case "$entry" in',
|
|
2731
|
+
" type:*)",
|
|
2732
|
+
' type_labels="${type_labels}${entry} "',
|
|
2733
|
+
" type_count=$(( type_count + 1 ))",
|
|
2734
|
+
" ;;",
|
|
2735
|
+
" esac",
|
|
2736
|
+
" done",
|
|
2737
|
+
' if [[ "$type_count" -eq 0 ]]; then',
|
|
2738
|
+
` violations+=("$rec_file:$rec_line: recipe for '$phase' carries no type:* label (expected $required)")`,
|
|
2739
|
+
' elif [[ "$type_count" -gt 1 ]]; then',
|
|
2740
|
+
" violations+=(\"$rec_file:$rec_line: recipe for '$phase' carries ${type_count} type:* labels (${type_labels% }); exactly one is allowed\")",
|
|
2741
|
+
' elif [[ "${type_labels% }" != "$required" ]]; then',
|
|
2742
|
+
` violations+=("$rec_file:$rec_line: recipe for '$phase' carries \${type_labels% } but the phase label requires $required")`,
|
|
2743
|
+
" fi",
|
|
2744
|
+
' if [[ "$hastype" != "1" ]]; then',
|
|
2745
|
+
` violations+=("$rec_file:$rec_line: recipe for '$phase' has no GitHub issue-type assignment step")`,
|
|
2746
|
+
" fi",
|
|
2747
|
+
' done < <(scan_file "$file")',
|
|
2748
|
+
"done",
|
|
2749
|
+
"",
|
|
2750
|
+
"if [[ ${#violations[@]} -gt 0 ]]; then",
|
|
2751
|
+
' err "issue-template recipes disagree with the phase-label invariant:"',
|
|
2752
|
+
' for v in "${violations[@]}"; do',
|
|
2753
|
+
' err " - $v"',
|
|
2754
|
+
" done",
|
|
2755
|
+
` err "each '## Template: <phase-label>' recipe must pass its own phase label, exactly one matching type:<bundle> label, and a set-issue-type.sh (or updateIssueIssueType) step"`,
|
|
2756
|
+
" exit 1",
|
|
2757
|
+
"fi",
|
|
2758
|
+
"",
|
|
2759
|
+
'printf "check-issue-template-labels.sh: %d recipe(s) OK\\n" "$checked"',
|
|
2760
|
+
"exit 0"
|
|
2761
|
+
].join("\n");
|
|
2762
|
+
}
|
|
2763
|
+
function basename(path8) {
|
|
2764
|
+
const slash = path8.lastIndexOf("/");
|
|
2765
|
+
return slash === -1 ? path8 : path8.slice(slash + 1);
|
|
2766
|
+
}
|
|
2327
2767
|
function assertValidTemplatesPath(value) {
|
|
2328
2768
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2329
2769
|
throw new Error(
|
|
@@ -4587,7 +5027,15 @@ var createRuleSkill = {
|
|
|
4587
5027
|
"- Use `ruleExtensions` to add project-specific content to existing bundle rules instead of replacing them"
|
|
4588
5028
|
].join("\n")
|
|
4589
5029
|
};
|
|
4590
|
-
|
|
5030
|
+
var DEFAULT_BASE_CONVENTIONS = {
|
|
5031
|
+
progressFiles: resolveProgressFiles(),
|
|
5032
|
+
sharedEditing: resolveSharedEditing(),
|
|
5033
|
+
temporalFraming: resolveTemporalFraming(),
|
|
5034
|
+
skillEvals: resolveSkillEvals(),
|
|
5035
|
+
issueTemplates: resolveIssueTemplates(),
|
|
5036
|
+
hasDownstreamIssueKindBundles: true
|
|
5037
|
+
};
|
|
5038
|
+
function buildBaseBundle(paths = DEFAULT_AGENT_PATHS, conventions = DEFAULT_BASE_CONVENTIONS) {
|
|
4591
5039
|
return {
|
|
4592
5040
|
name: "base",
|
|
4593
5041
|
description: "Core rules: project overview, interaction style, and general coding conventions",
|
|
@@ -5256,7 +5704,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5256
5704
|
// them via `agentConfig.additionalRulePaths`; other consumers
|
|
5257
5705
|
// get a clean default.
|
|
5258
5706
|
filePatterns: [".claude/agents/*.md", ".claude/procedures/**"],
|
|
5259
|
-
content: renderProgressFilesRuleContent(
|
|
5707
|
+
content: renderProgressFilesRuleContent(conventions.progressFiles),
|
|
5260
5708
|
platforms: {
|
|
5261
5709
|
cursor: { exclude: true }
|
|
5262
5710
|
},
|
|
@@ -5267,7 +5715,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5267
5715
|
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.",
|
|
5268
5716
|
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
5269
5717
|
filePatterns: DEFAULT_SHARED_INDEX_PATHS,
|
|
5270
|
-
content: renderSharedEditingRuleContent(
|
|
5718
|
+
content: renderSharedEditingRuleContent(conventions.sharedEditing),
|
|
5271
5719
|
platforms: {
|
|
5272
5720
|
cursor: { exclude: true }
|
|
5273
5721
|
},
|
|
@@ -5285,7 +5733,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5285
5733
|
"docs/src/content/docs/standards-research/**/*.md",
|
|
5286
5734
|
"docs/src/content/docs/customer-research/**/*.md"
|
|
5287
5735
|
],
|
|
5288
|
-
content: renderTemporalFramingRuleContent(
|
|
5736
|
+
content: renderTemporalFramingRuleContent(conventions.temporalFraming),
|
|
5289
5737
|
platforms: {
|
|
5290
5738
|
cursor: { exclude: true }
|
|
5291
5739
|
},
|
|
@@ -5298,7 +5746,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5298
5746
|
// Bundle defaults exclude paths into configulator's own
|
|
5299
5747
|
// source — see comment on progress-file-convention above.
|
|
5300
5748
|
filePatterns: [".claude/skills/**"],
|
|
5301
|
-
content: renderSkillEvalsRuleContent(
|
|
5749
|
+
content: renderSkillEvalsRuleContent(conventions.skillEvals),
|
|
5302
5750
|
platforms: {
|
|
5303
5751
|
cursor: { exclude: true }
|
|
5304
5752
|
},
|
|
@@ -5315,7 +5763,10 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5315
5763
|
".claude/agents/*.md",
|
|
5316
5764
|
`${paths.docsRoot}/agents/issue-templates.md`
|
|
5317
5765
|
],
|
|
5318
|
-
content: renderIssueTemplatesRuleContent(
|
|
5766
|
+
content: renderIssueTemplatesRuleContent(
|
|
5767
|
+
conventions.issueTemplates,
|
|
5768
|
+
conventions.hasDownstreamIssueKindBundles
|
|
5769
|
+
),
|
|
5319
5770
|
platforms: {
|
|
5320
5771
|
cursor: { exclude: true }
|
|
5321
5772
|
},
|
|
@@ -5326,90 +5777,6 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
5326
5777
|
}
|
|
5327
5778
|
var baseBundle = buildBaseBundle();
|
|
5328
5779
|
|
|
5329
|
-
// src/agent/bundles/issue-defaults.ts
|
|
5330
|
-
var VALID_STATUS_VALUES = [
|
|
5331
|
-
"ready",
|
|
5332
|
-
"blocked",
|
|
5333
|
-
"in-progress",
|
|
5334
|
-
"ready-for-review",
|
|
5335
|
-
"needs-attention",
|
|
5336
|
-
"done",
|
|
5337
|
-
"deferred"
|
|
5338
|
-
];
|
|
5339
|
-
var VALID_PRIORITY_VALUES = [
|
|
5340
|
-
"critical",
|
|
5341
|
-
"high",
|
|
5342
|
-
"medium",
|
|
5343
|
-
"low",
|
|
5344
|
-
"trivial"
|
|
5345
|
-
];
|
|
5346
|
-
var DEFAULT_ISSUE_STATUS = "ready";
|
|
5347
|
-
var DEFAULT_ISSUE_PRIORITY = "medium";
|
|
5348
|
-
var DEFAULT_RESOLVED_ISSUE_DEFAULTS = {
|
|
5349
|
-
defaults: {
|
|
5350
|
-
status: DEFAULT_ISSUE_STATUS,
|
|
5351
|
-
priority: DEFAULT_ISSUE_PRIORITY
|
|
5352
|
-
},
|
|
5353
|
-
overrides: {}
|
|
5354
|
-
};
|
|
5355
|
-
function resolveIssueDefaults(config) {
|
|
5356
|
-
if (config === void 0) {
|
|
5357
|
-
return DEFAULT_RESOLVED_ISSUE_DEFAULTS;
|
|
5358
|
-
}
|
|
5359
|
-
const overrides = {};
|
|
5360
|
-
for (const [phaseLabel, override] of Object.entries(config)) {
|
|
5361
|
-
assertValidPhaseLabel(phaseLabel);
|
|
5362
|
-
assertValidOverride(phaseLabel, override);
|
|
5363
|
-
overrides[phaseLabel] = {
|
|
5364
|
-
status: override.status ?? DEFAULT_ISSUE_STATUS,
|
|
5365
|
-
priority: override.priority ?? DEFAULT_ISSUE_PRIORITY
|
|
5366
|
-
};
|
|
5367
|
-
}
|
|
5368
|
-
return {
|
|
5369
|
-
defaults: {
|
|
5370
|
-
status: DEFAULT_ISSUE_STATUS,
|
|
5371
|
-
priority: DEFAULT_ISSUE_PRIORITY
|
|
5372
|
-
},
|
|
5373
|
-
overrides
|
|
5374
|
-
};
|
|
5375
|
-
}
|
|
5376
|
-
function validateIssueDefaultsConfig(config) {
|
|
5377
|
-
return resolveIssueDefaults(config);
|
|
5378
|
-
}
|
|
5379
|
-
function labelsForPhase(resolved, phaseLabel) {
|
|
5380
|
-
return resolved.overrides[phaseLabel] ?? resolved.defaults;
|
|
5381
|
-
}
|
|
5382
|
-
function assertValidPhaseLabel(phaseLabel) {
|
|
5383
|
-
if (typeof phaseLabel !== "string" || phaseLabel.trim() === "") {
|
|
5384
|
-
throw new Error(
|
|
5385
|
-
"AgentConfigOptions.issueDefaults: phase-label keys must be non-empty strings (e.g. `people:research`)."
|
|
5386
|
-
);
|
|
5387
|
-
}
|
|
5388
|
-
}
|
|
5389
|
-
function assertValidOverride(phaseLabel, override) {
|
|
5390
|
-
if (override === null || typeof override !== "object" || Array.isArray(override)) {
|
|
5391
|
-
throw new Error(
|
|
5392
|
-
`AgentConfigOptions.issueDefaults["${phaseLabel}"] must be an object with optional \`status\` and \`priority\` fields.`
|
|
5393
|
-
);
|
|
5394
|
-
}
|
|
5395
|
-
const { status, priority } = override;
|
|
5396
|
-
if (status === void 0 && priority === void 0) {
|
|
5397
|
-
throw new Error(
|
|
5398
|
-
`AgentConfigOptions.issueDefaults["${phaseLabel}"] must declare at least one of \`status\` or \`priority\`. Empty entries are rejected because they are almost always a typo on the field name.`
|
|
5399
|
-
);
|
|
5400
|
-
}
|
|
5401
|
-
if (status !== void 0 && !VALID_STATUS_VALUES.includes(status)) {
|
|
5402
|
-
throw new Error(
|
|
5403
|
-
`AgentConfigOptions.issueDefaults["${phaseLabel}"].status="${status}" is not a recognised status value. Allowed values: ${VALID_STATUS_VALUES.join(", ")}.`
|
|
5404
|
-
);
|
|
5405
|
-
}
|
|
5406
|
-
if (priority !== void 0 && !VALID_PRIORITY_VALUES.includes(priority)) {
|
|
5407
|
-
throw new Error(
|
|
5408
|
-
`AgentConfigOptions.issueDefaults["${phaseLabel}"].priority="${priority}" is not a recognised priority value. Allowed values: ${VALID_PRIORITY_VALUES.join(", ")}.`
|
|
5409
|
-
);
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5412
|
-
|
|
5413
5780
|
// src/agent/bundles/bcm-writer.ts
|
|
5414
5781
|
function buildBcmWriterSubAgent(paths, issueDefaults) {
|
|
5415
5782
|
return {
|
|
@@ -19106,37 +19473,7 @@ var scanCommand = {
|
|
|
19106
19473
|
""
|
|
19107
19474
|
].join("\n")
|
|
19108
19475
|
};
|
|
19109
|
-
var
|
|
19110
|
-
name: "orchestrator",
|
|
19111
|
-
description: "Pipeline orchestrator agent for issue triage, PR review, and queue management",
|
|
19112
|
-
// Always included by default
|
|
19113
|
-
appliesWhen: () => true,
|
|
19114
|
-
rules: [
|
|
19115
|
-
{
|
|
19116
|
-
name: "orchestrator-conventions",
|
|
19117
|
-
description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
|
|
19118
|
-
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
19119
|
-
// Bundle defaults exclude paths into configulator's own source
|
|
19120
|
-
// (only meaningful when configulator is a workspace package).
|
|
19121
|
-
// codedrifters/packages restores them via
|
|
19122
|
-
// `agentConfig.additionalRulePaths`.
|
|
19123
|
-
filePatterns: [
|
|
19124
|
-
".claude/agents/orchestrator.md",
|
|
19125
|
-
".claude/scheduled-tasks/**"
|
|
19126
|
-
],
|
|
19127
|
-
content: buildOrchestratorConventionsContent(
|
|
19128
|
-
DEFAULT_AGENT_TIERS,
|
|
19129
|
-
resolveScopeGate(),
|
|
19130
|
-
resolveRunRatio(),
|
|
19131
|
-
resolveScheduledTasks(),
|
|
19132
|
-
resolveUnblockDependents()
|
|
19133
|
-
),
|
|
19134
|
-
platforms: {
|
|
19135
|
-
cursor: { exclude: true }
|
|
19136
|
-
},
|
|
19137
|
-
tags: ["workflow"]
|
|
19138
|
-
}
|
|
19139
|
-
],
|
|
19476
|
+
var ORCHESTRATOR_BUNDLE_STATIC = {
|
|
19140
19477
|
subAgents: [orchestratorSubAgent, issueWorkerSubAgent],
|
|
19141
19478
|
procedures: [
|
|
19142
19479
|
checkBlockedProcedure,
|
|
@@ -19152,6 +19489,51 @@ var orchestratorBundle = {
|
|
|
19152
19489
|
]
|
|
19153
19490
|
}
|
|
19154
19491
|
};
|
|
19492
|
+
var DEFAULT_ORCHESTRATOR_CONVENTIONS = {
|
|
19493
|
+
tiers: DEFAULT_AGENT_TIERS,
|
|
19494
|
+
scopeGate: resolveScopeGate(),
|
|
19495
|
+
runRatio: resolveRunRatio(),
|
|
19496
|
+
scheduledTasks: resolveScheduledTasks(),
|
|
19497
|
+
unblockDependents: resolveUnblockDependents(),
|
|
19498
|
+
excludeBundles: []
|
|
19499
|
+
};
|
|
19500
|
+
function buildOrchestratorBundle(conventions = DEFAULT_ORCHESTRATOR_CONVENTIONS) {
|
|
19501
|
+
return {
|
|
19502
|
+
name: "orchestrator",
|
|
19503
|
+
description: "Pipeline orchestrator agent for issue triage, PR review, and queue management",
|
|
19504
|
+
// Always included by default
|
|
19505
|
+
appliesWhen: () => true,
|
|
19506
|
+
rules: [
|
|
19507
|
+
{
|
|
19508
|
+
name: "orchestrator-conventions",
|
|
19509
|
+
description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
|
|
19510
|
+
scope: AGENT_RULE_SCOPE.FILE_PATTERN,
|
|
19511
|
+
// Bundle defaults exclude paths into configulator's own source
|
|
19512
|
+
// (only meaningful when configulator is a workspace package).
|
|
19513
|
+
// codedrifters/packages restores them via
|
|
19514
|
+
// `agentConfig.additionalRulePaths`.
|
|
19515
|
+
filePatterns: [
|
|
19516
|
+
".claude/agents/orchestrator.md",
|
|
19517
|
+
".claude/scheduled-tasks/**"
|
|
19518
|
+
],
|
|
19519
|
+
content: buildOrchestratorConventionsContent(
|
|
19520
|
+
conventions.tiers,
|
|
19521
|
+
conventions.scopeGate,
|
|
19522
|
+
conventions.runRatio,
|
|
19523
|
+
conventions.scheduledTasks,
|
|
19524
|
+
conventions.unblockDependents,
|
|
19525
|
+
conventions.excludeBundles
|
|
19526
|
+
),
|
|
19527
|
+
platforms: {
|
|
19528
|
+
cursor: { exclude: true }
|
|
19529
|
+
},
|
|
19530
|
+
tags: ["workflow"]
|
|
19531
|
+
}
|
|
19532
|
+
],
|
|
19533
|
+
...ORCHESTRATOR_BUNDLE_STATIC
|
|
19534
|
+
};
|
|
19535
|
+
}
|
|
19536
|
+
var orchestratorBundle = buildOrchestratorBundle();
|
|
19155
19537
|
|
|
19156
19538
|
// src/agent/bundles/people-profile.ts
|
|
19157
19539
|
function buildPeopleProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
@@ -32662,6 +33044,8 @@ var UPSTREAM_CONFIGULATOR_DOCS_DETAIL = [
|
|
|
32662
33044
|
" --body $'## Summary\\n\\nThe `software-profile-analyst` Phase 2 prompt does not capture the vendor pricing tier (free / paid / enterprise), which downstream consumers need for segment matching.\\n\\n## Details\\n\\n- Add a `Pricing tier` field to the profile template at `docs/src/content/docs/agents/software-profile-analyst.md`\\n- Update the Phase 2 instructions to require the field before commit\\n- Add a regression test in `bundles.test.ts`'",
|
|
32663
33045
|
"```",
|
|
32664
33046
|
"",
|
|
33047
|
+
"The call above does **not** set the GitHub issue type \u2014 `gh issue create` cannot. Assign it immediately, in the same session, with the cross-repo GraphQL flow above (`feat:` title \u2192 **Feature**; every other prefix except `epic:` / `fix:` \u2192 **Task**). Skipping it leaves the upstream issue untyped, and nobody comes back for it later.",
|
|
33048
|
+
"",
|
|
32665
33049
|
"After filing, reference the upstream issue from any local workaround so it can be reverted once the upstream fix lands:",
|
|
32666
33050
|
"",
|
|
32667
33051
|
"```typescript",
|
|
@@ -33623,10 +34007,14 @@ function renderPriorityRulesSection(rules) {
|
|
|
33623
34007
|
}
|
|
33624
34008
|
|
|
33625
34009
|
// src/agent/bundles/index.ts
|
|
33626
|
-
|
|
34010
|
+
var DEFAULT_RULE_CONVENTIONS = {
|
|
34011
|
+
base: DEFAULT_BASE_CONVENTIONS,
|
|
34012
|
+
orchestrator: DEFAULT_ORCHESTRATOR_CONVENTIONS
|
|
34013
|
+
};
|
|
34014
|
+
function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS, defaultAgentTier = AGENT_MODEL.BALANCED, bundleAgentTiers = /* @__PURE__ */ new Map(), prReviewPolicy = resolvePrReviewPolicy(), buildPolicy = DEFAULT_BUILD_POLICY, conventions = DEFAULT_RULE_CONVENTIONS) {
|
|
33627
34015
|
const tierFor = (bundle) => bundleAgentTiers.get(bundle) ?? defaultAgentTier;
|
|
33628
34016
|
return [
|
|
33629
|
-
buildBaseBundle(paths),
|
|
34017
|
+
buildBaseBundle(paths, conventions.base),
|
|
33630
34018
|
upstreamConfigulatorDocsBundle,
|
|
33631
34019
|
typescriptBundle,
|
|
33632
34020
|
vitestBundle,
|
|
@@ -33639,7 +34027,7 @@ function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAUL
|
|
|
33639
34027
|
slackBundle,
|
|
33640
34028
|
buildMeetingAnalysisBundle(tierFor("meeting-analysis")),
|
|
33641
34029
|
agendaBundle,
|
|
33642
|
-
|
|
34030
|
+
buildOrchestratorBundle(conventions.orchestrator),
|
|
33643
34031
|
buildPrReviewBundle(prReviewPolicy),
|
|
33644
34032
|
buildRequirementsAnalystBundle(paths, issueDefaults),
|
|
33645
34033
|
buildRequirementsWriterBundle(paths, issueDefaults),
|
|
@@ -34727,6 +35115,13 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
34727
35115
|
* credential requirement when a remote cache actually exists. The
|
|
34728
35116
|
* getter is lazy by design — `TurboRepo` must already be attached
|
|
34729
35117
|
* to the project when the bundles are first read.
|
|
35118
|
+
*
|
|
35119
|
+
* Every **config-driven convention rule** is likewise resolved here
|
|
35120
|
+
* and seeded into its owning bundle, so the rule enters the rule map
|
|
35121
|
+
* already carrying the consumer's settings. Rewriting those rules
|
|
35122
|
+
* after the map was assembled — the previous approach — silently
|
|
35123
|
+
* discarded any `ruleExtensions` append or same-name `rules`
|
|
35124
|
+
* override that had already been merged in.
|
|
34730
35125
|
*/
|
|
34731
35126
|
get pathAwareBundles() {
|
|
34732
35127
|
if (!this.cachedBundles) {
|
|
@@ -34736,11 +35131,52 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
34736
35131
|
resolveDefaultAgentTier(this.options),
|
|
34737
35132
|
resolveBundleAgentTiers(this.options),
|
|
34738
35133
|
resolvePrReviewPolicy(this.options.prReviewPolicy),
|
|
34739
|
-
resolveBuildPolicy(this.project)
|
|
35134
|
+
resolveBuildPolicy(this.project),
|
|
35135
|
+
this.resolvedRuleConventions
|
|
34740
35136
|
);
|
|
34741
35137
|
}
|
|
34742
35138
|
return this.cachedBundles;
|
|
34743
35139
|
}
|
|
35140
|
+
/**
|
|
35141
|
+
* Resolved settings for every config-driven convention rule, derived
|
|
35142
|
+
* from this project's options. Consumed by `buildBuiltInBundles` so
|
|
35143
|
+
* the `base` and `orchestrator` bundles seed final rule content.
|
|
35144
|
+
*
|
|
35145
|
+
* `excludeBundles` feeds two of these: the orchestrator's rendered
|
|
35146
|
+
* tier table / scope-gate overrides / scheduled-tasks registry drop
|
|
35147
|
+
* rows owned by excluded bundles, and the issue-templates rule falls
|
|
35148
|
+
* back to its disabled stub once every downstream-issue-kind bundle
|
|
35149
|
+
* has been excluded.
|
|
35150
|
+
*/
|
|
35151
|
+
get resolvedRuleConventions() {
|
|
35152
|
+
const excludeBundles = this.options.excludeBundles ?? [];
|
|
35153
|
+
const orchestratorAssets = resolveOrchestratorAssets(
|
|
35154
|
+
this.options.tiers,
|
|
35155
|
+
this.options.scopeGate,
|
|
35156
|
+
this.options.runRatio,
|
|
35157
|
+
this.options.scheduledTasks,
|
|
35158
|
+
this.options.unblockDependents,
|
|
35159
|
+
excludeBundles
|
|
35160
|
+
);
|
|
35161
|
+
return {
|
|
35162
|
+
base: {
|
|
35163
|
+
progressFiles: resolveProgressFiles(this.options.progressFiles),
|
|
35164
|
+
sharedEditing: resolveSharedEditing(this.options.sharedEditing),
|
|
35165
|
+
temporalFraming: resolveTemporalFraming(this.options.temporalFraming),
|
|
35166
|
+
skillEvals: resolveSkillEvals(this.options.skillEvals),
|
|
35167
|
+
issueTemplates: resolveIssueTemplates(this.options.issueTemplates),
|
|
35168
|
+
hasDownstreamIssueKindBundles: hasAnyDownstreamIssueKindBundle(excludeBundles)
|
|
35169
|
+
},
|
|
35170
|
+
orchestrator: {
|
|
35171
|
+
tiers: orchestratorAssets.tiers,
|
|
35172
|
+
scopeGate: orchestratorAssets.scopeGate,
|
|
35173
|
+
runRatio: orchestratorAssets.runRatio,
|
|
35174
|
+
scheduledTasks: orchestratorAssets.scheduledTasks,
|
|
35175
|
+
unblockDependents: orchestratorAssets.unblockDependents,
|
|
35176
|
+
excludeBundles
|
|
35177
|
+
}
|
|
35178
|
+
};
|
|
35179
|
+
}
|
|
34744
35180
|
/**
|
|
34745
35181
|
* Returns the bundles that are active for this project: auto-detected
|
|
34746
35182
|
* bundles (when `autoDetectBundles !== false`) plus force-included
|
|
@@ -34838,11 +35274,34 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
34838
35274
|
).split("\n"),
|
|
34839
35275
|
executable: true
|
|
34840
35276
|
});
|
|
35277
|
+
new TextFile4(
|
|
35278
|
+
this,
|
|
35279
|
+
".claude/procedures/check-issue-template-labels.sh",
|
|
35280
|
+
{
|
|
35281
|
+
lines: renderIssueTemplateLabelsCheckerScript(
|
|
35282
|
+
resolvedIssueTemplates
|
|
35283
|
+
).split("\n"),
|
|
35284
|
+
executable: true
|
|
35285
|
+
}
|
|
35286
|
+
);
|
|
34841
35287
|
}
|
|
34842
35288
|
if (resolvedIssueTemplates.emitStarterDoc) {
|
|
34843
35289
|
new SampleFile2(this.project, resolvedIssueTemplates.templatesPath, {
|
|
34844
35290
|
contents: renderIssueTemplatesStarterPage(resolvedIssueTemplates)
|
|
34845
35291
|
});
|
|
35292
|
+
new TextFile4(
|
|
35293
|
+
this,
|
|
35294
|
+
issueTemplatesGeneratedPath(resolvedIssueTemplates.templatesPath),
|
|
35295
|
+
{
|
|
35296
|
+
lines: renderIssueTemplatesGeneratedPage(
|
|
35297
|
+
resolvedIssueTemplates,
|
|
35298
|
+
collectIssueTemplateRecipeStubs(
|
|
35299
|
+
this.activeBundles,
|
|
35300
|
+
resolveIssueDefaults(this.options.issueDefaults)
|
|
35301
|
+
)
|
|
35302
|
+
).split("\n")
|
|
35303
|
+
}
|
|
35304
|
+
);
|
|
34846
35305
|
}
|
|
34847
35306
|
}
|
|
34848
35307
|
const resolvedTemporalFraming = validateTemporalFramingConfig(
|
|
@@ -35032,43 +35491,10 @@ ${section}`
|
|
|
35032
35491
|
}
|
|
35033
35492
|
}
|
|
35034
35493
|
const excludedBundleNames = this.options.excludeBundles ?? [];
|
|
35035
|
-
if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents || excludedBundleNames.length > 0) {
|
|
35036
|
-
const orchestratorRule = ruleMap.get("orchestrator-conventions");
|
|
35037
|
-
if (orchestratorRule) {
|
|
35038
|
-
const { conventionsContent } = resolveOrchestratorAssets(
|
|
35039
|
-
this.options.tiers,
|
|
35040
|
-
this.options.scopeGate,
|
|
35041
|
-
this.options.runRatio,
|
|
35042
|
-
this.options.scheduledTasks,
|
|
35043
|
-
this.options.unblockDependents,
|
|
35044
|
-
excludedBundleNames
|
|
35045
|
-
);
|
|
35046
|
-
if (conventionsContent !== orchestratorRule.content) {
|
|
35047
|
-
ruleMap.set("orchestrator-conventions", {
|
|
35048
|
-
...orchestratorRule,
|
|
35049
|
-
content: conventionsContent
|
|
35050
|
-
});
|
|
35051
|
-
}
|
|
35052
|
-
}
|
|
35053
|
-
}
|
|
35054
35494
|
const injectBundleHooks = this.options.claudeMd?.injectBundleHooks ?? true;
|
|
35055
35495
|
const resolvedProgressFiles = resolveProgressFiles(
|
|
35056
35496
|
this.options.progressFiles
|
|
35057
35497
|
);
|
|
35058
|
-
if (this.options.progressFiles) {
|
|
35059
|
-
const progressRule = ruleMap.get("progress-file-convention");
|
|
35060
|
-
if (progressRule) {
|
|
35061
|
-
const progressContent = renderProgressFilesRuleContent(
|
|
35062
|
-
resolvedProgressFiles
|
|
35063
|
-
);
|
|
35064
|
-
if (progressContent !== progressRule.content) {
|
|
35065
|
-
ruleMap.set("progress-file-convention", {
|
|
35066
|
-
...progressRule,
|
|
35067
|
-
content: progressContent
|
|
35068
|
-
});
|
|
35069
|
-
}
|
|
35070
|
-
}
|
|
35071
|
-
}
|
|
35072
35498
|
if (injectBundleHooks && resolvedProgressFiles.enabled) {
|
|
35073
35499
|
for (const [ruleName, label] of PROGRESS_FILE_BUNDLE_HOOKS) {
|
|
35074
35500
|
const existing = ruleMap.get(ruleName);
|
|
@@ -35095,20 +35521,6 @@ ${hook}`
|
|
|
35095
35521
|
const resolvedSharedEditingForRules = resolveSharedEditing(
|
|
35096
35522
|
this.options.sharedEditing
|
|
35097
35523
|
);
|
|
35098
|
-
if (this.options.sharedEditing) {
|
|
35099
|
-
const sharedEditingRule = ruleMap.get("shared-editing-safety");
|
|
35100
|
-
if (sharedEditingRule) {
|
|
35101
|
-
const sharedEditingContent = renderSharedEditingRuleContent(
|
|
35102
|
-
resolvedSharedEditingForRules
|
|
35103
|
-
);
|
|
35104
|
-
if (sharedEditingContent !== sharedEditingRule.content) {
|
|
35105
|
-
ruleMap.set("shared-editing-safety", {
|
|
35106
|
-
...sharedEditingRule,
|
|
35107
|
-
content: sharedEditingContent
|
|
35108
|
-
});
|
|
35109
|
-
}
|
|
35110
|
-
}
|
|
35111
|
-
}
|
|
35112
35524
|
if (injectBundleHooks && resolvedSharedEditingForRules.enabled) {
|
|
35113
35525
|
for (const [ruleName, label] of SHARED_EDITING_BUNDLE_HOOKS) {
|
|
35114
35526
|
const existing = ruleMap.get(ruleName);
|
|
@@ -35135,20 +35547,6 @@ ${hook}`
|
|
|
35135
35547
|
const resolvedSkillEvalsForRules = resolveSkillEvals(
|
|
35136
35548
|
this.options.skillEvals
|
|
35137
35549
|
);
|
|
35138
|
-
if (this.options.skillEvals) {
|
|
35139
|
-
const skillEvalsRule = ruleMap.get("skill-evals");
|
|
35140
|
-
if (skillEvalsRule) {
|
|
35141
|
-
const skillEvalsContent = renderSkillEvalsRuleContent(
|
|
35142
|
-
resolvedSkillEvalsForRules
|
|
35143
|
-
);
|
|
35144
|
-
if (skillEvalsContent !== skillEvalsRule.content) {
|
|
35145
|
-
ruleMap.set("skill-evals", {
|
|
35146
|
-
...skillEvalsRule,
|
|
35147
|
-
content: skillEvalsContent
|
|
35148
|
-
});
|
|
35149
|
-
}
|
|
35150
|
-
}
|
|
35151
|
-
}
|
|
35152
35550
|
if (injectBundleHooks && resolvedSkillEvalsForRules.enabled) {
|
|
35153
35551
|
for (const [ruleName, label] of SKILL_EVALS_BUNDLE_HOOKS) {
|
|
35154
35552
|
const existing = ruleMap.get(ruleName);
|
|
@@ -35176,21 +35574,6 @@ ${hook}`
|
|
|
35176
35574
|
this.options.issueTemplates
|
|
35177
35575
|
);
|
|
35178
35576
|
const hasDownstreamBundles = hasAnyDownstreamIssueKindBundle(excludedBundleNames);
|
|
35179
|
-
if (this.options.issueTemplates || !hasDownstreamBundles) {
|
|
35180
|
-
const issueTemplatesRule = ruleMap.get("issue-templates-convention");
|
|
35181
|
-
if (issueTemplatesRule) {
|
|
35182
|
-
const issueTemplatesContent = renderIssueTemplatesRuleContent(
|
|
35183
|
-
resolvedIssueTemplatesForRules,
|
|
35184
|
-
hasDownstreamBundles
|
|
35185
|
-
);
|
|
35186
|
-
if (issueTemplatesContent !== issueTemplatesRule.content) {
|
|
35187
|
-
ruleMap.set("issue-templates-convention", {
|
|
35188
|
-
...issueTemplatesRule,
|
|
35189
|
-
content: issueTemplatesContent
|
|
35190
|
-
});
|
|
35191
|
-
}
|
|
35192
|
-
}
|
|
35193
|
-
}
|
|
35194
35577
|
if (injectBundleHooks && resolvedIssueTemplatesForRules.enabled && hasDownstreamBundles) {
|
|
35195
35578
|
for (const [ruleName, label] of ISSUE_TEMPLATES_BUNDLE_HOOKS) {
|
|
35196
35579
|
const existing = ruleMap.get(ruleName);
|
|
@@ -35214,23 +35597,6 @@ ${hook}`
|
|
|
35214
35597
|
});
|
|
35215
35598
|
}
|
|
35216
35599
|
}
|
|
35217
|
-
const resolvedTemporalFramingForRules = resolveTemporalFraming(
|
|
35218
|
-
this.options.temporalFraming
|
|
35219
|
-
);
|
|
35220
|
-
if (this.options.temporalFraming) {
|
|
35221
|
-
const temporalFramingRule = ruleMap.get("temporal-framing-convention");
|
|
35222
|
-
if (temporalFramingRule) {
|
|
35223
|
-
const temporalFramingContent = renderTemporalFramingRuleContent(
|
|
35224
|
-
resolvedTemporalFramingForRules
|
|
35225
|
-
);
|
|
35226
|
-
if (temporalFramingContent !== temporalFramingRule.content) {
|
|
35227
|
-
ruleMap.set("temporal-framing-convention", {
|
|
35228
|
-
...temporalFramingRule,
|
|
35229
|
-
content: temporalFramingContent
|
|
35230
|
-
});
|
|
35231
|
-
}
|
|
35232
|
-
}
|
|
35233
|
-
}
|
|
35234
35600
|
const tierExamples = this.options.features?.sourceTierExamples;
|
|
35235
35601
|
if (_AgentConfig.hasActiveTierExamples(tierExamples)) {
|
|
35236
35602
|
const sourceRule = ruleMap.get("source-quality-verification");
|
|
@@ -41455,6 +41821,7 @@ export {
|
|
|
41455
41821
|
DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
|
|
41456
41822
|
DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
|
|
41457
41823
|
DEFAULT_AUDIT_REPORT_DIR,
|
|
41824
|
+
DEFAULT_BASE_CONVENTIONS,
|
|
41458
41825
|
DEFAULT_BUILD_POLICY,
|
|
41459
41826
|
DEFAULT_BUNDLE_OVERRIDES,
|
|
41460
41827
|
DEFAULT_DECOMPOSITION_TEMPLATE,
|
|
@@ -41471,6 +41838,7 @@ export {
|
|
|
41471
41838
|
DEFAULT_ISSUE_TEMPLATES_PATH,
|
|
41472
41839
|
DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
|
|
41473
41840
|
DEFAULT_OFF_PEAK_CRON_EXAMPLE,
|
|
41841
|
+
DEFAULT_ORCHESTRATOR_CONVENTIONS,
|
|
41474
41842
|
DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
|
|
41475
41843
|
DEFAULT_PATHS_EXEMPT_FROM_SIZE,
|
|
41476
41844
|
DEFAULT_PRIORITY_LABELS,
|
|
@@ -41483,6 +41851,7 @@ export {
|
|
|
41483
41851
|
DEFAULT_REQUIREMENT_CATEGORY_DIRS,
|
|
41484
41852
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
41485
41853
|
DEFAULT_RESOLVED_ISSUE_DEFAULTS,
|
|
41854
|
+
DEFAULT_RULE_CONVENTIONS,
|
|
41486
41855
|
DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
41487
41856
|
DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
41488
41857
|
DEFAULT_SCHEDULED_TASK_ENTRIES,
|
|
@@ -41509,6 +41878,7 @@ export {
|
|
|
41509
41878
|
DOCS_SYNC_AUDIT_SCHEMA_VERSION,
|
|
41510
41879
|
GITHUB_ISSUE_TYPES,
|
|
41511
41880
|
GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
|
|
41881
|
+
ISSUE_TEMPLATES_GENERATED_SUFFIX,
|
|
41512
41882
|
JsiiFaker,
|
|
41513
41883
|
LAYOUT_ENFORCEMENT,
|
|
41514
41884
|
LAYOUT_ROOT_BY_PROJECT_TYPE,
|
|
@@ -41585,6 +41955,7 @@ export {
|
|
|
41585
41955
|
buildIndustryDiscoveryBundle,
|
|
41586
41956
|
buildMaintenanceAuditBundle,
|
|
41587
41957
|
buildMeetingAnalysisBundle,
|
|
41958
|
+
buildOrchestratorBundle,
|
|
41588
41959
|
buildOrchestratorConventionsContent,
|
|
41589
41960
|
buildPeopleProfileBundle,
|
|
41590
41961
|
buildPrReviewBundle,
|
|
@@ -41604,6 +41975,7 @@ export {
|
|
|
41604
41975
|
checkLinksProcedure,
|
|
41605
41976
|
classifyIssueScope,
|
|
41606
41977
|
classifyRun,
|
|
41978
|
+
collectIssueTemplateRecipeStubs,
|
|
41607
41979
|
companyProfileBundle,
|
|
41608
41980
|
compileFencedSamples,
|
|
41609
41981
|
createApiDiffCheck,
|
|
@@ -41629,6 +42001,8 @@ export {
|
|
|
41629
42001
|
isScheduledTaskOwnedByExcluded,
|
|
41630
42002
|
isSuppressedWorkflowRule,
|
|
41631
42003
|
isTypeLabelOwnedByExcluded,
|
|
42004
|
+
issueTemplatesChildGlob,
|
|
42005
|
+
issueTemplatesGeneratedPath,
|
|
41632
42006
|
jestBundle,
|
|
41633
42007
|
labelsForPhase,
|
|
41634
42008
|
maintenanceAuditBundle,
|
|
@@ -41679,8 +42053,10 @@ export {
|
|
|
41679
42053
|
renderFocusSection,
|
|
41680
42054
|
renderGithubIssueTypeSection,
|
|
41681
42055
|
renderGithubIssueTypeSectionLines,
|
|
42056
|
+
renderIssueTemplateLabelsCheckerScript,
|
|
41682
42057
|
renderIssueTemplatesBundleHook,
|
|
41683
42058
|
renderIssueTemplatesCheckerScript,
|
|
42059
|
+
renderIssueTemplatesGeneratedPage,
|
|
41684
42060
|
renderIssueTemplatesRuleContent,
|
|
41685
42061
|
renderIssueTemplatesStarterPage,
|
|
41686
42062
|
renderIssueTypeAssignmentBlanket,
|