@codedrifters/configulator 0.0.257 → 0.0.258

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
@@ -1295,6 +1295,98 @@ var baseBundle = {
1295
1295
  ].join("\n"),
1296
1296
  tags: ["project"]
1297
1297
  },
1298
+ {
1299
+ name: "source-quality-verification",
1300
+ description: "Source tier hierarchy (T1\u2013T4), conflict resolution, temporal validity, and source-annotation format for research-producing agents",
1301
+ scope: AGENT_RULE_SCOPE.ALWAYS,
1302
+ content: [
1303
+ "# Source Quality & Verification",
1304
+ "",
1305
+ "All research-producing agents must follow this source hierarchy when",
1306
+ "writing profiles and reference documents. The tiers govern which",
1307
+ "source to prefer when claims conflict and when time-sensitive claims",
1308
+ "need re-verification.",
1309
+ "",
1310
+ "## Source Tier Hierarchy",
1311
+ "",
1312
+ "### T1 \u2014 Primary Living",
1313
+ "",
1314
+ "Authoritative sources that update when reality changes.",
1315
+ "Use these for current-state claims. Examples:",
1316
+ "",
1317
+ "- Official product docs and API references",
1318
+ "- Company about / leadership / team pages",
1319
+ "- Live government registries (SEC EDGAR, Companies House)",
1320
+ "- Current-role LinkedIn profiles",
1321
+ "- Investor relations pages",
1322
+ "",
1323
+ "### T2 \u2014 Primary Snapshot",
1324
+ "",
1325
+ "Authoritative but time-stamped sources straight from the subject.",
1326
+ "Trust for current state only when fresh (< 6 months) and no T1",
1327
+ "source contradicts them. Examples:",
1328
+ "",
1329
+ "- Press releases from the entity",
1330
+ "- Earnings transcripts and investor calls",
1331
+ "- Regulatory filings (10-K, 10-Q, 8-K)",
1332
+ "- Official announcements and conference presentations",
1333
+ "",
1334
+ "### T3 \u2014 Secondary",
1335
+ "",
1336
+ "Third-party sources that interpret or re-report primary material.",
1337
+ "Good for context and triangulation; T1 wins on conflict. Examples:",
1338
+ "",
1339
+ "- News articles and trade press",
1340
+ "- Industry reports and analyst research",
1341
+ "- Database aggregators (Crunchbase, PitchBook, Owler)",
1342
+ "",
1343
+ "### T4 \u2014 Self-Reported / Marketing",
1344
+ "",
1345
+ "Promotional or unverified sources. Use for discovery and leads only",
1346
+ "\u2014 never as the sole source for a factual claim, and always note the",
1347
+ "self-reported nature in the annotation. Examples:",
1348
+ "",
1349
+ "- Vendor comparison pages and customer testimonials on vendor sites",
1350
+ "- Social-media posts",
1351
+ "- Job postings",
1352
+ "- Wikipedia",
1353
+ "",
1354
+ "## Conflict Resolution",
1355
+ "",
1356
+ "Higher tier wins. Within the same tier, more recent wins. If a newer",
1357
+ "T2 source contradicts an older T1 source on a time-sensitive claim,",
1358
+ "flag for re-verification \u2014 the T1 source may simply not have been",
1359
+ "updated yet.",
1360
+ "",
1361
+ "## Temporal Validity",
1362
+ "",
1363
+ "Some claims decay faster than others. When a fast-decay claim comes",
1364
+ "from a source older than 6 months, agents **must** attempt to",
1365
+ "corroborate it with a T1 living source. If no T1 source is available,",
1366
+ "add an inline note: `_(source: YYYY-MM, may need re-verification)_`.",
1367
+ "",
1368
+ "| Decay | Re-verify if source older than | Typical claims |",
1369
+ "|-------|-------------------------------|----------------|",
1370
+ "| **Fast** | 6 months | Leadership roles, employee count, pricing, product features, funding status, office locations |",
1371
+ "| **Slow** | 18 months | Founding year, ownership structure, core business description, industry classification, HQ location |",
1372
+ "| **Stable** | Verify once, note source date | Historical facts (acquisition dates, founding story), published financials for a specific period |",
1373
+ "",
1374
+ "## Source Annotation Format",
1375
+ "",
1376
+ "In the `## Sources` section of profiles and reference documents,",
1377
+ "annotate each source with its tier and date:",
1378
+ "",
1379
+ "```",
1380
+ "1. [Company Leadership Page](https://...) \u2014 T1, accessed 2026-04",
1381
+ "2. [Company press release](https://...) \u2014 T2, published 2026-02",
1382
+ "3. [TechCrunch coverage](https://...) \u2014 T3, published 2025-11",
1383
+ "```",
1384
+ "",
1385
+ "Use `accessed YYYY-MM` for living sources (T1) and `published YYYY-MM`",
1386
+ "for snapshot sources (T2, T3, T4)."
1387
+ ].join("\n"),
1388
+ tags: ["workflow"]
1389
+ },
1298
1390
  {
1299
1391
  name: "issue-conventions",
1300
1392
  description: "Issue title prefixes, GitHub issue type mapping, prerequisite issues",
@@ -14488,6 +14580,141 @@ var vitestBundle = {
14488
14580
  }
14489
14581
  };
14490
14582
 
14583
+ // src/agent/bundles/features.ts
14584
+ var SOURCE_TIER_HEADINGS = [
14585
+ { key: "t1", heading: "### T1 \u2014 Primary Living" },
14586
+ { key: "t2", heading: "### T2 \u2014 Primary Snapshot" },
14587
+ { key: "t3", heading: "### T3 \u2014 Secondary" },
14588
+ { key: "t4", heading: "### T4 \u2014 Self-Reported / Marketing" }
14589
+ ];
14590
+ function renderSourceTierExamples(content, examples) {
14591
+ if (!examples) {
14592
+ return content;
14593
+ }
14594
+ const tierEntries = SOURCE_TIER_HEADINGS.flatMap(({ key, heading }) => {
14595
+ const list = examples[key];
14596
+ if (!list || list.length === 0) {
14597
+ return [];
14598
+ }
14599
+ return [{ heading, list }];
14600
+ });
14601
+ if (tierEntries.length === 0) {
14602
+ return content;
14603
+ }
14604
+ let updated = content;
14605
+ for (const { heading, list } of tierEntries) {
14606
+ const injection = [
14607
+ "",
14608
+ "**Project-specific examples:**",
14609
+ "",
14610
+ ...list.map((ex) => `- ${ex}`)
14611
+ ].join("\n");
14612
+ updated = insertAfterHeadingBlock(updated, heading, injection);
14613
+ }
14614
+ return updated;
14615
+ }
14616
+ function renderCustomDocSections(bundle, sections) {
14617
+ const matching = sections.filter((s) => s.bundleName === bundle.name);
14618
+ if (matching.length === 0) {
14619
+ return bundle;
14620
+ }
14621
+ let rules = bundle.rules;
14622
+ const currentAnchorByTarget = /* @__PURE__ */ new Map();
14623
+ for (const section of matching) {
14624
+ const injection = renderCustomDocSectionBlock(section);
14625
+ const seededAnchor = currentAnchorByTarget.get(section.afterSection);
14626
+ let targetIndex = -1;
14627
+ let targetHeading = "";
14628
+ for (let i = 0; i < rules.length; i += 1) {
14629
+ if (seededAnchor && rules[i].content.includes(seededAnchor)) {
14630
+ targetIndex = i;
14631
+ targetHeading = seededAnchor;
14632
+ break;
14633
+ }
14634
+ if (!seededAnchor) {
14635
+ const match = findHeadingForTitle(
14636
+ rules[i].content,
14637
+ section.afterSection
14638
+ );
14639
+ if (match) {
14640
+ targetIndex = i;
14641
+ targetHeading = match;
14642
+ break;
14643
+ }
14644
+ }
14645
+ }
14646
+ if (targetIndex === -1) {
14647
+ continue;
14648
+ }
14649
+ const targetRule = rules[targetIndex];
14650
+ const updatedRule = {
14651
+ ...targetRule,
14652
+ content: insertAfterHeadingBlock(
14653
+ targetRule.content,
14654
+ targetHeading,
14655
+ injection
14656
+ )
14657
+ };
14658
+ rules = [
14659
+ ...rules.slice(0, targetIndex),
14660
+ updatedRule,
14661
+ ...rules.slice(targetIndex + 1)
14662
+ ];
14663
+ currentAnchorByTarget.set(
14664
+ section.afterSection,
14665
+ `## ${section.sectionTitle}`
14666
+ );
14667
+ }
14668
+ if (rules === bundle.rules) {
14669
+ return bundle;
14670
+ }
14671
+ return { ...bundle, rules };
14672
+ }
14673
+ function renderCustomDocSectionBlock(section) {
14674
+ const body = section.body.replace(/[\r\n]+$/u, "");
14675
+ return ["", `## ${section.sectionTitle}`, "", body].join("\n");
14676
+ }
14677
+ function findHeadingForTitle(content, title) {
14678
+ const lines = content.split("\n");
14679
+ for (const line of lines) {
14680
+ const match = line.match(/^(#{1,6}) (.+)$/u);
14681
+ if (match && match[2] === title) {
14682
+ return line;
14683
+ }
14684
+ }
14685
+ return void 0;
14686
+ }
14687
+ function insertAfterHeadingBlock(content, heading, injection) {
14688
+ const lines = content.split("\n");
14689
+ const headingIndex = lines.findIndex((line) => line === heading);
14690
+ if (headingIndex === -1) {
14691
+ return content;
14692
+ }
14693
+ const headingLevel = countLeadingHashes(heading);
14694
+ let endIndex = lines.length - 1;
14695
+ for (let i = headingIndex + 1; i < lines.length; i += 1) {
14696
+ const nextLevel = countLeadingHashes(lines[i]);
14697
+ if (nextLevel > 0 && nextLevel <= headingLevel) {
14698
+ endIndex = i - 1;
14699
+ break;
14700
+ }
14701
+ }
14702
+ while (endIndex > headingIndex && lines[endIndex] === "") {
14703
+ endIndex -= 1;
14704
+ }
14705
+ const injectionLines = injection.split("\n");
14706
+ const next = [
14707
+ ...lines.slice(0, endIndex + 1),
14708
+ ...injectionLines,
14709
+ ...lines.slice(endIndex + 1)
14710
+ ];
14711
+ return next.join("\n");
14712
+ }
14713
+ function countLeadingHashes(line) {
14714
+ const match = line.match(/^(#{1,6}) /u);
14715
+ return match ? match[1].length : 0;
14716
+ }
14717
+
14491
14718
  // src/agent/bundles/focus.ts
14492
14719
  var DEFAULT_FOCUS_FILE_PATH = ".claude/focus.json";
14493
14720
  var DEFAULT_SCHEMA_VERSION = 1;
@@ -15458,6 +15685,19 @@ var AgentConfig = class _AgentConfig extends Component8 {
15458
15685
  const isAgentConfig = (c) => c instanceof _AgentConfig;
15459
15686
  return project.components.find(isAgentConfig);
15460
15687
  }
15688
+ /**
15689
+ * Returns `true` when at least one tier array on the supplied
15690
+ * `SourceTierExamples` is non-empty, signalling that the consuming
15691
+ * repo has opted into rendering the base bundle's
15692
+ * `source-quality-verification` rule. Returns `false` for
15693
+ * `undefined`, `{}`, or a fully-empty `{ t1: [], t2: [], t3: [], t4: [] }`.
15694
+ */
15695
+ static hasActiveTierExamples(examples) {
15696
+ if (!examples) {
15697
+ return false;
15698
+ }
15699
+ return (examples.t1?.length ?? 0) > 0 || (examples.t2?.length ?? 0) > 0 || (examples.t3?.length ?? 0) > 0 || (examples.t4?.length ?? 0) > 0;
15700
+ }
15461
15701
  /**
15462
15702
  * Merges default Claude permissions with bundle and user-supplied settings.
15463
15703
  *
@@ -15677,6 +15917,24 @@ ${section}`
15677
15917
  }
15678
15918
  }
15679
15919
  }
15920
+ const tierExamples = this.options.features?.sourceTierExamples;
15921
+ if (_AgentConfig.hasActiveTierExamples(tierExamples)) {
15922
+ const sourceRule = ruleMap.get("source-quality-verification");
15923
+ if (sourceRule) {
15924
+ const updated = renderSourceTierExamples(
15925
+ sourceRule.content,
15926
+ tierExamples
15927
+ );
15928
+ if (updated !== sourceRule.content) {
15929
+ ruleMap.set("source-quality-verification", {
15930
+ ...sourceRule,
15931
+ content: updated
15932
+ });
15933
+ }
15934
+ }
15935
+ } else {
15936
+ ruleMap.delete("source-quality-verification");
15937
+ }
15680
15938
  return [...ruleMap.values()].sort((a, b) => {
15681
15939
  if (a.name === "project-overview") return -1;
15682
15940
  if (b.name === "project-overview") return 1;
@@ -15689,19 +15947,24 @@ ${section}`
15689
15947
  /**
15690
15948
  * Return a bundle's rules with `filePatterns` narrowed to the projects
15691
15949
  * that actually matched the bundle's detection predicate (when the bundle
15692
- * provides `findApplicableProjects`). Rules with `ALWAYS` scope and rules
15693
- * on bundles that don't implement the hook are returned unchanged.
15950
+ * provides `findApplicableProjects`) and any project-specified
15951
+ * `features.customDocSections` entries injected into the matching rule
15952
+ * content. Rules with `ALWAYS` scope and rules on bundles that don't
15953
+ * implement the hook are returned with only the custom-section
15954
+ * transformation (if any) applied.
15694
15955
  */
15695
15956
  bundleRulesFor(bundle) {
15696
- if (!bundle.findApplicableProjects) {
15697
- return bundle.rules;
15957
+ const customSections = this.options.features?.customDocSections ?? [];
15958
+ const withCustomSections = customSections.length > 0 ? renderCustomDocSections(bundle, customSections) : bundle;
15959
+ if (!withCustomSections.findApplicableProjects) {
15960
+ return withCustomSections.rules;
15698
15961
  }
15699
- const detected = bundle.findApplicableProjects(this.project);
15962
+ const detected = withCustomSections.findApplicableProjects(this.project);
15700
15963
  if (detected.length === 0) {
15701
- return bundle.rules;
15964
+ return withCustomSections.rules;
15702
15965
  }
15703
15966
  const scoped = scopeFilePatternsToProjects(this.project, detected);
15704
- return bundle.rules.map(
15967
+ return withCustomSections.rules.map(
15705
15968
  (rule) => rule.scope === AGENT_RULE_SCOPE.FILE_PATTERN ? { ...rule, filePatterns: scoped } : rule
15706
15969
  );
15707
15970
  }
@@ -18500,9 +18763,12 @@ export {
18500
18763
  pnpmBundle,
18501
18764
  prReviewBundle,
18502
18765
  projenBundle,
18766
+ renderCustomDocSectionBlock,
18767
+ renderCustomDocSections,
18503
18768
  renderFocusSection,
18504
18769
  renderMeetingTypesSection,
18505
18770
  renderPriorityRulesSection,
18771
+ renderSourceTierExamples,
18506
18772
  requirementsAnalystBundle,
18507
18773
  requirementsReviewerBundle,
18508
18774
  requirementsWriterBundle,