truecourse 0.4.3 → 0.4.5
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/cli.mjs +764 -798
- package/package.json +1 -1
- package/public/assets/{index-CCtFa3au.js → index-8ZCcQOMQ.js} +124 -124
- package/public/index.html +1 -1
- package/server.mjs +1113 -1128
package/cli.mjs
CHANGED
|
@@ -3492,9 +3492,7 @@ var require_src = __commonJS({
|
|
|
3492
3492
|
|
|
3493
3493
|
// node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
|
|
3494
3494
|
import { stdout as S, stdin as $ } from "node:process";
|
|
3495
|
-
import * as _ from "node:readline";
|
|
3496
3495
|
import P from "node:readline";
|
|
3497
|
-
import { ReadStream as D } from "node:tty";
|
|
3498
3496
|
function d(r, t2, e) {
|
|
3499
3497
|
if (!e.some((o) => !o.disabled)) return r;
|
|
3500
3498
|
const s = r + t2, i = Math.max(e.length - 1, 0), n = s < 0 ? i : s > i ? 0 : s;
|
|
@@ -3520,27 +3518,6 @@ function w(r, t2) {
|
|
|
3520
3518
|
const e = r;
|
|
3521
3519
|
e.isTTY && e.setRawMode(t2);
|
|
3522
3520
|
}
|
|
3523
|
-
function z({ input: r = $, output: t2 = S, overwrite: e = true, hideCursor: s = true } = {}) {
|
|
3524
|
-
const i = _.createInterface({ input: r, output: t2, prompt: "", tabSize: 1 });
|
|
3525
|
-
_.emitKeypressEvents(r, i), r instanceof D && r.isTTY && r.setRawMode(true);
|
|
3526
|
-
const n = (o, { name: a, sequence: h }) => {
|
|
3527
|
-
const l = String(o);
|
|
3528
|
-
if (V([l, a, h], "cancel")) {
|
|
3529
|
-
s && t2.write(import_sisteransi.cursor.show), process.exit(0);
|
|
3530
|
-
return;
|
|
3531
|
-
}
|
|
3532
|
-
if (!e) return;
|
|
3533
|
-
const f = a === "return" ? 0 : -1, v = a === "return" ? -1 : 0;
|
|
3534
|
-
_.moveCursor(t2, f, v, () => {
|
|
3535
|
-
_.clearLine(t2, 1, () => {
|
|
3536
|
-
r.once("keypress", n);
|
|
3537
|
-
});
|
|
3538
|
-
});
|
|
3539
|
-
};
|
|
3540
|
-
return s && t2.write(import_sisteransi.cursor.hide), r.once("keypress", n), () => {
|
|
3541
|
-
r.off("keypress", n), s && t2.write(import_sisteransi.cursor.show), r instanceof D && r.isTTY && !Y && r.setRawMode(false), i.terminal = false, i.close();
|
|
3542
|
-
};
|
|
3543
|
-
}
|
|
3544
3521
|
function R(r, t2, e, s = e) {
|
|
3545
3522
|
const i = O(r ?? S);
|
|
3546
3523
|
return wrapAnsi(t2, i - e.length, { hard: true, trim: false }).split(`
|
|
@@ -3734,7 +3711,7 @@ import P2 from "node:process";
|
|
|
3734
3711
|
function Ze() {
|
|
3735
3712
|
return P2.platform !== "win32" ? P2.env.TERM !== "linux" : !!P2.env.CI || !!P2.env.WT_SESSION || !!P2.env.TERMINUS_SUBLIME || P2.env.ConEmuTask === "{cmd::Cmder}" || P2.env.TERM_PROGRAM === "Terminus-Sublime" || P2.env.TERM_PROGRAM === "vscode" || P2.env.TERM === "xterm-256color" || P2.env.TERM === "alacritty" || P2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
3736
3713
|
}
|
|
3737
|
-
var import_sisteransi2, ee,
|
|
3714
|
+
var import_sisteransi2, ee, w2, _e, oe, ue, F, le, d2, E2, Ie, Ee, z2, H2, te, U, J, xe, se, ce, Ge, $e, de, Oe, he, pe, me, ge, V2, ye, et2, Y2, ot2, O2, pt, mt, gt, Ve, re, _t, je;
|
|
3738
3715
|
var init_dist4 = __esm({
|
|
3739
3716
|
"node_modules/.pnpm/@clack+prompts@1.2.0/node_modules/@clack/prompts/dist/index.mjs"() {
|
|
3740
3717
|
init_dist3();
|
|
@@ -3743,7 +3720,6 @@ var init_dist4 = __esm({
|
|
|
3743
3720
|
init_dist2();
|
|
3744
3721
|
import_sisteransi2 = __toESM(require_src(), 1);
|
|
3745
3722
|
ee = Ze();
|
|
3746
|
-
ae = () => process.env.CI === "true";
|
|
3747
3723
|
w2 = (e, i) => ee ? e : i;
|
|
3748
3724
|
_e = w2("\u25C6", "*");
|
|
3749
3725
|
oe = w2("\u25A0", "x");
|
|
@@ -3820,7 +3796,7 @@ var init_dist4 = __esm({
|
|
|
3820
3796
|
}
|
|
3821
3797
|
if (f > $2) {
|
|
3822
3798
|
let b = 0, x = 0, G2 = f;
|
|
3823
|
-
const M2 = e - v, R2 = (j2,
|
|
3799
|
+
const M2 = e - v, R2 = (j2, D) => et2(h, G2, j2, D, $2);
|
|
3824
3800
|
m ? ({ lineCount: G2, removals: b } = R2(0, M2), G2 > $2 && ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length))) : ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length), G2 > $2 && ({ lineCount: G2, removals: b } = R2(0, M2))), b > 0 && (m = true, h.splice(0, b)), x > 0 && (g = true, h.splice(h.length - x, x));
|
|
3825
3801
|
}
|
|
3826
3802
|
const C2 = [];
|
|
@@ -3899,59 +3875,6 @@ ${t("gray", E2)} ` : "";
|
|
|
3899
3875
|
|
|
3900
3876
|
`);
|
|
3901
3877
|
};
|
|
3902
|
-
Ct = (e) => t("magenta", e);
|
|
3903
|
-
fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, cancelMessage: r, errorMessage: u2, frames: n = ee ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: o = ee ? 80 : 120, signal: c2, ...a } = {}) => {
|
|
3904
|
-
const l = ae();
|
|
3905
|
-
let $2, y, p2 = false, m = false, g = "", S2, h = performance.now();
|
|
3906
|
-
const f = O(s), v = a?.styleFrame ?? Ct, T = (_2) => {
|
|
3907
|
-
const A2 = _2 > 1 ? u2 ?? u.messages.error : r ?? u.messages.cancel;
|
|
3908
|
-
m = _2 === 1, p2 && (W(A2, _2), m && typeof i == "function" && i());
|
|
3909
|
-
}, C2 = () => T(2), b = () => T(1), x = () => {
|
|
3910
|
-
process.on("uncaughtExceptionMonitor", C2), process.on("unhandledRejection", C2), process.on("SIGINT", b), process.on("SIGTERM", b), process.on("exit", T), c2 && c2.addEventListener("abort", b);
|
|
3911
|
-
}, G2 = () => {
|
|
3912
|
-
process.removeListener("uncaughtExceptionMonitor", C2), process.removeListener("unhandledRejection", C2), process.removeListener("SIGINT", b), process.removeListener("SIGTERM", b), process.removeListener("exit", T), c2 && c2.removeEventListener("abort", b);
|
|
3913
|
-
}, M2 = () => {
|
|
3914
|
-
if (S2 === void 0) return;
|
|
3915
|
-
l && s.write(`
|
|
3916
|
-
`);
|
|
3917
|
-
const _2 = wrapAnsi(S2, f, { hard: true, trim: false }).split(`
|
|
3918
|
-
`);
|
|
3919
|
-
_2.length > 1 && s.write(import_sisteransi2.cursor.up(_2.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
|
|
3920
|
-
}, R2 = (_2) => _2.replace(/\.+$/, ""), j2 = (_2) => {
|
|
3921
|
-
const A2 = (performance.now() - _2) / 1e3, k = Math.floor(A2 / 60), L = Math.floor(A2 % 60);
|
|
3922
|
-
return k > 0 ? `[${k}m ${L}s]` : `[${L}s]`;
|
|
3923
|
-
}, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
|
|
3924
|
-
p2 = true, $2 = z({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${t("gray", d2)}
|
|
3925
|
-
`);
|
|
3926
|
-
let A2 = 0, k = 0;
|
|
3927
|
-
x(), y = setInterval(() => {
|
|
3928
|
-
if (l && g === S2) return;
|
|
3929
|
-
M2(), S2 = g;
|
|
3930
|
-
const L = v(n[A2]);
|
|
3931
|
-
let Z;
|
|
3932
|
-
if (l) Z = `${L} ${g}...`;
|
|
3933
|
-
else if (e === "timer") Z = `${L} ${g} ${j2(h)}`;
|
|
3934
|
-
else {
|
|
3935
|
-
const Be = ".".repeat(Math.floor(k)).slice(0, 3);
|
|
3936
|
-
Z = `${L} ${g}${Be}`;
|
|
3937
|
-
}
|
|
3938
|
-
const Ne = wrapAnsi(Z, f, { hard: true, trim: false });
|
|
3939
|
-
s.write(Ne), A2 = A2 + 1 < n.length ? A2 + 1 : 0, k = k < 4 ? k + 0.125 : 0;
|
|
3940
|
-
}, o);
|
|
3941
|
-
}, W = (_2 = "", A2 = 0, k = false) => {
|
|
3942
|
-
if (!p2) return;
|
|
3943
|
-
p2 = false, clearInterval(y), M2();
|
|
3944
|
-
const L = A2 === 0 ? t("green", F) : A2 === 1 ? t("red", oe) : t("red", ue);
|
|
3945
|
-
g = _2 ?? g, k || (e === "timer" ? s.write(`${L} ${g} ${j2(h)}
|
|
3946
|
-
`) : s.write(`${L} ${g}
|
|
3947
|
-
`)), G2(), $2();
|
|
3948
|
-
};
|
|
3949
|
-
return { start: ie, stop: (_2 = "") => W(_2, 0), message: (_2 = "") => {
|
|
3950
|
-
g = R2(_2 ?? g);
|
|
3951
|
-
}, cancel: (_2 = "") => W(_2, 1), error: (_2 = "") => W(_2, 2), clear: () => W("", 0, true), get isCancelled() {
|
|
3952
|
-
return m;
|
|
3953
|
-
} };
|
|
3954
|
-
};
|
|
3955
3878
|
Ve = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
|
|
3956
3879
|
re = (e, i) => e.includes(`
|
|
3957
3880
|
`) ? e.split(`
|
|
@@ -4425,32 +4348,184 @@ var init_helpers = __esm({
|
|
|
4425
4348
|
}
|
|
4426
4349
|
});
|
|
4427
4350
|
|
|
4428
|
-
// apps/server/dist/lib/
|
|
4351
|
+
// apps/server/dist/lib/atomic-write.js
|
|
4429
4352
|
import fs4 from "node:fs";
|
|
4430
4353
|
import path4 from "node:path";
|
|
4354
|
+
function atomicWriteJson(targetPath, data) {
|
|
4355
|
+
fs4.mkdirSync(path4.dirname(targetPath), { recursive: true });
|
|
4356
|
+
const tmp = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
4357
|
+
fs4.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
4358
|
+
fs4.renameSync(tmp, targetPath);
|
|
4359
|
+
}
|
|
4360
|
+
function lockPath(repoPath) {
|
|
4361
|
+
return path4.join(repoPath, ".truecourse", LOCK_FILENAME);
|
|
4362
|
+
}
|
|
4363
|
+
function acquireAnalyzeLock(repoPath) {
|
|
4364
|
+
const file = lockPath(repoPath);
|
|
4365
|
+
fs4.mkdirSync(path4.dirname(file), { recursive: true });
|
|
4366
|
+
try {
|
|
4367
|
+
const fd = fs4.openSync(file, "wx");
|
|
4368
|
+
fs4.writeSync(fd, `${process.pid}
|
|
4369
|
+
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
4370
|
+
`);
|
|
4371
|
+
fs4.closeSync(fd);
|
|
4372
|
+
} catch (err) {
|
|
4373
|
+
if (err.code === "EEXIST") {
|
|
4374
|
+
let owner = null;
|
|
4375
|
+
try {
|
|
4376
|
+
owner = parseInt(fs4.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
4377
|
+
} catch {
|
|
4378
|
+
}
|
|
4379
|
+
throw new AnalyzeLockError(repoPath, owner);
|
|
4380
|
+
}
|
|
4381
|
+
throw err;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
function releaseAnalyzeLock(repoPath) {
|
|
4385
|
+
try {
|
|
4386
|
+
fs4.unlinkSync(lockPath(repoPath));
|
|
4387
|
+
} catch (err) {
|
|
4388
|
+
if (err.code !== "ENOENT")
|
|
4389
|
+
throw err;
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
var LOCK_FILENAME, AnalyzeLockError;
|
|
4393
|
+
var init_atomic_write = __esm({
|
|
4394
|
+
"apps/server/dist/lib/atomic-write.js"() {
|
|
4395
|
+
"use strict";
|
|
4396
|
+
LOCK_FILENAME = ".analyze.lock";
|
|
4397
|
+
AnalyzeLockError = class extends Error {
|
|
4398
|
+
constructor(repoPath, ownerPid) {
|
|
4399
|
+
const who = ownerPid != null ? ` (held by pid ${ownerPid})` : "";
|
|
4400
|
+
super(`Another analyze is already running for ${repoPath}${who}. If you're sure no analyze is in progress, remove ${lockPath(repoPath)} and retry.`);
|
|
4401
|
+
this.name = "AnalyzeLockError";
|
|
4402
|
+
}
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
});
|
|
4406
|
+
|
|
4407
|
+
// apps/server/dist/lib/analysis-store.js
|
|
4408
|
+
import fs5 from "node:fs";
|
|
4409
|
+
import path5 from "node:path";
|
|
4410
|
+
function storeDir(repoPath) {
|
|
4411
|
+
return path5.join(repoPath, TRUECOURSE_DIR2);
|
|
4412
|
+
}
|
|
4413
|
+
function analysesDir(repoPath) {
|
|
4414
|
+
return path5.join(storeDir(repoPath), ANALYSES_DIR);
|
|
4415
|
+
}
|
|
4416
|
+
function analysisFilePath(repoPath, filename) {
|
|
4417
|
+
return path5.join(analysesDir(repoPath), filename);
|
|
4418
|
+
}
|
|
4419
|
+
function latestPath(repoPath) {
|
|
4420
|
+
return path5.join(storeDir(repoPath), LATEST_FILE);
|
|
4421
|
+
}
|
|
4422
|
+
function historyPath(repoPath) {
|
|
4423
|
+
return path5.join(storeDir(repoPath), HISTORY_FILE);
|
|
4424
|
+
}
|
|
4425
|
+
function diffPath(repoPath) {
|
|
4426
|
+
return path5.join(storeDir(repoPath), DIFF_FILE);
|
|
4427
|
+
}
|
|
4428
|
+
function buildAnalysisFilename(analysisId, createdAt) {
|
|
4429
|
+
const iso = createdAt.replace(/[:.]/g, "-").replace(/-\d{3}Z$/, "Z");
|
|
4430
|
+
const shortId = analysisId.replace(/-/g, "").slice(0, 8);
|
|
4431
|
+
return `${iso}_${shortId}.json`;
|
|
4432
|
+
}
|
|
4433
|
+
function readLatest(repoPath) {
|
|
4434
|
+
const file = latestPath(repoPath);
|
|
4435
|
+
let mtime;
|
|
4436
|
+
try {
|
|
4437
|
+
mtime = fs5.statSync(file).mtimeMs;
|
|
4438
|
+
} catch (err) {
|
|
4439
|
+
if (err.code === "ENOENT") {
|
|
4440
|
+
latestCache.delete(repoPath);
|
|
4441
|
+
return null;
|
|
4442
|
+
}
|
|
4443
|
+
throw err;
|
|
4444
|
+
}
|
|
4445
|
+
const cached = latestCache.get(repoPath);
|
|
4446
|
+
if (cached && cached.mtime === mtime)
|
|
4447
|
+
return cached.data;
|
|
4448
|
+
const data = JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4449
|
+
latestCache.set(repoPath, { mtime, data });
|
|
4450
|
+
return data;
|
|
4451
|
+
}
|
|
4452
|
+
function writeLatest(repoPath, latest) {
|
|
4453
|
+
atomicWriteJson(latestPath(repoPath), latest);
|
|
4454
|
+
latestCache.delete(repoPath);
|
|
4455
|
+
}
|
|
4456
|
+
function writeAnalysis(repoPath, snapshot) {
|
|
4457
|
+
const filename = buildAnalysisFilename(snapshot.id, snapshot.createdAt);
|
|
4458
|
+
atomicWriteJson(analysisFilePath(repoPath, filename), snapshot);
|
|
4459
|
+
return { filename, snapshot };
|
|
4460
|
+
}
|
|
4461
|
+
function readHistory(repoPath) {
|
|
4462
|
+
const file = historyPath(repoPath);
|
|
4463
|
+
if (!fs5.existsSync(file))
|
|
4464
|
+
return { analyses: [] };
|
|
4465
|
+
return JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4466
|
+
}
|
|
4467
|
+
function appendHistory(repoPath, entry) {
|
|
4468
|
+
const history = readHistory(repoPath);
|
|
4469
|
+
history.analyses.push(entry);
|
|
4470
|
+
atomicWriteJson(historyPath(repoPath), history);
|
|
4471
|
+
}
|
|
4472
|
+
function readDiff(repoPath) {
|
|
4473
|
+
const file = diffPath(repoPath);
|
|
4474
|
+
if (!fs5.existsSync(file))
|
|
4475
|
+
return null;
|
|
4476
|
+
return JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4477
|
+
}
|
|
4478
|
+
function writeDiff(repoPath, diff) {
|
|
4479
|
+
atomicWriteJson(diffPath(repoPath), diff);
|
|
4480
|
+
}
|
|
4481
|
+
function deleteDiff(repoPath) {
|
|
4482
|
+
try {
|
|
4483
|
+
fs5.unlinkSync(diffPath(repoPath));
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
if (err.code !== "ENOENT")
|
|
4486
|
+
throw err;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
var TRUECOURSE_DIR2, ANALYSES_DIR, LATEST_FILE, HISTORY_FILE, DIFF_FILE, latestCache;
|
|
4490
|
+
var init_analysis_store = __esm({
|
|
4491
|
+
"apps/server/dist/lib/analysis-store.js"() {
|
|
4492
|
+
"use strict";
|
|
4493
|
+
init_atomic_write();
|
|
4494
|
+
TRUECOURSE_DIR2 = ".truecourse";
|
|
4495
|
+
ANALYSES_DIR = "analyses";
|
|
4496
|
+
LATEST_FILE = "LATEST.json";
|
|
4497
|
+
HISTORY_FILE = "history.json";
|
|
4498
|
+
DIFF_FILE = "diff.json";
|
|
4499
|
+
latestCache = /* @__PURE__ */ new Map();
|
|
4500
|
+
}
|
|
4501
|
+
});
|
|
4502
|
+
|
|
4503
|
+
// apps/server/dist/lib/logger.js
|
|
4504
|
+
import fs6 from "node:fs";
|
|
4505
|
+
import path6 from "node:path";
|
|
4431
4506
|
function rotateLog(filePath) {
|
|
4432
|
-
if (!
|
|
4507
|
+
if (!fs6.existsSync(filePath))
|
|
4433
4508
|
return;
|
|
4434
|
-
const stats =
|
|
4509
|
+
const stats = fs6.statSync(filePath);
|
|
4435
4510
|
if (stats.size < MAX_LOG_SIZE)
|
|
4436
4511
|
return;
|
|
4437
4512
|
for (let i = MAX_LOG_FILES; i >= 1; i--) {
|
|
4438
4513
|
const older = `${filePath}.${i}`;
|
|
4439
4514
|
if (i === MAX_LOG_FILES) {
|
|
4440
|
-
if (
|
|
4441
|
-
|
|
4515
|
+
if (fs6.existsSync(older))
|
|
4516
|
+
fs6.unlinkSync(older);
|
|
4442
4517
|
} else {
|
|
4443
4518
|
const newer = `${filePath}.${i + 1}`;
|
|
4444
|
-
if (
|
|
4445
|
-
|
|
4519
|
+
if (fs6.existsSync(older))
|
|
4520
|
+
fs6.renameSync(older, newer);
|
|
4446
4521
|
}
|
|
4447
4522
|
}
|
|
4448
|
-
|
|
4523
|
+
fs6.renameSync(filePath, `${filePath}.1`);
|
|
4449
4524
|
}
|
|
4450
4525
|
function openSink(config2) {
|
|
4451
|
-
|
|
4526
|
+
fs6.mkdirSync(path6.dirname(config2.filePath), { recursive: true });
|
|
4452
4527
|
rotateLog(config2.filePath);
|
|
4453
|
-
const stream =
|
|
4528
|
+
const stream = fs6.createWriteStream(config2.filePath, { flags: "a" });
|
|
4454
4529
|
stream.write(`
|
|
4455
4530
|
--- ${(/* @__PURE__ */ new Date()).toISOString()} ---
|
|
4456
4531
|
`);
|
|
@@ -5082,7 +5157,7 @@ var require_node = __commonJS({
|
|
|
5082
5157
|
exports.inspectOpts = Object.keys(process.env).filter((key) => {
|
|
5083
5158
|
return /^debug_/i.test(key);
|
|
5084
5159
|
}).reduce((obj, key) => {
|
|
5085
|
-
const prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, (
|
|
5160
|
+
const prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, (_, k) => {
|
|
5086
5161
|
return k.toUpperCase();
|
|
5087
5162
|
});
|
|
5088
5163
|
let val = process.env[key];
|
|
@@ -6558,7 +6633,7 @@ function deleteBranchTask(branch, forceDelete = false) {
|
|
|
6558
6633
|
parser(stdOut, stdErr) {
|
|
6559
6634
|
return parseBranchDeletions(stdOut, stdErr).branches[branch];
|
|
6560
6635
|
},
|
|
6561
|
-
onError({ exitCode, stdErr, stdOut }, error,
|
|
6636
|
+
onError({ exitCode, stdErr, stdOut }, error, _, fail) {
|
|
6562
6637
|
if (!hasBranchDeletionError(String(error), exitCode)) {
|
|
6563
6638
|
return fail(error);
|
|
6564
6639
|
}
|
|
@@ -9762,20 +9837,20 @@ var init_git = __esm({
|
|
|
9762
9837
|
});
|
|
9763
9838
|
|
|
9764
9839
|
// apps/server/dist/config/project-config.js
|
|
9765
|
-
import
|
|
9840
|
+
import fs7 from "node:fs";
|
|
9766
9841
|
function readProjectConfig(repoDir) {
|
|
9767
9842
|
const file = getRepoConfigPath(repoDir);
|
|
9768
|
-
if (!
|
|
9843
|
+
if (!fs7.existsSync(file))
|
|
9769
9844
|
return { ...EMPTY };
|
|
9770
9845
|
try {
|
|
9771
|
-
return JSON.parse(
|
|
9846
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
9772
9847
|
} catch {
|
|
9773
9848
|
return { ...EMPTY };
|
|
9774
9849
|
}
|
|
9775
9850
|
}
|
|
9776
9851
|
function writeProjectConfig(repoDir, config2) {
|
|
9777
9852
|
ensureRepoTruecourseDir(repoDir);
|
|
9778
|
-
|
|
9853
|
+
fs7.writeFileSync(getRepoConfigPath(repoDir), JSON.stringify(config2, null, 2), "utf-8");
|
|
9779
9854
|
}
|
|
9780
9855
|
function updateProjectConfig(repoDir, patch) {
|
|
9781
9856
|
const current = readProjectConfig(repoDir);
|
|
@@ -9844,7 +9919,7 @@ var require_ignore = __commonJS({
|
|
|
9844
9919
|
// (a ) -> (a)
|
|
9845
9920
|
// (a \ ) -> (a )
|
|
9846
9921
|
/((?:\\\\)*?)(\\?\s+)$/,
|
|
9847
|
-
(
|
|
9922
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY2)
|
|
9848
9923
|
],
|
|
9849
9924
|
// Replace (\ ) with ' '
|
|
9850
9925
|
// (\ ) -> ' '
|
|
@@ -9852,7 +9927,7 @@ var require_ignore = __commonJS({
|
|
|
9852
9927
|
// (\\\ ) -> '\\ '
|
|
9853
9928
|
[
|
|
9854
9929
|
/(\\+?)\s/g,
|
|
9855
|
-
(
|
|
9930
|
+
(_, m1) => {
|
|
9856
9931
|
const { length } = m1;
|
|
9857
9932
|
return m1.slice(0, length - length % 2) + SPACE;
|
|
9858
9933
|
}
|
|
@@ -9923,7 +9998,7 @@ var require_ignore = __commonJS({
|
|
|
9923
9998
|
// Zero, one or several directories
|
|
9924
9999
|
// should not use '*', or it will be replaced by the next replacer
|
|
9925
10000
|
// Check if it is not the last `'/**'`
|
|
9926
|
-
(
|
|
10001
|
+
(_, index, str2) => index + 6 < str2.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
9927
10002
|
],
|
|
9928
10003
|
// normal intermediate wildcards
|
|
9929
10004
|
[
|
|
@@ -9935,7 +10010,7 @@ var require_ignore = __commonJS({
|
|
|
9935
10010
|
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
9936
10011
|
// '*.js' matches '.js'
|
|
9937
10012
|
// '*.js' doesn't match 'abc'
|
|
9938
|
-
(
|
|
10013
|
+
(_, p1, p2) => {
|
|
9939
10014
|
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
9940
10015
|
return p1 + unescaped;
|
|
9941
10016
|
}
|
|
@@ -9982,11 +10057,11 @@ var require_ignore = __commonJS({
|
|
|
9982
10057
|
var MODE_CHECK_IGNORE = "checkRegex";
|
|
9983
10058
|
var UNDERSCORE = "_";
|
|
9984
10059
|
var TRAILING_WILD_CARD_REPLACERS = {
|
|
9985
|
-
[MODE_IGNORE](
|
|
10060
|
+
[MODE_IGNORE](_, p1) {
|
|
9986
10061
|
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
9987
10062
|
return `${prefix}(?=$|\\/$)`;
|
|
9988
10063
|
},
|
|
9989
|
-
[MODE_CHECK_IGNORE](
|
|
10064
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
9990
10065
|
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
9991
10066
|
return `${prefix}(?=$|\\/$)`;
|
|
9992
10067
|
}
|
|
@@ -14772,7 +14847,7 @@ var init_ast = __esm({
|
|
|
14772
14847
|
if (!isExtglobAST(this)) {
|
|
14773
14848
|
const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
|
|
14774
14849
|
const src = this.#parts.map((p2) => {
|
|
14775
|
-
const [re2,
|
|
14850
|
+
const [re2, _, hasMagic, uflag] = typeof p2 === "string" ? _a2.#parseGlob(p2, this.#hasMagic, noEmpty) : p2.toRegExpSource(allowDot);
|
|
14776
14851
|
this.#hasMagic = this.#hasMagic || hasMagic;
|
|
14777
14852
|
this.#uflag = this.#uflag || uflag;
|
|
14778
14853
|
return re2;
|
|
@@ -14878,7 +14953,7 @@ var init_ast = __esm({
|
|
|
14878
14953
|
if (typeof p2 === "string") {
|
|
14879
14954
|
throw new Error("string type in extglob ast??");
|
|
14880
14955
|
}
|
|
14881
|
-
const [re2,
|
|
14956
|
+
const [re2, _, _hasMagic, uflag] = p2.toRegExpSource(dot);
|
|
14882
14957
|
this.#uflag = this.#uflag || uflag;
|
|
14883
14958
|
return re2;
|
|
14884
14959
|
}).filter((p2) => !(this.isStart() && this.isEnd()) || !!p2).join("|");
|
|
@@ -14951,7 +15026,7 @@ var init_escape = __esm({
|
|
|
14951
15026
|
});
|
|
14952
15027
|
|
|
14953
15028
|
// node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js
|
|
14954
|
-
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform,
|
|
15029
|
+
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path7, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
|
|
14955
15030
|
var init_esm4 = __esm({
|
|
14956
15031
|
"node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js"() {
|
|
14957
15032
|
init_esm3();
|
|
@@ -15020,11 +15095,11 @@ var init_esm4 = __esm({
|
|
|
15020
15095
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
15021
15096
|
};
|
|
15022
15097
|
defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
15023
|
-
|
|
15098
|
+
path7 = {
|
|
15024
15099
|
win32: { sep: "\\" },
|
|
15025
15100
|
posix: { sep: "/" }
|
|
15026
15101
|
};
|
|
15027
|
-
sep = defaultPlatform === "win32" ?
|
|
15102
|
+
sep = defaultPlatform === "win32" ? path7.win32.sep : path7.posix.sep;
|
|
15028
15103
|
minimatch.sep = sep;
|
|
15029
15104
|
GLOBSTAR = Symbol("globstar **");
|
|
15030
15105
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -15151,7 +15226,7 @@ var init_esm4 = __esm({
|
|
|
15151
15226
|
}
|
|
15152
15227
|
return false;
|
|
15153
15228
|
}
|
|
15154
|
-
debug(...
|
|
15229
|
+
debug(..._) {
|
|
15155
15230
|
}
|
|
15156
15231
|
make() {
|
|
15157
15232
|
const pattern = this.pattern;
|
|
@@ -15173,7 +15248,7 @@ var init_esm4 = __esm({
|
|
|
15173
15248
|
const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
|
|
15174
15249
|
this.globParts = this.preprocess(rawGlobParts);
|
|
15175
15250
|
this.debug(this.pattern, this.globParts);
|
|
15176
|
-
let set2 = this.globParts.map((s,
|
|
15251
|
+
let set2 = this.globParts.map((s, _, __) => {
|
|
15177
15252
|
if (this.isWindows && this.windowsNoMagicRoot) {
|
|
15178
15253
|
const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
|
|
15179
15254
|
const isDrive = /^[a-z]:/i.test(s[0]);
|
|
@@ -17070,7 +17145,7 @@ var init_database_detector = __esm({
|
|
|
17070
17145
|
});
|
|
17071
17146
|
|
|
17072
17147
|
// packages/analyzer/dist/module-extractor.js
|
|
17073
|
-
import
|
|
17148
|
+
import path8 from "path";
|
|
17074
17149
|
function extractModulesAndMethods(analyses, layerDetails, fileDependencies) {
|
|
17075
17150
|
const modules = [];
|
|
17076
17151
|
const methods = [];
|
|
@@ -17217,7 +17292,7 @@ function deriveModuleName(analysis) {
|
|
|
17217
17292
|
return localExports[0].name;
|
|
17218
17293
|
}
|
|
17219
17294
|
const baseName = fileBaseName(analysis.filePath);
|
|
17220
|
-
const strippedName = stripExtension(
|
|
17295
|
+
const strippedName = stripExtension(path8.basename(analysis.filePath));
|
|
17221
17296
|
if (INDEX_NAMES.has(strippedName)) {
|
|
17222
17297
|
return baseName;
|
|
17223
17298
|
}
|
|
@@ -17234,10 +17309,10 @@ function deriveModuleName(analysis) {
|
|
|
17234
17309
|
return baseName;
|
|
17235
17310
|
}
|
|
17236
17311
|
function deriveNextjsRouteName(filePath) {
|
|
17237
|
-
const base =
|
|
17312
|
+
const base = path8.basename(filePath).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
17238
17313
|
if (base !== "route" && base !== "page")
|
|
17239
17314
|
return null;
|
|
17240
|
-
const parts = filePath.split(
|
|
17315
|
+
const parts = filePath.split(path8.sep);
|
|
17241
17316
|
const appIdx = parts.lastIndexOf("app");
|
|
17242
17317
|
if (appIdx === -1)
|
|
17243
17318
|
return null;
|
|
@@ -17255,9 +17330,9 @@ function stripExtension(filename) {
|
|
|
17255
17330
|
return filename;
|
|
17256
17331
|
}
|
|
17257
17332
|
function fileBaseName(filePath) {
|
|
17258
|
-
const name = stripExtension(
|
|
17333
|
+
const name = stripExtension(path8.basename(filePath));
|
|
17259
17334
|
if (INDEX_NAMES.has(name)) {
|
|
17260
|
-
return
|
|
17335
|
+
return path8.basename(path8.dirname(filePath));
|
|
17261
17336
|
}
|
|
17262
17337
|
return name;
|
|
17263
17338
|
}
|
|
@@ -44020,10 +44095,10 @@ var init_secret_rules = __esm({
|
|
|
44020
44095
|
});
|
|
44021
44096
|
|
|
44022
44097
|
// packages/analyzer/dist/rules/security/secret-scanner.js
|
|
44023
|
-
import
|
|
44098
|
+
import path9 from "node:path";
|
|
44024
44099
|
function isSensitiveFile(filePath) {
|
|
44025
|
-
const basename2 =
|
|
44026
|
-
const ext2 =
|
|
44100
|
+
const basename2 = path9.basename(filePath);
|
|
44101
|
+
const ext2 = path9.extname(filePath);
|
|
44027
44102
|
if (basename2.startsWith(".env")) {
|
|
44028
44103
|
const envVariant = basename2;
|
|
44029
44104
|
if (SENSITIVE_FILE_EXTENSIONS.has(envVariant)) {
|
|
@@ -86706,7 +86781,7 @@ var init_js_naming_convention = __esm({
|
|
|
86706
86781
|
return null;
|
|
86707
86782
|
}
|
|
86708
86783
|
if (funcName.includes("_") && !funcName.startsWith("_")) {
|
|
86709
|
-
return makeViolation(this.ruleKey, node, filePath, "low", "Function uses snake_case naming", `Function '${funcName}' uses snake_case. JavaScript convention is camelCase.`, sourceCode, `Rename to camelCase: ${funcName.replace(/_([a-z])/g, (
|
|
86784
|
+
return makeViolation(this.ruleKey, node, filePath, "low", "Function uses snake_case naming", `Function '${funcName}' uses snake_case. JavaScript convention is camelCase.`, sourceCode, `Rename to camelCase: ${funcName.replace(/_([a-z])/g, (_, c2) => c2.toUpperCase())}.`);
|
|
86710
86785
|
}
|
|
86711
86786
|
return null;
|
|
86712
86787
|
}
|
|
@@ -93624,7 +93699,7 @@ var init_dist6 = __esm({
|
|
|
93624
93699
|
});
|
|
93625
93700
|
|
|
93626
93701
|
// apps/server/dist/services/analyzer.service.js
|
|
93627
|
-
import
|
|
93702
|
+
import path10 from "node:path";
|
|
93628
93703
|
function runDeterministicModuleChecks(result, enabledDeterministic) {
|
|
93629
93704
|
if (!result.modules || !result.methods)
|
|
93630
93705
|
return [];
|
|
@@ -93667,7 +93742,7 @@ async function runAnalysis(repoPath, _branch, onProgress, options) {
|
|
|
93667
93742
|
const statusResult = await git.status();
|
|
93668
93743
|
hasChanges = !statusResult.isClean();
|
|
93669
93744
|
const gitRoot = (await git.revparse(["--show-toplevel"])).trim();
|
|
93670
|
-
isSubdirectory =
|
|
93745
|
+
isSubdirectory = path10.resolve(repoPath) !== path10.resolve(gitRoot);
|
|
93671
93746
|
}
|
|
93672
93747
|
if (hasChanges && !options?.skipStash && !isSubdirectory && git) {
|
|
93673
93748
|
onProgress({ step: "stash", percent: 2, detail: "Stashing pending changes to analyze committed state..." });
|
|
@@ -94001,155 +94076,210 @@ var init_analysis_persistence_service = __esm({
|
|
|
94001
94076
|
}
|
|
94002
94077
|
});
|
|
94003
94078
|
|
|
94004
|
-
// apps/server/dist/
|
|
94005
|
-
import
|
|
94006
|
-
|
|
94007
|
-
|
|
94008
|
-
|
|
94009
|
-
|
|
94010
|
-
|
|
94011
|
-
|
|
94012
|
-
|
|
94013
|
-
|
|
94014
|
-
|
|
94015
|
-
|
|
94016
|
-
|
|
94017
|
-
const file = lockPath(repoPath);
|
|
94018
|
-
fs6.mkdirSync(path9.dirname(file), { recursive: true });
|
|
94019
|
-
try {
|
|
94020
|
-
const fd = fs6.openSync(file, "wx");
|
|
94021
|
-
fs6.writeSync(fd, `${process.pid}
|
|
94022
|
-
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
94023
|
-
`);
|
|
94024
|
-
fs6.closeSync(fd);
|
|
94025
|
-
} catch (err) {
|
|
94026
|
-
if (err.code === "EEXIST") {
|
|
94027
|
-
let owner = null;
|
|
94028
|
-
try {
|
|
94029
|
-
owner = parseInt(fs6.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
94030
|
-
} catch {
|
|
94079
|
+
// apps/server/dist/services/flow.service.js
|
|
94080
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
94081
|
+
function detectFlows(result) {
|
|
94082
|
+
const dbTypeMap = /* @__PURE__ */ new Map();
|
|
94083
|
+
for (const db of result.databaseResult.databases) {
|
|
94084
|
+
dbTypeMap.set(db.name, db.type);
|
|
94085
|
+
}
|
|
94086
|
+
const functionsByFile = /* @__PURE__ */ new Map();
|
|
94087
|
+
if (result.fileAnalyses) {
|
|
94088
|
+
for (const fa of result.fileAnalyses) {
|
|
94089
|
+
const entries = [];
|
|
94090
|
+
for (const fn of fa.functions) {
|
|
94091
|
+
entries.push({ name: fn.name, startLine: fn.location.startLine, endLine: fn.location.endLine });
|
|
94031
94092
|
}
|
|
94032
|
-
|
|
94093
|
+
for (const cls of fa.classes) {
|
|
94094
|
+
for (const method of cls.methods) {
|
|
94095
|
+
entries.push({ name: method.name, startLine: method.location.startLine, endLine: method.location.endLine });
|
|
94096
|
+
}
|
|
94097
|
+
}
|
|
94098
|
+
if (entries.length > 0)
|
|
94099
|
+
functionsByFile.set(fa.filePath, entries);
|
|
94033
94100
|
}
|
|
94034
|
-
throw err;
|
|
94035
94101
|
}
|
|
94036
|
-
|
|
94037
|
-
|
|
94038
|
-
|
|
94039
|
-
|
|
94040
|
-
} catch (err) {
|
|
94041
|
-
if (err.code !== "ENOENT")
|
|
94042
|
-
throw err;
|
|
94102
|
+
const crossServiceCalls = [];
|
|
94103
|
+
const fileToModule = /* @__PURE__ */ new Map();
|
|
94104
|
+
for (const mod of result.modules) {
|
|
94105
|
+
fileToModule.set(mod.filePath, `${mod.serviceName}::${mod.name}`);
|
|
94043
94106
|
}
|
|
94044
|
-
|
|
94045
|
-
|
|
94046
|
-
|
|
94047
|
-
|
|
94048
|
-
|
|
94049
|
-
|
|
94050
|
-
|
|
94051
|
-
|
|
94052
|
-
|
|
94053
|
-
|
|
94054
|
-
|
|
94107
|
+
const fileToLanguage = /* @__PURE__ */ new Map();
|
|
94108
|
+
if (result.fileAnalyses) {
|
|
94109
|
+
for (const fa of result.fileAnalyses) {
|
|
94110
|
+
fileToLanguage.set(fa.filePath, fa.language);
|
|
94111
|
+
}
|
|
94112
|
+
}
|
|
94113
|
+
for (const dep of result.dependencies) {
|
|
94114
|
+
if (!dep.httpCalls || dep.httpCalls.length === 0)
|
|
94115
|
+
continue;
|
|
94116
|
+
for (const call of dep.httpCalls) {
|
|
94117
|
+
const moduleKey = fileToModule.get(call.location.filePath);
|
|
94118
|
+
if (!moduleKey)
|
|
94119
|
+
continue;
|
|
94120
|
+
const [sourceService, sourceModule] = moduleKey.split("::");
|
|
94121
|
+
let sourceMethod;
|
|
94122
|
+
const fileFunctions = functionsByFile.get(call.location.filePath);
|
|
94123
|
+
if (fileFunctions) {
|
|
94124
|
+
for (const fn of fileFunctions) {
|
|
94125
|
+
if (call.location.startLine >= fn.startLine && call.location.startLine <= fn.endLine) {
|
|
94126
|
+
sourceMethod = fn.name;
|
|
94127
|
+
break;
|
|
94128
|
+
}
|
|
94129
|
+
}
|
|
94055
94130
|
}
|
|
94056
|
-
|
|
94131
|
+
const language = fileToLanguage.get(call.location.filePath);
|
|
94132
|
+
const normalizedUrl = language ? normalizeUrl(call.url, language) : call.url;
|
|
94133
|
+
crossServiceCalls.push({
|
|
94134
|
+
sourceService,
|
|
94135
|
+
sourceModule,
|
|
94136
|
+
sourceMethod,
|
|
94137
|
+
httpMethod: call.method,
|
|
94138
|
+
url: normalizedUrl,
|
|
94139
|
+
targetService: dep.target
|
|
94140
|
+
});
|
|
94141
|
+
}
|
|
94057
94142
|
}
|
|
94058
|
-
|
|
94059
|
-
|
|
94060
|
-
|
|
94061
|
-
|
|
94062
|
-
|
|
94063
|
-
|
|
94064
|
-
|
|
94065
|
-
|
|
94066
|
-
|
|
94067
|
-
|
|
94068
|
-
|
|
94069
|
-
|
|
94070
|
-
|
|
94071
|
-
}
|
|
94072
|
-
|
|
94073
|
-
|
|
94074
|
-
|
|
94075
|
-
|
|
94076
|
-
|
|
94077
|
-
|
|
94078
|
-
|
|
94079
|
-
|
|
94080
|
-
|
|
94081
|
-
|
|
94082
|
-
|
|
94083
|
-
|
|
94084
|
-
|
|
94143
|
+
const routeHandlers = buildRouteHandlerLookup(result);
|
|
94144
|
+
const graph = new AnalysisGraph({
|
|
94145
|
+
methods: result.methods,
|
|
94146
|
+
methodDependencies: result.methodLevelDependencies,
|
|
94147
|
+
modules: result.modules,
|
|
94148
|
+
services: result.services.map((s) => ({ name: s.name, type: s.type })),
|
|
94149
|
+
crossServiceCalls: crossServiceCalls.length > 0 ? crossServiceCalls : void 0,
|
|
94150
|
+
databaseConnections: result.databaseResult.connections.map((c2) => ({
|
|
94151
|
+
serviceName: c2.serviceName,
|
|
94152
|
+
databaseName: c2.databaseName,
|
|
94153
|
+
databaseType: dbTypeMap.get(c2.databaseName) || "unknown"
|
|
94154
|
+
})),
|
|
94155
|
+
routeHandlers: routeHandlers.size > 0 ? routeHandlers : void 0
|
|
94156
|
+
});
|
|
94157
|
+
const traced = traceFlows(graph);
|
|
94158
|
+
const out = traced.map((flow) => ({
|
|
94159
|
+
id: randomUUID2(),
|
|
94160
|
+
name: flow.name,
|
|
94161
|
+
description: null,
|
|
94162
|
+
entryService: flow.entryService,
|
|
94163
|
+
entryMethod: flow.entryMethod,
|
|
94164
|
+
category: flow.category,
|
|
94165
|
+
trigger: flow.trigger,
|
|
94166
|
+
stepCount: flow.steps.length,
|
|
94167
|
+
steps: flow.steps.map((step) => ({
|
|
94168
|
+
stepOrder: step.stepOrder,
|
|
94169
|
+
sourceService: step.sourceService,
|
|
94170
|
+
sourceModule: step.sourceModule,
|
|
94171
|
+
sourceMethod: step.sourceMethod,
|
|
94172
|
+
targetService: step.targetService,
|
|
94173
|
+
targetModule: step.targetModule,
|
|
94174
|
+
targetMethod: step.targetMethod,
|
|
94175
|
+
stepType: step.stepType,
|
|
94176
|
+
dataDescription: null,
|
|
94177
|
+
isAsync: step.isAsync,
|
|
94178
|
+
isConditional: step.isConditional
|
|
94179
|
+
}))
|
|
94180
|
+
}));
|
|
94181
|
+
log.info(`[Flows] Detected ${out.length} flows`);
|
|
94182
|
+
return out;
|
|
94085
94183
|
}
|
|
94086
|
-
function
|
|
94087
|
-
const
|
|
94088
|
-
|
|
94089
|
-
|
|
94090
|
-
|
|
94091
|
-
|
|
94092
|
-
|
|
94093
|
-
|
|
94094
|
-
|
|
94184
|
+
function buildRouteHandlerLookup(result) {
|
|
94185
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
94186
|
+
if (!result.fileAnalyses)
|
|
94187
|
+
return handlers;
|
|
94188
|
+
const fileToService = /* @__PURE__ */ new Map();
|
|
94189
|
+
const fileToModuleName = /* @__PURE__ */ new Map();
|
|
94190
|
+
for (const mod of result.modules) {
|
|
94191
|
+
fileToService.set(mod.filePath, mod.serviceName);
|
|
94192
|
+
fileToModuleName.set(mod.filePath, mod.name);
|
|
94193
|
+
}
|
|
94194
|
+
for (const ld of result.layerDetails) {
|
|
94195
|
+
for (const fp of ld.filePaths) {
|
|
94196
|
+
if (!fileToService.has(fp))
|
|
94197
|
+
fileToService.set(fp, ld.serviceName);
|
|
94095
94198
|
}
|
|
94096
|
-
throw err;
|
|
94097
94199
|
}
|
|
94098
|
-
const
|
|
94099
|
-
|
|
94100
|
-
|
|
94101
|
-
|
|
94102
|
-
|
|
94103
|
-
|
|
94104
|
-
|
|
94105
|
-
|
|
94106
|
-
|
|
94107
|
-
|
|
94108
|
-
|
|
94109
|
-
|
|
94110
|
-
|
|
94111
|
-
|
|
94112
|
-
|
|
94113
|
-
|
|
94114
|
-
|
|
94115
|
-
|
|
94116
|
-
|
|
94117
|
-
|
|
94118
|
-
|
|
94119
|
-
|
|
94120
|
-
|
|
94121
|
-
|
|
94122
|
-
|
|
94123
|
-
|
|
94124
|
-
|
|
94125
|
-
|
|
94126
|
-
|
|
94127
|
-
|
|
94128
|
-
|
|
94129
|
-
|
|
94130
|
-
}
|
|
94131
|
-
|
|
94132
|
-
|
|
94200
|
+
const fileMountPrefix = /* @__PURE__ */ new Map();
|
|
94201
|
+
for (const fa of result.fileAnalyses) {
|
|
94202
|
+
if (!fa.routerMounts || fa.routerMounts.length === 0)
|
|
94203
|
+
continue;
|
|
94204
|
+
for (const mount of fa.routerMounts) {
|
|
94205
|
+
for (const imp of fa.imports) {
|
|
94206
|
+
const spec = imp.specifiers.find((s) => s.name === mount.routerName || s.alias === mount.routerName);
|
|
94207
|
+
if (spec) {
|
|
94208
|
+
for (const dep of result.moduleDependencies) {
|
|
94209
|
+
if (dep.source === fa.filePath && dep.importedNames.includes(spec.name)) {
|
|
94210
|
+
fileMountPrefix.set(dep.target, mount.path);
|
|
94211
|
+
}
|
|
94212
|
+
}
|
|
94213
|
+
}
|
|
94214
|
+
}
|
|
94215
|
+
const hasLocal = fa.functions.some((f) => f.name === mount.routerName) || fa.exports.some((e) => e.name === mount.routerName);
|
|
94216
|
+
if (hasLocal)
|
|
94217
|
+
fileMountPrefix.set(fa.filePath, mount.path);
|
|
94218
|
+
}
|
|
94219
|
+
}
|
|
94220
|
+
for (const fa of result.fileAnalyses) {
|
|
94221
|
+
if (!fa.routeRegistrations || fa.routeRegistrations.length === 0)
|
|
94222
|
+
continue;
|
|
94223
|
+
const serviceName = fileToService.get(fa.filePath);
|
|
94224
|
+
if (!serviceName)
|
|
94225
|
+
continue;
|
|
94226
|
+
const mountPrefix = fileMountPrefix.get(fa.filePath) || "";
|
|
94227
|
+
for (const route of fa.routeRegistrations) {
|
|
94228
|
+
const fullPath = composePath(mountPrefix, route.path);
|
|
94229
|
+
const moduleName = resolveHandlerModule(route.handlerName, fa, result.fileAnalyses, fileToModuleName);
|
|
94230
|
+
const key = `${serviceName}::${route.httpMethod}::${fullPath}`;
|
|
94231
|
+
handlers.set(key, { handlerName: route.handlerName, moduleName });
|
|
94232
|
+
}
|
|
94233
|
+
}
|
|
94234
|
+
return handlers;
|
|
94133
94235
|
}
|
|
94134
|
-
function
|
|
94135
|
-
|
|
94136
|
-
|
|
94137
|
-
|
|
94138
|
-
|
|
94139
|
-
|
|
94236
|
+
function resolveHandlerModule(handlerName, routeFile, allFiles, fileToModuleName) {
|
|
94237
|
+
for (const cls of routeFile.classes) {
|
|
94238
|
+
for (const method of cls.methods) {
|
|
94239
|
+
if (method.name === handlerName)
|
|
94240
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94241
|
+
}
|
|
94242
|
+
}
|
|
94243
|
+
for (const fn of routeFile.functions) {
|
|
94244
|
+
if (fn.name === handlerName)
|
|
94245
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94246
|
+
}
|
|
94247
|
+
for (const imp of routeFile.imports) {
|
|
94248
|
+
const spec = imp.specifiers.find((s) => s.name === handlerName || s.alias === handlerName);
|
|
94249
|
+
if (spec) {
|
|
94250
|
+
for (const targetFile of allFiles) {
|
|
94251
|
+
const hasExport = targetFile.exports.some((e) => e.name === (spec.alias || spec.name) || e.name === spec.name);
|
|
94252
|
+
const hasFunction = targetFile.functions.some((f) => f.name === spec.name);
|
|
94253
|
+
const hasClassMethod = targetFile.classes.some((c2) => c2.methods.some((m) => m.name === spec.name));
|
|
94254
|
+
if (hasExport || hasFunction || hasClassMethod) {
|
|
94255
|
+
return fileToModuleName.get(targetFile.filePath) || handlerName;
|
|
94256
|
+
}
|
|
94257
|
+
}
|
|
94258
|
+
}
|
|
94259
|
+
for (const s of imp.specifiers) {
|
|
94260
|
+
for (const targetFile of allFiles) {
|
|
94261
|
+
for (const cls of targetFile.classes) {
|
|
94262
|
+
if (cls.name === s.name && cls.methods.some((m) => m.name === handlerName)) {
|
|
94263
|
+
return fileToModuleName.get(targetFile.filePath) || cls.name;
|
|
94264
|
+
}
|
|
94265
|
+
}
|
|
94266
|
+
}
|
|
94267
|
+
}
|
|
94140
94268
|
}
|
|
94269
|
+
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
94141
94270
|
}
|
|
94142
|
-
|
|
94143
|
-
|
|
94144
|
-
"
|
|
94271
|
+
function composePath(prefix, routePath) {
|
|
94272
|
+
const p2 = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
94273
|
+
const r = routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
94274
|
+
const full = `${p2}${r}`;
|
|
94275
|
+
return full.length > 1 && full.endsWith("/") ? full.slice(0, -1) : full || "/";
|
|
94276
|
+
}
|
|
94277
|
+
var init_flow_service = __esm({
|
|
94278
|
+
"apps/server/dist/services/flow.service.js"() {
|
|
94145
94279
|
"use strict";
|
|
94146
|
-
|
|
94147
|
-
|
|
94148
|
-
|
|
94149
|
-
LATEST_FILE = "LATEST.json";
|
|
94150
|
-
HISTORY_FILE = "history.json";
|
|
94151
|
-
DIFF_FILE = "diff.json";
|
|
94152
|
-
latestCache = /* @__PURE__ */ new Map();
|
|
94280
|
+
init_logger();
|
|
94281
|
+
init_dist6();
|
|
94282
|
+
init_analysis_store();
|
|
94153
94283
|
}
|
|
94154
94284
|
});
|
|
94155
94285
|
|
|
@@ -107145,7 +107275,7 @@ var require_sender = __commonJS({
|
|
|
107145
107275
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
107146
107276
|
this._bufferedBytes += options[kByteLength];
|
|
107147
107277
|
this._state = DEFLATING;
|
|
107148
|
-
perMessageDeflate.compress(data, options.fin, (
|
|
107278
|
+
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
|
107149
107279
|
if (this._socket.destroyed) {
|
|
107150
107280
|
const err = new Error(
|
|
107151
107281
|
"The socket was closed while data was being compressed"
|
|
@@ -115969,7 +116099,7 @@ var util, objectUtil, ZodParsedType, getParsedType;
|
|
|
115969
116099
|
var init_util3 = __esm({
|
|
115970
116100
|
"node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js"() {
|
|
115971
116101
|
(function(util2) {
|
|
115972
|
-
util2.assertEqual = (
|
|
116102
|
+
util2.assertEqual = (_) => {
|
|
115973
116103
|
};
|
|
115974
116104
|
function assertIs(_arg) {
|
|
115975
116105
|
}
|
|
@@ -116019,7 +116149,7 @@ var init_util3 = __esm({
|
|
|
116019
116149
|
return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
|
|
116020
116150
|
}
|
|
116021
116151
|
util2.joinValues = joinValues;
|
|
116022
|
-
util2.jsonStringifyReplacer = (
|
|
116152
|
+
util2.jsonStringifyReplacer = (_, value) => {
|
|
116023
116153
|
if (typeof value === "bigint") {
|
|
116024
116154
|
return value.toString();
|
|
116025
116155
|
}
|
|
@@ -120632,9 +120762,12 @@ var init_schemas = __esm({
|
|
|
120632
120762
|
path: external_exports.string().min(1)
|
|
120633
120763
|
});
|
|
120634
120764
|
AnalyzeRepoSchema = external_exports.object({
|
|
120635
|
-
|
|
120636
|
-
|
|
120637
|
-
|
|
120765
|
+
/** Which mode to run — full analyze (HEAD committed state) or diff (working tree
|
|
120766
|
+
* vs LATEST). Required; no silent default. */
|
|
120767
|
+
mode: external_exports.enum(["full", "diff"]),
|
|
120768
|
+
/** Skip git ops (branch detection, commit hash read, pre-parse stash). Useful
|
|
120769
|
+
* for non-git dirs or test environments. No per-repo-config equivalent —
|
|
120770
|
+
* only way to opt out for a single run. */
|
|
120638
120771
|
skipGit: external_exports.boolean().optional().default(false)
|
|
120639
120772
|
});
|
|
120640
120773
|
GenerateViolationsSchema = external_exports.object({
|
|
@@ -121328,7 +121461,7 @@ function parseStringDef(def, refs) {
|
|
|
121328
121461
|
case "trim":
|
|
121329
121462
|
break;
|
|
121330
121463
|
default:
|
|
121331
|
-
/* @__PURE__ */ ((
|
|
121464
|
+
/* @__PURE__ */ ((_) => {
|
|
121332
121465
|
})(check);
|
|
121333
121466
|
}
|
|
121334
121467
|
}
|
|
@@ -122176,7 +122309,7 @@ var init_selectParser = __esm({
|
|
|
122176
122309
|
case ZodFirstPartyTypeKind.ZodSymbol:
|
|
122177
122310
|
return void 0;
|
|
122178
122311
|
default:
|
|
122179
|
-
return /* @__PURE__ */ ((
|
|
122312
|
+
return /* @__PURE__ */ ((_) => void 0)(typeName);
|
|
122180
122313
|
}
|
|
122181
122314
|
};
|
|
122182
122315
|
}
|
|
@@ -125880,422 +126013,6 @@ var init_violation_pipeline_service = __esm({
|
|
|
125880
126013
|
}
|
|
125881
126014
|
});
|
|
125882
126015
|
|
|
125883
|
-
// apps/server/dist/commands/diff-in-process.js
|
|
125884
|
-
var diff_in_process_exports = {};
|
|
125885
|
-
__export(diff_in_process_exports, {
|
|
125886
|
-
diffInProcess: () => diffInProcess
|
|
125887
|
-
});
|
|
125888
|
-
import path14 from "node:path";
|
|
125889
|
-
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
125890
|
-
async function diffInProcess(project, options = {}) {
|
|
125891
|
-
const latest = readLatest(project.path);
|
|
125892
|
-
if (!latest) {
|
|
125893
|
-
throw new Error("Run a full analysis first before checking a diff.");
|
|
125894
|
-
}
|
|
125895
|
-
const projectConfig = readProjectConfig(project.path);
|
|
125896
|
-
const enabledCategories = options.enabledCategoriesOverride ?? projectConfig.enabledCategories ?? void 0;
|
|
125897
|
-
const enableLlmRules = options.enableLlmRulesOverride ?? projectConfig.enableLlmRules ?? false;
|
|
125898
|
-
const git = await getGit(project.path);
|
|
125899
|
-
const statusResult = await git.status();
|
|
125900
|
-
const changedFiles = [];
|
|
125901
|
-
for (const f of statusResult.not_added)
|
|
125902
|
-
changedFiles.push({ path: f, status: "new" });
|
|
125903
|
-
for (const f of statusResult.created)
|
|
125904
|
-
changedFiles.push({ path: f, status: "new" });
|
|
125905
|
-
for (const f of statusResult.modified)
|
|
125906
|
-
changedFiles.push({ path: f, status: "modified" });
|
|
125907
|
-
for (const f of statusResult.staged) {
|
|
125908
|
-
if (!changedFiles.some((cf) => cf.path === f)) {
|
|
125909
|
-
changedFiles.push({ path: f, status: "modified" });
|
|
125910
|
-
}
|
|
125911
|
-
}
|
|
125912
|
-
for (const f of statusResult.deleted)
|
|
125913
|
-
changedFiles.push({ path: f, status: "deleted" });
|
|
125914
|
-
const commitHash = (await git.revparse(["HEAD"])).trim() || null;
|
|
125915
|
-
options.tracker?.start("parse", "Analyzing working tree...");
|
|
125916
|
-
const result = await runAnalysis(project.path, latest.analysis.branch ?? void 0, (progress) => {
|
|
125917
|
-
options.tracker?.detail("parse", progress.detail ?? "Analyzing...");
|
|
125918
|
-
options.onProgress?.({ detail: progress.detail });
|
|
125919
|
-
}, { signal: options.signal, skipStash: true });
|
|
125920
|
-
if (options.signal?.aborted)
|
|
125921
|
-
throw new DOMException("Diff cancelled", "AbortError");
|
|
125922
|
-
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
125923
|
-
const analysisId = randomUUID7();
|
|
125924
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
125925
|
-
options.tracker?.done("parse", `${result.services.length} services, ${result.fileAnalyses?.length ?? 0} files`);
|
|
125926
|
-
const pipelineResult = await runViolationPipeline({
|
|
125927
|
-
repoPath: project.path,
|
|
125928
|
-
analysisId,
|
|
125929
|
-
now,
|
|
125930
|
-
result,
|
|
125931
|
-
serviceIdMap,
|
|
125932
|
-
moduleIdMap,
|
|
125933
|
-
methodIdMap,
|
|
125934
|
-
dbIdMap,
|
|
125935
|
-
previousActiveViolations: latest.violations,
|
|
125936
|
-
enabledCategories,
|
|
125937
|
-
enableLlmRules,
|
|
125938
|
-
tracker: options.tracker,
|
|
125939
|
-
signal: options.signal
|
|
125940
|
-
});
|
|
125941
|
-
const diff = buildDiffSnapshot({
|
|
125942
|
-
latest,
|
|
125943
|
-
graph,
|
|
125944
|
-
analysisId,
|
|
125945
|
-
now,
|
|
125946
|
-
branch: latest.analysis.branch,
|
|
125947
|
-
commitHash,
|
|
125948
|
-
changedFiles,
|
|
125949
|
-
pipelineResult,
|
|
125950
|
-
repoPath: project.path
|
|
125951
|
-
});
|
|
125952
|
-
writeDiff(project.path, diff);
|
|
125953
|
-
log.info(`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`);
|
|
125954
|
-
return { diff, isStale: false };
|
|
125955
|
-
}
|
|
125956
|
-
function buildDiffSnapshot(params) {
|
|
125957
|
-
const { latest, graph, analysisId, branch, commitHash, changedFiles, pipelineResult, repoPath } = params;
|
|
125958
|
-
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
125959
|
-
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
125960
|
-
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
125961
|
-
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
125962
|
-
const denormalize = (v) => ({
|
|
125963
|
-
...v,
|
|
125964
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
125965
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
125966
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
125967
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
125968
|
-
});
|
|
125969
|
-
const newViolations = pipelineResult.added.map(denormalize);
|
|
125970
|
-
const latestById = new Map(latest.violations.map((v) => [v.id, v]));
|
|
125971
|
-
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
125972
|
-
const changedAbs = new Set(changedFiles.map((c2) => path14.resolve(repoPath, c2.path)));
|
|
125973
|
-
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(path14.resolve(repoPath, p2)));
|
|
125974
|
-
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
125975
|
-
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
125976
|
-
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
125977
|
-
const layerKeyById = new Map(graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`]));
|
|
125978
|
-
const affectedServices = /* @__PURE__ */ new Set();
|
|
125979
|
-
const affectedLayers = /* @__PURE__ */ new Set();
|
|
125980
|
-
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
125981
|
-
for (const mod of affectedModules) {
|
|
125982
|
-
const svcName = serviceNameById.get(mod.serviceId);
|
|
125983
|
-
if (svcName) {
|
|
125984
|
-
affectedServices.add(svcName);
|
|
125985
|
-
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
125986
|
-
}
|
|
125987
|
-
const layerKey = layerKeyById.get(mod.layerId);
|
|
125988
|
-
if (layerKey)
|
|
125989
|
-
affectedLayers.add(layerKey);
|
|
125990
|
-
}
|
|
125991
|
-
const moduleNameById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
125992
|
-
const affectedMethodKeys = [];
|
|
125993
|
-
for (const method of graph.methods) {
|
|
125994
|
-
if (!affectedModuleIdSet.has(method.moduleId))
|
|
125995
|
-
continue;
|
|
125996
|
-
const modName = moduleNameById.get(method.moduleId);
|
|
125997
|
-
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
125998
|
-
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
125999
|
-
if (svcName && modName)
|
|
126000
|
-
affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
126001
|
-
}
|
|
126002
|
-
return {
|
|
126003
|
-
id: analysisId,
|
|
126004
|
-
baseAnalysisId: latest.analysis.id,
|
|
126005
|
-
createdAt: params.now,
|
|
126006
|
-
branch,
|
|
126007
|
-
commitHash,
|
|
126008
|
-
graph,
|
|
126009
|
-
changedFiles,
|
|
126010
|
-
newViolations,
|
|
126011
|
-
resolvedViolations,
|
|
126012
|
-
affectedNodeIds: {
|
|
126013
|
-
services: [...affectedServices],
|
|
126014
|
-
layers: [...affectedLayers],
|
|
126015
|
-
modules: [...affectedModuleKeys],
|
|
126016
|
-
methods: affectedMethodKeys
|
|
126017
|
-
},
|
|
126018
|
-
summary: {
|
|
126019
|
-
newCount: newViolations.length,
|
|
126020
|
-
unchangedCount: pipelineResult.unchanged.length,
|
|
126021
|
-
resolvedCount: resolvedViolations.length
|
|
126022
|
-
}
|
|
126023
|
-
};
|
|
126024
|
-
}
|
|
126025
|
-
var init_diff_in_process = __esm({
|
|
126026
|
-
"apps/server/dist/commands/diff-in-process.js"() {
|
|
126027
|
-
"use strict";
|
|
126028
|
-
init_git();
|
|
126029
|
-
init_logger();
|
|
126030
|
-
init_project_config();
|
|
126031
|
-
init_analyzer_service();
|
|
126032
|
-
init_analysis_persistence_service();
|
|
126033
|
-
init_violation_pipeline_service();
|
|
126034
|
-
init_analysis_store();
|
|
126035
|
-
}
|
|
126036
|
-
});
|
|
126037
|
-
|
|
126038
|
-
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
126039
|
-
var import_index = __toESM(require_commander(), 1);
|
|
126040
|
-
var {
|
|
126041
|
-
program,
|
|
126042
|
-
createCommand,
|
|
126043
|
-
createArgument,
|
|
126044
|
-
createOption,
|
|
126045
|
-
CommanderError,
|
|
126046
|
-
InvalidArgumentError,
|
|
126047
|
-
InvalidOptionArgumentError,
|
|
126048
|
-
// deprecated old name
|
|
126049
|
-
Command,
|
|
126050
|
-
Argument,
|
|
126051
|
-
Option,
|
|
126052
|
-
Help
|
|
126053
|
-
} = import_index.default;
|
|
126054
|
-
|
|
126055
|
-
// tools/cli/src/index.ts
|
|
126056
|
-
init_dist4();
|
|
126057
|
-
|
|
126058
|
-
// tools/cli/src/commands/add.ts
|
|
126059
|
-
init_dist4();
|
|
126060
|
-
init_paths();
|
|
126061
|
-
init_registry();
|
|
126062
|
-
init_helpers();
|
|
126063
|
-
async function runAdd() {
|
|
126064
|
-
const repoPath = resolveRepoDir(process.cwd()) ?? process.cwd();
|
|
126065
|
-
mt("Adding repository to TrueCourse");
|
|
126066
|
-
O2.step(repoPath);
|
|
126067
|
-
ensureRepoTruecourseDir(repoPath);
|
|
126068
|
-
const existing = getProjectByPath(repoPath);
|
|
126069
|
-
const entry = registerProject(repoPath);
|
|
126070
|
-
if (existing) {
|
|
126071
|
-
O2.info(`Repository "${entry.name}" is already registered.`);
|
|
126072
|
-
} else {
|
|
126073
|
-
O2.success(`Repository "${entry.name}" added.`);
|
|
126074
|
-
}
|
|
126075
|
-
await promptInstallSkills(repoPath);
|
|
126076
|
-
gt("Run `truecourse analyze` to generate analysis data.");
|
|
126077
|
-
}
|
|
126078
|
-
|
|
126079
|
-
// tools/cli/src/commands/analyze.ts
|
|
126080
|
-
init_dist4();
|
|
126081
|
-
import { execSync } from "node:child_process";
|
|
126082
|
-
import path15 from "node:path";
|
|
126083
|
-
|
|
126084
|
-
// apps/server/dist/commands/analyze-in-process.js
|
|
126085
|
-
init_logger();
|
|
126086
|
-
init_git();
|
|
126087
|
-
init_project_config();
|
|
126088
|
-
init_registry();
|
|
126089
|
-
init_analyzer_service();
|
|
126090
|
-
init_analysis_persistence_service();
|
|
126091
|
-
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
126092
|
-
|
|
126093
|
-
// apps/server/dist/services/flow.service.js
|
|
126094
|
-
init_logger();
|
|
126095
|
-
init_dist6();
|
|
126096
|
-
init_analysis_store();
|
|
126097
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
126098
|
-
function detectFlows(result) {
|
|
126099
|
-
const dbTypeMap = /* @__PURE__ */ new Map();
|
|
126100
|
-
for (const db of result.databaseResult.databases) {
|
|
126101
|
-
dbTypeMap.set(db.name, db.type);
|
|
126102
|
-
}
|
|
126103
|
-
const functionsByFile = /* @__PURE__ */ new Map();
|
|
126104
|
-
if (result.fileAnalyses) {
|
|
126105
|
-
for (const fa of result.fileAnalyses) {
|
|
126106
|
-
const entries = [];
|
|
126107
|
-
for (const fn of fa.functions) {
|
|
126108
|
-
entries.push({ name: fn.name, startLine: fn.location.startLine, endLine: fn.location.endLine });
|
|
126109
|
-
}
|
|
126110
|
-
for (const cls of fa.classes) {
|
|
126111
|
-
for (const method of cls.methods) {
|
|
126112
|
-
entries.push({ name: method.name, startLine: method.location.startLine, endLine: method.location.endLine });
|
|
126113
|
-
}
|
|
126114
|
-
}
|
|
126115
|
-
if (entries.length > 0)
|
|
126116
|
-
functionsByFile.set(fa.filePath, entries);
|
|
126117
|
-
}
|
|
126118
|
-
}
|
|
126119
|
-
const crossServiceCalls = [];
|
|
126120
|
-
const fileToModule = /* @__PURE__ */ new Map();
|
|
126121
|
-
for (const mod of result.modules) {
|
|
126122
|
-
fileToModule.set(mod.filePath, `${mod.serviceName}::${mod.name}`);
|
|
126123
|
-
}
|
|
126124
|
-
const fileToLanguage = /* @__PURE__ */ new Map();
|
|
126125
|
-
if (result.fileAnalyses) {
|
|
126126
|
-
for (const fa of result.fileAnalyses) {
|
|
126127
|
-
fileToLanguage.set(fa.filePath, fa.language);
|
|
126128
|
-
}
|
|
126129
|
-
}
|
|
126130
|
-
for (const dep of result.dependencies) {
|
|
126131
|
-
if (!dep.httpCalls || dep.httpCalls.length === 0)
|
|
126132
|
-
continue;
|
|
126133
|
-
for (const call of dep.httpCalls) {
|
|
126134
|
-
const moduleKey = fileToModule.get(call.location.filePath);
|
|
126135
|
-
if (!moduleKey)
|
|
126136
|
-
continue;
|
|
126137
|
-
const [sourceService, sourceModule] = moduleKey.split("::");
|
|
126138
|
-
let sourceMethod;
|
|
126139
|
-
const fileFunctions = functionsByFile.get(call.location.filePath);
|
|
126140
|
-
if (fileFunctions) {
|
|
126141
|
-
for (const fn of fileFunctions) {
|
|
126142
|
-
if (call.location.startLine >= fn.startLine && call.location.startLine <= fn.endLine) {
|
|
126143
|
-
sourceMethod = fn.name;
|
|
126144
|
-
break;
|
|
126145
|
-
}
|
|
126146
|
-
}
|
|
126147
|
-
}
|
|
126148
|
-
const language = fileToLanguage.get(call.location.filePath);
|
|
126149
|
-
const normalizedUrl = language ? normalizeUrl(call.url, language) : call.url;
|
|
126150
|
-
crossServiceCalls.push({
|
|
126151
|
-
sourceService,
|
|
126152
|
-
sourceModule,
|
|
126153
|
-
sourceMethod,
|
|
126154
|
-
httpMethod: call.method,
|
|
126155
|
-
url: normalizedUrl,
|
|
126156
|
-
targetService: dep.target
|
|
126157
|
-
});
|
|
126158
|
-
}
|
|
126159
|
-
}
|
|
126160
|
-
const routeHandlers = buildRouteHandlerLookup(result);
|
|
126161
|
-
const graph = new AnalysisGraph({
|
|
126162
|
-
methods: result.methods,
|
|
126163
|
-
methodDependencies: result.methodLevelDependencies,
|
|
126164
|
-
modules: result.modules,
|
|
126165
|
-
services: result.services.map((s) => ({ name: s.name, type: s.type })),
|
|
126166
|
-
crossServiceCalls: crossServiceCalls.length > 0 ? crossServiceCalls : void 0,
|
|
126167
|
-
databaseConnections: result.databaseResult.connections.map((c2) => ({
|
|
126168
|
-
serviceName: c2.serviceName,
|
|
126169
|
-
databaseName: c2.databaseName,
|
|
126170
|
-
databaseType: dbTypeMap.get(c2.databaseName) || "unknown"
|
|
126171
|
-
})),
|
|
126172
|
-
routeHandlers: routeHandlers.size > 0 ? routeHandlers : void 0
|
|
126173
|
-
});
|
|
126174
|
-
const traced = traceFlows(graph);
|
|
126175
|
-
const out = traced.map((flow) => ({
|
|
126176
|
-
id: randomUUID2(),
|
|
126177
|
-
name: flow.name,
|
|
126178
|
-
description: null,
|
|
126179
|
-
entryService: flow.entryService,
|
|
126180
|
-
entryMethod: flow.entryMethod,
|
|
126181
|
-
category: flow.category,
|
|
126182
|
-
trigger: flow.trigger,
|
|
126183
|
-
stepCount: flow.steps.length,
|
|
126184
|
-
steps: flow.steps.map((step) => ({
|
|
126185
|
-
stepOrder: step.stepOrder,
|
|
126186
|
-
sourceService: step.sourceService,
|
|
126187
|
-
sourceModule: step.sourceModule,
|
|
126188
|
-
sourceMethod: step.sourceMethod,
|
|
126189
|
-
targetService: step.targetService,
|
|
126190
|
-
targetModule: step.targetModule,
|
|
126191
|
-
targetMethod: step.targetMethod,
|
|
126192
|
-
stepType: step.stepType,
|
|
126193
|
-
dataDescription: null,
|
|
126194
|
-
isAsync: step.isAsync,
|
|
126195
|
-
isConditional: step.isConditional
|
|
126196
|
-
}))
|
|
126197
|
-
}));
|
|
126198
|
-
log.info(`[Flows] Detected ${out.length} flows`);
|
|
126199
|
-
return out;
|
|
126200
|
-
}
|
|
126201
|
-
function buildRouteHandlerLookup(result) {
|
|
126202
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
126203
|
-
if (!result.fileAnalyses)
|
|
126204
|
-
return handlers;
|
|
126205
|
-
const fileToService = /* @__PURE__ */ new Map();
|
|
126206
|
-
const fileToModuleName = /* @__PURE__ */ new Map();
|
|
126207
|
-
for (const mod of result.modules) {
|
|
126208
|
-
fileToService.set(mod.filePath, mod.serviceName);
|
|
126209
|
-
fileToModuleName.set(mod.filePath, mod.name);
|
|
126210
|
-
}
|
|
126211
|
-
for (const ld of result.layerDetails) {
|
|
126212
|
-
for (const fp of ld.filePaths) {
|
|
126213
|
-
if (!fileToService.has(fp))
|
|
126214
|
-
fileToService.set(fp, ld.serviceName);
|
|
126215
|
-
}
|
|
126216
|
-
}
|
|
126217
|
-
const fileMountPrefix = /* @__PURE__ */ new Map();
|
|
126218
|
-
for (const fa of result.fileAnalyses) {
|
|
126219
|
-
if (!fa.routerMounts || fa.routerMounts.length === 0)
|
|
126220
|
-
continue;
|
|
126221
|
-
for (const mount of fa.routerMounts) {
|
|
126222
|
-
for (const imp of fa.imports) {
|
|
126223
|
-
const spec = imp.specifiers.find((s) => s.name === mount.routerName || s.alias === mount.routerName);
|
|
126224
|
-
if (spec) {
|
|
126225
|
-
for (const dep of result.moduleDependencies) {
|
|
126226
|
-
if (dep.source === fa.filePath && dep.importedNames.includes(spec.name)) {
|
|
126227
|
-
fileMountPrefix.set(dep.target, mount.path);
|
|
126228
|
-
}
|
|
126229
|
-
}
|
|
126230
|
-
}
|
|
126231
|
-
}
|
|
126232
|
-
const hasLocal = fa.functions.some((f) => f.name === mount.routerName) || fa.exports.some((e) => e.name === mount.routerName);
|
|
126233
|
-
if (hasLocal)
|
|
126234
|
-
fileMountPrefix.set(fa.filePath, mount.path);
|
|
126235
|
-
}
|
|
126236
|
-
}
|
|
126237
|
-
for (const fa of result.fileAnalyses) {
|
|
126238
|
-
if (!fa.routeRegistrations || fa.routeRegistrations.length === 0)
|
|
126239
|
-
continue;
|
|
126240
|
-
const serviceName = fileToService.get(fa.filePath);
|
|
126241
|
-
if (!serviceName)
|
|
126242
|
-
continue;
|
|
126243
|
-
const mountPrefix = fileMountPrefix.get(fa.filePath) || "";
|
|
126244
|
-
for (const route of fa.routeRegistrations) {
|
|
126245
|
-
const fullPath = composePath(mountPrefix, route.path);
|
|
126246
|
-
const moduleName = resolveHandlerModule(route.handlerName, fa, result.fileAnalyses, fileToModuleName);
|
|
126247
|
-
const key = `${serviceName}::${route.httpMethod}::${fullPath}`;
|
|
126248
|
-
handlers.set(key, { handlerName: route.handlerName, moduleName });
|
|
126249
|
-
}
|
|
126250
|
-
}
|
|
126251
|
-
return handlers;
|
|
126252
|
-
}
|
|
126253
|
-
function resolveHandlerModule(handlerName, routeFile, allFiles, fileToModuleName) {
|
|
126254
|
-
for (const cls of routeFile.classes) {
|
|
126255
|
-
for (const method of cls.methods) {
|
|
126256
|
-
if (method.name === handlerName)
|
|
126257
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126258
|
-
}
|
|
126259
|
-
}
|
|
126260
|
-
for (const fn of routeFile.functions) {
|
|
126261
|
-
if (fn.name === handlerName)
|
|
126262
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126263
|
-
}
|
|
126264
|
-
for (const imp of routeFile.imports) {
|
|
126265
|
-
const spec = imp.specifiers.find((s) => s.name === handlerName || s.alias === handlerName);
|
|
126266
|
-
if (spec) {
|
|
126267
|
-
for (const targetFile of allFiles) {
|
|
126268
|
-
const hasExport = targetFile.exports.some((e) => e.name === (spec.alias || spec.name) || e.name === spec.name);
|
|
126269
|
-
const hasFunction = targetFile.functions.some((f) => f.name === spec.name);
|
|
126270
|
-
const hasClassMethod = targetFile.classes.some((c2) => c2.methods.some((m) => m.name === spec.name));
|
|
126271
|
-
if (hasExport || hasFunction || hasClassMethod) {
|
|
126272
|
-
return fileToModuleName.get(targetFile.filePath) || handlerName;
|
|
126273
|
-
}
|
|
126274
|
-
}
|
|
126275
|
-
}
|
|
126276
|
-
for (const s of imp.specifiers) {
|
|
126277
|
-
for (const targetFile of allFiles) {
|
|
126278
|
-
for (const cls of targetFile.classes) {
|
|
126279
|
-
if (cls.name === s.name && cls.methods.some((m) => m.name === handlerName)) {
|
|
126280
|
-
return fileToModuleName.get(targetFile.filePath) || cls.name;
|
|
126281
|
-
}
|
|
126282
|
-
}
|
|
126283
|
-
}
|
|
126284
|
-
}
|
|
126285
|
-
}
|
|
126286
|
-
return fileToModuleName.get(routeFile.filePath) || handlerName;
|
|
126287
|
-
}
|
|
126288
|
-
function composePath(prefix, routePath) {
|
|
126289
|
-
const p2 = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
126290
|
-
const r = routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
126291
|
-
const full = `${p2}${r}`;
|
|
126292
|
-
return full.length > 1 && full.endsWith("/") ? full.slice(0, -1) : full || "/";
|
|
126293
|
-
}
|
|
126294
|
-
|
|
126295
|
-
// apps/server/dist/commands/analyze-in-process.js
|
|
126296
|
-
init_violation_pipeline_service();
|
|
126297
|
-
init_provider();
|
|
126298
|
-
|
|
126299
126016
|
// apps/server/dist/services/usage.service.js
|
|
126300
126017
|
function toUsageRecords(records) {
|
|
126301
126018
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -126312,11 +126029,15 @@ function toUsageRecords(records) {
|
|
|
126312
126029
|
createdAt: now
|
|
126313
126030
|
}));
|
|
126314
126031
|
}
|
|
126032
|
+
var init_usage_service = __esm({
|
|
126033
|
+
"apps/server/dist/services/usage.service.js"() {
|
|
126034
|
+
"use strict";
|
|
126035
|
+
}
|
|
126036
|
+
});
|
|
126315
126037
|
|
|
126316
|
-
// apps/server/dist/commands/analyze-
|
|
126317
|
-
|
|
126318
|
-
|
|
126319
|
-
async function analyzeInProcess(project, options = {}) {
|
|
126038
|
+
// apps/server/dist/commands/analyze-core.js
|
|
126039
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
126040
|
+
async function analyzeCore(project, options) {
|
|
126320
126041
|
try {
|
|
126321
126042
|
acquireAnalyzeLock(project.path);
|
|
126322
126043
|
} catch (err) {
|
|
@@ -126325,12 +126046,27 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126325
126046
|
throw err;
|
|
126326
126047
|
}
|
|
126327
126048
|
try {
|
|
126328
|
-
const
|
|
126329
|
-
const
|
|
126049
|
+
const { mode, signal } = options;
|
|
126050
|
+
const isDiff = mode === "diff";
|
|
126051
|
+
const skipGit = !isDiff && !!options.skipGit;
|
|
126330
126052
|
const projectConfig = readProjectConfig(project.path);
|
|
126053
|
+
const latestBaseline = readLatest(project.path);
|
|
126054
|
+
if (isDiff && !latestBaseline) {
|
|
126055
|
+
throw new Error("Run a full analysis first before checking a diff.");
|
|
126056
|
+
}
|
|
126331
126057
|
let branch = options.branch ?? null;
|
|
126332
126058
|
let commitHash = options.commitHash ?? null;
|
|
126333
|
-
if (
|
|
126059
|
+
if (isDiff) {
|
|
126060
|
+
branch = latestBaseline.analysis.branch ?? branch;
|
|
126061
|
+
if (commitHash === null) {
|
|
126062
|
+
try {
|
|
126063
|
+
const git = await getGit(project.path);
|
|
126064
|
+
commitHash = (await git.revparse(["HEAD"])).trim() || null;
|
|
126065
|
+
} catch {
|
|
126066
|
+
commitHash = null;
|
|
126067
|
+
}
|
|
126068
|
+
}
|
|
126069
|
+
} else if (!skipGit && (branch === null || commitHash === null)) {
|
|
126334
126070
|
const git = await getGit(project.path);
|
|
126335
126071
|
if (branch === null)
|
|
126336
126072
|
branch = (await git.branch()).current || null;
|
|
@@ -126339,39 +126075,62 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126339
126075
|
}
|
|
126340
126076
|
const analysisId = randomUUID6();
|
|
126341
126077
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
126342
|
-
const
|
|
126078
|
+
const start = Date.now();
|
|
126343
126079
|
const effectiveCategories = options.enabledCategoriesOverride?.length ? options.enabledCategoriesOverride : projectConfig.enabledCategories ?? void 0;
|
|
126344
126080
|
const effectiveLlmRules = projectConfig.enableLlmRules ?? options.enableLlmRulesOverride ?? true;
|
|
126345
|
-
options.tracker?.start("parse", "Starting analysis...");
|
|
126081
|
+
options.tracker?.start("parse", isDiff ? "Analyzing working tree..." : "Starting analysis...");
|
|
126346
126082
|
const result = await runAnalysis(project.path, branch ?? void 0, (progress) => {
|
|
126347
126083
|
options.tracker?.detail("parse", progress.detail ?? "Analyzing...");
|
|
126348
126084
|
options.onProgress?.({ detail: progress.detail });
|
|
126349
|
-
}, { signal });
|
|
126350
|
-
if (signal?.aborted)
|
|
126351
|
-
throw new DOMException("Analysis cancelled", "AbortError");
|
|
126352
|
-
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
126353
|
-
const previousLatest = readLatest(project.path);
|
|
126354
|
-
const previousAnalysisId = previousLatest?.analysis.id ?? null;
|
|
126355
|
-
const previousActiveViolations = previousLatest ? previousLatest.violations.filter((v) => branch == null || previousLatest.analysis.branch == null || previousLatest.analysis.branch === branch) : [];
|
|
126356
|
-
try {
|
|
126357
|
-
graph.flows = detectFlows(result);
|
|
126358
|
-
} catch (flowError) {
|
|
126359
|
-
log.error(`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`);
|
|
126360
|
-
graph.flows = [];
|
|
126085
|
+
}, { signal, skipStash: isDiff });
|
|
126086
|
+
if (signal?.aborted) {
|
|
126087
|
+
throw new DOMException(isDiff ? "Diff cancelled" : "Analysis cancelled", "AbortError");
|
|
126361
126088
|
}
|
|
126362
|
-
|
|
126089
|
+
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
126090
|
+
let changedFiles = [];
|
|
126363
126091
|
let changedFileSet;
|
|
126364
|
-
if (
|
|
126092
|
+
if (isDiff) {
|
|
126093
|
+
try {
|
|
126094
|
+
const git = await getGit(project.path);
|
|
126095
|
+
const statusResult = await git.status();
|
|
126096
|
+
for (const f of statusResult.not_added)
|
|
126097
|
+
changedFiles.push({ path: f, status: "new" });
|
|
126098
|
+
for (const f of statusResult.created)
|
|
126099
|
+
changedFiles.push({ path: f, status: "new" });
|
|
126100
|
+
for (const f of statusResult.modified)
|
|
126101
|
+
changedFiles.push({ path: f, status: "modified" });
|
|
126102
|
+
for (const f of statusResult.staged) {
|
|
126103
|
+
if (!changedFiles.some((cf) => cf.path === f)) {
|
|
126104
|
+
changedFiles.push({ path: f, status: "modified" });
|
|
126105
|
+
}
|
|
126106
|
+
}
|
|
126107
|
+
for (const f of statusResult.deleted)
|
|
126108
|
+
changedFiles.push({ path: f, status: "deleted" });
|
|
126109
|
+
} catch (err) {
|
|
126110
|
+
log.warn(`[Diff] git status failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
126111
|
+
}
|
|
126112
|
+
} else if (latestBaseline?.analysis.commitHash && !skipGit) {
|
|
126365
126113
|
try {
|
|
126366
126114
|
const git = await getGit(project.path);
|
|
126367
|
-
const diffOutput = await git.diff([
|
|
126115
|
+
const diffOutput = await git.diff([latestBaseline.analysis.commitHash, "HEAD", "--name-only"]);
|
|
126368
126116
|
const files = diffOutput.trim().split("\n").filter(Boolean);
|
|
126369
126117
|
if (files.length > 0)
|
|
126370
126118
|
changedFileSet = new Set(files);
|
|
126371
126119
|
} catch {
|
|
126372
126120
|
}
|
|
126373
126121
|
}
|
|
126122
|
+
if (!isDiff) {
|
|
126123
|
+
try {
|
|
126124
|
+
graph.flows = detectFlows(result);
|
|
126125
|
+
} catch (flowError) {
|
|
126126
|
+
log.error(`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`);
|
|
126127
|
+
graph.flows = [];
|
|
126128
|
+
}
|
|
126129
|
+
touchProject(project.slug);
|
|
126130
|
+
}
|
|
126374
126131
|
options.tracker?.done("parse", `${result.services.length} services, ${result.fileAnalyses?.length ?? 0} files`);
|
|
126132
|
+
const previousActiveViolations = latestBaseline ? latestBaseline.violations.filter((v) => branch == null || latestBaseline.analysis.branch == null || latestBaseline.analysis.branch === branch) : [];
|
|
126133
|
+
const previousAnalysisId = latestBaseline?.analysis.id ?? null;
|
|
126375
126134
|
const provider = options.provider ?? (effectiveLlmRules ? createLLMProvider() : void 0);
|
|
126376
126135
|
if (provider) {
|
|
126377
126136
|
provider.setAnalysisId(analysisId);
|
|
@@ -126379,32 +126138,28 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126379
126138
|
if (signal)
|
|
126380
126139
|
provider.setAbortSignal(signal);
|
|
126381
126140
|
}
|
|
126382
|
-
|
|
126383
|
-
|
|
126384
|
-
|
|
126385
|
-
|
|
126386
|
-
|
|
126387
|
-
|
|
126388
|
-
|
|
126389
|
-
|
|
126390
|
-
|
|
126391
|
-
|
|
126392
|
-
|
|
126393
|
-
|
|
126394
|
-
|
|
126395
|
-
|
|
126396
|
-
|
|
126397
|
-
|
|
126398
|
-
|
|
126399
|
-
|
|
126400
|
-
|
|
126401
|
-
|
|
126402
|
-
|
|
126403
|
-
|
|
126404
|
-
} : void 0
|
|
126405
|
-
});
|
|
126406
|
-
} finally {
|
|
126407
|
-
}
|
|
126141
|
+
const pipelineResult = await runViolationPipeline({
|
|
126142
|
+
repoPath: project.path,
|
|
126143
|
+
analysisId,
|
|
126144
|
+
now,
|
|
126145
|
+
result,
|
|
126146
|
+
serviceIdMap,
|
|
126147
|
+
moduleIdMap,
|
|
126148
|
+
methodIdMap,
|
|
126149
|
+
dbIdMap,
|
|
126150
|
+
previousActiveViolations,
|
|
126151
|
+
changedFileSet,
|
|
126152
|
+
tracker: options.tracker,
|
|
126153
|
+
enabledCategories: effectiveCategories,
|
|
126154
|
+
enableLlmRules: effectiveLlmRules,
|
|
126155
|
+
provider,
|
|
126156
|
+
signal,
|
|
126157
|
+
onLlmEstimate: options.onLlmEstimate ? async (estimate) => {
|
|
126158
|
+
const proceed = await options.onLlmEstimate(estimate);
|
|
126159
|
+
options.onLlmResolved?.(proceed);
|
|
126160
|
+
return proceed;
|
|
126161
|
+
} : void 0
|
|
126162
|
+
});
|
|
126408
126163
|
if (pipelineResult.serviceDescriptions.length > 0) {
|
|
126409
126164
|
for (const desc of pipelineResult.serviceDescriptions) {
|
|
126410
126165
|
const svc = graph.services.find((s) => s.id === desc.id);
|
|
@@ -126416,56 +126171,105 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
126416
126171
|
enforceLocationInvariant(pipelineResult.added);
|
|
126417
126172
|
enforceLocationInvariant(pipelineResult.unchanged);
|
|
126418
126173
|
enforceLocationInvariant(pipelineResult.resolved);
|
|
126419
|
-
|
|
126420
|
-
|
|
126421
|
-
|
|
126174
|
+
log.info(`[${isDiff ? "Diff" : "Analysis"}] core complete in ${Date.now() - start}ms \u2014 ${pipelineResult.added.length} added, ${pipelineResult.unchanged.length} unchanged, ${pipelineResult.resolvedRefs.length} resolved`);
|
|
126175
|
+
return {
|
|
126176
|
+
mode,
|
|
126177
|
+
analysisId,
|
|
126178
|
+
now,
|
|
126422
126179
|
branch,
|
|
126423
126180
|
commitHash,
|
|
126424
126181
|
architecture: result.architecture,
|
|
126425
|
-
status: "completed",
|
|
126426
126182
|
metadata: result.metadata ?? null,
|
|
126427
126183
|
graph,
|
|
126428
|
-
|
|
126429
|
-
|
|
126430
|
-
|
|
126431
|
-
|
|
126432
|
-
|
|
126433
|
-
|
|
126434
|
-
};
|
|
126435
|
-
const latest = buildLatestSnapshot(snapshot, filename, pipelineResult.unchanged, pipelineResult.added);
|
|
126436
|
-
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
126437
|
-
writeAnalysis(project.path, snapshot);
|
|
126438
|
-
writeLatest(project.path, latest);
|
|
126439
|
-
const historyEntry = buildHistoryEntry(snapshot, filename, pipelineResult);
|
|
126440
|
-
appendHistory(project.path, historyEntry);
|
|
126441
|
-
deleteDiff(project.path);
|
|
126442
|
-
setLastAnalyzed(project.slug, now);
|
|
126443
|
-
return {
|
|
126444
|
-
analysisId,
|
|
126445
|
-
filename,
|
|
126446
|
-
serviceCount: result.services.length,
|
|
126447
|
-
fileCount: result.fileAnalyses?.length ?? 0,
|
|
126448
|
-
architecture: result.architecture,
|
|
126449
|
-
durationMs: Date.now() - start,
|
|
126450
|
-
violationsSummary: { total, bySeverity }
|
|
126184
|
+
changedFiles,
|
|
126185
|
+
pipelineResult,
|
|
126186
|
+
usage,
|
|
126187
|
+
latestBaseline,
|
|
126188
|
+
previousAnalysisId,
|
|
126189
|
+
analysisResult: result
|
|
126451
126190
|
};
|
|
126452
126191
|
} finally {
|
|
126453
126192
|
releaseAnalyzeLock(project.path);
|
|
126454
126193
|
}
|
|
126455
126194
|
}
|
|
126195
|
+
function enforceLocationInvariant(violations) {
|
|
126196
|
+
for (const v of violations) {
|
|
126197
|
+
const hasFile = v.filePath != null;
|
|
126198
|
+
const hasRange = v.lineStart != null && v.lineEnd != null;
|
|
126199
|
+
if (hasFile === hasRange)
|
|
126200
|
+
continue;
|
|
126201
|
+
log.warn(`[Violations] ${v.ruleKey}: partial location (filePath=${v.filePath}, lineStart=${v.lineStart}, lineEnd=${v.lineEnd}) \u2014 dropping to uphold the location invariant`);
|
|
126202
|
+
v.filePath = null;
|
|
126203
|
+
v.lineStart = null;
|
|
126204
|
+
v.lineEnd = null;
|
|
126205
|
+
}
|
|
126206
|
+
}
|
|
126207
|
+
var init_analyze_core = __esm({
|
|
126208
|
+
"apps/server/dist/commands/analyze-core.js"() {
|
|
126209
|
+
"use strict";
|
|
126210
|
+
init_logger();
|
|
126211
|
+
init_git();
|
|
126212
|
+
init_project_config();
|
|
126213
|
+
init_registry();
|
|
126214
|
+
init_analyzer_service();
|
|
126215
|
+
init_analysis_persistence_service();
|
|
126216
|
+
init_flow_service();
|
|
126217
|
+
init_violation_pipeline_service();
|
|
126218
|
+
init_provider();
|
|
126219
|
+
init_usage_service();
|
|
126220
|
+
init_analysis_store();
|
|
126221
|
+
init_atomic_write();
|
|
126222
|
+
}
|
|
126223
|
+
});
|
|
126224
|
+
|
|
126225
|
+
// apps/server/dist/commands/analyze-persist.js
|
|
126226
|
+
import path13 from "node:path";
|
|
126227
|
+
function persistFullAnalysis(project, core2, startedAt) {
|
|
126228
|
+
const filename = buildAnalysisFilename(core2.analysisId, core2.now);
|
|
126229
|
+
const snapshot = {
|
|
126230
|
+
id: core2.analysisId,
|
|
126231
|
+
createdAt: core2.now,
|
|
126232
|
+
branch: core2.branch,
|
|
126233
|
+
commitHash: core2.commitHash,
|
|
126234
|
+
architecture: core2.architecture,
|
|
126235
|
+
status: "completed",
|
|
126236
|
+
metadata: core2.metadata,
|
|
126237
|
+
graph: core2.graph,
|
|
126238
|
+
violations: {
|
|
126239
|
+
added: core2.pipelineResult.added,
|
|
126240
|
+
resolved: core2.pipelineResult.resolvedRefs,
|
|
126241
|
+
previousAnalysisId: core2.previousAnalysisId
|
|
126242
|
+
},
|
|
126243
|
+
usage: core2.usage
|
|
126244
|
+
};
|
|
126245
|
+
const latest = buildLatestSnapshot(snapshot, filename, core2.pipelineResult.unchanged, core2.pipelineResult.added);
|
|
126246
|
+
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
126247
|
+
writeAnalysis(project.path, snapshot);
|
|
126248
|
+
writeLatest(project.path, latest);
|
|
126249
|
+
appendHistory(project.path, buildHistoryEntry(snapshot, filename, core2.pipelineResult));
|
|
126250
|
+
deleteDiff(project.path);
|
|
126251
|
+
setLastAnalyzed(project.slug, core2.now);
|
|
126252
|
+
return {
|
|
126253
|
+
analysisId: core2.analysisId,
|
|
126254
|
+
filename,
|
|
126255
|
+
serviceCount: core2.graph.services.length,
|
|
126256
|
+
fileCount: core2.analysisResult.fileAnalyses?.length ?? 0,
|
|
126257
|
+
architecture: core2.architecture,
|
|
126258
|
+
durationMs: Date.now() - startedAt,
|
|
126259
|
+
violationsSummary: { total, bySeverity }
|
|
126260
|
+
};
|
|
126261
|
+
}
|
|
126262
|
+
function persistDiffAnalysis(project, core2) {
|
|
126263
|
+
if (!core2.latestBaseline) {
|
|
126264
|
+
throw new Error("Diff persist requires a latestBaseline \u2014 analyzeCore should have enforced this.");
|
|
126265
|
+
}
|
|
126266
|
+
const diff = buildDiffSnapshot(project.path, core2, core2.latestBaseline);
|
|
126267
|
+
writeDiff(project.path, diff);
|
|
126268
|
+
log.info(`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`);
|
|
126269
|
+
return { diff, isStale: false };
|
|
126270
|
+
}
|
|
126456
126271
|
function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
126457
|
-
const
|
|
126458
|
-
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126459
|
-
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126460
|
-
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
126461
|
-
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
126462
|
-
const denormalize = (v) => ({
|
|
126463
|
-
...v,
|
|
126464
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
126465
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
126466
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
126467
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
126468
|
-
});
|
|
126272
|
+
const denormalize = makeDenormalizer(snapshot.graph);
|
|
126469
126273
|
return {
|
|
126470
126274
|
head: filename,
|
|
126471
126275
|
analysis: {
|
|
@@ -126477,7 +126281,7 @@ function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
|
126477
126281
|
metadata: snapshot.metadata,
|
|
126478
126282
|
status: "completed"
|
|
126479
126283
|
},
|
|
126480
|
-
graph,
|
|
126284
|
+
graph: snapshot.graph,
|
|
126481
126285
|
violations: [...added.map(denormalize), ...unchanged.map(denormalize)]
|
|
126482
126286
|
};
|
|
126483
126287
|
}
|
|
@@ -126541,17 +126345,159 @@ function buildHistoryEntry(snapshot, filename, pipeline) {
|
|
|
126541
126345
|
}
|
|
126542
126346
|
};
|
|
126543
126347
|
}
|
|
126544
|
-
function
|
|
126545
|
-
|
|
126546
|
-
|
|
126547
|
-
|
|
126548
|
-
|
|
126348
|
+
function buildDiffSnapshot(repoPath, core2, baseline) {
|
|
126349
|
+
const { graph, changedFiles, pipelineResult } = core2;
|
|
126350
|
+
const denormalize = makeDenormalizer(graph);
|
|
126351
|
+
const newViolations = pipelineResult.added.map(denormalize);
|
|
126352
|
+
const latestById = new Map(baseline.violations.map((v) => [v.id, v]));
|
|
126353
|
+
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
126354
|
+
const changedAbs = new Set(changedFiles.map((c2) => path13.resolve(repoPath, c2.path)));
|
|
126355
|
+
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(path13.resolve(repoPath, p2)));
|
|
126356
|
+
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
126357
|
+
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
126358
|
+
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126359
|
+
const layerKeyById = new Map(graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`]));
|
|
126360
|
+
const affectedServices = /* @__PURE__ */ new Set();
|
|
126361
|
+
const affectedLayers = /* @__PURE__ */ new Set();
|
|
126362
|
+
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
126363
|
+
for (const mod of affectedModules) {
|
|
126364
|
+
const svcName = serviceNameById.get(mod.serviceId);
|
|
126365
|
+
if (svcName) {
|
|
126366
|
+
affectedServices.add(svcName);
|
|
126367
|
+
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
126368
|
+
}
|
|
126369
|
+
const layerKey = layerKeyById.get(mod.layerId);
|
|
126370
|
+
if (layerKey)
|
|
126371
|
+
affectedLayers.add(layerKey);
|
|
126372
|
+
}
|
|
126373
|
+
const moduleNameById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126374
|
+
const affectedMethodKeys = [];
|
|
126375
|
+
for (const method of graph.methods) {
|
|
126376
|
+
if (!affectedModuleIdSet.has(method.moduleId))
|
|
126549
126377
|
continue;
|
|
126550
|
-
|
|
126551
|
-
|
|
126552
|
-
|
|
126553
|
-
|
|
126378
|
+
const modName = moduleNameById.get(method.moduleId);
|
|
126379
|
+
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
126380
|
+
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
126381
|
+
if (svcName && modName)
|
|
126382
|
+
affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
126383
|
+
}
|
|
126384
|
+
return {
|
|
126385
|
+
id: core2.analysisId,
|
|
126386
|
+
baseAnalysisId: baseline.analysis.id,
|
|
126387
|
+
createdAt: core2.now,
|
|
126388
|
+
branch: core2.branch,
|
|
126389
|
+
commitHash: core2.commitHash,
|
|
126390
|
+
graph,
|
|
126391
|
+
changedFiles,
|
|
126392
|
+
newViolations,
|
|
126393
|
+
resolvedViolations,
|
|
126394
|
+
affectedNodeIds: {
|
|
126395
|
+
services: [...affectedServices],
|
|
126396
|
+
layers: [...affectedLayers],
|
|
126397
|
+
modules: [...affectedModuleKeys],
|
|
126398
|
+
methods: affectedMethodKeys
|
|
126399
|
+
},
|
|
126400
|
+
summary: {
|
|
126401
|
+
newCount: newViolations.length,
|
|
126402
|
+
unchangedCount: pipelineResult.unchanged.length,
|
|
126403
|
+
resolvedCount: resolvedViolations.length
|
|
126404
|
+
},
|
|
126405
|
+
usage: core2.usage
|
|
126406
|
+
};
|
|
126407
|
+
}
|
|
126408
|
+
function makeDenormalizer(graph) {
|
|
126409
|
+
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
126410
|
+
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
126411
|
+
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
126412
|
+
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
126413
|
+
return (v) => ({
|
|
126414
|
+
...v,
|
|
126415
|
+
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
126416
|
+
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
126417
|
+
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
126418
|
+
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
126419
|
+
});
|
|
126420
|
+
}
|
|
126421
|
+
var init_analyze_persist = __esm({
|
|
126422
|
+
"apps/server/dist/commands/analyze-persist.js"() {
|
|
126423
|
+
"use strict";
|
|
126424
|
+
init_logger();
|
|
126425
|
+
init_registry();
|
|
126426
|
+
init_analysis_store();
|
|
126427
|
+
}
|
|
126428
|
+
});
|
|
126429
|
+
|
|
126430
|
+
// apps/server/dist/commands/diff-in-process.js
|
|
126431
|
+
var diff_in_process_exports = {};
|
|
126432
|
+
__export(diff_in_process_exports, {
|
|
126433
|
+
diffInProcess: () => diffInProcess
|
|
126434
|
+
});
|
|
126435
|
+
async function diffInProcess(project, options = {}) {
|
|
126436
|
+
const core2 = await analyzeCore(project, { ...options, mode: "diff" });
|
|
126437
|
+
return persistDiffAnalysis(project, core2);
|
|
126438
|
+
}
|
|
126439
|
+
var init_diff_in_process = __esm({
|
|
126440
|
+
"apps/server/dist/commands/diff-in-process.js"() {
|
|
126441
|
+
"use strict";
|
|
126442
|
+
init_analyze_core();
|
|
126443
|
+
init_analyze_persist();
|
|
126554
126444
|
}
|
|
126445
|
+
});
|
|
126446
|
+
|
|
126447
|
+
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
126448
|
+
var import_index = __toESM(require_commander(), 1);
|
|
126449
|
+
var {
|
|
126450
|
+
program,
|
|
126451
|
+
createCommand,
|
|
126452
|
+
createArgument,
|
|
126453
|
+
createOption,
|
|
126454
|
+
CommanderError,
|
|
126455
|
+
InvalidArgumentError,
|
|
126456
|
+
InvalidOptionArgumentError,
|
|
126457
|
+
// deprecated old name
|
|
126458
|
+
Command,
|
|
126459
|
+
Argument,
|
|
126460
|
+
Option,
|
|
126461
|
+
Help
|
|
126462
|
+
} = import_index.default;
|
|
126463
|
+
|
|
126464
|
+
// tools/cli/src/index.ts
|
|
126465
|
+
init_dist4();
|
|
126466
|
+
|
|
126467
|
+
// tools/cli/src/commands/add.ts
|
|
126468
|
+
init_dist4();
|
|
126469
|
+
init_paths();
|
|
126470
|
+
init_registry();
|
|
126471
|
+
init_helpers();
|
|
126472
|
+
async function runAdd() {
|
|
126473
|
+
const repoPath = resolveRepoDir(process.cwd()) ?? process.cwd();
|
|
126474
|
+
mt("Adding repository to TrueCourse");
|
|
126475
|
+
O2.step(repoPath);
|
|
126476
|
+
ensureRepoTruecourseDir(repoPath);
|
|
126477
|
+
const existing = getProjectByPath(repoPath);
|
|
126478
|
+
const entry = registerProject(repoPath);
|
|
126479
|
+
if (existing) {
|
|
126480
|
+
O2.info(`Repository "${entry.name}" is already registered.`);
|
|
126481
|
+
} else {
|
|
126482
|
+
O2.success(`Repository "${entry.name}" added.`);
|
|
126483
|
+
}
|
|
126484
|
+
await promptInstallSkills(repoPath);
|
|
126485
|
+
gt("Run `truecourse analyze` to generate analysis data.");
|
|
126486
|
+
}
|
|
126487
|
+
|
|
126488
|
+
// tools/cli/src/commands/analyze.ts
|
|
126489
|
+
init_dist4();
|
|
126490
|
+
import { execSync } from "node:child_process";
|
|
126491
|
+
import path15 from "node:path";
|
|
126492
|
+
|
|
126493
|
+
// apps/server/dist/commands/analyze-in-process.js
|
|
126494
|
+
init_analysis_store();
|
|
126495
|
+
init_analyze_core();
|
|
126496
|
+
init_analyze_persist();
|
|
126497
|
+
async function analyzeInProcess(project, options = {}) {
|
|
126498
|
+
const startedAt = Date.now();
|
|
126499
|
+
const core2 = await analyzeCore(project, { ...options, mode: "full" });
|
|
126500
|
+
return persistFullAnalysis(project, core2, startedAt);
|
|
126555
126501
|
}
|
|
126556
126502
|
|
|
126557
126503
|
// tools/cli/src/commands/analyze.ts
|
|
@@ -126562,10 +126508,24 @@ init_project_config();
|
|
|
126562
126508
|
init_logger();
|
|
126563
126509
|
init_helpers();
|
|
126564
126510
|
|
|
126511
|
+
// tools/cli/src/commands/llm-prompt.ts
|
|
126512
|
+
init_dist4();
|
|
126513
|
+
async function promptLlmEstimate(estimate) {
|
|
126514
|
+
const totalRules = estimate.uniqueRuleCount ?? estimate.tiers.reduce((s, t2) => s + t2.ruleCount, 0);
|
|
126515
|
+
const totalFiles = estimate.uniqueFileCount ?? estimate.tiers.reduce((s, t2) => s + t2.fileCount, 0);
|
|
126516
|
+
const tokens = estimate.totalEstimatedTokens;
|
|
126517
|
+
const tokenStr = tokens >= 1e6 ? `~${(tokens / 1e6).toFixed(1)}M tokens` : `~${Math.round(tokens / 1e3)}k tokens`;
|
|
126518
|
+
O2.step(`LLM will analyze ${totalFiles} files with ${totalRules} rules (${tokenStr})`);
|
|
126519
|
+
const proceed = await ot2({ message: "Run LLM-powered rules?", initialValue: true });
|
|
126520
|
+
if (q(proceed)) return false;
|
|
126521
|
+
if (!proceed) O2.info("Skipping LLM rules.");
|
|
126522
|
+
return !!proceed;
|
|
126523
|
+
}
|
|
126524
|
+
|
|
126565
126525
|
// tools/cli/src/telemetry.ts
|
|
126566
126526
|
init_dist4();
|
|
126567
126527
|
import fs9 from "node:fs";
|
|
126568
|
-
import
|
|
126528
|
+
import path14 from "node:path";
|
|
126569
126529
|
import os4 from "node:os";
|
|
126570
126530
|
import crypto from "node:crypto";
|
|
126571
126531
|
var DEFAULT_CONFIG2 = {
|
|
@@ -126574,7 +126534,7 @@ var DEFAULT_CONFIG2 = {
|
|
|
126574
126534
|
noticeShown: false
|
|
126575
126535
|
};
|
|
126576
126536
|
function getTelemetryConfigPath() {
|
|
126577
|
-
return
|
|
126537
|
+
return path14.join(os4.homedir(), ".truecourse", "telemetry.json");
|
|
126578
126538
|
}
|
|
126579
126539
|
function readTelemetryConfig() {
|
|
126580
126540
|
const configPath = getTelemetryConfigPath();
|
|
@@ -126599,7 +126559,7 @@ function readTelemetryConfig() {
|
|
|
126599
126559
|
}
|
|
126600
126560
|
function writeTelemetryConfig(partial) {
|
|
126601
126561
|
const configPath = getTelemetryConfigPath();
|
|
126602
|
-
const dir =
|
|
126562
|
+
const dir = path14.dirname(configPath);
|
|
126603
126563
|
fs9.mkdirSync(dir, { recursive: true });
|
|
126604
126564
|
let current;
|
|
126605
126565
|
try {
|
|
@@ -126734,16 +126694,9 @@ async function runAnalyze(_options = {}) {
|
|
|
126734
126694
|
enableLlmRulesOverride: enableLlmRules,
|
|
126735
126695
|
onLlmEstimate: async (estimate) => {
|
|
126736
126696
|
stopSpinner();
|
|
126737
|
-
const
|
|
126738
|
-
const totalFiles = estimate.uniqueFileCount ?? estimate.tiers.reduce((s, t2) => s + t2.fileCount, 0);
|
|
126739
|
-
const tokens = estimate.totalEstimatedTokens;
|
|
126740
|
-
const tokenStr = tokens >= 1e6 ? `~${(tokens / 1e6).toFixed(1)}M tokens` : `~${Math.round(tokens / 1e3)}k tokens`;
|
|
126741
|
-
O2.step(`LLM will analyze ${totalFiles} files with ${totalRules} rules (${tokenStr})`);
|
|
126742
|
-
const proceed = await ot2({ message: "Run LLM-powered rules?", initialValue: true });
|
|
126697
|
+
const proceed = await promptLlmEstimate(estimate);
|
|
126743
126698
|
renderPhase = "post-llm";
|
|
126744
|
-
|
|
126745
|
-
if (!proceed) O2.info("Skipping LLM rules.");
|
|
126746
|
-
return !!proceed;
|
|
126699
|
+
return proceed;
|
|
126747
126700
|
}
|
|
126748
126701
|
});
|
|
126749
126702
|
stopSpinner();
|
|
@@ -126774,19 +126727,32 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126774
126727
|
configureLogger({
|
|
126775
126728
|
filePath: path15.join(project.path, ".truecourse/logs/analyze.log")
|
|
126776
126729
|
});
|
|
126777
|
-
const
|
|
126778
|
-
|
|
126730
|
+
const config2 = readProjectConfig(project.path);
|
|
126731
|
+
const enabledCategories = config2.enabledCategories ?? void 0;
|
|
126732
|
+
const enableLlmRules = config2.enableLlmRules ?? true;
|
|
126733
|
+
renderPhase = enableLlmRules ? "pre-llm" : "all";
|
|
126734
|
+
const stepDefs = buildAnalysisSteps(enabledCategories, enableLlmRules);
|
|
126735
|
+
const tracker = new StepTracker((payload) => {
|
|
126736
|
+
if (payload.steps) renderSteps(payload.steps);
|
|
126737
|
+
}, stepDefs);
|
|
126779
126738
|
const abortController = new AbortController();
|
|
126780
126739
|
const onSigint = () => abortController.abort();
|
|
126781
126740
|
process.on("SIGINT", onSigint);
|
|
126782
126741
|
try {
|
|
126783
126742
|
const { diff } = await diffInProcess2(project, {
|
|
126743
|
+
tracker,
|
|
126784
126744
|
signal: abortController.signal,
|
|
126785
|
-
|
|
126786
|
-
|
|
126745
|
+
enabledCategoriesOverride: enabledCategories,
|
|
126746
|
+
enableLlmRulesOverride: enableLlmRules,
|
|
126747
|
+
onLlmEstimate: async (estimate) => {
|
|
126748
|
+
stopSpinner();
|
|
126749
|
+
const proceed = await promptLlmEstimate(estimate);
|
|
126750
|
+
renderPhase = "post-llm";
|
|
126751
|
+
return proceed;
|
|
126787
126752
|
}
|
|
126788
126753
|
});
|
|
126789
|
-
|
|
126754
|
+
stopSpinner();
|
|
126755
|
+
O2.success("Diff check complete");
|
|
126790
126756
|
renderDiffResultsSummary2({
|
|
126791
126757
|
changedFiles: diff.changedFiles,
|
|
126792
126758
|
newViolations: diff.newViolations,
|
|
@@ -126796,7 +126762,7 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126796
126762
|
});
|
|
126797
126763
|
gt("Diff complete \u2014 view results with: truecourse dashboard");
|
|
126798
126764
|
} catch (err) {
|
|
126799
|
-
|
|
126765
|
+
stopSpinner();
|
|
126800
126766
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
126801
126767
|
gt("Diff cancelled");
|
|
126802
126768
|
process.exit(130);
|
|
@@ -130474,7 +130440,7 @@ async function runHooksRun() {
|
|
|
130474
130440
|
|
|
130475
130441
|
// tools/cli/src/index.ts
|
|
130476
130442
|
var program2 = new Command();
|
|
130477
|
-
program2.name("truecourse").version("0.4.
|
|
130443
|
+
program2.name("truecourse").version("0.4.5").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
130478
130444
|
var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").option("--reconfigure", "Re-prompt for console vs background service mode").action(async (options) => {
|
|
130479
130445
|
await runDashboard({ reconfigure: options.reconfigure });
|
|
130480
130446
|
});
|