@wairon/cli 5.1.1-dev.49 → 5.1.1-dev.50

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/dist/index.js CHANGED
@@ -600,6 +600,17 @@ var init_project = __esm({
600
600
  * carried as debt.
601
601
  */
602
602
  exclude: import_zod3.z.array(import_zod3.z.string()).optional(),
603
+ /**
604
+ * Project-relative directories holding this project's TESTS. Declaring none
605
+ * means no test scan and no `testsToRevisit` on any write — opt-in on the
606
+ * same terms as `sourceRoots`, so a project that never asked for it never
607
+ * pays for it.
608
+ *
609
+ * Separate from `sourceRoots` deliberately: tests are not code the specs are
610
+ * expected to claim, and putting them there would make every test file an
611
+ * UNCLAIMED_SOURCE_FILE.
612
+ */
613
+ testRoots: import_zod3.z.array(import_zod3.z.string()).optional(),
603
614
  /**
604
615
  * The files under the source roots that no spec names yet, frozen. A listed
605
616
  * file is not reported; an unlisted one is UNCLAIMED_SOURCE_FILE; an entry
@@ -1222,7 +1233,7 @@ var init_defaults = __esm({
1222
1233
  copilot: ".github/prompts",
1223
1234
  codex: ".codex/agents"
1224
1235
  };
1225
- WAIRON_VERSION = "5.1.1-dev.49";
1236
+ WAIRON_VERSION = "5.1.1-dev.50";
1226
1237
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1227
1238
  ARCHITECT_AGENT_ID = "agent-architect";
1228
1239
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -15767,6 +15778,17 @@ function stripComments(text, patterns) {
15767
15778
  function escapeRe(s) {
15768
15779
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15769
15780
  }
15781
+ function boundNames(captured, localOnly) {
15782
+ const names = [];
15783
+ for (const piece of captured.split(",")) {
15784
+ const sides = piece.split(" as ");
15785
+ for (const side of localOnly ? sides.slice(-1) : sides) {
15786
+ const name = side.trim();
15787
+ if (/^[A-Za-z_$][\w$]*$/.test(name)) names.push(name);
15788
+ }
15789
+ }
15790
+ return names;
15791
+ }
15770
15792
  function collectStrings(text) {
15771
15793
  const anchors = /* @__PURE__ */ new Set();
15772
15794
  for (const m of text.matchAll(STRING_RE)) {
@@ -15784,10 +15806,7 @@ function analyzeWithPatterns(text, patterns) {
15784
15806
  for (const m of stripped.matchAll(re)) {
15785
15807
  const captured = m[1];
15786
15808
  if (!captured) continue;
15787
- for (const piece of captured.split(",")) {
15788
- const name = (piece.includes(" as ") ? piece.split(" as ")[1] : piece).trim();
15789
- if (name && /^[A-Za-z_$][\w$]*$/.test(name)) declared.add(name);
15790
- }
15809
+ for (const name of boundNames(captured, true)) declared.add(name);
15791
15810
  }
15792
15811
  }
15793
15812
  for (const re of patterns.imports) {
@@ -15802,6 +15821,7 @@ function analyzeWithPatterns(text, patterns) {
15802
15821
  for (const re of patterns.declarations) {
15803
15822
  re.lastIndex = 0;
15804
15823
  const m = re.exec(line);
15824
+ re.lastIndex = 0;
15805
15825
  if (m?.[1]) exported.add(m[1]);
15806
15826
  }
15807
15827
  }
@@ -16119,7 +16139,7 @@ function looksBinary(buffer) {
16119
16139
  const probe = buffer.subarray(0, Math.min(buffer.length, 4096));
16120
16140
  return probe.includes(0);
16121
16141
  }
16122
- function walkSourceRoots(sourceRoots, exclude, projectRoot) {
16142
+ function walkDeclaredRoots(roots, exclude, projectRoot) {
16123
16143
  const found = [];
16124
16144
  const seen = /* @__PURE__ */ new Set();
16125
16145
  const excluded = exclude.map(pathKey);
@@ -16149,7 +16169,7 @@ function walkSourceRoots(sourceRoots, exclude, projectRoot) {
16149
16169
  }
16150
16170
  }
16151
16171
  };
16152
- for (const root of sourceRoots) {
16172
+ for (const root of roots) {
16153
16173
  const key = pathKey(root);
16154
16174
  if (path11.isAbsolute(key) || path11.normalize(key).split(path11.sep)[0] === "..") continue;
16155
16175
  const absolute = path11.resolve(projectRoot, key);
@@ -16170,7 +16190,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
16170
16190
  const files = [];
16171
16191
  const seen = /* @__PURE__ */ new Set();
16172
16192
  const exactCache = /* @__PURE__ */ new Map();
16173
- const rootFiles = walkSourceRoots(sourceRoots, exclude, projectRoot);
16193
+ const rootFiles = walkDeclaredRoots(sourceRoots, exclude, projectRoot);
16174
16194
  const declaredPaths = [];
16175
16195
  for (const impl of implementations) {
16176
16196
  declaredPaths.push(...implementationSourceFiles(impl));
@@ -16248,7 +16268,49 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
16248
16268
  }
16249
16269
  return { files, projectRoot, rootFiles };
16250
16270
  }
16251
- var fs8, path11, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16271
+ function readTestFiles(testRoots, projectRoot) {
16272
+ const scanned = [];
16273
+ for (const key of walkDeclaredRoots(testRoots, [], projectRoot)) {
16274
+ let text;
16275
+ try {
16276
+ const buffer = fs8.readFileSync(path11.resolve(projectRoot, key));
16277
+ if (looksBinary(buffer)) continue;
16278
+ text = buffer.toString("utf8");
16279
+ } catch {
16280
+ continue;
16281
+ }
16282
+ const imports = /* @__PURE__ */ new Set();
16283
+ for (const m of text.matchAll(NAMED_IMPORT_BINDINGS_RE)) {
16284
+ for (const name of boundNames(m[1] ?? "", false)) imports.add(name);
16285
+ }
16286
+ const words2 = /* @__PURE__ */ new Set();
16287
+ for (const m of text.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
16288
+ scanned.push({ path: key, imports, words: words2 });
16289
+ }
16290
+ return scanned;
16291
+ }
16292
+ function findTestsReferencing(methods, projectRoot, testRoots = []) {
16293
+ const scanned = readTestFiles(testRoots, projectRoot);
16294
+ if (scanned.length === 0) return [];
16295
+ const indiscriminateAbove = scanned.length / 10;
16296
+ const found = [];
16297
+ for (const method of methods) {
16298
+ const symbol = method.symbol ?? method.name;
16299
+ const imported = scanned.filter((f) => f.imports.has(symbol)).map((f) => f.path);
16300
+ const mentioned = scanned.filter((f) => !f.imports.has(symbol) && f.words.has(symbol)).map((f) => f.path);
16301
+ const indiscriminate = mentioned.length > indiscriminateAbove;
16302
+ if (imported.length === 0 && mentioned.length === 0) continue;
16303
+ found.push({
16304
+ method: method.name,
16305
+ symbol,
16306
+ imported,
16307
+ mentioned: indiscriminate ? [] : mentioned,
16308
+ indiscriminate
16309
+ });
16310
+ }
16311
+ return found;
16312
+ }
16313
+ var fs8, path11, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16252
16314
  var init_source_analysis = __esm({
16253
16315
  "src/core/source-analysis.ts"() {
16254
16316
  "use strict";
@@ -16281,6 +16343,7 @@ var init_source_analysis = __esm({
16281
16343
  ".swift": "swift"
16282
16344
  };
16283
16345
  C_FAMILY_COMMENTS = { lineComments: ["//"], blockComments: [["/*", "*/"]] };
16346
+ NAMED_IMPORT_BINDINGS_RE = /\bimport\s*\{([^}]*)\}/g;
16284
16347
  JS_PATTERNS = {
16285
16348
  ...C_FAMILY_COMMENTS,
16286
16349
  declarations: [
@@ -16290,7 +16353,7 @@ var init_source_analysis = __esm({
16290
16353
  /([A-Za-z_$][\w$]*)\s*[:=]\s*(?:async\s+)?(?:function\b|\()/g,
16291
16354
  /(?:^|\s)(?:public|private|protected|static|async|get|set)\s+([A-Za-z_$][\w$]*)\s*\(/g,
16292
16355
  // named import bindings realize forwarding adapters
16293
- /\bimport\s*\{([^}]*)\}/g
16356
+ NAMED_IMPORT_BINDINGS_RE
16294
16357
  ],
16295
16358
  imports: [
16296
16359
  /\b(?:import|export)\b[^'"\n]*['"]([^'"]+)['"]/g,
@@ -18018,6 +18081,9 @@ function projectPackSelections() {
18018
18081
  return [];
18019
18082
  }
18020
18083
  }
18084
+ function findTestsReferencing2(methods, projectRoot, testRoots = []) {
18085
+ return findTestsReferencing(methods, projectRoot, testRoots);
18086
+ }
18021
18087
  function issueKey(issue2) {
18022
18088
  return `${issue2.code}|${issue2.specId ?? ""}`;
18023
18089
  }
@@ -19707,6 +19773,7 @@ function moveChangeReports(edits, dryRun) {
19707
19773
  changes,
19708
19774
  ineffective: [],
19709
19775
  notices: [],
19776
+ testsToRevisit: [],
19710
19777
  summary: `${dryRun ? "Would move" : "Moved"} into ${edit.kind} "${edit.id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
19711
19778
  }));
19712
19779
  }
@@ -21771,6 +21838,7 @@ var init_specs2 = __esm({
21771
21838
  changes,
21772
21839
  ineffective,
21773
21840
  notices,
21841
+ testsToRevisit: [],
21774
21842
  summary: `No change to ${kind} "${id}" \u2014 the delta matches what is stored, so nothing ${dryRun ? "would be" : "was"} written.`
21775
21843
  };
21776
21844
  }
@@ -21785,6 +21853,7 @@ var init_specs2 = __esm({
21785
21853
  changes,
21786
21854
  ineffective,
21787
21855
  notices,
21856
+ testsToRevisit: [],
21788
21857
  summary: `Dry run on ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"} would be made. Nothing was written.`
21789
21858
  };
21790
21859
  }
@@ -21820,6 +21889,10 @@ var init_specs2 = __esm({
21820
21889
  changes,
21821
21890
  ineffective,
21822
21891
  notices,
21892
+ // The store never searches for tests: it holds the tree and knows
21893
+ // nothing about the rule engine that reads code. The GATED write above
21894
+ // it fills this in, which is also the only write path a human drives.
21895
+ testsToRevisit: [],
21823
21896
  summary: `Updated ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
21824
21897
  };
21825
21898
  }
@@ -27833,6 +27906,7 @@ __export(src_exports, {
27833
27906
  findLegacySpecFiles: () => findLegacySpecFiles,
27834
27907
  findProjectRoot: () => findProjectRoot,
27835
27908
  findSystemRoot: () => findSystemRoot,
27909
+ findTestsReferencing: () => findTestsReferencing2,
27836
27910
  fromOpenApi: () => fromOpenApi,
27837
27911
  fromProjectRoot: () => fromProjectRoot,
27838
27912
  generateAgent: () => generateAgent,
@@ -28577,6 +28651,7 @@ function loadProjectConfig4() {
28577
28651
  findLegacySpecFiles,
28578
28652
  findProjectRoot,
28579
28653
  findSystemRoot,
28654
+ findTestsReferencing,
28580
28655
  fromOpenApi,
28581
28656
  fromProjectRoot,
28582
28657
  generateAgent,