ultra11y 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/package.json +1 -1
- package/scripts/ultra11y.mjs +1471 -527
- 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 +1471 -527
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +1471 -527
- package/src/data/wcag-universe.json +1 -1
- 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
|
}
|
|
@@ -49771,13 +50715,13 @@ function render(r, lang, opts) {
|
|
|
49771
50715
|
out2.push(`- ${nc.length} ${s.ncCount}${adv.length ? ` \xB7 ${adv.length} ${s.advCount}` : ""}`);
|
|
49772
50716
|
const notes = pageScope.find((x) => x.id === pg.id)?.notes;
|
|
49773
50717
|
if (notes) out2.push(`- _${notes}_`);
|
|
49774
|
-
const shot =
|
|
49775
|
-
if (
|
|
50718
|
+
const shot = join32(PAGES_DIR, pg.id, "screen.png");
|
|
50719
|
+
if (existsSync18(shot)) {
|
|
49776
50720
|
let href = relative3(opts.outDir ?? ".", shot).split("\\").join("/");
|
|
49777
50721
|
if (opts.outDir) {
|
|
49778
50722
|
try {
|
|
49779
|
-
mkdirSync7(
|
|
49780
|
-
copyFileSync(shot,
|
|
50723
|
+
mkdirSync7(join32(opts.outDir, "assets"), { recursive: true });
|
|
50724
|
+
copyFileSync(shot, join32(opts.outDir, "assets", `${pg.id}.png`));
|
|
49781
50725
|
href = `./assets/${pg.id}.png`;
|
|
49782
50726
|
} catch {
|
|
49783
50727
|
}
|
|
@@ -49885,7 +50829,7 @@ function writeReport(r, opts) {
|
|
|
49885
50829
|
const core = isCore(opts.standard);
|
|
49886
50830
|
const md = core ? renderReport(r, opts.lang, opts.out, opts.cropFor) : renderPackReport(r, loadPack(opts.standard), opts.lang, opts.out, opts.cropFor);
|
|
49887
50831
|
mkdirSync7(opts.out, { recursive: true });
|
|
49888
|
-
const path =
|
|
50832
|
+
const path = join32(opts.out, `${core ? "wcag" : opts.standard}-${r.date}.md`);
|
|
49889
50833
|
writeFileSync8(path, md);
|
|
49890
50834
|
return path;
|
|
49891
50835
|
}
|
|
@@ -50369,7 +51313,7 @@ function providerFromRemote() {
|
|
|
50369
51313
|
|
|
50370
51314
|
// src/tickets/providers/github.ts
|
|
50371
51315
|
var CAPABILITIES = { bodyLimit: 65536, labels: true };
|
|
50372
|
-
function
|
|
51316
|
+
function resolve6(opts) {
|
|
50373
51317
|
const env = opts.env ?? process.env;
|
|
50374
51318
|
const mode = opts.transport ?? "auto";
|
|
50375
51319
|
const api = (env.GITHUB_API_URL || "https://api.github.com").replace(/\/+$/, "");
|
|
@@ -50398,7 +51342,7 @@ function resolve4(opts) {
|
|
|
50398
51342
|
};
|
|
50399
51343
|
}
|
|
50400
51344
|
function createGithubProvider(opts = {}) {
|
|
50401
|
-
const r =
|
|
51345
|
+
const r = resolve6(opts);
|
|
50402
51346
|
const auth = { Authorization: `Bearer ${r.token ?? ""}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" };
|
|
50403
51347
|
const http = {
|
|
50404
51348
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
|
|
@@ -50501,7 +51445,7 @@ function execReason(err2) {
|
|
|
50501
51445
|
if (line) return line;
|
|
50502
51446
|
return typeof e.message === "string" ? e.message.split("\n")[0]?.trim() ?? void 0 : void 0;
|
|
50503
51447
|
}
|
|
50504
|
-
function
|
|
51448
|
+
function resolve7(opts) {
|
|
50505
51449
|
const env = opts.env ?? process.env;
|
|
50506
51450
|
const mode = opts.transport ?? "auto";
|
|
50507
51451
|
const api = (env.CI_API_V4_URL || "https://gitlab.com/api/v4").replace(/\/+$/, "");
|
|
@@ -50521,7 +51465,7 @@ function resolve5(opts) {
|
|
|
50521
51465
|
return { transport: "cli", api, jobToken };
|
|
50522
51466
|
}
|
|
50523
51467
|
function createGitlabProvider(opts = {}) {
|
|
50524
|
-
const r =
|
|
51468
|
+
const r = resolve7(opts);
|
|
50525
51469
|
const id = encodeURIComponent(r.project ?? "");
|
|
50526
51470
|
const auth = { "PRIVATE-TOKEN": r.token ?? "" };
|
|
50527
51471
|
const http = {
|
|
@@ -50590,7 +51534,7 @@ function createGitlabProvider(opts = {}) {
|
|
|
50590
51534
|
var CAPABILITIES3 = { bodyLimit: 32767, labels: true };
|
|
50591
51535
|
var JIRA_SCOPE_LABEL = "ultra11y";
|
|
50592
51536
|
var PRIORITY = { bloquant: "Highest", majeur: "High", mineur: "Low" };
|
|
50593
|
-
function
|
|
51537
|
+
function resolve8(opts) {
|
|
50594
51538
|
const env = opts.env ?? process.env;
|
|
50595
51539
|
const issueType = env.ULTRA11Y_JIRA_ISSUE_TYPE || "Task";
|
|
50596
51540
|
const apiVersion = env.ULTRA11Y_JIRA_API === "2" ? "2" : "3";
|
|
@@ -50609,7 +51553,7 @@ function toAdf(markdown) {
|
|
|
50609
51553
|
return { type: "doc", version: 1, content: content.length ? content : [{ type: "paragraph", content: [] }] };
|
|
50610
51554
|
}
|
|
50611
51555
|
function createJiraProvider(opts = {}) {
|
|
50612
|
-
const r =
|
|
51556
|
+
const r = resolve8(opts);
|
|
50613
51557
|
const headers = { Authorization: r.auth ?? "" };
|
|
50614
51558
|
const http = {
|
|
50615
51559
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
|
|
@@ -51113,12 +52057,12 @@ function renderCriteriaReference() {
|
|
|
51113
52057
|
}
|
|
51114
52058
|
|
|
51115
52059
|
// src/check.ts
|
|
51116
|
-
import { existsSync as
|
|
51117
|
-
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";
|
|
51118
52062
|
|
|
51119
52063
|
// src/verify.ts
|
|
51120
52064
|
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
51121
|
-
import { join as
|
|
52065
|
+
import { join as join33 } from "path";
|
|
51122
52066
|
var VERIFY_MAX = 40;
|
|
51123
52067
|
var plain = (s) => s.replace(/\[([^\]]+)\]\(#[^)]*\)/g, "$1");
|
|
51124
52068
|
function auditorCriterionLine(standard) {
|
|
@@ -51293,16 +52237,16 @@ function applyVerdicts(items, expected) {
|
|
|
51293
52237
|
}
|
|
51294
52238
|
function writeWorklist(items, outDir, semantic, standard = "wcag", lang = "en") {
|
|
51295
52239
|
mkdirSync8(outDir, { recursive: true });
|
|
51296
|
-
const todoPath =
|
|
51297
|
-
const mdPath =
|
|
52240
|
+
const todoPath = join33(outDir, "VERIFY.todo.json");
|
|
52241
|
+
const mdPath = join33(outDir, "VERIFY.md");
|
|
51298
52242
|
writeFileSync9(todoPath, JSON.stringify(items, null, 2) + "\n");
|
|
51299
52243
|
writeFileSync9(mdPath, formatWorklist(items, semantic, standard, lang));
|
|
51300
52244
|
return { todoPath, mdPath, count: items.length };
|
|
51301
52245
|
}
|
|
51302
52246
|
|
|
51303
52247
|
// src/grounding.ts
|
|
51304
|
-
import { existsSync as
|
|
51305
|
-
import { resolve as
|
|
52248
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
52249
|
+
import { resolve as resolve9 } from "path";
|
|
51306
52250
|
var WINDOW = 10;
|
|
51307
52251
|
var norm2 = (s) => s.replace(/\s+/g, " ").trim();
|
|
51308
52252
|
var isUnresolvable = (file) => !file || file === "-" || file === "<stdin>" || file === "stdin";
|
|
@@ -51319,11 +52263,11 @@ function selectorProbes(selector) {
|
|
|
51319
52263
|
}
|
|
51320
52264
|
function groundFinding(g, opts = {}) {
|
|
51321
52265
|
if (isUnresolvable(g.file)) return { ok: true, moved: false };
|
|
51322
|
-
const path =
|
|
51323
|
-
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}` };
|
|
51324
52268
|
let text;
|
|
51325
52269
|
try {
|
|
51326
|
-
text =
|
|
52270
|
+
text = readFileSync17(path, "utf8");
|
|
51327
52271
|
} catch {
|
|
51328
52272
|
return { ok: false, moved: false, issue: `cited file unreadable: ${g.file}` };
|
|
51329
52273
|
}
|
|
@@ -51710,11 +52654,11 @@ function checkSemantic(md, opts) {
|
|
|
51710
52654
|
const standard = opts.standard ?? "wcag";
|
|
51711
52655
|
const s = M[lang];
|
|
51712
52656
|
const empty = { total: 0, grounded: 0, moved: 0, failed: 0 };
|
|
51713
|
-
const artifact = opts.verdictsPath ??
|
|
51714
|
-
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 };
|
|
51715
52659
|
let items;
|
|
51716
52660
|
try {
|
|
51717
|
-
const parsed = JSON.parse(
|
|
52661
|
+
const parsed = JSON.parse(readFileSync18(artifact, "utf8"));
|
|
51718
52662
|
if (!Array.isArray(parsed)) throw new Error("not an array");
|
|
51719
52663
|
items = parsed;
|
|
51720
52664
|
} catch {
|
|
@@ -51747,7 +52691,7 @@ function sectionBody(md, n) {
|
|
|
51747
52691
|
|
|
51748
52692
|
// src/adjudicate.ts
|
|
51749
52693
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
51750
|
-
import { join as
|
|
52694
|
+
import { join as join35 } from "path";
|
|
51751
52695
|
|
|
51752
52696
|
// src/data/adjudication.json
|
|
51753
52697
|
var adjudication_default = {
|
|
@@ -52682,7 +53626,7 @@ function docsForAudit(audit2, cwd) {
|
|
|
52682
53626
|
const docs = [];
|
|
52683
53627
|
for (const f of files) {
|
|
52684
53628
|
try {
|
|
52685
|
-
docs.push(parseSource(readText(cwd ?
|
|
53629
|
+
docs.push(parseSource(readText(cwd ? join35(cwd, f) : f), f));
|
|
52686
53630
|
} catch {
|
|
52687
53631
|
}
|
|
52688
53632
|
}
|
|
@@ -53097,8 +54041,8 @@ function formatAdjudication(items, lang = "en", standard = CORE2) {
|
|
|
53097
54041
|
}
|
|
53098
54042
|
function writeAdjudication(items, outDir, opts) {
|
|
53099
54043
|
mkdirSync9(outDir, { recursive: true });
|
|
53100
|
-
const todoPath =
|
|
53101
|
-
const mdPath =
|
|
54044
|
+
const todoPath = join35(outDir, "ADJUDICATE.todo.json");
|
|
54045
|
+
const mdPath = join35(outDir, "ADJUDICATE.md");
|
|
53102
54046
|
const file = {
|
|
53103
54047
|
tool: "ultra11y",
|
|
53104
54048
|
kind: "adjudication",
|
|
@@ -53303,9 +54247,9 @@ function applyRawVerdicts(items, verdicts) {
|
|
|
53303
54247
|
|
|
53304
54248
|
// src/scan.ts
|
|
53305
54249
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
53306
|
-
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync11, existsSync as
|
|
54250
|
+
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync11, existsSync as existsSync22, statSync as statSync8, readdirSync as readdirSync5, rmSync as rmSync3, readFileSync as readFileSync19 } from "fs";
|
|
53307
54251
|
import { tmpdir as tmpdir2 } from "os";
|
|
53308
|
-
import { join as
|
|
54252
|
+
import { join as join36, resolve as resolve10 } from "path";
|
|
53309
54253
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
53310
54254
|
|
|
53311
54255
|
// src/axe-map.ts
|
|
@@ -53565,7 +54509,7 @@ async function crawlUrls(start2, opts) {
|
|
|
53565
54509
|
}
|
|
53566
54510
|
|
|
53567
54511
|
// src/sample.ts
|
|
53568
|
-
import { existsSync as
|
|
54512
|
+
import { existsSync as existsSync21 } from "fs";
|
|
53569
54513
|
function looksLikeTarget(u) {
|
|
53570
54514
|
return /^https?:\/\//i.test(u) || /^(\.\.?[/\\]|[/\\])/.test(u) || /\.x?html?$/i.test(u);
|
|
53571
54515
|
}
|
|
@@ -53601,7 +54545,7 @@ function validateSample(raw) {
|
|
|
53601
54545
|
if (p.auth !== void 0 && typeof p.auth !== "boolean") err2(`sample.pages[${i2}].auth`, "auth must be a boolean");
|
|
53602
54546
|
if (p.storageState !== void 0 && (typeof p.storageState !== "string" || p.storageState.trim() === "")) {
|
|
53603
54547
|
err2(`sample.pages[${i2}].storageState`, "storageState must be a non-empty file path string");
|
|
53604
|
-
} else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !
|
|
54548
|
+
} else if (typeof p.storageState === "string" && p.storageState.trim() !== "" && !existsSync21(p.storageState)) {
|
|
53605
54549
|
warn(
|
|
53606
54550
|
`sample.pages[${i2}].storageState`,
|
|
53607
54551
|
`storageState path not found: "${p.storageState}" (resolved from the current directory) \u2014 the scan will not be able to load this session`
|
|
@@ -53791,11 +54735,11 @@ function imageExists(tag) {
|
|
|
53791
54735
|
}
|
|
53792
54736
|
var CTX_PREFIX = "ultra11y-dyn-";
|
|
53793
54737
|
function buildImage(tag = IMAGE_TAG) {
|
|
53794
|
-
const ctx = mkdtempSync2(
|
|
54738
|
+
const ctx = mkdtempSync2(join36(tmpdir2(), CTX_PREFIX));
|
|
53795
54739
|
try {
|
|
53796
|
-
writeFileSync11(
|
|
53797
|
-
writeFileSync11(
|
|
53798
|
-
writeFileSync11(
|
|
54740
|
+
writeFileSync11(join36(ctx, "runner.mjs"), RUNNER);
|
|
54741
|
+
writeFileSync11(join36(ctx, "package.json"), PKG);
|
|
54742
|
+
writeFileSync11(join36(ctx, "Dockerfile"), DOCKERFILE);
|
|
53799
54743
|
execFileSync6("docker", ["build", "-t", tag, ctx], { stdio: "inherit", timeout: 9e5 });
|
|
53800
54744
|
} finally {
|
|
53801
54745
|
rmSync3(ctx, { recursive: true, force: true });
|
|
@@ -53806,7 +54750,7 @@ function cleanTempContexts() {
|
|
|
53806
54750
|
const dir = tmpdir2();
|
|
53807
54751
|
for (const name2 of readdirSync5(dir)) {
|
|
53808
54752
|
if (!name2.startsWith(CTX_PREFIX)) continue;
|
|
53809
|
-
rmSync3(
|
|
54753
|
+
rmSync3(join36(dir, name2), { recursive: true, force: true });
|
|
53810
54754
|
removed++;
|
|
53811
54755
|
}
|
|
53812
54756
|
return removed;
|
|
@@ -53872,7 +54816,7 @@ var PROBE_FIELDS = [
|
|
|
53872
54816
|
function runRunner(target, isFile, tag, snapshot = true) {
|
|
53873
54817
|
const args2 = ["run", "--rm"];
|
|
53874
54818
|
if (!snapshot) args2.push("-e", "ULTRA11Y_SNAPSHOT=0");
|
|
53875
|
-
if (isFile) args2.push("-v", `${
|
|
54819
|
+
if (isFile) args2.push("-v", `${resolve10(target)}:${MOUNT}:ro`);
|
|
53876
54820
|
args2.push(tag, isFile ? MOUNT : target);
|
|
53877
54821
|
let stdout;
|
|
53878
54822
|
try {
|
|
@@ -53955,7 +54899,7 @@ function toDynamicResult(out2, target, lang = "en", engine = "axe-core@playwrigh
|
|
|
53955
54899
|
var DOCKER_TESTED_SCS = ["1.4.10"];
|
|
53956
54900
|
function runScan(opts) {
|
|
53957
54901
|
const isUrl3 = /^https?:\/\//i.test(opts.target);
|
|
53958
|
-
if (!isUrl3 && !
|
|
54902
|
+
if (!isUrl3 && !existsSync22(opts.target)) {
|
|
53959
54903
|
throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
|
|
53960
54904
|
}
|
|
53961
54905
|
if (!dockerAvailable()) {
|
|
@@ -53963,7 +54907,7 @@ function runScan(opts) {
|
|
|
53963
54907
|
}
|
|
53964
54908
|
const tag = opts.tag ?? IMAGE_TAG;
|
|
53965
54909
|
if (!imageExists(tag)) buildImage(tag);
|
|
53966
|
-
const isFile = !isUrl3 &&
|
|
54910
|
+
const isFile = !isUrl3 && existsSync22(opts.target) && statSync8(opts.target).isFile();
|
|
53967
54911
|
const out2 = runRunner(opts.target, isFile, tag, Boolean(opts.snapshotRoot));
|
|
53968
54912
|
const id = opts.snapshotRoot ? writeRunnerSnapshot(opts.snapshotRoot, out2, opts.target) : void 0;
|
|
53969
54913
|
return { ...toDynamicResult(out2, opts.target), testedScs: [...DOCKER_TESTED_SCS], ...id ? { snapshots: [id] } : {} };
|
|
@@ -54050,10 +54994,10 @@ async function runCrawlScan(opts) {
|
|
|
54050
54994
|
var sevRank = { bloquant: 3, majeur: 2, mineur: 1 };
|
|
54051
54995
|
function resolveHostAnchor(file, snippet2) {
|
|
54052
54996
|
const s = snippet2?.trim();
|
|
54053
|
-
if (!s || !
|
|
54997
|
+
if (!s || !existsSync22(file)) return null;
|
|
54054
54998
|
let source;
|
|
54055
54999
|
try {
|
|
54056
|
-
source =
|
|
55000
|
+
source = readFileSync19(file, "utf8");
|
|
54057
55001
|
} catch {
|
|
54058
55002
|
return null;
|
|
54059
55003
|
}
|
|
@@ -54180,9 +55124,9 @@ function mergeSnapshotAudit(base, snap) {
|
|
|
54180
55124
|
}
|
|
54181
55125
|
|
|
54182
55126
|
// src/scan-local.ts
|
|
54183
|
-
import { existsSync as
|
|
55127
|
+
import { existsSync as existsSync23, statSync as statSync9 } from "fs";
|
|
54184
55128
|
import { createRequire } from "module";
|
|
54185
|
-
import { resolve as
|
|
55129
|
+
import { resolve as resolve11 } from "path";
|
|
54186
55130
|
var LOCAL_ENGINE = "axe-core@playwright (local)";
|
|
54187
55131
|
var LOCAL_TESTED_SCS = ["1.4.4", "1.4.10", "1.4.12", "2.4.7", "1.4.13"];
|
|
54188
55132
|
function localTestedScs(interact) {
|
|
@@ -54192,7 +55136,7 @@ var PW_SPEC = "@playwright/test";
|
|
|
54192
55136
|
var AXE_SPEC = "@axe-core/playwright";
|
|
54193
55137
|
function localAvailable(cwd) {
|
|
54194
55138
|
try {
|
|
54195
|
-
const req = createRequire(
|
|
55139
|
+
const req = createRequire(resolve11(cwd, "package.json"));
|
|
54196
55140
|
req.resolve(PW_SPEC);
|
|
54197
55141
|
req.resolve(AXE_SPEC);
|
|
54198
55142
|
return true;
|
|
@@ -54204,7 +55148,7 @@ function resolveLocalDeps(cwd) {
|
|
|
54204
55148
|
let chromium;
|
|
54205
55149
|
let AxeBuilder;
|
|
54206
55150
|
try {
|
|
54207
|
-
const req = createRequire(
|
|
55151
|
+
const req = createRequire(resolve11(cwd, "package.json"));
|
|
54208
55152
|
const pw = req(PW_SPEC);
|
|
54209
55153
|
const axeMod = req(AXE_SPEC);
|
|
54210
55154
|
chromium = pw.chromium;
|
|
@@ -54683,7 +55627,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
|
|
|
54683
55627
|
const page = await context.newPage();
|
|
54684
55628
|
const empty = [];
|
|
54685
55629
|
try {
|
|
54686
|
-
const url = isFile ? "file://" +
|
|
55630
|
+
const url = isFile ? "file://" + resolve11(target) : target;
|
|
54687
55631
|
await page.goto(url, { waitUntil: "load", timeout: 45e3 });
|
|
54688
55632
|
await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => {
|
|
54689
55633
|
});
|
|
@@ -54751,7 +55695,7 @@ async function runOnPage(browser, AxeBuilder, target, isFile, opts) {
|
|
|
54751
55695
|
}
|
|
54752
55696
|
async function runScanLocal(opts) {
|
|
54753
55697
|
const isUrl3 = /^https?:\/\//i.test(opts.target);
|
|
54754
|
-
if (!isUrl3 && !
|
|
55698
|
+
if (!isUrl3 && !existsSync23(opts.target)) {
|
|
54755
55699
|
throw new Error(`File not found: ${opts.target}. Pass an http(s):// URL or an existing HTML file.`);
|
|
54756
55700
|
}
|
|
54757
55701
|
const isFile = !isUrl3 && statSync9(opts.target).isFile();
|
|
@@ -55338,7 +56282,7 @@ function fixSummary(r, lang = "fr", write = false) {
|
|
|
55338
56282
|
// src/init.ts
|
|
55339
56283
|
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync10, chmodSync, realpathSync as realpathSync2 } from "fs";
|
|
55340
56284
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
55341
|
-
import { join as
|
|
56285
|
+
import { join as join37, relative as relative4, sep as sep4 } from "path";
|
|
55342
56286
|
var EN_SEV = { bloquant: "blocking", majeur: "major", mineur: "minor" };
|
|
55343
56287
|
function repoRoot() {
|
|
55344
56288
|
try {
|
|
@@ -55456,23 +56400,23 @@ jobs:
|
|
|
55456
56400
|
`;
|
|
55457
56401
|
}
|
|
55458
56402
|
function writeHook(root, enginePath, failOn, mode = "staged") {
|
|
55459
|
-
const dir =
|
|
56403
|
+
const dir = join37(root, ".git", "hooks");
|
|
55460
56404
|
mkdirSync10(dir, { recursive: true });
|
|
55461
|
-
const path =
|
|
56405
|
+
const path = join37(dir, "pre-commit");
|
|
55462
56406
|
writeFileSync13(path, mode === "baseline" ? hookScript(enginePath, failOn) : stagedHookScript(enginePath, failOn));
|
|
55463
56407
|
chmodSync(path, 493);
|
|
55464
56408
|
return path;
|
|
55465
56409
|
}
|
|
55466
56410
|
function writeCi(root, enginePath, failOn) {
|
|
55467
|
-
const dir =
|
|
56411
|
+
const dir = join37(root, ".github", "workflows");
|
|
55468
56412
|
mkdirSync10(dir, { recursive: true });
|
|
55469
|
-
const path =
|
|
56413
|
+
const path = join37(dir, "a11y.yml");
|
|
55470
56414
|
writeFileSync13(path, ciWorkflow(enginePath, failOn));
|
|
55471
56415
|
return path;
|
|
55472
56416
|
}
|
|
55473
56417
|
|
|
55474
56418
|
// src/install/json-edit.ts
|
|
55475
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
56419
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync20, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync14 } from "fs";
|
|
55476
56420
|
import { dirname as dirname10 } from "path";
|
|
55477
56421
|
var SettingsParseError = class extends Error {
|
|
55478
56422
|
constructor(path, cause) {
|
|
@@ -55486,10 +56430,10 @@ function stamp() {
|
|
|
55486
56430
|
return (/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-");
|
|
55487
56431
|
}
|
|
55488
56432
|
function writeTextWithBackup(path, content, marker = "ultra11y") {
|
|
55489
|
-
if (
|
|
56433
|
+
if (existsSync24(path) && readFileSync20(path, "utf8") === content) return { path, changed: false };
|
|
55490
56434
|
mkdirSync11(dirname10(path), { recursive: true });
|
|
55491
56435
|
let backup;
|
|
55492
|
-
if (
|
|
56436
|
+
if (existsSync24(path)) {
|
|
55493
56437
|
backup = `${path}.${marker}-backup-${stamp()}`;
|
|
55494
56438
|
copyFileSync2(path, backup);
|
|
55495
56439
|
}
|
|
@@ -55505,8 +56449,8 @@ function writeTextWithBackup(path, content, marker = "ultra11y") {
|
|
|
55505
56449
|
}
|
|
55506
56450
|
function editJsonFile(path, mutate) {
|
|
55507
56451
|
let root = {};
|
|
55508
|
-
if (
|
|
55509
|
-
const raw =
|
|
56452
|
+
if (existsSync24(path)) {
|
|
56453
|
+
const raw = readFileSync20(path, "utf8");
|
|
55510
56454
|
if (raw.trim()) {
|
|
55511
56455
|
let parsed;
|
|
55512
56456
|
try {
|
|
@@ -55522,13 +56466,13 @@ function editJsonFile(path, mutate) {
|
|
|
55522
56466
|
}
|
|
55523
56467
|
const before = JSON.stringify(root);
|
|
55524
56468
|
mutate(root);
|
|
55525
|
-
if (JSON.stringify(root) === before &&
|
|
56469
|
+
if (JSON.stringify(root) === before && existsSync24(path)) return { path, changed: false };
|
|
55526
56470
|
return writeTextWithBackup(path, `${JSON.stringify(root, null, 2)}
|
|
55527
56471
|
`);
|
|
55528
56472
|
}
|
|
55529
56473
|
function readJsonSafe(path) {
|
|
55530
56474
|
try {
|
|
55531
|
-
const parsed = JSON.parse(
|
|
56475
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
55532
56476
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
55533
56477
|
} catch {
|
|
55534
56478
|
return null;
|
|
@@ -55569,7 +56513,7 @@ function claudeCodeWired(settingsPath) {
|
|
|
55569
56513
|
}
|
|
55570
56514
|
|
|
55571
56515
|
// src/install/text-edit.ts
|
|
55572
|
-
import { existsSync as
|
|
56516
|
+
import { existsSync as existsSync25, readFileSync as readFileSync21, rmSync as rmSync5 } from "fs";
|
|
55573
56517
|
var BLOCK_BEGIN = "<!-- BEGIN ultra11y (managed by `ultra11y install --agents-md`; edit outside this block) -->";
|
|
55574
56518
|
var BLOCK_END = "<!-- END ultra11y -->";
|
|
55575
56519
|
var blockRe = () => new RegExp(`${escapeRe2(BLOCK_BEGIN)}[\\s\\S]*?${escapeRe2(BLOCK_END)}\\n?`);
|
|
@@ -55581,15 +56525,15 @@ function upsertManagedBlock(path, body2) {
|
|
|
55581
56525
|
${body2.trim()}
|
|
55582
56526
|
${BLOCK_END}
|
|
55583
56527
|
`;
|
|
55584
|
-
if (!
|
|
55585
|
-
const current =
|
|
56528
|
+
if (!existsSync25(path)) return writeTextWithBackup(path, block);
|
|
56529
|
+
const current = readFileSync21(path, "utf8");
|
|
55586
56530
|
if (blockRe().test(current)) return writeTextWithBackup(path, current.replace(blockRe(), block));
|
|
55587
56531
|
const sep8 = current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
55588
56532
|
return writeTextWithBackup(path, `${current}${sep8}${block}`);
|
|
55589
56533
|
}
|
|
55590
56534
|
function removeManagedBlock(path) {
|
|
55591
|
-
if (!
|
|
55592
|
-
const current =
|
|
56535
|
+
if (!existsSync25(path)) return { path, changed: false };
|
|
56536
|
+
const current = readFileSync21(path, "utf8");
|
|
55593
56537
|
if (!blockRe().test(current)) return { path, changed: false };
|
|
55594
56538
|
const rest = current.replace(blockRe(), "");
|
|
55595
56539
|
if (rest.trim() === "") {
|
|
@@ -55599,13 +56543,13 @@ function removeManagedBlock(path) {
|
|
|
55599
56543
|
return writeTextWithBackup(path, rest.replace(/\n+$/, "\n"));
|
|
55600
56544
|
}
|
|
55601
56545
|
function hasManagedBlock(path) {
|
|
55602
|
-
return
|
|
56546
|
+
return existsSync25(path) && blockRe().test(readFileSync21(path, "utf8"));
|
|
55603
56547
|
}
|
|
55604
56548
|
|
|
55605
56549
|
// src/install/agents-md.ts
|
|
55606
|
-
import { join as
|
|
56550
|
+
import { join as join38 } from "path";
|
|
55607
56551
|
function agentsMdPath(root) {
|
|
55608
|
-
return
|
|
56552
|
+
return join38(root, "AGENTS.md");
|
|
55609
56553
|
}
|
|
55610
56554
|
function agentsMdBlock(root) {
|
|
55611
56555
|
const e = engineInvocation(root);
|
|
@@ -55660,25 +56604,25 @@ function agentsMdWired(root) {
|
|
|
55660
56604
|
}
|
|
55661
56605
|
|
|
55662
56606
|
// src/install/codex.ts
|
|
55663
|
-
import { existsSync as
|
|
55664
|
-
import { join as
|
|
56607
|
+
import { existsSync as existsSync27, readFileSync as readFileSync24 } from "fs";
|
|
56608
|
+
import { join as join40 } from "path";
|
|
55665
56609
|
|
|
55666
56610
|
// src/install/paths.ts
|
|
55667
|
-
import { copyFileSync as copyFileSync3, cpSync, existsSync as
|
|
56611
|
+
import { copyFileSync as copyFileSync3, cpSync, existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync23, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
|
|
55668
56612
|
import { homedir as homedir2 } from "os";
|
|
55669
|
-
import { dirname as dirname11, join as
|
|
56613
|
+
import { dirname as dirname11, join as join39 } from "path";
|
|
55670
56614
|
function codexHome() {
|
|
55671
|
-
return process.env.CODEX_HOME ||
|
|
56615
|
+
return process.env.CODEX_HOME || join39(homedir2(), ".codex");
|
|
55672
56616
|
}
|
|
55673
56617
|
function opencodeConfigDir() {
|
|
55674
56618
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
55675
|
-
return
|
|
56619
|
+
return join39(xdg && xdg !== "" ? xdg : join39(homedir2(), ".config"), "opencode");
|
|
55676
56620
|
}
|
|
55677
56621
|
function claudeSettingsPath(project, cwd = process.cwd()) {
|
|
55678
|
-
return project ?
|
|
56622
|
+
return project ? join39(cwd, ".claude", "settings.json") : join39(homedir2(), ".claude", "settings.json");
|
|
55679
56623
|
}
|
|
55680
56624
|
function pinnedEnginePath() {
|
|
55681
|
-
return
|
|
56625
|
+
return join39(homedir2(), ".ultra11y", "bin", "ultra11y.mjs");
|
|
55682
56626
|
}
|
|
55683
56627
|
function installedCliCommand() {
|
|
55684
56628
|
const override = process.env.ULTRA11Y_BIN;
|
|
@@ -55693,7 +56637,7 @@ function installedCliCommand() {
|
|
|
55693
56637
|
if (source && !ephemeral) return `node ${JSON.stringify(source)}`;
|
|
55694
56638
|
const pin = pinnedEnginePath();
|
|
55695
56639
|
if (source) {
|
|
55696
|
-
mkdirSync12(
|
|
56640
|
+
mkdirSync12(join39(pin, ".."), { recursive: true });
|
|
55697
56641
|
copyFileSync3(source, pin);
|
|
55698
56642
|
}
|
|
55699
56643
|
return `node ${JSON.stringify(pin)}`;
|
|
@@ -55706,32 +56650,32 @@ function packageRoot() {
|
|
|
55706
56650
|
return null;
|
|
55707
56651
|
}
|
|
55708
56652
|
for (let i2 = 0; i2 < 6 && dir && dir !== "/"; i2++) {
|
|
55709
|
-
if (
|
|
56653
|
+
if (existsSync26(join39(dir, "skills", "review-a11y", "SKILL.md"))) return dir;
|
|
55710
56654
|
dir = dirname11(dir);
|
|
55711
56655
|
}
|
|
55712
56656
|
return null;
|
|
55713
56657
|
}
|
|
55714
56658
|
function bundledSkillsDir() {
|
|
55715
56659
|
const root = packageRoot();
|
|
55716
|
-
const p = root ?
|
|
55717
|
-
return p &&
|
|
56660
|
+
const p = root ? join39(root, "skills") : null;
|
|
56661
|
+
return p && existsSync26(p) ? p : null;
|
|
55718
56662
|
}
|
|
55719
56663
|
function bundledOpencodePlugin() {
|
|
55720
56664
|
const root = packageRoot();
|
|
55721
|
-
const p = root ?
|
|
55722
|
-
return p &&
|
|
56665
|
+
const p = root ? join39(root, ".opencode", "plugins", "ultra11y.js") : null;
|
|
56666
|
+
return p && existsSync26(p) ? p : null;
|
|
55723
56667
|
}
|
|
55724
56668
|
function copySkillsInto(source, dest) {
|
|
55725
56669
|
const out2 = [];
|
|
55726
56670
|
for (const name2 of ["ultra11y", "review-a11y"]) {
|
|
55727
|
-
const from =
|
|
55728
|
-
if (!
|
|
55729
|
-
const to =
|
|
55730
|
-
const same = ["SKILL.md",
|
|
55731
|
-
const a =
|
|
55732
|
-
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);
|
|
55733
56677
|
try {
|
|
55734
|
-
return
|
|
56678
|
+
return existsSync26(b) && statSync10(a).size === statSync10(b).size && readFileSync23(a).equals(readFileSync23(b));
|
|
55735
56679
|
} catch {
|
|
55736
56680
|
return false;
|
|
55737
56681
|
}
|
|
@@ -55781,7 +56725,7 @@ function featuresBody(content) {
|
|
|
55781
56725
|
function installCodex({ codexDir, command, skillsSource }) {
|
|
55782
56726
|
const reports = [];
|
|
55783
56727
|
reports.push(
|
|
55784
|
-
editJsonFile(
|
|
56728
|
+
editJsonFile(join40(codexDir, "hooks.json"), (root) => {
|
|
55785
56729
|
const hooks = root.hooks ??= {};
|
|
55786
56730
|
hooks.PreToolUse = [
|
|
55787
56731
|
...withoutOurs2(hooks.PreToolUse),
|
|
@@ -55789,11 +56733,11 @@ function installCodex({ codexDir, command, skillsSource }) {
|
|
|
55789
56733
|
];
|
|
55790
56734
|
})
|
|
55791
56735
|
);
|
|
55792
|
-
const configPath =
|
|
55793
|
-
const current =
|
|
56736
|
+
const configPath = join40(codexDir, "config.toml");
|
|
56737
|
+
const current = existsSync27(configPath) ? readFileSync24(configPath, "utf8") : "";
|
|
55794
56738
|
const edit = enableHooksFeature(current);
|
|
55795
56739
|
if (edit.changed) reports.push(writeTextWithBackup(configPath, edit.content));
|
|
55796
|
-
if (skillsSource) reports.push(...copySkillsInto(skillsSource,
|
|
56740
|
+
if (skillsSource) reports.push(...copySkillsInto(skillsSource, join40(codexDir, "skills")));
|
|
55797
56741
|
return {
|
|
55798
56742
|
reports,
|
|
55799
56743
|
guidance: [
|
|
@@ -55808,8 +56752,8 @@ function installCodex({ codexDir, command, skillsSource }) {
|
|
|
55808
56752
|
}
|
|
55809
56753
|
function uninstallCodex({ codexDir }) {
|
|
55810
56754
|
const reports = [];
|
|
55811
|
-
const hooksPath =
|
|
55812
|
-
if (
|
|
56755
|
+
const hooksPath = join40(codexDir, "hooks.json");
|
|
56756
|
+
if (existsSync27(hooksPath)) {
|
|
55813
56757
|
reports.push(
|
|
55814
56758
|
editJsonFile(hooksPath, (root) => {
|
|
55815
56759
|
const hooks = root.hooks;
|
|
@@ -55824,33 +56768,33 @@ function uninstallCodex({ codexDir }) {
|
|
|
55824
56768
|
return { reports, guidance: ["codex: `[features] hooks = true` was left enabled \u2014 other hooks may depend on it."] };
|
|
55825
56769
|
}
|
|
55826
56770
|
function codexWired(codexDir) {
|
|
55827
|
-
const root = readJsonSafe(
|
|
56771
|
+
const root = readJsonSafe(join40(codexDir, "hooks.json"));
|
|
55828
56772
|
const groups = root?.hooks?.PreToolUse;
|
|
55829
56773
|
if (!Array.isArray(groups)) return 0;
|
|
55830
56774
|
return groups.reduce((n, g) => n + (g.hooks ?? []).filter((h) => String(h.command ?? "").includes(CODEX_MARKER)).length, 0);
|
|
55831
56775
|
}
|
|
55832
56776
|
function codexHooksEnabled(codexDir) {
|
|
55833
|
-
const p =
|
|
55834
|
-
return
|
|
56777
|
+
const p = join40(codexDir, "config.toml");
|
|
56778
|
+
return existsSync27(p) && HOOKS_ON.test(featuresBody(readFileSync24(p, "utf8")));
|
|
55835
56779
|
}
|
|
55836
56780
|
|
|
55837
56781
|
// src/install/opencode.ts
|
|
55838
|
-
import { existsSync as
|
|
55839
|
-
import { join as
|
|
56782
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25, rmSync as rmSync6 } from "fs";
|
|
56783
|
+
import { join as join41 } from "path";
|
|
55840
56784
|
var OPENCODE_MARKER = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
55841
56785
|
function pluginTarget(configDir) {
|
|
55842
|
-
return
|
|
56786
|
+
return join41(configDir, "plugin", "ultra11y.js");
|
|
55843
56787
|
}
|
|
55844
56788
|
function installOpencode({ configDir, pluginSource, skillsSource, enginePath }) {
|
|
55845
56789
|
const target = pluginTarget(configDir);
|
|
55846
|
-
if (
|
|
56790
|
+
if (existsSync28(target) && !readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
|
|
55847
56791
|
throw new Error(`refusing to overwrite ${target}: it is not a file ultra11y wrote. Remove it manually if you want the plugin here.`);
|
|
55848
56792
|
}
|
|
55849
|
-
const reports = [writeTextWithBackup(target,
|
|
55850
|
-
if (enginePath &&
|
|
55851
|
-
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")));
|
|
55852
56796
|
}
|
|
55853
|
-
if (skillsSource) reports.push(...copySkillsInto(skillsSource,
|
|
56797
|
+
if (skillsSource) reports.push(...copySkillsInto(skillsSource, join41(configDir, "skills")));
|
|
55854
56798
|
return {
|
|
55855
56799
|
reports,
|
|
55856
56800
|
guidance: [
|
|
@@ -55863,13 +56807,13 @@ function installOpencode({ configDir, pluginSource, skillsSource, enginePath })
|
|
|
55863
56807
|
function uninstallOpencode({ configDir }) {
|
|
55864
56808
|
const reports = [];
|
|
55865
56809
|
const target = pluginTarget(configDir);
|
|
55866
|
-
if (
|
|
56810
|
+
if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) {
|
|
55867
56811
|
rmSync6(target, { force: true });
|
|
55868
|
-
rmSync6(
|
|
56812
|
+
rmSync6(join41(configDir, "plugin", "ultra11y.mjs"), { force: true });
|
|
55869
56813
|
reports.push({ path: target, changed: true });
|
|
55870
56814
|
}
|
|
55871
|
-
const configPath =
|
|
55872
|
-
if (
|
|
56815
|
+
const configPath = join41(configDir, "opencode.json");
|
|
56816
|
+
if (existsSync28(configPath)) {
|
|
55873
56817
|
reports.push(
|
|
55874
56818
|
editJsonFile(configPath, (root) => {
|
|
55875
56819
|
if (!Array.isArray(root.plugin)) return;
|
|
@@ -55883,9 +56827,9 @@ function uninstallOpencode({ configDir }) {
|
|
|
55883
56827
|
}
|
|
55884
56828
|
function opencodeWired(configDir) {
|
|
55885
56829
|
const target = pluginTarget(configDir);
|
|
55886
|
-
if (
|
|
56830
|
+
if (existsSync28(target) && readFileSync25(target, "utf8").includes(OPENCODE_MARKER)) return true;
|
|
55887
56831
|
try {
|
|
55888
|
-
const cfg = JSON.parse(
|
|
56832
|
+
const cfg = JSON.parse(readFileSync25(join41(configDir, "opencode.json"), "utf8"));
|
|
55889
56833
|
return Array.isArray(cfg.plugin) && cfg.plugin.some((p) => /^ultra11y(@|$)/.test(String(p)));
|
|
55890
56834
|
} catch {
|
|
55891
56835
|
return false;
|
|
@@ -55893,8 +56837,8 @@ function opencodeWired(configDir) {
|
|
|
55893
56837
|
}
|
|
55894
56838
|
|
|
55895
56839
|
// src/install/index.ts
|
|
55896
|
-
import { existsSync as
|
|
55897
|
-
import { join as
|
|
56840
|
+
import { existsSync as existsSync29 } from "fs";
|
|
56841
|
+
import { join as join42 } from "path";
|
|
55898
56842
|
var ALL_TARGETS = ["claude-code", "codex", "opencode"];
|
|
55899
56843
|
function parseTargets(flags2) {
|
|
55900
56844
|
const picked = /* @__PURE__ */ new Set();
|
|
@@ -55933,7 +56877,7 @@ function installForTargets(opts) {
|
|
|
55933
56877
|
const out3 = installCodex({ codexDir: codexHome(), command: cmd(), skillsSource: opts.skills === false ? null : bundledSkillsDir() });
|
|
55934
56878
|
r.reports.push(...out3.reports);
|
|
55935
56879
|
r.guidance.push(...out3.guidance);
|
|
55936
|
-
} else r.reports.push({ path:
|
|
56880
|
+
} else r.reports.push({ path: join42(codexHome(), "hooks.json"), changed: false });
|
|
55937
56881
|
break;
|
|
55938
56882
|
}
|
|
55939
56883
|
case "opencode": {
|
|
@@ -55942,18 +56886,18 @@ function installForTargets(opts) {
|
|
|
55942
56886
|
r.error = "opencode: this engine was not run from a package carrying .opencode/plugins/ultra11y.js \u2014 install ultra11y from npm and retry.";
|
|
55943
56887
|
break;
|
|
55944
56888
|
}
|
|
55945
|
-
const configDir = opts.project === true ?
|
|
56889
|
+
const configDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
|
|
55946
56890
|
if (!opts.dryRun) {
|
|
55947
|
-
const engine =
|
|
56891
|
+
const engine = existsSync29(pinnedEnginePath()) ? pinnedEnginePath() : join42(packageRoot() ?? "", "scripts", "ultra11y.mjs");
|
|
55948
56892
|
const out3 = installOpencode({
|
|
55949
56893
|
configDir,
|
|
55950
56894
|
pluginSource: source,
|
|
55951
56895
|
skillsSource: opts.skills === false ? null : bundledSkillsDir(),
|
|
55952
|
-
enginePath:
|
|
56896
|
+
enginePath: existsSync29(engine) ? engine : null
|
|
55953
56897
|
});
|
|
55954
56898
|
r.reports.push(...out3.reports);
|
|
55955
56899
|
r.guidance.push(...out3.guidance);
|
|
55956
|
-
} else r.reports.push({ path:
|
|
56900
|
+
} else r.reports.push({ path: join42(configDir, "plugin", "ultra11y.js"), changed: false });
|
|
55957
56901
|
break;
|
|
55958
56902
|
}
|
|
55959
56903
|
case "agents-md": {
|
|
@@ -55991,7 +56935,7 @@ function uninstallForTargets(opts) {
|
|
|
55991
56935
|
break;
|
|
55992
56936
|
}
|
|
55993
56937
|
case "opencode": {
|
|
55994
|
-
const out3 = uninstallOpencode({ configDir: opts.project === true ?
|
|
56938
|
+
const out3 = uninstallOpencode({ configDir: opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir() });
|
|
55995
56939
|
r.reports.push(...out3.reports);
|
|
55996
56940
|
r.guidance.push(...out3.guidance);
|
|
55997
56941
|
break;
|
|
@@ -56012,18 +56956,18 @@ function statusReport(opts = {}) {
|
|
|
56012
56956
|
const claudePath = claudeSettingsPath(opts.project === true, cwd);
|
|
56013
56957
|
const root = projectRoot(cwd);
|
|
56014
56958
|
const codexDir = codexHome();
|
|
56015
|
-
const ocDir = opts.project === true ?
|
|
56959
|
+
const ocDir = opts.project === true ? join42(cwd, ".opencode") : opencodeConfigDir();
|
|
56016
56960
|
const codexOn = codexWired(codexDir) > 0;
|
|
56017
56961
|
return [
|
|
56018
56962
|
{ target: "claude-code", wired: claudeCodeWired(claudePath) > 0, path: claudePath, note: "the plugin route wires this separately and does not show here" },
|
|
56019
56963
|
{
|
|
56020
56964
|
target: "codex",
|
|
56021
56965
|
wired: codexOn,
|
|
56022
|
-
path:
|
|
56966
|
+
path: join42(codexDir, "hooks.json"),
|
|
56023
56967
|
// A wired hook behind a disabled feature flag never fires and looks fine. Say so.
|
|
56024
56968
|
note: codexOn && !codexHooksEnabled(codexDir) ? "WIRED BUT INERT: `[features] hooks = true` is not set in config.toml" : void 0
|
|
56025
56969
|
},
|
|
56026
|
-
{ target: "opencode", wired: opencodeWired(ocDir), path:
|
|
56970
|
+
{ target: "opencode", wired: opencodeWired(ocDir), path: join42(ocDir, "plugin", "ultra11y.js") },
|
|
56027
56971
|
{ target: "agents-md", wired: agentsMdWired(root), path: agentsMdPath(root) }
|
|
56028
56972
|
];
|
|
56029
56973
|
}
|
|
@@ -56450,8 +57394,8 @@ function perPageTable(result, standard = CORE2, lang = "en") {
|
|
|
56450
57394
|
|
|
56451
57395
|
// src/evidence.ts
|
|
56452
57396
|
import { createHash as createHash6 } from "crypto";
|
|
56453
|
-
import { existsSync as
|
|
56454
|
-
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";
|
|
56455
57399
|
var DEFAULT_GEOMETRY = { pad: 24, minWidth: 320, minHeight: 120, maxWidth: 960, maxHeight: 540 };
|
|
56456
57400
|
var DEFAULT_CAPS = { perRule: 6, perPage: 12, total: 200 };
|
|
56457
57401
|
var HALO = { r: 255, g: 255, b: 255, a: 0.92 };
|
|
@@ -56468,7 +57412,7 @@ function pageIdOfSnapshot(file) {
|
|
|
56468
57412
|
}
|
|
56469
57413
|
function readJson3(file) {
|
|
56470
57414
|
try {
|
|
56471
|
-
return JSON.parse(
|
|
57415
|
+
return JSON.parse(readFileSync26(file, "utf8"));
|
|
56472
57416
|
} catch {
|
|
56473
57417
|
return void 0;
|
|
56474
57418
|
}
|
|
@@ -56484,21 +57428,21 @@ function resolveScale(img, viewport, boxes) {
|
|
|
56484
57428
|
}
|
|
56485
57429
|
function loadPageEvidence(root, pageId) {
|
|
56486
57430
|
const dir = snapshotDir(root, pageId);
|
|
56487
|
-
const domFile =
|
|
56488
|
-
const shotFile =
|
|
56489
|
-
if (!
|
|
56490
|
-
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" };
|
|
56491
57435
|
let dom;
|
|
56492
57436
|
let shot;
|
|
56493
57437
|
try {
|
|
56494
|
-
dom =
|
|
56495
|
-
shot =
|
|
57438
|
+
dom = readFileSync26(domFile, "utf8");
|
|
57439
|
+
shot = readFileSync26(shotFile);
|
|
56496
57440
|
} catch {
|
|
56497
57441
|
return { skip: "no-snapshot" };
|
|
56498
57442
|
}
|
|
56499
57443
|
const img = decodePng(shot);
|
|
56500
57444
|
if (!img) return { skip: "unreadable-image" };
|
|
56501
|
-
const digest = readJson3(
|
|
57445
|
+
const digest = readJson3(join43(dir, "boxes.json"));
|
|
56502
57446
|
if (!digest) return { skip: "no-boxes" };
|
|
56503
57447
|
const doc = parseHtml(dom, domFile);
|
|
56504
57448
|
const boxes = alignedBoxes(dom, digest, doc);
|
|
@@ -56514,7 +57458,7 @@ function loadPageEvidence(root, pageId) {
|
|
|
56514
57458
|
return { pageId, img, scale, viewport, ordinalOf, boxes, truncated: Boolean(digest.truncated) };
|
|
56515
57459
|
}
|
|
56516
57460
|
function metaOf(dir) {
|
|
56517
|
-
const raw = readJson3(
|
|
57461
|
+
const raw = readJson3(join43(dir, "meta.json"));
|
|
56518
57462
|
if (raw === void 0) return void 0;
|
|
56519
57463
|
const v = validateSnapshotMeta(raw);
|
|
56520
57464
|
return v.ok && v.meta ? v.meta : void 0;
|
|
@@ -56679,7 +57623,7 @@ function writeEvidence(result, opts) {
|
|
|
56679
57623
|
}
|
|
56680
57624
|
continue;
|
|
56681
57625
|
}
|
|
56682
|
-
const dir =
|
|
57626
|
+
const dir = join43(opts.outDir, "assets", pageId);
|
|
56683
57627
|
let wrote = false;
|
|
56684
57628
|
const perRule = /* @__PURE__ */ new Map();
|
|
56685
57629
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -56704,7 +57648,7 @@ function writeEvidence(result, opts) {
|
|
|
56704
57648
|
continue;
|
|
56705
57649
|
}
|
|
56706
57650
|
const name2 = cropName(f);
|
|
56707
|
-
const path =
|
|
57651
|
+
const path = join43(dir, name2);
|
|
56708
57652
|
try {
|
|
56709
57653
|
if (!wrote) {
|
|
56710
57654
|
mkdirSync13(dir, { recursive: true });
|
|
@@ -56742,8 +57686,8 @@ function writeEvidence(result, opts) {
|
|
|
56742
57686
|
}
|
|
56743
57687
|
|
|
56744
57688
|
// src/html-emit.ts
|
|
56745
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
56746
|
-
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";
|
|
56747
57691
|
|
|
56748
57692
|
// src/html.ts
|
|
56749
57693
|
var MARK4 = { C: "C", NC: "NC", NA: "\u2014", manual: "?" };
|
|
@@ -57305,7 +58249,7 @@ function inlineSize(path) {
|
|
|
57305
58249
|
}
|
|
57306
58250
|
function dataUri(path) {
|
|
57307
58251
|
try {
|
|
57308
|
-
return `data:image/png;base64,${
|
|
58252
|
+
return `data:image/png;base64,${readFileSync27(path).toString("base64")}`;
|
|
57309
58253
|
} catch {
|
|
57310
58254
|
return void 0;
|
|
57311
58255
|
}
|
|
@@ -57376,7 +58320,7 @@ function writeHtml(result, opts) {
|
|
|
57376
58320
|
return drawn;
|
|
57377
58321
|
};
|
|
57378
58322
|
const flat = opts.layout === "pages";
|
|
57379
|
-
const sheetDir = flat ? opts.outDir :
|
|
58323
|
+
const sheetDir = flat ? opts.outDir : join44(opts.outDir, "pages");
|
|
57380
58324
|
const up = flat ? "./" : "../";
|
|
57381
58325
|
const compositeName = `ultra11y-${stdKey}-${result.date}.html`;
|
|
57382
58326
|
const nav = flat ? [{ href: "./index.html", text: t2.pagesTitle }] : [
|
|
@@ -57395,13 +58339,13 @@ function writeHtml(result, opts) {
|
|
|
57395
58339
|
nav: nav.map((n) => ({ ...n, current: n.href === `./${compositeName}` }))
|
|
57396
58340
|
});
|
|
57397
58341
|
if (notices.length) composite.blocks.unshift({ kind: "note", tone: "warn", runs: noticeRuns(notices) });
|
|
57398
|
-
compositePath =
|
|
58342
|
+
compositePath = join44(opts.outDir, compositeName);
|
|
57399
58343
|
writeFileSync16(compositePath, renderHtmlDocument(composite));
|
|
57400
58344
|
}
|
|
57401
58345
|
const fileCrops = cropLookup(opts.evidence, lang, (_p, href) => href.replace(/^\.\//, up));
|
|
57402
58346
|
const indexNav = nav.map((n) => ({ ...n, current: n.href === "./index.html" }));
|
|
57403
58347
|
const index = flat ? pagesIndexDoc(result, { standard, lang, nav: indexNav, sheetHref: (id) => `./page-${id}.html` }) : indexDoc(result, { standard, lang, nav: indexNav, links: nav.filter((n) => n.href !== "./index.html") });
|
|
57404
|
-
const indexPath =
|
|
58348
|
+
const indexPath = join44(opts.outDir, "index.html");
|
|
57405
58349
|
writeFileSync16(indexPath, renderHtmlDocument(index));
|
|
57406
58350
|
const sheets = [];
|
|
57407
58351
|
if (opts.pages) {
|
|
@@ -57414,7 +58358,7 @@ function writeHtml(result, opts) {
|
|
|
57414
58358
|
];
|
|
57415
58359
|
if (!flat) {
|
|
57416
58360
|
writeFileSync16(
|
|
57417
|
-
|
|
58361
|
+
join44(sheetDir, "index.html"),
|
|
57418
58362
|
renderHtmlDocument(
|
|
57419
58363
|
pagesIndexDoc(result, {
|
|
57420
58364
|
standard,
|
|
@@ -57424,7 +58368,7 @@ function writeHtml(result, opts) {
|
|
|
57424
58368
|
})
|
|
57425
58369
|
)
|
|
57426
58370
|
);
|
|
57427
|
-
sheets.push(
|
|
58371
|
+
sheets.push(join44(sheetDir, "index.html"));
|
|
57428
58372
|
}
|
|
57429
58373
|
for (const p of derived) {
|
|
57430
58374
|
const doc = pageDoc(result, p, {
|
|
@@ -57437,7 +58381,7 @@ function writeHtml(result, opts) {
|
|
|
57437
58381
|
// beside the entry point, seen from wherever this sheet sits.
|
|
57438
58382
|
...opts.screenshots?.has(p.id) ? { screenshot: `${up}assets/${p.id}.png` } : {}
|
|
57439
58383
|
});
|
|
57440
|
-
const path =
|
|
58384
|
+
const path = join44(sheetDir, `page-${p.id}.html`);
|
|
57441
58385
|
writeFileSync16(path, renderHtmlDocument(doc));
|
|
57442
58386
|
sheets.push(path);
|
|
57443
58387
|
}
|
|
@@ -57458,8 +58402,8 @@ function noticeRuns(notices) {
|
|
|
57458
58402
|
|
|
57459
58403
|
// src/dev.ts
|
|
57460
58404
|
import { createServer } from "http";
|
|
57461
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
57462
|
-
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";
|
|
57463
58407
|
var DEV_DEFAULT_PORT = 4111;
|
|
57464
58408
|
function criterionLabel3(f, standard) {
|
|
57465
58409
|
if (isCore(standard)) return `WCAG ${f.criteriaId}`;
|
|
@@ -57681,13 +58625,13 @@ function auditCollected(root, payload) {
|
|
|
57681
58625
|
...payload.css ? { css: payload.css } : {},
|
|
57682
58626
|
...shot ? { screenshotBase64: shot } : {}
|
|
57683
58627
|
});
|
|
57684
|
-
return { ok: true, result: runAudit({ inputs: [
|
|
58628
|
+
return { ok: true, result: runAudit({ inputs: [join45(dir, "dom.html")] }) };
|
|
57685
58629
|
}
|
|
57686
58630
|
function projectPages(root) {
|
|
57687
58631
|
const snaps = readSnapshots(root);
|
|
57688
58632
|
if (!snaps.length) return { result: null, pages: [] };
|
|
57689
58633
|
const scope = pageScopesFrom(snaps);
|
|
57690
|
-
const result = runAudit({ inputs: [
|
|
58634
|
+
const result = runAudit({ inputs: [join45(root, ".ultra11y/pages")] });
|
|
57691
58635
|
result.scope.pages = scope;
|
|
57692
58636
|
attributePages(result, scope);
|
|
57693
58637
|
foldRecordedAdjudication(root, result);
|
|
@@ -57701,7 +58645,7 @@ function judgePages(result) {
|
|
|
57701
58645
|
function foldRecordedAdjudication(root, fresh) {
|
|
57702
58646
|
let prior;
|
|
57703
58647
|
try {
|
|
57704
|
-
prior = JSON.parse(
|
|
58648
|
+
prior = JSON.parse(readFileSync28(join45(root, "audits", "audit-latest.json"), "utf8"));
|
|
57705
58649
|
} catch {
|
|
57706
58650
|
return;
|
|
57707
58651
|
}
|
|
@@ -57816,7 +58760,7 @@ function startDevServer(opts) {
|
|
|
57816
58760
|
});
|
|
57817
58761
|
let auditPath;
|
|
57818
58762
|
if (applied.ok) {
|
|
57819
|
-
auditPath =
|
|
58763
|
+
auditPath = join45(opts.root, "audits", "audit-latest.json");
|
|
57820
58764
|
mkdirSync15(dirname12(auditPath), { recursive: true });
|
|
57821
58765
|
writeFileSync17(auditPath, `${JSON.stringify(applied.audit, null, 2)}
|
|
57822
58766
|
`);
|
|
@@ -57845,10 +58789,10 @@ function startDevServer(opts) {
|
|
|
57845
58789
|
}
|
|
57846
58790
|
res.writeHead(404, { "content-type": "text/plain" }).end("not found");
|
|
57847
58791
|
});
|
|
57848
|
-
return new Promise((
|
|
58792
|
+
return new Promise((resolve15, reject) => {
|
|
57849
58793
|
server.once("error", reject);
|
|
57850
58794
|
server.listen(opts.port, "127.0.0.1", () => {
|
|
57851
|
-
|
|
58795
|
+
resolve15({
|
|
57852
58796
|
port: server.address().port,
|
|
57853
58797
|
close: () => new Promise((r) => server.close(() => r()))
|
|
57854
58798
|
});
|
|
@@ -58132,11 +59076,11 @@ function e2eSetupPlan(runners, paths, lang = "en") {
|
|
|
58132
59076
|
}
|
|
58133
59077
|
|
|
58134
59078
|
// src/orchestrate.ts
|
|
58135
|
-
import { existsSync as
|
|
58136
|
-
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";
|
|
58137
59081
|
|
|
58138
59082
|
// src/orchestrate-templates.ts
|
|
58139
|
-
import { join as
|
|
59083
|
+
import { join as join46 } from "path";
|
|
58140
59084
|
var ONE_WRITER_FOOTER = `
|
|
58141
59085
|
## Return, don't write
|
|
58142
59086
|
|
|
@@ -58208,7 +59152,7 @@ var PHASE_SPECS = {
|
|
|
58208
59152
|
title: "Adjudicate",
|
|
58209
59153
|
schema: ADJUDICATE_SCHEMA,
|
|
58210
59154
|
description: (n) => `Adjudicate the ${n} residual judgment criterion(ia) of an ultra11y audit (fan-out, fail-closed fold)`,
|
|
58211
|
-
applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${
|
|
59155
|
+
applyHint: (engine, worklist, run2) => `node ${engine} verify --apply ${worklist} --in ${join46(run2, "audit-latest.json")} --out ${run2}`
|
|
58212
59156
|
},
|
|
58213
59157
|
"verify-report": {
|
|
58214
59158
|
role: "refuter",
|
|
@@ -58230,7 +59174,7 @@ function toBatches(ids, batchSize) {
|
|
|
58230
59174
|
}
|
|
58231
59175
|
function phaseWorkflowScript(ph, runAbs, engineAbs, batchSize) {
|
|
58232
59176
|
const spec = phaseSpec(ph.name);
|
|
58233
|
-
const scriptPath =
|
|
59177
|
+
const scriptPath = join46(runAbs, "orchestration", `${ph.name}.workflow.mjs`);
|
|
58234
59178
|
const meta2 = { name: `ultra11y-${ph.name}`, description: spec.description(ph.items), phases: [{ title: spec.title }] };
|
|
58235
59179
|
return [
|
|
58236
59180
|
`export const meta = ${JSON.stringify(meta2)}`,
|
|
@@ -58274,7 +59218,7 @@ function agentContracts(runAbs, engineAbs) {
|
|
|
58274
59218
|
|
|
58275
59219
|
You adjudicate the residual judgment criteria of an ultra11y audit \u2014 the ones the deterministic engine could not decide (alt-text relevance, link purpose in context, reading order\u2026). The ACTIVE STANDARD is recorded in the worklist's \`standard\` field: under a country standard (e.g. \`rgaa\`) the items are that standard's OWN criteria, each carrying its numbered tests \u2014 not WCAG success criteria.
|
|
58276
59220
|
|
|
58277
|
-
Worklist: \`${
|
|
59221
|
+
Worklist: \`${join46(runAbs, "ADJUDICATE.todo.json")}\` (an object with \`kind: "adjudication"\` and \`items[]\`). Handle ONLY the criteria whose \`criteriaId\` is named in your prompt (\`ITEMS=<id,\u2026>\`).
|
|
58278
59222
|
|
|
58279
59223
|
For EACH of your criteria:
|
|
58280
59224
|
|
|
@@ -58292,7 +59236,7 @@ ${footer}`,
|
|
|
58292
59236
|
|
|
58293
59237
|
You are an adversarial skeptic verifying the non-conformities of an ultra11y report. Your job is to try to REFUTE each claim: assume it is wrong until the source proves it.
|
|
58294
59238
|
|
|
58295
|
-
Worklist: \`${
|
|
59239
|
+
Worklist: \`${join46(runAbs, "VERIFY.todo.json")}\` (a JSON array; each entry has \`n\`, \`criteriaId\`, \`file\`, \`line\`, \`selector\`, \`claim\`). Handle ONLY the entries whose \`n\` is named in your prompt (\`ITEMS=<n,\u2026>\`).
|
|
58296
59240
|
|
|
58297
59241
|
For EACH of your entries:
|
|
58298
59242
|
|
|
@@ -58328,14 +59272,14 @@ ${status}
|
|
|
58328
59272
|
|
|
58329
59273
|
## The loop (play every role yourself, one item at a time)
|
|
58330
59274
|
|
|
58331
|
-
1. **Audit** (if not done): \`${engine} audit "<globs>" --graph --out ${runAbs}\` \u2192 \`${
|
|
58332
|
-
2. **Adjudicate the residual criteria** \u2014 \`${engine} verify --manual --in ${
|
|
58333
|
-
3. **Report**: \`${engine} report --in ${
|
|
58334
|
-
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>\`.
|
|
58335
59279
|
5. **Gate**: \`${engine} check --report <the report .md> --semantic\` must exit 0 before presenting anything.
|
|
58336
59280
|
6. **Fix & re-audit**: \`${engine} fix <globs> --write --iterate\`, hand-apply the judgment fixes, then loop from step 1 until the gate stays green.
|
|
58337
59281
|
|
|
58338
|
-
With subagents available, prefer the emitted workflows instead: \`orchestrate --run ${runAbs} --phase <p>\` then \`Workflow({ scriptPath: "${
|
|
59282
|
+
With subagents available, prefer the emitted workflows instead: \`orchestrate --run ${runAbs} --phase <p>\` then \`Workflow({ scriptPath: "${join46(runAbs, "orchestration", "<p>.workflow.mjs")}" })\` \u2014 you stay the sole writer either way.
|
|
58339
59283
|
`;
|
|
58340
59284
|
}
|
|
58341
59285
|
|
|
@@ -58344,13 +59288,13 @@ var PHASES = ["adjudicate", "verify-report"];
|
|
|
58344
59288
|
var SMALL_WORKLIST = 3;
|
|
58345
59289
|
var BATCH_SIZE2 = 8;
|
|
58346
59290
|
function listPhases(runDir, engineAbs) {
|
|
58347
|
-
const run2 =
|
|
58348
|
-
const adjPath =
|
|
59291
|
+
const run2 = resolve12(runDir);
|
|
59292
|
+
const adjPath = join47(run2, "ADJUDICATE.todo.json");
|
|
58349
59293
|
let adjIds = [];
|
|
58350
59294
|
let adjReady = false;
|
|
58351
|
-
if (
|
|
59295
|
+
if (existsSync31(adjPath)) {
|
|
58352
59296
|
try {
|
|
58353
|
-
const f = JSON.parse(
|
|
59297
|
+
const f = JSON.parse(readFileSync29(adjPath, "utf8"));
|
|
58354
59298
|
if (f && f.kind === "adjudication" && Array.isArray(f.items)) {
|
|
58355
59299
|
adjReady = true;
|
|
58356
59300
|
adjIds = f.items.map((i2) => i2.criteriaId);
|
|
@@ -58358,12 +59302,12 @@ function listPhases(runDir, engineAbs) {
|
|
|
58358
59302
|
} catch {
|
|
58359
59303
|
}
|
|
58360
59304
|
}
|
|
58361
|
-
const verPath =
|
|
59305
|
+
const verPath = join47(run2, "VERIFY.todo.json");
|
|
58362
59306
|
let verIds = [];
|
|
58363
59307
|
let verReady = false;
|
|
58364
|
-
if (
|
|
59308
|
+
if (existsSync31(verPath)) {
|
|
58365
59309
|
try {
|
|
58366
|
-
const items = JSON.parse(
|
|
59310
|
+
const items = JSON.parse(readFileSync29(verPath, "utf8"));
|
|
58367
59311
|
if (Array.isArray(items)) {
|
|
58368
59312
|
verReady = true;
|
|
58369
59313
|
verIds = items.map((i2) => String(i2.n));
|
|
@@ -58378,7 +59322,7 @@ function listPhases(runDir, engineAbs) {
|
|
|
58378
59322
|
worklist: adjPath,
|
|
58379
59323
|
items: adjIds.length,
|
|
58380
59324
|
ids: adjIds,
|
|
58381
|
-
prerequisite: `node ${engineAbs} verify --manual --in ${
|
|
59325
|
+
prerequisite: `node ${engineAbs} verify --manual --in ${join47(run2, "audit-latest.json")} --out ${run2}`
|
|
58382
59326
|
},
|
|
58383
59327
|
{
|
|
58384
59328
|
name: "verify-report",
|
|
@@ -58391,8 +59335,8 @@ function listPhases(runDir, engineAbs) {
|
|
|
58391
59335
|
];
|
|
58392
59336
|
}
|
|
58393
59337
|
function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
58394
|
-
const run2 =
|
|
58395
|
-
if (!
|
|
59338
|
+
const run2 = resolve12(runDir);
|
|
59339
|
+
if (!existsSync31(run2)) {
|
|
58396
59340
|
return { exitCode: 2, written: [], notices: [], errors: [`run dir not found: ${run2}`], phases: [] };
|
|
58397
59341
|
}
|
|
58398
59342
|
const phases = listPhases(run2, engineAbs);
|
|
@@ -58419,22 +59363,22 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58419
59363
|
}
|
|
58420
59364
|
selected = [ph];
|
|
58421
59365
|
}
|
|
58422
|
-
const orchDir =
|
|
58423
|
-
const agentsDir =
|
|
58424
|
-
mkdirSync16(
|
|
59366
|
+
const orchDir = join47(run2, "orchestration");
|
|
59367
|
+
const agentsDir = join47(orchDir, "agents");
|
|
59368
|
+
mkdirSync16(join47(orchDir, "out"), { recursive: true });
|
|
58425
59369
|
mkdirSync16(agentsDir, { recursive: true });
|
|
58426
59370
|
const written = [];
|
|
58427
59371
|
const notices = [];
|
|
58428
59372
|
for (const [name2, content] of Object.entries(agentContracts(run2, engineAbs))) {
|
|
58429
|
-
const p =
|
|
59373
|
+
const p = join47(agentsDir, `${name2}.md`);
|
|
58430
59374
|
writeFileSync18(p, content);
|
|
58431
59375
|
written.push(p);
|
|
58432
59376
|
}
|
|
58433
59377
|
const emitted = new Set(opts.eco ? [] : selected.filter((p) => p.items > 0).map((p) => p.name));
|
|
58434
59378
|
for (const ph of phases) {
|
|
58435
59379
|
if (emitted.has(ph.name)) continue;
|
|
58436
|
-
const stale =
|
|
58437
|
-
if (
|
|
59380
|
+
const stale = join47(orchDir, `${ph.name}.workflow.mjs`);
|
|
59381
|
+
if (existsSync31(stale)) {
|
|
58438
59382
|
rmSync7(stale, { force: true });
|
|
58439
59383
|
notices.push(`phase "${ph.name}": stale workflow removed \u2014 its worklist ${ph.ready ? "is now empty" : "no longer exists"}.`);
|
|
58440
59384
|
}
|
|
@@ -58448,12 +59392,12 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58448
59392
|
if (ph.items <= SMALL_WORKLIST) {
|
|
58449
59393
|
notices.push(`phase "${ph.name}": only ${ph.items} item(s) \u2014 the sequential --eco path is equivalent and cheaper.`);
|
|
58450
59394
|
}
|
|
58451
|
-
const p =
|
|
59395
|
+
const p = join47(orchDir, `${ph.name}.workflow.mjs`);
|
|
58452
59396
|
writeFileSync18(p, phaseWorkflowScript(ph, run2, engineAbs, BATCH_SIZE2));
|
|
58453
59397
|
written.push(p);
|
|
58454
59398
|
}
|
|
58455
59399
|
}
|
|
58456
|
-
const rb =
|
|
59400
|
+
const rb = join47(orchDir, "RUNBOOK.md");
|
|
58457
59401
|
writeFileSync18(rb, runbookMd(phases, run2, engineAbs));
|
|
58458
59402
|
written.push(rb);
|
|
58459
59403
|
return { exitCode: 0, written, notices, errors: [], phases };
|
|
@@ -58463,8 +59407,8 @@ function orchestrateRun(runDir, engineAbs, opts = {}) {
|
|
|
58463
59407
|
import { createInterface as createInterface2 } from "readline";
|
|
58464
59408
|
|
|
58465
59409
|
// src/mcp/handlers.ts
|
|
58466
|
-
import { existsSync as
|
|
58467
|
-
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";
|
|
58468
59412
|
|
|
58469
59413
|
// src/project-lock.ts
|
|
58470
59414
|
var chains = /* @__PURE__ */ new Map();
|
|
@@ -58511,8 +59455,8 @@ function positive(v, key) {
|
|
|
58511
59455
|
function requiredCwd(args2, defaults) {
|
|
58512
59456
|
const cwd = str2(args2.cwd) ?? defaults.defaultCwd;
|
|
58513
59457
|
if (!cwd) throw new ToolError("`cwd` is required: an absolute path to the project root.");
|
|
58514
|
-
const abs =
|
|
58515
|
-
if (!
|
|
59458
|
+
const abs = resolve13(cwd);
|
|
59459
|
+
if (!existsSync32(abs)) throw new ToolError(`project root not found: ${abs}`);
|
|
58516
59460
|
if (!statSync12(abs).isDirectory()) throw new ToolError(`\`cwd\` is not a directory: ${abs}`);
|
|
58517
59461
|
return abs;
|
|
58518
59462
|
}
|
|
@@ -58578,7 +59522,7 @@ function audit(args2, cwd) {
|
|
|
58578
59522
|
process.chdir(cwd);
|
|
58579
59523
|
const inputs = strArray2(args2.globs) ?? DEFAULT_GLOBS;
|
|
58580
59524
|
const scopedToDiff = bool(args2.changed) || bool(args2.staged) || str2(args2.since) !== void 0;
|
|
58581
|
-
const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff &&
|
|
59525
|
+
const extra = [CAPTURES_DIR, PAGES_DIR].filter((d) => !scopedToDiff && existsSync32(d) && !inputs.includes(d));
|
|
58582
59526
|
const result = runAudit({
|
|
58583
59527
|
inputs: [...inputs, ...extra],
|
|
58584
59528
|
include: strArray2(args2.include),
|
|
@@ -58697,13 +59641,13 @@ function handlePackCheck(args2, cwd) {
|
|
|
58697
59641
|
return { cwd, ...res };
|
|
58698
59642
|
}
|
|
58699
59643
|
function handleSampleCheck(cwd, standard) {
|
|
58700
|
-
const file =
|
|
58701
|
-
if (!
|
|
59644
|
+
const file = join48(cwd, ".ultra11yrc.json");
|
|
59645
|
+
if (!existsSync32(file)) {
|
|
58702
59646
|
throw new ToolError(`no .ultra11yrc.json at ${cwd} \u2014 a page sample must be declared before it can be linted.`);
|
|
58703
59647
|
}
|
|
58704
59648
|
let raw;
|
|
58705
59649
|
try {
|
|
58706
|
-
raw = JSON.parse(
|
|
59650
|
+
raw = JSON.parse(readFileSync30(file, "utf8"));
|
|
58707
59651
|
} catch (e) {
|
|
58708
59652
|
throw new ToolError(`.ultra11yrc.json is not valid JSON: ${e.message}`);
|
|
58709
59653
|
}
|
|
@@ -58794,10 +59738,10 @@ function handleInit(args2, cwd) {
|
|
|
58794
59738
|
throw new ToolError(`\`fail_on\` must be one of: error, warning, notice (got "${failOnRaw}")`);
|
|
58795
59739
|
}
|
|
58796
59740
|
const failOn = failOnRaw;
|
|
58797
|
-
const enginePath =
|
|
59741
|
+
const enginePath = resolve13("scripts/ultra11y.mjs");
|
|
58798
59742
|
const written = [];
|
|
58799
59743
|
if (bool(args2.hook)) written.push(writeHook(cwd, enginePath, failOn));
|
|
58800
|
-
if (bool(args2.ci)) written.push(
|
|
59744
|
+
if (bool(args2.ci)) written.push(join48(cwd, ".github/workflows/a11y.yml"));
|
|
58801
59745
|
if (!written.length) {
|
|
58802
59746
|
throw new ToolError("nothing to install \u2014 pass hook:true and/or ci:true.");
|
|
58803
59747
|
}
|
|
@@ -58817,8 +59761,8 @@ function reportText(args2, tool) {
|
|
|
58817
59761
|
if (inline) return inline;
|
|
58818
59762
|
if (!file) throw new ToolError(`\`report_text\` is required \u2014 the report markdown for ultra11y_${tool} to work on.`);
|
|
58819
59763
|
if (!isAbsolute3(file)) throw new ToolError("`report_file` must be an absolute path.");
|
|
58820
|
-
if (!
|
|
58821
|
-
return
|
|
59764
|
+
if (!existsSync32(file)) throw new ToolError(`report file not found: ${file}`);
|
|
59765
|
+
return readFileSync30(file, "utf8");
|
|
58822
59766
|
}
|
|
58823
59767
|
function handleCriteria(args2) {
|
|
58824
59768
|
const lang = langOf(args2);
|
|
@@ -58833,7 +59777,7 @@ function handleCriteria(args2) {
|
|
|
58833
59777
|
function handleRead(args2, cwd) {
|
|
58834
59778
|
const raw = str2(args2.path);
|
|
58835
59779
|
if (!raw) throw new ToolError("`path` is required \u2014 relative to the project root, or an absolute path inside it.");
|
|
58836
|
-
const target = isAbsolute3(raw) ? raw :
|
|
59780
|
+
const target = isAbsolute3(raw) ? raw : join48(cwd, raw);
|
|
58837
59781
|
let real;
|
|
58838
59782
|
try {
|
|
58839
59783
|
real = realpathSync4(target);
|
|
@@ -58847,7 +59791,7 @@ function handleRead(args2, cwd) {
|
|
|
58847
59791
|
const st = statSync12(real);
|
|
58848
59792
|
if (!st.isFile()) throw new ToolError(`not a file: ${raw}`);
|
|
58849
59793
|
if (st.size > MAX_READ_BYTES) throw new ToolError(`file is too large to read (${st.size} bytes): ${raw}`);
|
|
58850
|
-
const lines =
|
|
59794
|
+
const lines = readFileSync30(real, "utf8").split("\n");
|
|
58851
59795
|
const total = lines.length;
|
|
58852
59796
|
const start2 = Math.max(1, Math.floor(num2(args2.start_line) ?? 1));
|
|
58853
59797
|
if (start2 > total) throw new ToolError(`start_line ${start2} is past the end of the file (${total} lines).`);
|
|
@@ -59393,25 +60337,25 @@ function str3(v) {
|
|
|
59393
60337
|
var DECLARED = new Set([...TOOLS2, ...WRITE_TOOLS].map((t2) => t2.name));
|
|
59394
60338
|
|
|
59395
60339
|
// src/mcp/resources.ts
|
|
59396
|
-
import { existsSync as
|
|
59397
|
-
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";
|
|
59398
60342
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
59399
60343
|
var SKILL_NAME = "ultra11y";
|
|
59400
60344
|
var URI_SCHEME = "skill://";
|
|
59401
60345
|
function resolveSkillRoot(moduleDir) {
|
|
59402
60346
|
const here = moduleDir ?? dirname13(fileURLToPath4(import.meta.url));
|
|
59403
|
-
const candidates2 = [
|
|
59404
|
-
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")));
|
|
59405
60349
|
}
|
|
59406
60350
|
function listResources(moduleDir) {
|
|
59407
60351
|
const root = resolveSkillRoot(moduleDir);
|
|
59408
60352
|
if (!root) return [];
|
|
59409
60353
|
const out2 = [describe(root, "SKILL.md", `${SKILL_NAME}: the skill`)];
|
|
59410
|
-
const refDir =
|
|
59411
|
-
if (!
|
|
60354
|
+
const refDir = join49(root, "references");
|
|
60355
|
+
if (!existsSync33(refDir)) return out2;
|
|
59412
60356
|
for (const file of readdirSync6(refDir).sort()) {
|
|
59413
60357
|
if (!file.endsWith(".md")) continue;
|
|
59414
|
-
out2.push(describe(root,
|
|
60358
|
+
out2.push(describe(root, join49("references", file), `${SKILL_NAME} reference: ${basename3(file, ".md")}`));
|
|
59415
60359
|
}
|
|
59416
60360
|
return out2;
|
|
59417
60361
|
}
|
|
@@ -59423,7 +60367,7 @@ function readResource(uri, moduleDir) {
|
|
|
59423
60367
|
if (!root) throw new ResourceError("no skill payload found next to this build \u2014 nothing to read");
|
|
59424
60368
|
const rel2 = uri.slice(URI_SCHEME.length);
|
|
59425
60369
|
if (!rel2) throw new ResourceError("empty resource path");
|
|
59426
|
-
const target =
|
|
60370
|
+
const target = resolve14(root, rel2);
|
|
59427
60371
|
const rootReal = realpathSync5(root);
|
|
59428
60372
|
let targetReal;
|
|
59429
60373
|
try {
|
|
@@ -59435,7 +60379,7 @@ function readResource(uri, moduleDir) {
|
|
|
59435
60379
|
throw new ResourceError(`resource path escapes the skill root: ${uri}`);
|
|
59436
60380
|
}
|
|
59437
60381
|
if (!statSync13(targetReal).isFile()) throw new ResourceError(`not a file: ${uri}`);
|
|
59438
|
-
return { uri, mimeType: "text/markdown", text:
|
|
60382
|
+
return { uri, mimeType: "text/markdown", text: readFileSync31(targetReal, "utf8") };
|
|
59439
60383
|
}
|
|
59440
60384
|
var ResourceError = class extends Error {
|
|
59441
60385
|
};
|
|
@@ -59446,14 +60390,14 @@ function describe(root, rel2, fallbackTitle) {
|
|
|
59446
60390
|
title: fallbackTitle,
|
|
59447
60391
|
mimeType: "text/markdown"
|
|
59448
60392
|
};
|
|
59449
|
-
const summary = firstProse(
|
|
60393
|
+
const summary = firstProse(join49(root, rel2));
|
|
59450
60394
|
if (summary) decl.description = summary;
|
|
59451
60395
|
return decl;
|
|
59452
60396
|
}
|
|
59453
60397
|
function firstProse(file) {
|
|
59454
60398
|
let text;
|
|
59455
60399
|
try {
|
|
59456
|
-
text =
|
|
60400
|
+
text = readFileSync31(file, "utf8");
|
|
59457
60401
|
} catch {
|
|
59458
60402
|
return void 0;
|
|
59459
60403
|
}
|
|
@@ -59704,14 +60648,14 @@ function startHttpServer(opts = {}) {
|
|
|
59704
60648
|
server.requestTimeout = 0;
|
|
59705
60649
|
server.headersTimeout = 6e4;
|
|
59706
60650
|
server.keepAliveTimeout = 12e4;
|
|
59707
|
-
return new Promise((
|
|
60651
|
+
return new Promise((resolve15, reject) => {
|
|
59708
60652
|
server.once("error", reject);
|
|
59709
60653
|
server.listen(opts.port ?? 0, bind, () => {
|
|
59710
60654
|
server.removeListener("error", reject);
|
|
59711
60655
|
const addr2 = server.address();
|
|
59712
60656
|
const port = typeof addr2 === "object" && addr2 ? addr2.port : opts.port ?? 0;
|
|
59713
60657
|
const host = bind.includes(":") ? `[${bind}]` : bind;
|
|
59714
|
-
|
|
60658
|
+
resolve15({
|
|
59715
60659
|
server,
|
|
59716
60660
|
port,
|
|
59717
60661
|
url: `http://${host}:${port}${MCP_PATH}`,
|
|
@@ -59820,7 +60764,7 @@ function sendJson(res, status, body2, origin, extra = {}) {
|
|
|
59820
60764
|
}
|
|
59821
60765
|
var DRAIN_LIMIT = MAX_BODY_BYTES * 8;
|
|
59822
60766
|
function readBody2(req) {
|
|
59823
|
-
return new Promise((
|
|
60767
|
+
return new Promise((resolve15, reject) => {
|
|
59824
60768
|
const chunks = [];
|
|
59825
60769
|
let size = 0;
|
|
59826
60770
|
let over = false;
|
|
@@ -59844,7 +60788,7 @@ function readBody2(req) {
|
|
|
59844
60788
|
});
|
|
59845
60789
|
req.on("end", () => {
|
|
59846
60790
|
if (over) reject(new Error("too large"));
|
|
59847
|
-
else
|
|
60791
|
+
else resolve15(Buffer.concat(chunks).toString("utf8"));
|
|
59848
60792
|
});
|
|
59849
60793
|
req.on("error", reject);
|
|
59850
60794
|
req.on("aborted", () => reject(new Error("client aborted the request")));
|
|
@@ -60575,9 +61519,9 @@ async function cmdAudit(p) {
|
|
|
60575
61519
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
60576
61520
|
const capturesDir = capturesFlag ?? CAPTURES_DIR;
|
|
60577
61521
|
const scopedToDiff = p.flags.changed === true || p.flags.staged === true || since !== void 0;
|
|
60578
|
-
const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 ||
|
|
61522
|
+
const capturesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && (capturesFlag !== void 0 || existsSync34(capturesDir));
|
|
60579
61523
|
const useCaptures = capturesWanted && !scopedToDiff && !inputs.includes(capturesDir);
|
|
60580
|
-
const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") &&
|
|
61524
|
+
const pagesWanted = p.flags["no-captures"] !== true && !inputs.includes("-") && existsSync34(PAGES_DIR);
|
|
60581
61525
|
const usePages = pagesWanted && !scopedToDiff && !inputs.includes(PAGES_DIR);
|
|
60582
61526
|
const pagesInScope = pagesWanted && !scopedToDiff;
|
|
60583
61527
|
const auditInputs = [...inputs, ...useCaptures ? [capturesDir] : [], ...usePages ? [PAGES_DIR] : []];
|
|
@@ -60619,7 +61563,7 @@ async function cmdAudit(p) {
|
|
|
60619
61563
|
if (typeof p.flags.out === "string") {
|
|
60620
61564
|
const out2 = p.flags.out;
|
|
60621
61565
|
const asFile = out2.toLowerCase().endsWith(".json");
|
|
60622
|
-
const target = asFile ? out2 :
|
|
61566
|
+
const target = asFile ? out2 : join50(out2, "audit-latest.json");
|
|
60623
61567
|
try {
|
|
60624
61568
|
mkdirSync17(asFile ? dirname14(out2) : out2, { recursive: true });
|
|
60625
61569
|
writeFileSync19(target, JSON.stringify(result, null, 2) + "\n");
|
|
@@ -60641,7 +61585,7 @@ async function cmdAudit(p) {
|
|
|
60641
61585
|
const baselineFlag = p.flags.baseline;
|
|
60642
61586
|
if (typeof baselineFlag === "string" && baselineFlag) {
|
|
60643
61587
|
let baseline = null;
|
|
60644
|
-
if (
|
|
61588
|
+
if (existsSync34(baselineFlag)) {
|
|
60645
61589
|
try {
|
|
60646
61590
|
const parsed = JSON.parse(readText(baselineFlag));
|
|
60647
61591
|
if (isCurrentAudit(parsed)) baseline = parsed;
|
|
@@ -60695,11 +61639,11 @@ async function cmdDev(p) {
|
|
|
60695
61639
|
return 2;
|
|
60696
61640
|
}
|
|
60697
61641
|
if (p.flags.next === true) {
|
|
60698
|
-
const dir =
|
|
61642
|
+
const dir = join50(root, ".ultra11y", "next");
|
|
60699
61643
|
const rel2 = ".ultra11y/next/overlay.jsx";
|
|
60700
61644
|
try {
|
|
60701
61645
|
mkdirSync17(dir, { recursive: true });
|
|
60702
|
-
writeFileSync19(
|
|
61646
|
+
writeFileSync19(join50(dir, "overlay.jsx"), nextOverlayComponent(port));
|
|
60703
61647
|
} catch (e) {
|
|
60704
61648
|
console.error(`ultra11y dev: could not write ${rel2}: ${e instanceof Error ? e.message : String(e)}`);
|
|
60705
61649
|
return 1;
|
|
@@ -60730,9 +61674,9 @@ async function cmdDev(p) {
|
|
|
60730
61674
|
}
|
|
60731
61675
|
console.error(fr ? `ultra11y dev : tableau de bord sur http://127.0.0.1:${server.port}` : `ultra11y dev: dashboard on http://127.0.0.1:${server.port}`);
|
|
60732
61676
|
console.error(fr ? "Boucle locale uniquement (l'outil \xE9crit des fichiers). Ctrl-C pour arr\xEAter." : "Loopback only (the tool writes files). Ctrl-C to stop.");
|
|
60733
|
-
await new Promise((
|
|
61677
|
+
await new Promise((resolve15) => {
|
|
60734
61678
|
const stop2 = () => {
|
|
60735
|
-
void server.close().then(
|
|
61679
|
+
void server.close().then(resolve15);
|
|
60736
61680
|
};
|
|
60737
61681
|
process.once("SIGINT", stop2);
|
|
60738
61682
|
process.once("SIGTERM", stop2);
|
|
@@ -60748,7 +61692,7 @@ async function cmdPagesDiscover(p) {
|
|
|
60748
61692
|
const snaps = sampleFromSnapshots(readSnapshots(root));
|
|
60749
61693
|
if (!snaps.length) {
|
|
60750
61694
|
console.error(
|
|
60751
|
-
lang === "fr" ? `ultra11y pages discover : aucun instantan\xE9 sous ${
|
|
61695
|
+
lang === "fr" ? `ultra11y pages discover : aucun instantan\xE9 sous ${join50(root, PAGES_DIR)} \u2014 lancez d'abord vos tests E2E avec checkA11y, ou \`scan --sample\`.` : `ultra11y pages discover: no snapshot under ${join50(root, PAGES_DIR)} \u2014 run your E2E tests with checkA11y first, or \`scan --sample\`.`
|
|
60752
61696
|
);
|
|
60753
61697
|
return 1;
|
|
60754
61698
|
}
|
|
@@ -60828,9 +61772,9 @@ ${found} page(s) discovered, ${merged.added.length} new. Nothing was written \u2
|
|
|
60828
61772
|
}
|
|
60829
61773
|
return 0;
|
|
60830
61774
|
}
|
|
60831
|
-
const file =
|
|
61775
|
+
const file = join50(process.cwd(), ".ultra11yrc.json");
|
|
60832
61776
|
let doc = {};
|
|
60833
|
-
if (
|
|
61777
|
+
if (existsSync34(file)) {
|
|
60834
61778
|
try {
|
|
60835
61779
|
doc = JSON.parse(readText(file));
|
|
60836
61780
|
} catch {
|
|
@@ -60899,27 +61843,27 @@ async function cmdPages(p) {
|
|
|
60899
61843
|
const derived = derivePages(result, scope);
|
|
60900
61844
|
const split = p.flags.split === "page";
|
|
60901
61845
|
const outDir = typeof p.flags.out === "string" && p.flags.out ? p.flags.out : void 0;
|
|
60902
|
-
const shotOf = (id) =>
|
|
61846
|
+
const shotOf = (id) => join50(PAGES_DIR, id, "screen.png");
|
|
60903
61847
|
const shotsRelative = (fileDir) => {
|
|
60904
61848
|
const m = /* @__PURE__ */ new Map();
|
|
60905
61849
|
for (const pg of derived) {
|
|
60906
|
-
if (
|
|
61850
|
+
if (existsSync34(shotOf(pg.id))) m.set(pg.id, relative5(fileDir, shotOf(pg.id)).split("\\").join("/"));
|
|
60907
61851
|
}
|
|
60908
61852
|
return m;
|
|
60909
61853
|
};
|
|
60910
61854
|
const shotPaths = (pages) => {
|
|
60911
61855
|
const m = /* @__PURE__ */ new Map();
|
|
60912
|
-
for (const pg of pages) if (
|
|
61856
|
+
for (const pg of pages) if (existsSync34(shotOf(pg.id))) m.set(pg.id, shotOf(pg.id));
|
|
60913
61857
|
return m;
|
|
60914
61858
|
};
|
|
60915
61859
|
const shotsCopiedInto = (dir) => {
|
|
60916
61860
|
const m = /* @__PURE__ */ new Map();
|
|
60917
|
-
const assets =
|
|
61861
|
+
const assets = join50(dir, "assets");
|
|
60918
61862
|
for (const pg of derived) {
|
|
60919
61863
|
const src = shotOf(pg.id);
|
|
60920
|
-
if (!
|
|
61864
|
+
if (!existsSync34(src)) continue;
|
|
60921
61865
|
mkdirSync17(assets, { recursive: true });
|
|
60922
|
-
copyFileSync4(src,
|
|
61866
|
+
copyFileSync4(src, join50(assets, `${pg.id}.png`));
|
|
60923
61867
|
m.set(pg.id, `./assets/${pg.id}.png`);
|
|
60924
61868
|
}
|
|
60925
61869
|
return m;
|
|
@@ -60945,7 +61889,7 @@ async function cmdPages(p) {
|
|
|
60945
61889
|
if (manifest) {
|
|
60946
61890
|
const total = evidenceNotice(manifest, null, lang);
|
|
60947
61891
|
console.error(
|
|
60948
|
-
lang === "fr" ? `ultra11y : ${manifest.totals.imaged} vignette(s) \xE9crite(s) dans ${
|
|
61892
|
+
lang === "fr" ? `ultra11y : ${manifest.totals.imaged} vignette(s) \xE9crite(s) dans ${join50(outDir, "assets")}.` : `ultra11y: ${manifest.totals.imaged} crop(s) written to ${join50(outDir, "assets")}.`
|
|
60949
61893
|
);
|
|
60950
61894
|
for (const line of total) console.error(line);
|
|
60951
61895
|
}
|
|
@@ -60954,7 +61898,7 @@ async function cmdPages(p) {
|
|
|
60954
61898
|
...manifest ? { evidenceNotice: (id) => evidenceNotice(manifest, id, lang) } : {}
|
|
60955
61899
|
};
|
|
60956
61900
|
if (!split) {
|
|
60957
|
-
const file =
|
|
61901
|
+
const file = join50(outDir, `pages-${result.date}.md`);
|
|
60958
61902
|
writeFileSync19(file, `${renderPagesDocument(result, derived, { standard, lang, screenshots: shotsCopiedInto(outDir), ...evidenceOpts })}
|
|
60959
61903
|
`);
|
|
60960
61904
|
const html2 = wantHtml ? emitHtml(result, {
|
|
@@ -60974,10 +61918,10 @@ async function cmdPages(p) {
|
|
|
60974
61918
|
const sheet = (id) => `page-${id}.md`;
|
|
60975
61919
|
const hrefs = new Map(derived.map((pg) => [pg.id, `./${sheet(pg.id)}`]));
|
|
60976
61920
|
for (const pg of derived) {
|
|
60977
|
-
writeFileSync19(
|
|
61921
|
+
writeFileSync19(join50(outDir, sheet(pg.id)), `${renderPageDocument(result, pg, { standard, lang, screenshots: shots, ...evidenceOpts })}
|
|
60978
61922
|
`);
|
|
60979
61923
|
}
|
|
60980
|
-
const index =
|
|
61924
|
+
const index = join50(outDir, "index.md");
|
|
60981
61925
|
writeFileSync19(index, `${renderPagesIndex(result, derived, { standard, lang, hrefs })}
|
|
60982
61926
|
`);
|
|
60983
61927
|
const html = wantHtml ? emitHtml(result, {
|
|
@@ -61029,7 +61973,7 @@ async function cmdSnapshot(p) {
|
|
|
61029
61973
|
2
|
|
61030
61974
|
)
|
|
61031
61975
|
);
|
|
61032
|
-
else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${
|
|
61976
|
+
else if (!snaps.length) console.log(lang === "fr" ? `Aucun instantan\xE9 dans ${join50(root, PAGES_DIR)}.` : `No snapshot in ${join50(root, PAGES_DIR)}.`);
|
|
61033
61977
|
else for (const s of snaps) console.log(`${s.meta.id} ${s.meta.name} ${s.meta.url}${s.meta.auth ? " [auth]" : ""}`);
|
|
61034
61978
|
return 0;
|
|
61035
61979
|
}
|
|
@@ -61076,7 +62020,7 @@ async function cmdSnapshot(p) {
|
|
|
61076
62020
|
console.error(`ultra11y snapshot write: could not write the snapshot: ${e instanceof Error ? e.message : String(e)}`);
|
|
61077
62021
|
return 1;
|
|
61078
62022
|
}
|
|
61079
|
-
const result = runAudit({ inputs: [
|
|
62023
|
+
const result = runAudit({ inputs: [join50(dir, "dom.html")], onWarn: (m) => console.error(m) });
|
|
61080
62024
|
const failOnRaw = p.flags["fail-on"];
|
|
61081
62025
|
const failOnParsed = parseFailOn(failOnRaw);
|
|
61082
62026
|
if (failOnRaw !== void 0 && failOnParsed === null) {
|
|
@@ -61108,8 +62052,8 @@ function cmdInit(p) {
|
|
|
61108
62052
|
if (want.baseline) {
|
|
61109
62053
|
const inputs = p.positionals.length ? p.positionals : ["."];
|
|
61110
62054
|
const result = runAudit({ inputs, onWarn: (m) => console.error(m) });
|
|
61111
|
-
mkdirSync17(
|
|
61112
|
-
const bp =
|
|
62055
|
+
mkdirSync17(join50(root, "audits"), { recursive: true });
|
|
62056
|
+
const bp = join50(root, "audits", "baseline.json");
|
|
61113
62057
|
writeFileSync19(bp, JSON.stringify(result, null, 2) + "\n");
|
|
61114
62058
|
wrote.push(bp);
|
|
61115
62059
|
}
|
|
@@ -61410,7 +62354,7 @@ async function cmdTickets(p) {
|
|
|
61410
62354
|
let setPath;
|
|
61411
62355
|
if (ticketsOut) {
|
|
61412
62356
|
mkdirSync17(ticketsOut, { recursive: true });
|
|
61413
|
-
setPath =
|
|
62357
|
+
setPath = join50(ticketsOut, `issues-${result.date}.json`);
|
|
61414
62358
|
const payload = {
|
|
61415
62359
|
tool: "ultra11y",
|
|
61416
62360
|
kind: "issues",
|
|
@@ -61511,8 +62455,8 @@ async function cmdTickets(p) {
|
|
|
61511
62455
|
return pushed.failed > 0 ? 1 : 0;
|
|
61512
62456
|
}
|
|
61513
62457
|
function depsAt(root) {
|
|
61514
|
-
const pkgPath =
|
|
61515
|
-
if (!
|
|
62458
|
+
const pkgPath = join50(root, "package.json");
|
|
62459
|
+
if (!existsSync34(pkgPath)) return {};
|
|
61516
62460
|
try {
|
|
61517
62461
|
const pkg = JSON.parse(readText(pkgPath));
|
|
61518
62462
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
@@ -61538,24 +62482,24 @@ function cmdRender(p) {
|
|
|
61538
62482
|
console.error(`ultra11y render: --runner must be playwright|cypress|auto (got "${forced}").`);
|
|
61539
62483
|
return 2;
|
|
61540
62484
|
}
|
|
61541
|
-
const detected = detectE2eRunner(depsAt(root), (f) =>
|
|
62485
|
+
const detected = detectE2eRunner(depsAt(root), (f) => existsSync34(join50(root, f)));
|
|
61542
62486
|
const runners = forced && forced !== "auto" ? [forced] : detected;
|
|
61543
62487
|
if (!runners.length) {
|
|
61544
62488
|
console.error(e2eSetupPlan([], {}, lang));
|
|
61545
62489
|
return 1;
|
|
61546
62490
|
}
|
|
61547
62491
|
const engineRef = engineRefFor(root);
|
|
61548
|
-
const dir =
|
|
62492
|
+
const dir = join50(root, ".ultra11y", "e2e");
|
|
61549
62493
|
const paths = {};
|
|
61550
62494
|
try {
|
|
61551
62495
|
mkdirSync17(dir, { recursive: true });
|
|
61552
62496
|
if (runners.includes("playwright")) {
|
|
61553
|
-
writeFileSync19(
|
|
62497
|
+
writeFileSync19(join50(dir, "playwright.mjs"), playwrightFixture(engineRef));
|
|
61554
62498
|
paths.playwright = ".ultra11y/e2e/playwright.mjs";
|
|
61555
62499
|
}
|
|
61556
62500
|
if (runners.includes("cypress")) {
|
|
61557
|
-
writeFileSync19(
|
|
61558
|
-
writeFileSync19(
|
|
62501
|
+
writeFileSync19(join50(dir, "cypress-plugin.mjs"), cypressPlugin(engineRef));
|
|
62502
|
+
writeFileSync19(join50(dir, "cypress-commands.mjs"), cypressCommands());
|
|
61559
62503
|
paths.cypressPlugin = ".ultra11y/e2e/cypress-plugin.mjs";
|
|
61560
62504
|
paths.cypressCommands = ".ultra11y/e2e/cypress-commands.mjs";
|
|
61561
62505
|
}
|
|
@@ -61584,7 +62528,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61584
62528
|
}
|
|
61585
62529
|
if (p.flags.setup === true) {
|
|
61586
62530
|
const rel2 = ".ultra11y/capture-setup.mjs";
|
|
61587
|
-
const out2 =
|
|
62531
|
+
const out2 = join50(root, rel2);
|
|
61588
62532
|
try {
|
|
61589
62533
|
mkdirSync17(dirname14(out2), { recursive: true });
|
|
61590
62534
|
writeFileSync19(out2, captureSetup());
|
|
@@ -61593,20 +62537,20 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61593
62537
|
return 1;
|
|
61594
62538
|
}
|
|
61595
62539
|
let setupDeps = {};
|
|
61596
|
-
const setupPkg =
|
|
61597
|
-
if (
|
|
62540
|
+
const setupPkg = join50(root, "package.json");
|
|
62541
|
+
if (existsSync34(setupPkg)) {
|
|
61598
62542
|
try {
|
|
61599
62543
|
const pkg = JSON.parse(readText(setupPkg));
|
|
61600
62544
|
setupDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
61601
62545
|
} catch {
|
|
61602
62546
|
}
|
|
61603
62547
|
}
|
|
61604
|
-
const tr = detectTestRunner(setupDeps, (f) =>
|
|
62548
|
+
const tr = detectTestRunner(setupDeps, (f) => existsSync34(join50(root, f)));
|
|
61605
62549
|
console.log(captureSetupPlan(tr, rel2, lang));
|
|
61606
62550
|
const gaLine = ".ultra11y/captures/*.html text eol=lf linguist-generated=true";
|
|
61607
|
-
const gaPath =
|
|
62551
|
+
const gaPath = join50(root, ".gitattributes");
|
|
61608
62552
|
try {
|
|
61609
|
-
const existing =
|
|
62553
|
+
const existing = existsSync34(gaPath) ? readFileSync32(gaPath, "utf8") : "";
|
|
61610
62554
|
if (!existing.includes(".ultra11y/captures/")) {
|
|
61611
62555
|
appendFileSync(gaPath, (existing && !existing.endsWith("\n") ? "\n" : "") + gaLine + "\n");
|
|
61612
62556
|
console.log(lang === "fr" ? `.gitattributes : ajout\xE9 \xAB ${gaLine} \xBB` : `.gitattributes: added "${gaLine}"`);
|
|
@@ -61614,8 +62558,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61614
62558
|
} catch {
|
|
61615
62559
|
}
|
|
61616
62560
|
try {
|
|
61617
|
-
const giPath =
|
|
61618
|
-
if (
|
|
62561
|
+
const giPath = join50(root, ".gitignore");
|
|
62562
|
+
if (existsSync34(giPath) && /^\s*\/?\.ultra11y(\/\**)?\/?\s*$/m.test(readFileSync32(giPath, "utf8")))
|
|
61619
62563
|
console.error(
|
|
61620
62564
|
lang === "fr" ? "\u26A0\uFE0F .ultra11y semble ignor\xE9 par .gitignore \u2014 les captures doivent \xEAtre committ\xE9es pour le gate (ajoutez \xAB !.ultra11y/captures/ \xBB)." : '\u26A0\uFE0F .ultra11y appears gitignored \u2014 captures must be committed for the gate (add "!.ultra11y/captures/").'
|
|
61621
62565
|
);
|
|
@@ -61625,8 +62569,8 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61625
62569
|
}
|
|
61626
62570
|
if (p.flags.storybook === true || typeof p.flags.storybook === "string") {
|
|
61627
62571
|
const sbDir = p.positionals[0] ?? "storybook-static";
|
|
61628
|
-
const indexPath =
|
|
61629
|
-
if (!
|
|
62572
|
+
const indexPath = existsSync34(join50(sbDir, "index.json")) ? join50(sbDir, "index.json") : join50(sbDir, "stories.json");
|
|
62573
|
+
if (!existsSync34(indexPath)) {
|
|
61630
62574
|
console.error(
|
|
61631
62575
|
lang === "fr" ? `ultra11y render : aucun index Storybook (index.json/stories.json) dans ${sbDir}.` : `ultra11y render: no Storybook index (index.json/stories.json) in ${sbDir}.`
|
|
61632
62576
|
);
|
|
@@ -61636,7 +62580,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61636
62580
|
const provById = new Map(stories.map((s) => [s.id, storyProvenance(s)]));
|
|
61637
62581
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
61638
62582
|
const htmlDir = capturesFlag ?? sbDir;
|
|
61639
|
-
const htmlFiles =
|
|
62583
|
+
const htmlFiles = existsSync34(htmlDir) ? discover([htmlDir]).files.filter((f) => /\.html?$/i.test(f)) : [];
|
|
61640
62584
|
const outDir = ".ultra11y/captures";
|
|
61641
62585
|
let attributed = 0;
|
|
61642
62586
|
let skipped = 0;
|
|
@@ -61659,7 +62603,7 @@ Fill in COMPONENTS, run it (e.g. npx tsx ${out2}), then: node scripts/ultra11y.m
|
|
|
61659
62603
|
}
|
|
61660
62604
|
try {
|
|
61661
62605
|
mkdirSync17(outDir, { recursive: true });
|
|
61662
|
-
writeFileSync19(
|
|
62606
|
+
writeFileSync19(join50(outDir, `${hitId}.html`), `${formatCaptureComment(prov)}
|
|
61663
62607
|
${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
61664
62608
|
attributed++;
|
|
61665
62609
|
} catch {
|
|
@@ -61681,11 +62625,11 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
|
61681
62625
|
}
|
|
61682
62626
|
if (p.flags.coverage === true) {
|
|
61683
62627
|
const capturesFlag = typeof p.flags.captures === "string" && p.flags.captures ? p.flags.captures : void 0;
|
|
61684
|
-
const capturesDir = capturesFlag ??
|
|
62628
|
+
const capturesDir = capturesFlag ?? join50(root, ".ultra11y/captures");
|
|
61685
62629
|
const graphExt = [...GRAPH_ONLY_EXT, ...asList(p.flags.ext) ?? []];
|
|
61686
62630
|
const sourceFiles = discover([root], { include: asList(p.flags.include), exclude: asList(p.flags.exclude), ext: graphExt }).files;
|
|
61687
62631
|
const graph = buildGraphStreaming(sourceFiles);
|
|
61688
|
-
const capFiles =
|
|
62632
|
+
const capFiles = existsSync34(capturesDir) ? discover([capturesDir]).files : [];
|
|
61689
62633
|
const entries = capFiles.map((f) => ({ file: toPosix(f), provenance: parseCaptureProvenance(readText(f)) }));
|
|
61690
62634
|
const cov = computeCaptureCoverage(graph, entries);
|
|
61691
62635
|
if (p.flags.json) console.log(JSON.stringify(cov, null, 2));
|
|
@@ -61693,15 +62637,15 @@ ${raw}${raw.endsWith("\n") ? "" : "\n"}`);
|
|
|
61693
62637
|
return 0;
|
|
61694
62638
|
}
|
|
61695
62639
|
let deps = {};
|
|
61696
|
-
const pkgPath =
|
|
61697
|
-
if (
|
|
62640
|
+
const pkgPath = join50(root, "package.json");
|
|
62641
|
+
if (existsSync34(pkgPath)) {
|
|
61698
62642
|
try {
|
|
61699
62643
|
const pkg = JSON.parse(readText(pkgPath));
|
|
61700
62644
|
deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
61701
62645
|
} catch {
|
|
61702
62646
|
}
|
|
61703
62647
|
}
|
|
61704
|
-
const detection = detectFrameworks(deps, (f) =>
|
|
62648
|
+
const detection = detectFrameworks(deps, (f) => existsSync34(join50(root, f)));
|
|
61705
62649
|
if (p.flags.json) console.log(JSON.stringify(detection, null, 2));
|
|
61706
62650
|
else console.log(renderPlan(detection, lang));
|
|
61707
62651
|
return 0;
|
|
@@ -61901,7 +62845,7 @@ function applyAdjudicationFile(p, adj, lang) {
|
|
|
61901
62845
|
}
|
|
61902
62846
|
const out2 = typeof p.flags.out === "string" ? p.flags.out : ".";
|
|
61903
62847
|
mkdirSync17(out2, { recursive: true });
|
|
61904
|
-
const auditPath =
|
|
62848
|
+
const auditPath = join50(out2, "audit-latest.json");
|
|
61905
62849
|
writeFileSync19(auditPath, JSON.stringify(r.audit, null, 2) + "\n");
|
|
61906
62850
|
if (p.flags.json)
|
|
61907
62851
|
console.log(
|
|
@@ -62011,7 +62955,7 @@ async function cmdJudge(p) {
|
|
|
62011
62955
|
return 1;
|
|
62012
62956
|
}
|
|
62013
62957
|
mkdirSync17(out2, { recursive: true });
|
|
62014
|
-
const auditPath =
|
|
62958
|
+
const auditPath = join50(out2, "audit-latest.json");
|
|
62015
62959
|
writeFileSync19(auditPath, `${JSON.stringify(r.audit, null, 2)}
|
|
62016
62960
|
`);
|
|
62017
62961
|
console.log(
|
|
@@ -62081,7 +63025,7 @@ async function cmdScan(p) {
|
|
|
62081
63025
|
return 0;
|
|
62082
63026
|
}
|
|
62083
63027
|
for (const target of p.positionals.filter((a) => a !== "-")) {
|
|
62084
|
-
if (/^https?:\/\//i.test(target) ||
|
|
63028
|
+
if (/^https?:\/\//i.test(target) || existsSync34(target)) continue;
|
|
62085
63029
|
console.error(
|
|
62086
63030
|
lang === "fr" ? `ultra11y scan : fichier introuvable (file not found) : ${target}. Passez une URL http(s):// ou un fichier HTML existant.` : `ultra11y scan: File not found: ${target}. Pass an http(s):// URL or an existing HTML file.`
|
|
62087
63031
|
);
|
|
@@ -62208,7 +63152,7 @@ async function cmdScan(p) {
|
|
|
62208
63152
|
}
|
|
62209
63153
|
let merged = mergeDynamic(audit2, dynamic, lang);
|
|
62210
63154
|
if (dynamic.snapshots?.length) {
|
|
62211
|
-
const doms = dynamic.snapshots.map((id) =>
|
|
63155
|
+
const doms = dynamic.snapshots.map((id) => join50(snapshotRoot ?? ".", PAGES_DIR, id, "dom.html")).filter((f) => existsSync34(f));
|
|
62212
63156
|
if (doms.length) {
|
|
62213
63157
|
const snapAudit = runAudit({ inputs: doms, onWarn: (m) => console.error(m) });
|
|
62214
63158
|
merged = mergeSnapshotAudit(merged, snapAudit);
|
|
@@ -62220,11 +63164,11 @@ async function cmdScan(p) {
|
|
|
62220
63164
|
}
|
|
62221
63165
|
}
|
|
62222
63166
|
mkdirSync17(out2, { recursive: true });
|
|
62223
|
-
writeFileSync19(
|
|
63167
|
+
writeFileSync19(join50(out2, "audit-latest.json"), JSON.stringify(merged, null, 2) + "\n");
|
|
62224
63168
|
if (p.flags.json) console.log(JSON.stringify(merged, null, 2));
|
|
62225
63169
|
else {
|
|
62226
63170
|
console.log(
|
|
62227
|
-
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${
|
|
63171
|
+
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join50(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join50(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
|
|
62228
63172
|
);
|
|
62229
63173
|
if (dynamic.snapshots?.length)
|
|
62230
63174
|
console.log(
|
|
@@ -62467,7 +63411,7 @@ async function cmdImport(p) {
|
|
|
62467
63411
|
}
|
|
62468
63412
|
if (outDir) {
|
|
62469
63413
|
mkdirSync17(outDir, { recursive: true });
|
|
62470
|
-
const rawFile =
|
|
63414
|
+
const rawFile = join50(outDir, `external-${adapterId}-${arg}.raw.json`);
|
|
62471
63415
|
writeFileSync19(rawFile, rawText.endsWith("\n") ? rawText : `${rawText}
|
|
62472
63416
|
`);
|
|
62473
63417
|
if (!p.flags.json) console.log(rawFile);
|
|
@@ -62503,7 +63447,7 @@ async function cmdImport(p) {
|
|
|
62503
63447
|
}
|
|
62504
63448
|
if (outDir) {
|
|
62505
63449
|
mkdirSync17(outDir, { recursive: true });
|
|
62506
|
-
const file =
|
|
63450
|
+
const file = join50(outDir, "external-latest.json");
|
|
62507
63451
|
writeFileSync19(file, `${JSON.stringify(audit2, null, 2)}
|
|
62508
63452
|
`);
|
|
62509
63453
|
console.log(file);
|
|
@@ -62609,7 +63553,7 @@ function cmdOrchestrate(p) {
|
|
|
62609
63553
|
}
|
|
62610
63554
|
const engineAbs = realpathSync6(fileURLToPath5(import.meta.url));
|
|
62611
63555
|
if (p.flags.list === true) {
|
|
62612
|
-
if (!
|
|
63556
|
+
if (!existsSync34(runFlag)) {
|
|
62613
63557
|
console.error(`ultra11y orchestrate: run dir not found: ${runFlag}.`);
|
|
62614
63558
|
return 2;
|
|
62615
63559
|
}
|
|
@@ -62636,7 +63580,7 @@ function cmdOrchestrate(p) {
|
|
|
62636
63580
|
);
|
|
62637
63581
|
} else {
|
|
62638
63582
|
console.log(
|
|
62639
|
-
lang === "fr" ? `Suivez ${
|
|
63583
|
+
lang === "fr" ? `Suivez ${join50(runFlag, "orchestration", "RUNBOOK.md")} s\xE9quentiellement (chemin \xE9co).` : `Follow ${join50(runFlag, "orchestration", "RUNBOOK.md")} sequentially (the eco path).`
|
|
62640
63584
|
);
|
|
62641
63585
|
}
|
|
62642
63586
|
if (p.flags.phase === void 0 && workflows.length === 0 && p.flags.eco !== true) {
|