ultra11y 4.1.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/README.md +13 -7
- package/package.json +1 -1
- package/scripts/ultra11y.mjs +1568 -583
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +1568 -583
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/references/ci.md +33 -11
- package/skills/ultra11y/references/pages.md +28 -9
- package/skills/ultra11y/scripts/ultra11y.mjs +1568 -583
- package/src/cli.ts +52 -24
- package/src/data/wcag-universe.json +1 -1
- package/src/evidence.ts +72 -37
- package/src/html-emit.ts +20 -3
- package/src/html-report.ts +27 -4
- package/src/pages-report.ts +10 -4
- package/src/report.ts +16 -7
- package/src/tickets/providers/github.ts +9 -4
- package/src/types.ts +1 -1
- package/src/vendor/codeindex-engine.d.mts +324 -3
- package/src/vendor/codeindex-engine.mjs +1146 -175
- package/src/vendor/engine.meta.json +5 -5
package/scripts/ultra11y.mjs
CHANGED
|
@@ -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
|
|
5
|
-
import { join as
|
|
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.
|
|
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-
|
|
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
|
|
18798
|
-
import { join as
|
|
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
|
|
18860
|
-
import { readFileSync as
|
|
18861
|
-
import { isAbsolute, join as
|
|
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
|
|
18877
|
-
import { join as
|
|
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.
|
|
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((
|
|
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
|
-
|
|
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((
|
|
21050
|
+
return new Promise((resolve52, reject) => {
|
|
21036
21051
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
21037
|
-
|
|
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((
|
|
22369
|
-
readyPromiseResolve =
|
|
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)
|
|
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
|
|
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))
|
|
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 (
|
|
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
|
-
|
|
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] ?
|
|
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 &&
|
|
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
|
|
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
|
|
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
|
-
|
|
31616
|
-
|
|
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
|
-
|
|
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:
|
|
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: {
|
|
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(
|
|
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
|
-
|
|
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
|
|
32477
|
-
return JSON.stringify({ results:
|
|
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
|
|
33349
|
+
const results3 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
|
|
32480
33350
|
return JSON.stringify(
|
|
32481
|
-
{ results:
|
|
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
|
|
32495
|
-
return JSON.stringify({ results:
|
|
33364
|
+
const results3 = searchSemantic(scan2, query, index, { model, limit, fuzzy });
|
|
33365
|
+
return JSON.stringify({ results: results3, tier: "static" }, null, 2);
|
|
32496
33366
|
}
|
|
32497
|
-
const
|
|
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
|
-
|
|
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 :
|
|
33440
|
+
const abs = isAbsolute(configPath) ? configPath : join22(repo, configPath);
|
|
32556
33441
|
try {
|
|
32557
|
-
payload = JSON.parse(
|
|
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
|
-
|
|
33565
|
+
init_protocol2();
|
|
32679
33566
|
init_session();
|
|
32680
33567
|
init_tools();
|
|
32681
|
-
|
|
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((
|
|
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(() =>
|
|
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 (
|
|
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,
|
|
34149
|
-
dead_code, complexity, mermaid, grep, search,
|
|
34150
|
-
check_rules, the memory quartet and
|
|
34151
|
-
writes). Flags: --repo <dir> pins ONE
|
|
34152
|
-
repo argument becomes optional (an
|
|
34153
|
-
wins); --server-name <name> overrides
|
|
34154
|
-
--max-response-bytes <n> caps a single
|
|
34155
|
-
a response under the cap is
|
|
34156
|
-
|
|
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 =
|
|
35136
|
+
if (a === "--repo") flags2.repo = resolve4(next());
|
|
34221
35137
|
else if (a === "--out") {
|
|
34222
35138
|
const v = next();
|
|
34223
|
-
flags2.out = 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 =
|
|
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 =
|
|
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 && !
|
|
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 (!
|
|
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 =
|
|
35345
|
+
const cachePath = join23(outDir, "cache.json");
|
|
34420
35346
|
let cache;
|
|
34421
35347
|
let meta2 = {};
|
|
34422
35348
|
try {
|
|
34423
|
-
const parsed = JSON.parse(
|
|
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 =
|
|
34445
|
-
const symbolsPath =
|
|
34446
|
-
const embedPath =
|
|
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(
|
|
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 ??
|
|
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,
|
|
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 =
|
|
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(
|
|
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 ??
|
|
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 ${
|
|
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(
|
|
34690
|
-
process.stderr.write(`codeindex: model written to ${
|
|
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) =>
|
|
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(
|
|
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(
|
|
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 (
|
|
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
|
|
35081
|
-
import { dirname as dirname5, join as
|
|
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
|
|
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(
|
|
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(
|
|
36209
|
+
writeFileSync5(join25(dir, "dom.html"), `${comment}
|
|
35266
36210
|
${snap.dom}
|
|
35267
36211
|
`);
|
|
35268
|
-
if (snap.styles) writeFileSync5(
|
|
36212
|
+
if (snap.styles) writeFileSync5(join25(dir, "styles.json"), `${JSON.stringify(snap.styles)}
|
|
35269
36213
|
`);
|
|
35270
|
-
if (snap.boxes) writeFileSync5(
|
|
36214
|
+
if (snap.boxes) writeFileSync5(join25(dir, "boxes.json"), `${JSON.stringify(snap.boxes)}
|
|
35271
36215
|
`);
|
|
35272
|
-
if (snap.axtree) writeFileSync5(
|
|
36216
|
+
if (snap.axtree) writeFileSync5(join25(dir, "axtree.json"), `${JSON.stringify(snap.axtree)}
|
|
35273
36217
|
`);
|
|
35274
|
-
if (snap.css) writeFileSync5(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
36242
|
+
dom = readFileSync14(join25(dir, "dom.html"), "utf8");
|
|
35299
36243
|
} catch {
|
|
35300
36244
|
return null;
|
|
35301
36245
|
}
|
|
35302
|
-
const styles = readJson2(
|
|
35303
|
-
const boxes = readJson2(
|
|
35304
|
-
const axtree = readJson2(
|
|
35305
|
-
const css = readJson2(
|
|
35306
|
-
const shot =
|
|
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
|
-
...
|
|
36258
|
+
...existsSync12(shot) ? { screenshot: "screen.png" } : {}
|
|
35315
36259
|
};
|
|
35316
36260
|
}
|
|
35317
36261
|
function readSnapshots(root) {
|
|
35318
|
-
const base =
|
|
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(
|
|
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(
|
|
35370
|
-
const boxes = readJson2(
|
|
35371
|
-
const axtree = readJson2(
|
|
35372
|
-
const css = readJson2(
|
|
35373
|
-
const shot =
|
|
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
|
-
...
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
39842
|
-
import { dirname as dirname7, join as
|
|
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 =
|
|
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 =
|
|
40796
|
+
let dir = resolve5(startDir);
|
|
39853
40797
|
for (let i2 = 0; i2 < 30; i2++) {
|
|
39854
|
-
const p =
|
|
39855
|
-
if (
|
|
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 = [
|
|
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 (!
|
|
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") ? [
|
|
39880
|
-
const hit = cands.find((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(
|
|
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 =
|
|
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,
|
|
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
|
|
46098
|
+
import { existsSync as existsSync14 } from "fs";
|
|
45155
46099
|
import { execFileSync } from "child_process";
|
|
45156
|
-
import { join as
|
|
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,
|
|
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,
|
|
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) =>
|
|
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
|
|
46476
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
45533
46477
|
import { tmpdir } from "os";
|
|
45534
|
-
import { join as
|
|
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
|
|
45579
|
-
import { join as
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
48246
|
-
if (!
|
|
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 (!
|
|
49203
|
+
if (!existsSync16(path)) return null;
|
|
48260
49204
|
if (statSync7(path).isDirectory()) {
|
|
48261
|
-
const pack =
|
|
48262
|
-
if (!
|
|
48263
|
-
const glossary =
|
|
48264
|
-
const guidance =
|
|
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:
|
|
48268
|
-
guidance:
|
|
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 :
|
|
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 :
|
|
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 (!
|
|
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 =
|
|
49383
|
+
const dir = join30(tmpdir(), "ultra11y-hook");
|
|
48440
49384
|
mkdirSync5(dir, { recursive: true });
|
|
48441
|
-
const marker =
|
|
48442
|
-
if (
|
|
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
|
|
48526
|
-
import { join as
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
}
|
|
@@ -49748,12 +50692,13 @@ function render(r, lang, opts) {
|
|
|
49748
50692
|
const group = ncUnits.filter((u) => u.severity === sev);
|
|
49749
50693
|
if (!group.length) continue;
|
|
49750
50694
|
out2.push(`### ${ICON3[sev]} ${s.sev[sev]} (${group.length})`, "");
|
|
49751
|
-
for (const u of group) out2.push(...renderAuditorUnit(u, opts.standard, lang, { heading: "####" }));
|
|
50695
|
+
for (const u of group) out2.push(...renderAuditorUnit(u, opts.standard, lang, { heading: "####", ...opts.cropFor ? { cropFor: opts.cropFor } : {} }));
|
|
49752
50696
|
}
|
|
49753
50697
|
}
|
|
49754
50698
|
if (advisoryUnits.length) {
|
|
49755
50699
|
out2.push(`## \u{1F4A1} ${s.recTitle}`, "", `> ${s.recNote}`, "");
|
|
49756
|
-
for (const u of advisoryUnits)
|
|
50700
|
+
for (const u of advisoryUnits)
|
|
50701
|
+
out2.push(...renderAuditorUnit(u, opts.standard, lang, { heading: "###", ...opts.cropFor ? { cropFor: opts.cropFor } : {} }));
|
|
49757
50702
|
}
|
|
49758
50703
|
const pageScope = pagesOf(r);
|
|
49759
50704
|
if (pageScope.length) attributePages(r, pageScope);
|
|
@@ -49770,13 +50715,13 @@ function render(r, lang, opts) {
|
|
|
49770
50715
|
out2.push(`- ${nc.length} ${s.ncCount}${adv.length ? ` \xB7 ${adv.length} ${s.advCount}` : ""}`);
|
|
49771
50716
|
const notes = pageScope.find((x) => x.id === pg.id)?.notes;
|
|
49772
50717
|
if (notes) out2.push(`- _${notes}_`);
|
|
49773
|
-
const shot =
|
|
49774
|
-
if (
|
|
50718
|
+
const shot = join32(PAGES_DIR, pg.id, "screen.png");
|
|
50719
|
+
if (existsSync18(shot)) {
|
|
49775
50720
|
let href = relative3(opts.outDir ?? ".", shot).split("\\").join("/");
|
|
49776
50721
|
if (opts.outDir) {
|
|
49777
50722
|
try {
|
|
49778
|
-
mkdirSync7(
|
|
49779
|
-
copyFileSync(shot,
|
|
50723
|
+
mkdirSync7(join32(opts.outDir, "assets"), { recursive: true });
|
|
50724
|
+
copyFileSync(shot, join32(opts.outDir, "assets", `${pg.id}.png`));
|
|
49780
50725
|
href = `./assets/${pg.id}.png`;
|
|
49781
50726
|
} catch {
|
|
49782
50727
|
}
|
|
@@ -49837,9 +50782,9 @@ function reportGroups(r, lang = "en") {
|
|
|
49837
50782
|
}
|
|
49838
50783
|
return r.guidelines.map((g) => ({ key: g.key, title: guidelineTitle(g.key, lang) ?? g.title, rows: byGuideline.get(g.key) ?? [] }));
|
|
49839
50784
|
}
|
|
49840
|
-
function renderReport(r, lang = "en", outDir) {
|
|
50785
|
+
function renderReport(r, lang = "en", outDir, cropFor) {
|
|
49841
50786
|
const s = L4[lang];
|
|
49842
|
-
return render(r, lang, { std: s.wcagStd, groupHead: s.byGuideline, groups: reportGroups(r, lang), standard: CORE2, outDir });
|
|
50787
|
+
return render(r, lang, { std: s.wcagStd, groupHead: s.byGuideline, groups: reportGroups(r, lang), standard: CORE2, outDir, ...cropFor ? { cropFor } : {} });
|
|
49843
50788
|
}
|
|
49844
50789
|
function packReportGroups(r, pack, lang = "en") {
|
|
49845
50790
|
const derived = derivePackResults(r, pack.key);
|
|
@@ -49862,7 +50807,7 @@ function packReportGroups(r, pack, lang = "en") {
|
|
|
49862
50807
|
}
|
|
49863
50808
|
return pack.themes.map((t2) => ({ key: `${t2.number}.`, title: themeName(pack, t2.number, lang) ?? "", rows: byTheme.get(t2.number) ?? [] }));
|
|
49864
50809
|
}
|
|
49865
|
-
function renderPackReport(r, pack, lang = "en", outDir) {
|
|
50810
|
+
function renderPackReport(r, pack, lang = "en", outDir, cropFor) {
|
|
49866
50811
|
const derived = derivePackResults(r, pack.key);
|
|
49867
50812
|
const std = `${pack.name} ${pack.baseVersion}`;
|
|
49868
50813
|
return render(r, lang, {
|
|
@@ -49876,14 +50821,15 @@ function renderPackReport(r, pack, lang = "en", outDir) {
|
|
|
49876
50821
|
// Forwarded, unlike before: without it the per-page screenshots resolved against the
|
|
49877
50822
|
// CWD instead of the report's own directory, so a pack report written to `audits/`
|
|
49878
50823
|
// carried links that only worked when read from the repo root.
|
|
49879
|
-
outDir
|
|
50824
|
+
outDir,
|
|
50825
|
+
...cropFor ? { cropFor } : {}
|
|
49880
50826
|
});
|
|
49881
50827
|
}
|
|
49882
50828
|
function writeReport(r, opts) {
|
|
49883
50829
|
const core = isCore(opts.standard);
|
|
49884
|
-
const md = core ? renderReport(r, opts.lang, opts.out) : renderPackReport(r, loadPack(opts.standard), opts.lang, opts.out);
|
|
50830
|
+
const md = core ? renderReport(r, opts.lang, opts.out, opts.cropFor) : renderPackReport(r, loadPack(opts.standard), opts.lang, opts.out, opts.cropFor);
|
|
49885
50831
|
mkdirSync7(opts.out, { recursive: true });
|
|
49886
|
-
const path =
|
|
50832
|
+
const path = join32(opts.out, `${core ? "wcag" : opts.standard}-${r.date}.md`);
|
|
49887
50833
|
writeFileSync8(path, md);
|
|
49888
50834
|
return path;
|
|
49889
50835
|
}
|
|
@@ -50367,22 +51313,24 @@ function providerFromRemote() {
|
|
|
50367
51313
|
|
|
50368
51314
|
// src/tickets/providers/github.ts
|
|
50369
51315
|
var CAPABILITIES = { bodyLimit: 65536, labels: true };
|
|
50370
|
-
function
|
|
51316
|
+
function resolve6(opts) {
|
|
50371
51317
|
const env = opts.env ?? process.env;
|
|
50372
51318
|
const mode = opts.transport ?? "auto";
|
|
50373
51319
|
const api = (env.GITHUB_API_URL || "https://api.github.com").replace(/\/+$/, "");
|
|
50374
51320
|
const token = env.GH_TOKEN || env.GITHUB_TOKEN;
|
|
50375
|
-
const
|
|
51321
|
+
const probe = opts.cliAvailable ?? ghAvailable;
|
|
51322
|
+
let cached;
|
|
51323
|
+
const cliOk = () => cached ??= probe();
|
|
50376
51324
|
if (mode === "cli") {
|
|
50377
|
-
return cliOk ? { transport: "cli", api } : { transport: "cli", api, reason: "`gh` is not installed or not authenticated (run `gh auth login`)" };
|
|
51325
|
+
return cliOk() ? { transport: "cli", api } : { transport: "cli", api, reason: "`gh` is not installed or not authenticated (run `gh auth login`)" };
|
|
50378
51326
|
}
|
|
50379
|
-
const repo = env.ULTRA11Y_GITHUB_REPO || env.GITHUB_REPOSITORY || (mode === "rest" || !cliOk ? gitRemoteSlug("github.com") : void 0);
|
|
51327
|
+
const repo = env.ULTRA11Y_GITHUB_REPO || env.GITHUB_REPOSITORY || (mode === "rest" || !cliOk() ? gitRemoteSlug("github.com") : void 0);
|
|
50380
51328
|
if (mode === "rest") {
|
|
50381
51329
|
if (!token) return { transport: "rest", api, reason: "no GitHub token \u2014 set GH_TOKEN or GITHUB_TOKEN" };
|
|
50382
51330
|
if (!repo) return { transport: "rest", api, token, reason: "no repository \u2014 set GITHUB_REPOSITORY (owner/name) or ULTRA11Y_GITHUB_REPO" };
|
|
50383
51331
|
return { transport: "rest", api, token, repo };
|
|
50384
51332
|
}
|
|
50385
|
-
if (cliOk) return { transport: "cli", api };
|
|
51333
|
+
if (cliOk()) return { transport: "cli", api };
|
|
50386
51334
|
if (token && repo) return { transport: "rest", api, token, repo };
|
|
50387
51335
|
const missing = [!token ? "a token (GH_TOKEN/GITHUB_TOKEN)" : "", !repo ? "a repository (GITHUB_REPOSITORY)" : ""].filter(Boolean);
|
|
50388
51336
|
return {
|
|
@@ -50394,7 +51342,7 @@ function resolve4(opts) {
|
|
|
50394
51342
|
};
|
|
50395
51343
|
}
|
|
50396
51344
|
function createGithubProvider(opts = {}) {
|
|
50397
|
-
const r =
|
|
51345
|
+
const r = resolve6(opts);
|
|
50398
51346
|
const auth = { Authorization: `Bearer ${r.token ?? ""}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" };
|
|
50399
51347
|
const http = {
|
|
50400
51348
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
|
|
@@ -50497,7 +51445,7 @@ function execReason(err2) {
|
|
|
50497
51445
|
if (line) return line;
|
|
50498
51446
|
return typeof e.message === "string" ? e.message.split("\n")[0]?.trim() ?? void 0 : void 0;
|
|
50499
51447
|
}
|
|
50500
|
-
function
|
|
51448
|
+
function resolve7(opts) {
|
|
50501
51449
|
const env = opts.env ?? process.env;
|
|
50502
51450
|
const mode = opts.transport ?? "auto";
|
|
50503
51451
|
const api = (env.CI_API_V4_URL || "https://gitlab.com/api/v4").replace(/\/+$/, "");
|
|
@@ -50517,7 +51465,7 @@ function resolve5(opts) {
|
|
|
50517
51465
|
return { transport: "cli", api, jobToken };
|
|
50518
51466
|
}
|
|
50519
51467
|
function createGitlabProvider(opts = {}) {
|
|
50520
|
-
const r =
|
|
51468
|
+
const r = resolve7(opts);
|
|
50521
51469
|
const id = encodeURIComponent(r.project ?? "");
|
|
50522
51470
|
const auth = { "PRIVATE-TOKEN": r.token ?? "" };
|
|
50523
51471
|
const http = {
|
|
@@ -50586,7 +51534,7 @@ function createGitlabProvider(opts = {}) {
|
|
|
50586
51534
|
var CAPABILITIES3 = { bodyLimit: 32767, labels: true };
|
|
50587
51535
|
var JIRA_SCOPE_LABEL = "ultra11y";
|
|
50588
51536
|
var PRIORITY = { bloquant: "Highest", majeur: "High", mineur: "Low" };
|
|
50589
|
-
function
|
|
51537
|
+
function resolve8(opts) {
|
|
50590
51538
|
const env = opts.env ?? process.env;
|
|
50591
51539
|
const issueType = env.ULTRA11Y_JIRA_ISSUE_TYPE || "Task";
|
|
50592
51540
|
const apiVersion = env.ULTRA11Y_JIRA_API === "2" ? "2" : "3";
|
|
@@ -50605,7 +51553,7 @@ function toAdf(markdown) {
|
|
|
50605
51553
|
return { type: "doc", version: 1, content: content.length ? content : [{ type: "paragraph", content: [] }] };
|
|
50606
51554
|
}
|
|
50607
51555
|
function createJiraProvider(opts = {}) {
|
|
50608
|
-
const r =
|
|
51556
|
+
const r = resolve8(opts);
|
|
50609
51557
|
const headers = { Authorization: r.auth ?? "" };
|
|
50610
51558
|
const http = {
|
|
50611
51559
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
|
|
@@ -51109,12 +52057,12 @@ function renderCriteriaReference() {
|
|
|
51109
52057
|
}
|
|
51110
52058
|
|
|
51111
52059
|
// src/check.ts
|
|
51112
|
-
import { existsSync as
|
|
51113
|
-
import { dirname as dirname9, join as
|
|
52060
|
+
import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
|
|
52061
|
+
import { dirname as dirname9, join as join34 } from "path";
|
|
51114
52062
|
|
|
51115
52063
|
// src/verify.ts
|
|
51116
52064
|
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
51117
|
-
import { join as
|
|
52065
|
+
import { join as join33 } from "path";
|
|
51118
52066
|
var VERIFY_MAX = 40;
|
|
51119
52067
|
var plain = (s) => s.replace(/\[([^\]]+)\]\(#[^)]*\)/g, "$1");
|
|
51120
52068
|
function auditorCriterionLine(standard) {
|
|
@@ -51289,16 +52237,16 @@ function applyVerdicts(items, expected) {
|
|
|
51289
52237
|
}
|
|
51290
52238
|
function writeWorklist(items, outDir, semantic, standard = "wcag", lang = "en") {
|
|
51291
52239
|
mkdirSync8(outDir, { recursive: true });
|
|
51292
|
-
const todoPath =
|
|
51293
|
-
const mdPath =
|
|
52240
|
+
const todoPath = join33(outDir, "VERIFY.todo.json");
|
|
52241
|
+
const mdPath = join33(outDir, "VERIFY.md");
|
|
51294
52242
|
writeFileSync9(todoPath, JSON.stringify(items, null, 2) + "\n");
|
|
51295
52243
|
writeFileSync9(mdPath, formatWorklist(items, semantic, standard, lang));
|
|
51296
52244
|
return { todoPath, mdPath, count: items.length };
|
|
51297
52245
|
}
|
|
51298
52246
|
|
|
51299
52247
|
// src/grounding.ts
|
|
51300
|
-
import { existsSync as
|
|
51301
|
-
import { resolve as
|
|
52248
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
52249
|
+
import { resolve as resolve9 } from "path";
|
|
51302
52250
|
var WINDOW = 10;
|
|
51303
52251
|
var norm2 = (s) => s.replace(/\s+/g, " ").trim();
|
|
51304
52252
|
var isUnresolvable = (file) => !file || file === "-" || file === "<stdin>" || file === "stdin";
|
|
@@ -51315,11 +52263,11 @@ function selectorProbes(selector) {
|
|
|
51315
52263
|
}
|
|
51316
52264
|
function groundFinding(g, opts = {}) {
|
|
51317
52265
|
if (isUnresolvable(g.file)) return { ok: true, moved: false };
|
|
51318
|
-
const path =
|
|
51319
|
-
if (!
|
|
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}` };
|
|
51320
52268
|
let text;
|
|
51321
52269
|
try {
|
|
51322
|
-
text =
|
|
52270
|
+
text = readFileSync17(path, "utf8");
|
|
51323
52271
|
} catch {
|
|
51324
52272
|
return { ok: false, moved: false, issue: `cited file unreadable: ${g.file}` };
|
|
51325
52273
|
}
|
|
@@ -51516,7 +52464,8 @@ function renderPageReport(result, page, opts = {}) {
|
|
|
51516
52464
|
const shot = opts.screenshots?.get(page.id);
|
|
51517
52465
|
if (shot) out2.push(``, "");
|
|
51518
52466
|
else out2.push(`_${s.noScreenshot}_`, "");
|
|
51519
|
-
|
|
52467
|
+
const refused = opts.evidenceNotice?.(page.id) ?? [];
|
|
52468
|
+
if (refused.length) out2.push(...refused, "");
|
|
51520
52469
|
const withTests = rows.some((r) => r.tests.length);
|
|
51521
52470
|
out2.push(`${h}# ${s.gridTitle}`, "", `> ${s.gridNote}`, "");
|
|
51522
52471
|
out2.push(withTests ? `| ${s.criterion} | ${s.tests} | ${s.status} |` : `| ${s.criterion} | ${s.status} |`);
|
|
@@ -51705,11 +52654,11 @@ function checkSemantic(md, opts) {
|
|
|
51705
52654
|
const standard = opts.standard ?? "wcag";
|
|
51706
52655
|
const s = M[lang];
|
|
51707
52656
|
const empty = { total: 0, grounded: 0, moved: 0, failed: 0 };
|
|
51708
|
-
const artifact = opts.verdictsPath ??
|
|
51709
|
-
if (!
|
|
52657
|
+
const artifact = opts.verdictsPath ?? join34(dirname9(opts.reportPath), "VERIFY.todo.json");
|
|
52658
|
+
if (!existsSync20(artifact)) return { ok: false, issues: [s.semanticMissing(artifact)], ...empty };
|
|
51710
52659
|
let items;
|
|
51711
52660
|
try {
|
|
51712
|
-
const parsed = JSON.parse(
|
|
52661
|
+
const parsed = JSON.parse(readFileSync18(artifact, "utf8"));
|
|
51713
52662
|
if (!Array.isArray(parsed)) throw new Error("not an array");
|
|
51714
52663
|
items = parsed;
|
|
51715
52664
|
} catch {
|
|
@@ -51742,7 +52691,7 @@ function sectionBody(md, n) {
|
|
|
51742
52691
|
|
|
51743
52692
|
// src/adjudicate.ts
|
|
51744
52693
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
51745
|
-
import { join as
|
|
52694
|
+
import { join as join35 } from "path";
|
|
51746
52695
|
|
|
51747
52696
|
// src/data/adjudication.json
|
|
51748
52697
|
var adjudication_default = {
|
|
@@ -52677,7 +53626,7 @@ function docsForAudit(audit2, cwd) {
|
|
|
52677
53626
|
const docs = [];
|
|
52678
53627
|
for (const f of files) {
|
|
52679
53628
|
try {
|
|
52680
|
-
docs.push(parseSource(readText(cwd ?
|
|
53629
|
+
docs.push(parseSource(readText(cwd ? join35(cwd, f) : f), f));
|
|
52681
53630
|
} catch {
|
|
52682
53631
|
}
|
|
52683
53632
|
}
|
|
@@ -53092,8 +54041,8 @@ function formatAdjudication(items, lang = "en", standard = CORE2) {
|
|
|
53092
54041
|
}
|
|
53093
54042
|
function writeAdjudication(items, outDir, opts) {
|
|
53094
54043
|
mkdirSync9(outDir, { recursive: true });
|
|
53095
|
-
const todoPath =
|
|
53096
|
-
const mdPath =
|
|
54044
|
+
const todoPath = join35(outDir, "ADJUDICATE.todo.json");
|
|
54045
|
+
const mdPath = join35(outDir, "ADJUDICATE.md");
|
|
53097
54046
|
const file = {
|
|
53098
54047
|
tool: "ultra11y",
|
|
53099
54048
|
kind: "adjudication",
|
|
@@ -53298,9 +54247,9 @@ function applyRawVerdicts(items, verdicts) {
|
|
|
53298
54247
|
|
|
53299
54248
|
// src/scan.ts
|
|
53300
54249
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
53301
|
-
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync11, existsSync as
|
|
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";
|
|
53302
54251
|
import { tmpdir as tmpdir2 } from "os";
|
|
53303
|
-
import { join as
|
|
54252
|
+
import { join as join36, resolve as resolve10 } from "path";
|
|
53304
54253
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
53305
54254
|
|
|
53306
54255
|
// src/axe-map.ts
|
|
@@ -53560,7 +54509,7 @@ async function crawlUrls(start2, opts) {
|
|
|
53560
54509
|
}
|
|
53561
54510
|
|
|
53562
54511
|
// src/sample.ts
|
|
53563
|
-
import { existsSync as
|
|
54512
|
+
import { existsSync as existsSync21 } from "fs";
|
|
53564
54513
|
function looksLikeTarget(u) {
|
|
53565
54514
|
return /^https?:\/\//i.test(u) || /^(\.\.?[/\\]|[/\\])/.test(u) || /\.x?html?$/i.test(u);
|
|
53566
54515
|
}
|
|
@@ -53596,7 +54545,7 @@ function validateSample(raw) {
|
|
|
53596
54545
|
if (p.auth !== void 0 && typeof p.auth !== "boolean") err2(`sample.pages[${i2}].auth`, "auth must be a boolean");
|
|
53597
54546
|
if (p.storageState !== void 0 && (typeof p.storageState !== "string" || p.storageState.trim() === "")) {
|
|
53598
54547
|
err2(`sample.pages[${i2}].storageState`, "storageState must be a non-empty file path string");
|
|
53599
|
-
} else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !
|
|
54548
|
+
} else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !existsSync21(p.storageState)) {
|
|
53600
54549
|
warn(
|
|
53601
54550
|
`sample.pages[${i2}].storageState`,
|
|
53602
54551
|
`storageState path not found: "${p.storageState}" (resolved from the current directory) \u2014 the scan will not be able to load this session`
|
|
@@ -53786,11 +54735,11 @@ function imageExists(tag) {
|
|
|
53786
54735
|
}
|
|
53787
54736
|
var CTX_PREFIX = "ultra11y-dyn-";
|
|
53788
54737
|
function buildImage(tag = IMAGE_TAG) {
|
|
53789
|
-
const ctx = mkdtempSync2(
|
|
54738
|
+
const ctx = mkdtempSync2(join36(tmpdir2(), CTX_PREFIX));
|
|
53790
54739
|
try {
|
|
53791
|
-
writeFileSync11(
|
|
53792
|
-
writeFileSync11(
|
|
53793
|
-
writeFileSync11(
|
|
54740
|
+
writeFileSync11(join36(ctx, "runner.mjs"), RUNNER);
|
|
54741
|
+
writeFileSync11(join36(ctx, "package.json"), PKG);
|
|
54742
|
+
writeFileSync11(join36(ctx, "Dockerfile"), DOCKERFILE);
|
|
53794
54743
|
execFileSync6("docker", ["build", "-t", tag, ctx], { stdio: "inherit", timeout: 9e5 });
|
|
53795
54744
|
} finally {
|
|
53796
54745
|
rmSync3(ctx, { recursive: true, force: true });
|
|
@@ -53801,7 +54750,7 @@ function cleanTempContexts() {
|
|
|
53801
54750
|
const dir = tmpdir2();
|
|
53802
54751
|
for (const name2 of readdirSync5(dir)) {
|
|
53803
54752
|
if (!name2.startsWith(CTX_PREFIX)) continue;
|
|
53804
|
-
rmSync3(
|
|
54753
|
+
rmSync3(join36(dir, name2), { recursive: true, force: true });
|
|
53805
54754
|
removed++;
|
|
53806
54755
|
}
|
|
53807
54756
|
return removed;
|
|
@@ -53867,7 +54816,7 @@ var PROBE_FIELDS = [
|
|
|
53867
54816
|
function runRunner(target, isFile, tag, snapshot = true) {
|
|
53868
54817
|
const args2 = ["run", "--rm"];
|
|
53869
54818
|
if (!snapshot) args2.push("-e", "ULTRA11Y_SNAPSHOT=0");
|
|
53870
|
-
if (isFile) args2.push("-v", `${
|
|
54819
|
+
if (isFile) args2.push("-v", `${resolve10(target)}:${MOUNT}:ro`);
|
|
53871
54820
|
args2.push(tag, isFile ? MOUNT : target);
|
|
53872
54821
|
let stdout;
|
|
53873
54822
|
try {
|
|
@@ -53950,7 +54899,7 @@ function toDynamicResult(out2, target, lang = "en", engine = "axe-core@playwrigh
|
|
|
53950
54899
|
var DOCKER_TESTED_SCS = ["1.4.10"];
|
|
53951
54900
|
function runScan(opts) {
|
|
53952
54901
|
const isUrl3 = /^https?:\/\//i.test(opts.target);
|
|
53953
|
-
if (!isUrl3 && !
|
|
54902
|
+
if (!isUrl3 && !existsSync22(opts.target)) {
|
|
53954
54903
|
throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
|
|
53955
54904
|
}
|
|
53956
54905
|
if (!dockerAvailable()) {
|
|
@@ -53958,7 +54907,7 @@ function runScan(opts) {
|
|
|
53958
54907
|
}
|
|
53959
54908
|
const tag = opts.tag ?? IMAGE_TAG;
|
|
53960
54909
|
if (!imageExists(tag)) buildImage(tag);
|
|
53961
|
-
const isFile = !isUrl3 &&
|
|
54910
|
+
const isFile = !isUrl3 && existsSync22(opts.target) && statSync8(opts.target).isFile();
|
|
53962
54911
|
const out2 = runRunner(opts.target, isFile, tag, Boolean(opts.snapshotRoot));
|
|
53963
54912
|
const id = opts.snapshotRoot ? writeRunnerSnapshot(opts.snapshotRoot, out2, opts.target) : void 0;
|
|
53964
54913
|
return { ...toDynamicResult(out2, opts.target), testedScs: [...DOCKER_TESTED_SCS], ...id ? { snapshots: [id] } : {} };
|
|
@@ -54045,10 +54994,10 @@ async function runCrawlScan(opts) {
|
|
|
54045
54994
|
var sevRank = { bloquant: 3, majeur: 2, mineur: 1 };
|
|
54046
54995
|
function resolveHostAnchor(file, snippet2) {
|
|
54047
54996
|
const s = snippet2?.trim();
|
|
54048
|
-
if (!s || !
|
|
54997
|
+
if (!s || !existsSync22(file)) return null;
|
|
54049
54998
|
let source;
|
|
54050
54999
|
try {
|
|
54051
|
-
source =
|
|
55000
|
+
source = readFileSync19(file, "utf8");
|
|
54052
55001
|
} catch {
|
|
54053
55002
|
return null;
|
|
54054
55003
|
}
|
|
@@ -54175,9 +55124,9 @@ function mergeSnapshotAudit(base, snap) {
|
|
|
54175
55124
|
}
|
|
54176
55125
|
|
|
54177
55126
|
// src/scan-local.ts
|
|
54178
|
-
import { existsSync as
|
|
55127
|
+
import { existsSync as existsSync23, statSync as statSync9 } from "fs";
|
|
54179
55128
|
import { createRequire } from "module";
|
|
54180
|
-
import { resolve as
|
|
55129
|
+
import { resolve as resolve11 } from "path";
|
|
54181
55130
|
var LOCAL_ENGINE = "axe-core@playwright (local)";
|
|
54182
55131
|
var LOCAL_TESTED_SCS = ["1.4.4", "1.4.10", "1.4.12", "2.4.7", "1.4.13"];
|
|
54183
55132
|
function localTestedScs(interact) {
|
|
@@ -54187,7 +55136,7 @@ var PW_SPEC = "@playwright/test";
|
|
|
54187
55136
|
var AXE_SPEC = "@axe-core/playwright";
|
|
54188
55137
|
function localAvailable(cwd) {
|
|
54189
55138
|
try {
|
|
54190
|
-
const req = createRequire(
|
|
55139
|
+
const req = createRequire(resolve11(cwd, "package.json"));
|
|
54191
55140
|
req.resolve(PW_SPEC);
|
|
54192
55141
|
req.resolve(AXE_SPEC);
|
|
54193
55142
|
return true;
|
|
@@ -54199,7 +55148,7 @@ function resolveLocalDeps(cwd) {
|
|
|
54199
55148
|
let chromium;
|
|
54200
55149
|
let AxeBuilder;
|
|
54201
55150
|
try {
|
|
54202
|
-
const req = createRequire(
|
|
55151
|
+
const req = createRequire(resolve11(cwd, "package.json"));
|
|
54203
55152
|
const pw = req(PW_SPEC);
|
|
54204
55153
|
const axeMod = req(AXE_SPEC);
|
|
54205
55154
|
chromium = pw.chromium;
|
|
@@ -54678,7 +55627,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
|
|
|
54678
55627
|
const page = await context.newPage();
|
|
54679
55628
|
const empty = [];
|
|
54680
55629
|
try {
|
|
54681
|
-
const url = isFile ? "file://" +
|
|
55630
|
+
const url = isFile ? "file://" + resolve11(target) : target;
|
|
54682
55631
|
await page.goto(url, { waitUntil: "load", timeout: 45e3 });
|
|
54683
55632
|
await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => {
|
|
54684
55633
|
});
|
|
@@ -54746,7 +55695,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
|
|
|
54746
55695
|
}
|
|
54747
55696
|
async function runScanLocal(opts) {
|
|
54748
55697
|
const isUrl3 = /^https?:\/\//i.test(opts.target);
|
|
54749
|
-
if (!isUrl3 && !
|
|
55698
|
+
if (!isUrl3 && !existsSync23(opts.target)) {
|
|
54750
55699
|
throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
|
|
54751
55700
|
}
|
|
54752
55701
|
const isFile = !isUrl3 && statSync9(opts.target).isFile();
|
|
@@ -55333,7 +56282,7 @@ function fixSummary(r, lang = "fr", write = false) {
|
|
|
55333
56282
|
// src/init.ts
|
|
55334
56283
|
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync10, chmodSync, realpathSync as realpathSync2 } from "fs";
|
|
55335
56284
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
55336
|
-
import { join as
|
|
56285
|
+
import { join as join37, relative as relative4, sep as sep4 } from "path";
|
|
55337
56286
|
var EN_SEV = { bloquant: "blocking", majeur: "major", mineur: "minor" };
|
|
55338
56287
|
function repoRoot() {
|
|
55339
56288
|
try {
|
|
@@ -55451,23 +56400,23 @@ jobs:
|
|
|
55451
56400
|
`;
|
|
55452
56401
|
}
|
|
55453
56402
|
function writeHook(root, enginePath, failOn, mode = "staged") {
|
|
55454
|
-
const dir =
|
|
56403
|
+
const dir = join37(root, ".git", "hooks");
|
|
55455
56404
|
mkdirSync10(dir, { recursive: true });
|
|
55456
|
-
const path =
|
|
56405
|
+
const path = join37(dir, "pre-commit");
|
|
55457
56406
|
writeFileSync13(path, mode === "baseline" ? hookScript(enginePath, failOn) : stagedHookScript(enginePath, failOn));
|
|
55458
56407
|
chmodSync(path, 493);
|
|
55459
56408
|
return path;
|
|
55460
56409
|
}
|
|
55461
56410
|
function writeCi(root, enginePath, failOn) {
|
|
55462
|
-
const dir =
|
|
56411
|
+
const dir = join37(root, ".github", "workflows");
|
|
55463
56412
|
mkdirSync10(dir, { recursive: true });
|
|
55464
|
-
const path =
|
|
56413
|
+
const path = join37(dir, "a11y.yml");
|
|
55465
56414
|
writeFileSync13(path, ciWorkflow(enginePath, failOn));
|
|
55466
56415
|
return path;
|
|
55467
56416
|
}
|
|
55468
56417
|
|
|
55469
56418
|
// src/install/json-edit.ts
|
|
55470
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
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";
|
|
55471
56420
|
import { dirname as dirname10 } from "path";
|
|
55472
56421
|
var SettingsParseError = class extends Error {
|
|
55473
56422
|
constructor(path, cause) {
|
|
@@ -55481,10 +56430,10 @@ function stamp() {
|
|
|
55481
56430
|
return (/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-");
|
|
55482
56431
|
}
|
|
55483
56432
|
function writeTextWithBackup(path, content, marker = "ultra11y") {
|
|
55484
|
-
if (
|
|
56433
|
+
if (existsSync24(path) && readFileSync20(path, "utf8") === content) return { path, changed: false };
|
|
55485
56434
|
mkdirSync11(dirname10(path), { recursive: true });
|
|
55486
56435
|
let backup;
|
|
55487
|
-
if (
|
|
56436
|
+
if (existsSync24(path)) {
|
|
55488
56437
|
backup = `${path}.${marker}-backup-${stamp()}`;
|
|
55489
56438
|
copyFileSync2(path, backup);
|
|
55490
56439
|
}
|
|
@@ -55500,8 +56449,8 @@ function writeTextWithBackup(path, content, marker = "ultra11y") {
|
|
|
55500
56449
|
}
|
|
55501
56450
|
function editJsonFile(path, mutate) {
|
|
55502
56451
|
let root = {};
|
|
55503
|
-
if (
|
|
55504
|
-
const raw =
|
|
56452
|
+
if (existsSync24(path)) {
|
|
56453
|
+
const raw = readFileSync20(path, "utf8");
|
|
55505
56454
|
if (raw.trim()) {
|
|
55506
56455
|
let parsed;
|
|
55507
56456
|
try {
|
|
@@ -55517,13 +56466,13 @@ function editJsonFile(path, mutate) {
|
|
|
55517
56466
|
}
|
|
55518
56467
|
const before = JSON.stringify(root);
|
|
55519
56468
|
mutate(root);
|
|
55520
|
-
if (JSON.stringify(root) === before &&
|
|
56469
|
+
if (JSON.stringify(root) === before && existsSync24(path)) return { path, changed: false };
|
|
55521
56470
|
return writeTextWithBackup(path, `${JSON.stringify(root, null, 2)}
|
|
55522
56471
|
`);
|
|
55523
56472
|
}
|
|
55524
56473
|
function readJsonSafe(path) {
|
|
55525
56474
|
try {
|
|
55526
|
-
const parsed = JSON.parse(
|
|
56475
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
55527
56476
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
55528
56477
|
} catch {
|
|
55529
56478
|
return null;
|
|
@@ -55564,7 +56513,7 @@ function claudeCodeWired(settingsPath) {
|
|
|
55564
56513
|
}
|
|
55565
56514
|
|
|
55566
56515
|
// src/install/text-edit.ts
|
|
55567
|
-
import { existsSync as
|
|
56516
|
+
import { existsSync as existsSync25, readFileSync as readFileSync21, rmSync as rmSync5 } from "fs";
|
|
55568
56517
|
var BLOCK_BEGIN = "<!-- BEGIN ultra11y (managed by `ultra11y install --agents-md`; edit outside this block) -->";
|
|
55569
56518
|
var BLOCK_END = "<!-- END ultra11y -->";
|
|
55570
56519
|
var blockRe = () => new RegExp(`${escapeRe2(BLOCK_BEGIN)}[\\s\\S]*?${escapeRe2(BLOCK_END)}\\n?`);
|
|
@@ -55576,15 +56525,15 @@ function upsertManagedBlock(path, body2) {
|
|
|
55576
56525
|
${body2.trim()}
|
|
55577
56526
|
${BLOCK_END}
|
|
55578
56527
|
`;
|
|
55579
|
-
if (!
|
|
55580
|
-
const current =
|
|
56528
|
+
if (!existsSync25(path)) return writeTextWithBackup(path, block);
|
|
56529
|
+
const current = readFileSync21(path, "utf8");
|
|
55581
56530
|
if (blockRe().test(current)) return writeTextWithBackup(path, current.replace(blockRe(), block));
|
|
55582
56531
|
const sep8 = current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
55583
56532
|
return writeTextWithBackup(path, `${current}${sep8}${block}`);
|
|
55584
56533
|
}
|
|
55585
56534
|
function removeManagedBlock(path) {
|
|
55586
|
-
if (!
|
|
55587
|
-
const current =
|
|
56535
|
+
if (!existsSync25(path)) return { path, changed: false };
|
|
56536
|
+
const current = readFileSync21(path, "utf8");
|
|
55588
56537
|
if (!blockRe().test(current)) return { path, changed: false };
|
|
55589
56538
|
const rest = current.replace(blockRe(), "");
|
|
55590
56539
|
if (rest.trim() === "") {
|
|
@@ -55594,13 +56543,13 @@ function removeManagedBlock(path) {
|
|
|
55594
56543
|
return writeTextWithBackup(path, rest.replace(/\n+$/, "\n"));
|
|
55595
56544
|
}
|
|
55596
56545
|
function hasManagedBlock(path) {
|
|
55597
|
-
return
|
|
56546
|
+
return existsSync25(path) && blockRe().test(readFileSync21(path, "utf8"));
|
|
55598
56547
|
}
|
|
55599
56548
|
|
|
55600
56549
|
// src/install/agents-md.ts
|
|
55601
|
-
import { join as
|
|
56550
|
+
import { join as join38 } from "path";
|
|
55602
56551
|
function agentsMdPath(root) {
|
|
55603
|
-
return
|
|
56552
|
+
return join38(root, "AGENTS.md");
|
|
55604
56553
|
}
|
|
55605
56554
|
function agentsMdBlock(root) {
|
|
55606
56555
|
const e = engineInvocation(root);
|
|
@@ -55655,25 +56604,25 @@ function agentsMdWired(root) {
|
|
|
55655
56604
|
}
|
|
55656
56605
|
|
|
55657
56606
|
// src/install/codex.ts
|
|
55658
|
-
import { existsSync as
|
|
55659
|
-
import { join as
|
|
56607
|
+
import { existsSync as existsSync27, readFileSync as readFileSync24 } from "fs";
|
|
56608
|
+
import { join as join40 } from "path";
|
|
55660
56609
|
|
|
55661
56610
|
// src/install/paths.ts
|
|
55662
|
-
import { copyFileSync as copyFileSync3, cpSync, existsSync as
|
|
56611
|
+
import { copyFileSync as copyFileSync3, cpSync, existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync23, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
|
|
55663
56612
|
import { homedir as homedir2 } from "os";
|
|
55664
|
-
import { dirname as dirname11, join as
|
|
56613
|
+
import { dirname as dirname11, join as join39 } from "path";
|
|
55665
56614
|
function codexHome() {
|
|
55666
|
-
return process.env.CODEX_HOME ||
|
|
56615
|
+
return process.env.CODEX_HOME || join39(homedir2(), ".codex");
|
|
55667
56616
|
}
|
|
55668
56617
|
function opencodeConfigDir() {
|
|
55669
56618
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
55670
|
-
return
|
|
56619
|
+
return join39(xdg && xdg !== "" ? xdg : join39(homedir2(), ".config"), "opencode");
|
|
55671
56620
|
}
|
|
55672
56621
|
function claudeSettingsPath(project, cwd = process.cwd()) {
|
|
55673
|
-
return project ?
|
|
56622
|
+
return project ? join39(cwd, ".claude", "settings.json") : join39(homedir2(), ".claude", "settings.json");
|
|
55674
56623
|
}
|
|
55675
56624
|
function pinnedEnginePath() {
|
|
55676
|
-
return
|
|
56625
|
+
return join39(homedir2(), ".ultra11y", "bin", "ultra11y.mjs");
|
|
55677
56626
|
}
|
|
55678
56627
|
function installedCliCommand() {
|
|
55679
56628
|
const override = process.env.ULTRA11Y_BIN;
|
|
@@ -55688,7 +56637,7 @@ function installedCliCommand() {
|
|
|
55688
56637
|
if (source && !ephemeral) return `node ${JSON.stringify(source)}`;
|
|
55689
56638
|
const pin = pinnedEnginePath();
|
|
55690
56639
|
if (source) {
|
|
55691
|
-
mkdirSync12(
|
|
56640
|
+
mkdirSync12(join39(pin, ".."), { recursive: true });
|
|
55692
56641
|
copyFileSync3(source, pin);
|
|
55693
56642
|
}
|
|
55694
56643
|
return `node ${JSON.stringify(pin)}`;
|
|
@@ -55701,32 +56650,32 @@ function packageRoot() {
|
|
|
55701
56650
|
return null;
|
|
55702
56651
|
}
|
|
55703
56652
|
for (let i2 = 0; i2 < 6 && dir && dir !== "/"; i2++) {
|
|
55704
|
-
if (
|
|
56653
|
+
if (existsSync26(join39(dir, "skills", "review-a11y", "SKILL.md"))) return dir;
|
|
55705
56654
|
dir = dirname11(dir);
|
|
55706
56655
|
}
|
|
55707
56656
|
return null;
|
|
55708
56657
|
}
|
|
55709
56658
|
function bundledSkillsDir() {
|
|
55710
56659
|
const root = packageRoot();
|
|
55711
|
-
const p = root ?
|
|
55712
|
-
return p &&
|
|
56660
|
+
const p = root ? join39(root, "skills") : null;
|
|
56661
|
+
return p && existsSync26(p) ? p : null;
|
|
55713
56662
|
}
|
|
55714
56663
|
function bundledOpencodePlugin() {
|
|
55715
56664
|
const root = packageRoot();
|
|
55716
|
-
const p = root ?
|
|
55717
|
-
return p &&
|
|
56665
|
+
const p = root ? join39(root, ".opencode", "plugins", "ultra11y.js") : null;
|
|
56666
|
+
return p && existsSync26(p) ? p : null;
|
|
55718
56667
|
}
|
|
55719
56668
|
function copySkillsInto(source, dest) {
|
|
55720
56669
|
const out2 = [];
|
|
55721
56670
|
for (const name2 of ["ultra11y", "review-a11y"]) {
|
|
55722
|
-
const from =
|
|
55723
|
-
if (!
|
|
55724
|
-
const to =
|
|
55725
|
-
const same = ["SKILL.md",
|
|
55726
|
-
const a =
|
|
55727
|
-
const b =
|
|
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);
|
|
55728
56677
|
try {
|
|
55729
|
-
return
|
|
56678
|
+
return existsSync26(b) && statSync10(a).size === statSync10(b).size && readFileSync23(a).equals(readFileSync23(b));
|
|
55730
56679
|
} catch {
|
|
55731
56680
|
return false;
|
|
55732
56681
|
}
|
|
@@ -55776,7 +56725,7 @@ function featuresBody(content) {
|
|
|
55776
56725
|
function installCodex({ codexDir, command, skillsSource }) {
|
|
55777
56726
|
const reports = [];
|
|
55778
56727
|
reports.push(
|
|
55779
|
-
editJsonFile(
|
|
56728
|
+
editJsonFile(join40(codexDir, "hooks.json"), (root) => {
|
|
55780
56729
|
const hooks = root.hooks ??= {};
|
|
55781
56730
|
hooks.PreToolUse = [
|
|
55782
56731
|
...withoutOurs2(hooks.PreToolUse),
|
|
@@ -55784,11 +56733,11 @@ function installCodex({ codexDir, command, skillsSource }) {
|
|
|
55784
56733
|
];
|
|
55785
56734
|
})
|
|
55786
56735
|
);
|
|
55787
|
-
const configPath =
|
|
55788
|
-
const current =
|
|
56736
|
+
const configPath = join40(codexDir, "config.toml");
|
|
56737
|
+
const current = existsSync27(configPath) ? readFileSync24(configPath, "utf8") : "";
|
|
55789
56738
|
const edit = enableHooksFeature(current);
|
|
55790
56739
|
if (edit.changed) reports.push(writeTextWithBackup(configPath, edit.content));
|
|
55791
|
-
if (skillsSource) reports.push(...copySkillsInto(skillsSource,
|
|
56740
|
+
if (skillsSource) reports.push(...copySkillsInto(skillsSource, join40(codexDir, "skills")));
|
|
55792
56741
|
return {
|
|
55793
56742
|
reports,
|
|
55794
56743
|
guidance: [
|
|
@@ -55803,8 +56752,8 @@ function installCodex({ codexDir, command, skillsSource }) {
|
|
|
55803
56752
|
}
|
|
55804
56753
|
function uninstallCodex({ codexDir }) {
|
|
55805
56754
|
const reports = [];
|
|
55806
|
-
const hooksPath =
|
|
55807
|
-
if (
|
|
56755
|
+
const hooksPath = join40(codexDir, "hooks.json");
|
|
56756
|
+
if (existsSync27(hooksPath)) {
|
|
55808
56757
|
reports.push(
|
|
55809
56758
|
editJsonFile(hooksPath, (root) => {
|
|
55810
56759
|
const hooks = root.hooks;
|
|
@@ -55819,33 +56768,33 @@ function uninstallCodex({ codexDir }) {
|
|
|
55819
56768
|
return { reports, guidance: ["codex: `[features] hooks = true` was left enabled \u2014 other hooks may depend on it."] };
|
|
55820
56769
|
}
|
|
55821
56770
|
function codexWired(codexDir) {
|
|
55822
|
-
const root = readJsonSafe(
|
|
56771
|
+
const root = readJsonSafe(join40(codexDir, "hooks.json"));
|
|
55823
56772
|
const groups = root?.hooks?.PreToolUse;
|
|
55824
56773
|
if (!Array.isArray(groups)) return 0;
|
|
55825
56774
|
return groups.reduce((n, g) => n + (g.hooks ?? []).filter((h) => String(h.command ?? "").includes(CODEX_MARKER)).length, 0);
|
|
55826
56775
|
}
|
|
55827
56776
|
function codexHooksEnabled(codexDir) {
|
|
55828
|
-
const p =
|
|
55829
|
-
return
|
|
56777
|
+
const p = join40(codexDir, "config.toml");
|
|
56778
|
+
return existsSync27(p) && HOOKS_ON.test(featuresBody(readFileSync24(p, "utf8")));
|
|
55830
56779
|
}
|
|
55831
56780
|
|
|
55832
56781
|
// src/install/opencode.ts
|
|
55833
|
-
import { existsSync as
|
|
55834
|
-
import { join as
|
|
56782
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25, rmSync as rmSync6 } from "fs";
|
|
56783
|
+
import { join as join41 } from "path";
|
|
55835
56784
|
var OPENCODE_MARKER = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
55836
56785
|
function pluginTarget(configDir) {
|
|
55837
|
-
return
|
|
56786
|
+
return join41(configDir, "plugin", "ultra11y.js");
|
|
55838
56787
|
}
|
|
55839
56788
|
function installOpencode({ configDir, pluginSource, skillsSource, enginePath }) {
|
|
55840
56789
|
const target = pluginTarget(configDir);
|
|
55841
|
-
if (
|
|
56790
|
+
if (existsSync28(target) && !readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
|
|
55842
56791
|
throw new Error(`refusing to overwrite ${target}: it is not a file ultra11y wrote. Remove it manually if you want the plugin here.`);
|
|
55843
56792
|
}
|
|
55844
|
-
const reports = [writeTextWithBackup(target,
|
|
55845
|
-
if (enginePath &&
|
|
55846
|
-
reports.push(writeTextWithBackup(
|
|
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")));
|
|
55847
56796
|
}
|
|
55848
|
-
if (skillsSource) reports.push(...copySkillsInto(skillsSource,
|
|
56797
|
+
if (skillsSource) reports.push(...copySkillsInto(skillsSource, join41(configDir, "skills")));
|
|
55849
56798
|
return {
|
|
55850
56799
|
reports,
|
|
55851
56800
|
guidance: [
|
|
@@ -55858,13 +56807,13 @@ function installOpencode({ configDir, pluginSource, skillsSource, enginePath })
|
|
|
55858
56807
|
function uninstallOpencode({ configDir }) {
|
|
55859
56808
|
const reports = [];
|
|
55860
56809
|
const target = pluginTarget(configDir);
|
|
55861
|
-
if (
|
|
56810
|
+
if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
|
|
55862
56811
|
rmSync6(target, { force: true });
|
|
55863
|
-
rmSync6(
|
|
56812
|
+
rmSync6(join41(configDir, "plugin", "ultra11y.mjs"), { force: true });
|
|
55864
56813
|
reports.push({ path: target, changed: true });
|
|
55865
56814
|
}
|
|
55866
|
-
const configPath =
|
|
55867
|
-
if (
|
|
56815
|
+
const configPath = join41(configDir, "opencode.json");
|
|
56816
|
+
if (existsSync28(configPath)) {
|
|
55868
56817
|
reports.push(
|
|
55869
56818
|
editJsonFile(configPath, (root) => {
|
|
55870
56819
|
if (!Array.isArray(root.plugin)) return;
|
|
@@ -55878,9 +56827,9 @@ function uninstallOpencode({ configDir }) {
|
|
|
55878
56827
|
}
|
|
55879
56828
|
function opencodeWired(configDir) {
|
|
55880
56829
|
const target = pluginTarget(configDir);
|
|
55881
|
-
if (
|
|
56830
|
+
if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) return true;
|
|
55882
56831
|
try {
|
|
55883
|
-
const cfg = JSON.parse(
|
|
56832
|
+
const cfg = JSON.parse(readFileSync25(join41(configDir, "opencode.json"), "utf8"));
|
|
55884
56833
|
return Array.isArray(cfg.plugin) && cfg.plugin.some((p) => /^ultra11y(@|$)/.test(String(p)));
|
|
55885
56834
|
} catch {
|
|
55886
56835
|
return false;
|
|
@@ -55888,8 +56837,8 @@ function opencodeWired(configDir) {
|
|
|
55888
56837
|
}
|
|
55889
56838
|
|
|
55890
56839
|
// src/install/index.ts
|
|
55891
|
-
import { existsSync as
|
|
55892
|
-
import { join as
|
|
56840
|
+
import { existsSync as existsSync29 } from "fs";
|
|
56841
|
+
import { join as join42 } from "path";
|
|
55893
56842
|
var ALL_TARGETS = ["claude-code", "codex", "opencode"];
|
|
55894
56843
|
function parseTargets(flags2) {
|
|
55895
56844
|
const picked = /* @__PURE__ */ new Set();
|
|
@@ -55928,7 +56877,7 @@ function installForTargets(opts) {
|
|
|
55928
56877
|
const out3 = installCodex({ codexDir: codexHome(), command: cmd(), skillsSource: opts.skills === false ? null : bundledSkillsDir() });
|
|
55929
56878
|
r.reports.push(...out3.reports);
|
|
55930
56879
|
r.guidance.push(...out3.guidance);
|
|
55931
|
-
} else r.reports.push({ path:
|
|
56880
|
+
} else r.reports.push({ path: join42(codexHome(), "hooks.json"), changed: false });
|
|
55932
56881
|
break;
|
|
55933
56882
|
}
|
|
55934
56883
|
case "opencode": {
|
|
@@ -55937,18 +56886,18 @@ function installForTargets(opts) {
|
|
|
55937
56886
|
r.error = "opencode: this engine was not run from a package carrying .opencode/plugins/ultra11y.js \u2014 install ultra11y from npm and retry.";
|
|
55938
56887
|
break;
|
|
55939
56888
|
}
|
|
55940
|
-
const configDir = opts.project === true ?
|
|
56889
|
+
const configDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
|
|
55941
56890
|
if (!opts.dryRun) {
|
|
55942
|
-
const engine =
|
|
56891
|
+
const engine = existsSync29(pinnedEnginePath()) ? pinnedEnginePath() : join42(packageRoot() ?? "", "scripts", "ultra11y.mjs");
|
|
55943
56892
|
const out3 = installOpencode({
|
|
55944
56893
|
configDir,
|
|
55945
56894
|
pluginSource: source,
|
|
55946
56895
|
skillsSource: opts.skills === false ? null : bundledSkillsDir(),
|
|
55947
|
-
enginePath:
|
|
56896
|
+
enginePath: existsSync29(engine) ? engine : null
|
|
55948
56897
|
});
|
|
55949
56898
|
r.reports.push(...out3.reports);
|
|
55950
56899
|
r.guidance.push(...out3.guidance);
|
|
55951
|
-
} else r.reports.push({ path:
|
|
56900
|
+
} else r.reports.push({ path: join42(configDir, "plugin", "ultra11y.js"), changed: false });
|
|
55952
56901
|
break;
|
|
55953
56902
|
}
|
|
55954
56903
|
case "agents-md": {
|
|
@@ -55986,7 +56935,7 @@ function uninstallForTargets(opts) {
|
|
|
55986
56935
|
break;
|
|
55987
56936
|
}
|
|
55988
56937
|
case "opencode": {
|
|
55989
|
-
const out3 = uninstallOpencode({ configDir: opts.project === true ?
|
|
56938
|
+
const out3 = uninstallOpencode({ configDir: opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir() });
|
|
55990
56939
|
r.reports.push(...out3.reports);
|
|
55991
56940
|
r.guidance.push(...out3.guidance);
|
|
55992
56941
|
break;
|
|
@@ -56007,18 +56956,18 @@ function statusReport(opts = {}) {
|
|
|
56007
56956
|
const claudePath = claudeSettingsPath(opts.project === true, cwd);
|
|
56008
56957
|
const root = projectRoot(cwd);
|
|
56009
56958
|
const codexDir = codexHome();
|
|
56010
|
-
const ocDir = opts.project === true ?
|
|
56959
|
+
const ocDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
|
|
56011
56960
|
const codexOn = codexWired(codexDir) > 0;
|
|
56012
56961
|
return [
|
|
56013
56962
|
{ target: "claude-code", wired: claudeCodeWired(claudePath) > 0, path: claudePath, note: "the plugin route wires this separately and does not show here" },
|
|
56014
56963
|
{
|
|
56015
56964
|
target: "codex",
|
|
56016
56965
|
wired: codexOn,
|
|
56017
|
-
path:
|
|
56966
|
+
path: join42(codexDir, "hooks.json"),
|
|
56018
56967
|
// A wired hook behind a disabled feature flag never fires and looks fine. Say so.
|
|
56019
56968
|
note: codexOn && !codexHooksEnabled(codexDir) ? "WIRED BUT INERT: `[features] hooks = true` is not set in config.toml" : void 0
|
|
56020
56969
|
},
|
|
56021
|
-
{ target: "opencode", wired: opencodeWired(ocDir), path:
|
|
56970
|
+
{ target: "opencode", wired: opencodeWired(ocDir), path: join42(ocDir, "plugin", "ultra11y.js") },
|
|
56022
56971
|
{ target: "agents-md", wired: agentsMdWired(root), path: agentsMdPath(root) }
|
|
56023
56972
|
];
|
|
56024
56973
|
}
|
|
@@ -56445,8 +57394,8 @@ function perPageTable(result, standard = CORE2, lang = "en") {
|
|
|
56445
57394
|
|
|
56446
57395
|
// src/evidence.ts
|
|
56447
57396
|
import { createHash as createHash6 } from "crypto";
|
|
56448
|
-
import { existsSync as
|
|
56449
|
-
import { join as
|
|
57397
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync13, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "fs";
|
|
57398
|
+
import { join as join43 } from "path";
|
|
56450
57399
|
var DEFAULT_GEOMETRY = { pad: 24, minWidth: 320, minHeight: 120, maxWidth: 960, maxHeight: 540 };
|
|
56451
57400
|
var DEFAULT_CAPS = { perRule: 6, perPage: 12, total: 200 };
|
|
56452
57401
|
var HALO = { r: 255, g: 255, b: 255, a: 0.92 };
|
|
@@ -56463,7 +57412,7 @@ function pageIdOfSnapshot(file) {
|
|
|
56463
57412
|
}
|
|
56464
57413
|
function readJson3(file) {
|
|
56465
57414
|
try {
|
|
56466
|
-
return JSON.parse(
|
|
57415
|
+
return JSON.parse(readFileSync26(file, "utf8"));
|
|
56467
57416
|
} catch {
|
|
56468
57417
|
return void 0;
|
|
56469
57418
|
}
|
|
@@ -56479,21 +57428,21 @@ function resolveScale(img, viewport, boxes) {
|
|
|
56479
57428
|
}
|
|
56480
57429
|
function loadPageEvidence(root, pageId) {
|
|
56481
57430
|
const dir = snapshotDir(root, pageId);
|
|
56482
|
-
const domFile =
|
|
56483
|
-
const shotFile =
|
|
56484
|
-
if (!
|
|
56485
|
-
if (!
|
|
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" };
|
|
56486
57435
|
let dom;
|
|
56487
57436
|
let shot;
|
|
56488
57437
|
try {
|
|
56489
|
-
dom =
|
|
56490
|
-
shot =
|
|
57438
|
+
dom = readFileSync26(domFile, "utf8");
|
|
57439
|
+
shot = readFileSync26(shotFile);
|
|
56491
57440
|
} catch {
|
|
56492
57441
|
return { skip: "no-snapshot" };
|
|
56493
57442
|
}
|
|
56494
57443
|
const img = decodePng(shot);
|
|
56495
57444
|
if (!img) return { skip: "unreadable-image" };
|
|
56496
|
-
const digest = readJson3(
|
|
57445
|
+
const digest = readJson3(join43(dir, "boxes.json"));
|
|
56497
57446
|
if (!digest) return { skip: "no-boxes" };
|
|
56498
57447
|
const doc = parseHtml(dom, domFile);
|
|
56499
57448
|
const boxes = alignedBoxes(dom, digest, doc);
|
|
@@ -56509,7 +57458,7 @@ function loadPageEvidence(root, pageId) {
|
|
|
56509
57458
|
return { pageId, img, scale, viewport, ordinalOf, boxes, truncated: Boolean(digest.truncated) };
|
|
56510
57459
|
}
|
|
56511
57460
|
function metaOf(dir) {
|
|
56512
|
-
const raw = readJson3(
|
|
57461
|
+
const raw = readJson3(join43(dir, "meta.json"));
|
|
56513
57462
|
if (raw === void 0) return void 0;
|
|
56514
57463
|
const v = validateSnapshotMeta(raw);
|
|
56515
57464
|
return v.ok && v.meta ? v.meta : void 0;
|
|
@@ -56586,7 +57535,7 @@ var S2 = {
|
|
|
56586
57535
|
alt: (sel, page) => `Capture recadr\xE9e de l'\xE9l\xE9ment ${sel} sur la page ${page}, entour\xE9 d'un cadre`,
|
|
56587
57536
|
notImaged: (n) => `${n} occurrence(s) ne sont pas illustr\xE9es :`,
|
|
56588
57537
|
reasons: {
|
|
56589
|
-
"no-snapshot": "
|
|
57538
|
+
"no-snapshot": "la capture de page que ce constat d\xE9signe n'est pas sur ce disque \u2014 sa r\xE9f\xE9rence fichier:ligne reste valable",
|
|
56590
57539
|
"no-screenshot": "le producteur n'a pas fourni de capture pour cette page, donc le niveau pixel est inactif ici",
|
|
56591
57540
|
"unreadable-image": "la capture n'est pas d\xE9codable (le moteur lit le truecolour 8 bits uniquement) \u2014 mieux vaut aucune image qu'une fausse",
|
|
56592
57541
|
"no-boxes": "les positions d'\xE9l\xE9ments ne se v\xE9rifient pas contre le DOM s\xE9rialis\xE9, elles ont donc \xE9t\xE9 refus\xE9es en bloc plut\xF4t que d'encadrer le mauvais \xE9l\xE9ment",
|
|
@@ -56597,14 +57546,15 @@ var S2 = {
|
|
|
56597
57546
|
"zero-area": "l'\xE9l\xE9ment n'occupe aucune surface peinte",
|
|
56598
57547
|
"below-the-fold": "l'\xE9l\xE9ment est hors de la capture (la capture couvre la fen\xEAtre, pas la page enti\xE8re) \u2014 sa r\xE9f\xE9rence fichier:ligne reste valable",
|
|
56599
57548
|
"unknown-scale": "le rapport pixels/CSS est ind\xE9termin\xE9 pour cette page ; un rapport devin\xE9 encadrerait le mauvais endroit",
|
|
56600
|
-
|
|
57549
|
+
deduplicated: "m\xEAme d\xE9faut sur le m\xEAme \xE9l\xE9ment : il est montr\xE9 par l'illustration d'une autre occurrence, rien n'a \xE9t\xE9 perdu",
|
|
57550
|
+
capped: "d\xE9faut distinct laiss\xE9 sans image : le plafond de vignettes de ce lot est atteint \u2014 relevez `--evidence-max` pour les obtenir"
|
|
56601
57551
|
}
|
|
56602
57552
|
},
|
|
56603
57553
|
en: {
|
|
56604
57554
|
alt: (sel, page) => `Cropped screenshot of the ${sel} element on the ${page} page, outlined`,
|
|
56605
57555
|
notImaged: (n) => `${n} occurrence(s) are not illustrated:`,
|
|
56606
57556
|
reasons: {
|
|
56607
|
-
"no-snapshot": "
|
|
57557
|
+
"no-snapshot": "the page snapshot this finding names is not on this disk \u2014 its file:line reference still holds",
|
|
56608
57558
|
"no-screenshot": "the producer supplied no screenshot for this page, so the pixel tier is inactive here",
|
|
56609
57559
|
"unreadable-image": "the screenshot does not decode (the engine reads 8-bit truecolour only) \u2014 no image beats a wrong one",
|
|
56610
57560
|
"no-boxes": "the element positions do not verify against the serialized DOM, so they were refused wholesale rather than outlining the wrong element",
|
|
@@ -56615,22 +57565,26 @@ var S2 = {
|
|
|
56615
57565
|
"zero-area": "the element paints no box",
|
|
56616
57566
|
"below-the-fold": "the element sits outside the capture (the screenshot covers the viewport, not the whole page) \u2014 its file:line reference still holds",
|
|
56617
57567
|
"unknown-scale": "the image-pixel to CSS-pixel ratio is indeterminate for this page; a guessed ratio would outline the wrong place",
|
|
56618
|
-
|
|
57568
|
+
deduplicated: "the same defect on the same element: it is shown by the illustration of another occurrence, nothing was lost",
|
|
57569
|
+
capped: "a distinct defect left undrawn: this run's crop limit was reached \u2014 raise `--evidence-max` to get them"
|
|
56619
57570
|
}
|
|
56620
57571
|
}
|
|
56621
57572
|
};
|
|
56622
|
-
function
|
|
57573
|
+
function evidenceRefusals(m, pageId, lang) {
|
|
56623
57574
|
const t2 = pageId === null ? m.totals : m.perPage.get(pageId);
|
|
56624
|
-
if (!t2) return
|
|
57575
|
+
if (!t2) return void 0;
|
|
56625
57576
|
const missing = t2.located - t2.imaged;
|
|
56626
|
-
if (missing <= 0) return
|
|
57577
|
+
if (missing <= 0) return void 0;
|
|
57578
|
+
const entries = Object.entries(t2.skipped).filter(([, n]) => n);
|
|
56627
57579
|
const s = S2[lang];
|
|
56628
|
-
|
|
56629
|
-
|
|
56630
|
-
|
|
56631
|
-
|
|
56632
|
-
|
|
56633
|
-
|
|
57580
|
+
return {
|
|
57581
|
+
headline: s.notImaged(missing),
|
|
57582
|
+
reasons: entries.sort((a, b) => b[1] - a[1]).map(([reason, n]) => `${n} \u2014 ${s.reasons[reason]}`)
|
|
57583
|
+
};
|
|
57584
|
+
}
|
|
57585
|
+
function evidenceNotice(m, pageId, lang) {
|
|
57586
|
+
const r = evidenceRefusals(m, pageId, lang);
|
|
57587
|
+
return r ? [r.headline, ...r.reasons.map((x) => `- ${x}`)] : [];
|
|
56634
57588
|
}
|
|
56635
57589
|
function bump(t2, reason) {
|
|
56636
57590
|
t2.skipped[reason] = (t2.skipped[reason] ?? 0) + 1;
|
|
@@ -56669,7 +57623,7 @@ function writeEvidence(result, opts) {
|
|
|
56669
57623
|
}
|
|
56670
57624
|
continue;
|
|
56671
57625
|
}
|
|
56672
|
-
const dir =
|
|
57626
|
+
const dir = join43(opts.outDir, "assets", pageId);
|
|
56673
57627
|
let wrote = false;
|
|
56674
57628
|
const perRule = /* @__PURE__ */ new Map();
|
|
56675
57629
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -56678,10 +57632,12 @@ function writeEvidence(result, opts) {
|
|
|
56678
57632
|
const id = findingId(f);
|
|
56679
57633
|
const key = `${f.ruleId}\0${f.selectorHint}`;
|
|
56680
57634
|
const ruleCount = perRule.get(f.ruleId) ?? 0;
|
|
56681
|
-
|
|
56682
|
-
|
|
56683
|
-
|
|
56684
|
-
|
|
57635
|
+
const overLimit = ruleCount >= caps.perRule || onPage >= caps.perPage || manifest.crops.size >= caps.total;
|
|
57636
|
+
const refusal = seen.has(key) ? "deduplicated" : overLimit ? "capped" : null;
|
|
57637
|
+
if (refusal) {
|
|
57638
|
+
manifest.skipped.set(id, refusal);
|
|
57639
|
+
bump(tally, refusal);
|
|
57640
|
+
bump(manifest.totals, refusal);
|
|
56685
57641
|
continue;
|
|
56686
57642
|
}
|
|
56687
57643
|
const crop = cropFinding(ctx, f, opts.geometry);
|
|
@@ -56692,7 +57648,7 @@ function writeEvidence(result, opts) {
|
|
|
56692
57648
|
continue;
|
|
56693
57649
|
}
|
|
56694
57650
|
const name2 = cropName(f);
|
|
56695
|
-
const path =
|
|
57651
|
+
const path = join43(dir, name2);
|
|
56696
57652
|
try {
|
|
56697
57653
|
if (!wrote) {
|
|
56698
57654
|
mkdirSync13(dir, { recursive: true });
|
|
@@ -56730,8 +57686,8 @@ function writeEvidence(result, opts) {
|
|
|
56730
57686
|
}
|
|
56731
57687
|
|
|
56732
57688
|
// src/html-emit.ts
|
|
56733
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
56734
|
-
import { join as
|
|
57689
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync27, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
|
|
57690
|
+
import { join as join44 } from "path";
|
|
56735
57691
|
|
|
56736
57692
|
// src/html.ts
|
|
56737
57693
|
var MARK4 = { C: "C", NC: "NC", NA: "\u2014", manual: "?" };
|
|
@@ -56888,6 +57844,13 @@ function renderHtmlDocument(doc) {
|
|
|
56888
57844
|
}
|
|
56889
57845
|
|
|
56890
57846
|
// src/html-report.ts
|
|
57847
|
+
function refusalBlocks(r) {
|
|
57848
|
+
if (!r) return [];
|
|
57849
|
+
return [
|
|
57850
|
+
{ kind: "note", tone: "warn", runs: [{ text: r.headline }] },
|
|
57851
|
+
{ kind: "list", items: r.reasons.map((text) => [{ text }]) }
|
|
57852
|
+
];
|
|
57853
|
+
}
|
|
56891
57854
|
var T4 = {
|
|
56892
57855
|
fr: {
|
|
56893
57856
|
reportTitle: "Rapport de conformit\xE9",
|
|
@@ -56937,8 +57900,7 @@ var T4 = {
|
|
|
56937
57900
|
openComposite: "Rapport complet, en un seul fichier (imprimable en PDF)",
|
|
56938
57901
|
openPages: "Rapport page par page",
|
|
56939
57902
|
noScreenshot: "Aucune capture d'\xE9cran pour cette page.",
|
|
56940
|
-
screenshotAlt: (n) => `Capture d'\xE9cran de la page ${n}
|
|
56941
|
-
notImaged: (n) => `${n} occurrence(s) n'ont pas pu \xEAtre illustr\xE9es.`
|
|
57903
|
+
screenshotAlt: (n) => `Capture d'\xE9cran de la page ${n}`
|
|
56942
57904
|
},
|
|
56943
57905
|
en: {
|
|
56944
57906
|
reportTitle: "Conformance report",
|
|
@@ -56988,8 +57950,7 @@ var T4 = {
|
|
|
56988
57950
|
openComposite: "Full report, in a single file (printable to PDF)",
|
|
56989
57951
|
openPages: "Page-by-page report",
|
|
56990
57952
|
noScreenshot: "No screenshot for this page.",
|
|
56991
|
-
screenshotAlt: (n) => `Screenshot of the ${n} page
|
|
56992
|
-
notImaged: (n) => `${n} occurrence(s) could not be illustrated.`
|
|
57953
|
+
screenshotAlt: (n) => `Screenshot of the ${n} page`
|
|
56993
57954
|
}
|
|
56994
57955
|
};
|
|
56995
57956
|
function ticks(text) {
|
|
@@ -57084,10 +58045,11 @@ function criterionBlocks(unit, standard, lang, level, crops) {
|
|
|
57084
58045
|
function resolveOccurrence(f, lang) {
|
|
57085
58046
|
return f.msg ? auditorUnitModel({ criteriaId: f.criteriaId, title: "", label: "", refs: [], severity: f.severity, findings: [f] }, CORE2, lang).messages[0] ?? f.message : f.message;
|
|
57086
58047
|
}
|
|
57087
|
-
function findingsBlocks(result, standard, lang, level, crops) {
|
|
58048
|
+
function findingsBlocks(result, standard, lang, level, crops, refusals) {
|
|
57088
58049
|
const t2 = T4[lang];
|
|
57089
58050
|
const { nc, advisory } = partitionUnits(prdUnits(result, standard, lang));
|
|
57090
58051
|
const out2 = [{ kind: "heading", level: 2, text: t2.ncTitle, id: "nc" }];
|
|
58052
|
+
out2.push(...refusalBlocks(refusals?.(null)));
|
|
57091
58053
|
if (!nc.length) out2.push({ kind: "para", runs: [{ text: t2.noNc }] });
|
|
57092
58054
|
for (const u of nc) out2.push(...criterionBlocks(u, standard, lang, level === 2 ? 3 : 4, crops));
|
|
57093
58055
|
if (advisory.length) {
|
|
@@ -57199,7 +58161,7 @@ function compositeDoc(result, opts = {}) {
|
|
|
57199
58161
|
const h = headline(result, standard, lang);
|
|
57200
58162
|
const blocks = [...synthesisBlocks(result, standard, lang)];
|
|
57201
58163
|
if (h.agentRuled) blocks.push({ kind: "note", tone: "warn", runs: ticks(agentMarkNote(lang)) });
|
|
57202
|
-
blocks.push(...findingsBlocks(result, standard, lang, 2, opts.crops));
|
|
58164
|
+
blocks.push(...findingsBlocks(result, standard, lang, 2, opts.crops, opts.refusals));
|
|
57203
58165
|
blocks.push(...scoreboardBlocks(result, standard, lang));
|
|
57204
58166
|
const scope = pagesOf(result);
|
|
57205
58167
|
if (scope.length) blocks.push(...crossGridBlocks(result, derivePages(result, scope), standard, lang));
|
|
@@ -57236,6 +58198,7 @@ function pageDoc(result, page, opts = {}) {
|
|
|
57236
58198
|
blocks.push(
|
|
57237
58199
|
opts.screenshot ? { kind: "figure", src: opts.screenshot, alt: t2.screenshotAlt(page.name) } : { kind: "para", runs: [{ text: t2.noScreenshot, em: true }] }
|
|
57238
58200
|
);
|
|
58201
|
+
blocks.push(...refusalBlocks(opts.refusals?.(page.id)));
|
|
57239
58202
|
blocks.push(...pageGridBlocks(result, page, standard, lang));
|
|
57240
58203
|
const view = { ...result, criteria: page.criteria, findings: page.findings };
|
|
57241
58204
|
const { nc, advisory } = partitionUnits(prdUnits(view, standard, lang));
|
|
@@ -57286,7 +58249,7 @@ function inlineSize(path) {
|
|
|
57286
58249
|
}
|
|
57287
58250
|
function dataUri(path) {
|
|
57288
58251
|
try {
|
|
57289
|
-
return `data:image/png;base64,${
|
|
58252
|
+
return `data:image/png;base64,${readFileSync27(path).toString("base64")}`;
|
|
57290
58253
|
} catch {
|
|
57291
58254
|
return void 0;
|
|
57292
58255
|
}
|
|
@@ -57300,6 +58263,10 @@ function cropLookup(m, lang, hrefOf) {
|
|
|
57300
58263
|
return href ? { href, alt: c2.alt[lang] } : void 0;
|
|
57301
58264
|
};
|
|
57302
58265
|
}
|
|
58266
|
+
function refusalLookup(m, lang) {
|
|
58267
|
+
if (!m) return void 0;
|
|
58268
|
+
return (pageId) => evidenceRefusals(m, pageId, lang);
|
|
58269
|
+
}
|
|
57303
58270
|
function pickRung(cropBytes, shotBytes, budget) {
|
|
57304
58271
|
const sum = (xs) => xs.reduce((a, b) => a + b, 0);
|
|
57305
58272
|
if (sum(cropBytes) + sum(shotBytes) <= budget) return { steps: [], cropsPerCriterion: Number.POSITIVE_INFINITY, shots: true };
|
|
@@ -57353,7 +58320,7 @@ function writeHtml(result, opts) {
|
|
|
57353
58320
|
return drawn;
|
|
57354
58321
|
};
|
|
57355
58322
|
const flat = opts.layout === "pages";
|
|
57356
|
-
const sheetDir = flat ? opts.outDir :
|
|
58323
|
+
const sheetDir = flat ? opts.outDir : join44(opts.outDir, "pages");
|
|
57357
58324
|
const up = flat ? "./" : "../";
|
|
57358
58325
|
const compositeName = `ultra11y-${stdKey}-${result.date}.html`;
|
|
57359
58326
|
const nav = flat ? [{ href: "./index.html", text: t2.pagesTitle }] : [
|
|
@@ -57361,17 +58328,24 @@ function writeHtml(result, opts) {
|
|
|
57361
58328
|
{ href: `./${compositeName}`, text: t2.docTitle },
|
|
57362
58329
|
...opts.pages ? [{ href: "./pages/index.html", text: t2.pagesTitle }] : []
|
|
57363
58330
|
];
|
|
58331
|
+
const refusals = refusalLookup(opts.evidence, lang);
|
|
57364
58332
|
let compositePath;
|
|
57365
58333
|
if (!flat) {
|
|
57366
|
-
const composite = compositeDoc(result, {
|
|
58334
|
+
const composite = compositeDoc(result, {
|
|
58335
|
+
standard,
|
|
58336
|
+
lang,
|
|
58337
|
+
crops: budgetedCrops,
|
|
58338
|
+
...refusals ? { refusals } : {},
|
|
58339
|
+
nav: nav.map((n) => ({ ...n, current: n.href === `./${compositeName}` }))
|
|
58340
|
+
});
|
|
57367
58341
|
if (notices.length) composite.blocks.unshift({ kind: "note", tone: "warn", runs: noticeRuns(notices) });
|
|
57368
|
-
compositePath =
|
|
58342
|
+
compositePath = join44(opts.outDir, compositeName);
|
|
57369
58343
|
writeFileSync16(compositePath, renderHtmlDocument(composite));
|
|
57370
58344
|
}
|
|
57371
58345
|
const fileCrops = cropLookup(opts.evidence, lang, (_p, href) => href.replace(/^\.\//, up));
|
|
57372
58346
|
const indexNav = nav.map((n) => ({ ...n, current: n.href === "./index.html" }));
|
|
57373
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") });
|
|
57374
|
-
const indexPath =
|
|
58348
|
+
const indexPath = join44(opts.outDir, "index.html");
|
|
57375
58349
|
writeFileSync16(indexPath, renderHtmlDocument(index));
|
|
57376
58350
|
const sheets = [];
|
|
57377
58351
|
if (opts.pages) {
|
|
@@ -57384,7 +58358,7 @@ function writeHtml(result, opts) {
|
|
|
57384
58358
|
];
|
|
57385
58359
|
if (!flat) {
|
|
57386
58360
|
writeFileSync16(
|
|
57387
|
-
|
|
58361
|
+
join44(sheetDir, "index.html"),
|
|
57388
58362
|
renderHtmlDocument(
|
|
57389
58363
|
pagesIndexDoc(result, {
|
|
57390
58364
|
standard,
|
|
@@ -57394,19 +58368,20 @@ function writeHtml(result, opts) {
|
|
|
57394
58368
|
})
|
|
57395
58369
|
)
|
|
57396
58370
|
);
|
|
57397
|
-
sheets.push(
|
|
58371
|
+
sheets.push(join44(sheetDir, "index.html"));
|
|
57398
58372
|
}
|
|
57399
58373
|
for (const p of derived) {
|
|
57400
58374
|
const doc = pageDoc(result, p, {
|
|
57401
58375
|
standard,
|
|
57402
58376
|
lang,
|
|
57403
58377
|
crops: fileCrops,
|
|
58378
|
+
...refusals ? { refusals } : {},
|
|
57404
58379
|
nav: sheetNav,
|
|
57405
58380
|
// Where `cmdPages`' screenshot copier already put the capture: `assets/<id>.png`,
|
|
57406
58381
|
// beside the entry point, seen from wherever this sheet sits.
|
|
57407
58382
|
...opts.screenshots?.has(p.id) ? { screenshot: `${up}assets/${p.id}.png` } : {}
|
|
57408
58383
|
});
|
|
57409
|
-
const path =
|
|
58384
|
+
const path = join44(sheetDir, `page-${p.id}.html`);
|
|
57410
58385
|
writeFileSync16(path, renderHtmlDocument(doc));
|
|
57411
58386
|
sheets.push(path);
|
|
57412
58387
|
}
|
|
@@ -57427,8 +58402,8 @@ function noticeRuns(notices) {
|
|
|
57427
58402
|
|
|
57428
58403
|
// src/dev.ts
|
|
57429
58404
|
import { createServer } from "http";
|
|
57430
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
57431
|
-
import { dirname as dirname12, join as
|
|
58405
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
|
|
58406
|
+
import { dirname as dirname12, join as join45 } from "path";
|
|
57432
58407
|
var DEV_DEFAULT_PORT = 4111;
|
|
57433
58408
|
function criterionLabel3(f, standard) {
|
|
57434
58409
|
if (isCore(standard)) return `WCAG ${f.criteriaId}`;
|
|
@@ -57650,13 +58625,13 @@ function auditCollected(root, payload) {
|
|
|
57650
58625
|
...payload.css ? { css: payload.css } : {},
|
|
57651
58626
|
...shot ? { screenshotBase64: shot } : {}
|
|
57652
58627
|
});
|
|
57653
|
-
return { ok: true, result: runAudit({ inputs: [
|
|
58628
|
+
return { ok: true, result: runAudit({ inputs: [join45(dir, "dom.html")] }) };
|
|
57654
58629
|
}
|
|
57655
58630
|
function projectPages(root) {
|
|
57656
58631
|
const snaps = readSnapshots(root);
|
|
57657
58632
|
if (!snaps.length) return { result: null, pages: [] };
|
|
57658
58633
|
const scope = pageScopesFrom(snaps);
|
|
57659
|
-
const result = runAudit({ inputs: [
|
|
58634
|
+
const result = runAudit({ inputs: [join45(root, ".ultra11y/pages")] });
|
|
57660
58635
|
result.scope.pages = scope;
|
|
57661
58636
|
attributePages(result, scope);
|
|
57662
58637
|
foldRecordedAdjudication(root, result);
|
|
@@ -57670,7 +58645,7 @@ function judgePages(result) {
|
|
|
57670
58645
|
function foldRecordedAdjudication(root, fresh) {
|
|
57671
58646
|
let prior;
|
|
57672
58647
|
try {
|
|
57673
|
-
prior = JSON.parse(
|
|
58648
|
+
prior = JSON.parse(readFileSync28(join45(root, "audits", "audit-latest.json"), "utf8"));
|
|
57674
58649
|
} catch {
|
|
57675
58650
|
return;
|
|
57676
58651
|
}
|
|
@@ -57785,7 +58760,7 @@ function startDevServer(opts) {
|
|
|
57785
58760
|
});
|
|
57786
58761
|
let auditPath;
|
|
57787
58762
|
if (applied.ok) {
|
|
57788
|
-
auditPath =
|
|
58763
|
+
auditPath = join45(opts.root, "audits", "audit-latest.json");
|
|
57789
58764
|
mkdirSync15(dirname12(auditPath), { recursive: true });
|
|
57790
58765
|
writeFileSync17(auditPath, `${JSON.stringify(applied.audit, null, 2)}
|
|
57791
58766
|
`);
|
|
@@ -57814,10 +58789,10 @@ function startDevServer(opts) {
|
|
|
57814
58789
|
}
|
|
57815
58790
|
res.writeHead(404, { "content-type": "text/plain" }).end("not found");
|
|
57816
58791
|
});
|
|
57817
|
-
return new Promise((
|
|
58792
|
+
return new Promise((resolve15, reject) => {
|
|
57818
58793
|
server.once("error", reject);
|
|
57819
58794
|
server.listen(opts.port, "127.0.0.1", () => {
|
|
57820
|
-
|
|
58795
|
+
resolve15({
|
|
57821
58796
|
port: server.address().port,
|
|
57822
58797
|
close: () => new Promise((r) => server.close(() => r()))
|
|
57823
58798
|
});
|
|
@@ -58101,11 +59076,11 @@ function e2eSetupPlan(runners, paths, lang = "en") {
|
|
|
58101
59076
|
}
|
|
58102
59077
|
|
|
58103
59078
|
// src/orchestrate.ts
|
|
58104
|
-
import { existsSync as
|
|
58105
|
-
import { join as
|
|
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";
|
|
58106
59081
|
|
|
58107
59082
|
// src/orchestrate-templates.ts
|
|
58108
|
-
import { join as
|
|
59083
|
+
import { join as join46 } from "path";
|
|
58109
59084
|
var ONE_WRITER_FOOTER = `
|
|
58110
59085
|
## Return, don't write
|
|
58111
59086
|
|
|
@@ -58177,7 +59152,7 @@ var PHASE_SPECS = {
|
|
|
58177
59152
|
title: "Adjudicate",
|
|
58178
59153
|
schema: ADJUDICATE_SCHEMA,
|
|
58179
59154
|
description: (n) => `Adjudicate the ${n} residual judgment criterion(ia) of an ultra11y audit (fan-out, fail-closed fold)`,
|
|
58180
|
-
applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${
|
|
59155
|
+
applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${join46(run2, "audit-latest.json")} --out ${run2}`
|
|
58181
59156
|
},
|
|
58182
59157
|
"verify-report": {
|
|
58183
59158
|
role: "refuter",
|
|
@@ -58199,7 +59174,7 @@ function toBatches(ids, batchSize) {
|
|
|
58199
59174
|
}
|
|
58200
59175
|
function phaseWorkflowScript(ph, runAbs, engineAbs, batchSize) {
|
|
58201
59176
|
const spec = phaseSpec(ph.name);
|
|
58202
|
-
const scriptPath =
|
|
59177
|
+
const scriptPath = join46(runAbs, "orchestration", `${ph.name}.workflow.mjs`);
|
|
58203
59178
|
const meta2 = { name: `ultra11y-${ph.name}`, description: spec.description(ph.items), phases: [{ title: spec.title }] };
|
|
58204
59179
|
return [
|
|
58205
59180
|
`export const meta = ${JSON.stringify(meta2)}`,
|
|
@@ -58243,7 +59218,7 @@ function agentContracts(runAbs, engineAbs) {
|
|
|
58243
59218
|
|
|
58244
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.
|
|
58245
59220
|
|
|
58246
|
-
Worklist: \`${
|
|
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>\`).
|
|
58247
59222
|
|
|
58248
59223
|
For EACH of your criteria:
|
|
58249
59224
|
|
|
@@ -58261,7 +59236,7 @@ ${footer}`,
|
|
|
58261
59236
|
|
|
58262
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.
|
|
58263
59238
|
|
|
58264
|
-
Worklist: \`${
|
|
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>\`).
|
|
58265
59240
|
|
|
58266
59241
|
For EACH of your entries:
|
|
58267
59242
|
|
|
@@ -58297,14 +59272,14 @@ ${status}
|
|
|
58297
59272
|
|
|
58298
59273
|
## The loop (play every role yourself, one item at a time)
|
|
58299
59274
|
|
|
58300
|
-
1. **Audit** (if not done): \`${engine} audit "<globs>" --graph --out ${runAbs}\` \u2192 \`${
|
|
58301
|
-
2. **Adjudicate the residual criteria** \u2014 \`${engine} verify --manual --in ${
|
|
58302
|
-
3. **Report**: \`${engine} report --in ${
|
|
58303
|
-
4. **Verify the report's claims** \u2014 \`${engine} verify --report <the report .md> --out ${runAbs}\` writes \`${
|
|
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>\`.
|
|
58304
59279
|
5. **Gate**: \`${engine} check --report <the report .md> --semantic\` must exit 0 before presenting anything.
|
|
58305
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.
|
|
58306
59281
|
|
|
58307
|
-
With subagents available, prefer the emitted workflows instead: \`orchestrate --run ${runAbs} --phase <p>\` then \`Workflow({ scriptPath: "${
|
|
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.
|
|
58308
59283
|
`;
|
|
58309
59284
|
}
|
|
58310
59285
|
|
|
@@ -58313,13 +59288,13 @@ var PHASES = ["adjudicate", "verify-report"];
|
|
|
58313
59288
|
var SMALL_WORKLIST = 3;
|
|
58314
59289
|
var BATCH_SIZE2 = 8;
|
|
58315
59290
|
function listPhases(runDir, engineAbs) {
|
|
58316
|
-
const run2 =
|
|
58317
|
-
const adjPath =
|
|
59291
|
+
const run2 = resolve12(runDir);
|
|
59292
|
+
const adjPath = join47(run2, "ADJUDICATE.todo.json");
|
|
58318
59293
|
let adjIds = [];
|
|
58319
59294
|
let adjReady = false;
|
|
58320
|
-
if (
|
|
59295
|
+
if (existsSync31(adjPath)) {
|
|
58321
59296
|
try {
|
|
58322
|
-
const f = JSON.parse(
|
|
59297
|
+
const f = JSON.parse(readFileSync29(adjPath, "utf8"));
|
|
58323
59298
|
if (f && f.kind === "adjudication" && Array.isArray(f.items)) {
|
|
58324
59299
|
adjReady = true;
|
|
58325
59300
|
adjIds = f.items.map((i2) => i2.criteriaId);
|
|
@@ -58327,12 +59302,12 @@ function listPhases(runDir, engineAbs) {
|
|
|
58327
59302
|
} catch {
|
|
58328
59303
|
}
|
|
58329
59304
|
}
|
|
58330
|
-
const verPath =
|
|
59305
|
+
const verPath = join47(run2, "VERIFY.todo.json");
|
|
58331
59306
|
let verIds = [];
|
|
58332
59307
|
let verReady = false;
|
|
58333
|
-
if (
|
|
59308
|
+
if (existsSync31(verPath)) {
|
|
58334
59309
|
try {
|
|
58335
|
-
const items = JSON.parse(
|
|
59310
|
+
const items = JSON.parse(readFileSync29(verPath, "utf8"));
|
|
58336
59311
|
if (Array.isArray(items)) {
|
|
58337
59312
|
verReady = true;
|
|
58338
59313
|
verIds = items.map((i2) => String(i2.n));
|
|
@@ -58347,7 +59322,7 @@ function listPhases(runDir, engineAbs) {
|
|
|
58347
59322
|
worklist: adjPath,
|
|
58348
59323
|
items: adjIds.length,
|
|
58349
59324
|
ids: adjIds,
|
|
58350
|
-
prerequisite: `node ${engineAbs} verify --manual --in ${
|
|
59325
|
+
prerequisite: `node ${engineAbs} verify --manual --in ${join47(run2, "audit-latest.json")} --out ${run2}`
|
|
58351
59326
|
},
|
|
58352
59327
|
{
|
|
58353
59328
|
name: "verify-report",
|
|
@@ -58360,8 +59335,8 @@ function listPhases(runDir, engineAbs) {
|
|
|
58360
59335
|
];
|
|
58361
59336
|
}
|
|
58362
59337
|
function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
58363
|
-
const run2 =
|
|
58364
|
-
if (!
|
|
59338
|
+
const run2 = resolve12(runDir);
|
|
59339
|
+
if (!existsSync31(run2)) {
|
|
58365
59340
|
return { exitCode: 2, written: [], notices: [], errors: [`run dir not found: ${run2}`], phases: [] };
|
|
58366
59341
|
}
|
|
58367
59342
|
const phases = listPhases(run2, engineAbs);
|
|
@@ -58388,22 +59363,22 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58388
59363
|
}
|
|
58389
59364
|
selected = [ph];
|
|
58390
59365
|
}
|
|
58391
|
-
const orchDir =
|
|
58392
|
-
const agentsDir =
|
|
58393
|
-
mkdirSync16(
|
|
59366
|
+
const orchDir = join47(run2, "orchestration");
|
|
59367
|
+
const agentsDir = join47(orchDir, "agents");
|
|
59368
|
+
mkdirSync16(join47(orchDir, "out"), { recursive: true });
|
|
58394
59369
|
mkdirSync16(agentsDir, { recursive: true });
|
|
58395
59370
|
const written = [];
|
|
58396
59371
|
const notices = [];
|
|
58397
59372
|
for (const [name2, content] of Object.entries(agentContracts(run2, engineAbs))) {
|
|
58398
|
-
const p =
|
|
59373
|
+
const p = join47(agentsDir, `${name2}.md`);
|
|
58399
59374
|
writeFileSync18(p, content);
|
|
58400
59375
|
written.push(p);
|
|
58401
59376
|
}
|
|
58402
59377
|
const emitted = new Set(opts.eco ? [] : selected.filter((p) => p.items > 0).map((p) => p.name));
|
|
58403
59378
|
for (const ph of phases) {
|
|
58404
59379
|
if (emitted.has(ph.name)) continue;
|
|
58405
|
-
const stale =
|
|
58406
|
-
if (
|
|
59380
|
+
const stale = join47(orchDir, `${ph.name}.workflow.mjs`);
|
|
59381
|
+
if (existsSync31(stale)) {
|
|
58407
59382
|
rmSync7(stale, { force: true });
|
|
58408
59383
|
notices.push(`phase "${ph.name}": stale workflow removed \u2014 its worklist ${ph.ready ? "is now empty" : "no longer exists"}.`);
|
|
58409
59384
|
}
|
|
@@ -58417,12 +59392,12 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58417
59392
|
if (ph.items <= SMALL_WORKLIST) {
|
|
58418
59393
|
notices.push(`phase "${ph.name}": only ${ph.items} item(s) \u2014 the sequential --eco path is equivalent and cheaper.`);
|
|
58419
59394
|
}
|
|
58420
|
-
const p =
|
|
59395
|
+
const p = join47(orchDir, `${ph.name}.workflow.mjs`);
|
|
58421
59396
|
writeFileSync18(p, phaseWorkflowScript(ph, run2, engineAbs, BATCH_SIZE2));
|
|
58422
59397
|
written.push(p);
|
|
58423
59398
|
}
|
|
58424
59399
|
}
|
|
58425
|
-
const rb =
|
|
59400
|
+
const rb = join47(orchDir, "RUNBOOK.md");
|
|
58426
59401
|
writeFileSync18(rb, runbookMd(phases, run2, engineAbs));
|
|
58427
59402
|
written.push(rb);
|
|
58428
59403
|
return { exitCode: 0, written, notices, errors: [], phases };
|
|
@@ -58432,8 +59407,8 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58432
59407
|
import { createInterface as createInterface2 } from "readline";
|
|
58433
59408
|
|
|
58434
59409
|
// src/mcp/handlers.ts
|
|
58435
|
-
import { existsSync as
|
|
58436
|
-
import { isAbsolute as isAbsolute3, join as
|
|
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";
|
|
58437
59412
|
|
|
58438
59413
|
// src/project-lock.ts
|
|
58439
59414
|
var chains = /* @__PURE__ */ new Map();
|
|
@@ -58480,8 +59455,8 @@ function positive(v, key) {
|
|
|
58480
59455
|
function requiredCwd(args2, defaults) {
|
|
58481
59456
|
const cwd = str2(args2.cwd) ?? defaults.defaultCwd;
|
|
58482
59457
|
if (!cwd) throw new ToolError("`cwd` is required: an absolute path to the project root.");
|
|
58483
|
-
const abs =
|
|
58484
|
-
if (!
|
|
59458
|
+
const abs = resolve13(cwd);
|
|
59459
|
+
if (!existsSync32(abs)) throw new ToolError(`project root not found: ${abs}`);
|
|
58485
59460
|
if (!statSync12(abs).isDirectory()) throw new ToolError(`\`cwd\` is not a directory: ${abs}`);
|
|
58486
59461
|
return abs;
|
|
58487
59462
|
}
|
|
@@ -58547,7 +59522,7 @@ function audit(args2, cwd) {
|
|
|
58547
59522
|
process.chdir(cwd);
|
|
58548
59523
|
const inputs = strArray2(args2.globs) ?? DEFAULT_GLOBS;
|
|
58549
59524
|
const scopedToDiff = bool(args2.changed) || bool(args2.staged) || str2(args2.since) !== void 0;
|
|
58550
|
-
const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff &&
|
|
59525
|
+
const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff && existsSync32(d) && !inputs.includes(d));
|
|
58551
59526
|
const result = runAudit({
|
|
58552
59527
|
inputs: [...inputs, ...extra],
|
|
58553
59528
|
include: strArray2(args2.include),
|
|
@@ -58666,13 +59641,13 @@ function handlePackCheck(args2, cwd) {
|
|
|
58666
59641
|
return { cwd, ...res };
|
|
58667
59642
|
}
|
|
58668
59643
|
function handleSampleCheck(cwd, standard) {
|
|
58669
|
-
const file =
|
|
58670
|
-
if (!
|
|
59644
|
+
const file = join48(cwd, ".ultra11yrc.json");
|
|
59645
|
+
if (!existsSync32(file)) {
|
|
58671
59646
|
throw new ToolError(`no .ultra11yrc.json at ${cwd} \u2014 a page sample must be declared before it can be linted.`);
|
|
58672
59647
|
}
|
|
58673
59648
|
let raw;
|
|
58674
59649
|
try {
|
|
58675
|
-
raw = JSON.parse(
|
|
59650
|
+
raw = JSON.parse(readFileSync30(file, "utf8"));
|
|
58676
59651
|
} catch (e) {
|
|
58677
59652
|
throw new ToolError(`.ultra11yrc.json is not valid JSON: ${e.message}`);
|
|
58678
59653
|
}
|
|
@@ -58763,10 +59738,10 @@ function handleInit(args2, cwd) {
|
|
|
58763
59738
|
throw new ToolError(`\`fail_on\` must be one of: error, warning, notice (got "${failOnRaw}")`);
|
|
58764
59739
|
}
|
|
58765
59740
|
const failOn = failOnRaw;
|
|
58766
|
-
const enginePath =
|
|
59741
|
+
const enginePath = resolve13("scripts/ultra11y.mjs");
|
|
58767
59742
|
const written = [];
|
|
58768
59743
|
if (bool(args2.hook)) written.push(writeHook(cwd, enginePath, failOn));
|
|
58769
|
-
if (bool(args2.ci)) written.push(
|
|
59744
|
+
if (bool(args2.ci)) written.push(join48(cwd, ".github/workflows/a11y.yml"));
|
|
58770
59745
|
if (!written.length) {
|
|
58771
59746
|
throw new ToolError("nothing to install \u2014 pass hook:true and/or ci:true.");
|
|
58772
59747
|
}
|
|
@@ -58786,8 +59761,8 @@ function reportText(args2, tool) {
|
|
|
58786
59761
|
if (inline) return inline;
|
|
58787
59762
|
if (!file) throw new ToolError(`\`report_text\` is required \u2014 the report markdown for ultra11y_${tool} to work on.`);
|
|
58788
59763
|
if (!isAbsolute3(file)) throw new ToolError("`report_file` must be an absolute path.");
|
|
58789
|
-
if (!
|
|
58790
|
-
return
|
|
59764
|
+
if (!existsSync32(file)) throw new ToolError(`report file not found: ${file}`);
|
|
59765
|
+
return readFileSync30(file, "utf8");
|
|
58791
59766
|
}
|
|
58792
59767
|
function handleCriteria(args2) {
|
|
58793
59768
|
const lang = langOf(args2);
|
|
@@ -58802,7 +59777,7 @@ function handleCriteria(args2) {
|
|
|
58802
59777
|
function handleRead(args2, cwd) {
|
|
58803
59778
|
const raw = str2(args2.path);
|
|
58804
59779
|
if (!raw) throw new ToolError("`path` is required \u2014 relative to the project root, or an absolute path inside it.");
|
|
58805
|
-
const target = isAbsolute3(raw) ? raw :
|
|
59780
|
+
const target = isAbsolute3(raw) ? raw : join48(cwd, raw);
|
|
58806
59781
|
let real;
|
|
58807
59782
|
try {
|
|
58808
59783
|
real = realpathSync4(target);
|
|
@@ -58816,7 +59791,7 @@ function handleRead(args2, cwd) {
|
|
|
58816
59791
|
const st = statSync12(real);
|
|
58817
59792
|
if (!st.isFile()) throw new ToolError(`not a file: ${raw}`);
|
|
58818
59793
|
if (st.size > MAX_READ_BYTES) throw new ToolError(`file is too large to read (${st.size} bytes): ${raw}`);
|
|
58819
|
-
const lines =
|
|
59794
|
+
const lines = readFileSync30(real, "utf8").split("\n");
|
|
58820
59795
|
const total = lines.length;
|
|
58821
59796
|
const start2 = Math.max(1, Math.floor(num2(args2.start_line) ?? 1));
|
|
58822
59797
|
if (start2 > total) throw new ToolError(`start_line ${start2} is past the end of the file (${total} lines).`);
|
|
@@ -59362,25 +60337,25 @@ function str3(v) {
|
|
|
59362
60337
|
var DECLARED = new Set([...TOOLS2, ...WRITE_TOOLS].map((t2) => t2.name));
|
|
59363
60338
|
|
|
59364
60339
|
// src/mcp/resources.ts
|
|
59365
|
-
import { existsSync as
|
|
59366
|
-
import { basename as basename3, dirname as dirname13, join as
|
|
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";
|
|
59367
60342
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
59368
60343
|
var SKILL_NAME = "ultra11y";
|
|
59369
60344
|
var URI_SCHEME = "skill://";
|
|
59370
60345
|
function resolveSkillRoot(moduleDir) {
|
|
59371
60346
|
const here = moduleDir ?? dirname13(fileURLToPath4(import.meta.url));
|
|
59372
|
-
const candidates2 = [
|
|
59373
|
-
return candidates2.find((dir) =>
|
|
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")));
|
|
59374
60349
|
}
|
|
59375
60350
|
function listResources(moduleDir) {
|
|
59376
60351
|
const root = resolveSkillRoot(moduleDir);
|
|
59377
60352
|
if (!root) return [];
|
|
59378
60353
|
const out2 = [describe(root, "SKILL.md", `${SKILL_NAME}: the skill`)];
|
|
59379
|
-
const refDir =
|
|
59380
|
-
if (!
|
|
60354
|
+
const refDir = join49(root, "references");
|
|
60355
|
+
if (!existsSync33(refDir)) return out2;
|
|
59381
60356
|
for (const file of readdirSync6(refDir).sort()) {
|
|
59382
60357
|
if (!file.endsWith(".md")) continue;
|
|
59383
|
-
out2.push(describe(root,
|
|
60358
|
+
out2.push(describe(root, join49("references", file), `${SKILL_NAME} reference: ${basename3(file, ".md")}`));
|
|
59384
60359
|
}
|
|
59385
60360
|
return out2;
|
|
59386
60361
|
}
|
|
@@ -59392,7 +60367,7 @@ function readResource(uri, moduleDir) {
|
|
|
59392
60367
|
if (!root) throw new ResourceError("no skill payload found next to this build \u2014 nothing to read");
|
|
59393
60368
|
const rel2 = uri.slice(URI_SCHEME.length);
|
|
59394
60369
|
if (!rel2) throw new ResourceError("empty resource path");
|
|
59395
|
-
const target =
|
|
60370
|
+
const target = resolve14(root, rel2);
|
|
59396
60371
|
const rootReal = realpathSync5(root);
|
|
59397
60372
|
let targetReal;
|
|
59398
60373
|
try {
|
|
@@ -59404,7 +60379,7 @@ function readResource(uri, moduleDir) {
|
|
|
59404
60379
|
throw new ResourceError(`resource path escapes the skill root: ${uri}`);
|
|
59405
60380
|
}
|
|
59406
60381
|
if (!statSync13(targetReal).isFile()) throw new ResourceError(`not a file: ${uri}`);
|
|
59407
|
-
return { uri, mimeType: "text/markdown", text:
|
|
60382
|
+
return { uri, mimeType: "text/markdown", text: readFileSync31(targetReal, "utf8") };
|
|
59408
60383
|
}
|
|
59409
60384
|
var ResourceError = class extends Error {
|
|
59410
60385
|
};
|
|
@@ -59415,14 +60390,14 @@ function describe(root, rel2, fallbackTitle) {
|
|
|
59415
60390
|
title: fallbackTitle,
|
|
59416
60391
|
mimeType: "text/markdown"
|
|
59417
60392
|
};
|
|
59418
|
-
const summary = firstProse(
|
|
60393
|
+
const summary = firstProse(join49(root, rel2));
|
|
59419
60394
|
if (summary) decl.description = summary;
|
|
59420
60395
|
return decl;
|
|
59421
60396
|
}
|
|
59422
60397
|
function firstProse(file) {
|
|
59423
60398
|
let text;
|
|
59424
60399
|
try {
|
|
59425
|
-
text =
|
|
60400
|
+
text = readFileSync31(file, "utf8");
|
|
59426
60401
|
} catch {
|
|
59427
60402
|
return void 0;
|
|
59428
60403
|
}
|
|
@@ -59673,14 +60648,14 @@ function startHttpServer(opts = {}) {
|
|
|
59673
60648
|
server.requestTimeout = 0;
|
|
59674
60649
|
server.headersTimeout = 6e4;
|
|
59675
60650
|
server.keepAliveTimeout = 12e4;
|
|
59676
|
-
return new Promise((
|
|
60651
|
+
return new Promise((resolve15, reject) => {
|
|
59677
60652
|
server.once("error", reject);
|
|
59678
60653
|
server.listen(opts.port ?? 0, bind, () => {
|
|
59679
60654
|
server.removeListener("error", reject);
|
|
59680
60655
|
const addr2 = server.address();
|
|
59681
60656
|
const port = typeof addr2 === "object" && addr2 ? addr2.port : opts.port ?? 0;
|
|
59682
60657
|
const host = bind.includes(":") ? `[${bind}]` : bind;
|
|
59683
|
-
|
|
60658
|
+
resolve15({
|
|
59684
60659
|
server,
|
|
59685
60660
|
port,
|
|
59686
60661
|
url: `http://${host}:${port}${MCP_PATH}`,
|
|
@@ -59789,7 +60764,7 @@ function sendJson(res, status, body2, origin, extra = {}) {
|
|
|
59789
60764
|
}
|
|
59790
60765
|
var DRAIN_LIMIT = MAX_BODY_BYTES * 8;
|
|
59791
60766
|
function readBody2(req) {
|
|
59792
|
-
return new Promise((
|
|
60767
|
+
return new Promise((resolve15, reject) => {
|
|
59793
60768
|
const chunks = [];
|
|
59794
60769
|
let size = 0;
|
|
59795
60770
|
let over = false;
|
|
@@ -59813,7 +60788,7 @@ function readBody2(req) {
|
|
|
59813
60788
|
});
|
|
59814
60789
|
req.on("end", () => {
|
|
59815
60790
|
if (over) reject(new Error("too large"));
|
|
59816
|
-
else
|
|
60791
|
+
else resolve15(Buffer.concat(chunks).toString("utf8"));
|
|
59817
60792
|
});
|
|
59818
60793
|
req.on("error", reject);
|
|
59819
60794
|
req.on("aborted", () => reject(new Error("client aborted the request")));
|
|
@@ -59835,7 +60810,8 @@ Usage:
|
|
|
59835
60810
|
ultra11y audit [--format sarif|github] (CI: SARIF for code scanning, or inline annotations + job summary)
|
|
59836
60811
|
ultra11y audit --in <audit.json> [--fail-on blocking|major|minor] [--format sarif|github] (re-gate an audit already computed \u2014 e.g. one carrying adjudicated verdicts \u2014 without a second detection pass)
|
|
59837
60812
|
ultra11y report --in <audit.json> [--out <dir>] [--standard <pack>] [--format sarif|github] [--lang auto|en|fr]
|
|
59838
|
-
ultra11y report --in <audit.json>
|
|
60813
|
+
ultra11y report --in <audit.json> [--evidence [--evidence-max <n>]] [--out <dir>] (the Markdown report, illustrated with annotated crops)
|
|
60814
|
+
ultra11y report --in <audit.json> --html [--evidence] [--inline-budget <bytes>] [--out <dir>] (index.html + a printable single file)
|
|
59839
60815
|
ultra11y prd --in <audit.json> [--out <dir>] [--split criterion] [--format audit|doc|remediation] [--no-technical] [--standard <pack>] [--lang auto|en|fr]
|
|
59840
60816
|
ultra11y tickets --in <audit.json> [--provider auto|github|gitlab|jira] [--grain criterion|page|page-criterion|single|file] [--transport auto|cli|rest]
|
|
59841
60817
|
ultra11y tickets [--out <dir>] [--max-tickets <n>] [--dry-run] [--json] [--standard <pack>] [--format audit|remediation] [--lang auto|en|fr]
|
|
@@ -59860,7 +60836,7 @@ Usage:
|
|
|
59860
60836
|
ultra11y snapshot list [--root <dir>] [--json]
|
|
59861
60837
|
ultra11y pages --in <audit.json> [--standard <pack>] [--json] [--lang auto|en|fr] (the per-page criterion grid)
|
|
59862
60838
|
ultra11y pages --in <audit.json> --format report [--split page] [--out <dir>] (the per-page report, with screenshots)
|
|
59863
|
-
ultra11y pages --in <audit.json> --format report --out <dir> [--evidence] [--html] (annotated crops of each non-conformity, and the HTML site)
|
|
60839
|
+
ultra11y pages --in <audit.json> --format report --out <dir> [--evidence [--evidence-max <n>]] [--html] (annotated crops of each non-conformity, and the HTML site)
|
|
59864
60840
|
ultra11y pages discover --sitemap <url> | --crawl <url> | --from-snapshots [--depth <n>] [--max <n>] [--write] [--json] (build the page sample)
|
|
59865
60841
|
ultra11y pages --in <audit.json> --standard <pack> --diff <external.json> (hold the grid against an audit someone else performed)
|
|
59866
60842
|
ultra11y import --from file <report.json> | --from ara <id> [--source <adapter>] [--out <dir>] [--json]
|
|
@@ -60259,6 +61235,8 @@ var VALUE_FLAGS2 = /* @__PURE__ */ new Set([
|
|
|
60259
61235
|
"format",
|
|
60260
61236
|
// Bytes of image data the single-file HTML report may carry inline (src/html-emit.ts).
|
|
60261
61237
|
"inline-budget",
|
|
61238
|
+
// Hard cap on the crops one run writes (src/evidence.ts `DEFAULT_CAPS.total`).
|
|
61239
|
+
"evidence-max",
|
|
60262
61240
|
"guidance",
|
|
60263
61241
|
"runtime",
|
|
60264
61242
|
"cwd",
|
|
@@ -60541,9 +61519,9 @@ async function cmdAudit(p) {
|
|
|
60541
61519
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
60542
61520
|
const capturesDir = capturesFlag ?? CAPTURES_DIR;
|
|
60543
61521
|
const scopedToDiff = p.flags.changed === true || p.flags.staged === true || since !== void 0;
|
|
60544
|
-
const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 ||
|
|
61522
|
+
const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 || existsSync34(capturesDir));
|
|
60545
61523
|
const useCaptures = capturesWanted && !scopedToDiff && !inputs.includes(capturesDir);
|
|
60546
|
-
const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") &&
|
|
61524
|
+
const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && existsSync34(PAGES_DIR);
|
|
60547
61525
|
const usePages = pagesWanted && !scopedToDiff && !inputs.includes(PAGES_DIR);
|
|
60548
61526
|
const pagesInScope = pagesWanted && !scopedToDiff;
|
|
60549
61527
|
const auditInputs = [...inputs, ...useCaptures ? [capturesDir] : [], ...usePages ? [PAGES_DIR] : []];
|
|
@@ -60585,7 +61563,7 @@ async function cmdAudit(p) {
|
|
|
60585
61563
|
if (typeof p.flags.out === "string") {
|
|
60586
61564
|
const out2 = p.flags.out;
|
|
60587
61565
|
const asFile = out2.toLowerCase().endsWith(".json");
|
|
60588
|
-
const target = asFile ? out2 :
|
|
61566
|
+
const target = asFile ? out2 : join50(out2, "audit-latest.json");
|
|
60589
61567
|
try {
|
|
60590
61568
|
mkdirSync17(asFile ? dirname14(out2) : out2, { recursive: true });
|
|
60591
61569
|
writeFileSync19(target, JSON.stringify(result, null, 2) + "\n");
|
|
@@ -60607,7 +61585,7 @@ async function cmdAudit(p) {
|
|
|
60607
61585
|
const baselineFlag = p.flags.baseline;
|
|
60608
61586
|
if (typeof baselineFlag === "string" && baselineFlag) {
|
|
60609
61587
|
let baseline = null;
|
|
60610
|
-
if (
|
|
61588
|
+
if (existsSync34(baselineFlag)) {
|
|
60611
61589
|
try {
|
|
60612
61590
|
const parsed = JSON.parse(readText(baselineFlag));
|
|
60613
61591
|
if (isCurrentAudit(parsed)) baseline = parsed;
|
|
@@ -60661,11 +61639,11 @@ async function cmdDev(p) {
|
|
|
60661
61639
|
return 2;
|
|
60662
61640
|
}
|
|
60663
61641
|
if (p.flags.next === true) {
|
|
60664
|
-
const dir =
|
|
61642
|
+
const dir = join50(root, ".ultra11y", "next");
|
|
60665
61643
|
const rel2 = ".ultra11y/next/overlay.jsx";
|
|
60666
61644
|
try {
|
|
60667
61645
|
mkdirSync17(dir, { recursive: true });
|
|
60668
|
-
writeFileSync19(
|
|
61646
|
+
writeFileSync19(join50(dir, "overlay.jsx"), nextOverlayComponent(port));
|
|
60669
61647
|
} catch (e) {
|
|
60670
61648
|
console.error(`ultra11y dev: could not write ${rel2}: ${e instanceof Error ? e.message : String(e)}`);
|
|
60671
61649
|
return 1;
|
|
@@ -60696,9 +61674,9 @@ async function cmdDev(p) {
|
|
|
60696
61674
|
}
|
|
60697
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}`);
|
|
60698
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.");
|
|
60699
|
-
await new Promise((
|
|
61677
|
+
await new Promise((resolve15) => {
|
|
60700
61678
|
const stop2 = () => {
|
|
60701
|
-
void server.close().then(
|
|
61679
|
+
void server.close().then(resolve15);
|
|
60702
61680
|
};
|
|
60703
61681
|
process.once("SIGINT", stop2);
|
|
60704
61682
|
process.once("SIGTERM", stop2);
|
|
@@ -60714,7 +61692,7 @@ async function cmdPagesDiscover(p) {
|
|
|
60714
61692
|
const snaps = sampleFromSnapshots(readSnapshots(root));
|
|
60715
61693
|
if (!snaps.length) {
|
|
60716
61694
|
console.error(
|
|
60717
|
-
lang === "fr" ? `ultra11y pages discover : aucun instantan\xE9 sous ${
|
|
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\`.`
|
|
60718
61696
|
);
|
|
60719
61697
|
return 1;
|
|
60720
61698
|
}
|
|
@@ -60794,9 +61772,9 @@ ${found} page(s) discovered, ${merged.added.length} new. Nothing was written \u2
|
|
|
60794
61772
|
}
|
|
60795
61773
|
return 0;
|
|
60796
61774
|
}
|
|
60797
|
-
const file =
|
|
61775
|
+
const file = join50(process.cwd(), ".ultra11yrc.json");
|
|
60798
61776
|
let doc = {};
|
|
60799
|
-
if (
|
|
61777
|
+
if (existsSync34(file)) {
|
|
60800
61778
|
try {
|
|
60801
61779
|
doc = JSON.parse(readText(file));
|
|
60802
61780
|
} catch {
|
|
@@ -60865,27 +61843,27 @@ async function cmdPages(p) {
|
|
|
60865
61843
|
const derived = derivePages(result, scope);
|
|
60866
61844
|
const split = p.flags.split === "page";
|
|
60867
61845
|
const outDir = typeof p.flags.out === "string" && p.flags.out ? p.flags.out : void 0;
|
|
60868
|
-
const shotOf = (id) =>
|
|
61846
|
+
const shotOf = (id) => join50(PAGES_DIR, id, "screen.png");
|
|
60869
61847
|
const shotsRelative = (fileDir) => {
|
|
60870
61848
|
const m = /* @__PURE__ */ new Map();
|
|
60871
61849
|
for (const pg of derived) {
|
|
60872
|
-
if (
|
|
61850
|
+
if (existsSync34(shotOf(pg.id))) m.set(pg.id, relative5(fileDir, shotOf(pg.id)).split("\\").join("/"));
|
|
60873
61851
|
}
|
|
60874
61852
|
return m;
|
|
60875
61853
|
};
|
|
60876
61854
|
const shotPaths = (pages) => {
|
|
60877
61855
|
const m = /* @__PURE__ */ new Map();
|
|
60878
|
-
for (const pg of pages) if (
|
|
61856
|
+
for (const pg of pages) if (existsSync34(shotOf(pg.id))) m.set(pg.id, shotOf(pg.id));
|
|
60879
61857
|
return m;
|
|
60880
61858
|
};
|
|
60881
61859
|
const shotsCopiedInto = (dir) => {
|
|
60882
61860
|
const m = /* @__PURE__ */ new Map();
|
|
60883
|
-
const assets =
|
|
61861
|
+
const assets = join50(dir, "assets");
|
|
60884
61862
|
for (const pg of derived) {
|
|
60885
61863
|
const src = shotOf(pg.id);
|
|
60886
|
-
if (!
|
|
61864
|
+
if (!existsSync34(src)) continue;
|
|
60887
61865
|
mkdirSync17(assets, { recursive: true });
|
|
60888
|
-
copyFileSync4(src,
|
|
61866
|
+
copyFileSync4(src, join50(assets, `${pg.id}.png`));
|
|
60889
61867
|
m.set(pg.id, `./assets/${pg.id}.png`);
|
|
60890
61868
|
}
|
|
60891
61869
|
return m;
|
|
@@ -60903,7 +61881,7 @@ async function cmdPages(p) {
|
|
|
60903
61881
|
return 0;
|
|
60904
61882
|
}
|
|
60905
61883
|
mkdirSync17(outDir, { recursive: true });
|
|
60906
|
-
const manifest = wantEvidence ? writeEvidence(result, { outDir }) : void 0;
|
|
61884
|
+
const manifest = wantEvidence ? writeEvidence(result, { outDir, ...evidenceCapsOf(p) }) : void 0;
|
|
60907
61885
|
const cropFor = manifest ? (f) => {
|
|
60908
61886
|
const c2 = manifest.crops.get(findingId(f));
|
|
60909
61887
|
return c2 ? { href: c2.href, alt: c2.alt[lang] } : void 0;
|
|
@@ -60911,15 +61889,16 @@ async function cmdPages(p) {
|
|
|
60911
61889
|
if (manifest) {
|
|
60912
61890
|
const total = evidenceNotice(manifest, null, lang);
|
|
60913
61891
|
console.error(
|
|
60914
|
-
lang === "fr" ? `ultra11y : ${manifest.totals.imaged} vignette(s) \xE9crite(s) dans ${
|
|
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")}.`
|
|
60915
61893
|
);
|
|
60916
61894
|
for (const line of total) console.error(line);
|
|
60917
61895
|
}
|
|
60918
61896
|
const evidenceOpts = {
|
|
60919
|
-
...cropFor ? { cropFor } : {}
|
|
61897
|
+
...cropFor ? { cropFor } : {},
|
|
61898
|
+
...manifest ? { evidenceNotice: (id) => evidenceNotice(manifest, id, lang) } : {}
|
|
60920
61899
|
};
|
|
60921
61900
|
if (!split) {
|
|
60922
|
-
const file =
|
|
61901
|
+
const file = join50(outDir, `pages-${result.date}.md`);
|
|
60923
61902
|
writeFileSync19(file, `${renderPagesDocument(result, derived, { standard, lang, screenshots: shotsCopiedInto(outDir), ...evidenceOpts })}
|
|
60924
61903
|
`);
|
|
60925
61904
|
const html2 = wantHtml ? emitHtml(result, {
|
|
@@ -60939,14 +61918,10 @@ async function cmdPages(p) {
|
|
|
60939
61918
|
const sheet = (id) => `page-${id}.md`;
|
|
60940
61919
|
const hrefs = new Map(derived.map((pg) => [pg.id, `./${sheet(pg.id)}`]));
|
|
60941
61920
|
for (const pg of derived) {
|
|
60942
|
-
|
|
60943
|
-
|
|
60944
|
-
join47(outDir, sheet(pg.id)),
|
|
60945
|
-
`${renderPageDocument(result, pg, { standard, lang, screenshots: shots, ...evidenceOpts, ...notice.length ? { evidenceNotice: notice } : {} })}
|
|
60946
|
-
`
|
|
60947
|
-
);
|
|
61921
|
+
writeFileSync19(join50(outDir, sheet(pg.id)), `${renderPageDocument(result, pg, { standard, lang, screenshots: shots, ...evidenceOpts })}
|
|
61922
|
+
`);
|
|
60948
61923
|
}
|
|
60949
|
-
const index =
|
|
61924
|
+
const index = join50(outDir, "index.md");
|
|
60950
61925
|
writeFileSync19(index, `${renderPagesIndex(result, derived, { standard, lang, hrefs })}
|
|
60951
61926
|
`);
|
|
60952
61927
|
const html = wantHtml ? emitHtml(result, {
|
|
@@ -60962,11 +61937,20 @@ async function cmdPages(p) {
|
|
|
60962
61937
|
console.log(index);
|
|
60963
61938
|
return html?.imagesDropped ? 1 : 0;
|
|
60964
61939
|
}
|
|
60965
|
-
function
|
|
60966
|
-
const raw = p.flags[
|
|
61940
|
+
function positiveIntFlag(p, name2) {
|
|
61941
|
+
const raw = p.flags[name2];
|
|
60967
61942
|
if (typeof raw !== "string" || !raw) return void 0;
|
|
60968
61943
|
const n = Number.parseInt(raw, 10);
|
|
60969
|
-
|
|
61944
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
61945
|
+
console.error(`ultra11y: --${name2} expects a positive integer (got "${raw}"). Ignored \u2014 the default applies.`);
|
|
61946
|
+
return void 0;
|
|
61947
|
+
}
|
|
61948
|
+
function budgetOf(p) {
|
|
61949
|
+
return positiveIntFlag(p, "inline-budget");
|
|
61950
|
+
}
|
|
61951
|
+
function evidenceCapsOf(p) {
|
|
61952
|
+
const total = positiveIntFlag(p, "evidence-max");
|
|
61953
|
+
return total === void 0 ? {} : { caps: { total } };
|
|
60970
61954
|
}
|
|
60971
61955
|
function emitHtml(result, opts) {
|
|
60972
61956
|
const res = writeHtml(result, opts);
|
|
@@ -60989,7 +61973,7 @@ async function cmdSnapshot(p) {
|
|
|
60989
61973
|
2
|
|
60990
61974
|
)
|
|
60991
61975
|
);
|
|
60992
|
-
else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${
|
|
61976
|
+
else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${join50(root, PAGES_DIR)}.` : `No snapshot in ${join50(root, PAGES_DIR)}.`);
|
|
60993
61977
|
else for (const s of snaps) console.log(`${s.meta.id} ${s.meta.name} ${s.meta.url}${s.meta.auth ? " [auth]" : ""}`);
|
|
60994
61978
|
return 0;
|
|
60995
61979
|
}
|
|
@@ -61036,7 +62020,7 @@ async function cmdSnapshot(p) {
|
|
|
61036
62020
|
console.error(`ultra11y snapshot write: could not write the snapshot: ${e instanceof Error ? e.message : String(e)}`);
|
|
61037
62021
|
return 1;
|
|
61038
62022
|
}
|
|
61039
|
-
const result = runAudit({ inputs: [
|
|
62023
|
+
const result = runAudit({ inputs: [join50(dir, "dom.html")], onWarn: (m) => console.error(m) });
|
|
61040
62024
|
const failOnRaw = p.flags["fail-on"];
|
|
61041
62025
|
const failOnParsed = parseFailOn(failOnRaw);
|
|
61042
62026
|
if (failOnRaw !== void 0 && failOnParsed === null) {
|
|
@@ -61068,8 +62052,8 @@ function cmdInit(p) {
|
|
|
61068
62052
|
if (want.baseline) {
|
|
61069
62053
|
const inputs = p.positionals.length ? p.positionals : ["."];
|
|
61070
62054
|
const result = runAudit({ inputs, onWarn: (m) => console.error(m) });
|
|
61071
|
-
mkdirSync17(
|
|
61072
|
-
const bp =
|
|
62055
|
+
mkdirSync17(join50(root, "audits"), { recursive: true });
|
|
62056
|
+
const bp = join50(root, "audits", "baseline.json");
|
|
61073
62057
|
writeFileSync19(bp, JSON.stringify(result, null, 2) + "\n");
|
|
61074
62058
|
wrote.push(bp);
|
|
61075
62059
|
}
|
|
@@ -61205,17 +62189,18 @@ async function cmdReport(p) {
|
|
|
61205
62189
|
emitCiFormat(result, ciFormat, standard, lang);
|
|
61206
62190
|
return 0;
|
|
61207
62191
|
}
|
|
61208
|
-
const
|
|
62192
|
+
const manifest = p.flags.evidence === true ? writeEvidence(result, { outDir: out2, ...evidenceCapsOf(p) }) : void 0;
|
|
62193
|
+
if (manifest) {
|
|
62194
|
+
for (const line of evidenceNotice(manifest, null, lang)) console.error(line);
|
|
62195
|
+
}
|
|
62196
|
+
const cropFor = manifest ? (f) => {
|
|
62197
|
+
const c2 = manifest.crops.get(findingId(f));
|
|
62198
|
+
return c2 ? { href: c2.href, alt: c2.alt[lang] } : void 0;
|
|
62199
|
+
} : void 0;
|
|
62200
|
+
const path = writeReport(result, { out: out2, lang, standard, ...cropFor ? { cropFor } : {} });
|
|
61209
62201
|
let html;
|
|
61210
62202
|
if (p.flags.html === true) {
|
|
61211
|
-
const manifest = p.flags.evidence === true ? writeEvidence(result, { outDir: out2 }) : void 0;
|
|
61212
|
-
if (manifest) for (const line of evidenceNotice(manifest, null, lang)) console.error(line);
|
|
61213
62203
|
html = emitHtml(result, { outDir: out2, standard, lang, ...manifest ? { evidence: manifest } : {}, inlineBudget: budgetOf(p) });
|
|
61214
|
-
} else if (p.flags.evidence === true) {
|
|
61215
|
-
console.error(
|
|
61216
|
-
lang === "fr" ? "ultra11y report : --evidence n'illustre que le rapport HTML. Ajoutez --html, ou utilisez `pages --format report --evidence` pour des fiches Markdown illustr\xE9es." : "ultra11y report: --evidence only illustrates the HTML report. Add --html, or use `pages --format report --evidence` for illustrated Markdown sheets."
|
|
61217
|
-
);
|
|
61218
|
-
return 2;
|
|
61219
62204
|
}
|
|
61220
62205
|
const untested = isCore(standard) ? [] : untestedNeedsRendering(result);
|
|
61221
62206
|
const partial = untested.length > 0;
|
|
@@ -61369,7 +62354,7 @@ async function cmdTickets(p) {
|
|
|
61369
62354
|
let setPath;
|
|
61370
62355
|
if (ticketsOut) {
|
|
61371
62356
|
mkdirSync17(ticketsOut, { recursive: true });
|
|
61372
|
-
setPath =
|
|
62357
|
+
setPath = join50(ticketsOut, `issues-${result.date}.json`);
|
|
61373
62358
|
const payload = {
|
|
61374
62359
|
tool: "ultra11y",
|
|
61375
62360
|
kind: "issues",
|
|
@@ -61470,8 +62455,8 @@ async function cmdTickets(p) {
|
|
|
61470
62455
|
return pushed.failed > 0 ? 1 : 0;
|
|
61471
62456
|
}
|
|
61472
62457
|
function depsAt(root) {
|
|
61473
|
-
const pkgPath =
|
|
61474
|
-
if (!
|
|
62458
|
+
const pkgPath = join50(root, "package.json");
|
|
62459
|
+
if (!existsSync34(pkgPath)) return {};
|
|
61475
62460
|
try {
|
|
61476
62461
|
const pkg = JSON.parse(readText(pkgPath));
|
|
61477
62462
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
@@ -61497,24 +62482,24 @@ function cmdRender(p) {
|
|
|
61497
62482
|
console.error(`ultra11y render: --runner must be playwright|cypress|auto (got "${forced}").`);
|
|
61498
62483
|
return 2;
|
|
61499
62484
|
}
|
|
61500
|
-
const detected = detectE2eRunner(depsAt(root), (f) =>
|
|
62485
|
+
const detected = detectE2eRunner(depsAt(root), (f) => existsSync34(join50(root, f)));
|
|
61501
62486
|
const runners = forced && forced !== "auto" ? [forced] : detected;
|
|
61502
62487
|
if (!runners.length) {
|
|
61503
62488
|
console.error(e2eSetupPlan([], {}, lang));
|
|
61504
62489
|
return 1;
|
|
61505
62490
|
}
|
|
61506
62491
|
const engineRef = engineRefFor(root);
|
|
61507
|
-
const dir =
|
|
62492
|
+
const dir = join50(root, ".ultra11y", "e2e");
|
|
61508
62493
|
const paths = {};
|
|
61509
62494
|
try {
|
|
61510
62495
|
mkdirSync17(dir, { recursive: true });
|
|
61511
62496
|
if (runners.includes("playwright")) {
|
|
61512
|
-
writeFileSync19(
|
|
62497
|
+
writeFileSync19(join50(dir, "playwright.mjs"), playwrightFixture(engineRef));
|
|
61513
62498
|
paths.playwright = ".ultra11y/e2e/playwright.mjs";
|
|
61514
62499
|
}
|
|
61515
62500
|
if (runners.includes("cypress")) {
|
|
61516
|
-
writeFileSync19(
|
|
61517
|
-
writeFileSync19(
|
|
62501
|
+
writeFileSync19(join50(dir, "cypress-plugin.mjs"), cypressPlugin(engineRef));
|
|
62502
|
+
writeFileSync19(join50(dir, "cypress-commands.mjs"), cypressCommands());
|
|
61518
62503
|
paths.cypressPlugin = ".ultra11y/e2e/cypress-plugin.mjs";
|
|
61519
62504
|
paths.cypressCommands = ".ultra11y/e2e/cypress-commands.mjs";
|
|
61520
62505
|
}
|
|
@@ -61543,7 +62528,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61543
62528
|
}
|
|
61544
62529
|
if (p.flags.setup === true) {
|
|
61545
62530
|
const rel2 = ".ultra11y/capture-setup.mjs";
|
|
61546
|
-
const out2 =
|
|
62531
|
+
const out2 = join50(root, rel2);
|
|
61547
62532
|
try {
|
|
61548
62533
|
mkdirSync17(dirname14(out2), { recursive: true });
|
|
61549
62534
|
writeFileSync19(out2, captureSetup());
|
|
@@ -61552,20 +62537,20 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61552
62537
|
return 1;
|
|
61553
62538
|
}
|
|
61554
62539
|
let setupDeps = {};
|
|
61555
|
-
const setupPkg =
|
|
61556
|
-
if (
|
|
62540
|
+
const setupPkg = join50(root, "package.json");
|
|
62541
|
+
if (existsSync34(setupPkg)) {
|
|
61557
62542
|
try {
|
|
61558
62543
|
const pkg = JSON.parse(readText(setupPkg));
|
|
61559
62544
|
setupDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
61560
62545
|
} catch {
|
|
61561
62546
|
}
|
|
61562
62547
|
}
|
|
61563
|
-
const tr = detectTestRunner(setupDeps, (f) =>
|
|
62548
|
+
const tr = detectTestRunner(setupDeps, (f) => existsSync34(join50(root, f)));
|
|
61564
62549
|
console.log(captureSetupPlan(tr, rel2, lang));
|
|
61565
62550
|
const gaLine = ".ultra11y/captures/*.html text eol=lf linguist-generated=true";
|
|
61566
|
-
const gaPath =
|
|
62551
|
+
const gaPath = join50(root, ".gitattributes");
|
|
61567
62552
|
try {
|
|
61568
|
-
const existing =
|
|
62553
|
+
const existing = existsSync34(gaPath) ? readFileSync32(gaPath, "utf8") : "";
|
|
61569
62554
|
if (!existing.includes(".ultra11y/captures/")) {
|
|
61570
62555
|
appendFileSync(gaPath, (existing && !existing.endsWith("\n") ? "\n" : "") + gaLine + "\n");
|
|
61571
62556
|
console.log(lang === "fr" ? `.gitattributes : ajout\xE9 \xAB ${gaLine} \xBB` : `.gitattributes: added "${gaLine}"`);
|
|
@@ -61573,8 +62558,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61573
62558
|
} catch {
|
|
61574
62559
|
}
|
|
61575
62560
|
try {
|
|
61576
|
-
const giPath =
|
|
61577
|
-
if (
|
|
62561
|
+
const giPath = join50(root, ".gitignore");
|
|
62562
|
+
if (existsSync34(giPath) && /^\s*\/?\.ultra11y(\/\**)?\/?\s*$/m.test(readFileSync32(giPath, "utf8")))
|
|
61578
62563
|
console.error(
|
|
61579
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/").'
|
|
61580
62565
|
);
|
|
@@ -61584,8 +62569,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61584
62569
|
}
|
|
61585
62570
|
if (p.flags.storybook === true || typeof p.flags.storybook === "string") {
|
|
61586
62571
|
const sbDir = p.positionals[0] ?? "storybook-static";
|
|
61587
|
-
const indexPath =
|
|
61588
|
-
if (!
|
|
62572
|
+
const indexPath = existsSync34(join50(sbDir, "index.json")) ? join50(sbDir, "index.json") : join50(sbDir, "stories.json");
|
|
62573
|
+
if (!existsSync34(indexPath)) {
|
|
61589
62574
|
console.error(
|
|
61590
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}.`
|
|
61591
62576
|
);
|
|
@@ -61595,7 +62580,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61595
62580
|
const provById = new Map(stories.map((s) => [s.id, storyProvenance(s)]));
|
|
61596
62581
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
61597
62582
|
const htmlDir = capturesFlag ?? sbDir;
|
|
61598
|
-
const htmlFiles =
|
|
62583
|
+
const htmlFiles = existsSync34(htmlDir) ? discover([htmlDir]).files.filter((f) => /\.html?$/i.test(f)) : [];
|
|
61599
62584
|
const outDir = ".ultra11y/captures";
|
|
61600
62585
|
let attributed = 0;
|
|
61601
62586
|
let skipped = 0;
|
|
@@ -61618,7 +62603,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61618
62603
|
}
|
|
61619
62604
|
try {
|
|
61620
62605
|
mkdirSync17(outDir, { recursive: true });
|
|
61621
|
-
writeFileSync19(
|
|
62606
|
+
writeFileSync19(join50(outDir, `${hitId}.html`), `${formatCaptureComment(prov)}
|
|
61622
62607
|
${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
61623
62608
|
attributed++;
|
|
61624
62609
|
} catch {
|
|
@@ -61640,11 +62625,11 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
|
61640
62625
|
}
|
|
61641
62626
|
if (p.flags.coverage === true) {
|
|
61642
62627
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
61643
|
-
const capturesDir = capturesFlag ??
|
|
62628
|
+
const capturesDir = capturesFlag ?? join50(root, ".ultra11y/captures");
|
|
61644
62629
|
const graphExt = [...GRAPH_ONLY_EXT, ...asList(p.flags.ext) ?? []];
|
|
61645
62630
|
const sourceFiles = discover([root], { include: asList(p.flags.include), exclude: asList(p.flags.exclude), ext: graphExt }).files;
|
|
61646
62631
|
const graph = buildGraphStreaming(sourceFiles);
|
|
61647
|
-
const capFiles =
|
|
62632
|
+
const capFiles = existsSync34(capturesDir) ? discover([capturesDir]).files : [];
|
|
61648
62633
|
const entries = capFiles.map((f) => ({ file: toPosix(f), provenance: parseCaptureProvenance(readText(f)) }));
|
|
61649
62634
|
const cov = computeCaptureCoverage(graph, entries);
|
|
61650
62635
|
if (p.flags.json) console.log(JSON.stringify(cov, null, 2));
|
|
@@ -61652,15 +62637,15 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
|
61652
62637
|
return 0;
|
|
61653
62638
|
}
|
|
61654
62639
|
let deps = {};
|
|
61655
|
-
const pkgPath =
|
|
61656
|
-
if (
|
|
62640
|
+
const pkgPath = join50(root, "package.json");
|
|
62641
|
+
if (existsSync34(pkgPath)) {
|
|
61657
62642
|
try {
|
|
61658
62643
|
const pkg = JSON.parse(readText(pkgPath));
|
|
61659
62644
|
deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
61660
62645
|
} catch {
|
|
61661
62646
|
}
|
|
61662
62647
|
}
|
|
61663
|
-
const detection = detectFrameworks(deps, (f) =>
|
|
62648
|
+
const detection = detectFrameworks(deps, (f) => existsSync34(join50(root, f)));
|
|
61664
62649
|
if (p.flags.json) console.log(JSON.stringify(detection, null, 2));
|
|
61665
62650
|
else console.log(renderPlan(detection, lang));
|
|
61666
62651
|
return 0;
|
|
@@ -61860,7 +62845,7 @@ function applyAdjudicationFile(p, adj, lang) {
|
|
|
61860
62845
|
}
|
|
61861
62846
|
const out2 = typeof p.flags.out === "string" ? p.flags.out : ".";
|
|
61862
62847
|
mkdirSync17(out2, { recursive: true });
|
|
61863
|
-
const auditPath =
|
|
62848
|
+
const auditPath = join50(out2, "audit-latest.json");
|
|
61864
62849
|
writeFileSync19(auditPath, JSON.stringify(r.audit, null, 2) + "\n");
|
|
61865
62850
|
if (p.flags.json)
|
|
61866
62851
|
console.log(
|
|
@@ -61970,7 +62955,7 @@ async function cmdJudge(p) {
|
|
|
61970
62955
|
return 1;
|
|
61971
62956
|
}
|
|
61972
62957
|
mkdirSync17(out2, { recursive: true });
|
|
61973
|
-
const auditPath =
|
|
62958
|
+
const auditPath = join50(out2, "audit-latest.json");
|
|
61974
62959
|
writeFileSync19(auditPath, `${JSON.stringify(r.audit, null, 2)}
|
|
61975
62960
|
`);
|
|
61976
62961
|
console.log(
|
|
@@ -62040,7 +63025,7 @@ async function cmdScan(p) {
|
|
|
62040
63025
|
return 0;
|
|
62041
63026
|
}
|
|
62042
63027
|
for (const target of p.positionals.filter((a) => a !== "-")) {
|
|
62043
|
-
if (/^https?:\/\//i.test(target) ||
|
|
63028
|
+
if (/^https?:\/\//i.test(target) || existsSync34(target)) continue;
|
|
62044
63029
|
console.error(
|
|
62045
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.`
|
|
62046
63031
|
);
|
|
@@ -62167,7 +63152,7 @@ async function cmdScan(p) {
|
|
|
62167
63152
|
}
|
|
62168
63153
|
let merged = mergeDynamic(audit2, dynamic, lang);
|
|
62169
63154
|
if (dynamic.snapshots?.length) {
|
|
62170
|
-
const doms = dynamic.snapshots.map((id) =>
|
|
63155
|
+
const doms = dynamic.snapshots.map((id) => join50(snapshotRoot ?? ".", PAGES_DIR, id, "dom.html")).filter((f) => existsSync34(f));
|
|
62171
63156
|
if (doms.length) {
|
|
62172
63157
|
const snapAudit = runAudit({ inputs: doms, onWarn: (m) => console.error(m) });
|
|
62173
63158
|
merged = mergeSnapshotAudit(merged, snapAudit);
|
|
@@ -62179,11 +63164,11 @@ async function cmdScan(p) {
|
|
|
62179
63164
|
}
|
|
62180
63165
|
}
|
|
62181
63166
|
mkdirSync17(out2, { recursive: true });
|
|
62182
|
-
writeFileSync19(
|
|
63167
|
+
writeFileSync19(join50(out2, "audit-latest.json"), JSON.stringify(merged, null, 2) + "\n");
|
|
62183
63168
|
if (p.flags.json) console.log(JSON.stringify(merged, null, 2));
|
|
62184
63169
|
else {
|
|
62185
63170
|
console.log(
|
|
62186
|
-
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${
|
|
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).`
|
|
62187
63172
|
);
|
|
62188
63173
|
if (dynamic.snapshots?.length)
|
|
62189
63174
|
console.log(
|
|
@@ -62426,7 +63411,7 @@ async function cmdImport(p) {
|
|
|
62426
63411
|
}
|
|
62427
63412
|
if (outDir) {
|
|
62428
63413
|
mkdirSync17(outDir, { recursive: true });
|
|
62429
|
-
const rawFile =
|
|
63414
|
+
const rawFile = join50(outDir, `external-${adapterId}-${arg}.raw.json`);
|
|
62430
63415
|
writeFileSync19(rawFile, rawText.endsWith("\n") ? rawText : `${rawText}
|
|
62431
63416
|
`);
|
|
62432
63417
|
if (!p.flags.json) console.log(rawFile);
|
|
@@ -62462,7 +63447,7 @@ async function cmdImport(p) {
|
|
|
62462
63447
|
}
|
|
62463
63448
|
if (outDir) {
|
|
62464
63449
|
mkdirSync17(outDir, { recursive: true });
|
|
62465
|
-
const file =
|
|
63450
|
+
const file = join50(outDir, "external-latest.json");
|
|
62466
63451
|
writeFileSync19(file, `${JSON.stringify(audit2, null, 2)}
|
|
62467
63452
|
`);
|
|
62468
63453
|
console.log(file);
|
|
@@ -62568,7 +63553,7 @@ function cmdOrchestrate(p) {
|
|
|
62568
63553
|
}
|
|
62569
63554
|
const engineAbs = realpathSync6(fileURLToPath5(import.meta.url));
|
|
62570
63555
|
if (p.flags.list === true) {
|
|
62571
|
-
if (!
|
|
63556
|
+
if (!existsSync34(runFlag)) {
|
|
62572
63557
|
console.error(`ultra11y orchestrate: run dir not found: ${runFlag}.`);
|
|
62573
63558
|
return 2;
|
|
62574
63559
|
}
|
|
@@ -62595,7 +63580,7 @@ function cmdOrchestrate(p) {
|
|
|
62595
63580
|
);
|
|
62596
63581
|
} else {
|
|
62597
63582
|
console.log(
|
|
62598
|
-
lang === "fr" ? `Suivez ${
|
|
63583
|
+
lang === "fr" ? `Suivez ${join50(runFlag, "orchestration", "RUNBOOK.md")} s\xE9quentiellement (chemin \xE9co).` : `Follow ${join50(runFlag, "orchestration", "RUNBOOK.md")} sequentially (the eco path).`
|
|
62599
63584
|
);
|
|
62600
63585
|
}
|
|
62601
63586
|
if (p.flags.phase === void 0 && workflows.length === 0 && p.flags.eco !== true) {
|