@codedrifters/configulator 0.0.255 → 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,126 @@ 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
+
14491
14611
  // src/agent/bundles/priority-rules.ts
14492
14612
  function renderPriorityRulesSection(rules) {
14493
14613
  if (rules.length === 0) {
@@ -15461,6 +15581,22 @@ ${section}`
15461
15581
  });
15462
15582
  }
15463
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
+ }
15464
15600
  return [...ruleMap.values()].sort((a, b) => {
15465
15601
  if (a.name === "project-overview") return -1;
15466
15602
  if (b.name === "project-overview") return 1;
@@ -18284,6 +18420,7 @@ export {
18284
18420
  pnpmBundle,
18285
18421
  prReviewBundle,
18286
18422
  projenBundle,
18423
+ renderFocusSection,
18287
18424
  renderPriorityRulesSection,
18288
18425
  requirementsAnalystBundle,
18289
18426
  requirementsReviewerBundle,