@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.mjs CHANGED
@@ -2867,20 +2867,26 @@ var createPackageSkill = {
2867
2867
  " places the sub-project at `packages/<scope>/<name>`.",
2868
2868
  "",
2869
2869
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
2870
- " the repo root:",
2870
+ " the repo root, the safe three-step regen sequence:",
2871
2871
  "",
2872
2872
  " ```",
2873
+ " pnpm i",
2873
2874
  " pnpm exec projen",
2874
- " pnpm install",
2875
+ " pnpm i",
2875
2876
  " ```",
2876
2877
  "",
2877
- " `pnpm exec projen` regenerates the sub-project tree;",
2878
- " `pnpm install` updates the workspace lockfile. Do **not** run",
2879
- " these commands yourself \u2014 the user runs them.",
2878
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
2879
+ " so synth runs against the right configulator/projen/plugin",
2880
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
2881
+ " stale resolved version of any pnpm-managed component and produce",
2882
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
2883
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
2884
+ " lockfile to pick up any dependency changes projen wrote during",
2885
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
2880
2886
  "",
2881
2887
  "## Guardrails",
2882
2888
  "",
2883
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
2889
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
2884
2890
  " `pnpm test`, or any other package-manager, build, or test command.",
2885
2891
  " Emit the projen block and instructions only.",
2886
2892
  "- Never scaffold outside `packages/`. Deployable apps belong under",
@@ -2990,20 +2996,26 @@ var createAppSkill = {
2990
2996
  " sub-project at `apps/<scope>/<name>`.",
2991
2997
  "",
2992
2998
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
2993
- " the repo root:",
2999
+ " the repo root, the safe three-step regen sequence:",
2994
3000
  "",
2995
3001
  " ```",
3002
+ " pnpm i",
2996
3003
  " pnpm exec projen",
2997
- " pnpm install",
3004
+ " pnpm i",
2998
3005
  " ```",
2999
3006
  "",
3000
- " `pnpm exec projen` regenerates the sub-project tree;",
3001
- " `pnpm install` updates the workspace lockfile. Do **not** run",
3002
- " these commands yourself \u2014 the user runs them.",
3007
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
3008
+ " so synth runs against the right configulator/projen/plugin",
3009
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
3010
+ " stale resolved version of any pnpm-managed component and produce",
3011
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
3012
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
3013
+ " lockfile to pick up any dependency changes projen wrote during",
3014
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
3003
3015
  "",
3004
3016
  "## Guardrails",
3005
3017
  "",
3006
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
3018
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
3007
3019
  " `pnpm test`, or any other package-manager, build, or test command.",
3008
3020
  " Emit the projen block and instructions only.",
3009
3021
  "- Never scaffold outside `apps/`. Shared libraries belong under",
@@ -3149,20 +3161,26 @@ var createSiteSkill = {
3149
3161
  " `sites/<scope>/<name>`.",
3150
3162
  "",
3151
3163
  "6. **Tell the user how to synthesize.** Instruct them to run, from",
3152
- " the repo root:",
3164
+ " the repo root, the safe three-step regen sequence:",
3153
3165
  "",
3154
3166
  " ```",
3167
+ " pnpm i",
3155
3168
  " pnpm exec projen",
3156
- " pnpm install",
3169
+ " pnpm i",
3157
3170
  " ```",
3158
3171
  "",
3159
- " `pnpm exec projen` regenerates the sub-project tree;",
3160
- " `pnpm install` updates the workspace lockfile. Do **not** run",
3161
- " these commands yourself \u2014 the user runs them.",
3172
+ " The leading `pnpm i` syncs `node_modules` with `pnpm-lock.yaml`",
3173
+ " so synth runs against the right configulator/projen/plugin",
3174
+ " versions \u2014 without it, `pnpm exec projen` may synth against a",
3175
+ " stale resolved version of any pnpm-managed component and produce",
3176
+ " phantom drift in generated files. `pnpm exec projen` regenerates",
3177
+ " the sub-project tree; the trailing `pnpm i` updates the workspace",
3178
+ " lockfile to pick up any dependency changes projen wrote during",
3179
+ " synth. Do **not** run these commands yourself \u2014 the user runs them.",
3162
3180
  "",
3163
3181
  "## Guardrails",
3164
3182
  "",
3165
- "- Never run `pnpm exec projen`, `pnpm install`, `pnpm build`,",
3183
+ "- Never run `pnpm exec projen`, `pnpm install`, `pnpm i`, `pnpm build`,",
3166
3184
  " `pnpm test`, or any other package-manager, build, or test command.",
3167
3185
  " Emit the projen block and instructions only.",
3168
3186
  "- Never scaffold the monorepo-wide docs site under `sites/`. The",
@@ -3266,7 +3284,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
3266
3284
  "## Important Notes",
3267
3285
  "",
3268
3286
  "- **Never edit generated files** \u2014 they are marked with `// ~~ Generated by projen`",
3269
- "- **After modifying Projen configuration**, run `pnpm exec projen` to regenerate files, then `pnpm install` to update the lockfile.",
3287
+ "- **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`.",
3270
3288
  "- **Configure dependencies through Projen** \u2014 never use `npm install`, `pnpm add`, or `yarn add`. Add them to `deps` or `devDeps` in Projen config.",
3271
3289
  "- **Export from index.ts** to maintain clean public APIs",
3272
3290
  "",
@@ -3325,7 +3343,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
3325
3343
  "",
3326
3344
  "After making changes that need validation, tell the user the specific commands to run:",
3327
3345
  "",
3328
- "1. **After projen config changes** \u2014 tell the user to run `pnpm exec projen && pnpm install`",
3346
+ "1. **After projen config changes** \u2014 tell the user to run the three-step regen sequence: `pnpm i && pnpm exec projen && pnpm i`",
3329
3347
  "2. **After source code changes** \u2014 tell the user to run `pnpm --filter @codedrifters/<package> test`",
3330
3348
  "3. **After multi-package changes** \u2014 tell the user to run `pnpm build:all`"
3331
3349
  ].join("\n"),
@@ -9101,7 +9119,7 @@ var githubWorkflowBundle = {
9101
9119
  "",
9102
9120
  "When the user says **open a PR** (or similar), follow these steps exactly:",
9103
9121
  "",
9104
- "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.",
9122
+ "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.",
9105
9123
  "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.",
9106
9124
  "3. **Check for uncommitted changes** \u2014 if any exist, commit them with a conventional commit message",
9107
9125
  "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",
@@ -13799,7 +13817,7 @@ var issueWorkerSubAgent = {
13799
13817
  "",
13800
13818
  "Run the appropriate verification commands depending on what changed:",
13801
13819
  "",
13802
- "1. If Projen config was changed: `pnpm exec projen && pnpm install`",
13820
+ "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)",
13803
13821
  "2. Compile the affected package: `pnpm --filter @codedrifters/<package> compile`",
13804
13822
  "3. Test the affected package: `pnpm --filter @codedrifters/<package> test`",
13805
13823
  "4. If changes span multiple packages: `pnpm build:all`",
@@ -15090,7 +15108,7 @@ var pnpmBundle = {
15090
15108
  "- Configure dependencies in Projen configuration files (`.projenrc.ts` or `projenrc/*.ts`)",
15091
15109
  "- Add dependencies to `deps`, `devDeps`, or `peerDeps` arrays in project configuration",
15092
15110
  '- Use catalog dependencies when available (e.g., `"aws-cdk-lib@catalog:"`)',
15093
- "- Ask the user to run `pnpm exec projen` and `pnpm install` after updating dependency configuration",
15111
+ "- 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.",
15094
15112
  "",
15095
15113
  "**DO NOT:**",
15096
15114
  "- Run `npm install some-package`",
@@ -16527,14 +16545,19 @@ var projenBundle = {
16527
16545
  "",
16528
16546
  "## Synthesizing Projen Configuration",
16529
16547
  "",
16530
- "After modifying any file in `projenrc/` or `.projenrc.ts`, regenerate project files:",
16548
+ "After modifying any file in `projenrc/` or `.projenrc.ts`, regenerate project files using the safe three-step sequence:",
16531
16549
  "",
16532
16550
  "```sh",
16551
+ "pnpm i",
16533
16552
  "pnpm exec projen",
16534
- "pnpm install",
16553
+ "pnpm i",
16535
16554
  "```",
16536
16555
  "",
16537
- "Both steps are required \u2014 `pnpm exec projen` regenerates files, `pnpm install` updates the lockfile to match.",
16556
+ "All three steps are required:",
16557
+ "",
16558
+ "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`.",
16559
+ "2. `pnpm exec projen` regenerates files against the now-synced templates.",
16560
+ "3. The trailing `pnpm i` picks up any dependency changes projen wrote into `package.json` during the synth pass and refreshes the lockfile.",
16538
16561
  "",
16539
16562
  "## Building",
16540
16563
  "",
@@ -16615,7 +16638,7 @@ var projenBundle = {
16615
16638
  "After finishing implementation work, validate that changes are correct by running the appropriate commands depending on what was changed:",
16616
16639
  "",
16617
16640
  "1. **Projen config changes** (`projenrc/`, `.projenrc.ts`):",
16618
- " - Run `pnpm exec projen` then `pnpm install`",
16641
+ " - Run the three-step regen sequence: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again",
16619
16642
  " - Verify no unexpected generated file changes with `git diff`",
16620
16643
  "",
16621
16644
  "2. **Source code changes** (in a sub-package):",
@@ -16632,8 +16655,8 @@ var projenBundle = {
16632
16655
  "",
16633
16656
  "| Task | Command |",
16634
16657
  "|------|---------|",
16635
- "| Synthesize projen | `pnpm exec projen` |",
16636
- "| Install deps | `pnpm install` |",
16658
+ "| Regenerate projen configuration | `pnpm i && pnpm exec projen && pnpm i` |",
16659
+ "| Install deps | `pnpm i` |",
16637
16660
  "| Full monorepo build | `pnpm build:all` |",
16638
16661
  "| Root build only | `pnpm build` |",
16639
16662
  "| Compile one package | `pnpm --filter @codedrifters/<pkg> compile` |",
@@ -16691,7 +16714,7 @@ var projenBundle = {
16691
16714
  "",
16692
16715
  "## After Any Change",
16693
16716
  "",
16694
- "Run `pnpm exec projen` then `pnpm install` to regenerate the output files."
16717
+ "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."
16695
16718
  ].join("\n"),
16696
16719
  tags: ["workflow"]
16697
16720
  },
@@ -16709,7 +16732,7 @@ var projenBundle = {
16709
16732
  "- Edit Projen configuration in:",
16710
16733
  " - `.projenrc.ts` (root)",
16711
16734
  " - `projenrc/*.ts` (package-specific)",
16712
- "- After making Projen changes, run `pnpm exec projen` to synthesize",
16735
+ "- 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.",
16713
16736
  "",
16714
16737
  "## Workspace Dependencies",
16715
16738
  "",
@@ -26554,8 +26577,8 @@ var FALLBACKS = {
26554
26577
  monorepoLayoutSeedBlock: ""
26555
26578
  };
26556
26579
  var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
26557
- function getNestedValue(obj, path7) {
26558
- const parts = path7.split(".");
26580
+ function getNestedValue(obj, path8) {
26581
+ const parts = path8.split(".");
26559
26582
  let current = obj;
26560
26583
  for (const part of parts) {
26561
26584
  if (current == null || typeof current !== "object") {
@@ -29164,9 +29187,388 @@ function auditReportJsonSchema() {
29164
29187
  };
29165
29188
  }
29166
29189
 
29190
+ // src/docs-sync/scan/checks/api-diff-check.ts
29191
+ function parseApiRollup(rollup) {
29192
+ const fenceContents = extractFirstTsFence(rollup);
29193
+ if (fenceContents === void 0) {
29194
+ return [];
29195
+ }
29196
+ const lines = fenceContents.split("\n");
29197
+ const entries = [];
29198
+ let i = 0;
29199
+ while (i < lines.length) {
29200
+ const line = lines[i];
29201
+ const trimmed = line.trim();
29202
+ if (!trimmed.startsWith("export ")) {
29203
+ i += 1;
29204
+ continue;
29205
+ }
29206
+ const startIndex = i;
29207
+ let buffer = trimmed;
29208
+ let braceDepth = countBraceDelta(trimmed);
29209
+ let endsWithSemicolon = trimmed.endsWith(";");
29210
+ i += 1;
29211
+ while (i < lines.length && !isComplete(buffer, braceDepth, endsWithSemicolon)) {
29212
+ const next = lines[i];
29213
+ const nextTrimmed = next.trim();
29214
+ if (nextTrimmed.length === 0 || nextTrimmed.startsWith("//")) {
29215
+ i += 1;
29216
+ continue;
29217
+ }
29218
+ buffer = `${buffer} ${nextTrimmed}`;
29219
+ braceDepth += countBraceDelta(nextTrimmed);
29220
+ endsWithSemicolon = nextTrimmed.endsWith(";");
29221
+ i += 1;
29222
+ }
29223
+ const normalized = normalizeWhitespace(buffer);
29224
+ const name = extractDeclarationName(normalized);
29225
+ if (name) {
29226
+ entries.push({ name, signature: normalized });
29227
+ }
29228
+ if (name && normalized.startsWith("export {")) {
29229
+ const names = extractExportListNames(normalized);
29230
+ if (names.length > 1) {
29231
+ entries.pop();
29232
+ for (const exportName of names) {
29233
+ entries.push({ name: exportName, signature: normalized });
29234
+ }
29235
+ }
29236
+ }
29237
+ if (i === startIndex) {
29238
+ i += 1;
29239
+ }
29240
+ }
29241
+ const dedup = /* @__PURE__ */ new Map();
29242
+ for (const entry of entries) {
29243
+ if (!dedup.has(entry.name)) {
29244
+ dedup.set(entry.name, entry);
29245
+ }
29246
+ }
29247
+ return Array.from(dedup.values()).sort(
29248
+ (a, b) => a.name.localeCompare(b.name)
29249
+ );
29250
+ }
29251
+ function diffApiRollups(baselineRollup, currentRollup) {
29252
+ const baseline = /* @__PURE__ */ new Map();
29253
+ for (const entry of parseApiRollup(baselineRollup)) {
29254
+ baseline.set(entry.name, entry);
29255
+ }
29256
+ const current = /* @__PURE__ */ new Map();
29257
+ for (const entry of parseApiRollup(currentRollup)) {
29258
+ current.set(entry.name, entry);
29259
+ }
29260
+ const added = [];
29261
+ const removed = [];
29262
+ const changed = [];
29263
+ for (const [name, entry] of current.entries()) {
29264
+ const before = baseline.get(name);
29265
+ if (!before) {
29266
+ added.push(entry);
29267
+ continue;
29268
+ }
29269
+ if (before.signature !== entry.signature) {
29270
+ changed.push(entry);
29271
+ }
29272
+ }
29273
+ for (const [name, entry] of baseline.entries()) {
29274
+ if (!current.has(name)) {
29275
+ removed.push(entry);
29276
+ }
29277
+ }
29278
+ added.sort((a, b) => a.name.localeCompare(b.name));
29279
+ removed.sort((a, b) => a.name.localeCompare(b.name));
29280
+ changed.sort((a, b) => a.name.localeCompare(b.name));
29281
+ return { added, removed, changed };
29282
+ }
29283
+ function createApiDiffCheck(options) {
29284
+ const baseline = options.baselineRollup;
29285
+ const current = options.currentRollup;
29286
+ const rollupPath = options.rollupPath ?? "";
29287
+ return {
29288
+ name: options.name ?? "apiDiff",
29289
+ run(_context) {
29290
+ const diff = diffApiRollups(baseline, current);
29291
+ const findings = [];
29292
+ for (const entry of diff.added) {
29293
+ findings.push({
29294
+ category: AuditCategory.ApiDiff,
29295
+ severity: AuditSeverity.Mechanical,
29296
+ location: { file: rollupPath, line: 0 },
29297
+ subject: entry.name,
29298
+ details: `New public export \`${entry.name}\` not present in baseline rollup.`,
29299
+ change: "added",
29300
+ symbol: entry.name,
29301
+ fixHint: "stub-tsdoc"
29302
+ });
29303
+ }
29304
+ for (const entry of diff.removed) {
29305
+ findings.push({
29306
+ category: AuditCategory.ApiDiff,
29307
+ severity: AuditSeverity.Advisory,
29308
+ location: { file: rollupPath, line: 0 },
29309
+ subject: entry.name,
29310
+ details: `Public export \`${entry.name}\` was present in baseline rollup but is gone in the current rollup.`,
29311
+ change: "removed",
29312
+ symbol: entry.name
29313
+ });
29314
+ }
29315
+ for (const entry of diff.changed) {
29316
+ findings.push({
29317
+ category: AuditCategory.ApiDiff,
29318
+ severity: AuditSeverity.Advisory,
29319
+ location: { file: rollupPath, line: 0 },
29320
+ subject: entry.name,
29321
+ details: `Public export \`${entry.name}\` signature changed since baseline rollup.`,
29322
+ change: "changed",
29323
+ symbol: entry.name
29324
+ });
29325
+ }
29326
+ return findings;
29327
+ }
29328
+ };
29329
+ }
29330
+ function extractFirstTsFence(rollup) {
29331
+ const lines = rollup.split("\n");
29332
+ let start = -1;
29333
+ for (let i = 0; i < lines.length; i++) {
29334
+ const line = lines[i].trim();
29335
+ if (line === "```ts" || line === "```typescript") {
29336
+ start = i + 1;
29337
+ break;
29338
+ }
29339
+ }
29340
+ if (start < 0) {
29341
+ return void 0;
29342
+ }
29343
+ for (let j = start; j < lines.length; j++) {
29344
+ if (lines[j].trim() === "```") {
29345
+ return lines.slice(start, j).join("\n");
29346
+ }
29347
+ }
29348
+ return lines.slice(start).join("\n");
29349
+ }
29350
+ function countBraceDelta(line) {
29351
+ let delta = 0;
29352
+ for (const ch of line) {
29353
+ if (ch === "{") {
29354
+ delta += 1;
29355
+ } else if (ch === "}") {
29356
+ delta -= 1;
29357
+ }
29358
+ }
29359
+ return delta;
29360
+ }
29361
+ function isComplete(buffer, braceDepth, endsWithSemicolon) {
29362
+ if (braceDepth > 0) {
29363
+ return false;
29364
+ }
29365
+ if (endsWithSemicolon) {
29366
+ return true;
29367
+ }
29368
+ return buffer.trimEnd().endsWith("}");
29369
+ }
29370
+ function normalizeWhitespace(s) {
29371
+ 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();
29372
+ }
29373
+ var DECLARATION_KEYWORDS = [
29374
+ "function",
29375
+ "class",
29376
+ "interface",
29377
+ "type",
29378
+ "enum",
29379
+ "const",
29380
+ "let",
29381
+ "var",
29382
+ "namespace",
29383
+ "abstract",
29384
+ "default",
29385
+ "async"
29386
+ ];
29387
+ function extractDeclarationName(declaration) {
29388
+ const tokens = declaration.split(/\s+/);
29389
+ if (tokens.length === 0 || tokens[0] !== "export") {
29390
+ return void 0;
29391
+ }
29392
+ let cursor = 1;
29393
+ while (cursor < tokens.length && DECLARATION_KEYWORDS.includes(tokens[cursor])) {
29394
+ cursor += 1;
29395
+ }
29396
+ if (cursor >= tokens.length) {
29397
+ return void 0;
29398
+ }
29399
+ const candidate = tokens[cursor];
29400
+ if (candidate.startsWith("{")) {
29401
+ const stripped = candidate.replace(/^\{/, "").replace(/[},].*$/, "");
29402
+ return stripped || void 0;
29403
+ }
29404
+ const cleaned = candidate.replace(/[<(:;].*$/, "");
29405
+ return cleaned || void 0;
29406
+ }
29407
+ function extractExportListNames(declaration) {
29408
+ const open = declaration.indexOf("{");
29409
+ const close = declaration.indexOf("}", open);
29410
+ if (open < 0 || close < 0) {
29411
+ return [];
29412
+ }
29413
+ const inside = declaration.slice(open + 1, close);
29414
+ const names = [];
29415
+ for (const part of inside.split(",")) {
29416
+ const token = part.trim();
29417
+ if (!token) {
29418
+ continue;
29419
+ }
29420
+ const asMatch = token.match(/\sas\s+(\S+)$/);
29421
+ if (asMatch) {
29422
+ names.push(asMatch[1]);
29423
+ } else {
29424
+ names.push(token);
29425
+ }
29426
+ }
29427
+ return names;
29428
+ }
29429
+
29430
+ // src/docs-sync/scan/checks/reference-mismatch-check.ts
29431
+ function referenceRecordToFinding(record, signatureChangedSymbols) {
29432
+ if (signatureChangedSymbols.has(record.symbol)) {
29433
+ return {
29434
+ category: AuditCategory.ReferenceMismatches,
29435
+ severity: AuditSeverity.Advisory,
29436
+ location: { file: record.docPath, line: record.line },
29437
+ subject: record.symbol,
29438
+ details: `Inline reference \`${record.symbol}\` resolves to a symbol whose signature has changed; review the surrounding prose.`,
29439
+ mismatch: "signature-changed",
29440
+ symbol: record.symbol
29441
+ };
29442
+ }
29443
+ if (!record.isKnown) {
29444
+ return {
29445
+ category: AuditCategory.ReferenceMismatches,
29446
+ severity: AuditSeverity.Advisory,
29447
+ location: { file: record.docPath, line: record.line },
29448
+ subject: record.symbol,
29449
+ details: `Inline reference \`${record.symbol}\` does not match any known public export.`,
29450
+ mismatch: "unknown-symbol",
29451
+ symbol: record.symbol
29452
+ };
29453
+ }
29454
+ return void 0;
29455
+ }
29456
+ function createReferenceMismatchCheck(options) {
29457
+ const records = options.records;
29458
+ const signatureChangedSymbols = new Set(
29459
+ options.signatureChangedSymbols ?? []
29460
+ );
29461
+ return {
29462
+ name: options.name ?? "referenceMismatches",
29463
+ run(_context) {
29464
+ const findings = [];
29465
+ for (const record of records) {
29466
+ const finding = referenceRecordToFinding(
29467
+ record,
29468
+ signatureChangedSymbols
29469
+ );
29470
+ if (finding !== void 0) {
29471
+ findings.push(finding);
29472
+ }
29473
+ }
29474
+ return findings;
29475
+ }
29476
+ };
29477
+ }
29478
+
29479
+ // src/docs-sync/scan/checks/tsdoc-coverage-check.ts
29480
+ import * as path5 from "path";
29481
+ function tsdocRecordToFindings(record, context) {
29482
+ const findings = [];
29483
+ const file = relativizeFile(record.location.file, context.repoRoot);
29484
+ const line = record.location.line;
29485
+ const symbol = record.symbol;
29486
+ if (!record.hasSummary) {
29487
+ findings.push({
29488
+ category: AuditCategory.TsdocCoverage,
29489
+ severity: AuditSeverity.Mechanical,
29490
+ location: { file, line },
29491
+ subject: symbol,
29492
+ details: `Public export \`${symbol}\` is missing a TSDoc summary.`,
29493
+ shortfall: "missing-summary",
29494
+ symbol,
29495
+ fixHint: "stub-tsdoc"
29496
+ });
29497
+ return findings;
29498
+ }
29499
+ if (record.hasThinSummary) {
29500
+ findings.push({
29501
+ category: AuditCategory.TsdocCoverage,
29502
+ severity: AuditSeverity.Advisory,
29503
+ location: { file, line },
29504
+ subject: symbol,
29505
+ details: `Public export \`${symbol}\` carries a thin TSDoc summary; consider expanding it.`,
29506
+ shortfall: "thin-summary",
29507
+ symbol
29508
+ });
29509
+ }
29510
+ if (!record.hasParams && isParamCarryingKind(record)) {
29511
+ findings.push({
29512
+ category: AuditCategory.TsdocCoverage,
29513
+ severity: AuditSeverity.Advisory,
29514
+ location: { file, line },
29515
+ subject: symbol,
29516
+ details: `Public export \`${symbol}\` has parameters with no \`@param\` block tag.`,
29517
+ shortfall: "missing-params",
29518
+ symbol
29519
+ });
29520
+ }
29521
+ if (!record.hasReturns && isReturnCarryingKind(record)) {
29522
+ findings.push({
29523
+ category: AuditCategory.TsdocCoverage,
29524
+ severity: AuditSeverity.Advisory,
29525
+ location: { file, line },
29526
+ subject: symbol,
29527
+ details: `Public export \`${symbol}\` has a return value with no \`@returns\` block tag.`,
29528
+ shortfall: "missing-returns",
29529
+ symbol
29530
+ });
29531
+ }
29532
+ return findings;
29533
+ }
29534
+ function createTsdocCoverageCheck(options) {
29535
+ const records = options.records;
29536
+ return {
29537
+ name: options.name ?? "tsdocCoverage",
29538
+ run(context) {
29539
+ const findings = [];
29540
+ for (const record of records) {
29541
+ for (const finding of tsdocRecordToFindings(record, context)) {
29542
+ findings.push(finding);
29543
+ }
29544
+ }
29545
+ return findings;
29546
+ }
29547
+ };
29548
+ }
29549
+ function isParamCarryingKind(record) {
29550
+ return record.kind === "Function";
29551
+ }
29552
+ function isReturnCarryingKind(record) {
29553
+ return record.kind === "Function";
29554
+ }
29555
+ function relativizeFile(file, repoRoot) {
29556
+ if (!file) {
29557
+ return "";
29558
+ }
29559
+ if (!path5.isAbsolute(file)) {
29560
+ return toPosix3(file);
29561
+ }
29562
+ const rel = path5.relative(repoRoot, file);
29563
+ return toPosix3(rel);
29564
+ }
29565
+ function toPosix3(p) {
29566
+ return p.split(path5.sep).join("/");
29567
+ }
29568
+
29167
29569
  // src/docs-sync/scan/run-scan.ts
29168
29570
  import * as fs3 from "fs";
29169
- import * as path5 from "path";
29571
+ import * as path6 from "path";
29170
29572
  var DEFAULT_AUDIT_REPORT_DIR = ".claude/state/docs-sync";
29171
29573
  var SEVERITY_RANK = {
29172
29574
  blocking: 0,
@@ -29181,7 +29583,7 @@ var AUDIT_CATEGORY_ORDER = [
29181
29583
  AuditCategory.SampleFailures
29182
29584
  ];
29183
29585
  function runScan(options) {
29184
- const repoRoot = path5.resolve(options.repoRoot);
29586
+ const repoRoot = path6.resolve(options.repoRoot);
29185
29587
  const mode = options.mode;
29186
29588
  const scope = options.scope ?? "";
29187
29589
  const issueNumber = options.issueNumber;
@@ -29256,11 +29658,11 @@ function buildReport(args) {
29256
29658
  };
29257
29659
  }
29258
29660
  function persistAuditReport(args) {
29259
- const repoRoot = path5.resolve(args.repoRoot);
29661
+ const repoRoot = path6.resolve(args.repoRoot);
29260
29662
  const reportDir = args.reportDir ?? DEFAULT_AUDIT_REPORT_DIR;
29261
- const targetDir = path5.resolve(repoRoot, reportDir);
29663
+ const targetDir = path6.resolve(repoRoot, reportDir);
29262
29664
  fs3.mkdirSync(targetDir, { recursive: true });
29263
- const targetFile = path5.join(
29665
+ const targetFile = path6.join(
29264
29666
  targetDir,
29265
29667
  `${args.report.issueNumber}-audit.json`
29266
29668
  );
@@ -29323,7 +29725,7 @@ function compareFindings(a, b) {
29323
29725
  }
29324
29726
 
29325
29727
  // src/docs-sync/tsdoc-coverage/coverage.ts
29326
- import * as path6 from "path";
29728
+ import * as path7 from "path";
29327
29729
  import { TSDocParser } from "@microsoft/tsdoc";
29328
29730
  import * as ts2 from "typescript";
29329
29731
  var TsDocCoverageKind = {
@@ -29341,8 +29743,8 @@ var DEFAULT_THIN_SUMMARY_WORD_THRESHOLD = 4;
29341
29743
  var DEFAULT_ENTRY_POINT = "src/index.ts";
29342
29744
  function analyzeTsDocCoverage(options) {
29343
29745
  const resolvedOptions = typeof options === "string" ? { packageRoot: options } : options;
29344
- const packageRoot = path6.resolve(resolvedOptions.packageRoot);
29345
- const entryPoint = path6.resolve(
29746
+ const packageRoot = path7.resolve(resolvedOptions.packageRoot);
29747
+ const entryPoint = path7.resolve(
29346
29748
  packageRoot,
29347
29749
  resolvedOptions.entryPoint ?? DEFAULT_ENTRY_POINT
29348
29750
  );
@@ -29405,7 +29807,7 @@ function analyzeTsDocCoverage(options) {
29405
29807
  }
29406
29808
  function resolveCompilerOptions(packageRoot, tsconfigPath) {
29407
29809
  if (tsconfigPath) {
29408
- const absoluteTsconfig = path6.resolve(packageRoot, tsconfigPath);
29810
+ const absoluteTsconfig = path7.resolve(packageRoot, tsconfigPath);
29409
29811
  const configFile = ts2.readConfigFile(absoluteTsconfig, ts2.sys.readFile);
29410
29812
  if (configFile.error) {
29411
29813
  throw new Error(
@@ -29415,7 +29817,7 @@ function resolveCompilerOptions(packageRoot, tsconfigPath) {
29415
29817
  const parsed = ts2.parseJsonConfigFileContent(
29416
29818
  configFile.config,
29417
29819
  ts2.sys,
29418
- path6.dirname(absoluteTsconfig)
29820
+ path7.dirname(absoluteTsconfig)
29419
29821
  );
29420
29822
  return { ...parsed.options, noEmit: true };
29421
29823
  }
@@ -29724,14 +30126,14 @@ var LAYOUT_ROOT_BY_PROJECT_TYPE = {
29724
30126
  };
29725
30127
  function validateMonorepoLayout(root) {
29726
30128
  const violations = [];
29727
- const rootOutdir = toPosix3(root.outdir);
30129
+ const rootOutdir = toPosix4(root.outdir);
29728
30130
  for (const sub of root.subprojects) {
29729
30131
  const className = sub.constructor.name;
29730
30132
  const expectedRoot = expectedRootFor(sub, className);
29731
30133
  if (expectedRoot === void 0) {
29732
30134
  continue;
29733
30135
  }
29734
- const relOutdir = relativeOutdir(rootOutdir, toPosix3(sub.outdir));
30136
+ const relOutdir = relativeOutdir(rootOutdir, toPosix4(sub.outdir));
29735
30137
  if (!outdirMatchesRoot(relOutdir, expectedRoot)) {
29736
30138
  violations.push({
29737
30139
  projectName: sub.name,
@@ -29790,7 +30192,7 @@ function outdirMatchesRoot(relOutdir, expectedRoot) {
29790
30192
  }
29791
30193
  return segments.length >= 2;
29792
30194
  }
29793
- function toPosix3(p) {
30195
+ function toPosix4(p) {
29794
30196
  return p.replace(/\\/g, "/");
29795
30197
  }
29796
30198
  function relativeOutdir(rootOutdir, subOutdir) {
@@ -29879,8 +30281,8 @@ var ResetTask = class _ResetTask extends Component14 {
29879
30281
  const resetTask = this.project.tasks.addTask(this.taskName, {
29880
30282
  description: "Delete build artifacts specified by pathsToRemove option, or artifactsDirectory if pathsToRemove is empty"
29881
30283
  });
29882
- this.pathsToRemove.forEach((path7) => {
29883
- resetTask.exec(`[ -e "${path7}" ] && rm -rf ${path7} || true`);
30284
+ this.pathsToRemove.forEach((path8) => {
30285
+ resetTask.exec(`[ -e "${path8}" ] && rm -rf ${path8} || true`);
29884
30286
  });
29885
30287
  const rootHasTurbo = TurboRepo.of(this.project.root) !== void 0;
29886
30288
  const isSubproject = this.project !== this.project.root;
@@ -31801,7 +32203,7 @@ export const collections = {
31801
32203
  `;
31802
32204
 
31803
32205
  // src/typescript/typescript-config.ts
31804
- import { relative as relative6 } from "path";
32206
+ import { relative as relative7 } from "path";
31805
32207
  import { Component as Component19 } from "projen";
31806
32208
  import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
31807
32209
  var TypeScriptConfig = class extends Component19 {
@@ -31820,7 +32222,7 @@ var TypeScriptConfig = class extends Component19 {
31820
32222
  ...tsPaths,
31821
32223
  [dep.name]: [
31822
32224
  ensureRelativePathStartsWithDot(
31823
- relative6(project.outdir, subproject.outdir)
32225
+ relative7(project.outdir, subproject.outdir)
31824
32226
  )
31825
32227
  ]
31826
32228
  };
@@ -31976,7 +32378,11 @@ export {
31976
32378
  classifyRun,
31977
32379
  companyProfileBundle,
31978
32380
  compileFencedSamples,
32381
+ createApiDiffCheck,
32382
+ createReferenceMismatchCheck,
32383
+ createTsdocCoverageCheck,
31979
32384
  customerProfileBundle,
32385
+ diffApiRollups,
31980
32386
  docsSyncBundle,
31981
32387
  emptyCategoryBuckets,
31982
32388
  extractApiProcedure,
@@ -31991,11 +32397,13 @@ export {
31991
32397
  maintenanceAuditBundle,
31992
32398
  meetingAnalysisBundle,
31993
32399
  orchestratorBundle,
32400
+ parseApiRollup,
31994
32401
  peopleProfileBundle,
31995
32402
  persistAuditReport,
31996
32403
  pnpmBundle,
31997
32404
  prReviewBundle,
31998
32405
  projenBundle,
32406
+ referenceRecordToFinding,
31999
32407
  regulatoryResearchBundle,
32000
32408
  renderAgentTierCaseStatement,
32001
32409
  renderAgentTierSection,
@@ -32058,6 +32466,7 @@ export {
32058
32466
  slackBundle,
32059
32467
  softwareProfileBundle,
32060
32468
  standardsResearchBundle,
32469
+ tsdocRecordToFindings,
32061
32470
  turborepoBundle,
32062
32471
  typescriptBundle,
32063
32472
  validateAgentTierConfig,