@codedrifters/configulator 0.0.254 → 0.0.256

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.mjs CHANGED
@@ -14488,6 +14488,165 @@ var vitestBundle = {
14488
14488
  }
14489
14489
  };
14490
14490
 
14491
+ // src/agent/bundles/focus.ts
14492
+ var DEFAULT_FOCUS_FILE_PATH = ".claude/focus.json";
14493
+ var DEFAULT_SCHEMA_VERSION = 1;
14494
+ function renderFocusSection(focus) {
14495
+ if (!focus) {
14496
+ return "";
14497
+ }
14498
+ const focusFilePath = focus.focusFilePath ?? DEFAULT_FOCUS_FILE_PATH;
14499
+ const schemaVersion = focus.schemaVersion ?? DEFAULT_SCHEMA_VERSION;
14500
+ const lines = [
14501
+ "## Focus scoring",
14502
+ "",
14503
+ "This project runs a **focus-scoring engine** alongside the generic",
14504
+ "`priority:*` inference above. Focus scoring boosts the priority of",
14505
+ "issues that match the project's currently-declared focus areas \u2014",
14506
+ "named customers, segments, organizations, software products,",
14507
+ "regulations, or people the project cares about right now.",
14508
+ "",
14509
+ "### Reading `focus.json`",
14510
+ "",
14511
+ `The focus file lives at \`${focusFilePath}\` and conforms to schema`,
14512
+ `version \`${schemaVersion}\`. Each entry declares a match predicate`,
14513
+ "(any combination of `labels`, `titleKeywords`, `bodyKeywords`) and",
14514
+ "a positive integer `weight`. At triage time:",
14515
+ "",
14516
+ `1. Read \`${focusFilePath}\`. If the file is missing or empty,`,
14517
+ " skip focus scoring entirely \u2014 the generic priority heuristics",
14518
+ " apply unchanged.",
14519
+ "2. For each focus area, test its match predicate against the",
14520
+ " issue's labels, title, and body. A match on **any** sub-field",
14521
+ " (labels / title keywords / body keywords) fires the area and",
14522
+ " contributes its `weight` to the issue's focus score.",
14523
+ "3. Keyword matching is case-insensitive substring matching \u2014",
14524
+ ' `titleKeywords: ["Acme"]` matches an issue titled',
14525
+ " `feat: onboard acme corp`.",
14526
+ "4. Sum all matched weights to produce the issue's **focus score**.",
14527
+ ""
14528
+ ];
14529
+ lines.push("### How focus score interacts with `priority:*`");
14530
+ lines.push("");
14531
+ if (focus.thresholds) {
14532
+ const { high, medium } = focus.thresholds;
14533
+ lines.push(`A focus score of \`${high}\` or more boosts the`);
14534
+ const mediumBand = high - medium === 1 ? `A focus score of \`${medium}\` keeps` : `A focus score of \`${medium}\` up to \`${high - 1}\` keeps`;
14535
+ lines.push(
14536
+ "issue to `priority:high`.",
14537
+ `${mediumBand} the issue at \`priority:medium\`. Scores below`,
14538
+ `\`${medium}\` do **not** alter the priority that`,
14539
+ "the generic heuristics would otherwise infer."
14540
+ );
14541
+ } else {
14542
+ lines.push(
14543
+ "This project has not declared explicit focus-score thresholds.",
14544
+ "Treat any non-zero focus score as a soft signal to lift one",
14545
+ "priority tier (never above `priority:high`) unless the generic",
14546
+ "heuristics already pin a higher tier."
14547
+ );
14548
+ }
14549
+ lines.push("");
14550
+ lines.push(
14551
+ "Focus scoring **never lowers** an issue's priority \u2014 the generic",
14552
+ "heuristics set the floor, and focus weight can only lift it.",
14553
+ "`priority:critical` is never set by focus scoring alone; the",
14554
+ "generic heuristics own that tier."
14555
+ );
14556
+ lines.push("");
14557
+ lines.push("### Agent-driven expansion");
14558
+ lines.push("");
14559
+ if (focus.agentExpansionRules) {
14560
+ const rules = focus.agentExpansionRules;
14561
+ const allowedList = rules.allowedTypes.map((t) => `\`${t}\``).join(", ");
14562
+ const forbiddenList = rules.forbiddenTypes.map((t) => `\`${t}\``).join(", ");
14563
+ lines.push(
14564
+ `Agents may **append** new focus areas to \`${focusFilePath}\``,
14565
+ "when they discover a relevant entity during work, subject to",
14566
+ "these guard-rails:"
14567
+ );
14568
+ lines.push("");
14569
+ lines.push(
14570
+ `- **Max weight.** Agent-appended entries must set \`weight\` to`,
14571
+ ` \`${rules.maxWeight}\` or less. Higher weights require a human.`
14572
+ );
14573
+ lines.push(
14574
+ "- **Allowed types.** Agents may only introduce entries whose",
14575
+ ` \`type\` is one of: ${allowedList || "(none)"}.`
14576
+ );
14577
+ if (rules.forbiddenTypes.length > 0) {
14578
+ lines.push(
14579
+ "- **Forbidden types.** Even inside `allowedTypes`, agents must",
14580
+ ` never introduce entries whose \`type\` is one of: ${forbiddenList}.`
14581
+ );
14582
+ }
14583
+ if (rules.maxKeywords !== void 0) {
14584
+ lines.push(
14585
+ "- **Max keywords per match field.** Agent-appended entries",
14586
+ ` may list at most \`${rules.maxKeywords}\` items in each of`,
14587
+ " `match.labels`, `match.titleKeywords`, and `match.bodyKeywords`."
14588
+ );
14589
+ }
14590
+ lines.push(
14591
+ '- **Provenance.** Agent-appended entries must set `source: "agent"`',
14592
+ ' and `discoveredIn: "#<issue>"` referencing the issue that',
14593
+ " surfaced the focus area."
14594
+ );
14595
+ lines.push(
14596
+ "- **Append-only.** Never modify or remove existing entries.",
14597
+ " Human curators own edits and deletions."
14598
+ );
14599
+ } else {
14600
+ lines.push(
14601
+ `Agents must **not** append to \`${focusFilePath}\`. This project`,
14602
+ "has not declared agent-expansion rules \u2014 all focus-area curation",
14603
+ "is performed by humans. If you discover a relevant entity that",
14604
+ "is not covered by an existing focus area, raise it in the issue",
14605
+ "or PR discussion instead of editing the focus file."
14606
+ );
14607
+ }
14608
+ return lines.join("\n");
14609
+ }
14610
+
14611
+ // src/agent/bundles/priority-rules.ts
14612
+ function renderPriorityRulesSection(rules) {
14613
+ if (rules.length === 0) {
14614
+ return "";
14615
+ }
14616
+ const lines = [
14617
+ "## Project-specific priority rules",
14618
+ "",
14619
+ "These rules are supplied by this project through",
14620
+ "`AgentConfigOptions.priorityRules` and override the generic inference",
14621
+ "heuristics above. Rules evaluate in the order listed; **first match",
14622
+ "wins**. The bundle's default heuristics apply only when no rule below",
14623
+ "matches.",
14624
+ ""
14625
+ ];
14626
+ rules.forEach((rule, index) => {
14627
+ const matchDetails = [];
14628
+ if (rule.match.labels && rule.match.labels.length > 0) {
14629
+ const labelList = rule.match.labels.map((l) => `\`${l}\``).join(", ");
14630
+ matchDetails.push(`labels: ${labelList}`);
14631
+ }
14632
+ if (rule.match.titleRegex) {
14633
+ matchDetails.push(`title regex: \`${rule.match.titleRegex}\``);
14634
+ }
14635
+ if (rule.match.bodyRegex) {
14636
+ matchDetails.push(`body regex: \`${rule.match.bodyRegex}\``);
14637
+ }
14638
+ if (rule.match.issueNumbers && rule.match.issueNumbers.length > 0) {
14639
+ const numberList = rule.match.issueNumbers.map((n) => `#${n}`).join(", ");
14640
+ matchDetails.push(`issue numbers: ${numberList}`);
14641
+ }
14642
+ const matchSummary = matchDetails.length > 0 ? matchDetails.join("; ") : "(no match fields)";
14643
+ lines.push(
14644
+ `${index + 1}. **\`priority:${rule.priority}\`** when ${matchSummary} \u2014 ${rule.rationale}`
14645
+ );
14646
+ });
14647
+ return lines.join("\n");
14648
+ }
14649
+
14491
14650
  // src/agent/bundles/index.ts
14492
14651
  var BUILT_IN_BUNDLES = [
14493
14652
  baseBundle,
@@ -15408,6 +15567,36 @@ ${extra}`
15408
15567
  }
15409
15568
  }
15410
15569
  }
15570
+ if (this.options.priorityRules && this.options.priorityRules.length > 0) {
15571
+ const issueLabelRule = ruleMap.get("issue-label-conventions");
15572
+ if (issueLabelRule) {
15573
+ const section = renderPriorityRulesSection(this.options.priorityRules);
15574
+ ruleMap.set("issue-label-conventions", {
15575
+ ...issueLabelRule,
15576
+ content: `${issueLabelRule.content}
15577
+
15578
+ ---
15579
+
15580
+ ${section}`
15581
+ });
15582
+ }
15583
+ }
15584
+ if (this.options.focus) {
15585
+ const issueLabelRule = ruleMap.get("issue-label-conventions");
15586
+ if (issueLabelRule) {
15587
+ const section = renderFocusSection(this.options.focus);
15588
+ if (section.length > 0) {
15589
+ ruleMap.set("issue-label-conventions", {
15590
+ ...issueLabelRule,
15591
+ content: `${issueLabelRule.content}
15592
+
15593
+ ---
15594
+
15595
+ ${section}`
15596
+ });
15597
+ }
15598
+ }
15599
+ }
15411
15600
  return [...ruleMap.values()].sort((a, b) => {
15412
15601
  if (a.name === "project-overview") return -1;
15413
15602
  if (b.name === "project-overview") return 1;
@@ -18231,6 +18420,8 @@ export {
18231
18420
  pnpmBundle,
18232
18421
  prReviewBundle,
18233
18422
  projenBundle,
18423
+ renderFocusSection,
18424
+ renderPriorityRulesSection,
18234
18425
  requirementsAnalystBundle,
18235
18426
  requirementsReviewerBundle,
18236
18427
  requirementsWriterBundle,