@codedrifters/configulator 0.0.255 → 0.0.257

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,190 @@ 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/meeting-types.ts
14612
+ var DEFAULT_AGENDA_TEMPLATE_ROOT = "<meetingsRoot>/_agenda-templates";
14613
+ function renderMeetingTypesSection(meetings) {
14614
+ if (!meetings) {
14615
+ return "";
14616
+ }
14617
+ const types = meetings.meetingTypes ?? [];
14618
+ const areas = meetings.meetingAreas ?? [];
14619
+ if (types.length === 0 && areas.length === 0) {
14620
+ return "";
14621
+ }
14622
+ const sections = [];
14623
+ if (types.length > 0) {
14624
+ sections.push(renderMeetingTypes(types, meetings.agendaTemplateRoot));
14625
+ }
14626
+ if (areas.length > 0) {
14627
+ sections.push(renderMeetingAreas(areas));
14628
+ }
14629
+ return sections.join("\n\n");
14630
+ }
14631
+ function renderMeetingTypes(types, agendaTemplateRoot) {
14632
+ const resolvedRoot = agendaTemplateRoot ?? DEFAULT_AGENDA_TEMPLATE_ROOT;
14633
+ const lines = [
14634
+ "## Recognized meeting types",
14635
+ "",
14636
+ "This project declares a meeting-type taxonomy through",
14637
+ "`AgentConfigOptions.meetings.meetingTypes`. When classifying a",
14638
+ "meeting transcript, pick the `id` from this list that best fits the",
14639
+ "meeting; do **not** invent new type identifiers. Agenda-template",
14640
+ `paths below are resolved relative to \`${resolvedRoot}\`.`,
14641
+ "",
14642
+ "| ID | Label | Scope | Cadence | Default duration | Agenda template |",
14643
+ "|----|-------|-------|---------|------------------|-----------------|"
14644
+ ];
14645
+ for (const type of types) {
14646
+ const cadence = type.cadence ? `\`${type.cadence}\`` : "\u2014";
14647
+ const duration = type.defaultDurationMinutes !== void 0 ? `${type.defaultDurationMinutes} min` : "\u2014";
14648
+ const template = type.agendaTemplatePath ? `\`${type.agendaTemplatePath}\`` : "\u2014";
14649
+ lines.push(
14650
+ `| \`${type.id}\` | ${type.label} | \`${type.scope}\` | ${cadence} | ${duration} | ${template} |`
14651
+ );
14652
+ }
14653
+ return lines.join("\n");
14654
+ }
14655
+ function renderMeetingAreas(areas) {
14656
+ const lines = [
14657
+ "## Area \u2192 doc-root mapping",
14658
+ "",
14659
+ "This project declares a meeting-area routing map through",
14660
+ "`AgentConfigOptions.meetings.meetingAreas`. When a meeting note's",
14661
+ "frontmatter carries an `area:` value matching an `id` below, route",
14662
+ "phase-4 direct edits into the corresponding sub-tree of the docs",
14663
+ "root (`AgentPathsConfig.docsRoot`). Meetings whose `area` does not",
14664
+ "match any declared entry fall back to the default meetings root.",
14665
+ "",
14666
+ "| Area ID | Label | Doc-root sub-folder |",
14667
+ "|---------|-------|---------------------|"
14668
+ ];
14669
+ for (const area of areas) {
14670
+ lines.push(`| \`${area.id}\` | ${area.label} | \`${area.docRoot}\` |`);
14671
+ }
14672
+ return lines.join("\n");
14673
+ }
14674
+
14491
14675
  // src/agent/bundles/priority-rules.ts
14492
14676
  function renderPriorityRulesSection(rules) {
14493
14677
  if (rules.length === 0) {
@@ -15461,6 +15645,38 @@ ${section}`
15461
15645
  });
15462
15646
  }
15463
15647
  }
15648
+ if (this.options.focus) {
15649
+ const issueLabelRule = ruleMap.get("issue-label-conventions");
15650
+ if (issueLabelRule) {
15651
+ const section = renderFocusSection(this.options.focus);
15652
+ if (section.length > 0) {
15653
+ ruleMap.set("issue-label-conventions", {
15654
+ ...issueLabelRule,
15655
+ content: `${issueLabelRule.content}
15656
+
15657
+ ---
15658
+
15659
+ ${section}`
15660
+ });
15661
+ }
15662
+ }
15663
+ }
15664
+ if (this.options.meetings) {
15665
+ const meetingRule = ruleMap.get("meeting-processing-workflow");
15666
+ if (meetingRule) {
15667
+ const section = renderMeetingTypesSection(this.options.meetings);
15668
+ if (section.length > 0) {
15669
+ ruleMap.set("meeting-processing-workflow", {
15670
+ ...meetingRule,
15671
+ content: `${meetingRule.content}
15672
+
15673
+ ---
15674
+
15675
+ ${section}`
15676
+ });
15677
+ }
15678
+ }
15679
+ }
15464
15680
  return [...ruleMap.values()].sort((a, b) => {
15465
15681
  if (a.name === "project-overview") return -1;
15466
15682
  if (b.name === "project-overview") return 1;
@@ -18284,6 +18500,8 @@ export {
18284
18500
  pnpmBundle,
18285
18501
  prReviewBundle,
18286
18502
  projenBundle,
18503
+ renderFocusSection,
18504
+ renderMeetingTypesSection,
18287
18505
  renderPriorityRulesSection,
18288
18506
  requirementsAnalystBundle,
18289
18507
  requirementsReviewerBundle,