ultra11y 4.2.0 → 4.3.0

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.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { realpathSync as realpathSync6, writeFileSync as writeFileSync19, mkdirSync as mkdirSync17, existsSync as existsSync32, readFileSync as readFileSync29, appendFileSync, copyFileSync as copyFileSync4 } from "fs";
5
- import { join as join47, relative as relative5, sep as sep7, dirname as dirname14 } from "path";
4
+ import { realpathSync as realpathSync6, writeFileSync as writeFileSync19, mkdirSync as mkdirSync17, existsSync as existsSync34, readFileSync as readFileSync32, appendFileSync, copyFileSync as copyFileSync4 } from "fs";
5
+ import { join as join50, relative as relative5, sep as sep7, dirname as dirname14 } from "path";
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "4.2.0";
9
+ var VERSION = "4.3.0";
10
10
  var SCHEMA_VERSION = 2;
11
11
 
12
12
  // src/audit.ts
@@ -1197,7 +1197,7 @@ var wcag_universe_default = {
1197
1197
  wcagVersion: "2.2",
1198
1198
  source: "https://www.w3.org/TR/WCAG22/",
1199
1199
  criteriaSource: "https://github.com/w3c/wcag",
1200
- provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-12 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
1200
+ provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
1201
1201
  criteria: [
1202
1202
  {
1203
1203
  id: "1.1.1",
@@ -18794,8 +18794,8 @@ function opaqueLibrarySpecifiers(ast, elements) {
18794
18794
  }
18795
18795
 
18796
18796
  // src/glob.ts
18797
- import { existsSync as existsSync9, statSync as statSync6 } from "fs";
18798
- import { join as join21, sep as sep3 } from "path";
18797
+ import { existsSync as existsSync11, statSync as statSync6 } from "fs";
18798
+ import { join as join24, sep as sep3 } from "path";
18799
18799
 
18800
18800
  // src/util.ts
18801
18801
  import { readFileSync } from "fs";
@@ -18852,13 +18852,20 @@ import { join as join14 } from "path";
18852
18852
  import { createHash as createHash3 } from "crypto";
18853
18853
  import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
18854
18854
  import { join as join16 } from "path";
18855
- import { existsSync as existsSync7 } from "fs";
18856
- import { join as join17 } from "path";
18855
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
18856
+ import { join as join17, resolve as resolve2 } from "path";
18857
+ import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
18858
+ import { join as join18, resolve as resolve3 } from "path";
18859
+ import { readFileSync as readFileSync11 } from "fs";
18860
+ import { join as join19 } from "path";
18861
+ import { spawn } from "child_process";
18862
+ import { existsSync as existsSync9 } from "fs";
18863
+ import { join as join20 } from "path";
18857
18864
  import { pathToFileURL as pathToFileURL2 } from "url";
18858
18865
  import { statSync as statSync5 } from "fs";
18859
- import { join as join18 } from "path";
18860
- import { readFileSync as readFileSync9 } from "fs";
18861
- import { isAbsolute, join as join19 } from "path";
18866
+ import { join as join21 } from "path";
18867
+ import { readFileSync as readFileSync12 } from "fs";
18868
+ import { isAbsolute, join as join22 } from "path";
18862
18869
  import { createInterface } from "readline";
18863
18870
  import { existsSync as existsSync2, statSync as statSync2 } from "fs";
18864
18871
  import { availableParallelism } from "os";
@@ -18873,8 +18880,8 @@ import { existsSync as existsSync4, mkdirSync, mkdtempSync, readFileSync as read
18873
18880
  import { dirname as dirname3, join as join6, resolve, sep as sep2 } from "path";
18874
18881
  import { gunzipSync } from "zlib";
18875
18882
  import { join as join15 } from "path";
18876
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
18877
- import { join as join20, resolve as resolve2 } from "path";
18883
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
18884
+ import { join as join23, resolve as resolve4 } from "path";
18878
18885
  var __defProp = Object.defineProperty;
18879
18886
  var __getOwnPropNames = Object.getOwnPropertyNames;
18880
18887
  var __esm = (fn, res) => function __init() {
@@ -18890,7 +18897,7 @@ var EXTRACTOR_VERSION;
18890
18897
  var init_types = __esm({
18891
18898
  "src/types.ts"() {
18892
18899
  "use strict";
18893
- ENGINE_VERSION = "2.27.1";
18900
+ ENGINE_VERSION = "2.28.0";
18894
18901
  SCHEMA_VERSION2 = 5;
18895
18902
  EXTRACTOR_VERSION = 13;
18896
18903
  }
@@ -18958,6 +18965,14 @@ function keywords2(question) {
18958
18965
  }
18959
18966
  return out2;
18960
18967
  }
18968
+ function droppedKeywords(question) {
18969
+ const out2 = [];
18970
+ for (const raw of foldText(question).split(/[^A-Za-z0-9_]+/)) {
18971
+ if (!raw) continue;
18972
+ if (raw.length < 2 || STOPWORDS.has(raw.toLowerCase())) out2.push(raw);
18973
+ }
18974
+ return out2;
18975
+ }
18961
18976
  function rankedKeywords(question) {
18962
18977
  const base = keywords2(question);
18963
18978
  const score = (raw) => {
@@ -20830,13 +20845,13 @@ async function Module2(moduleArg = {}) {
20830
20845
  }
20831
20846
  readAsync = /* @__PURE__ */ __name(async (url) => {
20832
20847
  if (isFileURI(url)) {
20833
- return new Promise((resolve32, reject) => {
20848
+ return new Promise((resolve52, reject) => {
20834
20849
  var xhr = new XMLHttpRequest();
20835
20850
  xhr.open("GET", url, true);
20836
20851
  xhr.responseType = "arraybuffer";
20837
20852
  xhr.onload = () => {
20838
20853
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
20839
- resolve32(xhr.response);
20854
+ resolve52(xhr.response);
20840
20855
  return;
20841
20856
  }
20842
20857
  reject(xhr.status);
@@ -21032,9 +21047,9 @@ async function Module2(moduleArg = {}) {
21032
21047
  __name(receiveInstantiationResult, "receiveInstantiationResult");
21033
21048
  var info2 = getWasmImports();
21034
21049
  if (Module["instantiateWasm"]) {
21035
- return new Promise((resolve32, reject) => {
21050
+ return new Promise((resolve52, reject) => {
21036
21051
  Module["instantiateWasm"](info2, (mod, inst) => {
21037
- resolve32(receiveInstance(mod, inst));
21052
+ resolve52(receiveInstance(mod, inst));
21038
21053
  });
21039
21054
  });
21040
21055
  }
@@ -22365,8 +22380,8 @@ async function Module2(moduleArg = {}) {
22365
22380
  if (runtimeInitialized) {
22366
22381
  moduleRtn = Module;
22367
22382
  } else {
22368
- moduleRtn = new Promise((resolve32, reject) => {
22369
- readyPromiseResolve = resolve32;
22383
+ moduleRtn = new Promise((resolve52, reject) => {
22384
+ readyPromiseResolve = resolve52;
22370
22385
  readyPromiseReject = reject;
22371
22386
  });
22372
22387
  }
@@ -28772,6 +28787,27 @@ function buildTrigramIndex(docs) {
28772
28787
  return index;
28773
28788
  }
28774
28789
  function searchIndex(scan2, query, opts = {}) {
28790
+ return runSearch(scan2, query, opts).results;
28791
+ }
28792
+ function explainQuery(scan2, query, opts = {}) {
28793
+ return runSearch(scan2, query, opts);
28794
+ }
28795
+ function emptyExplanation(query, verdict, note) {
28796
+ return {
28797
+ results: [],
28798
+ explain: {
28799
+ query,
28800
+ terms: [],
28801
+ droppedStopwords: droppedKeywords(query),
28802
+ unresolvedTerms: [],
28803
+ verdict,
28804
+ note,
28805
+ bridgedOnlyResults: 0,
28806
+ resultCount: 0
28807
+ }
28808
+ };
28809
+ }
28810
+ function runSearch(scan2, query, opts = {}) {
28775
28811
  const terms = [];
28776
28812
  const seen = /* @__PURE__ */ new Set();
28777
28813
  for (const kw of keywords2(query)) {
@@ -28781,11 +28817,18 @@ function searchIndex(scan2, query, opts = {}) {
28781
28817
  terms.push(t2);
28782
28818
  }
28783
28819
  }
28784
- if (!terms.length) return [];
28820
+ if (!terms.length) {
28821
+ const dropped = droppedKeywords(query);
28822
+ return emptyExplanation(
28823
+ query,
28824
+ "none",
28825
+ dropped.length ? `Nothing was searched for: every token in this query (${dropped.join(", ")}) is a stopword or too short to index. Search with the identifiers or domain words you are actually looking for.` : "Nothing was searched for: the query carried no indexable token."
28826
+ );
28827
+ }
28785
28828
  const queryWantsTests = terms.some((t2) => TEST_INTENT.test(t2));
28786
28829
  const docs = bm25DocsFor(scan2);
28787
28830
  const n = docs.length;
28788
- if (!n) return [];
28831
+ if (!n) return emptyExplanation(query, "none", "This index contains no files.");
28789
28832
  const avgLen = {};
28790
28833
  for (const f of FIELDS) {
28791
28834
  let total = 0;
@@ -28847,7 +28890,7 @@ function searchIndex(scan2, query, opts = {}) {
28847
28890
  const matchedFields = /* @__PURE__ */ new Set();
28848
28891
  const symbolTerms = /* @__PURE__ */ new Set();
28849
28892
  const fuzzyHit = /* @__PURE__ */ new Set();
28850
- let exact = false;
28893
+ let exactNameHit = false;
28851
28894
  const weightedTf = (term) => {
28852
28895
  let wtf = 0;
28853
28896
  for (const f of FIELDS) {
@@ -28865,7 +28908,7 @@ function searchIndex(scan2, query, opts = {}) {
28865
28908
  if (wtf > 0) {
28866
28909
  matched.push(t2);
28867
28910
  symbolTerms.add(t2);
28868
- if (d.exactNames.has(t2)) exact = true;
28911
+ if (d.exactNames.has(t2)) exactNameHit = true;
28869
28912
  score += idfOf(df.get(t2)) * (wtf / (K1 + wtf));
28870
28913
  continue;
28871
28914
  }
@@ -28878,7 +28921,7 @@ function searchIndex(scan2, query, opts = {}) {
28878
28921
  }
28879
28922
  }
28880
28923
  if (!matched.length && !fuzzyHit.size) continue;
28881
- if (exact) score *= EXACT_NAME_BOOST;
28924
+ if (exactNameHit) score *= EXACT_NAME_BOOST;
28882
28925
  if (d.isTest && !queryWantsTests) score *= TEST_DEMOTION;
28883
28926
  if (prior) {
28884
28927
  score *= 1 + 0.35 * Math.log1p(prior.get(d.file) ?? 0);
@@ -28902,10 +28945,60 @@ function searchIndex(scan2, query, opts = {}) {
28902
28945
  result.line = Math.min(...hits.map((h) => h.line));
28903
28946
  }
28904
28947
  if (fuzzyHit.size) result.fuzzyTerms = [...fuzzyHit].sort(byStr);
28948
+ if (!matched.length) result.bridgedOnly = true;
28905
28949
  results.push(result);
28906
28950
  }
28907
28951
  results.sort((a, b) => b.score - a.score || byStr(a.file, b.file));
28908
- return results.slice(0, opts.limit ?? DEFAULT_LIMIT);
28952
+ const kept = (opts.exact ? results.filter((r) => !r.bridgedOnly) : results).slice(0, opts.limit ?? DEFAULT_LIMIT);
28953
+ return { results: kept, explain: explainOf(query, terms, df, fuzzyCandidates, kept) };
28954
+ }
28955
+ function explainOf(query, terms, df, fuzzyCandidates, kept) {
28956
+ const diagnostics = terms.map((term) => {
28957
+ const frequency = df.get(term) ?? 0;
28958
+ const candidates2 = frequency === 0 ? fuzzyCandidates.get(term) ?? [] : [];
28959
+ if (!candidates2.length) return { term, df: frequency };
28960
+ const via = candidates2[0].dice === STEM_WEIGHT ? "stem" : "trigram";
28961
+ return { term, df: frequency, bridge: { via, to: candidates2.map((c2) => c2.term), dice: candidates2[0].dice } };
28962
+ });
28963
+ const unresolvedTerms = diagnostics.filter((d) => d.df === 0 && !d.bridge).map((d) => d.term).sort(byStr);
28964
+ const wholeIdentifier = !/\s/.test(query.trim()) && terms.length ? { term: terms[0], df: df.get(terms[0]) ?? 0 } : void 0;
28965
+ const bridgedOnlyResults = kept.filter((r) => r.bridgedOnly).length;
28966
+ const allBridged = kept.length > 0 && bridgedOnlyResults === kept.length;
28967
+ const missingIdentifier = wholeIdentifier?.df === 0;
28968
+ const verdict = !kept.length ? "none" : missingIdentifier || allBridged ? "weak" : "match";
28969
+ const explain = {
28970
+ query,
28971
+ terms: diagnostics,
28972
+ droppedStopwords: droppedKeywords(query),
28973
+ unresolvedTerms,
28974
+ ...wholeIdentifier ? { wholeIdentifier } : {},
28975
+ verdict,
28976
+ bridgedOnlyResults,
28977
+ resultCount: kept.length
28978
+ };
28979
+ const note = noteFor(explain, missingIdentifier, allBridged);
28980
+ if (note) explain.note = note;
28981
+ return explain;
28982
+ }
28983
+ function noteFor(explain, missingIdentifier, allBridged) {
28984
+ const { verdict, wholeIdentifier, resultCount, terms } = explain;
28985
+ if (verdict === "match") return void 0;
28986
+ if (!resultCount) {
28987
+ return explain.unresolvedTerms.length ? `No file matches. ${explain.unresolvedTerms.length === 1 ? "The term" : "The terms"} ${explain.unresolvedTerms.join(", ")} appear nowhere in this index.` : "No file matches this query.";
28988
+ }
28989
+ if (missingIdentifier && wholeIdentifier) {
28990
+ const parts2 = terms.filter((t2) => t2.term !== wholeIdentifier.term && t2.df > 0).map((t2) => t2.term).join(", ");
28991
+ const near = terms.find((t2) => t2.term === wholeIdentifier.term)?.bridge?.to ?? [];
28992
+ const because = parts2 ? ` The ${resultCount} result${resultCount === 1 ? "" : "s"} below match only its parts (${parts2}).` : "";
28993
+ const suggestion = near.length ? ` Closest indexed name${near.length === 1 ? "" : "s"}: ${near.join(", ")}.` : "";
28994
+ return `No file in this index defines or mentions "${wholeIdentifier.term}".${because}${suggestion} If you expected it here, check you are indexing the right branch or commit.`;
28995
+ }
28996
+ if (allBridged) {
28997
+ const bridged = terms.filter((t2) => t2.bridge);
28998
+ const spelled = bridged.map((t2) => `"${t2.term}" \u2192 ${t2.bridge.to.join(", ")}`).join("; ");
28999
+ return `Nothing matched verbatim. Every result below came from a near match: ${spelled}. Re-run with --exact to see only literal matches.`;
29000
+ }
29001
+ return void 0;
28909
29002
  }
28910
29003
  var K1;
28911
29004
  var DEFAULT_LIMIT;
@@ -29443,6 +29536,15 @@ function findSymbol(scan2, namePath, opts = {}) {
29443
29536
  m.body = content.split("\n").slice(m.line - 1, end).join("\n");
29444
29537
  }
29445
29538
  }
29539
+ if (opts.concise) {
29540
+ return capped.map((m) => ({
29541
+ name: m.name,
29542
+ kind: m.kind,
29543
+ file: m.file,
29544
+ line: m.line,
29545
+ ...m.body !== void 0 ? { body: m.body } : {}
29546
+ }));
29547
+ }
29446
29548
  return capped;
29447
29549
  }
29448
29550
  function findReferences(scan2, name2) {
@@ -31101,6 +31203,703 @@ var init_endpoint = __esm({
31101
31203
  init_embed();
31102
31204
  }
31103
31205
  });
31206
+ function renderRepoMap(scan2, graph, opts = {}) {
31207
+ const budgetChars = (opts.budgetTokens ?? 1024) * CHARS_PER_TOKEN;
31208
+ const maxSymbols = opts.maxSymbolsPerFile ?? 8;
31209
+ const ranked = [...graph.files].filter((f) => f.fileKind === "code").sort((a, b) => (b.pagerank ?? 0) - (a.pagerank ?? 0) || b.symbols - a.symbols || byStr(a.rel, b.rel));
31210
+ const records = new Map(scan2.files.map((f) => [f.rel, f]));
31211
+ const header4 = `# repo map \u2014 ${graph.fileCount} files
31212
+ `;
31213
+ let out2 = header4;
31214
+ let files = 0;
31215
+ for (const node of ranked) {
31216
+ const rec = records.get(node.rel);
31217
+ if (!rec) continue;
31218
+ const symbols = [...rec.symbols].filter((s) => s.kind !== "reexport" && s.kind !== "reexport-all").sort((a, b) => Number(b.exported) - Number(a.exported) || a.line - b.line).slice(0, maxSymbols);
31219
+ let block = `
31220
+ ${node.rel}:
31221
+ `;
31222
+ for (const s of symbols) {
31223
+ const sig = (s.signature ?? `${s.kind} ${s.name}`).replace(/\s+/g, " ").trim().slice(0, 120);
31224
+ block += ` ${s.line}: ${sig}
31225
+ `;
31226
+ }
31227
+ if (out2.length + block.length > budgetChars) break;
31228
+ out2 += block;
31229
+ files++;
31230
+ }
31231
+ return `${out2}
31232
+ (${files} of ${ranked.length} code files shown, ~${Math.ceil(out2.length / CHARS_PER_TOKEN)} tokens)
31233
+ `;
31234
+ }
31235
+ var CHARS_PER_TOKEN;
31236
+ var init_repomap = __esm({
31237
+ "src/repomap.ts"() {
31238
+ "use strict";
31239
+ init_sort();
31240
+ CHARS_PER_TOKEN = 4;
31241
+ }
31242
+ });
31243
+ function changeCoupling(dir, opts = {}) {
31244
+ const maxCommitFiles = opts.maxCommitFiles ?? 30;
31245
+ const minTogether = opts.minTogether ?? 3;
31246
+ const maxPairs = opts.maxPairs ?? 100;
31247
+ const range = opts.since ? [`${opts.since}..HEAD`] : [];
31248
+ const res = sh("git", ["-C", dir, "-c", "core.quotePath=false", "log", ...range, "--pretty=format:%x1e", "--name-only"]);
31249
+ if (!res.ok) return { ok: false, couplings: [] };
31250
+ const totals = /* @__PURE__ */ new Map();
31251
+ const pairs = /* @__PURE__ */ new Map();
31252
+ for (const block of res.stdout.split("")) {
31253
+ const files = block.split("\n").map((l) => l.trim()).filter(Boolean);
31254
+ if (!files.length || files.length > maxCommitFiles) continue;
31255
+ const unique = [...new Set(files)].sort(byStr);
31256
+ for (const f of unique) totals.set(f, (totals.get(f) ?? 0) + 1);
31257
+ for (let i2 = 0; i2 < unique.length; i2++) {
31258
+ for (let j = i2 + 1; j < unique.length; j++) {
31259
+ const key = `${unique[i2]}${SEP4}${unique[j]}`;
31260
+ pairs.set(key, (pairs.get(key) ?? 0) + 1);
31261
+ }
31262
+ }
31263
+ }
31264
+ const out2 = [];
31265
+ for (const [key, together] of pairs) {
31266
+ if (together < minTogether) continue;
31267
+ const [a, b] = key.split(SEP4);
31268
+ const totalA = totals.get(a) ?? together;
31269
+ const totalB = totals.get(b) ?? together;
31270
+ out2.push({ a, b, together, totalA, totalB, strength: Number((together / Math.min(totalA, totalB)).toFixed(3)) });
31271
+ }
31272
+ out2.sort((x, y) => y.strength - x.strength || y.together - x.together || byStr(x.a, y.a) || byStr(x.b, y.b));
31273
+ return { ok: true, couplings: out2.slice(0, maxPairs) };
31274
+ }
31275
+ function rankHotspots(scan2, churn, top = 20) {
31276
+ const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
31277
+ const commits = churn.get(f.rel) ?? 0;
31278
+ return { rel: f.rel, lines: f.lines, commits, score: Number((commits * Math.log2(f.lines + 1)).toFixed(2)) };
31279
+ });
31280
+ out2.sort((a, b) => b.score - a.score || b.lines - a.lines || byStr(a.rel, b.rel));
31281
+ return out2.slice(0, top);
31282
+ }
31283
+ var SEP4;
31284
+ var init_coupling = __esm({
31285
+ "src/coupling.ts"() {
31286
+ "use strict";
31287
+ init_util();
31288
+ init_sort();
31289
+ SEP4 = "\0";
31290
+ }
31291
+ });
31292
+ function tagline(root) {
31293
+ for (const name2 of README_NAMES) {
31294
+ const path = join17(root, name2);
31295
+ if (!existsSync7(path)) continue;
31296
+ let text;
31297
+ try {
31298
+ text = readFileSync9(path, "utf8");
31299
+ } catch {
31300
+ continue;
31301
+ }
31302
+ for (const raw of text.split(/\n\s*\n/)) {
31303
+ const line = raw.trim();
31304
+ if (!line || line.startsWith("#") || line.startsWith("<")) continue;
31305
+ if (/^(\[!\[|!\[|\[)/.test(line) && !/[.:]\s/.test(line)) continue;
31306
+ const cleaned = line.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_`]/g, "").replace(/\s+/g, " ").trim();
31307
+ if (cleaned.length > 20) return cleaned.slice(0, 400);
31308
+ }
31309
+ break;
31310
+ }
31311
+ return void 0;
31312
+ }
31313
+ function onboardBrief(scan2, graph, opts = {}) {
31314
+ const lines = [];
31315
+ const name2 = resolve2(scan2.root).replace(/\/+$/, "").split("/").pop() || "repository";
31316
+ lines.push(`# ${name2}`, "");
31317
+ const summary = tagline(scan2.root);
31318
+ if (summary) lines.push(summary, "");
31319
+ const languages = Object.entries(graph.languages).sort((a, b) => b[1] - a[1] || byStr(a[0], b[0])).slice(0, 6).map(([lang, count]) => `${lang} ${count}`).join(", ");
31320
+ lines.push(`**${graph.fileCount} indexed files** \xB7 ${languages}`, "");
31321
+ const workspaces = detectWorkspaces(scan2.root);
31322
+ if (workspaces.packages.length > 1) {
31323
+ lines.push(`## Layout \u2014 monorepo, ${workspaces.packages.length} packages`, "");
31324
+ for (const pkg of workspaces.packages.slice(0, 20)) lines.push(`- \`${pkg.dir}\` \u2014 ${pkg.name}`);
31325
+ if (workspaces.packages.length > 20) lines.push(`- \u2026and ${workspaces.packages.length - 20} more`);
31326
+ if (workspaces.cycle?.length) lines.push("", `\u26A0 dependency cycle: ${workspaces.cycle.join(" \u2192 ")}`);
31327
+ lines.push("");
31328
+ }
31329
+ lines.push("## Key files", "", renderRepoMap(scan2, graph, { budgetTokens: opts.budgetTokens ?? 900 }).trim(), "");
31330
+ const { churn, ok: churnOk } = gitChurn(scan2.root);
31331
+ if (churnOk && churn.size) {
31332
+ const hotspots = rankHotspots(scan2, churn, 8);
31333
+ if (hotspots.length) {
31334
+ lines.push("## Where work concentrates", "", "Files ranked by commits \xD7 size \u2014 where changes and defects cluster.", "");
31335
+ for (const spot of hotspots) lines.push(`- \`${spot.rel}\` \u2014 ${spot.commits} commits, ${spot.lines} lines`);
31336
+ lines.push("");
31337
+ }
31338
+ }
31339
+ lines.push(
31340
+ "## Next",
31341
+ "",
31342
+ "- `search <query>` \u2014 BM25 over names, paths, doc comments and prose; `explain_search` says whether it really matched",
31343
+ "- `find_symbol <Name>` / `symbols_overview <file>` \u2014 read structure without reading files",
31344
+ "- `find_references <Name>` \u2014 three labelled tiers; add `lsp: true` when a language server is configured",
31345
+ ""
31346
+ );
31347
+ const brief = lines.join("\n");
31348
+ if (opts.remember === false) return { brief };
31349
+ const memoryName = opts.memoryName ?? "onboarding";
31350
+ writeMemory(scan2.root, memoryName, brief);
31351
+ return { brief, memory: memoryName };
31352
+ }
31353
+ var README_NAMES;
31354
+ var init_onboard = __esm({
31355
+ "src/onboard.ts"() {
31356
+ "use strict";
31357
+ init_repomap();
31358
+ init_workspaces();
31359
+ init_coupling();
31360
+ init_git();
31361
+ init_memory();
31362
+ init_sort();
31363
+ README_NAMES = ["README.md", "README.markdown", "README.rst", "README.txt", "README"];
31364
+ }
31365
+ });
31366
+ function encodeMessage(msg) {
31367
+ const body2 = JSON.stringify(msg);
31368
+ return `Content-Length: ${byteLength(body2)}${HEADER_END}${body2}`;
31369
+ }
31370
+ function byteLength(s) {
31371
+ return new TextEncoder().encode(s).byteLength;
31372
+ }
31373
+ function createFramer() {
31374
+ const encoder = new TextEncoder();
31375
+ const decoder = new TextDecoder();
31376
+ let buffer = new Uint8Array(0);
31377
+ const append = (chunk2) => {
31378
+ const next = new Uint8Array(buffer.length + chunk2.length);
31379
+ next.set(buffer);
31380
+ next.set(chunk2, buffer.length);
31381
+ buffer = next;
31382
+ };
31383
+ const headerEnd = () => {
31384
+ for (let i2 = 0; i2 + 3 < buffer.length; i2++) {
31385
+ if (buffer[i2] === 13 && buffer[i2 + 1] === 10 && buffer[i2 + 2] === 13 && buffer[i2 + 3] === 10) return i2;
31386
+ }
31387
+ return -1;
31388
+ };
31389
+ return {
31390
+ push(chunk2) {
31391
+ append(typeof chunk2 === "string" ? encoder.encode(chunk2) : chunk2);
31392
+ const out2 = [];
31393
+ for (; ; ) {
31394
+ const end = headerEnd();
31395
+ if (end < 0) return out2;
31396
+ const headers = decoder.decode(buffer.subarray(0, end));
31397
+ const match = /content-length:\s*(\d+)/i.exec(headers);
31398
+ if (!match) {
31399
+ buffer = buffer.subarray(end + 4);
31400
+ continue;
31401
+ }
31402
+ const length = Number(match[1]);
31403
+ if (!Number.isSafeInteger(length) || length < 0 || length > MAX_FRAME_BYTES) {
31404
+ buffer = buffer.subarray(end + 4);
31405
+ continue;
31406
+ }
31407
+ const start2 = end + 4;
31408
+ if (buffer.length < start2 + length) return out2;
31409
+ const body2 = decoder.decode(buffer.subarray(start2, start2 + length));
31410
+ buffer = buffer.subarray(start2 + length);
31411
+ try {
31412
+ out2.push(JSON.parse(body2));
31413
+ } catch {
31414
+ }
31415
+ }
31416
+ }
31417
+ };
31418
+ }
31419
+ function fileUri(root, rel2) {
31420
+ const abs = `${root.replace(/\/+$/, "")}/${rel2.replace(/^\/+/, "")}`;
31421
+ const drive = /^([A-Za-z]):/.exec(abs);
31422
+ const path = drive ? `/${abs}` : abs;
31423
+ return "file://" + path.split("/").map((segment, i2) => i2 === 0 ? segment : encodeURIComponent(segment)).join("/");
31424
+ }
31425
+ function relFromUri(root, uri) {
31426
+ if (!uri.startsWith("file://")) return void 0;
31427
+ let path = decodeURIComponent(uri.slice("file://".length));
31428
+ if (/^\/[A-Za-z]:/.test(path)) path = path.slice(1);
31429
+ const base = root.replace(/\/+$/, "");
31430
+ if (path === base) return "";
31431
+ if (!path.startsWith(`${base}/`)) return void 0;
31432
+ return path.slice(base.length + 1);
31433
+ }
31434
+ function locationsToRefs(root, raw) {
31435
+ const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" ? [raw] : [];
31436
+ const seen = /* @__PURE__ */ new Set();
31437
+ const out2 = [];
31438
+ for (const item of list) {
31439
+ if (!item || typeof item !== "object") continue;
31440
+ const uri = item.uri ?? item.targetUri;
31441
+ if (!uri) continue;
31442
+ const file = relFromUri(root, uri);
31443
+ if (file === void 0 || file === "") continue;
31444
+ const start2 = (item.range ?? item.targetSelectionRange ?? item.targetRange)?.start;
31445
+ const line = typeof start2?.line === "number" ? start2.line + 1 : 1;
31446
+ const character = typeof start2?.character === "number" ? start2.character : void 0;
31447
+ const key = `${file}:${line}:${character ?? ""}`;
31448
+ if (seen.has(key)) continue;
31449
+ seen.add(key);
31450
+ out2.push(character === void 0 ? { file, line } : { file, line, character });
31451
+ }
31452
+ return out2.sort((a, b) => byStr(a.file, b.file) || a.line - b.line || (a.character ?? 0) - (b.character ?? 0));
31453
+ }
31454
+ var MAX_FRAME_BYTES;
31455
+ var HEADER_END;
31456
+ var init_protocol = __esm({
31457
+ "src/lsp/protocol.ts"() {
31458
+ "use strict";
31459
+ init_sort();
31460
+ MAX_FRAME_BYTES = 32 * 1024 * 1024;
31461
+ HEADER_END = "\r\n\r\n";
31462
+ }
31463
+ });
31464
+ async function openLspSession(transport, options) {
31465
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT;
31466
+ const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT;
31467
+ const framer = createFramer();
31468
+ const pending = /* @__PURE__ */ new Map();
31469
+ const open = /* @__PURE__ */ new Set();
31470
+ let nextId = 1;
31471
+ let dead;
31472
+ const failAll = (error) => {
31473
+ dead = error;
31474
+ for (const [, p] of pending) {
31475
+ clearTimeout(p.timer);
31476
+ p.reject(error);
31477
+ }
31478
+ pending.clear();
31479
+ };
31480
+ transport.onData((chunk2) => {
31481
+ for (const message of framer.push(chunk2)) {
31482
+ if (typeof message.id !== "number") continue;
31483
+ const waiter = pending.get(message.id);
31484
+ if (!waiter) continue;
31485
+ pending.delete(message.id);
31486
+ clearTimeout(waiter.timer);
31487
+ if (message.error) waiter.reject(new Error(`${message.error.message} (code ${message.error.code})`));
31488
+ else waiter.resolve(message.result);
31489
+ }
31490
+ });
31491
+ transport.onExit((code2) => failAll(new Error(`language server exited (code ${code2 ?? "unknown"})`)));
31492
+ const notify = (method, params) => {
31493
+ if (dead) return;
31494
+ transport.write(encodeMessage({ jsonrpc: "2.0", method, params }));
31495
+ };
31496
+ const request = (method, params, budget = timeoutMs) => {
31497
+ if (dead) return Promise.reject(dead);
31498
+ const id = nextId++;
31499
+ return new Promise((resolve52, reject) => {
31500
+ const timer = setTimeout(() => {
31501
+ pending.delete(id);
31502
+ reject(new LspTimeout(method, budget));
31503
+ }, budget);
31504
+ timer.unref?.();
31505
+ pending.set(id, { resolve: resolve52, reject, timer });
31506
+ transport.write(encodeMessage({ jsonrpc: "2.0", id, method, params }));
31507
+ });
31508
+ };
31509
+ const initResult = await request(
31510
+ "initialize",
31511
+ {
31512
+ processId: null,
31513
+ rootUri: fileUri(options.root, ""),
31514
+ workspaceFolders: [{ uri: fileUri(options.root, ""), name: "repo" }],
31515
+ capabilities: {
31516
+ textDocument: {
31517
+ references: { dynamicRegistration: false },
31518
+ definition: { dynamicRegistration: false, linkSupport: true },
31519
+ implementation: { dynamicRegistration: false, linkSupport: true }
31520
+ }
31521
+ },
31522
+ ...options.initializationOptions !== void 0 ? { initializationOptions: options.initializationOptions } : {}
31523
+ },
31524
+ startupTimeoutMs
31525
+ );
31526
+ notify("initialized", {});
31527
+ const provides = (key) => {
31528
+ const value = initResult?.capabilities?.[key];
31529
+ return value === true || typeof value === "object" && value !== null;
31530
+ };
31531
+ const capabilities = {
31532
+ references: provides("referencesProvider"),
31533
+ definition: provides("definitionProvider"),
31534
+ implementation: provides("implementationProvider"),
31535
+ typeHierarchy: provides("typeHierarchyProvider")
31536
+ };
31537
+ const positionOf = (rel2, line, character) => ({
31538
+ textDocument: { uri: fileUri(options.root, rel2) },
31539
+ // The engine counts lines from 1; LSP counts from 0.
31540
+ position: { line: Math.max(0, line - 1), character }
31541
+ });
31542
+ return {
31543
+ capabilities,
31544
+ didOpen(rel2, text, languageId) {
31545
+ if (open.has(rel2)) return;
31546
+ open.add(rel2);
31547
+ notify("textDocument/didOpen", {
31548
+ textDocument: { uri: fileUri(options.root, rel2), languageId, version: 1, text }
31549
+ });
31550
+ },
31551
+ async references(rel2, line, character) {
31552
+ if (!capabilities.references) return [];
31553
+ const raw = await request("textDocument/references", {
31554
+ ...positionOf(rel2, line, character),
31555
+ context: { includeDeclaration: true }
31556
+ });
31557
+ return locationsToRefs(options.root, raw);
31558
+ },
31559
+ async definition(rel2, line, character) {
31560
+ if (!capabilities.definition) return [];
31561
+ return locationsToRefs(options.root, await request("textDocument/definition", positionOf(rel2, line, character)));
31562
+ },
31563
+ async shutdown() {
31564
+ try {
31565
+ for (const rel2 of open) notify("textDocument/didClose", { textDocument: { uri: fileUri(options.root, rel2) } });
31566
+ open.clear();
31567
+ await request("shutdown", null, Math.min(timeoutMs, 2e3));
31568
+ notify("exit", void 0);
31569
+ } catch {
31570
+ } finally {
31571
+ failAll(new Error("session closed"));
31572
+ transport.close();
31573
+ }
31574
+ }
31575
+ };
31576
+ }
31577
+ var LspTimeout;
31578
+ var DEFAULT_TIMEOUT;
31579
+ var DEFAULT_STARTUP_TIMEOUT;
31580
+ var init_client = __esm({
31581
+ "src/lsp/client.ts"() {
31582
+ "use strict";
31583
+ init_protocol();
31584
+ LspTimeout = class extends Error {
31585
+ constructor(method, ms) {
31586
+ super(`${method} exceeded ${ms}ms`);
31587
+ this.name = "LspTimeout";
31588
+ }
31589
+ };
31590
+ DEFAULT_TIMEOUT = 5e3;
31591
+ DEFAULT_STARTUP_TIMEOUT = 15e3;
31592
+ }
31593
+ });
31594
+ function resolveLspConfigPath(repo) {
31595
+ const env = process.env.CODEINDEX_LSP_CONFIG;
31596
+ if (env !== void 0) {
31597
+ const trimmed = env.trim();
31598
+ if (!trimmed || trimmed === "0" || trimmed.toLowerCase() === "off") return { path: void 0, source: "none" };
31599
+ return { path: resolve3(trimmed), source: "env" };
31600
+ }
31601
+ const inRepo = join18(repo, LSP_CONFIG_DIR, LSP_CONFIG_NAME);
31602
+ if (existsSync8(inRepo)) return { path: inRepo, source: "repo" };
31603
+ const inCwd = join18(process.cwd(), LSP_CONFIG_DIR, LSP_CONFIG_NAME);
31604
+ if (inCwd !== inRepo && existsSync8(inCwd)) return { path: inCwd, source: "cwd" };
31605
+ return { path: void 0, source: "none" };
31606
+ }
31607
+ function parseLspConfig(payload) {
31608
+ if (!payload || typeof payload !== "object") throw new Error("lsp.json must be a JSON object");
31609
+ const raw = payload;
31610
+ if (raw.version !== 1) throw new Error(`lsp.json: unsupported version ${JSON.stringify(raw.version)} (expected 1)`);
31611
+ if (!Array.isArray(raw.servers)) throw new Error("lsp.json: `servers` must be an array");
31612
+ const ids = /* @__PURE__ */ new Set();
31613
+ const servers = raw.servers.map((entry, i2) => {
31614
+ if (!entry || typeof entry !== "object") throw new Error(`lsp.json: servers[${i2}] must be an object`);
31615
+ const s = entry;
31616
+ const id = typeof s.id === "string" && s.id.trim() ? s.id.trim() : void 0;
31617
+ if (!id) throw new Error(`lsp.json: servers[${i2}].id must be a non-empty string`);
31618
+ if (ids.has(id)) throw new Error(`lsp.json: duplicate server id ${JSON.stringify(id)}`);
31619
+ ids.add(id);
31620
+ if (typeof s.command !== "string" || !s.command.trim()) throw new Error(`lsp.json: servers[${i2}].command must be a non-empty string`);
31621
+ if (!Array.isArray(s.languages) || !s.languages.length || s.languages.some((l) => typeof l !== "string")) {
31622
+ throw new Error(`lsp.json: servers[${i2}].languages must be a non-empty array of strings`);
31623
+ }
31624
+ if (s.args !== void 0 && (!Array.isArray(s.args) || s.args.some((a) => typeof a !== "string"))) {
31625
+ throw new Error(`lsp.json: servers[${i2}].args must be an array of strings`);
31626
+ }
31627
+ return {
31628
+ id,
31629
+ languages: s.languages,
31630
+ ...typeof s.languageId === "string" ? { languageId: s.languageId } : {},
31631
+ command: s.command,
31632
+ ...Array.isArray(s.args) ? { args: s.args } : {},
31633
+ ...s.env && typeof s.env === "object" ? { env: s.env } : {},
31634
+ ...s.initializationOptions !== void 0 ? { initializationOptions: s.initializationOptions } : {},
31635
+ ...typeof s.timeoutMs === "number" ? { timeoutMs: s.timeoutMs } : {},
31636
+ ...typeof s.startupTimeoutMs === "number" ? { startupTimeoutMs: s.startupTimeoutMs } : {}
31637
+ };
31638
+ });
31639
+ return { version: 1, servers };
31640
+ }
31641
+ function loadLspConfig(repo) {
31642
+ const { path } = resolveLspConfigPath(repo);
31643
+ if (!path || !existsSync8(path)) return void 0;
31644
+ let payload;
31645
+ try {
31646
+ payload = JSON.parse(readFileSync10(path, "utf8"));
31647
+ } catch (e) {
31648
+ throw new Error(`${path}: ${e instanceof Error ? e.message : String(e)}`);
31649
+ }
31650
+ return parseLspConfig(payload);
31651
+ }
31652
+ function serverForLang(config, lang) {
31653
+ return config.servers.find((s) => s.languages.includes(lang));
31654
+ }
31655
+ function timeoutFor(server) {
31656
+ return positiveEnv("CODEINDEX_LSP_TIMEOUT_MS") ?? server.timeoutMs ?? DEFAULT_TIMEOUT_MS;
31657
+ }
31658
+ function startupTimeoutFor(server) {
31659
+ return positiveEnv("CODEINDEX_LSP_STARTUP_TIMEOUT_MS") ?? server.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
31660
+ }
31661
+ function positiveEnv(name2) {
31662
+ const raw = process.env[name2];
31663
+ if (raw === void 0) return void 0;
31664
+ const value = Number(raw);
31665
+ return Number.isFinite(value) && value > 0 ? value : void 0;
31666
+ }
31667
+ var LSP_CONFIG_NAME;
31668
+ var LSP_CONFIG_DIR;
31669
+ var DEFAULT_TIMEOUT_MS;
31670
+ var DEFAULT_STARTUP_TIMEOUT_MS;
31671
+ var init_config2 = __esm({
31672
+ "src/lsp/config.ts"() {
31673
+ "use strict";
31674
+ LSP_CONFIG_NAME = "lsp.json";
31675
+ LSP_CONFIG_DIR = ".codeindex";
31676
+ DEFAULT_TIMEOUT_MS = 5e3;
31677
+ DEFAULT_STARTUP_TIMEOUT_MS = 15e3;
31678
+ }
31679
+ });
31680
+ function lspUnavailable(server, reason) {
31681
+ return { server, ok: false, reason, refs: [], agreement: { both: [], lspOnly: [], staticOnly: [] } };
31682
+ }
31683
+ function columnOfSymbol(root, rel2, line, name2) {
31684
+ try {
31685
+ const lines = readFileSync11(join19(root, rel2), "utf8").split(/\r?\n/);
31686
+ const index = lines[line - 1]?.indexOf(name2) ?? -1;
31687
+ return index < 0 ? 0 : index;
31688
+ } catch {
31689
+ return 0;
31690
+ }
31691
+ }
31692
+ function agreementOf(refs, statik) {
31693
+ const lspFiles = new Set(refs.map((r) => r.file));
31694
+ const staticFiles = /* @__PURE__ */ new Set([
31695
+ ...statik.callSites.map((c2) => c2.file),
31696
+ ...statik.referencingFiles,
31697
+ // Declaration sites are references in the LSP sense (includeDeclaration),
31698
+ // so counting them keeps the two sides comparing the same population.
31699
+ ...statik.defs.map((d) => d.file)
31700
+ ]);
31701
+ const both = [];
31702
+ const lspOnly = [];
31703
+ const staticOnly = [];
31704
+ for (const file of lspFiles) (staticFiles.has(file) ? both : lspOnly).push(file);
31705
+ for (const file of staticFiles) if (!lspFiles.has(file)) staticOnly.push(file);
31706
+ return { both: both.sort(byStr), lspOnly: lspOnly.sort(byStr), staticOnly: staticOnly.sort(byStr) };
31707
+ }
31708
+ async function annotateWithLsp(scan2, name2, statik, session, serverId, languageId) {
31709
+ if (!session.capabilities.references) {
31710
+ return { ...statik, lsp: lspUnavailable(serverId, "server does not provide textDocument/references") };
31711
+ }
31712
+ if (!statik.defs.length) {
31713
+ return { ...statik, lsp: lspUnavailable(serverId, `no declaration of ${name2} to anchor a request on`) };
31714
+ }
31715
+ const seen = /* @__PURE__ */ new Set();
31716
+ const refs = [];
31717
+ try {
31718
+ for (const def of statik.defs) {
31719
+ const text = readTextOrEmpty(scan2.root, def.file);
31720
+ if (!text) continue;
31721
+ session.didOpen(def.file, text, languageId);
31722
+ const character = columnOfSymbol(scan2.root, def.file, def.line, name2);
31723
+ for (const ref of await session.references(def.file, def.line, character)) {
31724
+ const key = `${ref.file}:${ref.line}:${ref.character ?? ""}`;
31725
+ if (seen.has(key)) continue;
31726
+ seen.add(key);
31727
+ refs.push(ref);
31728
+ }
31729
+ }
31730
+ } catch (e) {
31731
+ return {
31732
+ ...statik,
31733
+ lsp: {
31734
+ server: serverId,
31735
+ ok: false,
31736
+ reason: e instanceof Error ? e.message : String(e),
31737
+ refs: refs.sort(refOrder),
31738
+ agreement: agreementOf(refs, statik)
31739
+ }
31740
+ };
31741
+ }
31742
+ refs.sort(refOrder);
31743
+ return { ...statik, lsp: { server: serverId, ok: true, refs, agreement: agreementOf(refs, statik) } };
31744
+ }
31745
+ function refOrder(a, b) {
31746
+ return byStr(a.file, b.file) || a.line - b.line || (a.character ?? 0) - (b.character ?? 0);
31747
+ }
31748
+ function readTextOrEmpty(root, rel2) {
31749
+ try {
31750
+ return readFileSync11(join19(root, rel2), "utf8");
31751
+ } catch {
31752
+ return "";
31753
+ }
31754
+ }
31755
+ var init_refs = __esm({
31756
+ "src/lsp/refs.ts"() {
31757
+ "use strict";
31758
+ init_sort();
31759
+ }
31760
+ });
31761
+ function spawnLspTransport(server, cwd) {
31762
+ let child;
31763
+ try {
31764
+ child = spawn(server.command, server.args ?? [], {
31765
+ cwd,
31766
+ stdio: ["pipe", "pipe", "pipe"],
31767
+ ...server.env ? { env: { ...process.env, ...server.env } } : {}
31768
+ });
31769
+ } catch {
31770
+ return void 0;
31771
+ }
31772
+ let exited = false;
31773
+ const dataListeners = [];
31774
+ const exitListeners = [];
31775
+ const fireExit = (code2) => {
31776
+ if (exited) return;
31777
+ exited = true;
31778
+ for (const listener of exitListeners) listener(code2);
31779
+ };
31780
+ child.on("error", () => fireExit(null));
31781
+ child.on("close", (code2) => fireExit(code2));
31782
+ child.stdout?.on("data", (chunk2) => {
31783
+ for (const listener of dataListeners) listener(chunk2);
31784
+ });
31785
+ child.stderr?.on("data", () => {
31786
+ });
31787
+ return {
31788
+ write(chunk2) {
31789
+ if (exited) return;
31790
+ try {
31791
+ child.stdin?.write(chunk2);
31792
+ } catch {
31793
+ fireExit(null);
31794
+ }
31795
+ },
31796
+ onData(cb) {
31797
+ dataListeners.push(cb);
31798
+ },
31799
+ onExit(cb) {
31800
+ if (exited) cb(null);
31801
+ else exitListeners.push(cb);
31802
+ },
31803
+ close() {
31804
+ try {
31805
+ child.stdin?.end();
31806
+ } catch {
31807
+ }
31808
+ const timer = setTimeout(() => {
31809
+ try {
31810
+ child.kill("SIGKILL");
31811
+ } catch {
31812
+ }
31813
+ }, 2e3);
31814
+ timer.unref?.();
31815
+ child.on("close", () => clearTimeout(timer));
31816
+ }
31817
+ };
31818
+ }
31819
+ var init_spawn = __esm({
31820
+ "src/lsp/spawn.ts"() {
31821
+ "use strict";
31822
+ }
31823
+ });
31824
+ async function lspStatus(scan2, repo, probe = false) {
31825
+ const { path, source } = resolveLspConfigPath(repo);
31826
+ const config = loadLspConfig(repo);
31827
+ if (!config) return { lspVersion: 1, mode: "none", configPath: path ?? null, source, servers: [], unmappedLanguages: [] };
31828
+ const counts = /* @__PURE__ */ new Map();
31829
+ for (const file of scan2.files) counts.set(file.lang, (counts.get(file.lang) ?? 0) + 1);
31830
+ const servers = [];
31831
+ for (const server of config.servers) {
31832
+ const status = {
31833
+ id: server.id,
31834
+ languages: server.languages,
31835
+ command: server.command,
31836
+ onPath: have(server.command),
31837
+ filesInRepo: server.languages.reduce((sum, lang) => sum + (counts.get(lang) ?? 0), 0)
31838
+ };
31839
+ if (probe) {
31840
+ const session = await tryOpen(server, scan2.root);
31841
+ if (session.ok) {
31842
+ status.reachable = true;
31843
+ status.capabilities = session.session.capabilities;
31844
+ await session.session.shutdown();
31845
+ } else {
31846
+ status.reachable = false;
31847
+ status.error = session.reason;
31848
+ }
31849
+ }
31850
+ servers.push(status);
31851
+ }
31852
+ const claimed = new Set(config.servers.flatMap((s) => s.languages));
31853
+ const unmappedLanguages = [...counts.keys()].filter((lang) => !claimed.has(lang) && lang !== "other").sort();
31854
+ return { lspVersion: 1, mode: "configured", configPath: path ?? null, source, servers, unmappedLanguages };
31855
+ }
31856
+ async function tryOpen(server, root) {
31857
+ if (!have(server.command)) return { ok: false, reason: `${server.command} is not on PATH` };
31858
+ const transport = spawnLspTransport(server, root);
31859
+ if (!transport) return { ok: false, reason: `could not start ${server.command}` };
31860
+ try {
31861
+ const session = await openLspSession(transport, {
31862
+ root,
31863
+ timeoutMs: timeoutFor(server),
31864
+ startupTimeoutMs: startupTimeoutFor(server),
31865
+ ...server.initializationOptions !== void 0 ? { initializationOptions: server.initializationOptions } : {}
31866
+ });
31867
+ return { ok: true, session };
31868
+ } catch (e) {
31869
+ transport.close();
31870
+ return { ok: false, reason: e instanceof Error ? e.message : String(e) };
31871
+ }
31872
+ }
31873
+ async function referencesWithLsp(scan2, repo, name2, statik) {
31874
+ let config;
31875
+ try {
31876
+ config = loadLspConfig(repo);
31877
+ } catch (e) {
31878
+ return { ...statik, lsp: lspUnavailable("(config)", e instanceof Error ? e.message : String(e)) };
31879
+ }
31880
+ if (!config) return statik;
31881
+ const lang = statik.defs[0]?.lang;
31882
+ if (!lang) return { ...statik, lsp: lspUnavailable("(none)", `no declaration of ${name2} to anchor a request on`) };
31883
+ const server = serverForLang(config, lang);
31884
+ if (!server) return { ...statik, lsp: lspUnavailable("(none)", `no server configured for ${lang}`) };
31885
+ const opened = await tryOpen(server, scan2.root);
31886
+ if (!opened.ok) return { ...statik, lsp: lspUnavailable(server.id, opened.reason) };
31887
+ try {
31888
+ return await annotateWithLsp(scan2, name2, statik, opened.session, server.id, server.languageId ?? server.languages[0]);
31889
+ } finally {
31890
+ await opened.session.shutdown();
31891
+ }
31892
+ }
31893
+ var init_lsp = __esm({
31894
+ "src/lsp/index.ts"() {
31895
+ "use strict";
31896
+ init_util();
31897
+ init_client();
31898
+ init_config2();
31899
+ init_refs();
31900
+ init_spawn();
31901
+ }
31902
+ });
31104
31903
  function isEntrypointLike(rel2) {
31105
31904
  const base = rel2.split("/").pop();
31106
31905
  const stem = base.split(".")[0].toLowerCase();
@@ -31305,92 +32104,6 @@ var init_rules = __esm({
31305
32104
  ]);
31306
32105
  }
31307
32106
  });
31308
- function changeCoupling(dir, opts = {}) {
31309
- const maxCommitFiles = opts.maxCommitFiles ?? 30;
31310
- const minTogether = opts.minTogether ?? 3;
31311
- const maxPairs = opts.maxPairs ?? 100;
31312
- const range = opts.since ? [`${opts.since}..HEAD`] : [];
31313
- const res = sh("git", ["-C", dir, "-c", "core.quotePath=false", "log", ...range, "--pretty=format:%x1e", "--name-only"]);
31314
- if (!res.ok) return { ok: false, couplings: [] };
31315
- const totals = /* @__PURE__ */ new Map();
31316
- const pairs = /* @__PURE__ */ new Map();
31317
- for (const block of res.stdout.split("")) {
31318
- const files = block.split("\n").map((l) => l.trim()).filter(Boolean);
31319
- if (!files.length || files.length > maxCommitFiles) continue;
31320
- const unique = [...new Set(files)].sort(byStr);
31321
- for (const f of unique) totals.set(f, (totals.get(f) ?? 0) + 1);
31322
- for (let i2 = 0; i2 < unique.length; i2++) {
31323
- for (let j = i2 + 1; j < unique.length; j++) {
31324
- const key = `${unique[i2]}${SEP4}${unique[j]}`;
31325
- pairs.set(key, (pairs.get(key) ?? 0) + 1);
31326
- }
31327
- }
31328
- }
31329
- const out2 = [];
31330
- for (const [key, together] of pairs) {
31331
- if (together < minTogether) continue;
31332
- const [a, b] = key.split(SEP4);
31333
- const totalA = totals.get(a) ?? together;
31334
- const totalB = totals.get(b) ?? together;
31335
- out2.push({ a, b, together, totalA, totalB, strength: Number((together / Math.min(totalA, totalB)).toFixed(3)) });
31336
- }
31337
- out2.sort((x, y) => y.strength - x.strength || y.together - x.together || byStr(x.a, y.a) || byStr(x.b, y.b));
31338
- return { ok: true, couplings: out2.slice(0, maxPairs) };
31339
- }
31340
- function rankHotspots(scan2, churn, top = 20) {
31341
- const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
31342
- const commits = churn.get(f.rel) ?? 0;
31343
- return { rel: f.rel, lines: f.lines, commits, score: Number((commits * Math.log2(f.lines + 1)).toFixed(2)) };
31344
- });
31345
- out2.sort((a, b) => b.score - a.score || b.lines - a.lines || byStr(a.rel, b.rel));
31346
- return out2.slice(0, top);
31347
- }
31348
- var SEP4;
31349
- var init_coupling = __esm({
31350
- "src/coupling.ts"() {
31351
- "use strict";
31352
- init_util();
31353
- init_sort();
31354
- SEP4 = "\0";
31355
- }
31356
- });
31357
- function renderRepoMap(scan2, graph, opts = {}) {
31358
- const budgetChars = (opts.budgetTokens ?? 1024) * CHARS_PER_TOKEN;
31359
- const maxSymbols = opts.maxSymbolsPerFile ?? 8;
31360
- const ranked = [...graph.files].filter((f) => f.fileKind === "code").sort((a, b) => (b.pagerank ?? 0) - (a.pagerank ?? 0) || b.symbols - a.symbols || byStr(a.rel, b.rel));
31361
- const records = new Map(scan2.files.map((f) => [f.rel, f]));
31362
- const header4 = `# repo map \u2014 ${graph.fileCount} files
31363
- `;
31364
- let out2 = header4;
31365
- let files = 0;
31366
- for (const node of ranked) {
31367
- const rec = records.get(node.rel);
31368
- if (!rec) continue;
31369
- const symbols = [...rec.symbols].filter((s) => s.kind !== "reexport" && s.kind !== "reexport-all").sort((a, b) => Number(b.exported) - Number(a.exported) || a.line - b.line).slice(0, maxSymbols);
31370
- let block = `
31371
- ${node.rel}:
31372
- `;
31373
- for (const s of symbols) {
31374
- const sig = (s.signature ?? `${s.kind} ${s.name}`).replace(/\s+/g, " ").trim().slice(0, 120);
31375
- block += ` ${s.line}: ${sig}
31376
- `;
31377
- }
31378
- if (out2.length + block.length > budgetChars) break;
31379
- out2 += block;
31380
- files++;
31381
- }
31382
- return `${out2}
31383
- (${files} of ${ranked.length} code files shown, ~${Math.ceil(out2.length / CHARS_PER_TOKEN)} tokens)
31384
- `;
31385
- }
31386
- var CHARS_PER_TOKEN;
31387
- var init_repomap = __esm({
31388
- "src/repomap.ts"() {
31389
- "use strict";
31390
- init_sort();
31391
- CHARS_PER_TOKEN = 4;
31392
- }
31393
- });
31394
32107
  function findDeadCode(scan2) {
31395
32108
  const callers = callerIndexFor(scan2);
31396
32109
  const refs = symbolRefsFor(scan2);
@@ -31540,7 +32253,7 @@ function negotiateProtocol(requested) {
31540
32253
  function capResponse(text, tool, repo, maxBytes) {
31541
32254
  const bytes = Buffer.byteLength(text, "utf8");
31542
32255
  if (bytes <= maxBytes) return text;
31543
- const artifact = ARTIFACT_FOR[tool] ? join17(repo, INDEX_DIR, ARTIFACT_FOR[tool]) : void 0;
32256
+ const artifact = ARTIFACT_FOR[tool] ? join20(repo, INDEX_DIR, ARTIFACT_FOR[tool]) : void 0;
31544
32257
  return JSON.stringify(
31545
32258
  {
31546
32259
  truncated: true,
@@ -31549,7 +32262,7 @@ function capResponse(text, tool, repo, maxBytes) {
31549
32262
  maxBytes,
31550
32263
  reason: "This response exceeds the configured limit and was withheld rather than sent as an unusable partial payload.",
31551
32264
  narrower: NARROWER[tool] ?? "narrow the request with `scope`, `include`/`exclude`, or a `limit`",
31552
- ...artifact && existsSync7(artifact) ? { artifact, artifactNote: "The full result is already on disk here \u2014 read it directly if you need all of it." } : artifact ? { artifactNote: `Run \`codeindex index --repo ${repo} --out ${join17(repo, INDEX_DIR)}\` to get this as a file.` } : {}
32265
+ ...artifact && existsSync9(artifact) ? { artifact, artifactNote: "The full result is already on disk here \u2014 read it directly if you need all of it." } : artifact ? { artifactNote: `Run \`codeindex index --repo ${repo} --out ${join20(repo, INDEX_DIR)}\` to get this as a file.` } : {}
31553
32266
  },
31554
32267
  null,
31555
32268
  2
@@ -31580,7 +32293,7 @@ var RICH_TOOLS_SINCE;
31580
32293
  var DEFAULT_MAX_RESPONSE_BYTES;
31581
32294
  var NARROWER;
31582
32295
  var ARTIFACT_FOR;
31583
- var init_protocol = __esm({
32296
+ var init_protocol2 = __esm({
31584
32297
  "src/mcp/protocol.ts"() {
31585
32298
  "use strict";
31586
32299
  init_preload();
@@ -31609,11 +32322,27 @@ function annotationsFor(name2) {
31609
32322
  openWorldHint: meta2.openWorld === true
31610
32323
  };
31611
32324
  }
31612
- function toolsFor(defaultRepo, protocolVersion = PROTOCOL_VERSIONS[0]) {
32325
+ function profileNames() {
32326
+ return ["all", ...Object.keys(TOOL_PROFILES).sort()];
32327
+ }
32328
+ function toolsInProfiles(spec) {
32329
+ const names = spec.split(",").map((s) => s.trim()).filter(Boolean);
32330
+ const out2 = /* @__PURE__ */ new Set();
32331
+ for (const name2 of names) {
32332
+ if (name2 === "all") return new Set(TOOLS.map((t2) => t2.name));
32333
+ const profile = TOOL_PROFILES[name2];
32334
+ if (!profile) throw new Error(`unknown tool profile "${name2}" \u2014 one of: ${profileNames().join(", ")}`);
32335
+ for (const tool of profile) out2.add(tool);
32336
+ }
32337
+ return out2;
32338
+ }
32339
+ function toolsFor(defaultRepo, protocolVersion = PROTOCOL_VERSIONS[0], profile) {
31613
32340
  const withAnnotations = protocolVersion >= ANNOTATIONS_SINCE;
31614
32341
  const withRich = protocolVersion >= RICH_TOOLS_SINCE;
31615
- if (!defaultRepo && !withAnnotations && !withRich) return TOOLS;
31616
- return TOOLS.map((t2) => ({
32342
+ const allowed = profile ? toolsInProfiles(profile) : void 0;
32343
+ const TOOLS_ = allowed ? TOOLS.filter((t2) => allowed.has(t2.name)) : TOOLS;
32344
+ if (!defaultRepo && !withAnnotations && !withRich) return TOOLS_;
32345
+ return TOOLS_.map((t2) => ({
31617
32346
  ...t2,
31618
32347
  ...withRich && TOOL_META[t2.name] ? { title: TOOL_META[t2.name].title } : {},
31619
32348
  ...withRich && OUTPUT_SCHEMAS[t2.name] ? { outputSchema: OUTPUT_SCHEMAS[t2.name] } : {},
@@ -31638,10 +32367,11 @@ var strArr;
31638
32367
  var anyObj;
31639
32368
  var OUTPUT_SCHEMAS;
31640
32369
  var TOOL_META;
32370
+ var TOOL_PROFILES;
31641
32371
  var init_tools = __esm({
31642
32372
  "src/mcp/tools.ts"() {
31643
32373
  "use strict";
31644
- init_protocol();
32374
+ init_protocol2();
31645
32375
  repoProp = { repo: { type: "string", description: "Absolute path to the repository root" } };
31646
32376
  scopeProps = {
31647
32377
  scope: { type: "string", description: "Restrict to one directory (repo-relative)" },
@@ -31709,7 +32439,7 @@ var init_tools = __esm({
31709
32439
  },
31710
32440
  {
31711
32441
  name: "find_symbol",
31712
- description: "Find symbol declarations by name or name path ('Class/method' matches a method inside Class). Options: substring matching, includeBody to return the declaration's source. Exact-name matches rank first.",
32442
+ description: `Find symbol declarations by name or name path ('Class/method' matches a method inside Class). Each match carries its COMPLETE SIGNATURE (parameters and return type) by default, because "what shape is it" is the question that follows "where is it" almost every time and one round trip beats two. Options: substring matching, includeBody for the declaration's source, concise to drop everything but name/kind/file/line when you genuinely only want a location. Exact-name matches rank first.`,
31713
32443
  inputSchema: {
31714
32444
  type: "object",
31715
32445
  properties: {
@@ -31717,6 +32447,10 @@ var init_tools = __esm({
31717
32447
  namePath: { type: "string", description: "Symbol name or Parent/child path" },
31718
32448
  substring: { type: "boolean" },
31719
32449
  includeBody: { type: "boolean" },
32450
+ concise: {
32451
+ type: "boolean",
32452
+ description: "Return only name/kind/file/line \u2014 drop the signature, line span, visibility and language. Roughly 2.5x smaller; use it when you are resolving a path and nothing more (default false)."
32453
+ },
31720
32454
  maxResults: { type: "number", description: "Cap matches (default 50)" }
31721
32455
  },
31722
32456
  required: ["repo", "namePath"]
@@ -31727,10 +32461,42 @@ var init_tools = __esm({
31727
32461
  description: "Who references a symbol? Three labeled tiers: defs (declarations), callSites (line-precise, import-corroborated call bindings), referencingFiles (file-level identifier/doc mentions \u2014 may include homonyms). Confidence decreases across tiers; the labels let you decide what to trust.",
31728
32462
  inputSchema: {
31729
32463
  type: "object",
31730
- properties: { ...repoProp, name: { type: "string", description: "Symbol name" } },
32464
+ properties: {
32465
+ ...repoProp,
32466
+ name: { type: "string", description: "Symbol name" },
32467
+ lsp: {
32468
+ type: "boolean",
32469
+ description: "Also ask a configured language server (see lsp_status) and append an `lsp` block: its references plus an `agreement` matrix (both / lspOnly / staticOnly). The three static tiers are unchanged either way. `staticOnly` is where the homonyms are. No config, no binary, a crash or a timeout all degrade to the static answer with a stated reason (default false)."
32470
+ }
32471
+ },
31731
32472
  required: ["repo", "name"]
31732
32473
  }
31733
32474
  },
32475
+ {
32476
+ name: "lsp_status",
32477
+ description: "Is the optional LSP tier configured, and would it answer? Reports the config path and its source, each server with whether its command is on PATH and how many files in this repo it claims, and the languages no server covers. Opt-in by asset: the tier is active only when <repo>/.codeindex/lsp.json exists (or CODEINDEX_LSP_CONFIG points at one). `probe: true` additionally starts each server to read the capabilities it really advertises. The tier never touches graph.json/symbols.json \u2014 it annotates query answers only.",
32478
+ inputSchema: {
32479
+ type: "object",
32480
+ properties: {
32481
+ ...repoProp,
32482
+ probe: { type: "boolean", description: "Start each server and read its real capabilities (default false: no spawn)" }
32483
+ },
32484
+ required: ["repo"]
32485
+ }
32486
+ },
32487
+ {
32488
+ name: "onboard",
32489
+ description: "One call that says what this repository IS: its own tagline, size and language mix, monorepo layout when there is one, a token-budgeted map of the highest-PageRank files with their key signatures, and where git says work concentrates. Composes scan_summary + workspaces + repo_map + hotspots so the first four round trips of a session become one, and persists the result as the `onboarding` memory (read_memory) so the next session does not repeat them. Set remember:false to skip the write.",
32490
+ inputSchema: {
32491
+ type: "object",
32492
+ properties: {
32493
+ ...repoProp,
32494
+ budgetTokens: { type: "number", description: "Token budget for the key-files section (default 900)" },
32495
+ remember: { type: "boolean", description: "Persist the brief as the `onboarding` memory (default true)" }
32496
+ },
32497
+ required: ["repo"]
32498
+ }
32499
+ },
31734
32500
  {
31735
32501
  name: "repo_map",
31736
32502
  description: "Token-budgeted map of the repository: the highest-PageRank files with their key exported signatures, deterministically rendered to fit `budgetTokens` (default 1024). The densest single read to understand an unfamiliar codebase.",
@@ -31903,6 +32669,14 @@ var init_tools = __esm({
31903
32669
  type: "string",
31904
32670
  description: `Structural prior: "graph" multiplies the lexical score by the file's PageRank over the resolved import graph; "lexical" (default) scores on text alone. Unproven on the judged corpus \u2014 see SearchOptions.rank.`
31905
32671
  },
32672
+ exact: {
32673
+ type: "boolean",
32674
+ description: "Drop results that carry no verbatim query-term match \u2014 the ones the stem/trigram bridge produced (default false)."
32675
+ },
32676
+ explain: {
32677
+ type: "boolean",
32678
+ description: "Wrap the response as `{ results, explain }` with the query verdict (default false = bare array). See explain_search."
32679
+ },
31906
32680
  semantic: {
31907
32681
  type: "boolean",
31908
32682
  description: 'RRF-fuse an embedding tier with lexical (default false). Precedence: the HTTP endpoint (CODEINDEX_EMBED_ENDPOINT) if set, else a local static model. The response reports the effective tier as a top-level `tier` field ("endpoint"/"static" on success, "lexical" plus `degradedReason` when neither is available/reachable) instead of degrading silently \u2014 see embed_status.'
@@ -31911,6 +32685,22 @@ var init_tools = __esm({
31911
32685
  required: ["repo", "query"]
31912
32686
  }
31913
32687
  },
32688
+ {
32689
+ name: "explain_search",
32690
+ description: 'Search, and say whether the query actually found anything. Returns `{ results, explain }` where explain.verdict is "match" (a verbatim term matched), "weak" (results exist but rest on a near match, or the identifier you asked for has document frequency 0) or "none". Use this instead of `search` whenever an empty-feeling or surprising result matters: a query for an identifier that is NOT in the indexed tree still returns confident-looking rows built from its subtokens \u2014 searching "nullGipStep7" in a repo that only has "nullGipStep2" ranks files matching "null" and "gip" \u2014 and only the verdict distinguishes that from a real hit. Also names the terms dropped as stopwords, the terms that exist nowhere, and what each near match bridged to.',
32691
+ inputSchema: {
32692
+ type: "object",
32693
+ properties: {
32694
+ ...repoProp,
32695
+ ...scopeProps,
32696
+ query: { type: "string", description: "Natural-language or identifier query" },
32697
+ limit: { type: "number", description: "Max results (default 20)" },
32698
+ fuzzy: { type: "boolean", description: "Stem/trigram fallback for zero-document-frequency terms (default true)" },
32699
+ exact: { type: "boolean", description: "Drop results carrying no verbatim term match (default false)" }
32700
+ },
32701
+ required: ["repo", "query"]
32702
+ }
32703
+ },
31914
32704
  {
31915
32705
  name: "embed_status",
31916
32706
  description: "Report the embedding tier: the effective mode (none/static/endpoint; endpoint > static model), the resolved model (opt-in, never shipped in the package) with its modelId/dim, EMBED_VERSION, and the configured HTTP endpoint with its reachability. Use to check whether `search` with semantic:true will fuse embeddings or degrade to lexical.",
@@ -32050,6 +32840,45 @@ var init_tools = __esm({
32050
32840
  },
32051
32841
  required: ["defs", "callSites", "referencingFiles"]
32052
32842
  },
32843
+ lsp_status: {
32844
+ type: "object",
32845
+ properties: {
32846
+ lspVersion: { type: "number" },
32847
+ mode: { type: "string", enum: ["none", "configured"] },
32848
+ configPath: { type: ["string", "null"] },
32849
+ source: { type: "string", enum: ["env", "repo", "cwd", "none"] },
32850
+ servers: { type: "array", items: anyObj },
32851
+ unmappedLanguages: strArr
32852
+ },
32853
+ required: ["lspVersion", "mode", "source", "servers", "unmappedLanguages"]
32854
+ },
32855
+ onboard: {
32856
+ type: "object",
32857
+ properties: { brief: { type: "string" }, memory: { type: "string" } },
32858
+ required: ["brief"]
32859
+ },
32860
+ explain_search: {
32861
+ type: "object",
32862
+ properties: {
32863
+ results: { type: "array", items: anyObj },
32864
+ explain: {
32865
+ type: "object",
32866
+ properties: {
32867
+ query: { type: "string" },
32868
+ terms: { type: "array", items: anyObj },
32869
+ droppedStopwords: strArr,
32870
+ unresolvedTerms: strArr,
32871
+ wholeIdentifier: anyObj,
32872
+ verdict: { type: "string", enum: ["match", "weak", "none"] },
32873
+ note: { type: "string" },
32874
+ bridgedOnlyResults: { type: "number" },
32875
+ resultCount: { type: "number" }
32876
+ },
32877
+ required: ["query", "terms", "droppedStopwords", "unresolvedTerms", "verdict", "bridgedOnlyResults", "resultCount"]
32878
+ }
32879
+ },
32880
+ required: ["results", "explain"]
32881
+ },
32053
32882
  hotspots: {
32054
32883
  type: "object",
32055
32884
  properties: { churnOk: { type: "boolean" }, hotspots: { type: "array", items: anyObj } },
@@ -32113,6 +32942,7 @@ var init_tools = __esm({
32113
32942
  find_symbol: { title: "Find symbol" },
32114
32943
  find_references: { title: "Find references" },
32115
32944
  repo_map: { title: "Repository map" },
32945
+ onboard: { title: "Project brief", write: true, destructive: false, idempotent: true },
32116
32946
  hotspots: { title: "Hotspots" },
32117
32947
  coupling: { title: "Change coupling" },
32118
32948
  replace_symbol_body: { title: "Replace symbol body", write: true, destructive: true, idempotent: true },
@@ -32128,12 +32958,29 @@ var init_tools = __esm({
32128
32958
  mermaid: { title: "Mermaid module diagram" },
32129
32959
  grep: { title: "Grep file contents" },
32130
32960
  search: { title: "Lexical search", openWorld: true },
32961
+ explain_search: { title: "Search with a verdict", openWorld: true },
32962
+ // openWorld: it spawns a process the user configured, whose answer this
32963
+ // engine does not determine — the same honesty `search` and `embed_status`
32964
+ // already carry for reaching outside their own artifacts.
32965
+ lsp_status: { title: "LSP tier status", openWorld: true },
32131
32966
  embed_status: { title: "Embedding tier status", openWorld: true },
32132
32967
  type_hierarchy: { title: "Type hierarchy" },
32133
32968
  implementations: { title: "Implementations" },
32134
32969
  call_graph: { title: "Call graph neighborhood" },
32135
32970
  check_rules: { title: "Check architecture rules" }
32136
32971
  };
32972
+ TOOL_PROFILES = {
32973
+ // Land in an unfamiliar repository and get your bearings.
32974
+ orient: ["scan_summary", "repo_map", "onboard", "workspaces", "mermaid", "read_memory", "list_memories"],
32975
+ // Locate a thing.
32976
+ find: ["search", "explain_search", "grep", "find_symbol", "symbols", "symbols_overview"],
32977
+ // Decide whether changing it is safe.
32978
+ impact: ["find_references", "callers", "call_graph", "dead_code", "type_hierarchy", "implementations", "lsp_status"],
32979
+ // Change it.
32980
+ edit: ["find_symbol", "symbols_overview", "replace_symbol_body", "insert_after_symbol", "insert_before_symbol"],
32981
+ // Where the work and the risk concentrate.
32982
+ risk: ["hotspots", "churn", "coupling", "complexity", "check_rules", "duplicated_literals", "dead_code"]
32983
+ };
32137
32984
  }
32138
32985
  });
32139
32986
  function scanFingerprint(scan2) {
@@ -32149,7 +32996,7 @@ async function memoizedEmbeddingIndex(key, build) {
32149
32996
  function memoizedEmbedModel(modelDir) {
32150
32997
  let stat;
32151
32998
  try {
32152
- stat = statSync5(join18(modelDir, "model.json"));
32999
+ stat = statSync5(join21(modelDir, "model.json"));
32153
33000
  } catch {
32154
33001
  return void 0;
32155
33002
  }
@@ -32264,6 +33111,7 @@ __export(mcp_exports, {
32264
33111
  PROTOCOL_VERSIONS: () => PROTOCOL_VERSIONS,
32265
33112
  TOOLS: () => TOOLS,
32266
33113
  TOOL_META: () => TOOL_META,
33114
+ TOOL_PROFILES: () => TOOL_PROFILES,
32267
33115
  annotationsFor: () => annotationsFor,
32268
33116
  capResponse: () => capResponse,
32269
33117
  getArtifacts: () => getArtifacts,
@@ -32272,12 +33120,14 @@ __export(mcp_exports, {
32272
33120
  memoizedEmbedModel: () => memoizedEmbedModel,
32273
33121
  memoizedEmbeddingIndex: () => memoizedEmbeddingIndex,
32274
33122
  negotiateProtocol: () => negotiateProtocol,
33123
+ profileNames: () => profileNames,
32275
33124
  resourceLinkFor: () => resourceLinkFor,
32276
33125
  runMcpServer: () => runMcpServer,
32277
33126
  scanFingerprint: () => scanFingerprint,
32278
33127
  structuredContentFor: () => structuredContentFor,
32279
33128
  toCacheMap: () => toCacheMap,
32280
33129
  toolsFor: () => toolsFor,
33130
+ toolsInProfiles: () => toolsInProfiles,
32281
33131
  validateArgs: () => validateArgs,
32282
33132
  warmGrammarsForRepo: () => warmGrammarsForRepo,
32283
33133
  warmGrammarsForWalk: () => warmGrammarsForWalk
@@ -32358,6 +33208,7 @@ async function callTool(name2, args2, defaultRepo) {
32358
33208
  const matches = findSymbol(getScan(repo, scanOpts, walked), namePath, {
32359
33209
  substring: args2.substring === true,
32360
33210
  includeBody: args2.includeBody === true,
33211
+ concise: args2.concise === true,
32361
33212
  maxResults: num(args2.maxResults)
32362
33213
  });
32363
33214
  return JSON.stringify(matches, null, 2);
@@ -32365,7 +33216,13 @@ async function callTool(name2, args2, defaultRepo) {
32365
33216
  if (name2 === "find_references") {
32366
33217
  const symName = str(args2.name);
32367
33218
  if (!symName) throw new Error("`name` is required");
32368
- return JSON.stringify(findReferences(getScan(repo, scanOpts, walked), symName), null, 2);
33219
+ const scan2 = getScan(repo, scanOpts, walked);
33220
+ const statik = findReferences(scan2, symName);
33221
+ if (args2.lsp === true) return JSON.stringify(await referencesWithLsp(scan2, repo, symName, statik), null, 2);
33222
+ return JSON.stringify(statik, null, 2);
33223
+ }
33224
+ if (name2 === "lsp_status") {
33225
+ return JSON.stringify(await lspStatus(getScan(repo, scanOpts, walked), repo, args2.probe === true), null, 2);
32369
33226
  }
32370
33227
  if (name2 === "replace_symbol_body" || name2 === "insert_after_symbol" || name2 === "insert_before_symbol") {
32371
33228
  const namePath = str(args2.namePath);
@@ -32436,6 +33293,18 @@ async function callTool(name2, args2, defaultRepo) {
32436
33293
  const { graph } = getArtifacts(repo, scanOpts, walked);
32437
33294
  return renderMermaid(graph, { module: str(args2.module), maxEdges: num(args2.maxEdges) });
32438
33295
  }
33296
+ if (name2 === "onboard") {
33297
+ const scan2 = getScan(repo, scanOpts, walked);
33298
+ const { graph } = getArtifacts(repo, scanOpts, walked);
33299
+ return JSON.stringify(
33300
+ onboardBrief(scan2, graph, {
33301
+ ...typeof args2.budgetTokens === "number" ? { budgetTokens: args2.budgetTokens } : {},
33302
+ ...args2.remember === false ? { remember: false } : {}
33303
+ }),
33304
+ null,
33305
+ 2
33306
+ );
33307
+ }
32439
33308
  if (name2 === "repo_map") {
32440
33309
  const { scan: scan2, graph } = getArtifacts(repo, scanOpts, walked);
32441
33310
  return renderRepoMap(scan2, graph, { budgetTokens: typeof args2.budgetTokens === "number" ? args2.budgetTokens : void 0 });
@@ -32467,18 +33336,19 @@ async function callTool(name2, args2, defaultRepo) {
32467
33336
  const scan2 = getScan(repo, scanOpts, walked);
32468
33337
  const limit = typeof args2.limit === "number" ? args2.limit : void 0;
32469
33338
  const fuzzy = typeof args2.fuzzy === "boolean" ? args2.fuzzy : void 0;
33339
+ const exactOpt = args2.exact === true ? { exact: true } : {};
32470
33340
  if (args2.semantic === true) {
32471
33341
  const endpoint = resolveEmbedEndpoint();
32472
33342
  if (endpoint) {
32473
33343
  try {
32474
33344
  const index = await memoizedEmbeddingIndex({ mode: "endpoint", identity: endpoint, scan: scan2 }, () => buildEndpointIndex(scan2));
32475
33345
  const queryVec = await encodeQueryViaEndpoint(query);
32476
- const results2 = searchSemantic(scan2, query, index, { queryVec, limit, fuzzy });
32477
- return JSON.stringify({ results: results2, tier: "endpoint" }, null, 2);
33346
+ const results3 = searchSemantic(scan2, query, index, { queryVec, limit, fuzzy });
33347
+ return JSON.stringify({ results: results3, tier: "endpoint" }, null, 2);
32478
33348
  } catch (e) {
32479
- const results2 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
33349
+ const results3 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
32480
33350
  return JSON.stringify(
32481
- { results: results2, tier: "lexical", degradedReason: `embedding endpoint failed: ${errMessage(e)}` },
33351
+ { results: results3, tier: "lexical", degradedReason: `embedding endpoint failed: ${errMessage(e)}` },
32482
33352
  null,
32483
33353
  2
32484
33354
  );
@@ -32491,17 +33361,32 @@ async function callTool(name2, args2, defaultRepo) {
32491
33361
  { mode: "static", identity: `${modelDir}#${model.modelId}`, scan: scan2 },
32492
33362
  () => buildEmbeddingIndex(scan2, model)
32493
33363
  );
32494
- const results2 = searchSemantic(scan2, query, index, { model, limit, fuzzy });
32495
- return JSON.stringify({ results: results2, tier: "static" }, null, 2);
33364
+ const results3 = searchSemantic(scan2, query, index, { model, limit, fuzzy });
33365
+ return JSON.stringify({ results: results3, tier: "static" }, null, 2);
32496
33366
  }
32497
- const results = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
33367
+ const results2 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
32498
33368
  return JSON.stringify(
32499
- { results, tier: "lexical", degradedReason: "no embedding endpoint or static model configured \u2014 see embed_status" },
33369
+ { results: results2, tier: "lexical", degradedReason: "no embedding endpoint or static model configured \u2014 see embed_status" },
32500
33370
  null,
32501
33371
  2
32502
33372
  );
32503
33373
  }
32504
- return JSON.stringify(searchIndex(scan2, query, { limit, fuzzy, ...rankOpt }), null, 2);
33374
+ const { results, explain } = explainQuery(scan2, query, { limit, fuzzy, ...exactOpt, ...rankOpt });
33375
+ return JSON.stringify(args2.explain === true ? { results, explain } : results, null, 2);
33376
+ }
33377
+ if (name2 === "explain_search") {
33378
+ const query = str(args2.query);
33379
+ if (!query) throw new Error("`query` is required");
33380
+ const scan2 = getScan(repo, scanOpts, walked);
33381
+ const limit = typeof args2.limit === "number" ? args2.limit : void 0;
33382
+ const fuzzy = typeof args2.fuzzy === "boolean" ? args2.fuzzy : void 0;
33383
+ const { results, explain } = explainQuery(scan2, query, {
33384
+ limit,
33385
+ fuzzy,
33386
+ ...args2.exact === true ? { exact: true } : {},
33387
+ ...rankOpt
33388
+ });
33389
+ return JSON.stringify({ results, explain }, null, 2);
32505
33390
  }
32506
33391
  if (name2 === "embed_status") {
32507
33392
  const modelDir = resolveEmbedModelDir(repo);
@@ -32552,9 +33437,9 @@ async function callTool(name2, args2, defaultRepo) {
32552
33437
  const configPath = str(args2.configPath);
32553
33438
  let payload = args2.rules;
32554
33439
  if (payload === void 0 && configPath) {
32555
- const abs = isAbsolute(configPath) ? configPath : join19(repo, configPath);
33440
+ const abs = isAbsolute(configPath) ? configPath : join22(repo, configPath);
32556
33441
  try {
32557
- payload = JSON.parse(readFileSync9(abs, "utf8"));
33442
+ payload = JSON.parse(readFileSync12(abs, "utf8"));
32558
33443
  } catch (e) {
32559
33444
  throw new Error(`cannot read rules from ${abs}: ${errMessage(e)}`);
32560
33445
  }
@@ -32572,7 +33457,7 @@ async function runMcpServer(opts = {}) {
32572
33457
  version: opts.serverInfo?.version ?? ENGINE_VERSION
32573
33458
  };
32574
33459
  let protocolVersion = PROTOCOL_VERSIONS[0];
32575
- let tools = toolsFor(opts.defaultRepo, protocolVersion);
33460
+ let tools = toolsFor(opts.defaultRepo, protocolVersion, opts.profile);
32576
33461
  const send = (msg) => {
32577
33462
  process.stdout.write(JSON.stringify({ jsonrpc: "2.0", ...msg }) + "\n");
32578
33463
  };
@@ -32595,7 +33480,7 @@ async function runMcpServer(opts = {}) {
32595
33480
  try {
32596
33481
  if (req.method === "initialize") {
32597
33482
  protocolVersion = negotiateProtocol(req.params?.protocolVersion);
32598
- tools = toolsFor(opts.defaultRepo, protocolVersion);
33483
+ tools = toolsFor(opts.defaultRepo, protocolVersion, opts.profile);
32599
33484
  send({
32600
33485
  id: req.id,
32601
33486
  result: {
@@ -32665,6 +33550,8 @@ var init_mcp = __esm({
32665
33550
  init_complexity();
32666
33551
  init_viz();
32667
33552
  init_query();
33553
+ init_lsp();
33554
+ init_onboard();
32668
33555
  init_edit();
32669
33556
  init_memory();
32670
33557
  init_bm25();
@@ -32675,10 +33562,10 @@ var init_mcp = __esm({
32675
33562
  init_endpoint();
32676
33563
  init_walk();
32677
33564
  init_tools();
32678
- init_protocol();
33565
+ init_protocol2();
32679
33566
  init_session();
32680
33567
  init_tools();
32681
- init_protocol();
33568
+ init_protocol2();
32682
33569
  init_session();
32683
33570
  SCANLESS_TOOLS = /* @__PURE__ */ new Set([
32684
33571
  "workspaces",
@@ -32881,7 +33768,7 @@ runExtractWorker(workerData.input, (o) => parentPort.postMessage(o)).catch((e) =
32881
33768
  try {
32882
33769
  const outputs = await Promise.all(
32883
33770
  shards.map(
32884
- (jobsForShard) => new Promise((resolve32, reject) => {
33771
+ (jobsForShard) => new Promise((resolve52, reject) => {
32885
33772
  const w = new Worker(bootstrap, {
32886
33773
  eval: true,
32887
33774
  workerData: { input: { jobs: jobsForShard, grammarKeys: wanted, maxCallsPerFile: opts.maxCallsPerFile } }
@@ -32895,7 +33782,7 @@ runExtractWorker(workerData.input, (o) => parentPort.postMessage(o)).catch((e) =
32895
33782
  fn();
32896
33783
  };
32897
33784
  w.once("message", (m) => {
32898
- settle(() => resolve32(m));
33785
+ settle(() => resolve52(m));
32899
33786
  void w.terminate();
32900
33787
  });
32901
33788
  w.once("error", (e) => settle(() => reject(e)));
@@ -33647,6 +34534,13 @@ init_encode();
33647
34534
  init_embed();
33648
34535
  init_search();
33649
34536
  init_endpoint();
34537
+ init_onboard();
34538
+ init_lsp();
34539
+ init_config2();
34540
+ init_client();
34541
+ init_spawn();
34542
+ init_protocol();
34543
+ init_refs();
33650
34544
  init_rules();
33651
34545
  init_coupling();
33652
34546
  init_repomap();
@@ -34065,6 +34959,8 @@ init_embed();
34065
34959
  init_search();
34066
34960
  init_endpoint();
34067
34961
  init_util();
34962
+ init_lsp();
34963
+ init_tools();
34068
34964
  var HELP = `codeindex engine v${ENGINE_VERSION} \u2014 deterministic repo indexing
34069
34965
 
34070
34966
  Usage: engine.mjs <command> [flags]
@@ -34098,6 +34994,15 @@ Commands:
34098
34994
  the source with CODEINDEX_EMBED_URL
34099
34995
  embed serve Print (or --run) the docker command that starts the
34100
34996
  containerized embedding server (rich tier)
34997
+ lsp Optional LSP tier (opt-in by asset \u2014 the tier is active only when
34998
+ <repo>/.codeindex/lsp.json exists, or CODEINDEX_LSP_CONFIG points
34999
+ at one). It annotates QUERY answers only and never touches
35000
+ graph.json/symbols.json:
35001
+ lsp status Config path and source, each server with whether
35002
+ its command is on PATH and how many files it
35003
+ claims, and the languages nothing covers (JSON).
35004
+ --probe also starts each server to read the
35005
+ capabilities it really advertises
34101
35006
  grammars Tree-sitter wasm grammars (optional AST tier; regex without them).
34102
35007
  Two tiers: CORE ships with the bundle; EXTENDED (kotlin, elixir,
34103
35008
  zig, solidity, hcl/terraform) arrives only via \`grammars pull\`.
@@ -34143,17 +35048,22 @@ Commands:
34143
35048
  and exits 0, or exits 1 when it has no opinion (run the original).
34144
35049
  Deliberately conservative \u2014 any shell metacharacter or unknown
34145
35050
  flag refuses the rewrite
34146
- mcp Run as an MCP server over stdio (30 tools: scan_summary, graph,
35051
+ mcp Run as an MCP server over stdio (33 tools: scan_summary, graph,
34147
35052
  symbols, callers, workspaces, churn, symbols_overview,
34148
- find_symbol, find_references, repo_map, hotspots, coupling,
34149
- dead_code, complexity, mermaid, grep, search, embed_status,
34150
- check_rules, the memory quartet and the three symbolic-edit
34151
- writes). Flags: --repo <dir> pins ONE repository so the per-tool
34152
- repo argument becomes optional (an explicit per-call repo still
34153
- wins); --server-name <name> overrides the announced serverInfo;
34154
- --max-response-bytes <n> caps a single tool response (default 1e6;
34155
- a response under the cap is byte-identical, one over it is
34156
- replaced by an actionable notice instead of an unusable blob)
35053
+ find_symbol, find_references, lsp_status, onboard, repo_map,
35054
+ hotspots, coupling, dead_code, complexity, mermaid, grep, search,
35055
+ explain_search, embed_status, check_rules, the memory quartet and
35056
+ the three symbolic-edit writes). Flags: --repo <dir> pins ONE
35057
+ repository so the per-tool repo argument becomes optional (an
35058
+ explicit per-call repo still wins); --server-name <name> overrides
35059
+ the announced serverInfo; --max-response-bytes <n> caps a single
35060
+ tool response (default 1e6; a response under the cap is
35061
+ byte-identical, one over it is replaced by an actionable notice
35062
+ instead of an unusable blob); --tools <profile[,profile]>
35063
+ advertises a named subset (all | orient | find | impact | edit |
35064
+ risk, default all) \u2014 every advertised tool's schema costs an agent
35065
+ context on EVERY turn, and a tool left out is still answerable
35066
+ when called by name
34157
35067
  version Print the engine version
34158
35068
 
34159
35069
  Flags (accepted before OR after the subcommand: '--repo X scan' and
@@ -34188,10 +35098,16 @@ Flags (accepted before OR after the subcommand: '--repo X scan' and
34188
35098
  --limit <n> Max results for \`search\` (default 20)
34189
35099
  --no-fuzzy \`search\`: disable trigram fuzzy fallback for query terms
34190
35100
  with zero document frequency (default: enabled)
35101
+ --exact \`search\`: drop results that carry no verbatim term match
35102
+ (the ones the stem/trigram bridge produced)
35103
+ --explain \`search\`: emit { results, explain } \u2014 which terms matched,
35104
+ which bridged, and whether the query really found anything
34191
35105
  --semantic \`search\`: RRF-fuse an embedding tier with lexical \u2014 the
34192
35106
  HTTP endpoint if CODEINDEX_EMBED_ENDPOINT is set, else a
34193
35107
  local static model (lexical-only when neither is available)
34194
35108
  --run \`embed serve\`: run the docker command instead of printing it
35109
+ --probe \`lsp status\`: start each server and read the capabilities
35110
+ it really advertises (default: no spawn)
34195
35111
  --recall \`callers\`: recall-oriented binding (issue #7) \u2014 relaxes
34196
35112
  the JS/TS import gate to unique repo-wide names and labels
34197
35113
  each site corroborated|unique-name
@@ -34217,10 +35133,10 @@ function parseFlags(args2) {
34217
35133
  if (!Number.isFinite(n) || n <= 0) throw new Error(`${a} expects a positive number, got "${raw}"`);
34218
35134
  return n;
34219
35135
  };
34220
- if (a === "--repo") flags2.repo = resolve2(next());
35136
+ if (a === "--repo") flags2.repo = resolve4(next());
34221
35137
  else if (a === "--out") {
34222
35138
  const v = next();
34223
- flags2.out = v === "-" ? "-" : resolve2(v);
35139
+ flags2.out = v === "-" ? "-" : resolve4(v);
34224
35140
  } else if (a === "--project-root") flags2.projectRoot = next();
34225
35141
  else if (a === "--include") flags2.include.push(next());
34226
35142
  else if (a === "--exclude") flags2.exclude.push(next());
@@ -34245,12 +35161,15 @@ function parseFlags(args2) {
34245
35161
  if (!Number.isInteger(n) || n < 0) throw new Error(`--workers expects a non-negative integer, got "${raw}"`);
34246
35162
  flags2.workers = n;
34247
35163
  } else if (a === "--since") flags2.since = next();
34248
- else if (a === "--config") flags2.config = resolve2(next());
35164
+ else if (a === "--config") flags2.config = resolve4(next());
34249
35165
  else if (a === "--limit") flags2.limit = num22();
34250
35166
  else if (a === "--no-fuzzy") flags2.fuzzy = false;
35167
+ else if (a === "--exact") flags2.exact = true;
35168
+ else if (a === "--explain") flags2.explain = true;
34251
35169
  else if (a === "--semantic") flags2.semantic = true;
34252
35170
  else if (a === "--recall") flags2.recall = true;
34253
35171
  else if (a === "--run") flags2.run = true;
35172
+ else if (a === "--probe") flags2.probe = true;
34254
35173
  else if (a === "--base") flags2.base = next();
34255
35174
  else if (a === "--staged") flags2.staged = true;
34256
35175
  else if (a === "--depth") flags2.depth = num22();
@@ -34294,12 +35213,13 @@ function parseMcpFlags(argv) {
34294
35213
  let defaultRepo;
34295
35214
  let name2;
34296
35215
  let maxResponseBytes;
35216
+ let profile;
34297
35217
  for (let i2 = 0; i2 < argv.length; i2++) {
34298
35218
  const a = argv[i2];
34299
35219
  if (a === "--repo") {
34300
35220
  const v = argv[++i2];
34301
35221
  if (!v) throw new Error("--repo requires a directory");
34302
- defaultRepo = resolve2(v);
35222
+ defaultRepo = resolve4(v);
34303
35223
  } else if (a === "--server-name") {
34304
35224
  const v = argv[++i2];
34305
35225
  if (!v) throw new Error("--server-name requires a value");
@@ -34309,12 +35229,17 @@ function parseMcpFlags(argv) {
34309
35229
  const n = Number(v);
34310
35230
  if (!v || !Number.isFinite(n) || n <= 0) throw new Error("--max-response-bytes requires a positive number");
34311
35231
  maxResponseBytes = n;
35232
+ } else if (a === "--tools") {
35233
+ const v = argv[++i2];
35234
+ if (!v) throw new Error(`--tools requires a profile: ${profileNames().join(", ")}`);
35235
+ toolsInProfiles(v);
35236
+ profile = v === "all" ? void 0 : v;
34312
35237
  } else {
34313
35238
  throw new Error(`unknown flag for \`mcp\`: ${a}`);
34314
35239
  }
34315
35240
  }
34316
- if (defaultRepo && !existsSync8(defaultRepo)) throw new Error(`--repo path does not exist: ${defaultRepo}`);
34317
- return { defaultRepo, serverInfo: name2 ? { name: name2 } : void 0, maxResponseBytes };
35241
+ if (defaultRepo && !existsSync10(defaultRepo)) throw new Error(`--repo path does not exist: ${defaultRepo}`);
35242
+ return { defaultRepo, serverInfo: name2 ? { name: name2 } : void 0, maxResponseBytes, profile };
34318
35243
  }
34319
35244
  var VALUE_FLAGS = /* @__PURE__ */ new Set([
34320
35245
  "--repo",
@@ -34335,6 +35260,7 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
34335
35260
  "--config",
34336
35261
  "--limit",
34337
35262
  "--server-name",
35263
+ "--tools",
34338
35264
  "--workers",
34339
35265
  "--index",
34340
35266
  "--max-response-bytes"
@@ -34382,7 +35308,7 @@ async function runCli(rawArgv) {
34382
35308
  return;
34383
35309
  }
34384
35310
  const flags2 = parseFlags(rest);
34385
- if (!existsSync8(flags2.repo)) throw new Error(`--repo path does not exist: ${flags2.repo}`);
35311
+ if (!existsSync10(flags2.repo)) throw new Error(`--repo path does not exist: ${flags2.repo}`);
34386
35312
  const scans = !SCANLESS_COMMANDS.has(cmd) && !(cmd === "embed" && flags2.positional !== "build");
34387
35313
  let precomputedWalk;
34388
35314
  if (scans && !flags2.noAst) {
@@ -34416,11 +35342,11 @@ async function runCli(rawArgv) {
34416
35342
  if (!flags2.out) throw new Error("index needs --out <dir>");
34417
35343
  const outDir = flags2.out;
34418
35344
  mkdirSync3(outDir, { recursive: true });
34419
- const cachePath = join20(outDir, "cache.json");
35345
+ const cachePath = join23(outDir, "cache.json");
34420
35346
  let cache;
34421
35347
  let meta2 = {};
34422
35348
  try {
34423
- const parsed = JSON.parse(readFileSync10(cachePath, "utf8"));
35349
+ const parsed = JSON.parse(readFileSync13(cachePath, "utf8"));
34424
35350
  if (parsed.schemaVersion === SCHEMA_VERSION2 && parsed.extractorVersion === EXTRACTOR_VERSION) {
34425
35351
  cache = new Map(Object.entries(parsed.files));
34426
35352
  meta2 = {
@@ -34441,12 +35367,12 @@ async function runCli(rawArgv) {
34441
35367
  });
34442
35368
  const modelDir = resolveEmbedModelDir(flags2.repo);
34443
35369
  const model = modelDir ? loadEmbedModel(modelDir) : void 0;
34444
- const graphPath = join20(outDir, "graph.json");
34445
- const symbolsPath = join20(outDir, "symbols.json");
34446
- const embedPath = join20(outDir, "embeddings.bin");
35370
+ const graphPath = join23(outDir, "graph.json");
35371
+ const symbolsPath = join23(outDir, "symbols.json");
35372
+ const embedPath = join23(outDir, "embeddings.bin");
34447
35373
  const artifactSha = (path) => {
34448
35374
  try {
34449
- return sha1(readFileSync10(path));
35375
+ return sha1(readFileSync13(path));
34450
35376
  } catch {
34451
35377
  return void 0;
34452
35378
  }
@@ -34519,7 +35445,7 @@ async function runCli(rawArgv) {
34519
35445
  } else if (cmd === "scip") {
34520
35446
  const scan2 = readScan();
34521
35447
  const bytes = renderScip(scan2, { projectRoot: flags2.projectRoot });
34522
- const out2 = flags2.out ?? resolve2("index.scip");
35448
+ const out2 = flags2.out ?? resolve4("index.scip");
34523
35449
  if (out2 === "-") process.stdout.write(Buffer.from(bytes));
34524
35450
  else {
34525
35451
  writeFileSync4(out2, bytes);
@@ -34566,10 +35492,21 @@ async function runCli(rawArgv) {
34566
35492
  } else if (cmd === "search") {
34567
35493
  if (!flags2.positional) throw new Error('search needs a query: cli.mjs search "<query>" --repo <dir>');
34568
35494
  const scan2 = readScan();
35495
+ const searchOpts = {
35496
+ limit: flags2.limit,
35497
+ fuzzy: flags2.fuzzy,
35498
+ ...flags2.exact ? { exact: true } : {},
35499
+ ...flags2.rank ? { rank: flags2.rank } : {}
35500
+ };
35501
+ const warnIfWeak = () => {
35502
+ const { explain } = explainQuery(scan2, flags2.positional, searchOpts);
35503
+ if (explain.note) process.stderr.write(`codeindex: ${explain.note}
35504
+ `);
35505
+ };
34569
35506
  if (flags2.semantic) {
34570
35507
  const endpoint = resolveEmbedEndpoint();
34571
35508
  const lexical = () => {
34572
- const results = searchIndex(scan2, flags2.positional, { limit: flags2.limit, fuzzy: flags2.fuzzy, ...flags2.rank ? { rank: flags2.rank } : {} });
35509
+ const results = searchIndex(scan2, flags2.positional, searchOpts);
34573
35510
  emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
34574
35511
  };
34575
35512
  if (endpoint) {
@@ -34599,9 +35536,12 @@ async function runCli(rawArgv) {
34599
35536
  emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
34600
35537
  }
34601
35538
  }
35539
+ warnIfWeak();
34602
35540
  } else {
34603
- const results = searchIndex(scan2, flags2.positional, { limit: flags2.limit, fuzzy: flags2.fuzzy, ...flags2.rank ? { rank: flags2.rank } : {} });
34604
- emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
35541
+ const { results, explain } = explainQuery(scan2, flags2.positional, searchOpts);
35542
+ emit(JSON.stringify(flags2.explain ? { results, explain } : results, null, 2) + "\n", flags2.out);
35543
+ if (explain.note) process.stderr.write(`codeindex: ${explain.note}
35544
+ `);
34605
35545
  }
34606
35546
  } else if (cmd === "embed") {
34607
35547
  const sub = flags2.positional;
@@ -34658,14 +35598,14 @@ async function runCli(rawArgv) {
34658
35598
  mkdirSync3(flags2.out, { recursive: true });
34659
35599
  const scan2 = readScan();
34660
35600
  const index = buildEmbeddingIndex(scan2, model);
34661
- writeFileSync4(join20(flags2.out, "embeddings.bin"), serializeEmbeddings(index));
35601
+ writeFileSync4(join23(flags2.out, "embeddings.bin"), serializeEmbeddings(index));
34662
35602
  process.stderr.write(`codeindex: ${index.records.length} embedding records \u2192 ${flags2.out}/embeddings.bin (model ${model.modelId})
34663
35603
  `);
34664
35604
  } else if (sub === "pull") {
34665
35605
  const { url, sha256 } = resolveEmbedPullUrl();
34666
- const destDir = process.env.CODEINDEX_EMBED_DIR ?? join20(flags2.repo, ".codeindex", "models");
35606
+ const destDir = process.env.CODEINDEX_EMBED_DIR ?? join23(flags2.repo, ".codeindex", "models");
34667
35607
  mkdirSync3(destDir, { recursive: true });
34668
- process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${join20(destDir, "model.json")}
35608
+ process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${join23(destDir, "model.json")}
34669
35609
  `);
34670
35610
  let body2;
34671
35611
  try {
@@ -34686,18 +35626,22 @@ async function runCli(rawArgv) {
34686
35626
  process.exitCode = 1;
34687
35627
  return;
34688
35628
  }
34689
- writeFileSync4(join20(destDir, "model.json"), body2);
34690
- process.stderr.write(`codeindex: model written to ${join20(destDir, "model.json")}
35629
+ writeFileSync4(join23(destDir, "model.json"), body2);
35630
+ process.stderr.write(`codeindex: model written to ${join23(destDir, "model.json")}
34691
35631
  `);
34692
35632
  } else {
34693
35633
  throw new Error("embed needs a subcommand: status | build | pull | serve");
34694
35634
  }
35635
+ } else if (cmd === "lsp") {
35636
+ const sub = flags2.positional;
35637
+ if (sub !== "status") throw new Error("lsp needs a subcommand: status");
35638
+ emit(JSON.stringify(await lspStatus(readScan(), flags2.repo, flags2.probe === true), null, 2) + "\n", flags2.out);
34695
35639
  } else if (cmd === "grammars") {
34696
35640
  const sub = flags2.positional;
34697
35641
  const cacheDir = sharedGrammarsCacheDir();
34698
35642
  if (sub === "status") {
34699
35643
  const info2 = resolveGrammarsTier();
34700
- const present = (name2) => info2.dirs.some((d) => existsSync8(join20(d, name2)));
35644
+ const present = (name2) => info2.dirs.some((d) => existsSync10(join23(d, name2)));
34701
35645
  const runtimePresent = present("web-tree-sitter.wasm");
34702
35646
  const target = resolveGrammarsPullTarget();
34703
35647
  const resolvedIn = (keys) => [...keys].filter((k) => present(`${k}.wasm`)).sort();
@@ -34729,7 +35673,7 @@ async function runCli(rawArgv) {
34729
35673
  }
34730
35674
  } else if (cmd === "rules") {
34731
35675
  if (!flags2.config) throw new Error("rules needs --config <codeindex.rules.json>");
34732
- const rules = parseRules(JSON.parse(readFileSync10(flags2.config, "utf8")));
35676
+ const rules = parseRules(JSON.parse(readFileSync13(flags2.config, "utf8")));
34733
35677
  const { graph } = readArtifacts();
34734
35678
  const violations = checkRules(graph, rules);
34735
35679
  const errors = violations.filter((v) => v.severity === "error").length;
@@ -34873,7 +35817,7 @@ function inScopeMatcher(inputs) {
34873
35817
  function walk2(dir, acc) {
34874
35818
  for (const f of walk(dir, { maxFiles: Number.MAX_SAFE_INTEGER }).files) {
34875
35819
  if (f.rel.split("/").some((seg) => SKIP_DIR.has(seg))) continue;
34876
- acc.push(join21(dir, f.rel));
35820
+ acc.push(join24(dir, f.rel));
34877
35821
  }
34878
35822
  }
34879
35823
  function staticBase(glob) {
@@ -34902,7 +35846,7 @@ function expandInputs(inputs, opts = {}) {
34902
35846
  const match = compileGlobs([input]);
34903
35847
  const acc = walkCached(staticBase(input), opts);
34904
35848
  for (const f of acc) if (match(toPosix(f)) && exts.has(ext(f))) files.add(f);
34905
- } else if (existsSync9(input)) {
35849
+ } else if (existsSync11(input)) {
34906
35850
  if (statSync6(input).isDirectory()) {
34907
35851
  const acc = walkCached(input, opts);
34908
35852
  for (const f of acc) if (exts.has(ext(f))) files.add(f);
@@ -35077,8 +36021,8 @@ function parseSourceWithAst(source, file, opts = {}) {
35077
36021
  }
35078
36022
 
35079
36023
  // src/snapshot.ts
35080
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync4, writeFileSync as writeFileSync5 } from "fs";
35081
- import { dirname as dirname5, join as join22 } from "path";
36024
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync14, readdirSync as readdirSync4, writeFileSync as writeFileSync5 } from "fs";
36025
+ import { dirname as dirname5, join as join25 } from "path";
35082
36026
 
35083
36027
  // src/collector.ts
35084
36028
  var COLLECTED_CSS = [
@@ -35248,12 +36192,12 @@ function validateSnapshotMeta(raw) {
35248
36192
  };
35249
36193
  }
35250
36194
  function snapshotDir(root, id) {
35251
- return join22(root, PAGES_DIR, id);
36195
+ return join25(root, PAGES_DIR, id);
35252
36196
  }
35253
36197
  function writeSnapshot(root, snap) {
35254
36198
  const dir = snapshotDir(root, snap.meta.id);
35255
36199
  mkdirSync4(dir, { recursive: true });
35256
- writeFileSync5(join22(dir, "meta.json"), `${JSON.stringify(snap.meta, null, 2)}
36200
+ writeFileSync5(join25(dir, "meta.json"), `${JSON.stringify(snap.meta, null, 2)}
35257
36201
  `);
35258
36202
  const comment = formatCaptureComment({
35259
36203
  v: 1,
@@ -35262,20 +36206,20 @@ function writeSnapshot(root, snap) {
35262
36206
  ...snap.meta.sources?.[0] ? { sourceFile: snap.meta.sources[0] } : {},
35263
36207
  name: snap.meta.name
35264
36208
  });
35265
- writeFileSync5(join22(dir, "dom.html"), `${comment}
36209
+ writeFileSync5(join25(dir, "dom.html"), `${comment}
35266
36210
  ${snap.dom}
35267
36211
  `);
35268
- if (snap.styles) writeFileSync5(join22(dir, "styles.json"), `${JSON.stringify(snap.styles)}
36212
+ if (snap.styles) writeFileSync5(join25(dir, "styles.json"), `${JSON.stringify(snap.styles)}
35269
36213
  `);
35270
- if (snap.boxes) writeFileSync5(join22(dir, "boxes.json"), `${JSON.stringify(snap.boxes)}
36214
+ if (snap.boxes) writeFileSync5(join25(dir, "boxes.json"), `${JSON.stringify(snap.boxes)}
35271
36215
  `);
35272
- if (snap.axtree) writeFileSync5(join22(dir, "axtree.json"), `${JSON.stringify(snap.axtree)}
36216
+ if (snap.axtree) writeFileSync5(join25(dir, "axtree.json"), `${JSON.stringify(snap.axtree)}
35273
36217
  `);
35274
- if (snap.css) writeFileSync5(join22(dir, "css.json"), `${JSON.stringify(snap.css)}
36218
+ if (snap.css) writeFileSync5(join25(dir, "css.json"), `${JSON.stringify(snap.css)}
35275
36219
  `);
35276
36220
  if (snap.screenshotBase64) {
35277
36221
  try {
35278
- writeFileSync5(join22(dir, "screen.png"), Buffer.from(snap.screenshotBase64, "base64"));
36222
+ writeFileSync5(join25(dir, "screen.png"), Buffer.from(snap.screenshotBase64, "base64"));
35279
36223
  } catch {
35280
36224
  }
35281
36225
  }
@@ -35283,27 +36227,27 @@ ${snap.dom}
35283
36227
  }
35284
36228
  function readJson2(file) {
35285
36229
  try {
35286
- return JSON.parse(readFileSync11(file, "utf8"));
36230
+ return JSON.parse(readFileSync14(file, "utf8"));
35287
36231
  } catch {
35288
36232
  return void 0;
35289
36233
  }
35290
36234
  }
35291
36235
  function readSnapshot(dir) {
35292
- const rawMeta = readJson2(join22(dir, "meta.json"));
36236
+ const rawMeta = readJson2(join25(dir, "meta.json"));
35293
36237
  if (rawMeta === void 0) return null;
35294
36238
  const v = validateSnapshotMeta(rawMeta);
35295
36239
  if (!v.ok || !v.meta) return null;
35296
36240
  let dom;
35297
36241
  try {
35298
- dom = readFileSync11(join22(dir, "dom.html"), "utf8");
36242
+ dom = readFileSync14(join25(dir, "dom.html"), "utf8");
35299
36243
  } catch {
35300
36244
  return null;
35301
36245
  }
35302
- const styles = readJson2(join22(dir, "styles.json"));
35303
- const boxes = readJson2(join22(dir, "boxes.json"));
35304
- const axtree = readJson2(join22(dir, "axtree.json"));
35305
- const css = readJson2(join22(dir, "css.json"));
35306
- const shot = join22(dir, "screen.png");
36246
+ const styles = readJson2(join25(dir, "styles.json"));
36247
+ const boxes = readJson2(join25(dir, "boxes.json"));
36248
+ const axtree = readJson2(join25(dir, "axtree.json"));
36249
+ const css = readJson2(join25(dir, "css.json"));
36250
+ const shot = join25(dir, "screen.png");
35307
36251
  return {
35308
36252
  meta: v.meta,
35309
36253
  dom,
@@ -35311,11 +36255,11 @@ function readSnapshot(dir) {
35311
36255
  ...boxes ? { boxes } : {},
35312
36256
  ...axtree ? { axtree } : {},
35313
36257
  ...css ? { css } : {},
35314
- ...existsSync10(shot) ? { screenshot: "screen.png" } : {}
36258
+ ...existsSync12(shot) ? { screenshot: "screen.png" } : {}
35315
36259
  };
35316
36260
  }
35317
36261
  function readSnapshots(root) {
35318
- const base = join22(root, PAGES_DIR);
36262
+ const base = join25(root, PAGES_DIR);
35319
36263
  let dirs;
35320
36264
  try {
35321
36265
  dirs = readdirSync4(base, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -35324,7 +36268,7 @@ function readSnapshots(root) {
35324
36268
  }
35325
36269
  const out2 = [];
35326
36270
  for (const d of dirs.sort()) {
35327
- const s = readSnapshot(join22(base, d));
36271
+ const s = readSnapshot(join25(base, d));
35328
36272
  if (s) out2.push(s);
35329
36273
  }
35330
36274
  return out2;
@@ -35366,11 +36310,11 @@ function align(doc, entries) {
35366
36310
  function attachSignals(doc) {
35367
36311
  if (!isSnapshotDom(doc.file)) return;
35368
36312
  const dir = dirname5(doc.file);
35369
- const styles = readJson2(join22(dir, "styles.json"));
35370
- const boxes = readJson2(join22(dir, "boxes.json"));
35371
- const axtree = readJson2(join22(dir, "axtree.json"));
35372
- const css = readJson2(join22(dir, "css.json"));
35373
- const shot = join22(dir, "screen.png");
36313
+ const styles = readJson2(join25(dir, "styles.json"));
36314
+ const boxes = readJson2(join25(dir, "boxes.json"));
36315
+ const axtree = readJson2(join25(dir, "axtree.json"));
36316
+ const css = readJson2(join25(dir, "css.json"));
36317
+ const shot = join25(dir, "screen.png");
35374
36318
  const alignedStyleMap = styles ? align(doc, styles.entries) : null;
35375
36319
  const alignedBoxMap = boxes ? align(doc, boxes.entries) : null;
35376
36320
  const truncated = Boolean(styles?.truncated || boxes?.truncated);
@@ -35380,7 +36324,7 @@ function attachSignals(doc) {
35380
36324
  ...axtree ? { axtree } : {},
35381
36325
  // Not element-indexed, so it needs no alignment — it is a property of the stylesheet.
35382
36326
  ...css ? { css } : {},
35383
- ...existsSync10(shot) ? { screenshot: shot } : {},
36327
+ ...existsSync12(shot) ? { screenshot: shot } : {},
35384
36328
  ...truncated ? { truncated } : {}
35385
36329
  };
35386
36330
  if (Object.keys(signals).length) doc.signals = signals;
@@ -39156,7 +40100,7 @@ var contrastLiteral = {
39156
40100
  var colorsRules = [contrastLiteral];
39157
40101
 
39158
40102
  // src/rules/rendered.ts
39159
- import { readFileSync as readFileSync12 } from "fs";
40103
+ import { readFileSync as readFileSync15 } from "fs";
39160
40104
 
39161
40105
  // src/pixel.ts
39162
40106
  import { deflateSync, inflateSync } from "zlib";
@@ -39483,7 +40427,7 @@ var renderedContrastPixel = {
39483
40427
  if (!shot || !boxes || !doc.signals?.styles) return [];
39484
40428
  let img = null;
39485
40429
  try {
39486
- img = decodePng(readFileSync12(shot));
40430
+ img = decodePng(readFileSync15(shot));
39487
40431
  } catch {
39488
40432
  return [];
39489
40433
  }
@@ -39793,7 +40737,7 @@ function crossToFinding(doc, ruleId, def, cf) {
39793
40737
  import { resolve as absPath } from "path";
39794
40738
 
39795
40739
  // src/graph/resolve.ts
39796
- import { dirname as dirname6, join as join23 } from "path";
40740
+ import { dirname as dirname6, join as join26 } from "path";
39797
40741
  var EXT_ORDER = [".tsx", ".jsx", ".ts", ".js"];
39798
40742
  function candidates(base) {
39799
40743
  const out2 = [base];
@@ -39803,7 +40747,7 @@ function candidates(base) {
39803
40747
  for (const e of EXT_ORDER) out2.push(stripped + e);
39804
40748
  }
39805
40749
  for (const e of EXT_ORDER) out2.push(base + e);
39806
- for (const e of EXT_ORDER) out2.push(join23(base, `index${e}`));
40750
+ for (const e of EXT_ORDER) out2.push(join26(base, `index${e}`));
39807
40751
  return out2;
39808
40752
  }
39809
40753
  function matchKnown(base, known) {
@@ -39815,7 +40759,7 @@ function matchKnown(base, known) {
39815
40759
  }
39816
40760
  function resolveSpecifier(fromFile, spec, known, aliases) {
39817
40761
  if (spec.startsWith(".")) {
39818
- return matchKnown(join23(dirname6(toPosix(fromFile)), spec), known);
40762
+ return matchKnown(join26(dirname6(toPosix(fromFile)), spec), known);
39819
40763
  }
39820
40764
  if (aliases?.length) {
39821
40765
  for (const rule of aliases) {
@@ -39823,7 +40767,7 @@ function resolveSpecifier(fromFile, spec, known, aliases) {
39823
40767
  if (!spec.startsWith(rule.prefix)) continue;
39824
40768
  const rest = spec.slice(rule.prefix.length);
39825
40769
  for (const base of rule.bases) {
39826
- const hit = matchKnown(join23(base, rest), known);
40770
+ const hit = matchKnown(join26(base, rest), known);
39827
40771
  if (hit) return hit;
39828
40772
  }
39829
40773
  } else if (spec === rule.prefix) {
@@ -39838,21 +40782,21 @@ function resolveSpecifier(fromFile, spec, known, aliases) {
39838
40782
  }
39839
40783
 
39840
40784
  // src/graph/tsconfig.ts
39841
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
39842
- import { dirname as dirname7, join as join24, resolve as resolve3, relative } from "path";
40785
+ import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
40786
+ import { dirname as dirname7, join as join27, resolve as resolve5, relative } from "path";
39843
40787
  function readJsonish(path) {
39844
40788
  try {
39845
- const raw = readFileSync13(path, "utf8").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/,(\s*[}\]])/g, "$1");
40789
+ const raw = readFileSync16(path, "utf8").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/,(\s*[}\]])/g, "$1");
39846
40790
  return JSON.parse(raw);
39847
40791
  } catch {
39848
40792
  return null;
39849
40793
  }
39850
40794
  }
39851
40795
  function findTsconfig(startDir) {
39852
- let dir = resolve3(startDir);
40796
+ let dir = resolve5(startDir);
39853
40797
  for (let i2 = 0; i2 < 30; i2++) {
39854
- const p = join24(dir, "tsconfig.json");
39855
- if (existsSync11(p)) return p;
40798
+ const p = join27(dir, "tsconfig.json");
40799
+ if (existsSync13(p)) return p;
39856
40800
  const parent = dirname7(dir);
39857
40801
  if (parent === dir) break;
39858
40802
  dir = parent;
@@ -39864,20 +40808,20 @@ function tsconfigChain(startDir) {
39864
40808
  if (!first) return [];
39865
40809
  const out2 = [];
39866
40810
  const seen = /* @__PURE__ */ new Set();
39867
- const queue = [resolve3(first)];
40811
+ const queue = [resolve5(first)];
39868
40812
  while (queue.length) {
39869
40813
  const p = queue.shift();
39870
40814
  if (seen.has(p)) continue;
39871
40815
  seen.add(p);
39872
- if (!existsSync11(p)) continue;
40816
+ if (!existsSync13(p)) continue;
39873
40817
  out2.push(p);
39874
40818
  const cfg = readJsonish(p);
39875
40819
  const exts = Array.isArray(cfg?.extends) ? cfg.extends : typeof cfg?.extends === "string" ? [cfg.extends] : [];
39876
40820
  const dir = dirname7(p);
39877
40821
  for (const e of exts) {
39878
40822
  if (typeof e !== "string") continue;
39879
- const cands = e.endsWith(".json") ? [resolve3(dir, e)] : [resolve3(dir, `${e}.json`), resolve3(dir, e, "tsconfig.json")];
39880
- const hit = cands.find((c2) => existsSync11(c2));
40823
+ const cands = e.endsWith(".json") ? [resolve5(dir, e)] : [resolve5(dir, `${e}.json`), resolve5(dir, e, "tsconfig.json")];
40824
+ const hit = cands.find((c2) => existsSync13(c2));
39881
40825
  if (hit) queue.push(hit);
39882
40826
  }
39883
40827
  }
@@ -39894,11 +40838,11 @@ function readTsAliases(startDir, cwd = process.cwd()) {
39894
40838
  for (const e of exts) {
39895
40839
  if (typeof e !== "string") continue;
39896
40840
  const ext2 = e.endsWith(".json") ? e : `${e}.json`;
39897
- const base = readJsonish(resolve3(root, ext2));
40841
+ const base = readJsonish(resolve5(root, ext2));
39898
40842
  if (base?.compilerOptions) co = { ...base.compilerOptions, ...co };
39899
40843
  }
39900
40844
  const baseUrl = typeof co.baseUrl === "string" ? co.baseUrl : ".";
39901
- const baseAbs = resolve3(root, baseUrl);
40845
+ const baseAbs = resolve5(root, baseUrl);
39902
40846
  const paths = co.paths ?? {};
39903
40847
  const out2 = [];
39904
40848
  for (const [pattern, targetsRaw] of Object.entries(paths)) {
@@ -39910,7 +40854,7 @@ function readTsAliases(startDir, cwd = process.cwd()) {
39910
40854
  const bases = targets.map((t2) => {
39911
40855
  const star = t2.indexOf("*");
39912
40856
  const tp = wildcard && star >= 0 ? t2.slice(0, star) : t2;
39913
- return toPosix(relative(cwd, join24(baseAbs, tp))) || ".";
40857
+ return toPosix(relative(cwd, join27(baseAbs, tp))) || ".";
39914
40858
  });
39915
40859
  out2.push({ prefix: prefix2, wildcard, bases });
39916
40860
  }
@@ -45151,9 +46095,9 @@ function buildGraphAndDocs(files, opts = {}) {
45151
46095
  }
45152
46096
 
45153
46097
  // src/discover.ts
45154
- import { existsSync as existsSync12 } from "fs";
46098
+ import { existsSync as existsSync14 } from "fs";
45155
46099
  import { execFileSync } from "child_process";
45156
- import { join as join25, relative as relative2 } from "path";
46100
+ import { join as join28, relative as relative2 } from "path";
45157
46101
  function git(args2, maxBuffer) {
45158
46102
  try {
45159
46103
  return execFileSync("git", args2, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], ...maxBuffer ? { maxBuffer } : {} });
@@ -45178,7 +46122,7 @@ function gitChangedFiles(ref) {
45178
46122
  add2(git(["diff", "--name-only", "--diff-filter=d", "--cached", base]));
45179
46123
  add2(git(["ls-files", "--others", "--exclude-standard"]));
45180
46124
  const cwd = process.cwd();
45181
- return [...out2].map((p) => relative2(cwd, join25(repoRoot2, p)));
46125
+ return [...out2].map((p) => relative2(cwd, join28(repoRoot2, p)));
45182
46126
  }
45183
46127
  function gitStagedFiles() {
45184
46128
  const top = git(["rev-parse", "--show-toplevel"]);
@@ -45187,7 +46131,7 @@ function gitStagedFiles() {
45187
46131
  if (out2 === null) return null;
45188
46132
  const repoRoot2 = top.trim();
45189
46133
  const cwd = process.cwd();
45190
- return out2.split("\n").map((s) => s.trim()).filter(Boolean).map((p) => relative2(cwd, join25(repoRoot2, p)));
46134
+ return out2.split("\n").map((s) => s.trim()).filter(Boolean).map((p) => relative2(cwd, join28(repoRoot2, p)));
45191
46135
  }
45192
46136
  function stagedContent(file) {
45193
46137
  return git(["show", `:./${toPosix(file)}`], 32 * 1024 * 1024);
@@ -45272,7 +46216,7 @@ function discover(inputs, opts = {}) {
45272
46216
  } else {
45273
46217
  const filter = makeFilter(opts);
45274
46218
  const inScope = inScopeMatcher(inputs);
45275
- files = changed.filter((f) => existsSync12(f) && filter(f) && (!inScope || inScope(f)));
46219
+ files = changed.filter((f) => existsSync14(f) && filter(f) && (!inScope || inScope(f)));
45276
46220
  }
45277
46221
  } else {
45278
46222
  files = expandInputs(inputs, opts);
@@ -45529,9 +46473,9 @@ function runAudit(opts) {
45529
46473
  // src/hook.ts
45530
46474
  import { execFileSync as execFileSync2 } from "child_process";
45531
46475
  import { createHash as createHash5 } from "crypto";
45532
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
46476
+ import { existsSync as existsSync17, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
45533
46477
  import { tmpdir } from "os";
45534
- import { join as join27 } from "path";
46478
+ import { join as join30 } from "path";
45535
46479
 
45536
46480
  // src/baseline.ts
45537
46481
  var RANK = { bloquant: 0, majeur: 1, mineur: 2 };
@@ -45575,11 +46519,11 @@ function baselineSummary(diff, lang = "fr") {
45575
46519
  }
45576
46520
 
45577
46521
  // src/config.ts
45578
- import { existsSync as existsSync14, statSync as statSync7 } from "fs";
45579
- import { join as join26, isAbsolute as isAbsolute2 } from "path";
46522
+ import { existsSync as existsSync16, statSync as statSync7 } from "fs";
46523
+ import { join as join29, isAbsolute as isAbsolute2 } from "path";
45580
46524
 
45581
46525
  // src/pack.ts
45582
- import { existsSync as existsSync13 } from "fs";
46526
+ import { existsSync as existsSync15 } from "fs";
45583
46527
 
45584
46528
  // src/standards/pack.ts
45585
46529
  function localize(pack, s, lang) {
@@ -45700,7 +46644,7 @@ function checkGuidance(ds, pack) {
45700
46644
  function runPackCheck(packPath, guidancePath) {
45701
46645
  const errors = [];
45702
46646
  const warnings = [];
45703
- if (!existsSync13(packPath)) return { ok: false, errors: [`pack file not found: ${packPath}`], warnings };
46647
+ if (!existsSync15(packPath)) return { ok: false, errors: [`pack file not found: ${packPath}`], warnings };
45704
46648
  let raw;
45705
46649
  try {
45706
46650
  raw = JSON.parse(readText(packPath));
@@ -45724,7 +46668,7 @@ function runPackCheck(packPath, guidancePath) {
45724
46668
  }
45725
46669
  }
45726
46670
  if (v.ok && v.pack && guidancePath) {
45727
- if (!existsSync13(guidancePath)) {
46671
+ if (!existsSync15(guidancePath)) {
45728
46672
  errors.push(`guidance file not found: ${guidancePath}`);
45729
46673
  } else {
45730
46674
  let gRaw;
@@ -48242,8 +49186,8 @@ function hasGuidance(packKey) {
48242
49186
  // src/config.ts
48243
49187
  var CONFIG_FILE = ".ultra11yrc.json";
48244
49188
  function loadConfig(cwd) {
48245
- const p = join26(cwd, CONFIG_FILE);
48246
- if (!existsSync14(p)) return null;
49189
+ const p = join29(cwd, CONFIG_FILE);
49190
+ if (!existsSync16(p)) return null;
48247
49191
  let parsed;
48248
49192
  try {
48249
49193
  parsed = JSON.parse(readText(p));
@@ -48256,16 +49200,16 @@ function loadConfig(cwd) {
48256
49200
  return parsed;
48257
49201
  }
48258
49202
  function packPaths(path) {
48259
- if (!existsSync14(path)) return null;
49203
+ if (!existsSync16(path)) return null;
48260
49204
  if (statSync7(path).isDirectory()) {
48261
- const pack = join26(path, "pack.json");
48262
- if (!existsSync14(pack)) return null;
48263
- const glossary = join26(path, "glossary.json");
48264
- const guidance = join26(path, "guidance.json");
49205
+ const pack = join29(path, "pack.json");
49206
+ if (!existsSync16(pack)) return null;
49207
+ const glossary = join29(path, "glossary.json");
49208
+ const guidance = join29(path, "guidance.json");
48265
49209
  return {
48266
49210
  pack,
48267
- glossary: existsSync14(glossary) ? glossary : void 0,
48268
- guidance: existsSync14(guidance) ? guidance : void 0
49211
+ glossary: existsSync16(glossary) ? glossary : void 0,
49212
+ guidance: existsSync16(guidance) ? guidance : void 0
48269
49213
  };
48270
49214
  }
48271
49215
  return { pack: path };
@@ -48281,7 +49225,7 @@ function loadRuntimeStandards(cwd, packFlags, onWarn, override = false) {
48281
49225
  }
48282
49226
  result.defaultStandard = config?.standard;
48283
49227
  for (const raw of [...config?.packs ?? [], ...packFlags]) {
48284
- const paths = packPaths(isAbsolute2(raw) ? raw : join26(cwd, raw));
49228
+ const paths = packPaths(isAbsolute2(raw) ? raw : join29(cwd, raw));
48285
49229
  if (!paths) {
48286
49230
  result.errors.push(`--pack ${raw}: not found (expected a pack JSON file or a directory with pack.json)`);
48287
49231
  continue;
@@ -48311,7 +49255,7 @@ ${formatIssues(v.issues).join("\n")}`);
48311
49255
  result.loadedPacks.push(v.pack.key);
48312
49256
  if (paths.guidance) loadGuidanceFile(paths.guidance, result, onWarn);
48313
49257
  }
48314
- for (const g of config?.guidance ?? []) loadGuidanceFile(isAbsolute2(g) ? g : join26(cwd, g), result, onWarn);
49258
+ for (const g of config?.guidance ?? []) loadGuidanceFile(isAbsolute2(g) ? g : join29(cwd, g), result, onWarn);
48315
49259
  for (const sm of config?.secondaryMappings ?? []) {
48316
49260
  if (!sm || typeof sm.standard !== "string" || typeof sm.ruleId !== "string" || typeof sm.criterion !== "string") {
48317
49261
  result.errors.push("secondaryMappings: each entry must be { standard, ruleId, criterion, note? }");
@@ -48326,7 +49270,7 @@ ${formatIssues(v.issues).join("\n")}`);
48326
49270
  return result;
48327
49271
  }
48328
49272
  function loadGuidanceFile(path, result, onWarn) {
48329
- if (!existsSync14(path)) {
49273
+ if (!existsSync16(path)) {
48330
49274
  onWarn(`ultra11y: guidance ${path} not found \u2014 skipping.`);
48331
49275
  return;
48332
49276
  }
@@ -48436,10 +49380,10 @@ function loopKey(sessionId, intent, findings) {
48436
49380
  }
48437
49381
  function firstSighting(key) {
48438
49382
  try {
48439
- const dir = join27(tmpdir(), "ultra11y-hook");
49383
+ const dir = join30(tmpdir(), "ultra11y-hook");
48440
49384
  mkdirSync5(dir, { recursive: true });
48441
- const marker = join27(dir, key);
48442
- if (existsSync15(marker)) return false;
49385
+ const marker = join30(dir, key);
49386
+ if (existsSync17(marker)) return false;
48443
49387
  writeFileSync6(marker, "");
48444
49388
  return true;
48445
49389
  } catch {
@@ -48522,12 +49466,12 @@ function decide(payload, deps = {}) {
48522
49466
  }
48523
49467
 
48524
49468
  // src/report.ts
48525
- import { copyFileSync, existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
48526
- import { join as join29, relative as relative3 } from "path";
49469
+ import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
49470
+ import { join as join32, relative as relative3 } from "path";
48527
49471
 
48528
49472
  // src/prd.ts
48529
49473
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
48530
- import { join as join28 } from "path";
49474
+ import { join as join31 } from "path";
48531
49475
 
48532
49476
  // src/standards/derive.ts
48533
49477
  function aggregate2(statuses) {
@@ -49309,7 +50253,7 @@ function renderPrdDoc(r, lang = "en", standard = "wcag") {
49309
50253
  function writePrd(r, opts) {
49310
50254
  mkdirSync6(opts.out, { recursive: true });
49311
50255
  if (opts.format === "doc") {
49312
- const p2 = join28(opts.out, `prd-doc-${r.date}.md`);
50256
+ const p2 = join31(opts.out, `prd-doc-${r.date}.md`);
49313
50257
  writeFileSync7(p2, renderPrdDoc(r, opts.lang, opts.standard));
49314
50258
  return [p2];
49315
50259
  }
@@ -49319,13 +50263,13 @@ function writePrd(r, opts) {
49319
50263
  const files = remediation ? renderPerCriterion(r, opts.lang, opts.standard) : renderAuditorPerCriterion(r, opts.lang, opts.standard, { technical });
49320
50264
  const paths = [];
49321
50265
  for (const f of files) {
49322
- const p2 = join28(opts.out, f.name);
50266
+ const p2 = join31(opts.out, f.name);
49323
50267
  writeFileSync7(p2, f.content);
49324
50268
  paths.push(p2);
49325
50269
  }
49326
50270
  return paths;
49327
50271
  }
49328
- const p = join28(opts.out, `prd-${r.date}.md`);
50272
+ const p = join31(opts.out, `prd-${r.date}.md`);
49329
50273
  writeFileSync7(p, remediation ? renderBacklog(r, opts.lang, opts.standard) : renderAuditorBacklog(r, opts.lang, opts.standard, { technical }));
49330
50274
  return [p];
49331
50275
  }
@@ -49771,13 +50715,13 @@ function render(r, lang, opts) {
49771
50715
  out2.push(`- ${nc.length} ${s.ncCount}${adv.length ? ` \xB7 ${adv.length} ${s.advCount}` : ""}`);
49772
50716
  const notes = pageScope.find((x) => x.id === pg.id)?.notes;
49773
50717
  if (notes) out2.push(`- _${notes}_`);
49774
- const shot = join29(PAGES_DIR, pg.id, "screen.png");
49775
- if (existsSync16(shot)) {
50718
+ const shot = join32(PAGES_DIR, pg.id, "screen.png");
50719
+ if (existsSync18(shot)) {
49776
50720
  let href = relative3(opts.outDir ?? ".", shot).split("\\").join("/");
49777
50721
  if (opts.outDir) {
49778
50722
  try {
49779
- mkdirSync7(join29(opts.outDir, "assets"), { recursive: true });
49780
- copyFileSync(shot, join29(opts.outDir, "assets", `${pg.id}.png`));
50723
+ mkdirSync7(join32(opts.outDir, "assets"), { recursive: true });
50724
+ copyFileSync(shot, join32(opts.outDir, "assets", `${pg.id}.png`));
49781
50725
  href = `./assets/${pg.id}.png`;
49782
50726
  } catch {
49783
50727
  }
@@ -49885,7 +50829,7 @@ function writeReport(r, opts) {
49885
50829
  const core = isCore(opts.standard);
49886
50830
  const md = core ? renderReport(r, opts.lang, opts.out, opts.cropFor) : renderPackReport(r, loadPack(opts.standard), opts.lang, opts.out, opts.cropFor);
49887
50831
  mkdirSync7(opts.out, { recursive: true });
49888
- const path = join29(opts.out, `${core ? "wcag" : opts.standard}-${r.date}.md`);
50832
+ const path = join32(opts.out, `${core ? "wcag" : opts.standard}-${r.date}.md`);
49889
50833
  writeFileSync8(path, md);
49890
50834
  return path;
49891
50835
  }
@@ -50369,7 +51313,7 @@ function providerFromRemote() {
50369
51313
 
50370
51314
  // src/tickets/providers/github.ts
50371
51315
  var CAPABILITIES = { bodyLimit: 65536, labels: true };
50372
- function resolve4(opts) {
51316
+ function resolve6(opts) {
50373
51317
  const env = opts.env ?? process.env;
50374
51318
  const mode = opts.transport ?? "auto";
50375
51319
  const api = (env.GITHUB_API_URL || "https://api.github.com").replace(/\/+$/, "");
@@ -50398,7 +51342,7 @@ function resolve4(opts) {
50398
51342
  };
50399
51343
  }
50400
51344
  function createGithubProvider(opts = {}) {
50401
- const r = resolve4(opts);
51345
+ const r = resolve6(opts);
50402
51346
  const auth = { Authorization: `Bearer ${r.token ?? ""}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" };
50403
51347
  const http = {
50404
51348
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
@@ -50501,7 +51445,7 @@ function execReason(err2) {
50501
51445
  if (line) return line;
50502
51446
  return typeof e.message === "string" ? e.message.split("\n")[0]?.trim() ?? void 0 : void 0;
50503
51447
  }
50504
- function resolve5(opts) {
51448
+ function resolve7(opts) {
50505
51449
  const env = opts.env ?? process.env;
50506
51450
  const mode = opts.transport ?? "auto";
50507
51451
  const api = (env.CI_API_V4_URL || "https://gitlab.com/api/v4").replace(/\/+$/, "");
@@ -50521,7 +51465,7 @@ function resolve5(opts) {
50521
51465
  return { transport: "cli", api, jobToken };
50522
51466
  }
50523
51467
  function createGitlabProvider(opts = {}) {
50524
- const r = resolve5(opts);
51468
+ const r = resolve7(opts);
50525
51469
  const id = encodeURIComponent(r.project ?? "");
50526
51470
  const auth = { "PRIVATE-TOKEN": r.token ?? "" };
50527
51471
  const http = {
@@ -50590,7 +51534,7 @@ function createGitlabProvider(opts = {}) {
50590
51534
  var CAPABILITIES3 = { bodyLimit: 32767, labels: true };
50591
51535
  var JIRA_SCOPE_LABEL = "ultra11y";
50592
51536
  var PRIORITY = { bloquant: "Highest", majeur: "High", mineur: "Low" };
50593
- function resolve6(opts) {
51537
+ function resolve8(opts) {
50594
51538
  const env = opts.env ?? process.env;
50595
51539
  const issueType = env.ULTRA11Y_JIRA_ISSUE_TYPE || "Task";
50596
51540
  const apiVersion = env.ULTRA11Y_JIRA_API === "2" ? "2" : "3";
@@ -50609,7 +51553,7 @@ function toAdf(markdown) {
50609
51553
  return { type: "doc", version: 1, content: content.length ? content : [{ type: "paragraph", content: [] }] };
50610
51554
  }
50611
51555
  function createJiraProvider(opts = {}) {
50612
- const r = resolve6(opts);
51556
+ const r = resolve8(opts);
50613
51557
  const headers = { Authorization: r.auth ?? "" };
50614
51558
  const http = {
50615
51559
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
@@ -51113,12 +52057,12 @@ function renderCriteriaReference() {
51113
52057
  }
51114
52058
 
51115
52059
  // src/check.ts
51116
- import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
51117
- import { dirname as dirname9, join as join31 } from "path";
52060
+ import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
52061
+ import { dirname as dirname9, join as join34 } from "path";
51118
52062
 
51119
52063
  // src/verify.ts
51120
52064
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
51121
- import { join as join30 } from "path";
52065
+ import { join as join33 } from "path";
51122
52066
  var VERIFY_MAX = 40;
51123
52067
  var plain = (s) => s.replace(/\[([^\]]+)\]\(#[^)]*\)/g, "$1");
51124
52068
  function auditorCriterionLine(standard) {
@@ -51293,16 +52237,16 @@ function applyVerdicts(items, expected) {
51293
52237
  }
51294
52238
  function writeWorklist(items, outDir, semantic, standard = "wcag", lang = "en") {
51295
52239
  mkdirSync8(outDir, { recursive: true });
51296
- const todoPath = join30(outDir, "VERIFY.todo.json");
51297
- const mdPath = join30(outDir, "VERIFY.md");
52240
+ const todoPath = join33(outDir, "VERIFY.todo.json");
52241
+ const mdPath = join33(outDir, "VERIFY.md");
51298
52242
  writeFileSync9(todoPath, JSON.stringify(items, null, 2) + "\n");
51299
52243
  writeFileSync9(mdPath, formatWorklist(items, semantic, standard, lang));
51300
52244
  return { todoPath, mdPath, count: items.length };
51301
52245
  }
51302
52246
 
51303
52247
  // src/grounding.ts
51304
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
51305
- import { resolve as resolve7 } from "path";
52248
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
52249
+ import { resolve as resolve9 } from "path";
51306
52250
  var WINDOW = 10;
51307
52251
  var norm2 = (s) => s.replace(/\s+/g, " ").trim();
51308
52252
  var isUnresolvable = (file) => !file || file === "-" || file === "<stdin>" || file === "stdin";
@@ -51319,11 +52263,11 @@ function selectorProbes(selector) {
51319
52263
  }
51320
52264
  function groundFinding(g, opts = {}) {
51321
52265
  if (isUnresolvable(g.file)) return { ok: true, moved: false };
51322
- const path = resolve7(opts.cwd ?? process.cwd(), g.file);
51323
- if (!existsSync17(path)) return { ok: false, moved: false, issue: `cited file not found: ${g.file}` };
52266
+ const path = resolve9(opts.cwd ?? process.cwd(), g.file);
52267
+ if (!existsSync19(path)) return { ok: false, moved: false, issue: `cited file not found: ${g.file}` };
51324
52268
  let text;
51325
52269
  try {
51326
- text = readFileSync14(path, "utf8");
52270
+ text = readFileSync17(path, "utf8");
51327
52271
  } catch {
51328
52272
  return { ok: false, moved: false, issue: `cited file unreadable: ${g.file}` };
51329
52273
  }
@@ -51710,11 +52654,11 @@ function checkSemantic(md, opts) {
51710
52654
  const standard = opts.standard ?? "wcag";
51711
52655
  const s = M[lang];
51712
52656
  const empty = { total: 0, grounded: 0, moved: 0, failed: 0 };
51713
- const artifact = opts.verdictsPath ?? join31(dirname9(opts.reportPath), "VERIFY.todo.json");
51714
- if (!existsSync18(artifact)) return { ok: false, issues: [s.semanticMissing(artifact)], ...empty };
52657
+ const artifact = opts.verdictsPath ?? join34(dirname9(opts.reportPath), "VERIFY.todo.json");
52658
+ if (!existsSync20(artifact)) return { ok: false, issues: [s.semanticMissing(artifact)], ...empty };
51715
52659
  let items;
51716
52660
  try {
51717
- const parsed = JSON.parse(readFileSync15(artifact, "utf8"));
52661
+ const parsed = JSON.parse(readFileSync18(artifact, "utf8"));
51718
52662
  if (!Array.isArray(parsed)) throw new Error("not an array");
51719
52663
  items = parsed;
51720
52664
  } catch {
@@ -51747,7 +52691,7 @@ function sectionBody(md, n) {
51747
52691
 
51748
52692
  // src/adjudicate.ts
51749
52693
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
51750
- import { join as join32 } from "path";
52694
+ import { join as join35 } from "path";
51751
52695
 
51752
52696
  // src/data/adjudication.json
51753
52697
  var adjudication_default = {
@@ -52682,7 +53626,7 @@ function docsForAudit(audit2, cwd) {
52682
53626
  const docs = [];
52683
53627
  for (const f of files) {
52684
53628
  try {
52685
- docs.push(parseSource(readText(cwd ? join32(cwd, f) : f), f));
53629
+ docs.push(parseSource(readText(cwd ? join35(cwd, f) : f), f));
52686
53630
  } catch {
52687
53631
  }
52688
53632
  }
@@ -53097,8 +54041,8 @@ function formatAdjudication(items, lang = "en", standard = CORE2) {
53097
54041
  }
53098
54042
  function writeAdjudication(items, outDir, opts) {
53099
54043
  mkdirSync9(outDir, { recursive: true });
53100
- const todoPath = join32(outDir, "ADJUDICATE.todo.json");
53101
- const mdPath = join32(outDir, "ADJUDICATE.md");
54044
+ const todoPath = join35(outDir, "ADJUDICATE.todo.json");
54045
+ const mdPath = join35(outDir, "ADJUDICATE.md");
53102
54046
  const file = {
53103
54047
  tool: "ultra11y",
53104
54048
  kind: "adjudication",
@@ -53303,9 +54247,9 @@ function applyRawVerdicts(items, verdicts) {
53303
54247
 
53304
54248
  // src/scan.ts
53305
54249
  import { execFileSync as execFileSync6 } from "child_process";
53306
- import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync11, existsSync as existsSync20, statSync as statSync8, readdirSync as readdirSync5, rmSync as rmSync3, readFileSync as readFileSync16 } from "fs";
54250
+ import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync11, existsSync as existsSync22, statSync as statSync8, readdirSync as readdirSync5, rmSync as rmSync3, readFileSync as readFileSync19 } from "fs";
53307
54251
  import { tmpdir as tmpdir2 } from "os";
53308
- import { join as join33, resolve as resolve8 } from "path";
54252
+ import { join as join36, resolve as resolve10 } from "path";
53309
54253
  import { fileURLToPath as fileURLToPath3 } from "url";
53310
54254
 
53311
54255
  // src/axe-map.ts
@@ -53565,7 +54509,7 @@ async function crawlUrls(start2, opts) {
53565
54509
  }
53566
54510
 
53567
54511
  // src/sample.ts
53568
- import { existsSync as existsSync19 } from "fs";
54512
+ import { existsSync as existsSync21 } from "fs";
53569
54513
  function looksLikeTarget(u) {
53570
54514
  return /^https?:\/\//i.test(u) || /^(\.\.?[/\\]|[/\\])/.test(u) || /\.x?html?$/i.test(u);
53571
54515
  }
@@ -53601,7 +54545,7 @@ function validateSample(raw) {
53601
54545
  if (p.auth !== void 0 && typeof p.auth !== "boolean") err2(`sample.pages[${i2}].auth`, "auth must be a boolean");
53602
54546
  if (p.storageState !== void 0 && (typeof p.storageState !== "string" || p.storageState.trim() === "")) {
53603
54547
  err2(`sample.pages[${i2}].storageState`, "storageState must be a non-empty file path string");
53604
- } else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !existsSync19(p.storageState)) {
54548
+ } else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !existsSync21(p.storageState)) {
53605
54549
  warn(
53606
54550
  `sample.pages[${i2}].storageState`,
53607
54551
  `storageState path not found: "${p.storageState}" (resolved from the current directory) \u2014 the scan will not be able to load this session`
@@ -53791,11 +54735,11 @@ function imageExists(tag) {
53791
54735
  }
53792
54736
  var CTX_PREFIX = "ultra11y-dyn-";
53793
54737
  function buildImage(tag = IMAGE_TAG) {
53794
- const ctx = mkdtempSync2(join33(tmpdir2(), CTX_PREFIX));
54738
+ const ctx = mkdtempSync2(join36(tmpdir2(), CTX_PREFIX));
53795
54739
  try {
53796
- writeFileSync11(join33(ctx, "runner.mjs"), RUNNER);
53797
- writeFileSync11(join33(ctx, "package.json"), PKG);
53798
- writeFileSync11(join33(ctx, "Dockerfile"), DOCKERFILE);
54740
+ writeFileSync11(join36(ctx, "runner.mjs"), RUNNER);
54741
+ writeFileSync11(join36(ctx, "package.json"), PKG);
54742
+ writeFileSync11(join36(ctx, "Dockerfile"), DOCKERFILE);
53799
54743
  execFileSync6("docker", ["build", "-t", tag, ctx], { stdio: "inherit", timeout: 9e5 });
53800
54744
  } finally {
53801
54745
  rmSync3(ctx, { recursive: true, force: true });
@@ -53806,7 +54750,7 @@ function cleanTempContexts() {
53806
54750
  const dir = tmpdir2();
53807
54751
  for (const name2 of readdirSync5(dir)) {
53808
54752
  if (!name2.startsWith(CTX_PREFIX)) continue;
53809
- rmSync3(join33(dir, name2), { recursive: true, force: true });
54753
+ rmSync3(join36(dir, name2), { recursive: true, force: true });
53810
54754
  removed++;
53811
54755
  }
53812
54756
  return removed;
@@ -53872,7 +54816,7 @@ var PROBE_FIELDS = [
53872
54816
  function runRunner(target, isFile, tag, snapshot = true) {
53873
54817
  const args2 = ["run", "--rm"];
53874
54818
  if (!snapshot) args2.push("-e", "ULTRA11Y_SNAPSHOT=0");
53875
- if (isFile) args2.push("-v", `${resolve8(target)}:${MOUNT}:ro`);
54819
+ if (isFile) args2.push("-v", `${resolve10(target)}:${MOUNT}:ro`);
53876
54820
  args2.push(tag, isFile ? MOUNT : target);
53877
54821
  let stdout;
53878
54822
  try {
@@ -53955,7 +54899,7 @@ function toDynamicResult(out2, target, lang = "en", engine = "axe-core@playwrigh
53955
54899
  var DOCKER_TESTED_SCS = ["1.4.10"];
53956
54900
  function runScan(opts) {
53957
54901
  const isUrl3 = /^https?:\/\//i.test(opts.target);
53958
- if (!isUrl3 && !existsSync20(opts.target)) {
54902
+ if (!isUrl3 && !existsSync22(opts.target)) {
53959
54903
  throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
53960
54904
  }
53961
54905
  if (!dockerAvailable()) {
@@ -53963,7 +54907,7 @@ function runScan(opts) {
53963
54907
  }
53964
54908
  const tag = opts.tag ?? IMAGE_TAG;
53965
54909
  if (!imageExists(tag)) buildImage(tag);
53966
- const isFile = !isUrl3 && existsSync20(opts.target) && statSync8(opts.target).isFile();
54910
+ const isFile = !isUrl3 && existsSync22(opts.target) && statSync8(opts.target).isFile();
53967
54911
  const out2 = runRunner(opts.target, isFile, tag, Boolean(opts.snapshotRoot));
53968
54912
  const id = opts.snapshotRoot ? writeRunnerSnapshot(opts.snapshotRoot, out2, opts.target) : void 0;
53969
54913
  return { ...toDynamicResult(out2, opts.target), testedScs: [...DOCKER_TESTED_SCS], ...id ? { snapshots: [id] } : {} };
@@ -54050,10 +54994,10 @@ async function runCrawlScan(opts) {
54050
54994
  var sevRank = { bloquant: 3, majeur: 2, mineur: 1 };
54051
54995
  function resolveHostAnchor(file, snippet2) {
54052
54996
  const s = snippet2?.trim();
54053
- if (!s || !existsSync20(file)) return null;
54997
+ if (!s || !existsSync22(file)) return null;
54054
54998
  let source;
54055
54999
  try {
54056
- source = readFileSync16(file, "utf8");
55000
+ source = readFileSync19(file, "utf8");
54057
55001
  } catch {
54058
55002
  return null;
54059
55003
  }
@@ -54180,9 +55124,9 @@ function mergeSnapshotAudit(base, snap) {
54180
55124
  }
54181
55125
 
54182
55126
  // src/scan-local.ts
54183
- import { existsSync as existsSync21, statSync as statSync9 } from "fs";
55127
+ import { existsSync as existsSync23, statSync as statSync9 } from "fs";
54184
55128
  import { createRequire } from "module";
54185
- import { resolve as resolve9 } from "path";
55129
+ import { resolve as resolve11 } from "path";
54186
55130
  var LOCAL_ENGINE = "axe-core@playwright (local)";
54187
55131
  var LOCAL_TESTED_SCS = ["1.4.4", "1.4.10", "1.4.12", "2.4.7", "1.4.13"];
54188
55132
  function localTestedScs(interact) {
@@ -54192,7 +55136,7 @@ var PW_SPEC = "@playwright/test";
54192
55136
  var AXE_SPEC = "@axe-core/playwright";
54193
55137
  function localAvailable(cwd) {
54194
55138
  try {
54195
- const req = createRequire(resolve9(cwd, "package.json"));
55139
+ const req = createRequire(resolve11(cwd, "package.json"));
54196
55140
  req.resolve(PW_SPEC);
54197
55141
  req.resolve(AXE_SPEC);
54198
55142
  return true;
@@ -54204,7 +55148,7 @@ function resolveLocalDeps(cwd) {
54204
55148
  let chromium;
54205
55149
  let AxeBuilder;
54206
55150
  try {
54207
- const req = createRequire(resolve9(cwd, "package.json"));
55151
+ const req = createRequire(resolve11(cwd, "package.json"));
54208
55152
  const pw = req(PW_SPEC);
54209
55153
  const axeMod = req(AXE_SPEC);
54210
55154
  chromium = pw.chromium;
@@ -54683,7 +55627,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
54683
55627
  const page = await context.newPage();
54684
55628
  const empty = [];
54685
55629
  try {
54686
- const url = isFile ? "file://" + resolve9(target) : target;
55630
+ const url = isFile ? "file://" + resolve11(target) : target;
54687
55631
  await page.goto(url, { waitUntil: "load", timeout: 45e3 });
54688
55632
  await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => {
54689
55633
  });
@@ -54751,7 +55695,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
54751
55695
  }
54752
55696
  async function runScanLocal(opts) {
54753
55697
  const isUrl3 = /^https?:\/\//i.test(opts.target);
54754
- if (!isUrl3 && !existsSync21(opts.target)) {
55698
+ if (!isUrl3 && !existsSync23(opts.target)) {
54755
55699
  throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
54756
55700
  }
54757
55701
  const isFile = !isUrl3 && statSync9(opts.target).isFile();
@@ -55338,7 +56282,7 @@ function fixSummary(r, lang = "fr", write = false) {
55338
56282
  // src/init.ts
55339
56283
  import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync10, chmodSync, realpathSync as realpathSync2 } from "fs";
55340
56284
  import { execFileSync as execFileSync7 } from "child_process";
55341
- import { join as join34, relative as relative4, sep as sep4 } from "path";
56285
+ import { join as join37, relative as relative4, sep as sep4 } from "path";
55342
56286
  var EN_SEV = { bloquant: "blocking", majeur: "major", mineur: "minor" };
55343
56287
  function repoRoot() {
55344
56288
  try {
@@ -55456,23 +56400,23 @@ jobs:
55456
56400
  `;
55457
56401
  }
55458
56402
  function writeHook(root, enginePath, failOn, mode = "staged") {
55459
- const dir = join34(root, ".git", "hooks");
56403
+ const dir = join37(root, ".git", "hooks");
55460
56404
  mkdirSync10(dir, { recursive: true });
55461
- const path = join34(dir, "pre-commit");
56405
+ const path = join37(dir, "pre-commit");
55462
56406
  writeFileSync13(path, mode === "baseline" ? hookScript(enginePath, failOn) : stagedHookScript(enginePath, failOn));
55463
56407
  chmodSync(path, 493);
55464
56408
  return path;
55465
56409
  }
55466
56410
  function writeCi(root, enginePath, failOn) {
55467
- const dir = join34(root, ".github", "workflows");
56411
+ const dir = join37(root, ".github", "workflows");
55468
56412
  mkdirSync10(dir, { recursive: true });
55469
- const path = join34(dir, "a11y.yml");
56413
+ const path = join37(dir, "a11y.yml");
55470
56414
  writeFileSync13(path, ciWorkflow(enginePath, failOn));
55471
56415
  return path;
55472
56416
  }
55473
56417
 
55474
56418
  // src/install/json-edit.ts
55475
- import { copyFileSync as copyFileSync2, existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync14 } from "fs";
56419
+ import { copyFileSync as copyFileSync2, existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync20, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync14 } from "fs";
55476
56420
  import { dirname as dirname10 } from "path";
55477
56421
  var SettingsParseError = class extends Error {
55478
56422
  constructor(path, cause) {
@@ -55486,10 +56430,10 @@ function stamp() {
55486
56430
  return (/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-");
55487
56431
  }
55488
56432
  function writeTextWithBackup(path, content, marker = "ultra11y") {
55489
- if (existsSync22(path) && readFileSync17(path, "utf8") === content) return { path, changed: false };
56433
+ if (existsSync24(path) && readFileSync20(path, "utf8") === content) return { path, changed: false };
55490
56434
  mkdirSync11(dirname10(path), { recursive: true });
55491
56435
  let backup;
55492
- if (existsSync22(path)) {
56436
+ if (existsSync24(path)) {
55493
56437
  backup = `${path}.${marker}-backup-${stamp()}`;
55494
56438
  copyFileSync2(path, backup);
55495
56439
  }
@@ -55505,8 +56449,8 @@ function writeTextWithBackup(path, content, marker = "ultra11y") {
55505
56449
  }
55506
56450
  function editJsonFile(path, mutate) {
55507
56451
  let root = {};
55508
- if (existsSync22(path)) {
55509
- const raw = readFileSync17(path, "utf8");
56452
+ if (existsSync24(path)) {
56453
+ const raw = readFileSync20(path, "utf8");
55510
56454
  if (raw.trim()) {
55511
56455
  let parsed;
55512
56456
  try {
@@ -55522,13 +56466,13 @@ function editJsonFile(path, mutate) {
55522
56466
  }
55523
56467
  const before = JSON.stringify(root);
55524
56468
  mutate(root);
55525
- if (JSON.stringify(root) === before && existsSync22(path)) return { path, changed: false };
56469
+ if (JSON.stringify(root) === before && existsSync24(path)) return { path, changed: false };
55526
56470
  return writeTextWithBackup(path, `${JSON.stringify(root, null, 2)}
55527
56471
  `);
55528
56472
  }
55529
56473
  function readJsonSafe(path) {
55530
56474
  try {
55531
- const parsed = JSON.parse(readFileSync17(path, "utf8"));
56475
+ const parsed = JSON.parse(readFileSync20(path, "utf8"));
55532
56476
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
55533
56477
  } catch {
55534
56478
  return null;
@@ -55569,7 +56513,7 @@ function claudeCodeWired(settingsPath) {
55569
56513
  }
55570
56514
 
55571
56515
  // src/install/text-edit.ts
55572
- import { existsSync as existsSync23, readFileSync as readFileSync18, rmSync as rmSync5 } from "fs";
56516
+ import { existsSync as existsSync25, readFileSync as readFileSync21, rmSync as rmSync5 } from "fs";
55573
56517
  var BLOCK_BEGIN = "<!-- BEGIN ultra11y (managed by `ultra11y install --agents-md`; edit outside this block) -->";
55574
56518
  var BLOCK_END = "<!-- END ultra11y -->";
55575
56519
  var blockRe = () => new RegExp(`${escapeRe2(BLOCK_BEGIN)}[\\s\\S]*?${escapeRe2(BLOCK_END)}\\n?`);
@@ -55581,15 +56525,15 @@ function upsertManagedBlock(path, body2) {
55581
56525
  ${body2.trim()}
55582
56526
  ${BLOCK_END}
55583
56527
  `;
55584
- if (!existsSync23(path)) return writeTextWithBackup(path, block);
55585
- const current = readFileSync18(path, "utf8");
56528
+ if (!existsSync25(path)) return writeTextWithBackup(path, block);
56529
+ const current = readFileSync21(path, "utf8");
55586
56530
  if (blockRe().test(current)) return writeTextWithBackup(path, current.replace(blockRe(), block));
55587
56531
  const sep8 = current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
55588
56532
  return writeTextWithBackup(path, `${current}${sep8}${block}`);
55589
56533
  }
55590
56534
  function removeManagedBlock(path) {
55591
- if (!existsSync23(path)) return { path, changed: false };
55592
- const current = readFileSync18(path, "utf8");
56535
+ if (!existsSync25(path)) return { path, changed: false };
56536
+ const current = readFileSync21(path, "utf8");
55593
56537
  if (!blockRe().test(current)) return { path, changed: false };
55594
56538
  const rest = current.replace(blockRe(), "");
55595
56539
  if (rest.trim() === "") {
@@ -55599,13 +56543,13 @@ function removeManagedBlock(path) {
55599
56543
  return writeTextWithBackup(path, rest.replace(/\n+$/, "\n"));
55600
56544
  }
55601
56545
  function hasManagedBlock(path) {
55602
- return existsSync23(path) && blockRe().test(readFileSync18(path, "utf8"));
56546
+ return existsSync25(path) && blockRe().test(readFileSync21(path, "utf8"));
55603
56547
  }
55604
56548
 
55605
56549
  // src/install/agents-md.ts
55606
- import { join as join35 } from "path";
56550
+ import { join as join38 } from "path";
55607
56551
  function agentsMdPath(root) {
55608
- return join35(root, "AGENTS.md");
56552
+ return join38(root, "AGENTS.md");
55609
56553
  }
55610
56554
  function agentsMdBlock(root) {
55611
56555
  const e = engineInvocation(root);
@@ -55660,25 +56604,25 @@ function agentsMdWired(root) {
55660
56604
  }
55661
56605
 
55662
56606
  // src/install/codex.ts
55663
- import { existsSync as existsSync25, readFileSync as readFileSync20 } from "fs";
55664
- import { join as join37 } from "path";
56607
+ import { existsSync as existsSync27, readFileSync as readFileSync24 } from "fs";
56608
+ import { join as join40 } from "path";
55665
56609
 
55666
56610
  // src/install/paths.ts
55667
- import { copyFileSync as copyFileSync3, cpSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
56611
+ import { copyFileSync as copyFileSync3, cpSync, existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync23, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
55668
56612
  import { homedir as homedir2 } from "os";
55669
- import { dirname as dirname11, join as join36 } from "path";
56613
+ import { dirname as dirname11, join as join39 } from "path";
55670
56614
  function codexHome() {
55671
- return process.env.CODEX_HOME || join36(homedir2(), ".codex");
56615
+ return process.env.CODEX_HOME || join39(homedir2(), ".codex");
55672
56616
  }
55673
56617
  function opencodeConfigDir() {
55674
56618
  const xdg = process.env.XDG_CONFIG_HOME;
55675
- return join36(xdg && xdg !== "" ? xdg : join36(homedir2(), ".config"), "opencode");
56619
+ return join39(xdg && xdg !== "" ? xdg : join39(homedir2(), ".config"), "opencode");
55676
56620
  }
55677
56621
  function claudeSettingsPath(project, cwd = process.cwd()) {
55678
- return project ? join36(cwd, ".claude", "settings.json") : join36(homedir2(), ".claude", "settings.json");
56622
+ return project ? join39(cwd, ".claude", "settings.json") : join39(homedir2(), ".claude", "settings.json");
55679
56623
  }
55680
56624
  function pinnedEnginePath() {
55681
- return join36(homedir2(), ".ultra11y", "bin", "ultra11y.mjs");
56625
+ return join39(homedir2(), ".ultra11y", "bin", "ultra11y.mjs");
55682
56626
  }
55683
56627
  function installedCliCommand() {
55684
56628
  const override = process.env.ULTRA11Y_BIN;
@@ -55693,7 +56637,7 @@ function installedCliCommand() {
55693
56637
  if (source && !ephemeral) return `node ${JSON.stringify(source)}`;
55694
56638
  const pin = pinnedEnginePath();
55695
56639
  if (source) {
55696
- mkdirSync12(join36(pin, ".."), { recursive: true });
56640
+ mkdirSync12(join39(pin, ".."), { recursive: true });
55697
56641
  copyFileSync3(source, pin);
55698
56642
  }
55699
56643
  return `node ${JSON.stringify(pin)}`;
@@ -55706,32 +56650,32 @@ function packageRoot() {
55706
56650
  return null;
55707
56651
  }
55708
56652
  for (let i2 = 0; i2 < 6 && dir && dir !== "/"; i2++) {
55709
- if (existsSync24(join36(dir, "skills", "review-a11y", "SKILL.md"))) return dir;
56653
+ if (existsSync26(join39(dir, "skills", "review-a11y", "SKILL.md"))) return dir;
55710
56654
  dir = dirname11(dir);
55711
56655
  }
55712
56656
  return null;
55713
56657
  }
55714
56658
  function bundledSkillsDir() {
55715
56659
  const root = packageRoot();
55716
- const p = root ? join36(root, "skills") : null;
55717
- return p && existsSync24(p) ? p : null;
56660
+ const p = root ? join39(root, "skills") : null;
56661
+ return p && existsSync26(p) ? p : null;
55718
56662
  }
55719
56663
  function bundledOpencodePlugin() {
55720
56664
  const root = packageRoot();
55721
- const p = root ? join36(root, ".opencode", "plugins", "ultra11y.js") : null;
55722
- return p && existsSync24(p) ? p : null;
56665
+ const p = root ? join39(root, ".opencode", "plugins", "ultra11y.js") : null;
56666
+ return p && existsSync26(p) ? p : null;
55723
56667
  }
55724
56668
  function copySkillsInto(source, dest) {
55725
56669
  const out2 = [];
55726
56670
  for (const name2 of ["ultra11y", "review-a11y"]) {
55727
- const from = join36(source, name2);
55728
- if (!existsSync24(from)) continue;
55729
- const to = join36(dest, name2);
55730
- const same = ["SKILL.md", join36("scripts", "ultra11y.mjs")].every((f) => {
55731
- const a = join36(from, f);
55732
- const b = join36(to, f);
56671
+ const from = join39(source, name2);
56672
+ if (!existsSync26(from)) continue;
56673
+ const to = join39(dest, name2);
56674
+ const same = ["SKILL.md", join39("scripts", "ultra11y.mjs")].every((f) => {
56675
+ const a = join39(from, f);
56676
+ const b = join39(to, f);
55733
56677
  try {
55734
- return existsSync24(b) && statSync10(a).size === statSync10(b).size && readFileSync19(a).equals(readFileSync19(b));
56678
+ return existsSync26(b) && statSync10(a).size === statSync10(b).size && readFileSync23(a).equals(readFileSync23(b));
55735
56679
  } catch {
55736
56680
  return false;
55737
56681
  }
@@ -55781,7 +56725,7 @@ function featuresBody(content) {
55781
56725
  function installCodex({ codexDir, command, skillsSource }) {
55782
56726
  const reports = [];
55783
56727
  reports.push(
55784
- editJsonFile(join37(codexDir, "hooks.json"), (root) => {
56728
+ editJsonFile(join40(codexDir, "hooks.json"), (root) => {
55785
56729
  const hooks = root.hooks ??= {};
55786
56730
  hooks.PreToolUse = [
55787
56731
  ...withoutOurs2(hooks.PreToolUse),
@@ -55789,11 +56733,11 @@ function installCodex({ codexDir, command, skillsSource }) {
55789
56733
  ];
55790
56734
  })
55791
56735
  );
55792
- const configPath = join37(codexDir, "config.toml");
55793
- const current = existsSync25(configPath) ? readFileSync20(configPath, "utf8") : "";
56736
+ const configPath = join40(codexDir, "config.toml");
56737
+ const current = existsSync27(configPath) ? readFileSync24(configPath, "utf8") : "";
55794
56738
  const edit = enableHooksFeature(current);
55795
56739
  if (edit.changed) reports.push(writeTextWithBackup(configPath, edit.content));
55796
- if (skillsSource) reports.push(...copySkillsInto(skillsSource, join37(codexDir, "skills")));
56740
+ if (skillsSource) reports.push(...copySkillsInto(skillsSource, join40(codexDir, "skills")));
55797
56741
  return {
55798
56742
  reports,
55799
56743
  guidance: [
@@ -55808,8 +56752,8 @@ function installCodex({ codexDir, command, skillsSource }) {
55808
56752
  }
55809
56753
  function uninstallCodex({ codexDir }) {
55810
56754
  const reports = [];
55811
- const hooksPath = join37(codexDir, "hooks.json");
55812
- if (existsSync25(hooksPath)) {
56755
+ const hooksPath = join40(codexDir, "hooks.json");
56756
+ if (existsSync27(hooksPath)) {
55813
56757
  reports.push(
55814
56758
  editJsonFile(hooksPath, (root) => {
55815
56759
  const hooks = root.hooks;
@@ -55824,33 +56768,33 @@ function uninstallCodex({ codexDir }) {
55824
56768
  return { reports, guidance: ["codex: `[features] hooks = true` was left enabled \u2014 other hooks may depend on it."] };
55825
56769
  }
55826
56770
  function codexWired(codexDir) {
55827
- const root = readJsonSafe(join37(codexDir, "hooks.json"));
56771
+ const root = readJsonSafe(join40(codexDir, "hooks.json"));
55828
56772
  const groups = root?.hooks?.PreToolUse;
55829
56773
  if (!Array.isArray(groups)) return 0;
55830
56774
  return groups.reduce((n, g) => n + (g.hooks ?? []).filter((h) => String(h.command ?? "").includes(CODEX_MARKER)).length, 0);
55831
56775
  }
55832
56776
  function codexHooksEnabled(codexDir) {
55833
- const p = join37(codexDir, "config.toml");
55834
- return existsSync25(p) && HOOKS_ON.test(featuresBody(readFileSync20(p, "utf8")));
56777
+ const p = join40(codexDir, "config.toml");
56778
+ return existsSync27(p) && HOOKS_ON.test(featuresBody(readFileSync24(p, "utf8")));
55835
56779
  }
55836
56780
 
55837
56781
  // src/install/opencode.ts
55838
- import { existsSync as existsSync26, readFileSync as readFileSync21, rmSync as rmSync6 } from "fs";
55839
- import { join as join38 } from "path";
56782
+ import { existsSync as existsSync28, readFileSync as readFileSync25, rmSync as rmSync6 } from "fs";
56783
+ import { join as join41 } from "path";
55840
56784
  var OPENCODE_MARKER = "ULTRA11Y_OPENCODE_PLUGIN";
55841
56785
  function pluginTarget(configDir) {
55842
- return join38(configDir, "plugin", "ultra11y.js");
56786
+ return join41(configDir, "plugin", "ultra11y.js");
55843
56787
  }
55844
56788
  function installOpencode({ configDir, pluginSource, skillsSource, enginePath }) {
55845
56789
  const target = pluginTarget(configDir);
55846
- if (existsSync26(target) && !readFileSync21(target, "utf8").includes(OPENCODE_MARKER)) {
56790
+ if (existsSync28(target) && !readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
55847
56791
  throw new Error(`refusing to overwrite ${target}: it is not a file ultra11y wrote. Remove it manually if you want the plugin here.`);
55848
56792
  }
55849
- const reports = [writeTextWithBackup(target, readFileSync21(pluginSource, "utf8"))];
55850
- if (enginePath && existsSync26(enginePath)) {
55851
- reports.push(writeTextWithBackup(join38(configDir, "plugin", "ultra11y.mjs"), readFileSync21(enginePath, "utf8")));
56793
+ const reports = [writeTextWithBackup(target, readFileSync25(pluginSource, "utf8"))];
56794
+ if (enginePath && existsSync28(enginePath)) {
56795
+ reports.push(writeTextWithBackup(join41(configDir, "plugin", "ultra11y.mjs"), readFileSync25(enginePath, "utf8")));
55852
56796
  }
55853
- if (skillsSource) reports.push(...copySkillsInto(skillsSource, join38(configDir, "skills")));
56797
+ if (skillsSource) reports.push(...copySkillsInto(skillsSource, join41(configDir, "skills")));
55854
56798
  return {
55855
56799
  reports,
55856
56800
  guidance: [
@@ -55863,13 +56807,13 @@ function installOpencode({ configDir, pluginSource, skillsSource, enginePath })
55863
56807
  function uninstallOpencode({ configDir }) {
55864
56808
  const reports = [];
55865
56809
  const target = pluginTarget(configDir);
55866
- if (existsSync26(target) && readFileSync21(target, "utf8").includes(OPENCODE_MARKER)) {
56810
+ if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
55867
56811
  rmSync6(target, { force: true });
55868
- rmSync6(join38(configDir, "plugin", "ultra11y.mjs"), { force: true });
56812
+ rmSync6(join41(configDir, "plugin", "ultra11y.mjs"), { force: true });
55869
56813
  reports.push({ path: target, changed: true });
55870
56814
  }
55871
- const configPath = join38(configDir, "opencode.json");
55872
- if (existsSync26(configPath)) {
56815
+ const configPath = join41(configDir, "opencode.json");
56816
+ if (existsSync28(configPath)) {
55873
56817
  reports.push(
55874
56818
  editJsonFile(configPath, (root) => {
55875
56819
  if (!Array.isArray(root.plugin)) return;
@@ -55883,9 +56827,9 @@ function uninstallOpencode({ configDir }) {
55883
56827
  }
55884
56828
  function opencodeWired(configDir) {
55885
56829
  const target = pluginTarget(configDir);
55886
- if (existsSync26(target) && readFileSync21(target, "utf8").includes(OPENCODE_MARKER)) return true;
56830
+ if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) return true;
55887
56831
  try {
55888
- const cfg = JSON.parse(readFileSync21(join38(configDir, "opencode.json"), "utf8"));
56832
+ const cfg = JSON.parse(readFileSync25(join41(configDir, "opencode.json"), "utf8"));
55889
56833
  return Array.isArray(cfg.plugin) && cfg.plugin.some((p) => /^ultra11y(@|$)/.test(String(p)));
55890
56834
  } catch {
55891
56835
  return false;
@@ -55893,8 +56837,8 @@ function opencodeWired(configDir) {
55893
56837
  }
55894
56838
 
55895
56839
  // src/install/index.ts
55896
- import { existsSync as existsSync27 } from "fs";
55897
- import { join as join39 } from "path";
56840
+ import { existsSync as existsSync29 } from "fs";
56841
+ import { join as join42 } from "path";
55898
56842
  var ALL_TARGETS = ["claude-code", "codex", "opencode"];
55899
56843
  function parseTargets(flags2) {
55900
56844
  const picked = /* @__PURE__ */ new Set();
@@ -55933,7 +56877,7 @@ function installForTargets(opts) {
55933
56877
  const out3 = installCodex({ codexDir: codexHome(), command: cmd(), skillsSource: opts.skills === false ? null : bundledSkillsDir() });
55934
56878
  r.reports.push(...out3.reports);
55935
56879
  r.guidance.push(...out3.guidance);
55936
- } else r.reports.push({ path: join39(codexHome(), "hooks.json"), changed: false });
56880
+ } else r.reports.push({ path: join42(codexHome(), "hooks.json"), changed: false });
55937
56881
  break;
55938
56882
  }
55939
56883
  case "opencode": {
@@ -55942,18 +56886,18 @@ function installForTargets(opts) {
55942
56886
  r.error = "opencode: this engine was not run from a package carrying .opencode/plugins/ultra11y.js \u2014 install ultra11y from npm and retry.";
55943
56887
  break;
55944
56888
  }
55945
- const configDir = opts.project === true ? join39(cwd, ".opencode") : opencodeConfigDir();
56889
+ const configDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
55946
56890
  if (!opts.dryRun) {
55947
- const engine = existsSync27(pinnedEnginePath()) ? pinnedEnginePath() : join39(packageRoot() ?? "", "scripts", "ultra11y.mjs");
56891
+ const engine = existsSync29(pinnedEnginePath()) ? pinnedEnginePath() : join42(packageRoot() ?? "", "scripts", "ultra11y.mjs");
55948
56892
  const out3 = installOpencode({
55949
56893
  configDir,
55950
56894
  pluginSource: source,
55951
56895
  skillsSource: opts.skills === false ? null : bundledSkillsDir(),
55952
- enginePath: existsSync27(engine) ? engine : null
56896
+ enginePath: existsSync29(engine) ? engine : null
55953
56897
  });
55954
56898
  r.reports.push(...out3.reports);
55955
56899
  r.guidance.push(...out3.guidance);
55956
- } else r.reports.push({ path: join39(configDir, "plugin", "ultra11y.js"), changed: false });
56900
+ } else r.reports.push({ path: join42(configDir, "plugin", "ultra11y.js"), changed: false });
55957
56901
  break;
55958
56902
  }
55959
56903
  case "agents-md": {
@@ -55991,7 +56935,7 @@ function uninstallForTargets(opts) {
55991
56935
  break;
55992
56936
  }
55993
56937
  case "opencode": {
55994
- const out3 = uninstallOpencode({ configDir: opts.project === true ? join39(cwd, ".opencode") : opencodeConfigDir() });
56938
+ const out3 = uninstallOpencode({ configDir: opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir() });
55995
56939
  r.reports.push(...out3.reports);
55996
56940
  r.guidance.push(...out3.guidance);
55997
56941
  break;
@@ -56012,18 +56956,18 @@ function statusReport(opts = {}) {
56012
56956
  const claudePath = claudeSettingsPath(opts.project === true, cwd);
56013
56957
  const root = projectRoot(cwd);
56014
56958
  const codexDir = codexHome();
56015
- const ocDir = opts.project === true ? join39(cwd, ".opencode") : opencodeConfigDir();
56959
+ const ocDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
56016
56960
  const codexOn = codexWired(codexDir) > 0;
56017
56961
  return [
56018
56962
  { target: "claude-code", wired: claudeCodeWired(claudePath) > 0, path: claudePath, note: "the plugin route wires this separately and does not show here" },
56019
56963
  {
56020
56964
  target: "codex",
56021
56965
  wired: codexOn,
56022
- path: join39(codexDir, "hooks.json"),
56966
+ path: join42(codexDir, "hooks.json"),
56023
56967
  // A wired hook behind a disabled feature flag never fires and looks fine. Say so.
56024
56968
  note: codexOn && !codexHooksEnabled(codexDir) ? "WIRED BUT INERT: `[features] hooks = true` is not set in config.toml" : void 0
56025
56969
  },
56026
- { target: "opencode", wired: opencodeWired(ocDir), path: join39(ocDir, "plugin", "ultra11y.js") },
56970
+ { target: "opencode", wired: opencodeWired(ocDir), path: join42(ocDir, "plugin", "ultra11y.js") },
56027
56971
  { target: "agents-md", wired: agentsMdWired(root), path: agentsMdPath(root) }
56028
56972
  ];
56029
56973
  }
@@ -56450,8 +57394,8 @@ function perPageTable(result, standard = CORE2, lang = "en") {
56450
57394
 
56451
57395
  // src/evidence.ts
56452
57396
  import { createHash as createHash6 } from "crypto";
56453
- import { existsSync as existsSync28, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "fs";
56454
- import { join as join40 } from "path";
57397
+ import { existsSync as existsSync30, mkdirSync as mkdirSync13, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "fs";
57398
+ import { join as join43 } from "path";
56455
57399
  var DEFAULT_GEOMETRY = { pad: 24, minWidth: 320, minHeight: 120, maxWidth: 960, maxHeight: 540 };
56456
57400
  var DEFAULT_CAPS = { perRule: 6, perPage: 12, total: 200 };
56457
57401
  var HALO = { r: 255, g: 255, b: 255, a: 0.92 };
@@ -56468,7 +57412,7 @@ function pageIdOfSnapshot(file) {
56468
57412
  }
56469
57413
  function readJson3(file) {
56470
57414
  try {
56471
- return JSON.parse(readFileSync23(file, "utf8"));
57415
+ return JSON.parse(readFileSync26(file, "utf8"));
56472
57416
  } catch {
56473
57417
  return void 0;
56474
57418
  }
@@ -56484,21 +57428,21 @@ function resolveScale(img, viewport, boxes) {
56484
57428
  }
56485
57429
  function loadPageEvidence(root, pageId) {
56486
57430
  const dir = snapshotDir(root, pageId);
56487
- const domFile = join40(dir, "dom.html");
56488
- const shotFile = join40(dir, "screen.png");
56489
- if (!existsSync28(domFile)) return { skip: "no-snapshot" };
56490
- if (!existsSync28(shotFile)) return { skip: "no-screenshot" };
57431
+ const domFile = join43(dir, "dom.html");
57432
+ const shotFile = join43(dir, "screen.png");
57433
+ if (!existsSync30(domFile)) return { skip: "no-snapshot" };
57434
+ if (!existsSync30(shotFile)) return { skip: "no-screenshot" };
56491
57435
  let dom;
56492
57436
  let shot;
56493
57437
  try {
56494
- dom = readFileSync23(domFile, "utf8");
56495
- shot = readFileSync23(shotFile);
57438
+ dom = readFileSync26(domFile, "utf8");
57439
+ shot = readFileSync26(shotFile);
56496
57440
  } catch {
56497
57441
  return { skip: "no-snapshot" };
56498
57442
  }
56499
57443
  const img = decodePng(shot);
56500
57444
  if (!img) return { skip: "unreadable-image" };
56501
- const digest = readJson3(join40(dir, "boxes.json"));
57445
+ const digest = readJson3(join43(dir, "boxes.json"));
56502
57446
  if (!digest) return { skip: "no-boxes" };
56503
57447
  const doc = parseHtml(dom, domFile);
56504
57448
  const boxes = alignedBoxes(dom, digest, doc);
@@ -56514,7 +57458,7 @@ function loadPageEvidence(root, pageId) {
56514
57458
  return { pageId, img, scale, viewport, ordinalOf, boxes, truncated: Boolean(digest.truncated) };
56515
57459
  }
56516
57460
  function metaOf(dir) {
56517
- const raw = readJson3(join40(dir, "meta.json"));
57461
+ const raw = readJson3(join43(dir, "meta.json"));
56518
57462
  if (raw === void 0) return void 0;
56519
57463
  const v = validateSnapshotMeta(raw);
56520
57464
  return v.ok && v.meta ? v.meta : void 0;
@@ -56679,7 +57623,7 @@ function writeEvidence(result, opts) {
56679
57623
  }
56680
57624
  continue;
56681
57625
  }
56682
- const dir = join40(opts.outDir, "assets", pageId);
57626
+ const dir = join43(opts.outDir, "assets", pageId);
56683
57627
  let wrote = false;
56684
57628
  const perRule = /* @__PURE__ */ new Map();
56685
57629
  const seen = /* @__PURE__ */ new Set();
@@ -56704,7 +57648,7 @@ function writeEvidence(result, opts) {
56704
57648
  continue;
56705
57649
  }
56706
57650
  const name2 = cropName(f);
56707
- const path = join40(dir, name2);
57651
+ const path = join43(dir, name2);
56708
57652
  try {
56709
57653
  if (!wrote) {
56710
57654
  mkdirSync13(dir, { recursive: true });
@@ -56742,8 +57686,8 @@ function writeEvidence(result, opts) {
56742
57686
  }
56743
57687
 
56744
57688
  // src/html-emit.ts
56745
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync24, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
56746
- import { join as join41 } from "path";
57689
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync27, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
57690
+ import { join as join44 } from "path";
56747
57691
 
56748
57692
  // src/html.ts
56749
57693
  var MARK4 = { C: "C", NC: "NC", NA: "\u2014", manual: "?" };
@@ -57305,7 +58249,7 @@ function inlineSize(path) {
57305
58249
  }
57306
58250
  function dataUri(path) {
57307
58251
  try {
57308
- return `data:image/png;base64,${readFileSync24(path).toString("base64")}`;
58252
+ return `data:image/png;base64,${readFileSync27(path).toString("base64")}`;
57309
58253
  } catch {
57310
58254
  return void 0;
57311
58255
  }
@@ -57376,7 +58320,7 @@ function writeHtml(result, opts) {
57376
58320
  return drawn;
57377
58321
  };
57378
58322
  const flat = opts.layout === "pages";
57379
- const sheetDir = flat ? opts.outDir : join41(opts.outDir, "pages");
58323
+ const sheetDir = flat ? opts.outDir : join44(opts.outDir, "pages");
57380
58324
  const up = flat ? "./" : "../";
57381
58325
  const compositeName = `ultra11y-${stdKey}-${result.date}.html`;
57382
58326
  const nav = flat ? [{ href: "./index.html", text: t2.pagesTitle }] : [
@@ -57395,13 +58339,13 @@ function writeHtml(result, opts) {
57395
58339
  nav: nav.map((n) => ({ ...n, current: n.href === `./${compositeName}` }))
57396
58340
  });
57397
58341
  if (notices.length) composite.blocks.unshift({ kind: "note", tone: "warn", runs: noticeRuns(notices) });
57398
- compositePath = join41(opts.outDir, compositeName);
58342
+ compositePath = join44(opts.outDir, compositeName);
57399
58343
  writeFileSync16(compositePath, renderHtmlDocument(composite));
57400
58344
  }
57401
58345
  const fileCrops = cropLookup(opts.evidence, lang, (_p, href) => href.replace(/^\.\//, up));
57402
58346
  const indexNav = nav.map((n) => ({ ...n, current: n.href === "./index.html" }));
57403
58347
  const index = flat ? pagesIndexDoc(result, { standard, lang, nav: indexNav, sheetHref: (id) => `./page-${id}.html` }) : indexDoc(result, { standard, lang, nav: indexNav, links: nav.filter((n) => n.href !== "./index.html") });
57404
- const indexPath = join41(opts.outDir, "index.html");
58348
+ const indexPath = join44(opts.outDir, "index.html");
57405
58349
  writeFileSync16(indexPath, renderHtmlDocument(index));
57406
58350
  const sheets = [];
57407
58351
  if (opts.pages) {
@@ -57414,7 +58358,7 @@ function writeHtml(result, opts) {
57414
58358
  ];
57415
58359
  if (!flat) {
57416
58360
  writeFileSync16(
57417
- join41(sheetDir, "index.html"),
58361
+ join44(sheetDir, "index.html"),
57418
58362
  renderHtmlDocument(
57419
58363
  pagesIndexDoc(result, {
57420
58364
  standard,
@@ -57424,7 +58368,7 @@ function writeHtml(result, opts) {
57424
58368
  })
57425
58369
  )
57426
58370
  );
57427
- sheets.push(join41(sheetDir, "index.html"));
58371
+ sheets.push(join44(sheetDir, "index.html"));
57428
58372
  }
57429
58373
  for (const p of derived) {
57430
58374
  const doc = pageDoc(result, p, {
@@ -57437,7 +58381,7 @@ function writeHtml(result, opts) {
57437
58381
  // beside the entry point, seen from wherever this sheet sits.
57438
58382
  ...opts.screenshots?.has(p.id) ? { screenshot: `${up}assets/${p.id}.png` } : {}
57439
58383
  });
57440
- const path = join41(sheetDir, `page-${p.id}.html`);
58384
+ const path = join44(sheetDir, `page-${p.id}.html`);
57441
58385
  writeFileSync16(path, renderHtmlDocument(doc));
57442
58386
  sheets.push(path);
57443
58387
  }
@@ -57458,8 +58402,8 @@ function noticeRuns(notices) {
57458
58402
 
57459
58403
  // src/dev.ts
57460
58404
  import { createServer } from "http";
57461
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "fs";
57462
- import { dirname as dirname12, join as join42 } from "path";
58405
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
58406
+ import { dirname as dirname12, join as join45 } from "path";
57463
58407
  var DEV_DEFAULT_PORT = 4111;
57464
58408
  function criterionLabel3(f, standard) {
57465
58409
  if (isCore(standard)) return `WCAG ${f.criteriaId}`;
@@ -57681,13 +58625,13 @@ function auditCollected(root, payload) {
57681
58625
  ...payload.css ? { css: payload.css } : {},
57682
58626
  ...shot ? { screenshotBase64: shot } : {}
57683
58627
  });
57684
- return { ok: true, result: runAudit({ inputs: [join42(dir, "dom.html")] }) };
58628
+ return { ok: true, result: runAudit({ inputs: [join45(dir, "dom.html")] }) };
57685
58629
  }
57686
58630
  function projectPages(root) {
57687
58631
  const snaps = readSnapshots(root);
57688
58632
  if (!snaps.length) return { result: null, pages: [] };
57689
58633
  const scope = pageScopesFrom(snaps);
57690
- const result = runAudit({ inputs: [join42(root, ".ultra11y/pages")] });
58634
+ const result = runAudit({ inputs: [join45(root, ".ultra11y/pages")] });
57691
58635
  result.scope.pages = scope;
57692
58636
  attributePages(result, scope);
57693
58637
  foldRecordedAdjudication(root, result);
@@ -57701,7 +58645,7 @@ function judgePages(result) {
57701
58645
  function foldRecordedAdjudication(root, fresh) {
57702
58646
  let prior;
57703
58647
  try {
57704
- prior = JSON.parse(readFileSync25(join42(root, "audits", "audit-latest.json"), "utf8"));
58648
+ prior = JSON.parse(readFileSync28(join45(root, "audits", "audit-latest.json"), "utf8"));
57705
58649
  } catch {
57706
58650
  return;
57707
58651
  }
@@ -57816,7 +58760,7 @@ function startDevServer(opts) {
57816
58760
  });
57817
58761
  let auditPath;
57818
58762
  if (applied.ok) {
57819
- auditPath = join42(opts.root, "audits", "audit-latest.json");
58763
+ auditPath = join45(opts.root, "audits", "audit-latest.json");
57820
58764
  mkdirSync15(dirname12(auditPath), { recursive: true });
57821
58765
  writeFileSync17(auditPath, `${JSON.stringify(applied.audit, null, 2)}
57822
58766
  `);
@@ -57845,10 +58789,10 @@ function startDevServer(opts) {
57845
58789
  }
57846
58790
  res.writeHead(404, { "content-type": "text/plain" }).end("not found");
57847
58791
  });
57848
- return new Promise((resolve13, reject) => {
58792
+ return new Promise((resolve15, reject) => {
57849
58793
  server.once("error", reject);
57850
58794
  server.listen(opts.port, "127.0.0.1", () => {
57851
- resolve13({
58795
+ resolve15({
57852
58796
  port: server.address().port,
57853
58797
  close: () => new Promise((r) => server.close(() => r()))
57854
58798
  });
@@ -58132,11 +59076,11 @@ function e2eSetupPlan(runners, paths, lang = "en") {
58132
59076
  }
58133
59077
 
58134
59078
  // src/orchestrate.ts
58135
- import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as readFileSync26, rmSync as rmSync7, writeFileSync as writeFileSync18 } from "fs";
58136
- import { join as join44, resolve as resolve10 } from "path";
59079
+ import { existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync29, rmSync as rmSync7, writeFileSync as writeFileSync18 } from "fs";
59080
+ import { join as join47, resolve as resolve12 } from "path";
58137
59081
 
58138
59082
  // src/orchestrate-templates.ts
58139
- import { join as join43 } from "path";
59083
+ import { join as join46 } from "path";
58140
59084
  var ONE_WRITER_FOOTER = `
58141
59085
  ## Return, don't write
58142
59086
 
@@ -58208,7 +59152,7 @@ var PHASE_SPECS = {
58208
59152
  title: "Adjudicate",
58209
59153
  schema: ADJUDICATE_SCHEMA,
58210
59154
  description: (n) => `Adjudicate the ${n} residual judgment criterion(ia) of an ultra11y audit (fan-out, fail-closed fold)`,
58211
- applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${join43(run2, "audit-latest.json")} --out ${run2}`
59155
+ applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${join46(run2, "audit-latest.json")} --out ${run2}`
58212
59156
  },
58213
59157
  "verify-report": {
58214
59158
  role: "refuter",
@@ -58230,7 +59174,7 @@ function toBatches(ids, batchSize) {
58230
59174
  }
58231
59175
  function phaseWorkflowScript(ph, runAbs, engineAbs, batchSize) {
58232
59176
  const spec = phaseSpec(ph.name);
58233
- const scriptPath = join43(runAbs, "orchestration", `${ph.name}.workflow.mjs`);
59177
+ const scriptPath = join46(runAbs, "orchestration", `${ph.name}.workflow.mjs`);
58234
59178
  const meta2 = { name: `ultra11y-${ph.name}`, description: spec.description(ph.items), phases: [{ title: spec.title }] };
58235
59179
  return [
58236
59180
  `export const meta = ${JSON.stringify(meta2)}`,
@@ -58274,7 +59218,7 @@ function agentContracts(runAbs, engineAbs) {
58274
59218
 
58275
59219
  You adjudicate the residual judgment criteria of an ultra11y audit \u2014 the ones the deterministic engine could not decide (alt-text relevance, link purpose in context, reading order\u2026). The ACTIVE STANDARD is recorded in the worklist's \`standard\` field: under a country standard (e.g. \`rgaa\`) the items are that standard's OWN criteria, each carrying its numbered tests \u2014 not WCAG success criteria.
58276
59220
 
58277
- Worklist: \`${join43(runAbs, "ADJUDICATE.todo.json")}\` (an object with \`kind: "adjudication"\` and \`items[]\`). Handle ONLY the criteria whose \`criteriaId\` is named in your prompt (\`ITEMS=<id,\u2026>\`).
59221
+ Worklist: \`${join46(runAbs, "ADJUDICATE.todo.json")}\` (an object with \`kind: "adjudication"\` and \`items[]\`). Handle ONLY the criteria whose \`criteriaId\` is named in your prompt (\`ITEMS=<id,\u2026>\`).
58278
59222
 
58279
59223
  For EACH of your criteria:
58280
59224
 
@@ -58292,7 +59236,7 @@ ${footer}`,
58292
59236
 
58293
59237
  You are an adversarial skeptic verifying the non-conformities of an ultra11y report. Your job is to try to REFUTE each claim: assume it is wrong until the source proves it.
58294
59238
 
58295
- Worklist: \`${join43(runAbs, "VERIFY.todo.json")}\` (a JSON array; each entry has \`n\`, \`criteriaId\`, \`file\`, \`line\`, \`selector\`, \`claim\`). Handle ONLY the entries whose \`n\` is named in your prompt (\`ITEMS=<n,\u2026>\`).
59239
+ Worklist: \`${join46(runAbs, "VERIFY.todo.json")}\` (a JSON array; each entry has \`n\`, \`criteriaId\`, \`file\`, \`line\`, \`selector\`, \`claim\`). Handle ONLY the entries whose \`n\` is named in your prompt (\`ITEMS=<n,\u2026>\`).
58296
59240
 
58297
59241
  For EACH of your entries:
58298
59242
 
@@ -58328,14 +59272,14 @@ ${status}
58328
59272
 
58329
59273
  ## The loop (play every role yourself, one item at a time)
58330
59274
 
58331
- 1. **Audit** (if not done): \`${engine} audit "<globs>" --graph --out ${runAbs}\` \u2192 \`${join43(runAbs, "audit-latest.json")}\`.
58332
- 2. **Adjudicate the residual criteria** \u2014 \`${engine} verify --manual --in ${join43(runAbs, "audit-latest.json")} --out ${runAbs}\` writes \`${join43(runAbs, "ADJUDICATE.todo.json")}\`. For EVERY item, apply \`${join43(runAbs, "orchestration", "agents", "adjudicator.md")}\` yourself (read the evidence, rule C/NC/NA/manual, fill the required justification/findings/reason IN the todo file). Then fold, fail-closed: \`${engine} verify --apply ${join43(runAbs, "ADJUDICATE.todo.json")} --in ${join43(runAbs, "audit-latest.json")} --out ${runAbs}\`.
58333
- 3. **Report**: \`${engine} report --in ${join43(runAbs, "audit-latest.json")} --out ${runAbs}\`.
58334
- 4. **Verify the report's claims** \u2014 \`${engine} verify --report <the report .md> --out ${runAbs}\` writes \`${join43(runAbs, "VERIFY.todo.json")}\`. For EVERY entry, apply \`${join43(runAbs, "orchestration", "agents", "refuter.md")}\` yourself (open file:line, verdict supported/partial/refuted/unsupported + note IN the todo file). Then: \`${engine} verify --apply ${join43(runAbs, "VERIFY.todo.json")} --report <the report .md>\`.
59275
+ 1. **Audit** (if not done): \`${engine} audit "<globs>" --graph --out ${runAbs}\` \u2192 \`${join46(runAbs, "audit-latest.json")}\`.
59276
+ 2. **Adjudicate the residual criteria** \u2014 \`${engine} verify --manual --in ${join46(runAbs, "audit-latest.json")} --out ${runAbs}\` writes \`${join46(runAbs, "ADJUDICATE.todo.json")}\`. For EVERY item, apply \`${join46(runAbs, "orchestration", "agents", "adjudicator.md")}\` yourself (read the evidence, rule C/NC/NA/manual, fill the required justification/findings/reason IN the todo file). Then fold, fail-closed: \`${engine} verify --apply ${join46(runAbs, "ADJUDICATE.todo.json")} --in ${join46(runAbs, "audit-latest.json")} --out ${runAbs}\`.
59277
+ 3. **Report**: \`${engine} report --in ${join46(runAbs, "audit-latest.json")} --out ${runAbs}\`.
59278
+ 4. **Verify the report's claims** \u2014 \`${engine} verify --report <the report .md> --out ${runAbs}\` writes \`${join46(runAbs, "VERIFY.todo.json")}\`. For EVERY entry, apply \`${join46(runAbs, "orchestration", "agents", "refuter.md")}\` yourself (open file:line, verdict supported/partial/refuted/unsupported + note IN the todo file). Then: \`${engine} verify --apply ${join46(runAbs, "VERIFY.todo.json")} --report <the report .md>\`.
58335
59279
  5. **Gate**: \`${engine} check --report <the report .md> --semantic\` must exit 0 before presenting anything.
58336
59280
  6. **Fix & re-audit**: \`${engine} fix <globs> --write --iterate\`, hand-apply the judgment fixes, then loop from step 1 until the gate stays green.
58337
59281
 
58338
- With subagents available, prefer the emitted workflows instead: \`orchestrate --run ${runAbs} --phase <p>\` then \`Workflow({ scriptPath: "${join43(runAbs, "orchestration", "<p>.workflow.mjs")}" })\` \u2014 you stay the sole writer either way.
59282
+ With subagents available, prefer the emitted workflows instead: \`orchestrate --run ${runAbs} --phase <p>\` then \`Workflow({ scriptPath: "${join46(runAbs, "orchestration", "<p>.workflow.mjs")}" })\` \u2014 you stay the sole writer either way.
58339
59283
  `;
58340
59284
  }
58341
59285
 
@@ -58344,13 +59288,13 @@ var PHASES = ["adjudicate", "verify-report"];
58344
59288
  var SMALL_WORKLIST = 3;
58345
59289
  var BATCH_SIZE2 = 8;
58346
59290
  function listPhases(runDir, engineAbs) {
58347
- const run2 = resolve10(runDir);
58348
- const adjPath = join44(run2, "ADJUDICATE.todo.json");
59291
+ const run2 = resolve12(runDir);
59292
+ const adjPath = join47(run2, "ADJUDICATE.todo.json");
58349
59293
  let adjIds = [];
58350
59294
  let adjReady = false;
58351
- if (existsSync29(adjPath)) {
59295
+ if (existsSync31(adjPath)) {
58352
59296
  try {
58353
- const f = JSON.parse(readFileSync26(adjPath, "utf8"));
59297
+ const f = JSON.parse(readFileSync29(adjPath, "utf8"));
58354
59298
  if (f && f.kind === "adjudication" && Array.isArray(f.items)) {
58355
59299
  adjReady = true;
58356
59300
  adjIds = f.items.map((i2) => i2.criteriaId);
@@ -58358,12 +59302,12 @@ function listPhases(runDir, engineAbs) {
58358
59302
  } catch {
58359
59303
  }
58360
59304
  }
58361
- const verPath = join44(run2, "VERIFY.todo.json");
59305
+ const verPath = join47(run2, "VERIFY.todo.json");
58362
59306
  let verIds = [];
58363
59307
  let verReady = false;
58364
- if (existsSync29(verPath)) {
59308
+ if (existsSync31(verPath)) {
58365
59309
  try {
58366
- const items = JSON.parse(readFileSync26(verPath, "utf8"));
59310
+ const items = JSON.parse(readFileSync29(verPath, "utf8"));
58367
59311
  if (Array.isArray(items)) {
58368
59312
  verReady = true;
58369
59313
  verIds = items.map((i2) => String(i2.n));
@@ -58378,7 +59322,7 @@ function listPhases(runDir, engineAbs) {
58378
59322
  worklist: adjPath,
58379
59323
  items: adjIds.length,
58380
59324
  ids: adjIds,
58381
- prerequisite: `node ${engineAbs} verify --manual --in ${join44(run2, "audit-latest.json")} --out ${run2}`
59325
+ prerequisite: `node ${engineAbs} verify --manual --in ${join47(run2, "audit-latest.json")} --out ${run2}`
58382
59326
  },
58383
59327
  {
58384
59328
  name: "verify-report",
@@ -58391,8 +59335,8 @@ function listPhases(runDir, engineAbs) {
58391
59335
  ];
58392
59336
  }
58393
59337
  function orchestrateRun(runDir, engineAbs, opts = {}) {
58394
- const run2 = resolve10(runDir);
58395
- if (!existsSync29(run2)) {
59338
+ const run2 = resolve12(runDir);
59339
+ if (!existsSync31(run2)) {
58396
59340
  return { exitCode: 2, written: [], notices: [], errors: [`run dir not found: ${run2}`], phases: [] };
58397
59341
  }
58398
59342
  const phases = listPhases(run2, engineAbs);
@@ -58419,22 +59363,22 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
58419
59363
  }
58420
59364
  selected = [ph];
58421
59365
  }
58422
- const orchDir = join44(run2, "orchestration");
58423
- const agentsDir = join44(orchDir, "agents");
58424
- mkdirSync16(join44(orchDir, "out"), { recursive: true });
59366
+ const orchDir = join47(run2, "orchestration");
59367
+ const agentsDir = join47(orchDir, "agents");
59368
+ mkdirSync16(join47(orchDir, "out"), { recursive: true });
58425
59369
  mkdirSync16(agentsDir, { recursive: true });
58426
59370
  const written = [];
58427
59371
  const notices = [];
58428
59372
  for (const [name2, content] of Object.entries(agentContracts(run2, engineAbs))) {
58429
- const p = join44(agentsDir, `${name2}.md`);
59373
+ const p = join47(agentsDir, `${name2}.md`);
58430
59374
  writeFileSync18(p, content);
58431
59375
  written.push(p);
58432
59376
  }
58433
59377
  const emitted = new Set(opts.eco ? [] : selected.filter((p) => p.items > 0).map((p) => p.name));
58434
59378
  for (const ph of phases) {
58435
59379
  if (emitted.has(ph.name)) continue;
58436
- const stale = join44(orchDir, `${ph.name}.workflow.mjs`);
58437
- if (existsSync29(stale)) {
59380
+ const stale = join47(orchDir, `${ph.name}.workflow.mjs`);
59381
+ if (existsSync31(stale)) {
58438
59382
  rmSync7(stale, { force: true });
58439
59383
  notices.push(`phase "${ph.name}": stale workflow removed \u2014 its worklist ${ph.ready ? "is now empty" : "no longer exists"}.`);
58440
59384
  }
@@ -58448,12 +59392,12 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
58448
59392
  if (ph.items <= SMALL_WORKLIST) {
58449
59393
  notices.push(`phase "${ph.name}": only ${ph.items} item(s) \u2014 the sequential --eco path is equivalent and cheaper.`);
58450
59394
  }
58451
- const p = join44(orchDir, `${ph.name}.workflow.mjs`);
59395
+ const p = join47(orchDir, `${ph.name}.workflow.mjs`);
58452
59396
  writeFileSync18(p, phaseWorkflowScript(ph, run2, engineAbs, BATCH_SIZE2));
58453
59397
  written.push(p);
58454
59398
  }
58455
59399
  }
58456
- const rb = join44(orchDir, "RUNBOOK.md");
59400
+ const rb = join47(orchDir, "RUNBOOK.md");
58457
59401
  writeFileSync18(rb, runbookMd(phases, run2, engineAbs));
58458
59402
  written.push(rb);
58459
59403
  return { exitCode: 0, written, notices, errors: [], phases };
@@ -58463,8 +59407,8 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
58463
59407
  import { createInterface as createInterface2 } from "readline";
58464
59408
 
58465
59409
  // src/mcp/handlers.ts
58466
- import { existsSync as existsSync30, readFileSync as readFileSync27, realpathSync as realpathSync4, statSync as statSync12 } from "fs";
58467
- import { isAbsolute as isAbsolute3, join as join45, resolve as resolve11, sep as sep5 } from "path";
59410
+ import { existsSync as existsSync32, readFileSync as readFileSync30, realpathSync as realpathSync4, statSync as statSync12 } from "fs";
59411
+ import { isAbsolute as isAbsolute3, join as join48, resolve as resolve13, sep as sep5 } from "path";
58468
59412
 
58469
59413
  // src/project-lock.ts
58470
59414
  var chains = /* @__PURE__ */ new Map();
@@ -58511,8 +59455,8 @@ function positive(v, key) {
58511
59455
  function requiredCwd(args2, defaults) {
58512
59456
  const cwd = str2(args2.cwd) ?? defaults.defaultCwd;
58513
59457
  if (!cwd) throw new ToolError("`cwd` is required: an absolute path to the project root.");
58514
- const abs = resolve11(cwd);
58515
- if (!existsSync30(abs)) throw new ToolError(`project root not found: ${abs}`);
59458
+ const abs = resolve13(cwd);
59459
+ if (!existsSync32(abs)) throw new ToolError(`project root not found: ${abs}`);
58516
59460
  if (!statSync12(abs).isDirectory()) throw new ToolError(`\`cwd\` is not a directory: ${abs}`);
58517
59461
  return abs;
58518
59462
  }
@@ -58578,7 +59522,7 @@ function audit(args2, cwd) {
58578
59522
  process.chdir(cwd);
58579
59523
  const inputs = strArray2(args2.globs) ?? DEFAULT_GLOBS;
58580
59524
  const scopedToDiff = bool(args2.changed) || bool(args2.staged) || str2(args2.since) !== void 0;
58581
- const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff && existsSync30(d) && !inputs.includes(d));
59525
+ const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff && existsSync32(d) && !inputs.includes(d));
58582
59526
  const result = runAudit({
58583
59527
  inputs: [...inputs, ...extra],
58584
59528
  include: strArray2(args2.include),
@@ -58697,13 +59641,13 @@ function handlePackCheck(args2, cwd) {
58697
59641
  return { cwd, ...res };
58698
59642
  }
58699
59643
  function handleSampleCheck(cwd, standard) {
58700
- const file = join45(cwd, ".ultra11yrc.json");
58701
- if (!existsSync30(file)) {
59644
+ const file = join48(cwd, ".ultra11yrc.json");
59645
+ if (!existsSync32(file)) {
58702
59646
  throw new ToolError(`no .ultra11yrc.json at ${cwd} \u2014 a page sample must be declared before it can be linted.`);
58703
59647
  }
58704
59648
  let raw;
58705
59649
  try {
58706
- raw = JSON.parse(readFileSync27(file, "utf8"));
59650
+ raw = JSON.parse(readFileSync30(file, "utf8"));
58707
59651
  } catch (e) {
58708
59652
  throw new ToolError(`.ultra11yrc.json is not valid JSON: ${e.message}`);
58709
59653
  }
@@ -58794,10 +59738,10 @@ function handleInit(args2, cwd) {
58794
59738
  throw new ToolError(`\`fail_on\` must be one of: error, warning, notice (got "${failOnRaw}")`);
58795
59739
  }
58796
59740
  const failOn = failOnRaw;
58797
- const enginePath = resolve11("scripts/ultra11y.mjs");
59741
+ const enginePath = resolve13("scripts/ultra11y.mjs");
58798
59742
  const written = [];
58799
59743
  if (bool(args2.hook)) written.push(writeHook(cwd, enginePath, failOn));
58800
- if (bool(args2.ci)) written.push(join45(cwd, ".github/workflows/a11y.yml"));
59744
+ if (bool(args2.ci)) written.push(join48(cwd, ".github/workflows/a11y.yml"));
58801
59745
  if (!written.length) {
58802
59746
  throw new ToolError("nothing to install \u2014 pass hook:true and/or ci:true.");
58803
59747
  }
@@ -58817,8 +59761,8 @@ function reportText(args2, tool) {
58817
59761
  if (inline) return inline;
58818
59762
  if (!file) throw new ToolError(`\`report_text\` is required \u2014 the report markdown for ultra11y_${tool} to work on.`);
58819
59763
  if (!isAbsolute3(file)) throw new ToolError("`report_file` must be an absolute path.");
58820
- if (!existsSync30(file)) throw new ToolError(`report file not found: ${file}`);
58821
- return readFileSync27(file, "utf8");
59764
+ if (!existsSync32(file)) throw new ToolError(`report file not found: ${file}`);
59765
+ return readFileSync30(file, "utf8");
58822
59766
  }
58823
59767
  function handleCriteria(args2) {
58824
59768
  const lang = langOf(args2);
@@ -58833,7 +59777,7 @@ function handleCriteria(args2) {
58833
59777
  function handleRead(args2, cwd) {
58834
59778
  const raw = str2(args2.path);
58835
59779
  if (!raw) throw new ToolError("`path` is required \u2014 relative to the project root, or an absolute path inside it.");
58836
- const target = isAbsolute3(raw) ? raw : join45(cwd, raw);
59780
+ const target = isAbsolute3(raw) ? raw : join48(cwd, raw);
58837
59781
  let real;
58838
59782
  try {
58839
59783
  real = realpathSync4(target);
@@ -58847,7 +59791,7 @@ function handleRead(args2, cwd) {
58847
59791
  const st = statSync12(real);
58848
59792
  if (!st.isFile()) throw new ToolError(`not a file: ${raw}`);
58849
59793
  if (st.size > MAX_READ_BYTES) throw new ToolError(`file is too large to read (${st.size} bytes): ${raw}`);
58850
- const lines = readFileSync27(real, "utf8").split("\n");
59794
+ const lines = readFileSync30(real, "utf8").split("\n");
58851
59795
  const total = lines.length;
58852
59796
  const start2 = Math.max(1, Math.floor(num2(args2.start_line) ?? 1));
58853
59797
  if (start2 > total) throw new ToolError(`start_line ${start2} is past the end of the file (${total} lines).`);
@@ -59393,25 +60337,25 @@ function str3(v) {
59393
60337
  var DECLARED = new Set([...TOOLS2, ...WRITE_TOOLS].map((t2) => t2.name));
59394
60338
 
59395
60339
  // src/mcp/resources.ts
59396
- import { existsSync as existsSync31, readdirSync as readdirSync6, readFileSync as readFileSync28, realpathSync as realpathSync5, statSync as statSync13 } from "fs";
59397
- import { basename as basename3, dirname as dirname13, join as join46, resolve as resolve12, sep as sep6 } from "path";
60340
+ import { existsSync as existsSync33, readdirSync as readdirSync6, readFileSync as readFileSync31, realpathSync as realpathSync5, statSync as statSync13 } from "fs";
60341
+ import { basename as basename3, dirname as dirname13, join as join49, resolve as resolve14, sep as sep6 } from "path";
59398
60342
  import { fileURLToPath as fileURLToPath4 } from "url";
59399
60343
  var SKILL_NAME = "ultra11y";
59400
60344
  var URI_SCHEME = "skill://";
59401
60345
  function resolveSkillRoot(moduleDir) {
59402
60346
  const here = moduleDir ?? dirname13(fileURLToPath4(import.meta.url));
59403
- const candidates2 = [resolve12(here, ".."), resolve12(here, "..", "skills", SKILL_NAME), resolve12(here, "..", "..", "skills", SKILL_NAME)];
59404
- return candidates2.find((dir) => existsSync31(join46(dir, "SKILL.md")));
60347
+ const candidates2 = [resolve14(here, ".."), resolve14(here, "..", "skills", SKILL_NAME), resolve14(here, "..", "..", "skills", SKILL_NAME)];
60348
+ return candidates2.find((dir) => existsSync33(join49(dir, "SKILL.md")));
59405
60349
  }
59406
60350
  function listResources(moduleDir) {
59407
60351
  const root = resolveSkillRoot(moduleDir);
59408
60352
  if (!root) return [];
59409
60353
  const out2 = [describe(root, "SKILL.md", `${SKILL_NAME}: the skill`)];
59410
- const refDir = join46(root, "references");
59411
- if (!existsSync31(refDir)) return out2;
60354
+ const refDir = join49(root, "references");
60355
+ if (!existsSync33(refDir)) return out2;
59412
60356
  for (const file of readdirSync6(refDir).sort()) {
59413
60357
  if (!file.endsWith(".md")) continue;
59414
- out2.push(describe(root, join46("references", file), `${SKILL_NAME} reference: ${basename3(file, ".md")}`));
60358
+ out2.push(describe(root, join49("references", file), `${SKILL_NAME} reference: ${basename3(file, ".md")}`));
59415
60359
  }
59416
60360
  return out2;
59417
60361
  }
@@ -59423,7 +60367,7 @@ function readResource(uri, moduleDir) {
59423
60367
  if (!root) throw new ResourceError("no skill payload found next to this build \u2014 nothing to read");
59424
60368
  const rel2 = uri.slice(URI_SCHEME.length);
59425
60369
  if (!rel2) throw new ResourceError("empty resource path");
59426
- const target = resolve12(root, rel2);
60370
+ const target = resolve14(root, rel2);
59427
60371
  const rootReal = realpathSync5(root);
59428
60372
  let targetReal;
59429
60373
  try {
@@ -59435,7 +60379,7 @@ function readResource(uri, moduleDir) {
59435
60379
  throw new ResourceError(`resource path escapes the skill root: ${uri}`);
59436
60380
  }
59437
60381
  if (!statSync13(targetReal).isFile()) throw new ResourceError(`not a file: ${uri}`);
59438
- return { uri, mimeType: "text/markdown", text: readFileSync28(targetReal, "utf8") };
60382
+ return { uri, mimeType: "text/markdown", text: readFileSync31(targetReal, "utf8") };
59439
60383
  }
59440
60384
  var ResourceError = class extends Error {
59441
60385
  };
@@ -59446,14 +60390,14 @@ function describe(root, rel2, fallbackTitle) {
59446
60390
  title: fallbackTitle,
59447
60391
  mimeType: "text/markdown"
59448
60392
  };
59449
- const summary = firstProse(join46(root, rel2));
60393
+ const summary = firstProse(join49(root, rel2));
59450
60394
  if (summary) decl.description = summary;
59451
60395
  return decl;
59452
60396
  }
59453
60397
  function firstProse(file) {
59454
60398
  let text;
59455
60399
  try {
59456
- text = readFileSync28(file, "utf8");
60400
+ text = readFileSync31(file, "utf8");
59457
60401
  } catch {
59458
60402
  return void 0;
59459
60403
  }
@@ -59704,14 +60648,14 @@ function startHttpServer(opts = {}) {
59704
60648
  server.requestTimeout = 0;
59705
60649
  server.headersTimeout = 6e4;
59706
60650
  server.keepAliveTimeout = 12e4;
59707
- return new Promise((resolve13, reject) => {
60651
+ return new Promise((resolve15, reject) => {
59708
60652
  server.once("error", reject);
59709
60653
  server.listen(opts.port ?? 0, bind, () => {
59710
60654
  server.removeListener("error", reject);
59711
60655
  const addr2 = server.address();
59712
60656
  const port = typeof addr2 === "object" && addr2 ? addr2.port : opts.port ?? 0;
59713
60657
  const host = bind.includes(":") ? `[${bind}]` : bind;
59714
- resolve13({
60658
+ resolve15({
59715
60659
  server,
59716
60660
  port,
59717
60661
  url: `http://${host}:${port}${MCP_PATH}`,
@@ -59820,7 +60764,7 @@ function sendJson(res, status, body2, origin, extra = {}) {
59820
60764
  }
59821
60765
  var DRAIN_LIMIT = MAX_BODY_BYTES * 8;
59822
60766
  function readBody2(req) {
59823
- return new Promise((resolve13, reject) => {
60767
+ return new Promise((resolve15, reject) => {
59824
60768
  const chunks = [];
59825
60769
  let size = 0;
59826
60770
  let over = false;
@@ -59844,7 +60788,7 @@ function readBody2(req) {
59844
60788
  });
59845
60789
  req.on("end", () => {
59846
60790
  if (over) reject(new Error("too large"));
59847
- else resolve13(Buffer.concat(chunks).toString("utf8"));
60791
+ else resolve15(Buffer.concat(chunks).toString("utf8"));
59848
60792
  });
59849
60793
  req.on("error", reject);
59850
60794
  req.on("aborted", () => reject(new Error("client aborted the request")));
@@ -60575,9 +61519,9 @@ async function cmdAudit(p) {
60575
61519
  const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
60576
61520
  const capturesDir = capturesFlag ?? CAPTURES_DIR;
60577
61521
  const scopedToDiff = p.flags.changed === true || p.flags.staged === true || since !== void 0;
60578
- const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 || existsSync32(capturesDir));
61522
+ const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 || existsSync34(capturesDir));
60579
61523
  const useCaptures = capturesWanted && !scopedToDiff && !inputs.includes(capturesDir);
60580
- const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && existsSync32(PAGES_DIR);
61524
+ const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && existsSync34(PAGES_DIR);
60581
61525
  const usePages = pagesWanted && !scopedToDiff && !inputs.includes(PAGES_DIR);
60582
61526
  const pagesInScope = pagesWanted && !scopedToDiff;
60583
61527
  const auditInputs = [...inputs, ...useCaptures ? [capturesDir] : [], ...usePages ? [PAGES_DIR] : []];
@@ -60619,7 +61563,7 @@ async function cmdAudit(p) {
60619
61563
  if (typeof p.flags.out === "string") {
60620
61564
  const out2 = p.flags.out;
60621
61565
  const asFile = out2.toLowerCase().endsWith(".json");
60622
- const target = asFile ? out2 : join47(out2, "audit-latest.json");
61566
+ const target = asFile ? out2 : join50(out2, "audit-latest.json");
60623
61567
  try {
60624
61568
  mkdirSync17(asFile ? dirname14(out2) : out2, { recursive: true });
60625
61569
  writeFileSync19(target, JSON.stringify(result, null, 2) + "\n");
@@ -60641,7 +61585,7 @@ async function cmdAudit(p) {
60641
61585
  const baselineFlag = p.flags.baseline;
60642
61586
  if (typeof baselineFlag === "string" && baselineFlag) {
60643
61587
  let baseline = null;
60644
- if (existsSync32(baselineFlag)) {
61588
+ if (existsSync34(baselineFlag)) {
60645
61589
  try {
60646
61590
  const parsed = JSON.parse(readText(baselineFlag));
60647
61591
  if (isCurrentAudit(parsed)) baseline = parsed;
@@ -60695,11 +61639,11 @@ async function cmdDev(p) {
60695
61639
  return 2;
60696
61640
  }
60697
61641
  if (p.flags.next === true) {
60698
- const dir = join47(root, ".ultra11y", "next");
61642
+ const dir = join50(root, ".ultra11y", "next");
60699
61643
  const rel2 = ".ultra11y/next/overlay.jsx";
60700
61644
  try {
60701
61645
  mkdirSync17(dir, { recursive: true });
60702
- writeFileSync19(join47(dir, "overlay.jsx"), nextOverlayComponent(port));
61646
+ writeFileSync19(join50(dir, "overlay.jsx"), nextOverlayComponent(port));
60703
61647
  } catch (e) {
60704
61648
  console.error(`ultra11y dev: could not write ${rel2}: ${e instanceof Error ? e.message : String(e)}`);
60705
61649
  return 1;
@@ -60730,9 +61674,9 @@ async function cmdDev(p) {
60730
61674
  }
60731
61675
  console.error(fr ? `ultra11y dev : tableau de bord sur http://127.0.0.1:${server.port}` : `ultra11y dev: dashboard on http://127.0.0.1:${server.port}`);
60732
61676
  console.error(fr ? "Boucle locale uniquement (l'outil \xE9crit des fichiers). Ctrl-C pour arr\xEAter." : "Loopback only (the tool writes files). Ctrl-C to stop.");
60733
- await new Promise((resolve13) => {
61677
+ await new Promise((resolve15) => {
60734
61678
  const stop2 = () => {
60735
- void server.close().then(resolve13);
61679
+ void server.close().then(resolve15);
60736
61680
  };
60737
61681
  process.once("SIGINT", stop2);
60738
61682
  process.once("SIGTERM", stop2);
@@ -60748,7 +61692,7 @@ async function cmdPagesDiscover(p) {
60748
61692
  const snaps = sampleFromSnapshots(readSnapshots(root));
60749
61693
  if (!snaps.length) {
60750
61694
  console.error(
60751
- lang === "fr" ? `ultra11y pages discover : aucun instantan\xE9 sous ${join47(root, PAGES_DIR)} \u2014 lancez d'abord vos tests E2E avec checkA11y, ou \`scan --sample\`.` : `ultra11y pages discover: no snapshot under ${join47(root, PAGES_DIR)} \u2014 run your E2E tests with checkA11y first, or \`scan --sample\`.`
61695
+ lang === "fr" ? `ultra11y pages discover : aucun instantan\xE9 sous ${join50(root, PAGES_DIR)} \u2014 lancez d'abord vos tests E2E avec checkA11y, ou \`scan --sample\`.` : `ultra11y pages discover: no snapshot under ${join50(root, PAGES_DIR)} \u2014 run your E2E tests with checkA11y first, or \`scan --sample\`.`
60752
61696
  );
60753
61697
  return 1;
60754
61698
  }
@@ -60828,9 +61772,9 @@ ${found} page(s) discovered, ${merged.added.length} new. Nothing was written \u2
60828
61772
  }
60829
61773
  return 0;
60830
61774
  }
60831
- const file = join47(process.cwd(), ".ultra11yrc.json");
61775
+ const file = join50(process.cwd(), ".ultra11yrc.json");
60832
61776
  let doc = {};
60833
- if (existsSync32(file)) {
61777
+ if (existsSync34(file)) {
60834
61778
  try {
60835
61779
  doc = JSON.parse(readText(file));
60836
61780
  } catch {
@@ -60899,27 +61843,27 @@ async function cmdPages(p) {
60899
61843
  const derived = derivePages(result, scope);
60900
61844
  const split = p.flags.split === "page";
60901
61845
  const outDir = typeof p.flags.out === "string" && p.flags.out ? p.flags.out : void 0;
60902
- const shotOf = (id) => join47(PAGES_DIR, id, "screen.png");
61846
+ const shotOf = (id) => join50(PAGES_DIR, id, "screen.png");
60903
61847
  const shotsRelative = (fileDir) => {
60904
61848
  const m = /* @__PURE__ */ new Map();
60905
61849
  for (const pg of derived) {
60906
- if (existsSync32(shotOf(pg.id))) m.set(pg.id, relative5(fileDir, shotOf(pg.id)).split("\\").join("/"));
61850
+ if (existsSync34(shotOf(pg.id))) m.set(pg.id, relative5(fileDir, shotOf(pg.id)).split("\\").join("/"));
60907
61851
  }
60908
61852
  return m;
60909
61853
  };
60910
61854
  const shotPaths = (pages) => {
60911
61855
  const m = /* @__PURE__ */ new Map();
60912
- for (const pg of pages) if (existsSync32(shotOf(pg.id))) m.set(pg.id, shotOf(pg.id));
61856
+ for (const pg of pages) if (existsSync34(shotOf(pg.id))) m.set(pg.id, shotOf(pg.id));
60913
61857
  return m;
60914
61858
  };
60915
61859
  const shotsCopiedInto = (dir) => {
60916
61860
  const m = /* @__PURE__ */ new Map();
60917
- const assets = join47(dir, "assets");
61861
+ const assets = join50(dir, "assets");
60918
61862
  for (const pg of derived) {
60919
61863
  const src = shotOf(pg.id);
60920
- if (!existsSync32(src)) continue;
61864
+ if (!existsSync34(src)) continue;
60921
61865
  mkdirSync17(assets, { recursive: true });
60922
- copyFileSync4(src, join47(assets, `${pg.id}.png`));
61866
+ copyFileSync4(src, join50(assets, `${pg.id}.png`));
60923
61867
  m.set(pg.id, `./assets/${pg.id}.png`);
60924
61868
  }
60925
61869
  return m;
@@ -60945,7 +61889,7 @@ async function cmdPages(p) {
60945
61889
  if (manifest) {
60946
61890
  const total = evidenceNotice(manifest, null, lang);
60947
61891
  console.error(
60948
- lang === "fr" ? `ultra11y : ${manifest.totals.imaged} vignette(s) \xE9crite(s) dans ${join47(outDir, "assets")}.` : `ultra11y: ${manifest.totals.imaged} crop(s) written to ${join47(outDir, "assets")}.`
61892
+ lang === "fr" ? `ultra11y : ${manifest.totals.imaged} vignette(s) \xE9crite(s) dans ${join50(outDir, "assets")}.` : `ultra11y: ${manifest.totals.imaged} crop(s) written to ${join50(outDir, "assets")}.`
60949
61893
  );
60950
61894
  for (const line of total) console.error(line);
60951
61895
  }
@@ -60954,7 +61898,7 @@ async function cmdPages(p) {
60954
61898
  ...manifest ? { evidenceNotice: (id) => evidenceNotice(manifest, id, lang) } : {}
60955
61899
  };
60956
61900
  if (!split) {
60957
- const file = join47(outDir, `pages-${result.date}.md`);
61901
+ const file = join50(outDir, `pages-${result.date}.md`);
60958
61902
  writeFileSync19(file, `${renderPagesDocument(result, derived, { standard, lang, screenshots: shotsCopiedInto(outDir), ...evidenceOpts })}
60959
61903
  `);
60960
61904
  const html2 = wantHtml ? emitHtml(result, {
@@ -60974,10 +61918,10 @@ async function cmdPages(p) {
60974
61918
  const sheet = (id) => `page-${id}.md`;
60975
61919
  const hrefs = new Map(derived.map((pg) => [pg.id, `./${sheet(pg.id)}`]));
60976
61920
  for (const pg of derived) {
60977
- writeFileSync19(join47(outDir, sheet(pg.id)), `${renderPageDocument(result, pg, { standard, lang, screenshots: shots, ...evidenceOpts })}
61921
+ writeFileSync19(join50(outDir, sheet(pg.id)), `${renderPageDocument(result, pg, { standard, lang, screenshots: shots, ...evidenceOpts })}
60978
61922
  `);
60979
61923
  }
60980
- const index = join47(outDir, "index.md");
61924
+ const index = join50(outDir, "index.md");
60981
61925
  writeFileSync19(index, `${renderPagesIndex(result, derived, { standard, lang, hrefs })}
60982
61926
  `);
60983
61927
  const html = wantHtml ? emitHtml(result, {
@@ -61029,7 +61973,7 @@ async function cmdSnapshot(p) {
61029
61973
  2
61030
61974
  )
61031
61975
  );
61032
- else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${join47(root, PAGES_DIR)}.` : `No snapshot in ${join47(root, PAGES_DIR)}.`);
61976
+ else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${join50(root, PAGES_DIR)}.` : `No snapshot in ${join50(root, PAGES_DIR)}.`);
61033
61977
  else for (const s of snaps) console.log(`${s.meta.id} ${s.meta.name} ${s.meta.url}${s.meta.auth ? " [auth]" : ""}`);
61034
61978
  return 0;
61035
61979
  }
@@ -61076,7 +62020,7 @@ async function cmdSnapshot(p) {
61076
62020
  console.error(`ultra11y snapshot write: could not write the snapshot: ${e instanceof Error ? e.message : String(e)}`);
61077
62021
  return 1;
61078
62022
  }
61079
- const result = runAudit({ inputs: [join47(dir, "dom.html")], onWarn: (m) => console.error(m) });
62023
+ const result = runAudit({ inputs: [join50(dir, "dom.html")], onWarn: (m) => console.error(m) });
61080
62024
  const failOnRaw = p.flags["fail-on"];
61081
62025
  const failOnParsed = parseFailOn(failOnRaw);
61082
62026
  if (failOnRaw !== void 0 && failOnParsed === null) {
@@ -61108,8 +62052,8 @@ function cmdInit(p) {
61108
62052
  if (want.baseline) {
61109
62053
  const inputs = p.positionals.length ? p.positionals : ["."];
61110
62054
  const result = runAudit({ inputs, onWarn: (m) => console.error(m) });
61111
- mkdirSync17(join47(root, "audits"), { recursive: true });
61112
- const bp = join47(root, "audits", "baseline.json");
62055
+ mkdirSync17(join50(root, "audits"), { recursive: true });
62056
+ const bp = join50(root, "audits", "baseline.json");
61113
62057
  writeFileSync19(bp, JSON.stringify(result, null, 2) + "\n");
61114
62058
  wrote.push(bp);
61115
62059
  }
@@ -61410,7 +62354,7 @@ async function cmdTickets(p) {
61410
62354
  let setPath;
61411
62355
  if (ticketsOut) {
61412
62356
  mkdirSync17(ticketsOut, { recursive: true });
61413
- setPath = join47(ticketsOut, `issues-${result.date}.json`);
62357
+ setPath = join50(ticketsOut, `issues-${result.date}.json`);
61414
62358
  const payload = {
61415
62359
  tool: "ultra11y",
61416
62360
  kind: "issues",
@@ -61511,8 +62455,8 @@ async function cmdTickets(p) {
61511
62455
  return pushed.failed > 0 ? 1 : 0;
61512
62456
  }
61513
62457
  function depsAt(root) {
61514
- const pkgPath = join47(root, "package.json");
61515
- if (!existsSync32(pkgPath)) return {};
62458
+ const pkgPath = join50(root, "package.json");
62459
+ if (!existsSync34(pkgPath)) return {};
61516
62460
  try {
61517
62461
  const pkg = JSON.parse(readText(pkgPath));
61518
62462
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -61538,24 +62482,24 @@ function cmdRender(p) {
61538
62482
  console.error(`ultra11y render: --runner must be playwright|cypress|auto (got "${forced}").`);
61539
62483
  return 2;
61540
62484
  }
61541
- const detected = detectE2eRunner(depsAt(root), (f) => existsSync32(join47(root, f)));
62485
+ const detected = detectE2eRunner(depsAt(root), (f) => existsSync34(join50(root, f)));
61542
62486
  const runners = forced && forced !== "auto" ? [forced] : detected;
61543
62487
  if (!runners.length) {
61544
62488
  console.error(e2eSetupPlan([], {}, lang));
61545
62489
  return 1;
61546
62490
  }
61547
62491
  const engineRef = engineRefFor(root);
61548
- const dir = join47(root, ".ultra11y", "e2e");
62492
+ const dir = join50(root, ".ultra11y", "e2e");
61549
62493
  const paths = {};
61550
62494
  try {
61551
62495
  mkdirSync17(dir, { recursive: true });
61552
62496
  if (runners.includes("playwright")) {
61553
- writeFileSync19(join47(dir, "playwright.mjs"), playwrightFixture(engineRef));
62497
+ writeFileSync19(join50(dir, "playwright.mjs"), playwrightFixture(engineRef));
61554
62498
  paths.playwright = ".ultra11y/e2e/playwright.mjs";
61555
62499
  }
61556
62500
  if (runners.includes("cypress")) {
61557
- writeFileSync19(join47(dir, "cypress-plugin.mjs"), cypressPlugin(engineRef));
61558
- writeFileSync19(join47(dir, "cypress-commands.mjs"), cypressCommands());
62501
+ writeFileSync19(join50(dir, "cypress-plugin.mjs"), cypressPlugin(engineRef));
62502
+ writeFileSync19(join50(dir, "cypress-commands.mjs"), cypressCommands());
61559
62503
  paths.cypressPlugin = ".ultra11y/e2e/cypress-plugin.mjs";
61560
62504
  paths.cypressCommands = ".ultra11y/e2e/cypress-commands.mjs";
61561
62505
  }
@@ -61584,7 +62528,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61584
62528
  }
61585
62529
  if (p.flags.setup === true) {
61586
62530
  const rel2 = ".ultra11y/capture-setup.mjs";
61587
- const out2 = join47(root, rel2);
62531
+ const out2 = join50(root, rel2);
61588
62532
  try {
61589
62533
  mkdirSync17(dirname14(out2), { recursive: true });
61590
62534
  writeFileSync19(out2, captureSetup());
@@ -61593,20 +62537,20 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61593
62537
  return 1;
61594
62538
  }
61595
62539
  let setupDeps = {};
61596
- const setupPkg = join47(root, "package.json");
61597
- if (existsSync32(setupPkg)) {
62540
+ const setupPkg = join50(root, "package.json");
62541
+ if (existsSync34(setupPkg)) {
61598
62542
  try {
61599
62543
  const pkg = JSON.parse(readText(setupPkg));
61600
62544
  setupDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
61601
62545
  } catch {
61602
62546
  }
61603
62547
  }
61604
- const tr = detectTestRunner(setupDeps, (f) => existsSync32(join47(root, f)));
62548
+ const tr = detectTestRunner(setupDeps, (f) => existsSync34(join50(root, f)));
61605
62549
  console.log(captureSetupPlan(tr, rel2, lang));
61606
62550
  const gaLine = ".ultra11y/captures/*.html text eol=lf linguist-generated=true";
61607
- const gaPath = join47(root, ".gitattributes");
62551
+ const gaPath = join50(root, ".gitattributes");
61608
62552
  try {
61609
- const existing = existsSync32(gaPath) ? readFileSync29(gaPath, "utf8") : "";
62553
+ const existing = existsSync34(gaPath) ? readFileSync32(gaPath, "utf8") : "";
61610
62554
  if (!existing.includes(".ultra11y/captures/")) {
61611
62555
  appendFileSync(gaPath, (existing && !existing.endsWith("\n") ? "\n" : "") + gaLine + "\n");
61612
62556
  console.log(lang === "fr" ? `.gitattributes : ajout\xE9 \xAB ${gaLine} \xBB` : `.gitattributes: added "${gaLine}"`);
@@ -61614,8 +62558,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61614
62558
  } catch {
61615
62559
  }
61616
62560
  try {
61617
- const giPath = join47(root, ".gitignore");
61618
- if (existsSync32(giPath) && /^\s*\/?\.ultra11y(\/\**)?\/?\s*$/m.test(readFileSync29(giPath, "utf8")))
62561
+ const giPath = join50(root, ".gitignore");
62562
+ if (existsSync34(giPath) && /^\s*\/?\.ultra11y(\/\**)?\/?\s*$/m.test(readFileSync32(giPath, "utf8")))
61619
62563
  console.error(
61620
62564
  lang === "fr" ? "\u26A0\uFE0F .ultra11y semble ignor\xE9 par .gitignore \u2014 les captures doivent \xEAtre committ\xE9es pour le gate (ajoutez \xAB !.ultra11y/captures/ \xBB)." : '\u26A0\uFE0F .ultra11y appears gitignored \u2014 captures must be committed for the gate (add "!.ultra11y/captures/").'
61621
62565
  );
@@ -61625,8 +62569,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61625
62569
  }
61626
62570
  if (p.flags.storybook === true || typeof p.flags.storybook === "string") {
61627
62571
  const sbDir = p.positionals[0] ?? "storybook-static";
61628
- const indexPath = existsSync32(join47(sbDir, "index.json")) ? join47(sbDir, "index.json") : join47(sbDir, "stories.json");
61629
- if (!existsSync32(indexPath)) {
62572
+ const indexPath = existsSync34(join50(sbDir, "index.json")) ? join50(sbDir, "index.json") : join50(sbDir, "stories.json");
62573
+ if (!existsSync34(indexPath)) {
61630
62574
  console.error(
61631
62575
  lang === "fr" ? `ultra11y render : aucun index Storybook (index.json/stories.json) dans ${sbDir}.` : `ultra11y render: no Storybook index (index.json/stories.json) in ${sbDir}.`
61632
62576
  );
@@ -61636,7 +62580,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61636
62580
  const provById = new Map(stories.map((s) => [s.id, storyProvenance(s)]));
61637
62581
  const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
61638
62582
  const htmlDir = capturesFlag ?? sbDir;
61639
- const htmlFiles = existsSync32(htmlDir) ? discover([htmlDir]).files.filter((f) => /\.html?$/i.test(f)) : [];
62583
+ const htmlFiles = existsSync34(htmlDir) ? discover([htmlDir]).files.filter((f) => /\.html?$/i.test(f)) : [];
61640
62584
  const outDir = ".ultra11y/captures";
61641
62585
  let attributed = 0;
61642
62586
  let skipped = 0;
@@ -61659,7 +62603,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
61659
62603
  }
61660
62604
  try {
61661
62605
  mkdirSync17(outDir, { recursive: true });
61662
- writeFileSync19(join47(outDir, `${hitId}.html`), `${formatCaptureComment(prov)}
62606
+ writeFileSync19(join50(outDir, `${hitId}.html`), `${formatCaptureComment(prov)}
61663
62607
  ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
61664
62608
  attributed++;
61665
62609
  } catch {
@@ -61681,11 +62625,11 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
61681
62625
  }
61682
62626
  if (p.flags.coverage === true) {
61683
62627
  const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
61684
- const capturesDir = capturesFlag ?? join47(root, ".ultra11y/captures");
62628
+ const capturesDir = capturesFlag ?? join50(root, ".ultra11y/captures");
61685
62629
  const graphExt = [...GRAPH_ONLY_EXT, ...asList(p.flags.ext) ?? []];
61686
62630
  const sourceFiles = discover([root], { include: asList(p.flags.include), exclude: asList(p.flags.exclude), ext: graphExt }).files;
61687
62631
  const graph = buildGraphStreaming(sourceFiles);
61688
- const capFiles = existsSync32(capturesDir) ? discover([capturesDir]).files : [];
62632
+ const capFiles = existsSync34(capturesDir) ? discover([capturesDir]).files : [];
61689
62633
  const entries = capFiles.map((f) => ({ file: toPosix(f), provenance: parseCaptureProvenance(readText(f)) }));
61690
62634
  const cov = computeCaptureCoverage(graph, entries);
61691
62635
  if (p.flags.json) console.log(JSON.stringify(cov, null, 2));
@@ -61693,15 +62637,15 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
61693
62637
  return 0;
61694
62638
  }
61695
62639
  let deps = {};
61696
- const pkgPath = join47(root, "package.json");
61697
- if (existsSync32(pkgPath)) {
62640
+ const pkgPath = join50(root, "package.json");
62641
+ if (existsSync34(pkgPath)) {
61698
62642
  try {
61699
62643
  const pkg = JSON.parse(readText(pkgPath));
61700
62644
  deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
61701
62645
  } catch {
61702
62646
  }
61703
62647
  }
61704
- const detection = detectFrameworks(deps, (f) => existsSync32(join47(root, f)));
62648
+ const detection = detectFrameworks(deps, (f) => existsSync34(join50(root, f)));
61705
62649
  if (p.flags.json) console.log(JSON.stringify(detection, null, 2));
61706
62650
  else console.log(renderPlan(detection, lang));
61707
62651
  return 0;
@@ -61901,7 +62845,7 @@ function applyAdjudicationFile(p, adj, lang) {
61901
62845
  }
61902
62846
  const out2 = typeof p.flags.out === "string" ? p.flags.out : ".";
61903
62847
  mkdirSync17(out2, { recursive: true });
61904
- const auditPath = join47(out2, "audit-latest.json");
62848
+ const auditPath = join50(out2, "audit-latest.json");
61905
62849
  writeFileSync19(auditPath, JSON.stringify(r.audit, null, 2) + "\n");
61906
62850
  if (p.flags.json)
61907
62851
  console.log(
@@ -62011,7 +62955,7 @@ async function cmdJudge(p) {
62011
62955
  return 1;
62012
62956
  }
62013
62957
  mkdirSync17(out2, { recursive: true });
62014
- const auditPath = join47(out2, "audit-latest.json");
62958
+ const auditPath = join50(out2, "audit-latest.json");
62015
62959
  writeFileSync19(auditPath, `${JSON.stringify(r.audit, null, 2)}
62016
62960
  `);
62017
62961
  console.log(
@@ -62081,7 +63025,7 @@ async function cmdScan(p) {
62081
63025
  return 0;
62082
63026
  }
62083
63027
  for (const target of p.positionals.filter((a) => a !== "-")) {
62084
- if (/^https?:\/\//i.test(target) || existsSync32(target)) continue;
63028
+ if (/^https?:\/\//i.test(target) || existsSync34(target)) continue;
62085
63029
  console.error(
62086
63030
  lang === "fr" ? `ultra11y scan : fichier introuvable (file not found) : ${target}. Passez une URL http(s):// ou un fichier HTML existant.` : `ultra11y scan: File not found: ${target}. Pass an http(s):// URL or an existing HTML file.`
62087
63031
  );
@@ -62208,7 +63152,7 @@ async function cmdScan(p) {
62208
63152
  }
62209
63153
  let merged = mergeDynamic(audit2, dynamic, lang);
62210
63154
  if (dynamic.snapshots?.length) {
62211
- const doms = dynamic.snapshots.map((id) => join47(snapshotRoot ?? ".", PAGES_DIR, id, "dom.html")).filter((f) => existsSync32(f));
63155
+ const doms = dynamic.snapshots.map((id) => join50(snapshotRoot ?? ".", PAGES_DIR, id, "dom.html")).filter((f) => existsSync34(f));
62212
63156
  if (doms.length) {
62213
63157
  const snapAudit = runAudit({ inputs: doms, onWarn: (m) => console.error(m) });
62214
63158
  merged = mergeSnapshotAudit(merged, snapAudit);
@@ -62220,11 +63164,11 @@ async function cmdScan(p) {
62220
63164
  }
62221
63165
  }
62222
63166
  mkdirSync17(out2, { recursive: true });
62223
- writeFileSync19(join47(out2, "audit-latest.json"), JSON.stringify(merged, null, 2) + "\n");
63167
+ writeFileSync19(join50(out2, "audit-latest.json"), JSON.stringify(merged, null, 2) + "\n");
62224
63168
  if (p.flags.json) console.log(JSON.stringify(merged, null, 2));
62225
63169
  else {
62226
63170
  console.log(
62227
- lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join47(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join47(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
63171
+ lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join50(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join50(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
62228
63172
  );
62229
63173
  if (dynamic.snapshots?.length)
62230
63174
  console.log(
@@ -62467,7 +63411,7 @@ async function cmdImport(p) {
62467
63411
  }
62468
63412
  if (outDir) {
62469
63413
  mkdirSync17(outDir, { recursive: true });
62470
- const rawFile = join47(outDir, `external-${adapterId}-${arg}.raw.json`);
63414
+ const rawFile = join50(outDir, `external-${adapterId}-${arg}.raw.json`);
62471
63415
  writeFileSync19(rawFile, rawText.endsWith("\n") ? rawText : `${rawText}
62472
63416
  `);
62473
63417
  if (!p.flags.json) console.log(rawFile);
@@ -62503,7 +63447,7 @@ async function cmdImport(p) {
62503
63447
  }
62504
63448
  if (outDir) {
62505
63449
  mkdirSync17(outDir, { recursive: true });
62506
- const file = join47(outDir, "external-latest.json");
63450
+ const file = join50(outDir, "external-latest.json");
62507
63451
  writeFileSync19(file, `${JSON.stringify(audit2, null, 2)}
62508
63452
  `);
62509
63453
  console.log(file);
@@ -62609,7 +63553,7 @@ function cmdOrchestrate(p) {
62609
63553
  }
62610
63554
  const engineAbs = realpathSync6(fileURLToPath5(import.meta.url));
62611
63555
  if (p.flags.list === true) {
62612
- if (!existsSync32(runFlag)) {
63556
+ if (!existsSync34(runFlag)) {
62613
63557
  console.error(`ultra11y orchestrate: run dir not found: ${runFlag}.`);
62614
63558
  return 2;
62615
63559
  }
@@ -62636,7 +63580,7 @@ function cmdOrchestrate(p) {
62636
63580
  );
62637
63581
  } else {
62638
63582
  console.log(
62639
- lang === "fr" ? `Suivez ${join47(runFlag, "orchestration", "RUNBOOK.md")} s\xE9quentiellement (chemin \xE9co).` : `Follow ${join47(runFlag, "orchestration", "RUNBOOK.md")} sequentially (the eco path).`
63583
+ lang === "fr" ? `Suivez ${join50(runFlag, "orchestration", "RUNBOOK.md")} s\xE9quentiellement (chemin \xE9co).` : `Follow ${join50(runFlag, "orchestration", "RUNBOOK.md")} sequentially (the eco path).`
62640
63584
  );
62641
63585
  }
62642
63586
  if (p.flags.phase === void 0 && workflows.length === 0 && p.flags.eco !== true) {