@codedrifters/configulator 0.0.276 → 0.0.278

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.js CHANGED
@@ -321,7 +321,11 @@ __export(index_exports, {
321
321
  classifyRun: () => classifyRun,
322
322
  companyProfileBundle: () => companyProfileBundle,
323
323
  compileFencedSamples: () => compileFencedSamples,
324
+ createApiDiffCheck: () => createApiDiffCheck,
325
+ createReferenceMismatchCheck: () => createReferenceMismatchCheck,
326
+ createTsdocCoverageCheck: () => createTsdocCoverageCheck,
324
327
  customerProfileBundle: () => customerProfileBundle,
328
+ diffApiRollups: () => diffApiRollups,
325
329
  docsSyncBundle: () => docsSyncBundle,
326
330
  emptyCategoryBuckets: () => emptyCategoryBuckets,
327
331
  extractApiProcedure: () => extractApiProcedure,
@@ -336,11 +340,13 @@ __export(index_exports, {
336
340
  maintenanceAuditBundle: () => maintenanceAuditBundle,
337
341
  meetingAnalysisBundle: () => meetingAnalysisBundle,
338
342
  orchestratorBundle: () => orchestratorBundle,
343
+ parseApiRollup: () => parseApiRollup,
339
344
  peopleProfileBundle: () => peopleProfileBundle,
340
345
  persistAuditReport: () => persistAuditReport,
341
346
  pnpmBundle: () => pnpmBundle,
342
347
  prReviewBundle: () => prReviewBundle,
343
348
  projenBundle: () => projenBundle,
349
+ referenceRecordToFinding: () => referenceRecordToFinding,
344
350
  regulatoryResearchBundle: () => regulatoryResearchBundle,
345
351
  renderAgentTierCaseStatement: () => renderAgentTierCaseStatement,
346
352
  renderAgentTierSection: () => renderAgentTierSection,
@@ -403,6 +409,7 @@ __export(index_exports, {
403
409
  slackBundle: () => slackBundle,
404
410
  softwareProfileBundle: () => softwareProfileBundle,
405
411
  standardsResearchBundle: () => standardsResearchBundle,
412
+ tsdocRecordToFindings: () => tsdocRecordToFindings,
406
413
  turborepoBundle: () => turborepoBundle,
407
414
  typescriptBundle: () => typescriptBundle,
408
415
  validateAgentTierConfig: () => validateAgentTierConfig,
@@ -3116,20 +3123,26 @@ var createPackageSkill = {
3116
3123
  " places the sub-project at `packages/<scope>/<name>`.",
3117
3124
  "",
3118
3125
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
3119
- " the repo root:",
3126
+ " the repo root, the safe three-step regen sequence:",
3120
3127
  "",
3121
3128
  " ```",
3129
+ " pnpm i",
3122
3130
  " pnpm exec projen",
3123
- " pnpm install",
3131
+ " pnpm i",
3124
3132
  " ```",
3125
3133
  "",
3126
- " `pnpm exec projen` regenerates the sub-project tree;",
3127
- " `pnpm install` updates the workspace lockfile. Do **not** run",
3128
- " these commands yourself \u2014 the user runs them.",
3134
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
3135
+ " so synth runs against the right configulator/projen/plugin",
3136
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
3137
+ " stale resolved version of any pnpm-managed component and produce",
3138
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
3139
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
3140
+ " lockfile to pick up any dependency changes projen wrote during",
3141
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
3129
3142
  "",
3130
3143
  "## Guardrails",
3131
3144
  "",
3132
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
3145
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
3133
3146
  " `pnpm test`, or any other package-manager, build, or test command.",
3134
3147
  " Emit the projen block and instructions only.",
3135
3148
  "- Never scaffold outside `packages/`. Deployable apps belong under",
@@ -3239,20 +3252,26 @@ var createAppSkill = {
3239
3252
  " sub-project at `apps/<scope>/<name>`.",
3240
3253
  "",
3241
3254
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
3242
- " the repo root:",
3255
+ " the repo root, the safe three-step regen sequence:",
3243
3256
  "",
3244
3257
  " ```",
3258
+ " pnpm i",
3245
3259
  " pnpm exec projen",
3246
- " pnpm install",
3260
+ " pnpm i",
3247
3261
  " ```",
3248
3262
  "",
3249
- " `pnpm exec projen` regenerates the sub-project tree;",
3250
- " `pnpm install` updates the workspace lockfile. Do **not** run",
3251
- " these commands yourself \u2014 the user runs them.",
3263
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
3264
+ " so synth runs against the right configulator/projen/plugin",
3265
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
3266
+ " stale resolved version of any pnpm-managed component and produce",
3267
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
3268
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
3269
+ " lockfile to pick up any dependency changes projen wrote during",
3270
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
3252
3271
  "",
3253
3272
  "## Guardrails",
3254
3273
  "",
3255
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
3274
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
3256
3275
  " `pnpm test`, or any other package-manager, build, or test command.",
3257
3276
  " Emit the projen block and instructions only.",
3258
3277
  "- Never scaffold outside `apps/`. Shared libraries belong under",
@@ -3398,20 +3417,26 @@ var createSiteSkill = {
3398
3417
  " `sites/<scope>/<name>`.",
3399
3418
  "",
3400
3419
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
3401
- " the repo root:",
3420
+ " the repo root, the safe three-step regen sequence:",
3402
3421
  "",
3403
3422
  " ```",
3423
+ " pnpm i",
3404
3424
  " pnpm exec projen",
3405
- " pnpm install",
3425
+ " pnpm i",
3406
3426
  " ```",
3407
3427
  "",
3408
- " `pnpm exec projen` regenerates the sub-project tree;",
3409
- " `pnpm install` updates the workspace lockfile. Do **not** run",
3410
- " these commands yourself \u2014 the user runs them.",
3428
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
3429
+ " so synth runs against the right configulator/projen/plugin",
3430
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
3431
+ " stale resolved version of any pnpm-managed component and produce",
3432
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
3433
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
3434
+ " lockfile to pick up any dependency changes projen wrote during",
3435
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
3411
3436
  "",
3412
3437
  "## Guardrails",
3413
3438
  "",
3414
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
3439
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
3415
3440
  " `pnpm test`, or any other package-manager, build, or test command.",
3416
3441
  " Emit the projen block and instructions only.",
3417
3442
  "- Never scaffold the monorepo-wide docs site under `sites/`. The",
@@ -3515,7 +3540,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
3515
3540
  "## Important Notes",
3516
3541
  "",
3517
3542
  "- **Never edit generated files** \u2014 they are marked with `// ~~ Generated by projen`",
3518
- "- **After modifying Projen configuration**, run `pnpm exec projen` to regenerate files, then `pnpm install` to update the lockfile.",
3543
+ "- **After modifying Projen configuration**, run the three-step regen sequence: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again. The leading `pnpm i` syncs `node_modules` with the lockfile so synth runs against the right configulator/projen/plugin versions; the trailing `pnpm i` refreshes the lockfile to match anything projen rewrote in `package.json`.",
3519
3544
  "- **Configure dependencies through Projen** \u2014 never use `npm install`, `pnpm add`, or `yarn add`. Add them to `deps` or `devDeps` in Projen config.",
3520
3545
  "- **Export from index.ts** to maintain clean public APIs",
3521
3546
  "",
@@ -3574,7 +3599,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
3574
3599
  "",
3575
3600
  "After making changes that need validation, tell the user the specific commands to run:",
3576
3601
  "",
3577
- "1. **After projen config changes** \u2014 tell the user to run `pnpm exec projen && pnpm install`",
3602
+ "1. **After projen config changes** \u2014 tell the user to run the three-step regen sequence: `pnpm i && pnpm exec projen && pnpm i`",
3578
3603
  "2. **After source code changes** \u2014 tell the user to run `pnpm --filter @codedrifters/<package> test`",
3579
3604
  "3. **After multi-package changes** \u2014 tell the user to run `pnpm build:all`"
3580
3605
  ].join("\n"),
@@ -9350,7 +9375,7 @@ var githubWorkflowBundle = {
9350
9375
  "",
9351
9376
  "When the user says **open a PR** (or similar), follow these steps exactly:",
9352
9377
  "",
9353
- "1. **Regenerate project files** \u2014 run `pnpm exec projen` then `pnpm install` to ensure all generated files are up to date. Check `git diff` \u2014 if there are changes, commit them before proceeding.",
9378
+ "1. **Regenerate project files** \u2014 run the three-step regen sequence (`pnpm i`, then `pnpm exec projen`, then `pnpm i` again) to ensure all generated files are up to date. The leading `pnpm i` is required because `pnpm exec projen` synthesises against whatever version of configulator (and projen, and any projen plugins) is currently resolved in `node_modules`; if the lockfile has moved past `node_modules` (typically right after `git pull` lands a dependency upgrade, or on a fresh checkout), synth runs against stale templates and produces phantom drift in `.claude/`, `.github/labels.yml`, `CLAUDE.md`, and other generated files. The trailing `pnpm i` picks up any dependency changes projen wrote into `package.json` during synth. Check `git diff` after the third step \u2014 if there are changes, commit them before proceeding.",
9354
9379
  "2. **Run the full monorepo build** \u2014 run `pnpm build:all` to compile, lint, and test all packages (mirrors the CI pipeline). This command requires the user to be authenticated to AWS on the prod account used for Turborepo remote caching (`readonlyaccess-prod-525259625215-us-east-1` profile). If the command fails due to AWS credentials, ask the user to authenticate first. If the build produces changes to turbo inputs (typically snapshot files or ESLint auto-fixes), commit those changes and run `pnpm build:all` again \u2014 the build must complete cleanly with no uncommitted changes.",
9355
9380
  "3. **Check for uncommitted changes** \u2014 if any exist, commit them with a conventional commit message",
9356
9381
  "4. **Pull and rebase from the default branch** \u2014 run `git pull origin {{repository.defaultBranch}} --rebase` to incorporate the latest changes and resolve any conflicts before pushing",
@@ -14048,7 +14073,7 @@ var issueWorkerSubAgent = {
14048
14073
  "",
14049
14074
  "Run the appropriate verification commands depending on what changed:",
14050
14075
  "",
14051
- "1. If Projen config was changed: `pnpm exec projen && pnpm install`",
14076
+ "1. If Projen config was changed: run the three-step regen sequence `pnpm i && pnpm exec projen && pnpm i` (leading `pnpm i` syncs `node_modules` with the lockfile so synth runs against the right configulator/projen/plugin versions; trailing `pnpm i` refreshes the lockfile after synth)",
14052
14077
  "2. Compile the affected package: `pnpm --filter @codedrifters/<package> compile`",
14053
14078
  "3. Test the affected package: `pnpm --filter @codedrifters/<package> test`",
14054
14079
  "4. If changes span multiple packages: `pnpm build:all`",
@@ -15339,7 +15364,7 @@ var pnpmBundle = {
15339
15364
  "- Configure dependencies in Projen configuration files (`.projenrc.ts` or `projenrc/*.ts`)",
15340
15365
  "- Add dependencies to `deps`, `devDeps`, or `peerDeps` arrays in project configuration",
15341
15366
  '- Use catalog dependencies when available (e.g., `"aws-cdk-lib@catalog:"`)',
15342
- "- Ask the user to run `pnpm exec projen` and `pnpm install` after updating dependency configuration",
15367
+ "- Ask the user to run the three-step regen sequence after updating dependency configuration: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again. The leading `pnpm i` syncs `node_modules` with the lockfile so synth runs against the right configulator/projen/plugin versions; the trailing `pnpm i` picks up any dependency changes projen wrote during synth.",
15343
15368
  "",
15344
15369
  "**DO NOT:**",
15345
15370
  "- Run `npm install some-package`",
@@ -16776,14 +16801,19 @@ var projenBundle = {
16776
16801
  "",
16777
16802
  "## Synthesizing Projen Configuration",
16778
16803
  "",
16779
- "After modifying any file in `projenrc/` or `.projenrc.ts`, regenerate project files:",
16804
+ "After modifying any file in `projenrc/` or `.projenrc.ts`, regenerate project files using the safe three-step sequence:",
16780
16805
  "",
16781
16806
  "```sh",
16807
+ "pnpm i",
16782
16808
  "pnpm exec projen",
16783
- "pnpm install",
16809
+ "pnpm i",
16784
16810
  "```",
16785
16811
  "",
16786
- "Both steps are required \u2014 `pnpm exec projen` regenerates files, `pnpm install` updates the lockfile to match.",
16812
+ "All three steps are required:",
16813
+ "",
16814
+ "1. The leading `pnpm i` guarantees `node_modules` matches `pnpm-lock.yaml` before synth. Without this step, `pnpm exec projen` may synth against a stale version of any pnpm-resolved component (configulator itself, projen, or any plugin) that the lockfile has already moved past \u2014 most commonly right after `git pull` updates the lockfile or on a fresh checkout. The result is phantom drift in `.claude/`, `.github/labels.yml`, `CLAUDE.md`, and other configulator-generated files that looks like a real bundle change but vanishes after running `pnpm i`.",
16815
+ "2. `pnpm exec projen` regenerates files against the now-synced templates.",
16816
+ "3. The trailing `pnpm i` picks up any dependency changes projen wrote into `package.json` during the synth pass and refreshes the lockfile.",
16787
16817
  "",
16788
16818
  "## Building",
16789
16819
  "",
@@ -16864,7 +16894,7 @@ var projenBundle = {
16864
16894
  "After finishing implementation work, validate that changes are correct by running the appropriate commands depending on what was changed:",
16865
16895
  "",
16866
16896
  "1. **Projen config changes** (`projenrc/`, `.projenrc.ts`):",
16867
- " - Run `pnpm exec projen` then `pnpm install`",
16897
+ " - Run the three-step regen sequence: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again",
16868
16898
  " - Verify no unexpected generated file changes with `git diff`",
16869
16899
  "",
16870
16900
  "2. **Source code changes** (in a sub-package):",
@@ -16881,8 +16911,8 @@ var projenBundle = {
16881
16911
  "",
16882
16912
  "| Task | Command |",
16883
16913
  "|------|---------|",
16884
- "| Synthesize projen | `pnpm exec projen` |",
16885
- "| Install deps | `pnpm install` |",
16914
+ "| Regenerate projen configuration | `pnpm i && pnpm exec projen && pnpm i` |",
16915
+ "| Install deps | `pnpm i` |",
16886
16916
  "| Full monorepo build | `pnpm build:all` |",
16887
16917
  "| Root build only | `pnpm build` |",
16888
16918
  "| Compile one package | `pnpm --filter @codedrifters/<pkg> compile` |",
@@ -16940,7 +16970,7 @@ var projenBundle = {
16940
16970
  "",
16941
16971
  "## After Any Change",
16942
16972
  "",
16943
- "Run `pnpm exec projen` then `pnpm install` to regenerate the output files."
16973
+ "Run the three-step regen sequence to regenerate the output files: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again. The leading `pnpm i` syncs `node_modules` with the lockfile before synth so `pnpm exec projen` runs against the right configulator/projen/plugin versions; the trailing `pnpm i` refreshes the lockfile for any dependency changes projen made during synth."
16944
16974
  ].join("\n"),
16945
16975
  tags: ["workflow"]
16946
16976
  },
@@ -16958,7 +16988,7 @@ var projenBundle = {
16958
16988
  "- Edit Projen configuration in:",
16959
16989
  " - `.projenrc.ts` (root)",
16960
16990
  " - `projenrc/*.ts` (package-specific)",
16961
- "- After making Projen changes, run `pnpm exec projen` to synthesize",
16991
+ "- After making Projen changes, run the three-step regen sequence: `pnpm i`, `pnpm exec projen`, `pnpm i`. The leading `pnpm i` ensures `node_modules` matches the lockfile so synth runs against the right configulator/projen/plugin versions; without it, synth can produce phantom drift in generated files. The trailing `pnpm i` picks up dependency changes projen wrote during synth.",
16962
16992
  "",
16963
16993
  "## Workspace Dependencies",
16964
16994
  "",
@@ -26803,8 +26833,8 @@ var FALLBACKS = {
26803
26833
  monorepoLayoutSeedBlock: ""
26804
26834
  };
26805
26835
  var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
26806
- function getNestedValue(obj, path7) {
26807
- const parts = path7.split(".");
26836
+ function getNestedValue(obj, path8) {
26837
+ const parts = path8.split(".");
26808
26838
  let current = obj;
26809
26839
  for (const part of parts) {
26810
26840
  if (current == null || typeof current !== "object") {
@@ -29413,9 +29443,388 @@ function auditReportJsonSchema() {
29413
29443
  };
29414
29444
  }
29415
29445
 
29446
+ // src/docs-sync/scan/checks/api-diff-check.ts
29447
+ function parseApiRollup(rollup) {
29448
+ const fenceContents = extractFirstTsFence(rollup);
29449
+ if (fenceContents === void 0) {
29450
+ return [];
29451
+ }
29452
+ const lines = fenceContents.split("\n");
29453
+ const entries = [];
29454
+ let i = 0;
29455
+ while (i < lines.length) {
29456
+ const line = lines[i];
29457
+ const trimmed = line.trim();
29458
+ if (!trimmed.startsWith("export ")) {
29459
+ i += 1;
29460
+ continue;
29461
+ }
29462
+ const startIndex = i;
29463
+ let buffer = trimmed;
29464
+ let braceDepth = countBraceDelta(trimmed);
29465
+ let endsWithSemicolon = trimmed.endsWith(";");
29466
+ i += 1;
29467
+ while (i < lines.length && !isComplete(buffer, braceDepth, endsWithSemicolon)) {
29468
+ const next = lines[i];
29469
+ const nextTrimmed = next.trim();
29470
+ if (nextTrimmed.length === 0 || nextTrimmed.startsWith("//")) {
29471
+ i += 1;
29472
+ continue;
29473
+ }
29474
+ buffer = `${buffer} ${nextTrimmed}`;
29475
+ braceDepth += countBraceDelta(nextTrimmed);
29476
+ endsWithSemicolon = nextTrimmed.endsWith(";");
29477
+ i += 1;
29478
+ }
29479
+ const normalized = normalizeWhitespace(buffer);
29480
+ const name = extractDeclarationName(normalized);
29481
+ if (name) {
29482
+ entries.push({ name, signature: normalized });
29483
+ }
29484
+ if (name && normalized.startsWith("export {")) {
29485
+ const names = extractExportListNames(normalized);
29486
+ if (names.length > 1) {
29487
+ entries.pop();
29488
+ for (const exportName of names) {
29489
+ entries.push({ name: exportName, signature: normalized });
29490
+ }
29491
+ }
29492
+ }
29493
+ if (i === startIndex) {
29494
+ i += 1;
29495
+ }
29496
+ }
29497
+ const dedup = /* @__PURE__ */ new Map();
29498
+ for (const entry of entries) {
29499
+ if (!dedup.has(entry.name)) {
29500
+ dedup.set(entry.name, entry);
29501
+ }
29502
+ }
29503
+ return Array.from(dedup.values()).sort(
29504
+ (a, b) => a.name.localeCompare(b.name)
29505
+ );
29506
+ }
29507
+ function diffApiRollups(baselineRollup, currentRollup) {
29508
+ const baseline = /* @__PURE__ */ new Map();
29509
+ for (const entry of parseApiRollup(baselineRollup)) {
29510
+ baseline.set(entry.name, entry);
29511
+ }
29512
+ const current = /* @__PURE__ */ new Map();
29513
+ for (const entry of parseApiRollup(currentRollup)) {
29514
+ current.set(entry.name, entry);
29515
+ }
29516
+ const added = [];
29517
+ const removed = [];
29518
+ const changed = [];
29519
+ for (const [name, entry] of current.entries()) {
29520
+ const before = baseline.get(name);
29521
+ if (!before) {
29522
+ added.push(entry);
29523
+ continue;
29524
+ }
29525
+ if (before.signature !== entry.signature) {
29526
+ changed.push(entry);
29527
+ }
29528
+ }
29529
+ for (const [name, entry] of baseline.entries()) {
29530
+ if (!current.has(name)) {
29531
+ removed.push(entry);
29532
+ }
29533
+ }
29534
+ added.sort((a, b) => a.name.localeCompare(b.name));
29535
+ removed.sort((a, b) => a.name.localeCompare(b.name));
29536
+ changed.sort((a, b) => a.name.localeCompare(b.name));
29537
+ return { added, removed, changed };
29538
+ }
29539
+ function createApiDiffCheck(options) {
29540
+ const baseline = options.baselineRollup;
29541
+ const current = options.currentRollup;
29542
+ const rollupPath = options.rollupPath ?? "";
29543
+ return {
29544
+ name: options.name ?? "apiDiff",
29545
+ run(_context) {
29546
+ const diff = diffApiRollups(baseline, current);
29547
+ const findings = [];
29548
+ for (const entry of diff.added) {
29549
+ findings.push({
29550
+ category: AuditCategory.ApiDiff,
29551
+ severity: AuditSeverity.Mechanical,
29552
+ location: { file: rollupPath, line: 0 },
29553
+ subject: entry.name,
29554
+ details: `New public export \`${entry.name}\` not present in baseline rollup.`,
29555
+ change: "added",
29556
+ symbol: entry.name,
29557
+ fixHint: "stub-tsdoc"
29558
+ });
29559
+ }
29560
+ for (const entry of diff.removed) {
29561
+ findings.push({
29562
+ category: AuditCategory.ApiDiff,
29563
+ severity: AuditSeverity.Advisory,
29564
+ location: { file: rollupPath, line: 0 },
29565
+ subject: entry.name,
29566
+ details: `Public export \`${entry.name}\` was present in baseline rollup but is gone in the current rollup.`,
29567
+ change: "removed",
29568
+ symbol: entry.name
29569
+ });
29570
+ }
29571
+ for (const entry of diff.changed) {
29572
+ findings.push({
29573
+ category: AuditCategory.ApiDiff,
29574
+ severity: AuditSeverity.Advisory,
29575
+ location: { file: rollupPath, line: 0 },
29576
+ subject: entry.name,
29577
+ details: `Public export \`${entry.name}\` signature changed since baseline rollup.`,
29578
+ change: "changed",
29579
+ symbol: entry.name
29580
+ });
29581
+ }
29582
+ return findings;
29583
+ }
29584
+ };
29585
+ }
29586
+ function extractFirstTsFence(rollup) {
29587
+ const lines = rollup.split("\n");
29588
+ let start = -1;
29589
+ for (let i = 0; i < lines.length; i++) {
29590
+ const line = lines[i].trim();
29591
+ if (line === "```ts" || line === "```typescript") {
29592
+ start = i + 1;
29593
+ break;
29594
+ }
29595
+ }
29596
+ if (start < 0) {
29597
+ return void 0;
29598
+ }
29599
+ for (let j = start; j < lines.length; j++) {
29600
+ if (lines[j].trim() === "```") {
29601
+ return lines.slice(start, j).join("\n");
29602
+ }
29603
+ }
29604
+ return lines.slice(start).join("\n");
29605
+ }
29606
+ function countBraceDelta(line) {
29607
+ let delta = 0;
29608
+ for (const ch of line) {
29609
+ if (ch === "{") {
29610
+ delta += 1;
29611
+ } else if (ch === "}") {
29612
+ delta -= 1;
29613
+ }
29614
+ }
29615
+ return delta;
29616
+ }
29617
+ function isComplete(buffer, braceDepth, endsWithSemicolon) {
29618
+ if (braceDepth > 0) {
29619
+ return false;
29620
+ }
29621
+ if (endsWithSemicolon) {
29622
+ return true;
29623
+ }
29624
+ return buffer.trimEnd().endsWith("}");
29625
+ }
29626
+ function normalizeWhitespace(s) {
29627
+ return s.replace(/\s+/g, " ").replace(/\(\s+/g, "(").replace(/\s+\)/g, ")").replace(/\[\s+/g, "[").replace(/\s+\]/g, "]").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/\s+,/g, ",").replace(/\s+;/g, ";").replace(/\s+:/g, ":").trim();
29628
+ }
29629
+ var DECLARATION_KEYWORDS = [
29630
+ "function",
29631
+ "class",
29632
+ "interface",
29633
+ "type",
29634
+ "enum",
29635
+ "const",
29636
+ "let",
29637
+ "var",
29638
+ "namespace",
29639
+ "abstract",
29640
+ "default",
29641
+ "async"
29642
+ ];
29643
+ function extractDeclarationName(declaration) {
29644
+ const tokens = declaration.split(/\s+/);
29645
+ if (tokens.length === 0 || tokens[0] !== "export") {
29646
+ return void 0;
29647
+ }
29648
+ let cursor = 1;
29649
+ while (cursor < tokens.length && DECLARATION_KEYWORDS.includes(tokens[cursor])) {
29650
+ cursor += 1;
29651
+ }
29652
+ if (cursor >= tokens.length) {
29653
+ return void 0;
29654
+ }
29655
+ const candidate = tokens[cursor];
29656
+ if (candidate.startsWith("{")) {
29657
+ const stripped = candidate.replace(/^\{/, "").replace(/[},].*$/, "");
29658
+ return stripped || void 0;
29659
+ }
29660
+ const cleaned = candidate.replace(/[<(:;].*$/, "");
29661
+ return cleaned || void 0;
29662
+ }
29663
+ function extractExportListNames(declaration) {
29664
+ const open = declaration.indexOf("{");
29665
+ const close = declaration.indexOf("}", open);
29666
+ if (open < 0 || close < 0) {
29667
+ return [];
29668
+ }
29669
+ const inside = declaration.slice(open + 1, close);
29670
+ const names = [];
29671
+ for (const part of inside.split(",")) {
29672
+ const token = part.trim();
29673
+ if (!token) {
29674
+ continue;
29675
+ }
29676
+ const asMatch = token.match(/\sas\s+(\S+)$/);
29677
+ if (asMatch) {
29678
+ names.push(asMatch[1]);
29679
+ } else {
29680
+ names.push(token);
29681
+ }
29682
+ }
29683
+ return names;
29684
+ }
29685
+
29686
+ // src/docs-sync/scan/checks/reference-mismatch-check.ts
29687
+ function referenceRecordToFinding(record, signatureChangedSymbols) {
29688
+ if (signatureChangedSymbols.has(record.symbol)) {
29689
+ return {
29690
+ category: AuditCategory.ReferenceMismatches,
29691
+ severity: AuditSeverity.Advisory,
29692
+ location: { file: record.docPath, line: record.line },
29693
+ subject: record.symbol,
29694
+ details: `Inline reference \`${record.symbol}\` resolves to a symbol whose signature has changed; review the surrounding prose.`,
29695
+ mismatch: "signature-changed",
29696
+ symbol: record.symbol
29697
+ };
29698
+ }
29699
+ if (!record.isKnown) {
29700
+ return {
29701
+ category: AuditCategory.ReferenceMismatches,
29702
+ severity: AuditSeverity.Advisory,
29703
+ location: { file: record.docPath, line: record.line },
29704
+ subject: record.symbol,
29705
+ details: `Inline reference \`${record.symbol}\` does not match any known public export.`,
29706
+ mismatch: "unknown-symbol",
29707
+ symbol: record.symbol
29708
+ };
29709
+ }
29710
+ return void 0;
29711
+ }
29712
+ function createReferenceMismatchCheck(options) {
29713
+ const records = options.records;
29714
+ const signatureChangedSymbols = new Set(
29715
+ options.signatureChangedSymbols ?? []
29716
+ );
29717
+ return {
29718
+ name: options.name ?? "referenceMismatches",
29719
+ run(_context) {
29720
+ const findings = [];
29721
+ for (const record of records) {
29722
+ const finding = referenceRecordToFinding(
29723
+ record,
29724
+ signatureChangedSymbols
29725
+ );
29726
+ if (finding !== void 0) {
29727
+ findings.push(finding);
29728
+ }
29729
+ }
29730
+ return findings;
29731
+ }
29732
+ };
29733
+ }
29734
+
29735
+ // src/docs-sync/scan/checks/tsdoc-coverage-check.ts
29736
+ var path5 = __toESM(require("path"));
29737
+ function tsdocRecordToFindings(record, context) {
29738
+ const findings = [];
29739
+ const file = relativizeFile(record.location.file, context.repoRoot);
29740
+ const line = record.location.line;
29741
+ const symbol = record.symbol;
29742
+ if (!record.hasSummary) {
29743
+ findings.push({
29744
+ category: AuditCategory.TsdocCoverage,
29745
+ severity: AuditSeverity.Mechanical,
29746
+ location: { file, line },
29747
+ subject: symbol,
29748
+ details: `Public export \`${symbol}\` is missing a TSDoc summary.`,
29749
+ shortfall: "missing-summary",
29750
+ symbol,
29751
+ fixHint: "stub-tsdoc"
29752
+ });
29753
+ return findings;
29754
+ }
29755
+ if (record.hasThinSummary) {
29756
+ findings.push({
29757
+ category: AuditCategory.TsdocCoverage,
29758
+ severity: AuditSeverity.Advisory,
29759
+ location: { file, line },
29760
+ subject: symbol,
29761
+ details: `Public export \`${symbol}\` carries a thin TSDoc summary; consider expanding it.`,
29762
+ shortfall: "thin-summary",
29763
+ symbol
29764
+ });
29765
+ }
29766
+ if (!record.hasParams && isParamCarryingKind(record)) {
29767
+ findings.push({
29768
+ category: AuditCategory.TsdocCoverage,
29769
+ severity: AuditSeverity.Advisory,
29770
+ location: { file, line },
29771
+ subject: symbol,
29772
+ details: `Public export \`${symbol}\` has parameters with no \`@param\` block tag.`,
29773
+ shortfall: "missing-params",
29774
+ symbol
29775
+ });
29776
+ }
29777
+ if (!record.hasReturns && isReturnCarryingKind(record)) {
29778
+ findings.push({
29779
+ category: AuditCategory.TsdocCoverage,
29780
+ severity: AuditSeverity.Advisory,
29781
+ location: { file, line },
29782
+ subject: symbol,
29783
+ details: `Public export \`${symbol}\` has a return value with no \`@returns\` block tag.`,
29784
+ shortfall: "missing-returns",
29785
+ symbol
29786
+ });
29787
+ }
29788
+ return findings;
29789
+ }
29790
+ function createTsdocCoverageCheck(options) {
29791
+ const records = options.records;
29792
+ return {
29793
+ name: options.name ?? "tsdocCoverage",
29794
+ run(context) {
29795
+ const findings = [];
29796
+ for (const record of records) {
29797
+ for (const finding of tsdocRecordToFindings(record, context)) {
29798
+ findings.push(finding);
29799
+ }
29800
+ }
29801
+ return findings;
29802
+ }
29803
+ };
29804
+ }
29805
+ function isParamCarryingKind(record) {
29806
+ return record.kind === "Function";
29807
+ }
29808
+ function isReturnCarryingKind(record) {
29809
+ return record.kind === "Function";
29810
+ }
29811
+ function relativizeFile(file, repoRoot) {
29812
+ if (!file) {
29813
+ return "";
29814
+ }
29815
+ if (!path5.isAbsolute(file)) {
29816
+ return toPosix3(file);
29817
+ }
29818
+ const rel = path5.relative(repoRoot, file);
29819
+ return toPosix3(rel);
29820
+ }
29821
+ function toPosix3(p) {
29822
+ return p.split(path5.sep).join("/");
29823
+ }
29824
+
29416
29825
  // src/docs-sync/scan/run-scan.ts
29417
29826
  var fs3 = __toESM(require("fs"));
29418
- var path5 = __toESM(require("path"));
29827
+ var path6 = __toESM(require("path"));
29419
29828
  var DEFAULT_AUDIT_REPORT_DIR = ".claude/state/docs-sync";
29420
29829
  var SEVERITY_RANK = {
29421
29830
  blocking: 0,
@@ -29430,7 +29839,7 @@ var AUDIT_CATEGORY_ORDER = [
29430
29839
  AuditCategory.SampleFailures
29431
29840
  ];
29432
29841
  function runScan(options) {
29433
- const repoRoot = path5.resolve(options.repoRoot);
29842
+ const repoRoot = path6.resolve(options.repoRoot);
29434
29843
  const mode = options.mode;
29435
29844
  const scope = options.scope ?? "";
29436
29845
  const issueNumber = options.issueNumber;
@@ -29505,11 +29914,11 @@ function buildReport(args) {
29505
29914
  };
29506
29915
  }
29507
29916
  function persistAuditReport(args) {
29508
- const repoRoot = path5.resolve(args.repoRoot);
29917
+ const repoRoot = path6.resolve(args.repoRoot);
29509
29918
  const reportDir = args.reportDir ?? DEFAULT_AUDIT_REPORT_DIR;
29510
- const targetDir = path5.resolve(repoRoot, reportDir);
29919
+ const targetDir = path6.resolve(repoRoot, reportDir);
29511
29920
  fs3.mkdirSync(targetDir, { recursive: true });
29512
- const targetFile = path5.join(
29921
+ const targetFile = path6.join(
29513
29922
  targetDir,
29514
29923
  `${args.report.issueNumber}-audit.json`
29515
29924
  );
@@ -29572,7 +29981,7 @@ function compareFindings(a, b) {
29572
29981
  }
29573
29982
 
29574
29983
  // src/docs-sync/tsdoc-coverage/coverage.ts
29575
- var path6 = __toESM(require("path"));
29984
+ var path7 = __toESM(require("path"));
29576
29985
  var import_tsdoc = require("@microsoft/tsdoc");
29577
29986
  var ts2 = __toESM(require("typescript"));
29578
29987
  var TsDocCoverageKind = {
@@ -29590,8 +29999,8 @@ var DEFAULT_THIN_SUMMARY_WORD_THRESHOLD = 4;
29590
29999
  var DEFAULT_ENTRY_POINT = "src/index.ts";
29591
30000
  function analyzeTsDocCoverage(options) {
29592
30001
  const resolvedOptions = typeof options === "string" ? { packageRoot: options } : options;
29593
- const packageRoot = path6.resolve(resolvedOptions.packageRoot);
29594
- const entryPoint = path6.resolve(
30002
+ const packageRoot = path7.resolve(resolvedOptions.packageRoot);
30003
+ const entryPoint = path7.resolve(
29595
30004
  packageRoot,
29596
30005
  resolvedOptions.entryPoint ?? DEFAULT_ENTRY_POINT
29597
30006
  );
@@ -29654,7 +30063,7 @@ function analyzeTsDocCoverage(options) {
29654
30063
  }
29655
30064
  function resolveCompilerOptions(packageRoot, tsconfigPath) {
29656
30065
  if (tsconfigPath) {
29657
- const absoluteTsconfig = path6.resolve(packageRoot, tsconfigPath);
30066
+ const absoluteTsconfig = path7.resolve(packageRoot, tsconfigPath);
29658
30067
  const configFile = ts2.readConfigFile(absoluteTsconfig, ts2.sys.readFile);
29659
30068
  if (configFile.error) {
29660
30069
  throw new Error(
@@ -29664,7 +30073,7 @@ function resolveCompilerOptions(packageRoot, tsconfigPath) {
29664
30073
  const parsed = ts2.parseJsonConfigFileContent(
29665
30074
  configFile.config,
29666
30075
  ts2.sys,
29667
- path6.dirname(absoluteTsconfig)
30076
+ path7.dirname(absoluteTsconfig)
29668
30077
  );
29669
30078
  return { ...parsed.options, noEmit: true };
29670
30079
  }
@@ -29973,14 +30382,14 @@ var LAYOUT_ROOT_BY_PROJECT_TYPE = {
29973
30382
  };
29974
30383
  function validateMonorepoLayout(root) {
29975
30384
  const violations = [];
29976
- const rootOutdir = toPosix3(root.outdir);
30385
+ const rootOutdir = toPosix4(root.outdir);
29977
30386
  for (const sub of root.subprojects) {
29978
30387
  const className = sub.constructor.name;
29979
30388
  const expectedRoot = expectedRootFor(sub, className);
29980
30389
  if (expectedRoot === void 0) {
29981
30390
  continue;
29982
30391
  }
29983
- const relOutdir = relativeOutdir(rootOutdir, toPosix3(sub.outdir));
30392
+ const relOutdir = relativeOutdir(rootOutdir, toPosix4(sub.outdir));
29984
30393
  if (!outdirMatchesRoot(relOutdir, expectedRoot)) {
29985
30394
  violations.push({
29986
30395
  projectName: sub.name,
@@ -30039,7 +30448,7 @@ function outdirMatchesRoot(relOutdir, expectedRoot) {
30039
30448
  }
30040
30449
  return segments.length >= 2;
30041
30450
  }
30042
- function toPosix3(p) {
30451
+ function toPosix4(p) {
30043
30452
  return p.replace(/\\/g, "/");
30044
30453
  }
30045
30454
  function relativeOutdir(rootOutdir, subOutdir) {
@@ -30119,8 +30528,8 @@ var ResetTask = class _ResetTask extends import_projen14.Component {
30119
30528
  const resetTask = this.project.tasks.addTask(this.taskName, {
30120
30529
  description: "Delete build artifacts specified by pathsToRemove option, or artifactsDirectory if pathsToRemove is empty"
30121
30530
  });
30122
- this.pathsToRemove.forEach((path7) => {
30123
- resetTask.exec(`[ -e "${path7}" ] && rm -rf ${path7} || true`);
30531
+ this.pathsToRemove.forEach((path8) => {
30532
+ resetTask.exec(`[ -e "${path8}" ] && rm -rf ${path8} || true`);
30124
30533
  });
30125
30534
  const rootHasTurbo = TurboRepo.of(this.project.root) !== void 0;
30126
30535
  const isSubproject = this.project !== this.project.root;
@@ -32213,7 +32622,11 @@ var TypeScriptConfig = class extends import_projen23.Component {
32213
32622
  classifyRun,
32214
32623
  companyProfileBundle,
32215
32624
  compileFencedSamples,
32625
+ createApiDiffCheck,
32626
+ createReferenceMismatchCheck,
32627
+ createTsdocCoverageCheck,
32216
32628
  customerProfileBundle,
32629
+ diffApiRollups,
32217
32630
  docsSyncBundle,
32218
32631
  emptyCategoryBuckets,
32219
32632
  extractApiProcedure,
@@ -32228,11 +32641,13 @@ var TypeScriptConfig = class extends import_projen23.Component {
32228
32641
  maintenanceAuditBundle,
32229
32642
  meetingAnalysisBundle,
32230
32643
  orchestratorBundle,
32644
+ parseApiRollup,
32231
32645
  peopleProfileBundle,
32232
32646
  persistAuditReport,
32233
32647
  pnpmBundle,
32234
32648
  prReviewBundle,
32235
32649
  projenBundle,
32650
+ referenceRecordToFinding,
32236
32651
  regulatoryResearchBundle,
32237
32652
  renderAgentTierCaseStatement,
32238
32653
  renderAgentTierSection,
@@ -32295,6 +32710,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
32295
32710
  slackBundle,
32296
32711
  softwareProfileBundle,
32297
32712
  standardsResearchBundle,
32713
+ tsdocRecordToFindings,
32298
32714
  turborepoBundle,
32299
32715
  typescriptBundle,
32300
32716
  validateAgentTierConfig,