token-goat 2.8.6 → 2.9.1
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/README.md +11 -3
- package/dist/{token-goat-chunk-L2XHDICZ.mjs → token-goat-chunk-4KD2N46D.mjs} +28 -27
- package/dist/{token-goat-chunk-GUNYAGOZ.mjs → token-goat-chunk-4ZDRTNGV.mjs} +9942 -95
- package/dist/{token-goat-chunk-LJEETTER.mjs → token-goat-chunk-AUM22UQ2.mjs} +5 -5
- package/dist/{token-goat-chunk-ZIPBLIUZ.mjs → token-goat-chunk-CD6U5OD6.mjs} +28 -4
- package/dist/{token-goat-chunk-DK4VLLYB.mjs → token-goat-chunk-FDXINYK4.mjs} +2031 -3665
- package/dist/token-goat-chunk-LHMC5ENL.mjs +79 -0
- package/dist/{token-goat-chunk-TELKICYU.mjs → token-goat-chunk-MR7MNSDR.mjs} +155 -519
- package/dist/{token-goat-chunk-MA5237JN.mjs → token-goat-chunk-RRNRPCLX.mjs} +150 -56
- package/dist/{token-goat-chunk-U7M2LTGP.mjs → token-goat-chunk-SQSB5P5P.mjs} +690 -282
- package/dist/{token-goat-chunk-XAWIELXH.mjs → token-goat-chunk-SXSWQMNP.mjs} +405 -150
- package/dist/{token-goat-chunk-SHN4UTL4.mjs → token-goat-chunk-V5T3YNYR.mjs} +5 -5
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +1 -1
- package/dist/token-goat-chunk-2JZ66BBE.mjs +0 -7157
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
formatCsvTable,
|
|
30
30
|
getEmbeddingCoverage,
|
|
31
31
|
getFileEntry,
|
|
32
|
+
getTrackedFiles,
|
|
32
33
|
indexFileSync,
|
|
33
34
|
isAvailable,
|
|
34
35
|
isBlobStale,
|
|
@@ -66,7 +67,7 @@ import {
|
|
|
66
67
|
walkProject,
|
|
67
68
|
yamlLineClosesQuote,
|
|
68
69
|
yamlOpenQuoteAfter
|
|
69
|
-
} from "./token-goat-chunk-
|
|
70
|
+
} from "./token-goat-chunk-FDXINYK4.mjs";
|
|
70
71
|
import {
|
|
71
72
|
Database,
|
|
72
73
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -120,36 +121,11 @@ import {
|
|
|
120
121
|
unsupportedLanguageName,
|
|
121
122
|
windowsCmdQuoteArg,
|
|
122
123
|
withExtension
|
|
123
|
-
} from "./token-goat-chunk-
|
|
124
|
+
} from "./token-goat-chunk-4ZDRTNGV.mjs";
|
|
124
125
|
import {
|
|
125
126
|
registerReset
|
|
126
127
|
} from "./token-goat-chunk-AO2QD2AG.mjs";
|
|
127
128
|
|
|
128
|
-
// src/repomap.ts
|
|
129
|
-
import * as fs from "fs";
|
|
130
|
-
import * as path from "path";
|
|
131
|
-
function getTrackedFiles(cwd = process.cwd()) {
|
|
132
|
-
try {
|
|
133
|
-
let dir = cwd;
|
|
134
|
-
let onlyFile = "";
|
|
135
|
-
try {
|
|
136
|
-
if (fs.statSync(cwd).isFile()) {
|
|
137
|
-
dir = path.dirname(cwd);
|
|
138
|
-
onlyFile = path.basename(cwd);
|
|
139
|
-
}
|
|
140
|
-
} catch {
|
|
141
|
-
}
|
|
142
|
-
const args = onlyFile === "" ? ["ls-files"] : ["ls-files", "--error-unmatch", "--", onlyFile];
|
|
143
|
-
const result = runGit(args, { cwd: dir });
|
|
144
|
-
if (result.exitCode !== 0 || !result.stdout) {
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) => path.join(dir, rel));
|
|
148
|
-
} catch {
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
129
|
// src/section_reader.ts
|
|
154
130
|
import { readFileSync } from "node:fs";
|
|
155
131
|
function parseHeadingSpec(spec, headers) {
|
|
@@ -477,8 +453,8 @@ import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
|
477
453
|
import { randomUUID } from "node:crypto";
|
|
478
454
|
|
|
479
455
|
// src/read_commands.ts
|
|
480
|
-
import * as
|
|
481
|
-
import * as
|
|
456
|
+
import * as fs4 from "node:fs";
|
|
457
|
+
import * as path4 from "node:path";
|
|
482
458
|
|
|
483
459
|
// src/json_query.ts
|
|
484
460
|
function jsonType(value) {
|
|
@@ -3444,11 +3420,11 @@ var CHOMPING_STRIP = CHOMPING_MODE.STRIP;
|
|
|
3444
3420
|
var CHOMPING_KEEP = CHOMPING_MODE.KEEP;
|
|
3445
3421
|
|
|
3446
3422
|
// src/openapi_query.ts
|
|
3447
|
-
import * as
|
|
3423
|
+
import * as path from "node:path";
|
|
3448
3424
|
var HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
|
|
3449
3425
|
function parseOpenApiSpec(text, filePath) {
|
|
3450
3426
|
text = stripBom(text);
|
|
3451
|
-
const ext =
|
|
3427
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
3452
3428
|
if (ext === ".yaml" || ext === ".yml") return load(text);
|
|
3453
3429
|
if (ext === ".json") return JSON.parse(text);
|
|
3454
3430
|
try {
|
|
@@ -3802,24 +3778,24 @@ ${c.body}`).join("\n\n");
|
|
|
3802
3778
|
}
|
|
3803
3779
|
|
|
3804
3780
|
// src/sqlite_query.ts
|
|
3805
|
-
import * as
|
|
3781
|
+
import * as fs from "node:fs";
|
|
3806
3782
|
var SQLITE_QUERY_ROW_CAP = 5e3;
|
|
3807
3783
|
var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
|
|
3808
3784
|
function readMagicBytes(filePath) {
|
|
3809
3785
|
let fd;
|
|
3810
3786
|
try {
|
|
3811
|
-
fd =
|
|
3787
|
+
fd = fs.openSync(filePath, "r");
|
|
3812
3788
|
} catch {
|
|
3813
3789
|
return null;
|
|
3814
3790
|
}
|
|
3815
3791
|
try {
|
|
3816
3792
|
const buf = Buffer.alloc(SQLITE_MAGIC.length);
|
|
3817
|
-
const bytesRead =
|
|
3793
|
+
const bytesRead = fs.readSync(fd, buf, 0, buf.length, 0);
|
|
3818
3794
|
return bytesRead === buf.length ? buf : null;
|
|
3819
3795
|
} catch {
|
|
3820
3796
|
return null;
|
|
3821
3797
|
} finally {
|
|
3822
|
-
|
|
3798
|
+
fs.closeSync(fd);
|
|
3823
3799
|
}
|
|
3824
3800
|
}
|
|
3825
3801
|
function isSqliteFile(filePath) {
|
|
@@ -3827,7 +3803,7 @@ function isSqliteFile(filePath) {
|
|
|
3827
3803
|
return magic !== null && magic.equals(SQLITE_MAGIC);
|
|
3828
3804
|
}
|
|
3829
3805
|
function openReadonlySqlite(filePath) {
|
|
3830
|
-
if (!
|
|
3806
|
+
if (!fs.existsSync(filePath)) {
|
|
3831
3807
|
throw new Error(`file not found: ${filePath}`);
|
|
3832
3808
|
}
|
|
3833
3809
|
if (!isSqliteFile(filePath)) {
|
|
@@ -4530,8 +4506,8 @@ function formatConflictSummaries(summaries) {
|
|
|
4530
4506
|
|
|
4531
4507
|
// src/screenshot.ts
|
|
4532
4508
|
import dns from "node:dns/promises";
|
|
4533
|
-
import
|
|
4534
|
-
import
|
|
4509
|
+
import fs2 from "node:fs";
|
|
4510
|
+
import path2 from "node:path";
|
|
4535
4511
|
var loadPuppeteer = createLazyModuleLoader(
|
|
4536
4512
|
async () => await import("puppeteer-core"),
|
|
4537
4513
|
"screenshot disabled (puppeteer-core unavailable)"
|
|
@@ -4540,7 +4516,7 @@ var PLAYWRIGHT_CHROME_SUBDIRS = ["chrome-win64", "chrome-win"];
|
|
|
4540
4516
|
function findPlaywrightChromium(msPlaywrightDir) {
|
|
4541
4517
|
let entries;
|
|
4542
4518
|
try {
|
|
4543
|
-
entries =
|
|
4519
|
+
entries = fs2.readdirSync(msPlaywrightDir);
|
|
4544
4520
|
} catch {
|
|
4545
4521
|
return [];
|
|
4546
4522
|
}
|
|
@@ -4548,7 +4524,7 @@ function findPlaywrightChromium(msPlaywrightDir) {
|
|
|
4548
4524
|
const candidates = [];
|
|
4549
4525
|
for (const m of versioned) {
|
|
4550
4526
|
for (const sub of PLAYWRIGHT_CHROME_SUBDIRS) {
|
|
4551
|
-
candidates.push(
|
|
4527
|
+
candidates.push(path2.join(msPlaywrightDir, m[0], sub, "chrome.exe"));
|
|
4552
4528
|
}
|
|
4553
4529
|
}
|
|
4554
4530
|
return candidates;
|
|
@@ -4559,33 +4535,33 @@ function platformCandidatePaths() {
|
|
|
4559
4535
|
const programFiles = process.env["PROGRAMFILES"];
|
|
4560
4536
|
const programFilesX86 = process.env["PROGRAMFILES(X86)"];
|
|
4561
4537
|
const localAppData = process.env["LOCALAPPDATA"];
|
|
4562
|
-
if (programFiles) candidates2.push(
|
|
4563
|
-
if (programFilesX86) candidates2.push(
|
|
4538
|
+
if (programFiles) candidates2.push(path2.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"));
|
|
4539
|
+
if (programFilesX86) candidates2.push(path2.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"));
|
|
4564
4540
|
if (localAppData) {
|
|
4565
|
-
candidates2.push(
|
|
4566
|
-
candidates2.push(...findPlaywrightChromium(
|
|
4541
|
+
candidates2.push(path2.join(localAppData, "Google", "Chrome", "Application", "chrome.exe"));
|
|
4542
|
+
candidates2.push(...findPlaywrightChromium(path2.join(localAppData, "ms-playwright")));
|
|
4567
4543
|
}
|
|
4568
4544
|
return candidates2;
|
|
4569
4545
|
}
|
|
4570
4546
|
if (process.platform === "darwin") {
|
|
4571
4547
|
const home2 = process.env["HOME"];
|
|
4572
4548
|
const candidates2 = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
|
|
4573
|
-
if (home2) candidates2.push(...findPlaywrightChromium(
|
|
4549
|
+
if (home2) candidates2.push(...findPlaywrightChromium(path2.join(home2, "Library", "Caches", "ms-playwright")));
|
|
4574
4550
|
return candidates2;
|
|
4575
4551
|
}
|
|
4576
4552
|
const home = process.env["HOME"];
|
|
4577
4553
|
const candidates = ["/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"];
|
|
4578
|
-
if (home) candidates.push(...findPlaywrightChromium(
|
|
4554
|
+
if (home) candidates.push(...findPlaywrightChromium(path2.join(home, ".cache", "ms-playwright")));
|
|
4579
4555
|
return candidates;
|
|
4580
4556
|
}
|
|
4581
4557
|
function resolveBrowserExecutablePath(explicit) {
|
|
4582
|
-
if (explicit &&
|
|
4558
|
+
if (explicit && fs2.existsSync(explicit)) return explicit;
|
|
4583
4559
|
const cfgPath = loadConfig().screenshot.chrome_path;
|
|
4584
|
-
if (cfgPath &&
|
|
4560
|
+
if (cfgPath && fs2.existsSync(cfgPath)) return cfgPath;
|
|
4585
4561
|
const envPath = process.env["TOKEN_GOAT_CHROME_PATH"];
|
|
4586
|
-
if (envPath &&
|
|
4562
|
+
if (envPath && fs2.existsSync(envPath)) return envPath;
|
|
4587
4563
|
for (const candidate of platformCandidatePaths()) {
|
|
4588
|
-
if (
|
|
4564
|
+
if (fs2.existsSync(candidate)) return candidate;
|
|
4589
4565
|
}
|
|
4590
4566
|
return null;
|
|
4591
4567
|
}
|
|
@@ -4892,7 +4868,7 @@ async function takeScreenshot(url, destPath, opts) {
|
|
|
4892
4868
|
}
|
|
4893
4869
|
|
|
4894
4870
|
// src/notes.ts
|
|
4895
|
-
import * as
|
|
4871
|
+
import * as fs3 from "node:fs";
|
|
4896
4872
|
var WHOLE_FILE_NOTE_SYMBOL = "";
|
|
4897
4873
|
function toNoteRow(row) {
|
|
4898
4874
|
return {
|
|
@@ -4917,7 +4893,7 @@ function resolveSymbolMatch(filePath, symbolName, dbPath = globalDbPath()) {
|
|
|
4917
4893
|
}
|
|
4918
4894
|
function bodyFromSource(entry) {
|
|
4919
4895
|
try {
|
|
4920
|
-
return
|
|
4896
|
+
return fs3.readFileSync(entry.filePath, "utf8").split(/\r?\n/).slice(Math.max(0, entry.lineStart - 1), entry.lineEnd).join("\n");
|
|
4921
4897
|
} catch {
|
|
4922
4898
|
return "";
|
|
4923
4899
|
}
|
|
@@ -4968,7 +4944,7 @@ function isNoteStale(note, dbPath = globalDbPath()) {
|
|
|
4968
4944
|
|
|
4969
4945
|
// src/ts_refs.ts
|
|
4970
4946
|
import { createRequire } from "node:module";
|
|
4971
|
-
import * as
|
|
4947
|
+
import * as path3 from "node:path";
|
|
4972
4948
|
var _require = createRequire(import.meta.url);
|
|
4973
4949
|
var MAX_CANDIDATE_FILES = 50;
|
|
4974
4950
|
var _ts = null;
|
|
@@ -4999,7 +4975,7 @@ registerReset(() => {
|
|
|
4999
4975
|
var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
|
|
5000
4976
|
var TS_JS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
5001
4977
|
function isTsPath(filePath) {
|
|
5002
|
-
return TS_EXTENSIONS.has(
|
|
4978
|
+
return TS_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
|
|
5003
4979
|
}
|
|
5004
4980
|
function resolveTypedRefs(input) {
|
|
5005
4981
|
const ts = loadTs();
|
|
@@ -5029,7 +5005,7 @@ function resolveTypedRefs(input) {
|
|
|
5029
5005
|
if (defSymbol === null) return null;
|
|
5030
5006
|
const out = [];
|
|
5031
5007
|
for (const ref of input.candidates) {
|
|
5032
|
-
if (!TS_JS_EXTENSIONS.has(
|
|
5008
|
+
if (!TS_JS_EXTENSIONS.has(path3.extname(ref.filePath).toLowerCase())) {
|
|
5033
5009
|
out.push(ref);
|
|
5034
5010
|
continue;
|
|
5035
5011
|
}
|
|
@@ -5055,12 +5031,12 @@ function buildScopedProgram(ts, rootNames, searchFrom) {
|
|
|
5055
5031
|
resolveJsonModule: true,
|
|
5056
5032
|
esModuleInterop: true
|
|
5057
5033
|
};
|
|
5058
|
-
const configPath = ts.findConfigFile(
|
|
5034
|
+
const configPath = ts.findConfigFile(path3.dirname(searchFrom), ts.sys.fileExists, "tsconfig.json");
|
|
5059
5035
|
if (configPath !== void 0) {
|
|
5060
5036
|
try {
|
|
5061
5037
|
const raw = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
5062
5038
|
if (raw.error === void 0) {
|
|
5063
|
-
const parsed = ts.parseJsonConfigFileContent(raw.config, ts.sys,
|
|
5039
|
+
const parsed = ts.parseJsonConfigFileContent(raw.config, ts.sys, path3.dirname(configPath));
|
|
5064
5040
|
Object.assign(options, parsed.options);
|
|
5065
5041
|
}
|
|
5066
5042
|
} catch {
|
|
@@ -5164,7 +5140,7 @@ var STRUCTURED_MISS_MAX_NODES = 2e4;
|
|
|
5164
5140
|
var REFS_TOP_SCAN_LIMIT = 2e4;
|
|
5165
5141
|
function fileExists(p) {
|
|
5166
5142
|
try {
|
|
5167
|
-
|
|
5143
|
+
fs4.statSync(p);
|
|
5168
5144
|
return true;
|
|
5169
5145
|
} catch {
|
|
5170
5146
|
return false;
|
|
@@ -5195,30 +5171,30 @@ function withPinnedReads(pins, fn) {
|
|
|
5195
5171
|
}
|
|
5196
5172
|
}
|
|
5197
5173
|
function readPinnedBytes(p, pinned) {
|
|
5198
|
-
const fd =
|
|
5174
|
+
const fd = fs4.openSync(p, fs4.constants.O_RDONLY);
|
|
5199
5175
|
try {
|
|
5200
|
-
const actual = fileIdentity(
|
|
5176
|
+
const actual = fileIdentity(fs4.fstatSync(fd, { bigint: true }));
|
|
5201
5177
|
if (actual !== pinned) {
|
|
5202
5178
|
throw new ConfinementIdentityError(
|
|
5203
5179
|
`refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
|
|
5204
5180
|
);
|
|
5205
5181
|
}
|
|
5206
|
-
return
|
|
5182
|
+
return fs4.readFileSync(fd);
|
|
5207
5183
|
} finally {
|
|
5208
|
-
|
|
5184
|
+
fs4.closeSync(fd);
|
|
5209
5185
|
}
|
|
5210
5186
|
}
|
|
5211
5187
|
function verifyPinnedIdentity(p, pinned) {
|
|
5212
|
-
const fd =
|
|
5188
|
+
const fd = fs4.openSync(p, fs4.constants.O_RDONLY);
|
|
5213
5189
|
try {
|
|
5214
|
-
const actual = fileIdentity(
|
|
5190
|
+
const actual = fileIdentity(fs4.fstatSync(fd, { bigint: true }));
|
|
5215
5191
|
if (actual !== pinned) {
|
|
5216
5192
|
throw new ConfinementIdentityError(
|
|
5217
5193
|
`refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
|
|
5218
5194
|
);
|
|
5219
5195
|
}
|
|
5220
5196
|
} finally {
|
|
5221
|
-
|
|
5197
|
+
fs4.closeSync(fd);
|
|
5222
5198
|
}
|
|
5223
5199
|
}
|
|
5224
5200
|
function verifyStillAbsent(p) {
|
|
@@ -5236,7 +5212,7 @@ function verifyPin(p, pinned) {
|
|
|
5236
5212
|
verifyPinnedIdentity(p, pinned);
|
|
5237
5213
|
}
|
|
5238
5214
|
function indexFileSyncPinned(resolvedPath, dbPath) {
|
|
5239
|
-
const pinned = activePins?.get(pinKey(
|
|
5215
|
+
const pinned = activePins?.get(pinKey(path4.resolve(resolvedPath)));
|
|
5240
5216
|
if (pinned === void 0) {
|
|
5241
5217
|
indexFileSync(resolvedPath, dbPath);
|
|
5242
5218
|
return;
|
|
@@ -5258,21 +5234,21 @@ function indexFileSyncPinned(resolvedPath, dbPath) {
|
|
|
5258
5234
|
indexFileSync(resolvedPath, dbPath, bytes);
|
|
5259
5235
|
}
|
|
5260
5236
|
function readFileText(p) {
|
|
5261
|
-
const pinned = activePins?.get(pinKey(
|
|
5237
|
+
const pinned = activePins?.get(pinKey(path4.resolve(p)));
|
|
5262
5238
|
try {
|
|
5263
5239
|
if (pinned === ABSENT_PIN) {
|
|
5264
5240
|
verifyStillAbsent(p);
|
|
5265
5241
|
return null;
|
|
5266
5242
|
}
|
|
5267
5243
|
if (pinned !== void 0) return redactIfDotenv(p, decodeSource(readPinnedBytes(p, pinned)));
|
|
5268
|
-
return redactIfDotenv(p, decodeSource(
|
|
5244
|
+
return redactIfDotenv(p, decodeSource(fs4.readFileSync(p)));
|
|
5269
5245
|
} catch (err) {
|
|
5270
5246
|
if (err instanceof ConfinementIdentityError) throw err;
|
|
5271
5247
|
return null;
|
|
5272
5248
|
}
|
|
5273
5249
|
}
|
|
5274
5250
|
function readFileBytes(p) {
|
|
5275
|
-
const pinned = activePins?.get(pinKey(
|
|
5251
|
+
const pinned = activePins?.get(pinKey(path4.resolve(p)));
|
|
5276
5252
|
try {
|
|
5277
5253
|
if (pinned === ABSENT_PIN) {
|
|
5278
5254
|
verifyStillAbsent(p);
|
|
@@ -5283,9 +5259,9 @@ function readFileBytes(p) {
|
|
|
5283
5259
|
if (bytes.length > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, bytes.length, MAX_ZIP_INPUT_BYTES);
|
|
5284
5260
|
return bytes;
|
|
5285
5261
|
}
|
|
5286
|
-
const stat =
|
|
5262
|
+
const stat = fs4.statSync(p);
|
|
5287
5263
|
if (stat.size > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, stat.size, MAX_ZIP_INPUT_BYTES);
|
|
5288
|
-
return
|
|
5264
|
+
return fs4.readFileSync(p);
|
|
5289
5265
|
} catch (err) {
|
|
5290
5266
|
if (err instanceof ConfinementIdentityError || err instanceof ZipInputTooLargeError) throw err;
|
|
5291
5267
|
return null;
|
|
@@ -5304,7 +5280,7 @@ var STALE_WARNING = "\u26A0 STALE: index is older than the file on disk (worker
|
|
|
5304
5280
|
var DELETED_WARNING = "\u26A0 DELETED: this file is no longer on disk \u2014 what follows is what the index last saw of it";
|
|
5305
5281
|
var DELETED_TAG = "\u26A0 DELETED: file no longer on disk";
|
|
5306
5282
|
function fileIsGone(absPath) {
|
|
5307
|
-
if (!
|
|
5283
|
+
if (!path4.isAbsolute(absPath)) return false;
|
|
5308
5284
|
return fileIsAbsent(absPath);
|
|
5309
5285
|
}
|
|
5310
5286
|
function staleWarning(resolvedPath) {
|
|
@@ -5372,12 +5348,18 @@ function sumFileSizes(filePaths) {
|
|
|
5372
5348
|
let total = 0;
|
|
5373
5349
|
for (const fp of new Set(filePaths)) {
|
|
5374
5350
|
try {
|
|
5375
|
-
total += Math.min(
|
|
5351
|
+
total += Math.min(fs4.statSync(fp).size, PER_FILE_COUNTERFACTUAL_CEILING);
|
|
5376
5352
|
} catch {
|
|
5377
5353
|
}
|
|
5378
5354
|
}
|
|
5379
5355
|
return total;
|
|
5380
5356
|
}
|
|
5357
|
+
function refsSearchBaselineBytes(rows) {
|
|
5358
|
+
let total = 0;
|
|
5359
|
+
for (const ref of rows) total += Buffer.byteLength(`${refsDisplayPath(ref.filePath)}:${ref.line}: ${ref.context}
|
|
5360
|
+
`, "utf8");
|
|
5361
|
+
return total;
|
|
5362
|
+
}
|
|
5381
5363
|
function recordReadStat(kind, fullSourceBytes, emittedText, detail) {
|
|
5382
5364
|
const emittedBytes = Buffer.byteLength(emittedText, "utf8");
|
|
5383
5365
|
const bytesSaved = Math.max(1, fullSourceBytes - emittedBytes);
|
|
@@ -5550,7 +5532,7 @@ function findStructuredKeyPath(name, filePaths) {
|
|
|
5550
5532
|
if (!isYaml && !lower.endsWith(".json")) continue;
|
|
5551
5533
|
let size;
|
|
5552
5534
|
try {
|
|
5553
|
-
size =
|
|
5535
|
+
size = fs4.statSync(filePath).size;
|
|
5554
5536
|
} catch {
|
|
5555
5537
|
continue;
|
|
5556
5538
|
}
|
|
@@ -6088,7 +6070,7 @@ ${sub.text}`);
|
|
|
6088
6070
|
return { text, code: 1 };
|
|
6089
6071
|
}
|
|
6090
6072
|
function resolveAgainstProjectRoot(file, projectRoot) {
|
|
6091
|
-
return projectRoot !== void 0 && !
|
|
6073
|
+
return projectRoot !== void 0 && !path4.isAbsolute(file) ? path4.resolve(projectRoot, file) : file;
|
|
6092
6074
|
}
|
|
6093
6075
|
function literalHeadingExists(filePath, heading) {
|
|
6094
6076
|
const ordinalMatch = /^([^#\r\n]+)#(\d+)$/.exec(heading);
|
|
@@ -6112,7 +6094,7 @@ function runSection(opts) {
|
|
|
6112
6094
|
}
|
|
6113
6095
|
const result = readSection(filePath, heading, readFileText);
|
|
6114
6096
|
if (result === null) {
|
|
6115
|
-
if (!
|
|
6097
|
+
if (!fs4.existsSync(filePath)) {
|
|
6116
6098
|
return { text: `File not found: '${filePath}'`, code: 1 };
|
|
6117
6099
|
}
|
|
6118
6100
|
const ordSpec = /^(.*?)#(\d+)$/.exec(heading);
|
|
@@ -6199,7 +6181,7 @@ function runSectionCrossFile(pairs, opts) {
|
|
|
6199
6181
|
textBlocks.push(`${key}:
|
|
6200
6182
|
${sub.text}`);
|
|
6201
6183
|
}
|
|
6202
|
-
const resolvePath = (f) => opts.projectRoot !== void 0 && !
|
|
6184
|
+
const resolvePath = (f) => opts.projectRoot !== void 0 && !path4.isAbsolute(f) ? path4.resolve(opts.projectRoot, f) : f;
|
|
6203
6185
|
const text = opts.json === true ? JSON.stringify(jsonOut, null, 2) : textBlocks.join("\n\n");
|
|
6204
6186
|
if (anyFound) {
|
|
6205
6187
|
const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
|
|
@@ -6272,7 +6254,7 @@ function renderRefsTargets(targets, opts, {
|
|
|
6272
6254
|
const jsonOut = {};
|
|
6273
6255
|
let anyFound = false;
|
|
6274
6256
|
const lines = [];
|
|
6275
|
-
const
|
|
6257
|
+
const refRows = [];
|
|
6276
6258
|
for (const { file, symbol, key } of targets) {
|
|
6277
6259
|
const queryOpts = { name: symbol };
|
|
6278
6260
|
if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
|
|
@@ -6298,7 +6280,7 @@ function renderRefsTargets(targets, opts, {
|
|
|
6298
6280
|
results = results.slice(0, opts.limit ?? 100);
|
|
6299
6281
|
}
|
|
6300
6282
|
if (results.length > 0) anyFound = true;
|
|
6301
|
-
|
|
6283
|
+
refRows.push(...results);
|
|
6302
6284
|
if (opts.json === true) {
|
|
6303
6285
|
const hiddenByGrep = matchesGrep !== void 0 ? preGrepCount - (filteredTotal ?? results.length) : 0;
|
|
6304
6286
|
const withHidden = (payload) => ({ ...payload, ...annotateHiddenByGrep && hiddenByGrep > 0 ? { hiddenByGrep } : {} });
|
|
@@ -6340,7 +6322,7 @@ function renderRefsTargets(targets, opts, {
|
|
|
6340
6322
|
if (notice !== null) lines.push(` token-goat: ${notice}`);
|
|
6341
6323
|
}
|
|
6342
6324
|
}
|
|
6343
|
-
const fullSourceBytes =
|
|
6325
|
+
const fullSourceBytes = refsSearchBaselineBytes(refRows);
|
|
6344
6326
|
if (opts.json === true) {
|
|
6345
6327
|
const text2 = JSON.stringify(jsonOut, null, 2);
|
|
6346
6328
|
emit(text2);
|
|
@@ -6449,7 +6431,7 @@ function runRefsSingle(opts) {
|
|
|
6449
6431
|
}
|
|
6450
6432
|
return 1;
|
|
6451
6433
|
}
|
|
6452
|
-
const fullSourceBytes =
|
|
6434
|
+
const fullSourceBytes = refsSearchBaselineBytes(results);
|
|
6453
6435
|
if (opts.json === true) {
|
|
6454
6436
|
let payload;
|
|
6455
6437
|
if (opts.top !== void 0) {
|
|
@@ -6522,7 +6504,7 @@ function renderCallerGroups(refs, contextLines = 0) {
|
|
|
6522
6504
|
return lines;
|
|
6523
6505
|
}
|
|
6524
6506
|
function noSymbolsMessage(displayPath, resolvedPath) {
|
|
6525
|
-
if (!
|
|
6507
|
+
if (!fs4.existsSync(resolvedPath)) {
|
|
6526
6508
|
return `Could not read: ${displayPath}`;
|
|
6527
6509
|
}
|
|
6528
6510
|
const lang = unsupportedLanguageName(resolvedPath);
|
|
@@ -7361,10 +7343,10 @@ function runConflicts(opts) {
|
|
|
7361
7343
|
if (opts.path === void 0) {
|
|
7362
7344
|
files = walkProject(process.cwd()).files;
|
|
7363
7345
|
} else {
|
|
7364
|
-
const abs =
|
|
7346
|
+
const abs = path4.resolve(opts.path);
|
|
7365
7347
|
let stat;
|
|
7366
7348
|
try {
|
|
7367
|
-
stat =
|
|
7349
|
+
stat = fs4.statSync(abs);
|
|
7368
7350
|
} catch {
|
|
7369
7351
|
emitErr(`Could not read: ${opts.path}`);
|
|
7370
7352
|
return 1;
|
|
@@ -7399,7 +7381,7 @@ async function runPdfExtractText(file, pagesSpec, layout = false) {
|
|
|
7399
7381
|
if (!fileExists(file)) {
|
|
7400
7382
|
throw new Error(`Could not read: ${file}`);
|
|
7401
7383
|
}
|
|
7402
|
-
const data =
|
|
7384
|
+
const data = fs4.readFileSync(file);
|
|
7403
7385
|
const result = await extractPdfText(new Uint8Array(data), pagesSpec, layout);
|
|
7404
7386
|
return result.text;
|
|
7405
7387
|
}
|
|
@@ -7407,21 +7389,21 @@ async function runPdfOutline(file) {
|
|
|
7407
7389
|
if (!fileExists(file)) {
|
|
7408
7390
|
throw new Error(`Could not read: ${file}`);
|
|
7409
7391
|
}
|
|
7410
|
-
const data =
|
|
7392
|
+
const data = fs4.readFileSync(file);
|
|
7411
7393
|
return extractPdfOutline(new Uint8Array(data));
|
|
7412
7394
|
}
|
|
7413
7395
|
async function runPdfMeta(file) {
|
|
7414
7396
|
if (!fileExists(file)) {
|
|
7415
7397
|
throw new Error(`Could not read: ${file}`);
|
|
7416
7398
|
}
|
|
7417
|
-
const data =
|
|
7399
|
+
const data = fs4.readFileSync(file);
|
|
7418
7400
|
return extractPdfMeta(new Uint8Array(data));
|
|
7419
7401
|
}
|
|
7420
7402
|
async function runPdfLocate(file, pattern, opts) {
|
|
7421
7403
|
if (!fileExists(file)) {
|
|
7422
7404
|
throw new Error(`Could not read: ${file}`);
|
|
7423
7405
|
}
|
|
7424
|
-
const data =
|
|
7406
|
+
const data = fs4.readFileSync(file);
|
|
7425
7407
|
return locatePdfPages(new Uint8Array(data), pattern, opts);
|
|
7426
7408
|
}
|
|
7427
7409
|
async function runImageMeta(file) {
|
|
@@ -7431,7 +7413,7 @@ async function runImageMeta(file) {
|
|
|
7431
7413
|
if (!isImagePath(file)) {
|
|
7432
7414
|
throw new Error(`Not an image file: ${file}`);
|
|
7433
7415
|
}
|
|
7434
|
-
const data =
|
|
7416
|
+
const data = fs4.readFileSync(file);
|
|
7435
7417
|
const bytes = data.length;
|
|
7436
7418
|
let probe;
|
|
7437
7419
|
try {
|
|
@@ -7463,7 +7445,7 @@ async function runImageText(file) {
|
|
|
7463
7445
|
if (!isImagePath(file)) {
|
|
7464
7446
|
throw new Error(`Not an image file: ${file}`);
|
|
7465
7447
|
}
|
|
7466
|
-
const data =
|
|
7448
|
+
const data = fs4.readFileSync(file);
|
|
7467
7449
|
const ocr = await ocrImage(data);
|
|
7468
7450
|
if (ocr === null) {
|
|
7469
7451
|
if (ocrIntegrityFailed()) {
|
|
@@ -8167,7 +8149,7 @@ function runGrep(opts) {
|
|
|
8167
8149
|
if (activePins !== null) {
|
|
8168
8150
|
let realDir;
|
|
8169
8151
|
try {
|
|
8170
|
-
realDir = foldRealpath(
|
|
8152
|
+
realDir = foldRealpath(fs4.realpathSync(dir));
|
|
8171
8153
|
} catch {
|
|
8172
8154
|
return;
|
|
8173
8155
|
}
|
|
@@ -8176,12 +8158,12 @@ function runGrep(opts) {
|
|
|
8176
8158
|
visitedRealDirs.add(realDir);
|
|
8177
8159
|
}
|
|
8178
8160
|
try {
|
|
8179
|
-
for (const entry of
|
|
8161
|
+
for (const entry of fs4.readdirSync(dir)) {
|
|
8180
8162
|
if (entry.startsWith(".")) continue;
|
|
8181
|
-
const full =
|
|
8163
|
+
const full = path4.join(dir, entry);
|
|
8182
8164
|
let lst;
|
|
8183
8165
|
try {
|
|
8184
|
-
lst =
|
|
8166
|
+
lst = fs4.lstatSync(full);
|
|
8185
8167
|
} catch {
|
|
8186
8168
|
continue;
|
|
8187
8169
|
}
|
|
@@ -8189,7 +8171,7 @@ function runGrep(opts) {
|
|
|
8189
8171
|
if (lst.isSymbolicLink()) {
|
|
8190
8172
|
let real;
|
|
8191
8173
|
try {
|
|
8192
|
-
real =
|
|
8174
|
+
real = fs4.realpathSync(full);
|
|
8193
8175
|
} catch {
|
|
8194
8176
|
continue;
|
|
8195
8177
|
}
|
|
@@ -8198,7 +8180,7 @@ function runGrep(opts) {
|
|
|
8198
8180
|
}
|
|
8199
8181
|
let stat;
|
|
8200
8182
|
try {
|
|
8201
|
-
stat =
|
|
8183
|
+
stat = fs4.statSync(target);
|
|
8202
8184
|
} catch {
|
|
8203
8185
|
continue;
|
|
8204
8186
|
}
|
|
@@ -8218,16 +8200,16 @@ function runGrep(opts) {
|
|
|
8218
8200
|
emitErr(`Path not found: ${searchPath}`);
|
|
8219
8201
|
return 1;
|
|
8220
8202
|
}
|
|
8221
|
-
const stat =
|
|
8203
|
+
const stat = fs4.statSync(searchPath);
|
|
8222
8204
|
if (stat.isDirectory()) {
|
|
8223
|
-
const pinned = activePins?.get(pinKey(
|
|
8205
|
+
const pinned = activePins?.get(pinKey(path4.resolve(searchPath)));
|
|
8224
8206
|
if (pinned !== void 0) verifyPin(searchPath, pinned);
|
|
8225
8207
|
let boundaryReal;
|
|
8226
8208
|
try {
|
|
8227
|
-
boundaryReal =
|
|
8209
|
+
boundaryReal = fs4.realpathSync(searchPath);
|
|
8228
8210
|
} catch (err) {
|
|
8229
8211
|
if (pinned === void 0) {
|
|
8230
|
-
boundaryReal =
|
|
8212
|
+
boundaryReal = path4.resolve(searchPath);
|
|
8231
8213
|
} else {
|
|
8232
8214
|
throw new ConfinementIdentityError(
|
|
8233
8215
|
`refused: "${searchPath}" could not be resolved after validation (${String(err)}). The path may have been replaced or redirected after the confinement check, so the search was not performed.`
|
|
@@ -8358,7 +8340,7 @@ function runConfigGet(opts) {
|
|
|
8358
8340
|
emit(value);
|
|
8359
8341
|
return 0;
|
|
8360
8342
|
}
|
|
8361
|
-
const ext =
|
|
8343
|
+
const ext = path4.extname(opts.file).toLowerCase();
|
|
8362
8344
|
if (ext === ".json") {
|
|
8363
8345
|
try {
|
|
8364
8346
|
let obj = JSON.parse(text);
|
|
@@ -8481,7 +8463,7 @@ function runExports(opts) {
|
|
|
8481
8463
|
names.push(s.name);
|
|
8482
8464
|
}
|
|
8483
8465
|
}
|
|
8484
|
-
const ext =
|
|
8466
|
+
const ext = path4.extname(opts.file).toLowerCase();
|
|
8485
8467
|
const text = readFileText(diskPath);
|
|
8486
8468
|
if (text === null && symbols.length === 0) {
|
|
8487
8469
|
emitErr(`Could not read: ${opts.file}`);
|
|
@@ -8818,9 +8800,9 @@ function extractImports(text, ext) {
|
|
|
8818
8800
|
return found;
|
|
8819
8801
|
}
|
|
8820
8802
|
function importsExtensionFor(filePath) {
|
|
8821
|
-
const base =
|
|
8803
|
+
const base = path4.basename(filePath).toLowerCase();
|
|
8822
8804
|
if (base === "makefile" || base === "gnumakefile" || base === "bsdmakefile") return ".mk";
|
|
8823
|
-
return
|
|
8805
|
+
return path4.extname(filePath);
|
|
8824
8806
|
}
|
|
8825
8807
|
function runImports(opts) {
|
|
8826
8808
|
const multiFiles = parseMultiFileSpec(opts.file);
|
|
@@ -8912,7 +8894,7 @@ async function runSemantic(query, opts) {
|
|
|
8912
8894
|
}
|
|
8913
8895
|
const n = opts.limit !== void 0 && Number.isFinite(opts.limit) ? opts.limit : 20;
|
|
8914
8896
|
if (opts.projectRoot !== void 0) {
|
|
8915
|
-
if (!
|
|
8897
|
+
if (!path4.isAbsolute(opts.projectRoot) || !fs4.existsSync(opts.projectRoot) || !fs4.statSync(opts.projectRoot).isDirectory()) {
|
|
8916
8898
|
const message = `token-goat: projectRoot must be an absolute, existing directory, got '${opts.projectRoot}'`;
|
|
8917
8899
|
if (opts.json === true) {
|
|
8918
8900
|
return { text: JSON.stringify({ error: message }, null, 2), code: 1 };
|
|
@@ -9150,6 +9132,318 @@ function runNoteList(opts = {}) {
|
|
|
9150
9132
|
return { text: lines.join("\n"), code: 0 };
|
|
9151
9133
|
}
|
|
9152
9134
|
|
|
9135
|
+
// src/import_graph.ts
|
|
9136
|
+
import * as fs5 from "node:fs";
|
|
9137
|
+
import * as path5 from "node:path";
|
|
9138
|
+
var DIRECT_PROBE_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cjs", ".cts", ".py"];
|
|
9139
|
+
var INDEX_PROBE_EXTENSIONS = [".ts", ".js", ".tsx", ".jsx", ".mts", ".cts"];
|
|
9140
|
+
function buildImportGraph(cwd) {
|
|
9141
|
+
const files = getTrackedFiles(cwd);
|
|
9142
|
+
const filesByFoldedPath = /* @__PURE__ */ new Map();
|
|
9143
|
+
for (const f of files) filesByFoldedPath.set(foldPath(f), f);
|
|
9144
|
+
const resolve5 = (fromFile, spec) => {
|
|
9145
|
+
if (!spec.startsWith(".")) return null;
|
|
9146
|
+
const dir = path5.dirname(fromFile);
|
|
9147
|
+
const strippedSpec = spec.replace(/\.(m?js|cjs)$/, "");
|
|
9148
|
+
const base = path5.resolve(dir, strippedSpec);
|
|
9149
|
+
for (const ext of DIRECT_PROBE_EXTENSIONS) {
|
|
9150
|
+
const match = filesByFoldedPath.get(foldPath(base + ext));
|
|
9151
|
+
if (match !== void 0) return match;
|
|
9152
|
+
}
|
|
9153
|
+
const idx = path5.join(base, "index");
|
|
9154
|
+
for (const ext of INDEX_PROBE_EXTENSIONS) {
|
|
9155
|
+
const match = filesByFoldedPath.get(foldPath(idx + ext));
|
|
9156
|
+
if (match !== void 0) return match;
|
|
9157
|
+
}
|
|
9158
|
+
return null;
|
|
9159
|
+
};
|
|
9160
|
+
const graph = /* @__PURE__ */ new Map();
|
|
9161
|
+
const importedBy = /* @__PURE__ */ new Map();
|
|
9162
|
+
for (const file of files) {
|
|
9163
|
+
let text;
|
|
9164
|
+
try {
|
|
9165
|
+
text = fs5.readFileSync(file, "utf8");
|
|
9166
|
+
} catch {
|
|
9167
|
+
continue;
|
|
9168
|
+
}
|
|
9169
|
+
const internal = [];
|
|
9170
|
+
for (const spec of extractImports(text, importsExtensionFor(file))) {
|
|
9171
|
+
const resolved = resolve5(file, spec);
|
|
9172
|
+
if (resolved === null) continue;
|
|
9173
|
+
internal.push(resolved);
|
|
9174
|
+
let importers = importedBy.get(resolved);
|
|
9175
|
+
if (importers === void 0) {
|
|
9176
|
+
importers = /* @__PURE__ */ new Set();
|
|
9177
|
+
importedBy.set(resolved, importers);
|
|
9178
|
+
}
|
|
9179
|
+
importers.add(file);
|
|
9180
|
+
}
|
|
9181
|
+
graph.set(file, internal);
|
|
9182
|
+
}
|
|
9183
|
+
return { files, graph, importedBy, resolve: resolve5 };
|
|
9184
|
+
}
|
|
9185
|
+
|
|
9186
|
+
// src/modules.ts
|
|
9187
|
+
var WEAK_MODULARITY = 0.3;
|
|
9188
|
+
var MAX_PASSES = 32;
|
|
9189
|
+
var GAIN_EPSILON = 1e-12;
|
|
9190
|
+
function emptyGraph(n) {
|
|
9191
|
+
return { n, adj: Array.from({ length: n }, () => /* @__PURE__ */ new Map()), degree: new Array(n).fill(0), m: 0 };
|
|
9192
|
+
}
|
|
9193
|
+
function addEdge(g, a, b, w) {
|
|
9194
|
+
if (a === b) {
|
|
9195
|
+
g.adj[a].set(a, (g.adj[a].get(a) ?? 0) + w);
|
|
9196
|
+
g.degree[a] += 2 * w;
|
|
9197
|
+
} else {
|
|
9198
|
+
g.adj[a].set(b, (g.adj[a].get(b) ?? 0) + w);
|
|
9199
|
+
g.adj[b].set(a, (g.adj[b].get(a) ?? 0) + w);
|
|
9200
|
+
g.degree[a] += w;
|
|
9201
|
+
g.degree[b] += w;
|
|
9202
|
+
}
|
|
9203
|
+
g.m += w;
|
|
9204
|
+
}
|
|
9205
|
+
function localMoving(g) {
|
|
9206
|
+
const community = Array.from({ length: g.n }, (_, i) => i);
|
|
9207
|
+
const tot = g.degree.slice();
|
|
9208
|
+
const twoM = 2 * g.m;
|
|
9209
|
+
let movedEver = false;
|
|
9210
|
+
if (twoM === 0) return { community, moved: false };
|
|
9211
|
+
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
9212
|
+
let improved = false;
|
|
9213
|
+
for (let i = 0; i < g.n; i++) {
|
|
9214
|
+
const ki = g.degree[i];
|
|
9215
|
+
const ci = community[i];
|
|
9216
|
+
const links = /* @__PURE__ */ new Map();
|
|
9217
|
+
for (const [j, w] of g.adj[i]) {
|
|
9218
|
+
if (j === i) continue;
|
|
9219
|
+
const cj = community[j];
|
|
9220
|
+
links.set(cj, (links.get(cj) ?? 0) + w);
|
|
9221
|
+
}
|
|
9222
|
+
tot[ci] = tot[ci] - ki;
|
|
9223
|
+
let bestC = ci;
|
|
9224
|
+
let bestGain = (links.get(ci) ?? 0) - tot[ci] * ki / twoM;
|
|
9225
|
+
for (const c of [...links.keys()].sort((a, b) => a - b)) {
|
|
9226
|
+
if (c === ci) continue;
|
|
9227
|
+
const gain = links.get(c) - tot[c] * ki / twoM;
|
|
9228
|
+
if (gain > bestGain + GAIN_EPSILON) {
|
|
9229
|
+
bestGain = gain;
|
|
9230
|
+
bestC = c;
|
|
9231
|
+
}
|
|
9232
|
+
}
|
|
9233
|
+
tot[bestC] = tot[bestC] + ki;
|
|
9234
|
+
community[i] = bestC;
|
|
9235
|
+
if (bestC !== ci) {
|
|
9236
|
+
improved = true;
|
|
9237
|
+
movedEver = true;
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
if (!improved) break;
|
|
9241
|
+
}
|
|
9242
|
+
return { community, moved: movedEver };
|
|
9243
|
+
}
|
|
9244
|
+
function densify(community) {
|
|
9245
|
+
const firstMember = /* @__PURE__ */ new Map();
|
|
9246
|
+
for (let i = 0; i < community.length; i++) {
|
|
9247
|
+
const c = community[i];
|
|
9248
|
+
if (!firstMember.has(c)) firstMember.set(c, i);
|
|
9249
|
+
}
|
|
9250
|
+
const ordered = [...firstMember.entries()].sort((a, b) => a[1] - b[1]).map(([c]) => c);
|
|
9251
|
+
const remap = /* @__PURE__ */ new Map();
|
|
9252
|
+
ordered.forEach((c, idx) => remap.set(c, idx));
|
|
9253
|
+
return { dense: community.map((c) => remap.get(c)), count: ordered.length };
|
|
9254
|
+
}
|
|
9255
|
+
function modularityOf(g, partition) {
|
|
9256
|
+
const twoM = 2 * g.m;
|
|
9257
|
+
if (twoM === 0) return null;
|
|
9258
|
+
const internal = /* @__PURE__ */ new Map();
|
|
9259
|
+
const tot = /* @__PURE__ */ new Map();
|
|
9260
|
+
for (let i = 0; i < g.n; i++) {
|
|
9261
|
+
const ci = partition[i];
|
|
9262
|
+
tot.set(ci, (tot.get(ci) ?? 0) + g.degree[i]);
|
|
9263
|
+
for (const [j, w] of g.adj[i]) {
|
|
9264
|
+
if (j < i) continue;
|
|
9265
|
+
if (partition[j] === ci) internal.set(ci, (internal.get(ci) ?? 0) + w);
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
9268
|
+
let q = 0;
|
|
9269
|
+
for (const [c, t] of tot) {
|
|
9270
|
+
q += 2 * (internal.get(c) ?? 0) / twoM - (t / twoM) ** 2;
|
|
9271
|
+
}
|
|
9272
|
+
return q;
|
|
9273
|
+
}
|
|
9274
|
+
function louvain(g) {
|
|
9275
|
+
let current = g;
|
|
9276
|
+
let mapping = Array.from({ length: g.n }, (_, i) => i);
|
|
9277
|
+
for (let level = 0; level < MAX_PASSES; level++) {
|
|
9278
|
+
const { community, moved } = localMoving(current);
|
|
9279
|
+
const { dense, count } = densify(community);
|
|
9280
|
+
mapping = mapping.map((c) => dense[c]);
|
|
9281
|
+
if (!moved || count === current.n) break;
|
|
9282
|
+
const next = emptyGraph(count);
|
|
9283
|
+
for (let i = 0; i < current.n; i++) {
|
|
9284
|
+
for (const [j, w] of current.adj[i]) {
|
|
9285
|
+
if (j < i) continue;
|
|
9286
|
+
addEdge(next, dense[i], dense[j], w);
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
current = next;
|
|
9290
|
+
}
|
|
9291
|
+
return mapping;
|
|
9292
|
+
}
|
|
9293
|
+
function commonDirLabel(files) {
|
|
9294
|
+
const dirs = new Set(files.map(dirOf));
|
|
9295
|
+
if (dirs.size === 1) {
|
|
9296
|
+
const only = [...dirs][0];
|
|
9297
|
+
return only === "." ? "." : `${only}/`;
|
|
9298
|
+
}
|
|
9299
|
+
const segmentLists = files.map((f) => {
|
|
9300
|
+
const parts = f.split("/");
|
|
9301
|
+
parts.pop();
|
|
9302
|
+
return parts;
|
|
9303
|
+
});
|
|
9304
|
+
const first = segmentLists[0];
|
|
9305
|
+
if (first === void 0) return null;
|
|
9306
|
+
const prefix = [];
|
|
9307
|
+
for (let i = 0; i < first.length; i++) {
|
|
9308
|
+
const seg = first[i];
|
|
9309
|
+
if (!segmentLists.every((s) => s[i] === seg)) break;
|
|
9310
|
+
prefix.push(seg);
|
|
9311
|
+
}
|
|
9312
|
+
return prefix.length === 0 ? null : `${prefix.join("/")}/`;
|
|
9313
|
+
}
|
|
9314
|
+
function dirOf(file) {
|
|
9315
|
+
const idx = file.lastIndexOf("/");
|
|
9316
|
+
return idx === -1 ? "." : file.slice(0, idx);
|
|
9317
|
+
}
|
|
9318
|
+
function detectModules(imports, cwd, top) {
|
|
9319
|
+
const { files, graph } = imports;
|
|
9320
|
+
const root = getDisplayRoot(cwd);
|
|
9321
|
+
const degreeOf = /* @__PURE__ */ new Map();
|
|
9322
|
+
const bump = (f) => {
|
|
9323
|
+
degreeOf.set(f, (degreeOf.get(f) ?? 0) + 1);
|
|
9324
|
+
};
|
|
9325
|
+
for (const [from, targets] of graph) {
|
|
9326
|
+
for (const to of targets) {
|
|
9327
|
+
if (from === to) continue;
|
|
9328
|
+
bump(from);
|
|
9329
|
+
bump(to);
|
|
9330
|
+
}
|
|
9331
|
+
}
|
|
9332
|
+
const connected = files.filter((f) => (degreeOf.get(f) ?? 0) > 0);
|
|
9333
|
+
const isolatedCount = files.length - connected.length;
|
|
9334
|
+
if (connected.length === 0) {
|
|
9335
|
+
return { modules: [], modulesTotal: 0, modularity: null, isolatedCount, crossImports: [], crossImportsTotal: 0, noEdges: true };
|
|
9336
|
+
}
|
|
9337
|
+
const ordered = [...connected].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
9338
|
+
const idOf = /* @__PURE__ */ new Map();
|
|
9339
|
+
ordered.forEach((f, i) => idOf.set(f, i));
|
|
9340
|
+
const g = emptyGraph(ordered.length);
|
|
9341
|
+
const pairWeight = /* @__PURE__ */ new Map();
|
|
9342
|
+
for (const [from, targets] of graph) {
|
|
9343
|
+
const a = idOf.get(from);
|
|
9344
|
+
if (a === void 0) continue;
|
|
9345
|
+
for (const to of targets) {
|
|
9346
|
+
const b = idOf.get(to);
|
|
9347
|
+
if (b === void 0 || a === b) continue;
|
|
9348
|
+
const key = a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
9349
|
+
pairWeight.set(key, (pairWeight.get(key) ?? 0) + 1);
|
|
9350
|
+
}
|
|
9351
|
+
}
|
|
9352
|
+
for (const key of [...pairWeight.keys()].sort()) {
|
|
9353
|
+
const [a, b] = key.split(":").map(Number);
|
|
9354
|
+
addEdge(g, a, b, pairWeight.get(key));
|
|
9355
|
+
}
|
|
9356
|
+
const partition = louvain(g);
|
|
9357
|
+
const modularity = modularityOf(g, partition);
|
|
9358
|
+
const membersOf = /* @__PURE__ */ new Map();
|
|
9359
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
9360
|
+
const c = partition[i];
|
|
9361
|
+
const list = membersOf.get(c);
|
|
9362
|
+
if (list === void 0) membersOf.set(c, [ordered[i]]);
|
|
9363
|
+
else list.push(ordered[i]);
|
|
9364
|
+
}
|
|
9365
|
+
const inModuleDegree = /* @__PURE__ */ new Map();
|
|
9366
|
+
for (const [from, targets] of graph) {
|
|
9367
|
+
const a = idOf.get(from);
|
|
9368
|
+
if (a === void 0) continue;
|
|
9369
|
+
for (const to of targets) {
|
|
9370
|
+
const b = idOf.get(to);
|
|
9371
|
+
if (b === void 0 || a === b || partition[a] !== partition[b]) continue;
|
|
9372
|
+
inModuleDegree.set(from, (inModuleDegree.get(from) ?? 0) + 1);
|
|
9373
|
+
inModuleDegree.set(to, (inModuleDegree.get(to) ?? 0) + 1);
|
|
9374
|
+
}
|
|
9375
|
+
}
|
|
9376
|
+
const built = [...membersOf.entries()].map(([c, absFiles]) => {
|
|
9377
|
+
const display = absFiles.map((f) => toDisplayPath(root, f)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
9378
|
+
const coreAbs = [...absFiles].sort((a, b) => (inModuleDegree.get(b) ?? 0) - (inModuleDegree.get(a) ?? 0) || (a < b ? -1 : a > b ? 1 : 0))[0];
|
|
9379
|
+
return { community: c, core: toDisplayPath(root, coreAbs), commonDir: commonDirLabel(display), files: display, directories: new Set(display.map(dirOf)).size };
|
|
9380
|
+
}).sort((a, b) => b.files.length - a.files.length || (a.core < b.core ? -1 : a.core > b.core ? 1 : 0) || (a.files[0] < b.files[0] ? -1 : 1));
|
|
9381
|
+
const indexOfCommunity = /* @__PURE__ */ new Map();
|
|
9382
|
+
built.forEach((m, i) => indexOfCommunity.set(m.community, i + 1));
|
|
9383
|
+
const coreOfIndex = /* @__PURE__ */ new Map();
|
|
9384
|
+
built.forEach((m, i) => coreOfIndex.set(i + 1, m.core));
|
|
9385
|
+
const crossCount = /* @__PURE__ */ new Map();
|
|
9386
|
+
for (const [from, targets] of graph) {
|
|
9387
|
+
const a = idOf.get(from);
|
|
9388
|
+
if (a === void 0) continue;
|
|
9389
|
+
const ca = indexOfCommunity.get(partition[a]);
|
|
9390
|
+
if (ca === void 0) continue;
|
|
9391
|
+
for (const to of targets) {
|
|
9392
|
+
const b = idOf.get(to);
|
|
9393
|
+
if (b === void 0) continue;
|
|
9394
|
+
const cb = indexOfCommunity.get(partition[b]);
|
|
9395
|
+
if (cb === void 0 || ca === cb) continue;
|
|
9396
|
+
const key = `${ca}:${cb}`;
|
|
9397
|
+
crossCount.set(key, (crossCount.get(key) ?? 0) + 1);
|
|
9398
|
+
}
|
|
9399
|
+
}
|
|
9400
|
+
const allCross = [...crossCount.entries()].map(([key, imports2]) => {
|
|
9401
|
+
const [fromIndex, toIndex] = key.split(":").map(Number);
|
|
9402
|
+
return { fromIndex, toIndex, fromCore: coreOfIndex.get(fromIndex), toCore: coreOfIndex.get(toIndex), imports: imports2 };
|
|
9403
|
+
}).sort((a, b) => b.imports - a.imports || a.fromIndex - b.fromIndex || a.toIndex - b.toIndex);
|
|
9404
|
+
const modules = built.slice(0, top).map((m, i) => ({ index: i + 1, core: m.core, commonDir: m.commonDir, files: m.files, size: m.files.length, directories: m.directories }));
|
|
9405
|
+
return {
|
|
9406
|
+
modules,
|
|
9407
|
+
modulesTotal: built.length,
|
|
9408
|
+
modularity,
|
|
9409
|
+
isolatedCount,
|
|
9410
|
+
crossImports: allCross.slice(0, top),
|
|
9411
|
+
crossImportsTotal: allCross.length,
|
|
9412
|
+
noEdges: false
|
|
9413
|
+
};
|
|
9414
|
+
}
|
|
9415
|
+
function renderModules(result, top) {
|
|
9416
|
+
const lines = [];
|
|
9417
|
+
if (result.noEdges) {
|
|
9418
|
+
lines.push(`modules: none. No file in this project imports another by a relative path, so there is nothing to group (${countNoun(result.isolatedCount, "file")} checked).`);
|
|
9419
|
+
return lines;
|
|
9420
|
+
}
|
|
9421
|
+
const q = result.modularity;
|
|
9422
|
+
const strength = q === null ? "" : q < WEAK_MODULARITY ? `, modularity ${q.toFixed(2)} -- weak, so treat this grouping as a hint rather than a finding` : `, modularity ${q.toFixed(2)}`;
|
|
9423
|
+
lines.push(result.modules.length < result.modulesTotal ? `modules (top ${result.modules.length} of ${result.modulesTotal}${strength}):` : `modules (${result.modulesTotal} found${strength}):`);
|
|
9424
|
+
for (const m of result.modules) {
|
|
9425
|
+
const where = m.commonDir === null ? `across ${m.directories} directories` : m.commonDir === "." ? "all in the repository root" : `all in ${m.commonDir}`;
|
|
9426
|
+
lines.push(` #${m.index} ${m.size} files ${m.core} (${where})`);
|
|
9427
|
+
}
|
|
9428
|
+
if (result.isolatedCount > 0) {
|
|
9429
|
+
lines.push(` ${result.isolatedCount} files are in no module (they neither import nor are imported within the project).`);
|
|
9430
|
+
}
|
|
9431
|
+
if (result.crossImportsTotal === 0) {
|
|
9432
|
+
lines.push("cross-module imports: none. Every import stays inside its own module.");
|
|
9433
|
+
} else {
|
|
9434
|
+
lines.push(
|
|
9435
|
+
result.crossImports.length < result.crossImportsTotal ? `cross-module imports (top ${result.crossImports.length} of ${result.crossImportsTotal} pairs):` : `cross-module imports (${result.crossImportsTotal} pairs):`
|
|
9436
|
+
);
|
|
9437
|
+
for (const c of result.crossImports) {
|
|
9438
|
+
lines.push(` ${c.imports} imports #${c.fromIndex} ${c.fromCore} -> #${c.toIndex} ${c.toCore}`);
|
|
9439
|
+
}
|
|
9440
|
+
}
|
|
9441
|
+
if (result.modules.length < result.modulesTotal && result.crossImports.some((c) => c.fromIndex > top || c.toIndex > top)) {
|
|
9442
|
+
lines.push(` (a #N above ${top} refers to a module the --top cap left out; raise --top to see it.)`);
|
|
9443
|
+
}
|
|
9444
|
+
return lines;
|
|
9445
|
+
}
|
|
9446
|
+
|
|
9153
9447
|
// src/graph_commands.ts
|
|
9154
9448
|
var DEFAULT_REF_QUERY_LIMIT = 500;
|
|
9155
9449
|
var ALL_SYMBOLS_IN_FILE_LIMIT = 1e4;
|
|
@@ -10237,53 +10531,11 @@ function runArch(opts) {
|
|
|
10237
10531
|
}
|
|
10238
10532
|
const cwd = opts.cwd ?? process.cwd();
|
|
10239
10533
|
const top = opts.top ?? 10;
|
|
10240
|
-
const files =
|
|
10534
|
+
const { files, graph, importedBy, resolve: resolve5 } = buildImportGraph(cwd);
|
|
10241
10535
|
if (files.length === 0 && opts.json !== true) {
|
|
10242
10536
|
emit2(`no tracked files found under '${toDisplayPath(getDisplayRoot(cwd), cwd)}' (is it a git repo?). Nothing to analyse.`);
|
|
10243
10537
|
return 0;
|
|
10244
10538
|
}
|
|
10245
|
-
const filesByFoldedPath = /* @__PURE__ */ new Map();
|
|
10246
|
-
for (const f of files) filesByFoldedPath.set(foldPath(f), f);
|
|
10247
|
-
const graph = /* @__PURE__ */ new Map();
|
|
10248
|
-
const importedBy = /* @__PURE__ */ new Map();
|
|
10249
|
-
const resolveRelImport = (fromFile, spec) => {
|
|
10250
|
-
if (!spec.startsWith(".")) return null;
|
|
10251
|
-
const dir = path6.dirname(fromFile);
|
|
10252
|
-
const strippedSpec = spec.replace(/\.(m?js|cjs)$/, "");
|
|
10253
|
-
const base = path6.resolve(dir, strippedSpec);
|
|
10254
|
-
for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cjs", ".cts", ".py"]) {
|
|
10255
|
-
const candidate = base + ext;
|
|
10256
|
-
const match = filesByFoldedPath.get(foldPath(candidate));
|
|
10257
|
-
if (match !== void 0) return match;
|
|
10258
|
-
}
|
|
10259
|
-
const idx = path6.join(base, "index");
|
|
10260
|
-
for (const ext of [".ts", ".js", ".tsx", ".jsx", ".mts", ".cts"]) {
|
|
10261
|
-
const candidate = idx + ext;
|
|
10262
|
-
const match = filesByFoldedPath.get(foldPath(candidate));
|
|
10263
|
-
if (match !== void 0) return match;
|
|
10264
|
-
}
|
|
10265
|
-
return null;
|
|
10266
|
-
};
|
|
10267
|
-
for (const file of files) {
|
|
10268
|
-
let text;
|
|
10269
|
-
try {
|
|
10270
|
-
text = fs6.readFileSync(file, "utf8");
|
|
10271
|
-
} catch {
|
|
10272
|
-
continue;
|
|
10273
|
-
}
|
|
10274
|
-
const ext = importsExtensionFor(file);
|
|
10275
|
-
const rawImports = extractImports(text, ext);
|
|
10276
|
-
const internal = [];
|
|
10277
|
-
for (const spec of rawImports) {
|
|
10278
|
-
const resolved = resolveRelImport(file, spec);
|
|
10279
|
-
if (resolved !== null) {
|
|
10280
|
-
internal.push(resolved);
|
|
10281
|
-
if (!importedBy.has(resolved)) importedBy.set(resolved, /* @__PURE__ */ new Set());
|
|
10282
|
-
importedBy.get(resolved).add(file);
|
|
10283
|
-
}
|
|
10284
|
-
}
|
|
10285
|
-
graph.set(file, internal);
|
|
10286
|
-
}
|
|
10287
10539
|
const allHubs = [...importedBy.entries()].sort((a, b) => b[1].size - a[1].size).map(([f, importers]) => ({ file: f, importedBy: importers.size }));
|
|
10288
10540
|
const hubsTotal = allHubs.length;
|
|
10289
10541
|
const hubs = allHubs.slice(0, top);
|
|
@@ -10291,8 +10543,10 @@ function runArch(opts) {
|
|
|
10291
10543
|
const entryPointsTotal = allEntryPoints.length;
|
|
10292
10544
|
const entryPoints = allEntryPoints.slice(0, top);
|
|
10293
10545
|
const { cycles, truncated: cyclesTruncated } = findCyclesCapped(graph);
|
|
10546
|
+
const moduleResult = opts.modules === true ? detectModules({ files, graph, importedBy, resolve: resolve5 }, cwd, top) : null;
|
|
10294
10547
|
if (opts.json === true) {
|
|
10295
|
-
|
|
10548
|
+
const modulePayload = moduleResult === null ? {} : { modules: moduleResult.modules, modulesTotal: moduleResult.modulesTotal, modulesTruncated: moduleResult.modules.length < moduleResult.modulesTotal, modularity: moduleResult.modularity, isolatedCount: moduleResult.isolatedCount, crossImports: moduleResult.crossImports, crossImportsTotal: moduleResult.crossImportsTotal, crossImportsTruncated: moduleResult.crossImports.length < moduleResult.crossImportsTotal, noImportEdges: moduleResult.noEdges };
|
|
10549
|
+
emit2(JSON.stringify({ hubs, hubsTotal, hubsTruncated: hubs.length < hubsTotal, entryPoints, entryPointsTotal, entryPointsTruncated: entryPoints.length < entryPointsTotal, cycles, ...cyclesTruncated ? { cyclesTruncated: true } : {}, ...modulePayload }, null, 2));
|
|
10296
10550
|
return 0;
|
|
10297
10551
|
}
|
|
10298
10552
|
emit2(hubs.length < hubsTotal ? `hubs (top ${hubs.length} of ${hubsTotal} most-imported):` : `hubs (${hubsTotal} most-imported):`);
|
|
@@ -10301,6 +10555,7 @@ function runArch(opts) {
|
|
|
10301
10555
|
for (const e of entryPoints) emit2(` ${toDisplayPath(getDisplayRoot(opts.cwd), e.file)}`);
|
|
10302
10556
|
emit2(cyclesTruncated ? `cycles (first ${cycles.length}, truncated at the ${MAX_CYCLES}-cycle enumeration limit: more cycles exist):` : `cycles (${cycles.length} found):`);
|
|
10303
10557
|
for (const c of cycles) emit2(` ${c.map((f) => toDisplayPath(getDisplayRoot(opts.cwd), f)).join(" -> ")}`);
|
|
10558
|
+
if (moduleResult !== null) for (const line of renderModules(moduleResult, top)) emit2(line);
|
|
10304
10559
|
return 0;
|
|
10305
10560
|
}
|
|
10306
10561
|
function runBlame(opts) {
|
|
@@ -10556,10 +10811,10 @@ function resolveHandoff(name, opts = {}) {
|
|
|
10556
10811
|
}
|
|
10557
10812
|
|
|
10558
10813
|
export {
|
|
10559
|
-
getTrackedFiles,
|
|
10560
10814
|
extractSection,
|
|
10561
10815
|
readSection,
|
|
10562
10816
|
listSections,
|
|
10817
|
+
buildImportGraph,
|
|
10563
10818
|
ALL_SYMBOLS_IN_FILE_LIMIT,
|
|
10564
10819
|
enclosingSymbol,
|
|
10565
10820
|
runCallers,
|