truecourse 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.mjs +681 -410
- package/package.json +4 -10
- package/public/assets/{index-8ZCcQOMQ.js → index-BCi5yD5r.js} +94 -94
- package/public/assets/index-C6z6cTBb.css +1 -0
- package/public/index.html +2 -2
- package/server.mjs +859 -777
- package/skills/truecourse/truecourse-analyze/SKILL.md +31 -14
- package/skills/truecourse/truecourse-fix/SKILL.md +39 -13
- package/skills/truecourse/truecourse-list/SKILL.md +21 -6
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-tsx.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
- package/wasm/web-tree-sitter.wasm +0 -0
- package/postinstall.cjs +0 -74
- package/public/assets/index-CIKYHPJm.css +0 -1
package/cli.mjs
CHANGED
|
@@ -972,8 +972,8 @@ var require_command = __commonJS({
|
|
|
972
972
|
"node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
|
|
973
973
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
974
974
|
var childProcess = __require("node:child_process");
|
|
975
|
-
var
|
|
976
|
-
var
|
|
975
|
+
var path22 = __require("node:path");
|
|
976
|
+
var fs17 = __require("node:fs");
|
|
977
977
|
var process2 = __require("node:process");
|
|
978
978
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
979
979
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1905,11 +1905,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1905
1905
|
let launchWithNode = false;
|
|
1906
1906
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1907
1907
|
function findFile(baseDir, baseName) {
|
|
1908
|
-
const localBin =
|
|
1909
|
-
if (
|
|
1910
|
-
if (sourceExt.includes(
|
|
1908
|
+
const localBin = path22.resolve(baseDir, baseName);
|
|
1909
|
+
if (fs17.existsSync(localBin)) return localBin;
|
|
1910
|
+
if (sourceExt.includes(path22.extname(baseName))) return void 0;
|
|
1911
1911
|
const foundExt = sourceExt.find(
|
|
1912
|
-
(ext2) =>
|
|
1912
|
+
(ext2) => fs17.existsSync(`${localBin}${ext2}`)
|
|
1913
1913
|
);
|
|
1914
1914
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1915
1915
|
return void 0;
|
|
@@ -1921,21 +1921,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1921
1921
|
if (this._scriptPath) {
|
|
1922
1922
|
let resolvedScriptPath;
|
|
1923
1923
|
try {
|
|
1924
|
-
resolvedScriptPath =
|
|
1924
|
+
resolvedScriptPath = fs17.realpathSync(this._scriptPath);
|
|
1925
1925
|
} catch (err) {
|
|
1926
1926
|
resolvedScriptPath = this._scriptPath;
|
|
1927
1927
|
}
|
|
1928
|
-
executableDir =
|
|
1929
|
-
|
|
1928
|
+
executableDir = path22.resolve(
|
|
1929
|
+
path22.dirname(resolvedScriptPath),
|
|
1930
1930
|
executableDir
|
|
1931
1931
|
);
|
|
1932
1932
|
}
|
|
1933
1933
|
if (executableDir) {
|
|
1934
1934
|
let localFile = findFile(executableDir, executableFile);
|
|
1935
1935
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1936
|
-
const legacyName =
|
|
1936
|
+
const legacyName = path22.basename(
|
|
1937
1937
|
this._scriptPath,
|
|
1938
|
-
|
|
1938
|
+
path22.extname(this._scriptPath)
|
|
1939
1939
|
);
|
|
1940
1940
|
if (legacyName !== this._name) {
|
|
1941
1941
|
localFile = findFile(
|
|
@@ -1946,7 +1946,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1946
1946
|
}
|
|
1947
1947
|
executableFile = localFile || executableFile;
|
|
1948
1948
|
}
|
|
1949
|
-
launchWithNode = sourceExt.includes(
|
|
1949
|
+
launchWithNode = sourceExt.includes(path22.extname(executableFile));
|
|
1950
1950
|
let proc;
|
|
1951
1951
|
if (process2.platform !== "win32") {
|
|
1952
1952
|
if (launchWithNode) {
|
|
@@ -2786,7 +2786,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2786
2786
|
* @return {Command}
|
|
2787
2787
|
*/
|
|
2788
2788
|
nameFromFilename(filename) {
|
|
2789
|
-
this._name =
|
|
2789
|
+
this._name = path22.basename(filename, path22.extname(filename));
|
|
2790
2790
|
return this;
|
|
2791
2791
|
}
|
|
2792
2792
|
/**
|
|
@@ -2800,9 +2800,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2800
2800
|
* @param {string} [path]
|
|
2801
2801
|
* @return {(string|null|Command)}
|
|
2802
2802
|
*/
|
|
2803
|
-
executableDir(
|
|
2804
|
-
if (
|
|
2805
|
-
this._executableDir =
|
|
2803
|
+
executableDir(path23) {
|
|
2804
|
+
if (path23 === void 0) return this._executableDir;
|
|
2805
|
+
this._executableDir = path23;
|
|
2806
2806
|
return this;
|
|
2807
2807
|
}
|
|
2808
2808
|
/**
|
|
@@ -4062,8 +4062,11 @@ var init_registry = __esm({
|
|
|
4062
4062
|
// tools/cli/src/commands/helpers.ts
|
|
4063
4063
|
var helpers_exports = {};
|
|
4064
4064
|
__export(helpers_exports, {
|
|
4065
|
+
exitMissingNonInteractiveFlag: () => exitMissingNonInteractiveFlag,
|
|
4065
4066
|
getConfigPath: () => getConfigPath,
|
|
4066
4067
|
getServerUrl: () => getServerUrl,
|
|
4068
|
+
hasInstalledSkills: () => hasInstalledSkills,
|
|
4069
|
+
isInteractive: () => isInteractive,
|
|
4067
4070
|
openInBrowser: () => openInBrowser,
|
|
4068
4071
|
promptInstallSkills: () => promptInstallSkills,
|
|
4069
4072
|
readConfig: () => readConfig,
|
|
@@ -4316,11 +4319,24 @@ function openInBrowser(url) {
|
|
|
4316
4319
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
4317
4320
|
exec2(`${cmd} ${url}`);
|
|
4318
4321
|
}
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4322
|
+
function skillDestPath(repoPath) {
|
|
4323
|
+
return resolve(repoPath, ".claude", "skills", "truecourse");
|
|
4324
|
+
}
|
|
4325
|
+
function hasInstalledSkills(repoPath) {
|
|
4326
|
+
return existsSync(skillDestPath(repoPath));
|
|
4327
|
+
}
|
|
4328
|
+
function isInteractive() {
|
|
4329
|
+
return !!process.stdin.isTTY;
|
|
4330
|
+
}
|
|
4331
|
+
function exitMissingNonInteractiveFlag(context, flagGuidance) {
|
|
4332
|
+
O2.error(
|
|
4333
|
+
`${context}
|
|
4334
|
+
|
|
4335
|
+
Running non-interactively with no answer. ${flagGuidance}`
|
|
4336
|
+
);
|
|
4337
|
+
process.exit(1);
|
|
4338
|
+
}
|
|
4339
|
+
function copySkillsInto(repoPath) {
|
|
4324
4340
|
const __dirname4 = dirname(fileURLToPath(import.meta.url));
|
|
4325
4341
|
const srcPath = resolve(__dirname4, "..", "..", "skills", "truecourse");
|
|
4326
4342
|
const distPath = resolve(__dirname4, "skills", "truecourse");
|
|
@@ -4336,6 +4352,20 @@ async function promptInstallSkills(repoPath) {
|
|
|
4336
4352
|
O2.message(" - truecourse-list (list violations)");
|
|
4337
4353
|
O2.message(" - truecourse-fix (apply fixes)");
|
|
4338
4354
|
}
|
|
4355
|
+
async function promptInstallSkills(repoPath, { install } = {}) {
|
|
4356
|
+
if (hasInstalledSkills(repoPath)) return;
|
|
4357
|
+
if (install === true) {
|
|
4358
|
+
copySkillsInto(repoPath);
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
if (install === false) return;
|
|
4362
|
+
if (!isInteractive()) return;
|
|
4363
|
+
const answer = await ot2({
|
|
4364
|
+
message: "Would you like to install Claude Code skills?"
|
|
4365
|
+
});
|
|
4366
|
+
if (q(answer) || !answer) return;
|
|
4367
|
+
copySkillsInto(repoPath);
|
|
4368
|
+
}
|
|
4339
4369
|
var DEFAULT_PORT, DEFAULT_CONFIG;
|
|
4340
4370
|
var init_helpers = __esm({
|
|
4341
4371
|
"tools/cli/src/commands/helpers.ts"() {
|
|
@@ -4458,6 +4488,26 @@ function writeAnalysis(repoPath, snapshot) {
|
|
|
4458
4488
|
atomicWriteJson(analysisFilePath(repoPath, filename), snapshot);
|
|
4459
4489
|
return { filename, snapshot };
|
|
4460
4490
|
}
|
|
4491
|
+
function readAnalysis(repoPath, filename) {
|
|
4492
|
+
const file = analysisFilePath(repoPath, filename);
|
|
4493
|
+
if (!fs5.existsSync(file))
|
|
4494
|
+
return null;
|
|
4495
|
+
return JSON.parse(fs5.readFileSync(file, "utf-8"));
|
|
4496
|
+
}
|
|
4497
|
+
function listAnalyses(repoPath) {
|
|
4498
|
+
const dir = analysesDir(repoPath);
|
|
4499
|
+
if (!fs5.existsSync(dir))
|
|
4500
|
+
return [];
|
|
4501
|
+
return fs5.readdirSync(dir).filter((name) => name.endsWith(".json")).sort();
|
|
4502
|
+
}
|
|
4503
|
+
function findAnalysisFilename(repoPath, analysisId) {
|
|
4504
|
+
for (const name of listAnalyses(repoPath).reverse()) {
|
|
4505
|
+
const snap = readAnalysis(repoPath, name);
|
|
4506
|
+
if (snap?.id === analysisId)
|
|
4507
|
+
return name;
|
|
4508
|
+
}
|
|
4509
|
+
return null;
|
|
4510
|
+
}
|
|
4461
4511
|
function readHistory(repoPath) {
|
|
4462
4512
|
const file = historyPath(repoPath);
|
|
4463
4513
|
if (!fs5.existsSync(file))
|
|
@@ -5249,10 +5299,10 @@ var require_src3 = __commonJS({
|
|
|
5249
5299
|
var fs_1 = __require("fs");
|
|
5250
5300
|
var debug_1 = __importDefault(require_src2());
|
|
5251
5301
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
5252
|
-
function check(
|
|
5253
|
-
log2(`checking %s`,
|
|
5302
|
+
function check(path22, isFile, isDirectory) {
|
|
5303
|
+
log2(`checking %s`, path22);
|
|
5254
5304
|
try {
|
|
5255
|
-
const stat = fs_1.statSync(
|
|
5305
|
+
const stat = fs_1.statSync(path22);
|
|
5256
5306
|
if (stat.isFile() && isFile) {
|
|
5257
5307
|
log2(`[OK] path represents a file`);
|
|
5258
5308
|
return true;
|
|
@@ -5272,8 +5322,8 @@ var require_src3 = __commonJS({
|
|
|
5272
5322
|
throw e;
|
|
5273
5323
|
}
|
|
5274
5324
|
}
|
|
5275
|
-
function exists2(
|
|
5276
|
-
return check(
|
|
5325
|
+
function exists2(path22, type2 = exports.READABLE) {
|
|
5326
|
+
return check(path22, (type2 & exports.FILE) > 0, (type2 & exports.FOLDER) > 0);
|
|
5277
5327
|
}
|
|
5278
5328
|
exports.exists = exists2;
|
|
5279
5329
|
exports.FILE = 1;
|
|
@@ -5346,8 +5396,8 @@ function pathspec(...paths) {
|
|
|
5346
5396
|
cache.set(key, paths);
|
|
5347
5397
|
return key;
|
|
5348
5398
|
}
|
|
5349
|
-
function isPathSpec(
|
|
5350
|
-
return
|
|
5399
|
+
function isPathSpec(path22) {
|
|
5400
|
+
return path22 instanceof String && cache.has(path22);
|
|
5351
5401
|
}
|
|
5352
5402
|
function toPaths(pathSpec) {
|
|
5353
5403
|
return cache.get(pathSpec) || [];
|
|
@@ -5391,8 +5441,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
5391
5441
|
function forEachLineWithContent(input, callback) {
|
|
5392
5442
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
5393
5443
|
}
|
|
5394
|
-
function folderExists(
|
|
5395
|
-
return (0, import_file_exists.exists)(
|
|
5444
|
+
function folderExists(path22) {
|
|
5445
|
+
return (0, import_file_exists.exists)(path22, import_file_exists.FOLDER);
|
|
5396
5446
|
}
|
|
5397
5447
|
function append(target, item) {
|
|
5398
5448
|
if (Array.isArray(target)) {
|
|
@@ -5583,8 +5633,8 @@ function checkIsRepoRootTask() {
|
|
|
5583
5633
|
commands,
|
|
5584
5634
|
format: "utf-8",
|
|
5585
5635
|
onError,
|
|
5586
|
-
parser(
|
|
5587
|
-
return /^\.(git)?$/.test(
|
|
5636
|
+
parser(path22) {
|
|
5637
|
+
return /^\.(git)?$/.test(path22.trim());
|
|
5588
5638
|
}
|
|
5589
5639
|
};
|
|
5590
5640
|
}
|
|
@@ -5840,11 +5890,11 @@ function parseGrep(grep) {
|
|
|
5840
5890
|
const paths = /* @__PURE__ */ new Set();
|
|
5841
5891
|
const results = {};
|
|
5842
5892
|
forEachLineWithContent(grep, (input) => {
|
|
5843
|
-
const [
|
|
5844
|
-
paths.add(
|
|
5845
|
-
(results[
|
|
5893
|
+
const [path22, line, preview] = input.split(NULL);
|
|
5894
|
+
paths.add(path22);
|
|
5895
|
+
(results[path22] = results[path22] || []).push({
|
|
5846
5896
|
line: asNumber(line),
|
|
5847
|
-
path:
|
|
5897
|
+
path: path22,
|
|
5848
5898
|
preview
|
|
5849
5899
|
});
|
|
5850
5900
|
});
|
|
@@ -6141,14 +6191,14 @@ function hashObjectTask(filePath, write) {
|
|
|
6141
6191
|
}
|
|
6142
6192
|
return straightThroughStringTask(commands, true);
|
|
6143
6193
|
}
|
|
6144
|
-
function parseInit(bare,
|
|
6194
|
+
function parseInit(bare, path22, text) {
|
|
6145
6195
|
const response = String(text).trim();
|
|
6146
6196
|
let result;
|
|
6147
6197
|
if (result = initResponseRegex.exec(response)) {
|
|
6148
|
-
return new InitSummary(bare,
|
|
6198
|
+
return new InitSummary(bare, path22, false, result[1]);
|
|
6149
6199
|
}
|
|
6150
6200
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6151
|
-
return new InitSummary(bare,
|
|
6201
|
+
return new InitSummary(bare, path22, true, result[1]);
|
|
6152
6202
|
}
|
|
6153
6203
|
let gitDir = "";
|
|
6154
6204
|
const tokens = response.split(" ");
|
|
@@ -6159,12 +6209,12 @@ function parseInit(bare, path21, text) {
|
|
|
6159
6209
|
break;
|
|
6160
6210
|
}
|
|
6161
6211
|
}
|
|
6162
|
-
return new InitSummary(bare,
|
|
6212
|
+
return new InitSummary(bare, path22, /^re/i.test(response), gitDir);
|
|
6163
6213
|
}
|
|
6164
6214
|
function hasBareCommand(command) {
|
|
6165
6215
|
return command.includes(bareCommand);
|
|
6166
6216
|
}
|
|
6167
|
-
function initTask(bare = false,
|
|
6217
|
+
function initTask(bare = false, path22, customArgs) {
|
|
6168
6218
|
const commands = ["init", ...customArgs];
|
|
6169
6219
|
if (bare && !hasBareCommand(commands)) {
|
|
6170
6220
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6173,7 +6223,7 @@ function initTask(bare = false, path21, customArgs) {
|
|
|
6173
6223
|
commands,
|
|
6174
6224
|
format: "utf-8",
|
|
6175
6225
|
parser(text) {
|
|
6176
|
-
return parseInit(commands.includes("--bare"),
|
|
6226
|
+
return parseInit(commands.includes("--bare"), path22, text);
|
|
6177
6227
|
}
|
|
6178
6228
|
};
|
|
6179
6229
|
}
|
|
@@ -6452,14 +6502,14 @@ function splitLine(result, lineStr) {
|
|
|
6452
6502
|
default:
|
|
6453
6503
|
return;
|
|
6454
6504
|
}
|
|
6455
|
-
function data(index, workingDir,
|
|
6505
|
+
function data(index, workingDir, path22) {
|
|
6456
6506
|
const raw = `${index}${workingDir}`;
|
|
6457
6507
|
const handler = parsers6.get(raw);
|
|
6458
6508
|
if (handler) {
|
|
6459
|
-
handler(result,
|
|
6509
|
+
handler(result, path22);
|
|
6460
6510
|
}
|
|
6461
6511
|
if (raw !== "##" && raw !== "!!") {
|
|
6462
|
-
result.files.push(new FileStatusSummary(
|
|
6512
|
+
result.files.push(new FileStatusSummary(path22, index, workingDir));
|
|
6463
6513
|
}
|
|
6464
6514
|
}
|
|
6465
6515
|
}
|
|
@@ -6646,8 +6696,8 @@ function deleteBranchTask(branch, forceDelete = false) {
|
|
|
6646
6696
|
return task;
|
|
6647
6697
|
}
|
|
6648
6698
|
function toPath(input) {
|
|
6649
|
-
const
|
|
6650
|
-
return
|
|
6699
|
+
const path22 = input.trim().replace(/^["']|["']$/g, "");
|
|
6700
|
+
return path22 && normalize(path22);
|
|
6651
6701
|
}
|
|
6652
6702
|
function checkIgnoreTask(paths) {
|
|
6653
6703
|
return {
|
|
@@ -6786,8 +6836,8 @@ function stashListTask(opt = {}, customArgs) {
|
|
|
6786
6836
|
parser: parser4
|
|
6787
6837
|
};
|
|
6788
6838
|
}
|
|
6789
|
-
function addSubModuleTask(repo,
|
|
6790
|
-
return subModuleTask(["add", repo,
|
|
6839
|
+
function addSubModuleTask(repo, path22) {
|
|
6840
|
+
return subModuleTask(["add", repo, path22]);
|
|
6791
6841
|
}
|
|
6792
6842
|
function initSubModuleTask(customArgs) {
|
|
6793
6843
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8099,9 +8149,9 @@ var init_esm = __esm({
|
|
|
8099
8149
|
"src/lib/responses/InitSummary.ts"() {
|
|
8100
8150
|
"use strict";
|
|
8101
8151
|
InitSummary = class {
|
|
8102
|
-
constructor(bare,
|
|
8152
|
+
constructor(bare, path22, existing, gitDir) {
|
|
8103
8153
|
this.bare = bare;
|
|
8104
|
-
this.path =
|
|
8154
|
+
this.path = path22;
|
|
8105
8155
|
this.existing = existing;
|
|
8106
8156
|
this.gitDir = gitDir;
|
|
8107
8157
|
}
|
|
@@ -8635,12 +8685,12 @@ var init_esm = __esm({
|
|
|
8635
8685
|
"use strict";
|
|
8636
8686
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
8637
8687
|
FileStatusSummary = class {
|
|
8638
|
-
constructor(
|
|
8639
|
-
this.path =
|
|
8688
|
+
constructor(path22, index, working_dir) {
|
|
8689
|
+
this.path = path22;
|
|
8640
8690
|
this.index = index;
|
|
8641
8691
|
this.working_dir = working_dir;
|
|
8642
8692
|
if (index === "R" || working_dir === "R") {
|
|
8643
|
-
const detail = fromPathRegex.exec(
|
|
8693
|
+
const detail = fromPathRegex.exec(path22) || [null, path22, path22];
|
|
8644
8694
|
this.from = detail[2] || "";
|
|
8645
8695
|
this.path = detail[1] || "";
|
|
8646
8696
|
}
|
|
@@ -8903,9 +8953,9 @@ var init_esm = __esm({
|
|
|
8903
8953
|
next
|
|
8904
8954
|
);
|
|
8905
8955
|
}
|
|
8906
|
-
hashObject(
|
|
8956
|
+
hashObject(path22, write) {
|
|
8907
8957
|
return this._runTask(
|
|
8908
|
-
hashObjectTask(
|
|
8958
|
+
hashObjectTask(path22, write === true),
|
|
8909
8959
|
trailingFunctionArgument(arguments)
|
|
8910
8960
|
);
|
|
8911
8961
|
}
|
|
@@ -9542,8 +9592,8 @@ var init_esm = __esm({
|
|
|
9542
9592
|
}
|
|
9543
9593
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
9544
9594
|
};
|
|
9545
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
9546
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9595
|
+
Git2.prototype.submoduleAdd = function(repo, path22, then) {
|
|
9596
|
+
return this._runTask(addSubModuleTask2(repo, path22), trailingFunctionArgument2(arguments));
|
|
9547
9597
|
};
|
|
9548
9598
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
9549
9599
|
return this._runTask(
|
|
@@ -10164,7 +10214,7 @@ var require_ignore = __commonJS({
|
|
|
10164
10214
|
// path matching.
|
|
10165
10215
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
10166
10216
|
// @returns {TestResult} true if a file is ignored
|
|
10167
|
-
test(
|
|
10217
|
+
test(path22, checkUnignored, mode) {
|
|
10168
10218
|
let ignored = false;
|
|
10169
10219
|
let unignored = false;
|
|
10170
10220
|
let matchedRule;
|
|
@@ -10173,7 +10223,7 @@ var require_ignore = __commonJS({
|
|
|
10173
10223
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
10174
10224
|
return;
|
|
10175
10225
|
}
|
|
10176
|
-
const matched = rule[mode].test(
|
|
10226
|
+
const matched = rule[mode].test(path22);
|
|
10177
10227
|
if (!matched) {
|
|
10178
10228
|
return;
|
|
10179
10229
|
}
|
|
@@ -10194,17 +10244,17 @@ var require_ignore = __commonJS({
|
|
|
10194
10244
|
var throwError2 = (message, Ctor) => {
|
|
10195
10245
|
throw new Ctor(message);
|
|
10196
10246
|
};
|
|
10197
|
-
var checkPath = (
|
|
10198
|
-
if (!isString(
|
|
10247
|
+
var checkPath = (path22, originalPath, doThrow) => {
|
|
10248
|
+
if (!isString(path22)) {
|
|
10199
10249
|
return doThrow(
|
|
10200
10250
|
`path must be a string, but got \`${originalPath}\``,
|
|
10201
10251
|
TypeError
|
|
10202
10252
|
);
|
|
10203
10253
|
}
|
|
10204
|
-
if (!
|
|
10254
|
+
if (!path22) {
|
|
10205
10255
|
return doThrow(`path must not be empty`, TypeError);
|
|
10206
10256
|
}
|
|
10207
|
-
if (checkPath.isNotRelative(
|
|
10257
|
+
if (checkPath.isNotRelative(path22)) {
|
|
10208
10258
|
const r = "`path.relative()`d";
|
|
10209
10259
|
return doThrow(
|
|
10210
10260
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -10213,7 +10263,7 @@ var require_ignore = __commonJS({
|
|
|
10213
10263
|
}
|
|
10214
10264
|
return true;
|
|
10215
10265
|
};
|
|
10216
|
-
var isNotRelative = (
|
|
10266
|
+
var isNotRelative = (path22) => REGEX_TEST_INVALID_PATH.test(path22);
|
|
10217
10267
|
checkPath.isNotRelative = isNotRelative;
|
|
10218
10268
|
checkPath.convert = (p2) => p2;
|
|
10219
10269
|
var Ignore = class {
|
|
@@ -10243,19 +10293,19 @@ var require_ignore = __commonJS({
|
|
|
10243
10293
|
}
|
|
10244
10294
|
// @returns {TestResult}
|
|
10245
10295
|
_test(originalPath, cache2, checkUnignored, slices) {
|
|
10246
|
-
const
|
|
10296
|
+
const path22 = originalPath && checkPath.convert(originalPath);
|
|
10247
10297
|
checkPath(
|
|
10248
|
-
|
|
10298
|
+
path22,
|
|
10249
10299
|
originalPath,
|
|
10250
10300
|
this._strictPathCheck ? throwError2 : RETURN_FALSE
|
|
10251
10301
|
);
|
|
10252
|
-
return this._t(
|
|
10302
|
+
return this._t(path22, cache2, checkUnignored, slices);
|
|
10253
10303
|
}
|
|
10254
|
-
checkIgnore(
|
|
10255
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
10256
|
-
return this.test(
|
|
10304
|
+
checkIgnore(path22) {
|
|
10305
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path22)) {
|
|
10306
|
+
return this.test(path22);
|
|
10257
10307
|
}
|
|
10258
|
-
const slices =
|
|
10308
|
+
const slices = path22.split(SLASH).filter(Boolean);
|
|
10259
10309
|
slices.pop();
|
|
10260
10310
|
if (slices.length) {
|
|
10261
10311
|
const parent = this._t(
|
|
@@ -10268,18 +10318,18 @@ var require_ignore = __commonJS({
|
|
|
10268
10318
|
return parent;
|
|
10269
10319
|
}
|
|
10270
10320
|
}
|
|
10271
|
-
return this._rules.test(
|
|
10321
|
+
return this._rules.test(path22, false, MODE_CHECK_IGNORE);
|
|
10272
10322
|
}
|
|
10273
|
-
_t(
|
|
10274
|
-
if (
|
|
10275
|
-
return cache2[
|
|
10323
|
+
_t(path22, cache2, checkUnignored, slices) {
|
|
10324
|
+
if (path22 in cache2) {
|
|
10325
|
+
return cache2[path22];
|
|
10276
10326
|
}
|
|
10277
10327
|
if (!slices) {
|
|
10278
|
-
slices =
|
|
10328
|
+
slices = path22.split(SLASH).filter(Boolean);
|
|
10279
10329
|
}
|
|
10280
10330
|
slices.pop();
|
|
10281
10331
|
if (!slices.length) {
|
|
10282
|
-
return cache2[
|
|
10332
|
+
return cache2[path22] = this._rules.test(path22, checkUnignored, MODE_IGNORE);
|
|
10283
10333
|
}
|
|
10284
10334
|
const parent = this._t(
|
|
10285
10335
|
slices.join(SLASH) + SLASH,
|
|
@@ -10287,29 +10337,29 @@ var require_ignore = __commonJS({
|
|
|
10287
10337
|
checkUnignored,
|
|
10288
10338
|
slices
|
|
10289
10339
|
);
|
|
10290
|
-
return cache2[
|
|
10340
|
+
return cache2[path22] = parent.ignored ? parent : this._rules.test(path22, checkUnignored, MODE_IGNORE);
|
|
10291
10341
|
}
|
|
10292
|
-
ignores(
|
|
10293
|
-
return this._test(
|
|
10342
|
+
ignores(path22) {
|
|
10343
|
+
return this._test(path22, this._ignoreCache, false).ignored;
|
|
10294
10344
|
}
|
|
10295
10345
|
createFilter() {
|
|
10296
|
-
return (
|
|
10346
|
+
return (path22) => !this.ignores(path22);
|
|
10297
10347
|
}
|
|
10298
10348
|
filter(paths) {
|
|
10299
10349
|
return makeArray(paths).filter(this.createFilter());
|
|
10300
10350
|
}
|
|
10301
10351
|
// @returns {TestResult}
|
|
10302
|
-
test(
|
|
10303
|
-
return this._test(
|
|
10352
|
+
test(path22) {
|
|
10353
|
+
return this._test(path22, this._testCache, true);
|
|
10304
10354
|
}
|
|
10305
10355
|
};
|
|
10306
10356
|
var factory = (options) => new Ignore(options);
|
|
10307
|
-
var isPathValid = (
|
|
10357
|
+
var isPathValid = (path22) => checkPath(path22 && checkPath.convert(path22), path22, RETURN_FALSE);
|
|
10308
10358
|
var setupWindows = () => {
|
|
10309
10359
|
const makePosix = (str2) => /^\\\\\?\\/.test(str2) || /["<>|\u0000-\u001F]+/u.test(str2) ? str2 : str2.replace(/\\/g, "/");
|
|
10310
10360
|
checkPath.convert = makePosix;
|
|
10311
10361
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
10312
|
-
checkPath.isNotRelative = (
|
|
10362
|
+
checkPath.isNotRelative = (path22) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path22) || isNotRelative(path22);
|
|
10313
10363
|
};
|
|
10314
10364
|
if (
|
|
10315
10365
|
// Detect `process` so that it can run in browsers.
|
|
@@ -10589,30 +10639,52 @@ var init_file_discovery = __esm({
|
|
|
10589
10639
|
});
|
|
10590
10640
|
|
|
10591
10641
|
// packages/analyzer/dist/parser.js
|
|
10592
|
-
import Parser from "tree-sitter";
|
|
10593
|
-
import
|
|
10594
|
-
import
|
|
10595
|
-
import
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10642
|
+
import { Parser, Language } from "web-tree-sitter";
|
|
10643
|
+
import { createRequire as _createRequire } from "node:module";
|
|
10644
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10645
|
+
import path7 from "node:path";
|
|
10646
|
+
import fs8 from "node:fs";
|
|
10647
|
+
function resolveWasmPath(subpath) {
|
|
10648
|
+
const bundled = path7.join(BUNDLED_WASM_DIR, path7.basename(subpath));
|
|
10649
|
+
if (fs8.existsSync(bundled))
|
|
10650
|
+
return bundled;
|
|
10651
|
+
return _require.resolve(subpath);
|
|
10652
|
+
}
|
|
10653
|
+
function initParsers() {
|
|
10654
|
+
if (!initPromise) {
|
|
10655
|
+
initPromise = doInit().then(() => {
|
|
10656
|
+
initialized = true;
|
|
10657
|
+
});
|
|
10658
|
+
}
|
|
10659
|
+
return initPromise;
|
|
10660
|
+
}
|
|
10661
|
+
async function doInit() {
|
|
10662
|
+
await Parser.init({
|
|
10663
|
+
locateFile(file) {
|
|
10664
|
+
return resolveWasmPath(`web-tree-sitter/${file}`);
|
|
10665
|
+
}
|
|
10666
|
+
});
|
|
10667
|
+
await Promise.all(Object.keys(GRAMMAR_WASM).map(async (lang) => {
|
|
10668
|
+
const wasmPath = resolveWasmPath(GRAMMAR_WASM[lang]);
|
|
10669
|
+
const language = await Language.load(wasmPath);
|
|
10670
|
+
languageCache.set(lang, language);
|
|
10671
|
+
}));
|
|
10672
|
+
}
|
|
10673
|
+
function assertInitialized() {
|
|
10674
|
+
if (!initialized) {
|
|
10675
|
+
throw new Error("tree-sitter parsers are not loaded. Call `await initParsers()` from @truecourse/analyzer before using parseCode / parseFile / getParser. This is required once per process \u2014 the call is idempotent.");
|
|
10608
10676
|
}
|
|
10609
10677
|
}
|
|
10610
10678
|
function getParser(language) {
|
|
10679
|
+
if (!(language in GRAMMAR_WASM)) {
|
|
10680
|
+
throw new Error(`Unsupported language: ${language}`);
|
|
10681
|
+
}
|
|
10682
|
+
assertInitialized();
|
|
10611
10683
|
let parser4 = parserCache.get(language);
|
|
10612
10684
|
if (!parser4) {
|
|
10685
|
+
const lang = languageCache.get(language);
|
|
10613
10686
|
parser4 = new Parser();
|
|
10614
|
-
|
|
10615
|
-
parser4.setLanguage(tsLanguage);
|
|
10687
|
+
parser4.setLanguage(lang);
|
|
10616
10688
|
parserCache.set(language, parser4);
|
|
10617
10689
|
}
|
|
10618
10690
|
return parser4;
|
|
@@ -10632,11 +10704,22 @@ function parseFile(filePath, code, language) {
|
|
|
10632
10704
|
throw new Error(`Failed to parse file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
10633
10705
|
}
|
|
10634
10706
|
}
|
|
10635
|
-
var parserCache;
|
|
10707
|
+
var _require, BUNDLED_WASM_DIR, GRAMMAR_WASM, languageCache, parserCache, initPromise, initialized;
|
|
10636
10708
|
var init_parser = __esm({
|
|
10637
10709
|
"packages/analyzer/dist/parser.js"() {
|
|
10638
10710
|
"use strict";
|
|
10711
|
+
_require = _createRequire(import.meta.url);
|
|
10712
|
+
BUNDLED_WASM_DIR = path7.join(path7.dirname(fileURLToPath2(import.meta.url)), "wasm");
|
|
10713
|
+
GRAMMAR_WASM = {
|
|
10714
|
+
typescript: "tree-sitter-typescript/tree-sitter-typescript.wasm",
|
|
10715
|
+
tsx: "tree-sitter-typescript/tree-sitter-tsx.wasm",
|
|
10716
|
+
javascript: "tree-sitter-javascript/tree-sitter-javascript.wasm",
|
|
10717
|
+
python: "tree-sitter-python/tree-sitter-python.wasm"
|
|
10718
|
+
};
|
|
10719
|
+
languageCache = /* @__PURE__ */ new Map();
|
|
10639
10720
|
parserCache = /* @__PURE__ */ new Map();
|
|
10721
|
+
initPromise = null;
|
|
10722
|
+
initialized = false;
|
|
10640
10723
|
}
|
|
10641
10724
|
});
|
|
10642
10725
|
|
|
@@ -11575,18 +11658,18 @@ function extractRouterMount(callNode, filePath) {
|
|
|
11575
11658
|
const routerName = args[0]?.type === "identifier" ? args[0].text : null;
|
|
11576
11659
|
if (!routerName)
|
|
11577
11660
|
return null;
|
|
11578
|
-
let
|
|
11661
|
+
let path22 = "/";
|
|
11579
11662
|
for (const arg of args) {
|
|
11580
11663
|
if (arg.type === "keyword_argument") {
|
|
11581
11664
|
const key = arg.childForFieldName("name")?.text;
|
|
11582
11665
|
const value = arg.childForFieldName("value");
|
|
11583
11666
|
if ((key === "url_prefix" || key === "prefix") && value) {
|
|
11584
|
-
|
|
11667
|
+
path22 = value.text.replace(/^["']|["']$/g, "");
|
|
11585
11668
|
}
|
|
11586
11669
|
}
|
|
11587
11670
|
}
|
|
11588
11671
|
return {
|
|
11589
|
-
path:
|
|
11672
|
+
path: path22,
|
|
11590
11673
|
routerName,
|
|
11591
11674
|
location: {
|
|
11592
11675
|
filePath,
|
|
@@ -11610,11 +11693,11 @@ function extractChainedRoute(outerCall, filePath) {
|
|
|
11610
11693
|
const innerArgs = innerCall.childForFieldName("arguments");
|
|
11611
11694
|
if (!innerArgs)
|
|
11612
11695
|
return null;
|
|
11613
|
-
let
|
|
11696
|
+
let path22 = null;
|
|
11614
11697
|
let httpMethod = methodName === "route" ? "GET" : methodName.toUpperCase();
|
|
11615
11698
|
for (const arg of innerArgs.namedChildren) {
|
|
11616
|
-
if (!
|
|
11617
|
-
|
|
11699
|
+
if (!path22 && arg.type === "string") {
|
|
11700
|
+
path22 = arg.text.replace(/^["']|["']$/g, "");
|
|
11618
11701
|
}
|
|
11619
11702
|
if (arg.type === "keyword_argument") {
|
|
11620
11703
|
const key = arg.childForFieldName("name")?.text;
|
|
@@ -11626,14 +11709,14 @@ function extractChainedRoute(outerCall, filePath) {
|
|
|
11626
11709
|
}
|
|
11627
11710
|
}
|
|
11628
11711
|
}
|
|
11629
|
-
if (!
|
|
11712
|
+
if (!path22)
|
|
11630
11713
|
return null;
|
|
11631
11714
|
const outerArgs = outerCall.childForFieldName("arguments");
|
|
11632
11715
|
const handlerArg = outerArgs?.namedChildren[0];
|
|
11633
11716
|
const handlerName = handlerArg?.type === "identifier" ? handlerArg.text : "anonymous";
|
|
11634
11717
|
return {
|
|
11635
11718
|
httpMethod,
|
|
11636
|
-
path:
|
|
11719
|
+
path: path22,
|
|
11637
11720
|
handlerName,
|
|
11638
11721
|
location: {
|
|
11639
11722
|
filePath,
|
|
@@ -11709,8 +11792,8 @@ function extractRoute(methodName, argsNode, filePath, callNode) {
|
|
|
11709
11792
|
const firstArg = argsNode.namedChild(0);
|
|
11710
11793
|
if (!firstArg)
|
|
11711
11794
|
return null;
|
|
11712
|
-
const
|
|
11713
|
-
if (!
|
|
11795
|
+
const path22 = extractStringLiteral(firstArg);
|
|
11796
|
+
if (!path22)
|
|
11714
11797
|
return null;
|
|
11715
11798
|
const argCount = argsNode.namedChildCount;
|
|
11716
11799
|
if (argCount < 2)
|
|
@@ -11723,7 +11806,7 @@ function extractRoute(methodName, argsNode, filePath, callNode) {
|
|
|
11723
11806
|
return null;
|
|
11724
11807
|
return {
|
|
11725
11808
|
httpMethod: methodName.toUpperCase(),
|
|
11726
|
-
path:
|
|
11809
|
+
path: path22,
|
|
11727
11810
|
handlerName,
|
|
11728
11811
|
location: {
|
|
11729
11812
|
filePath,
|
|
@@ -11740,8 +11823,8 @@ function extractMount(argsNode, filePath, callNode) {
|
|
|
11740
11823
|
const firstArg = argsNode.namedChild(0);
|
|
11741
11824
|
if (!firstArg)
|
|
11742
11825
|
return null;
|
|
11743
|
-
const
|
|
11744
|
-
if (!
|
|
11826
|
+
const path22 = extractStringLiteral(firstArg);
|
|
11827
|
+
if (!path22)
|
|
11745
11828
|
return null;
|
|
11746
11829
|
const secondArg = argsNode.namedChild(1);
|
|
11747
11830
|
if (!secondArg)
|
|
@@ -11750,7 +11833,7 @@ function extractMount(argsNode, filePath, callNode) {
|
|
|
11750
11833
|
if (!routerName)
|
|
11751
11834
|
return null;
|
|
11752
11835
|
return {
|
|
11753
|
-
path:
|
|
11836
|
+
path: path22,
|
|
11754
11837
|
routerName,
|
|
11755
11838
|
location: {
|
|
11756
11839
|
filePath,
|
|
@@ -11871,7 +11954,7 @@ var init_common = __esm({
|
|
|
11871
11954
|
});
|
|
11872
11955
|
|
|
11873
11956
|
// packages/analyzer/dist/extractors/languages/typescript.js
|
|
11874
|
-
import
|
|
11957
|
+
import { Query as Query2 } from "web-tree-sitter";
|
|
11875
11958
|
function isNestedInFunction(node) {
|
|
11876
11959
|
let current = node.parent;
|
|
11877
11960
|
while (current) {
|
|
@@ -12167,8 +12250,10 @@ function extractTypeScriptFunctions(tree, filePath, language = "typescript") {
|
|
|
12167
12250
|
const seenLocations = /* @__PURE__ */ new Set();
|
|
12168
12251
|
const queryString = config2.functionQuery || config2.functionNodeTypes.map((type2) => `(${type2}) @function`).join("\n");
|
|
12169
12252
|
const parser4 = getParser(language);
|
|
12170
|
-
const tsLanguage = parser4.
|
|
12171
|
-
|
|
12253
|
+
const tsLanguage = parser4.language;
|
|
12254
|
+
if (!tsLanguage)
|
|
12255
|
+
throw new Error(`parser for '${language}' has no language set`);
|
|
12256
|
+
const query = new Query2(tsLanguage, queryString);
|
|
12172
12257
|
const captures = query.captures(tree.rootNode);
|
|
12173
12258
|
for (const capture of captures) {
|
|
12174
12259
|
const node = capture.node;
|
|
@@ -12206,8 +12291,10 @@ function extractTypeScriptClasses(tree, filePath, language = "typescript") {
|
|
|
12206
12291
|
const classes = [];
|
|
12207
12292
|
const queryString = config2.classQuery || config2.classNodeTypes.map((type2) => `(${type2}) @class`).join("\n");
|
|
12208
12293
|
const parser4 = getParser(language);
|
|
12209
|
-
const tsLanguage = parser4.
|
|
12210
|
-
|
|
12294
|
+
const tsLanguage = parser4.language;
|
|
12295
|
+
if (!tsLanguage)
|
|
12296
|
+
throw new Error(`parser for '${language}' has no language set`);
|
|
12297
|
+
const query = new Query2(tsLanguage, queryString);
|
|
12211
12298
|
const captures = query.captures(tree.rootNode);
|
|
12212
12299
|
for (const capture of captures) {
|
|
12213
12300
|
const node = capture.node;
|
|
@@ -12246,8 +12333,10 @@ function extractTypeScriptImports(tree, filePath, language = "typescript") {
|
|
|
12246
12333
|
const imports = [];
|
|
12247
12334
|
const queryString = config2.importQuery || config2.importNodeTypes.map((type2) => `(${type2}) @import`).join("\n");
|
|
12248
12335
|
const parser4 = getParser(language);
|
|
12249
|
-
const tsLanguage = parser4.
|
|
12250
|
-
|
|
12336
|
+
const tsLanguage = parser4.language;
|
|
12337
|
+
if (!tsLanguage)
|
|
12338
|
+
throw new Error(`parser for '${language}' has no language set`);
|
|
12339
|
+
const query = new Query2(tsLanguage, queryString);
|
|
12251
12340
|
const captures = query.captures(tree.rootNode);
|
|
12252
12341
|
for (const capture of captures) {
|
|
12253
12342
|
const node = capture.node;
|
|
@@ -12272,8 +12361,10 @@ function extractTypeScriptExports(tree, _filePath, language = "typescript") {
|
|
|
12272
12361
|
const exports = [];
|
|
12273
12362
|
const queryString = config2.exportQuery || config2.exportNodeTypes.map((type2) => `(${type2}) @export`).join("\n");
|
|
12274
12363
|
const parser4 = getParser(language);
|
|
12275
|
-
const tsLanguage = parser4.
|
|
12276
|
-
|
|
12364
|
+
const tsLanguage = parser4.language;
|
|
12365
|
+
if (!tsLanguage)
|
|
12366
|
+
throw new Error(`parser for '${language}' has no language set`);
|
|
12367
|
+
const query = new Query2(tsLanguage, queryString);
|
|
12277
12368
|
const captures = query.captures(tree.rootNode);
|
|
12278
12369
|
for (const capture of captures) {
|
|
12279
12370
|
const node = capture.node;
|
|
@@ -12312,7 +12403,7 @@ var init_typescript = __esm({
|
|
|
12312
12403
|
});
|
|
12313
12404
|
|
|
12314
12405
|
// packages/analyzer/dist/extractors/languages/javascript.js
|
|
12315
|
-
import
|
|
12406
|
+
import { Query as Query3 } from "web-tree-sitter";
|
|
12316
12407
|
function extractFunctionName2(node) {
|
|
12317
12408
|
const nameNode = node.childForFieldName("name");
|
|
12318
12409
|
if (nameNode?.text)
|
|
@@ -12394,8 +12485,10 @@ function extractJavaScriptFunctions(tree, filePath) {
|
|
|
12394
12485
|
const functions = [];
|
|
12395
12486
|
const queryString = config2.functionQuery || config2.functionNodeTypes.map((type2) => `(${type2}) @function`).join("\n");
|
|
12396
12487
|
const parser4 = getParser("javascript");
|
|
12397
|
-
const jsLanguage = parser4.
|
|
12398
|
-
|
|
12488
|
+
const jsLanguage = parser4.language;
|
|
12489
|
+
if (!jsLanguage)
|
|
12490
|
+
throw new Error("parser has no language set");
|
|
12491
|
+
const query = new Query3(jsLanguage, queryString);
|
|
12399
12492
|
const captures = query.captures(tree.rootNode);
|
|
12400
12493
|
for (const capture of captures) {
|
|
12401
12494
|
const node = capture.node;
|
|
@@ -12530,8 +12623,10 @@ function extractJavaScriptClasses(tree, filePath) {
|
|
|
12530
12623
|
const classes = [];
|
|
12531
12624
|
const queryString = config2.classQuery || config2.classNodeTypes.map((type2) => `(${type2}) @class`).join("\n");
|
|
12532
12625
|
const parser4 = getParser("javascript");
|
|
12533
|
-
const jsLanguage = parser4.
|
|
12534
|
-
|
|
12626
|
+
const jsLanguage = parser4.language;
|
|
12627
|
+
if (!jsLanguage)
|
|
12628
|
+
throw new Error("parser has no language set");
|
|
12629
|
+
const query = new Query3(jsLanguage, queryString);
|
|
12535
12630
|
const captures = query.captures(tree.rootNode);
|
|
12536
12631
|
for (const capture of captures) {
|
|
12537
12632
|
const node = capture.node;
|
|
@@ -12615,8 +12710,10 @@ function extractJavaScriptImports(tree, _filePath) {
|
|
|
12615
12710
|
const imports = [];
|
|
12616
12711
|
const queryString = config2.importQuery || config2.importNodeTypes.map((type2) => `(${type2}) @import`).join("\n");
|
|
12617
12712
|
const parser4 = getParser("javascript");
|
|
12618
|
-
const jsLanguage = parser4.
|
|
12619
|
-
|
|
12713
|
+
const jsLanguage = parser4.language;
|
|
12714
|
+
if (!jsLanguage)
|
|
12715
|
+
throw new Error("parser has no language set");
|
|
12716
|
+
const query = new Query3(jsLanguage, queryString);
|
|
12620
12717
|
const captures = query.captures(tree.rootNode);
|
|
12621
12718
|
for (const capture of captures) {
|
|
12622
12719
|
const node = capture.node;
|
|
@@ -12672,8 +12769,10 @@ function extractJavaScriptExports(tree, _filePath) {
|
|
|
12672
12769
|
const exports = [];
|
|
12673
12770
|
const queryString = config2.exportQuery || config2.exportNodeTypes.map((type2) => `(${type2}) @export`).join("\n");
|
|
12674
12771
|
const parser4 = getParser("javascript");
|
|
12675
|
-
const jsLanguage = parser4.
|
|
12676
|
-
|
|
12772
|
+
const jsLanguage = parser4.language;
|
|
12773
|
+
if (!jsLanguage)
|
|
12774
|
+
throw new Error("parser has no language set");
|
|
12775
|
+
const query = new Query3(jsLanguage, queryString);
|
|
12677
12776
|
const captures = query.captures(tree.rootNode);
|
|
12678
12777
|
for (const capture of captures) {
|
|
12679
12778
|
const node = capture.node;
|
|
@@ -15026,7 +15125,7 @@ var init_escape = __esm({
|
|
|
15026
15125
|
});
|
|
15027
15126
|
|
|
15028
15127
|
// node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js
|
|
15029
|
-
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform,
|
|
15128
|
+
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path8, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
|
|
15030
15129
|
var init_esm4 = __esm({
|
|
15031
15130
|
"node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js"() {
|
|
15032
15131
|
init_esm3();
|
|
@@ -15095,11 +15194,11 @@ var init_esm4 = __esm({
|
|
|
15095
15194
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
15096
15195
|
};
|
|
15097
15196
|
defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
15098
|
-
|
|
15197
|
+
path8 = {
|
|
15099
15198
|
win32: { sep: "\\" },
|
|
15100
15199
|
posix: { sep: "/" }
|
|
15101
15200
|
};
|
|
15102
|
-
sep = defaultPlatform === "win32" ?
|
|
15201
|
+
sep = defaultPlatform === "win32" ? path8.win32.sep : path8.posix.sep;
|
|
15103
15202
|
minimatch.sep = sep;
|
|
15104
15203
|
GLOBSTAR = Symbol("globstar **");
|
|
15105
15204
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -16274,9 +16373,9 @@ function detectDockerComposeServices(rootPath, allFiles) {
|
|
|
16274
16373
|
];
|
|
16275
16374
|
let composePath2 = null;
|
|
16276
16375
|
for (const file of composeFiles) {
|
|
16277
|
-
const
|
|
16278
|
-
if (existsSync7(
|
|
16279
|
-
composePath2 =
|
|
16376
|
+
const path22 = join6(rootPath, file);
|
|
16377
|
+
if (existsSync7(path22)) {
|
|
16378
|
+
composePath2 = path22;
|
|
16280
16379
|
break;
|
|
16281
16380
|
}
|
|
16282
16381
|
}
|
|
@@ -17145,7 +17244,7 @@ var init_database_detector = __esm({
|
|
|
17145
17244
|
});
|
|
17146
17245
|
|
|
17147
17246
|
// packages/analyzer/dist/module-extractor.js
|
|
17148
|
-
import
|
|
17247
|
+
import path9 from "path";
|
|
17149
17248
|
function extractModulesAndMethods(analyses, layerDetails, fileDependencies) {
|
|
17150
17249
|
const modules = [];
|
|
17151
17250
|
const methods = [];
|
|
@@ -17292,7 +17391,7 @@ function deriveModuleName(analysis) {
|
|
|
17292
17391
|
return localExports[0].name;
|
|
17293
17392
|
}
|
|
17294
17393
|
const baseName = fileBaseName(analysis.filePath);
|
|
17295
|
-
const strippedName = stripExtension(
|
|
17394
|
+
const strippedName = stripExtension(path9.basename(analysis.filePath));
|
|
17296
17395
|
if (INDEX_NAMES.has(strippedName)) {
|
|
17297
17396
|
return baseName;
|
|
17298
17397
|
}
|
|
@@ -17309,10 +17408,10 @@ function deriveModuleName(analysis) {
|
|
|
17309
17408
|
return baseName;
|
|
17310
17409
|
}
|
|
17311
17410
|
function deriveNextjsRouteName(filePath) {
|
|
17312
|
-
const base =
|
|
17411
|
+
const base = path9.basename(filePath).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
17313
17412
|
if (base !== "route" && base !== "page")
|
|
17314
17413
|
return null;
|
|
17315
|
-
const parts = filePath.split(
|
|
17414
|
+
const parts = filePath.split(path9.sep);
|
|
17316
17415
|
const appIdx = parts.lastIndexOf("app");
|
|
17317
17416
|
if (appIdx === -1)
|
|
17318
17417
|
return null;
|
|
@@ -17330,9 +17429,9 @@ function stripExtension(filename) {
|
|
|
17330
17429
|
return filename;
|
|
17331
17430
|
}
|
|
17332
17431
|
function fileBaseName(filePath) {
|
|
17333
|
-
const name = stripExtension(
|
|
17432
|
+
const name = stripExtension(path9.basename(filePath));
|
|
17334
17433
|
if (INDEX_NAMES.has(name)) {
|
|
17335
|
-
return
|
|
17434
|
+
return path9.basename(path9.dirname(filePath));
|
|
17336
17435
|
}
|
|
17337
17436
|
return name;
|
|
17338
17437
|
}
|
|
@@ -18029,7 +18128,11 @@ var init_entities = __esm({
|
|
|
18029
18128
|
init_patterns();
|
|
18030
18129
|
init_parser();
|
|
18031
18130
|
TypeScriptEntityDetector = class {
|
|
18032
|
-
|
|
18131
|
+
// Lazy: do not call getParser() at construction time — callers may
|
|
18132
|
+
// instantiate this before initParsers() has completed.
|
|
18133
|
+
get parser() {
|
|
18134
|
+
return getParser("typescript");
|
|
18135
|
+
}
|
|
18033
18136
|
shouldScanFile(filePath) {
|
|
18034
18137
|
if (!/\.(ts|tsx|js|jsx)$/.test(filePath)) {
|
|
18035
18138
|
return false;
|
|
@@ -18046,6 +18149,8 @@ var init_entities = __esm({
|
|
|
18046
18149
|
detectEntities(sourceCode, filePath, service) {
|
|
18047
18150
|
const tree = this.parser.parse(sourceCode);
|
|
18048
18151
|
const entities = [];
|
|
18152
|
+
if (!tree)
|
|
18153
|
+
return entities;
|
|
18049
18154
|
const classNodes = this.findClassDeclarations(tree.rootNode);
|
|
18050
18155
|
for (const classNode of classNodes) {
|
|
18051
18156
|
const entity = this.detectEntityFromClass(classNode, sourceCode, filePath, service);
|
|
@@ -18333,7 +18438,7 @@ var init_entities = __esm({
|
|
|
18333
18438
|
import { spawn as spawn2 } from "child_process";
|
|
18334
18439
|
import { readFileSync as readFileSync7 } from "fs";
|
|
18335
18440
|
import { resolve as resolve6 } from "path";
|
|
18336
|
-
import { pathToFileURL, fileURLToPath as
|
|
18441
|
+
import { pathToFileURL, fileURLToPath as fileURLToPath3 } from "url";
|
|
18337
18442
|
function encodeMessage(msg) {
|
|
18338
18443
|
const body = JSON.stringify(msg);
|
|
18339
18444
|
const header = `Content-Length: ${Buffer.byteLength(body)}\r
|
|
@@ -18478,7 +18583,7 @@ var init_lsp_client = __esm({
|
|
|
18478
18583
|
if (!uri)
|
|
18479
18584
|
return null;
|
|
18480
18585
|
try {
|
|
18481
|
-
return
|
|
18586
|
+
return fileURLToPath3(uri);
|
|
18482
18587
|
} catch {
|
|
18483
18588
|
return null;
|
|
18484
18589
|
}
|
|
@@ -18646,7 +18751,7 @@ var init_lsp_client = __esm({
|
|
|
18646
18751
|
// packages/analyzer/dist/lsp-servers/pyright.js
|
|
18647
18752
|
import { resolve as resolve7, dirname as dirname6 } from "path";
|
|
18648
18753
|
import { existsSync as existsSync9 } from "fs";
|
|
18649
|
-
import { fileURLToPath as
|
|
18754
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
18650
18755
|
function findPyrightBinary() {
|
|
18651
18756
|
let dir = resolve7(__dirname2, "..");
|
|
18652
18757
|
for (let i = 0; i < 10; i++) {
|
|
@@ -18678,7 +18783,7 @@ var __filename, __dirname2;
|
|
|
18678
18783
|
var init_pyright = __esm({
|
|
18679
18784
|
"packages/analyzer/dist/lsp-servers/pyright.js"() {
|
|
18680
18785
|
"use strict";
|
|
18681
|
-
__filename =
|
|
18786
|
+
__filename = fileURLToPath4(import.meta.url);
|
|
18682
18787
|
__dirname2 = dirname6(__filename);
|
|
18683
18788
|
}
|
|
18684
18789
|
});
|
|
@@ -19305,9 +19410,9 @@ function findSimpleCycles(component, componentSet, adjacency) {
|
|
|
19305
19410
|
for (const next of successors) {
|
|
19306
19411
|
if (!componentSet.has(next))
|
|
19307
19412
|
continue;
|
|
19308
|
-
if (next === start &&
|
|
19309
|
-
const chain = [...
|
|
19310
|
-
const minNode =
|
|
19413
|
+
if (next === start && path22.length >= 2) {
|
|
19414
|
+
const chain = [...path22, start];
|
|
19415
|
+
const minNode = path22.reduce((a, b) => a < b ? a : b);
|
|
19311
19416
|
if (minNode === start) {
|
|
19312
19417
|
cycles.push(chain);
|
|
19313
19418
|
}
|
|
@@ -19315,15 +19420,15 @@ function findSimpleCycles(component, componentSet, adjacency) {
|
|
|
19315
19420
|
}
|
|
19316
19421
|
if (!visited.has(next) && !seen.has(next)) {
|
|
19317
19422
|
visited.add(next);
|
|
19318
|
-
|
|
19423
|
+
path22.push(next);
|
|
19319
19424
|
dfs2(next);
|
|
19320
|
-
|
|
19425
|
+
path22.pop();
|
|
19321
19426
|
visited.delete(next);
|
|
19322
19427
|
}
|
|
19323
19428
|
}
|
|
19324
19429
|
};
|
|
19325
19430
|
var dfs = dfs2;
|
|
19326
|
-
const
|
|
19431
|
+
const path22 = [start];
|
|
19327
19432
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
19328
19433
|
dfs2(start);
|
|
19329
19434
|
seen.add(start);
|
|
@@ -19345,7 +19450,7 @@ function findShortestCycle(start, componentSet, adjacency) {
|
|
|
19345
19450
|
visited.add(next);
|
|
19346
19451
|
}
|
|
19347
19452
|
while (queue.length > 0) {
|
|
19348
|
-
const { node, path:
|
|
19453
|
+
const { node, path: path22 } = queue.shift();
|
|
19349
19454
|
const nexts = adjacency.get(node);
|
|
19350
19455
|
if (!nexts)
|
|
19351
19456
|
continue;
|
|
@@ -19353,11 +19458,11 @@ function findShortestCycle(start, componentSet, adjacency) {
|
|
|
19353
19458
|
if (!componentSet.has(next))
|
|
19354
19459
|
continue;
|
|
19355
19460
|
if (next === start) {
|
|
19356
|
-
return [...
|
|
19461
|
+
return [...path22, start];
|
|
19357
19462
|
}
|
|
19358
19463
|
if (!visited.has(next)) {
|
|
19359
19464
|
visited.add(next);
|
|
19360
|
-
queue.push({ node: next, path: [...
|
|
19465
|
+
queue.push({ node: next, path: [...path22, next] });
|
|
19361
19466
|
}
|
|
19362
19467
|
}
|
|
19363
19468
|
}
|
|
@@ -38153,7 +38258,8 @@ var init_python_helpers = __esm({
|
|
|
38153
38258
|
// packages/analyzer/dist/rules/_shared/python-framework-detection.js
|
|
38154
38259
|
function getPythonImportSources(node) {
|
|
38155
38260
|
const program3 = getPythonModuleNode(node);
|
|
38156
|
-
const
|
|
38261
|
+
const tree = program3.tree;
|
|
38262
|
+
const cached = importSourceCache.get(tree);
|
|
38157
38263
|
if (cached)
|
|
38158
38264
|
return cached;
|
|
38159
38265
|
const sources = /* @__PURE__ */ new Set();
|
|
@@ -38192,7 +38298,7 @@ function getPythonImportSources(node) {
|
|
|
38192
38298
|
walk(child);
|
|
38193
38299
|
}
|
|
38194
38300
|
walk(program3);
|
|
38195
|
-
importSourceCache.set(
|
|
38301
|
+
importSourceCache.set(tree, sources);
|
|
38196
38302
|
return sources;
|
|
38197
38303
|
}
|
|
38198
38304
|
function detectPythonOrm(node) {
|
|
@@ -44095,10 +44201,10 @@ var init_secret_rules = __esm({
|
|
|
44095
44201
|
});
|
|
44096
44202
|
|
|
44097
44203
|
// packages/analyzer/dist/rules/security/secret-scanner.js
|
|
44098
|
-
import
|
|
44204
|
+
import path10 from "node:path";
|
|
44099
44205
|
function isSensitiveFile(filePath) {
|
|
44100
|
-
const basename2 =
|
|
44101
|
-
const ext2 =
|
|
44206
|
+
const basename2 = path10.basename(filePath);
|
|
44207
|
+
const ext2 = path10.extname(filePath);
|
|
44102
44208
|
if (basename2.startsWith(".env")) {
|
|
44103
44209
|
const envVariant = basename2;
|
|
44104
44210
|
if (SENSITIVE_FILE_EXTENSIONS.has(envVariant)) {
|
|
@@ -46266,7 +46372,8 @@ var init_fallthrough_case = __esm({
|
|
|
46266
46372
|
const cases = body.namedChildren.filter((c2) => c2.type === "switch_case");
|
|
46267
46373
|
for (let i = 0; i < cases.length - 1; i++) {
|
|
46268
46374
|
const caseNode = cases[i];
|
|
46269
|
-
const
|
|
46375
|
+
const valueNode = caseNode.childForFieldName("value");
|
|
46376
|
+
const statements = caseNode.namedChildren.filter((c2) => c2.type !== "comment" && (!valueNode || c2.id !== valueNode.id));
|
|
46270
46377
|
if (statements.length === 0)
|
|
46271
46378
|
continue;
|
|
46272
46379
|
const last2 = statements[statements.length - 1];
|
|
@@ -67769,7 +67876,7 @@ var init_case_without_break = __esm({
|
|
|
67769
67876
|
nodeTypes: ["switch_case"],
|
|
67770
67877
|
visit(node, filePath, sourceCode) {
|
|
67771
67878
|
const valueNode = node.childForFieldName("value");
|
|
67772
|
-
const stmts = node.namedChildren.filter((c2) => c2 !== valueNode);
|
|
67879
|
+
const stmts = node.namedChildren.filter((c2) => !valueNode || c2.id !== valueNode.id);
|
|
67773
67880
|
if (stmts.length === 0)
|
|
67774
67881
|
return null;
|
|
67775
67882
|
let last2 = stmts[stmts.length - 1];
|
|
@@ -90697,8 +90804,8 @@ var init_missing_error_status_code = __esm({
|
|
|
90697
90804
|
});
|
|
90698
90805
|
|
|
90699
90806
|
// packages/analyzer/dist/rules/architecture/visitors/javascript/route-without-auth-middleware.js
|
|
90700
|
-
function isPublicPath(
|
|
90701
|
-
return PUBLIC_PATH_PATTERNS.some((re2) => re2.test(
|
|
90807
|
+
function isPublicPath(path22) {
|
|
90808
|
+
return PUBLIC_PATH_PATTERNS.some((re2) => re2.test(path22));
|
|
90702
90809
|
}
|
|
90703
90810
|
function extractMiddlewareNames(callNode) {
|
|
90704
90811
|
const args = callNode.childForFieldName("arguments");
|
|
@@ -90824,8 +90931,8 @@ var init_route_without_auth_middleware = __esm({
|
|
|
90824
90931
|
return null;
|
|
90825
90932
|
const firstArg = args.namedChildren[0];
|
|
90826
90933
|
if (firstArg?.type === "string") {
|
|
90827
|
-
const
|
|
90828
|
-
if (isPublicPath(
|
|
90934
|
+
const path22 = firstArg.text.replace(/^['"`]|['"`]$/g, "");
|
|
90935
|
+
if (isPublicPath(path22))
|
|
90829
90936
|
return null;
|
|
90830
90937
|
}
|
|
90831
90938
|
const middleware = extractMiddlewareNames(node);
|
|
@@ -91222,7 +91329,7 @@ var init_declarations_in_global_scope2 = __esm({
|
|
|
91222
91329
|
|
|
91223
91330
|
// packages/analyzer/dist/rules/architecture/visitors/python/unused-import.js
|
|
91224
91331
|
function hasIdentifierOutside(root, name, excludeNode) {
|
|
91225
|
-
if (root === excludeNode)
|
|
91332
|
+
if (root.id === excludeNode.id)
|
|
91226
91333
|
return false;
|
|
91227
91334
|
if (root.type === "identifier" && root.text === name)
|
|
91228
91335
|
return true;
|
|
@@ -93598,6 +93705,7 @@ __export(dist_exports2, {
|
|
|
93598
93705
|
hasLspServer: () => hasLspServer,
|
|
93599
93706
|
hasSchemaAwareVisitors: () => hasSchemaAwareVisitors,
|
|
93600
93707
|
hasTypeAwareVisitors: () => hasTypeAwareVisitors,
|
|
93708
|
+
initParsers: () => initParsers,
|
|
93601
93709
|
isBootstrapEntry: () => isBootstrapEntry,
|
|
93602
93710
|
makeViolation: () => makeViolation,
|
|
93603
93711
|
matchesPattern: () => matchesPattern,
|
|
@@ -93617,6 +93725,7 @@ __export(dist_exports2, {
|
|
|
93617
93725
|
walkAstWithVisitors: () => walkAstWithVisitors
|
|
93618
93726
|
});
|
|
93619
93727
|
async function analyzeRepository(rootPath) {
|
|
93728
|
+
await initParsers();
|
|
93620
93729
|
const files = discoverFiles(rootPath);
|
|
93621
93730
|
if (files.length === 0) {
|
|
93622
93731
|
return {
|
|
@@ -93660,6 +93769,7 @@ var init_dist6 = __esm({
|
|
|
93660
93769
|
init_file_analyzer();
|
|
93661
93770
|
init_dependency_graph();
|
|
93662
93771
|
init_split_analyzer();
|
|
93772
|
+
init_parser();
|
|
93663
93773
|
init_file_analyzer();
|
|
93664
93774
|
init_dependency_graph();
|
|
93665
93775
|
init_service_detector();
|
|
@@ -93699,7 +93809,7 @@ var init_dist6 = __esm({
|
|
|
93699
93809
|
});
|
|
93700
93810
|
|
|
93701
93811
|
// apps/server/dist/services/analyzer.service.js
|
|
93702
|
-
import
|
|
93812
|
+
import path11 from "node:path";
|
|
93703
93813
|
function runDeterministicModuleChecks(result, enabledDeterministic) {
|
|
93704
93814
|
if (!result.modules || !result.methods)
|
|
93705
93815
|
return [];
|
|
@@ -93742,7 +93852,7 @@ async function runAnalysis(repoPath, _branch, onProgress, options) {
|
|
|
93742
93852
|
const statusResult = await git.status();
|
|
93743
93853
|
hasChanges = !statusResult.isClean();
|
|
93744
93854
|
const gitRoot = (await git.revparse(["--show-toplevel"])).trim();
|
|
93745
|
-
isSubdirectory =
|
|
93855
|
+
isSubdirectory = path11.resolve(repoPath) !== path11.resolve(gitRoot);
|
|
93746
93856
|
}
|
|
93747
93857
|
if (hasChanges && !options?.skipStash && !isSubdirectory && git) {
|
|
93748
93858
|
onProgress({ step: "stash", percent: 2, detail: "Stashing pending changes to analyze committed state..." });
|
|
@@ -93756,6 +93866,7 @@ async function runAnalysis(repoPath, _branch, onProgress, options) {
|
|
|
93756
93866
|
try {
|
|
93757
93867
|
onProgress({ step: "discover", percent: 10, detail: "Discovering files..." });
|
|
93758
93868
|
const analyzer = await Promise.resolve().then(() => (init_dist6(), dist_exports2));
|
|
93869
|
+
await analyzer.initParsers();
|
|
93759
93870
|
const files = await analyzer.discoverFiles(repoPath);
|
|
93760
93871
|
onProgress({
|
|
93761
93872
|
step: "discover",
|
|
@@ -103367,11 +103478,11 @@ var require_mime_types = __commonJS({
|
|
|
103367
103478
|
}
|
|
103368
103479
|
return exts[0];
|
|
103369
103480
|
}
|
|
103370
|
-
function lookup(
|
|
103371
|
-
if (!
|
|
103481
|
+
function lookup(path22) {
|
|
103482
|
+
if (!path22 || typeof path22 !== "string") {
|
|
103372
103483
|
return false;
|
|
103373
103484
|
}
|
|
103374
|
-
var extension2 = extname("x." +
|
|
103485
|
+
var extension2 = extname("x." + path22).toLowerCase().substr(1);
|
|
103375
103486
|
if (!extension2) {
|
|
103376
103487
|
return false;
|
|
103377
103488
|
}
|
|
@@ -109586,11 +109697,11 @@ var require_server = __commonJS({
|
|
|
109586
109697
|
* @protected
|
|
109587
109698
|
*/
|
|
109588
109699
|
_computePath(options) {
|
|
109589
|
-
let
|
|
109700
|
+
let path22 = (options.path || "/engine.io").replace(/\/$/, "");
|
|
109590
109701
|
if (options.addTrailingSlash !== false) {
|
|
109591
|
-
|
|
109702
|
+
path22 += "/";
|
|
109592
109703
|
}
|
|
109593
|
-
return
|
|
109704
|
+
return path22;
|
|
109594
109705
|
}
|
|
109595
109706
|
/**
|
|
109596
109707
|
* Returns a list of available transports for upgrade given a certain transport.
|
|
@@ -110089,10 +110200,10 @@ var require_server = __commonJS({
|
|
|
110089
110200
|
* @param {Object} options
|
|
110090
110201
|
*/
|
|
110091
110202
|
attach(server, options = {}) {
|
|
110092
|
-
const
|
|
110203
|
+
const path22 = this._computePath(options);
|
|
110093
110204
|
const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1e3;
|
|
110094
110205
|
function check(req) {
|
|
110095
|
-
return
|
|
110206
|
+
return path22 === req.url.slice(0, path22.length);
|
|
110096
110207
|
}
|
|
110097
110208
|
const listeners = server.listeners("request").slice(0);
|
|
110098
110209
|
server.removeAllListeners("request");
|
|
@@ -110100,7 +110211,7 @@ var require_server = __commonJS({
|
|
|
110100
110211
|
server.on("listening", this.init.bind(this));
|
|
110101
110212
|
server.on("request", (req, res) => {
|
|
110102
110213
|
if (check(req)) {
|
|
110103
|
-
debug2('intercepting request for path "%s"',
|
|
110214
|
+
debug2('intercepting request for path "%s"', path22);
|
|
110104
110215
|
this.handleRequest(req, res);
|
|
110105
110216
|
} else {
|
|
110106
110217
|
let i = 0;
|
|
@@ -110939,8 +111050,8 @@ var require_userver = __commonJS({
|
|
|
110939
111050
|
* @param options
|
|
110940
111051
|
*/
|
|
110941
111052
|
attach(app, options = {}) {
|
|
110942
|
-
const
|
|
110943
|
-
app.any(
|
|
111053
|
+
const path22 = this._computePath(options);
|
|
111054
|
+
app.any(path22, this.handleRequest.bind(this)).ws(path22, {
|
|
110944
111055
|
compression: options.compression,
|
|
110945
111056
|
idleTimeout: options.idleTimeout,
|
|
110946
111057
|
maxBackpressure: options.maxBackpressure,
|
|
@@ -115365,7 +115476,7 @@ var require_dist4 = __commonJS({
|
|
|
115365
115476
|
var zlib_1 = __require("zlib");
|
|
115366
115477
|
var accepts = require_accepts();
|
|
115367
115478
|
var stream_1 = __require("stream");
|
|
115368
|
-
var
|
|
115479
|
+
var path22 = __require("path");
|
|
115369
115480
|
var engine_io_1 = require_engine_io();
|
|
115370
115481
|
var client_1 = require_client();
|
|
115371
115482
|
var events_1 = __require("events");
|
|
@@ -115560,7 +115671,7 @@ var require_dist4 = __commonJS({
|
|
|
115560
115671
|
res.writeHeader("cache-control", "public, max-age=0");
|
|
115561
115672
|
res.writeHeader("content-type", "application/" + (isMap ? "json" : "javascript") + "; charset=utf-8");
|
|
115562
115673
|
res.writeHeader("etag", expectedEtag);
|
|
115563
|
-
const filepath =
|
|
115674
|
+
const filepath = path22.join(__dirname, "../client-dist/", filename);
|
|
115564
115675
|
(0, uws_1.serveFile)(res, filepath);
|
|
115565
115676
|
});
|
|
115566
115677
|
}
|
|
@@ -115642,7 +115753,7 @@ var require_dist4 = __commonJS({
|
|
|
115642
115753
|
* @private
|
|
115643
115754
|
*/
|
|
115644
115755
|
static sendFile(filename, req, res) {
|
|
115645
|
-
const readStream = (0, fs_1.createReadStream)(
|
|
115756
|
+
const readStream = (0, fs_1.createReadStream)(path22.join(__dirname, "../client-dist/", filename));
|
|
115646
115757
|
const encoding = accepts(req).encodings(["br", "gzip", "deflate"]);
|
|
115647
115758
|
const onError2 = (err) => {
|
|
115648
115759
|
if (err) {
|
|
@@ -116506,8 +116617,8 @@ var init_parseUtil = __esm({
|
|
|
116506
116617
|
init_errors();
|
|
116507
116618
|
init_en();
|
|
116508
116619
|
makeIssue = (params) => {
|
|
116509
|
-
const { data, path:
|
|
116510
|
-
const fullPath = [...
|
|
116620
|
+
const { data, path: path22, errorMaps, issueData } = params;
|
|
116621
|
+
const fullPath = [...path22, ...issueData.path || []];
|
|
116511
116622
|
const fullIssue = {
|
|
116512
116623
|
...issueData,
|
|
116513
116624
|
path: fullPath
|
|
@@ -116815,11 +116926,11 @@ var init_types2 = __esm({
|
|
|
116815
116926
|
init_parseUtil();
|
|
116816
116927
|
init_util3();
|
|
116817
116928
|
ParseInputLazyPath = class {
|
|
116818
|
-
constructor(parent, value,
|
|
116929
|
+
constructor(parent, value, path22, key) {
|
|
116819
116930
|
this._cachedPath = [];
|
|
116820
116931
|
this.parent = parent;
|
|
116821
116932
|
this.data = value;
|
|
116822
|
-
this._path =
|
|
116933
|
+
this._path = path22;
|
|
116823
116934
|
this._key = key;
|
|
116824
116935
|
}
|
|
116825
116936
|
get path() {
|
|
@@ -122572,8 +122683,8 @@ var require_package2 = __commonJS({
|
|
|
122572
122683
|
// node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/lib/main.js
|
|
122573
122684
|
var require_main = __commonJS({
|
|
122574
122685
|
"node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/lib/main.js"(exports, module) {
|
|
122575
|
-
var
|
|
122576
|
-
var
|
|
122686
|
+
var fs17 = __require("fs");
|
|
122687
|
+
var path22 = __require("path");
|
|
122577
122688
|
var os8 = __require("os");
|
|
122578
122689
|
var crypto2 = __require("crypto");
|
|
122579
122690
|
var packageJson = require_package2();
|
|
@@ -122681,7 +122792,7 @@ var require_main = __commonJS({
|
|
|
122681
122792
|
if (options && options.path && options.path.length > 0) {
|
|
122682
122793
|
if (Array.isArray(options.path)) {
|
|
122683
122794
|
for (const filepath of options.path) {
|
|
122684
|
-
if (
|
|
122795
|
+
if (fs17.existsSync(filepath)) {
|
|
122685
122796
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
122686
122797
|
}
|
|
122687
122798
|
}
|
|
@@ -122689,15 +122800,15 @@ var require_main = __commonJS({
|
|
|
122689
122800
|
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
122690
122801
|
}
|
|
122691
122802
|
} else {
|
|
122692
|
-
possibleVaultPath =
|
|
122803
|
+
possibleVaultPath = path22.resolve(process.cwd(), ".env.vault");
|
|
122693
122804
|
}
|
|
122694
|
-
if (
|
|
122805
|
+
if (fs17.existsSync(possibleVaultPath)) {
|
|
122695
122806
|
return possibleVaultPath;
|
|
122696
122807
|
}
|
|
122697
122808
|
return null;
|
|
122698
122809
|
}
|
|
122699
122810
|
function _resolveHome(envPath) {
|
|
122700
|
-
return envPath[0] === "~" ?
|
|
122811
|
+
return envPath[0] === "~" ? path22.join(os8.homedir(), envPath.slice(1)) : envPath;
|
|
122701
122812
|
}
|
|
122702
122813
|
function _configVault(options) {
|
|
122703
122814
|
const debug2 = Boolean(options && options.debug);
|
|
@@ -122714,7 +122825,7 @@ var require_main = __commonJS({
|
|
|
122714
122825
|
return { parsed };
|
|
122715
122826
|
}
|
|
122716
122827
|
function configDotenv(options) {
|
|
122717
|
-
const dotenvPath =
|
|
122828
|
+
const dotenvPath = path22.resolve(process.cwd(), ".env");
|
|
122718
122829
|
let encoding = "utf8";
|
|
122719
122830
|
const debug2 = Boolean(options && options.debug);
|
|
122720
122831
|
const quiet = options && "quiet" in options ? options.quiet : true;
|
|
@@ -122738,13 +122849,13 @@ var require_main = __commonJS({
|
|
|
122738
122849
|
}
|
|
122739
122850
|
let lastError;
|
|
122740
122851
|
const parsedAll = {};
|
|
122741
|
-
for (const
|
|
122852
|
+
for (const path23 of optionPaths) {
|
|
122742
122853
|
try {
|
|
122743
|
-
const parsed = DotenvModule.parse(
|
|
122854
|
+
const parsed = DotenvModule.parse(fs17.readFileSync(path23, { encoding }));
|
|
122744
122855
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
122745
122856
|
} catch (e) {
|
|
122746
122857
|
if (debug2) {
|
|
122747
|
-
_debug(`Failed to load ${
|
|
122858
|
+
_debug(`Failed to load ${path23} ${e.message}`);
|
|
122748
122859
|
}
|
|
122749
122860
|
lastError = e;
|
|
122750
122861
|
}
|
|
@@ -122759,7 +122870,7 @@ var require_main = __commonJS({
|
|
|
122759
122870
|
const shortPaths = [];
|
|
122760
122871
|
for (const filePath of optionPaths) {
|
|
122761
122872
|
try {
|
|
122762
|
-
const relative2 =
|
|
122873
|
+
const relative2 = path22.relative(process.cwd(), filePath);
|
|
122763
122874
|
shortPaths.push(relative2);
|
|
122764
122875
|
} catch (e) {
|
|
122765
122876
|
if (debug2) {
|
|
@@ -122860,14 +122971,14 @@ var require_main = __commonJS({
|
|
|
122860
122971
|
});
|
|
122861
122972
|
|
|
122862
122973
|
// apps/server/dist/config/env.js
|
|
122863
|
-
import
|
|
122974
|
+
import path12 from "node:path";
|
|
122864
122975
|
import os3 from "node:os";
|
|
122865
122976
|
var import_dotenv;
|
|
122866
122977
|
var init_env = __esm({
|
|
122867
122978
|
"apps/server/dist/config/env.js"() {
|
|
122868
122979
|
"use strict";
|
|
122869
122980
|
import_dotenv = __toESM(require_main(), 1);
|
|
122870
|
-
import_dotenv.default.config({ path:
|
|
122981
|
+
import_dotenv.default.config({ path: path12.join(os3.homedir(), ".truecourse", ".env") });
|
|
122871
122982
|
import_dotenv.default.config({ path: "../../.env" });
|
|
122872
122983
|
}
|
|
122873
122984
|
});
|
|
@@ -123582,8 +123693,8 @@ var init_cli_provider = __esm({
|
|
|
123582
123693
|
this._repoId = repoId;
|
|
123583
123694
|
}
|
|
123584
123695
|
/** Set target repo path — used as cwd when spawning CLI so Read tool accesses the right files. */
|
|
123585
|
-
setRepoPath(
|
|
123586
|
-
this._repoPath =
|
|
123696
|
+
setRepoPath(path22) {
|
|
123697
|
+
this._repoPath = path22;
|
|
123587
123698
|
}
|
|
123588
123699
|
flushUsage() {
|
|
123589
123700
|
if (this._usageRecords.length === 0)
|
|
@@ -125005,8 +125116,8 @@ var init_violation_lifecycle_service = __esm({
|
|
|
125005
125116
|
|
|
125006
125117
|
// apps/server/dist/services/violation-pipeline.service.js
|
|
125007
125118
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
125008
|
-
import
|
|
125009
|
-
import
|
|
125119
|
+
import fs9 from "node:fs";
|
|
125120
|
+
import path13 from "node:path";
|
|
125010
125121
|
function throwIfAborted(signal) {
|
|
125011
125122
|
if (signal?.aborted)
|
|
125012
125123
|
throw new DOMException("Analysis cancelled", "AbortError");
|
|
@@ -125038,6 +125149,7 @@ function compareDeterministicViolations(current, previous) {
|
|
|
125038
125149
|
return { newDetections, unchangedDetections, resolvedDetections };
|
|
125039
125150
|
}
|
|
125040
125151
|
async function runViolationPipeline(input) {
|
|
125152
|
+
await initParsers();
|
|
125041
125153
|
const { repoPath, analysisId, now, result, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap, previousActiveViolations, changedFileSet, onProgress, tracker, provider: externalProvider, enabledCategories, enableLlmRules, signal } = input;
|
|
125042
125154
|
const added = [];
|
|
125043
125155
|
const unchanged = [];
|
|
@@ -125066,29 +125178,35 @@ async function runViolationPipeline(input) {
|
|
|
125066
125178
|
const enabledLlmCodeRules = enableLlmRules !== false ? allRules.filter((r) => (r.domain ? codeDomains.has(r.domain) : r.category === "code") && r.type === "llm" && r.prompt) : [];
|
|
125067
125179
|
const archLlmRules = allRules.filter((r) => r.type === "llm" && r.prompt && r.domain === "architecture").map((r) => ({ key: r.key, name: r.name, severity: r.severity, prompt: r.prompt, category: r.category }));
|
|
125068
125180
|
const dbSchemaLlmRules = allRules.filter((r) => r.type === "llm" && r.prompt && r.domain === "database" && r.category === "database").map((r) => ({ key: r.key, name: r.name, severity: r.severity, prompt: r.prompt, category: r.category }));
|
|
125069
|
-
const filesToScan = changedFileSet ? [...changedFileSet].map((relPath) => ({ filePath: relPath, resolve: true })) : (result.fileAnalyses || []).map((fa) => ({ filePath: fa.filePath, resolve: !
|
|
125181
|
+
const filesToScan = changedFileSet ? [...changedFileSet].map((relPath) => ({ filePath: relPath, resolve: true })) : (result.fileAnalyses || []).map((fa) => ({ filePath: fa.filePath, resolve: !path13.isAbsolute(fa.filePath) }));
|
|
125070
125182
|
const hasLlm = enabledLlm.length > 0;
|
|
125071
125183
|
if (hasLlm)
|
|
125072
125184
|
tracker?.start("scan", "Reading files...");
|
|
125073
125185
|
const fileContents = /* @__PURE__ */ new Map();
|
|
125186
|
+
const totalToScan = filesToScan.length;
|
|
125187
|
+
let scanned = 0;
|
|
125074
125188
|
for (const { filePath, resolve: resolve8 } of filesToScan) {
|
|
125075
125189
|
try {
|
|
125076
125190
|
const lang = detectLanguage(filePath);
|
|
125077
125191
|
if (!lang)
|
|
125078
125192
|
continue;
|
|
125079
|
-
const absPath = resolve8 ?
|
|
125080
|
-
if (!
|
|
125193
|
+
const absPath = resolve8 ? path13.resolve(repoPath, filePath) : path13.isAbsolute(filePath) ? filePath : path13.join(repoPath, filePath);
|
|
125194
|
+
if (!fs9.existsSync(absPath))
|
|
125081
125195
|
continue;
|
|
125082
|
-
const content =
|
|
125196
|
+
const content = fs9.readFileSync(absPath, "utf-8");
|
|
125083
125197
|
const lineCount = content.split("\n").length;
|
|
125084
125198
|
fileContents.set(changedFileSet ? absPath : filePath, { content, lineCount });
|
|
125085
125199
|
} catch {
|
|
125086
125200
|
}
|
|
125201
|
+
scanned++;
|
|
125202
|
+
if (hasLlm && (scanned % 20 === 0 || scanned === totalToScan)) {
|
|
125203
|
+
tracker?.detail("scan", `Reading ${scanned}/${totalToScan} files...`);
|
|
125204
|
+
}
|
|
125087
125205
|
}
|
|
125088
125206
|
let typeQuery;
|
|
125089
125207
|
const enabledCodeKeys = new Set(enabledCodeRules.filter((r) => r.type === "deterministic" && r.enabled).map((r) => r.key));
|
|
125090
125208
|
if (hasTypeAwareVisitors(enabledCodeKeys)) {
|
|
125091
|
-
const tsFiles = filesToScan.filter(({ filePath: fp }) => /\.(ts|tsx|js|jsx)$/.test(fp)).map(({ filePath: fp, resolve: res }) => res ?
|
|
125209
|
+
const tsFiles = filesToScan.filter(({ filePath: fp }) => /\.(ts|tsx|js|jsx)$/.test(fp)).map(({ filePath: fp, resolve: res }) => res ? path13.resolve(repoPath, fp) : path13.isAbsolute(fp) ? fp : path13.join(repoPath, fp));
|
|
125092
125210
|
if (tsFiles.length > 0) {
|
|
125093
125211
|
const scoped = buildScopedCompilerOptions(repoPath);
|
|
125094
125212
|
typeQuery = createTypeQueryService(tsFiles, scoped);
|
|
@@ -125167,7 +125285,7 @@ async function runViolationPipeline(input) {
|
|
|
125167
125285
|
const lang = detectLanguage(filePath);
|
|
125168
125286
|
if (!lang)
|
|
125169
125287
|
continue;
|
|
125170
|
-
const absPath = resolve8 ?
|
|
125288
|
+
const absPath = resolve8 ? path13.resolve(repoPath, filePath) : path13.isAbsolute(filePath) ? filePath : path13.join(repoPath, filePath);
|
|
125171
125289
|
const key = changedFileSet ? absPath : filePath;
|
|
125172
125290
|
const fc = fileContents.get(key);
|
|
125173
125291
|
if (!fc)
|
|
@@ -125181,11 +125299,11 @@ async function runViolationPipeline(input) {
|
|
|
125181
125299
|
}
|
|
125182
125300
|
log.info(`[Pipeline] Code scan: ${allCodeViolations.length} violations from ${filesToScan.length} files (${enabledCodeRules.length} det rules, ${enabledLlmCodeRules.length} LLM rules)`);
|
|
125183
125301
|
if (enabledCodeRules.some((r) => r.key === "bugs/deterministic/invalid-pyproject-toml")) {
|
|
125184
|
-
const pyprojectPath =
|
|
125185
|
-
if (
|
|
125302
|
+
const pyprojectPath = path13.join(repoPath, "pyproject.toml");
|
|
125303
|
+
if (fs9.existsSync(pyprojectPath)) {
|
|
125186
125304
|
try {
|
|
125187
125305
|
const { checkPyprojectToml: checkPyprojectToml2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports2));
|
|
125188
|
-
const content =
|
|
125306
|
+
const content = fs9.readFileSync(pyprojectPath, "utf-8");
|
|
125189
125307
|
const tomlViolations = checkPyprojectToml2(pyprojectPath, content);
|
|
125190
125308
|
allCodeViolations.push(...tomlViolations);
|
|
125191
125309
|
} catch {
|
|
@@ -125967,7 +126085,7 @@ function processLlmCodeViolations(codeResult, validFilePaths, fileContents, allC
|
|
|
125967
126085
|
for (const v of codeResult.violations) {
|
|
125968
126086
|
let filePath = v.filePath;
|
|
125969
126087
|
if (!validFilePaths.has(filePath)) {
|
|
125970
|
-
const resolved =
|
|
126088
|
+
const resolved = path13.resolve(repoPath, filePath);
|
|
125971
126089
|
if (validFilePaths.has(resolved)) {
|
|
125972
126090
|
filePath = resolved;
|
|
125973
126091
|
} else {
|
|
@@ -126223,7 +126341,7 @@ var init_analyze_core = __esm({
|
|
|
126223
126341
|
});
|
|
126224
126342
|
|
|
126225
126343
|
// apps/server/dist/commands/analyze-persist.js
|
|
126226
|
-
import
|
|
126344
|
+
import path14 from "node:path";
|
|
126227
126345
|
function persistFullAnalysis(project, core2, startedAt) {
|
|
126228
126346
|
const filename = buildAnalysisFilename(core2.analysisId, core2.now);
|
|
126229
126347
|
const snapshot = {
|
|
@@ -126351,8 +126469,8 @@ function buildDiffSnapshot(repoPath, core2, baseline) {
|
|
|
126351
126469
|
const newViolations = pipelineResult.added.map(denormalize);
|
|
126352
126470
|
const latestById = new Map(baseline.violations.map((v) => [v.id, v]));
|
|
126353
126471
|
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
126354
|
-
const changedAbs = new Set(changedFiles.map((c2) =>
|
|
126355
|
-
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(
|
|
126472
|
+
const changedAbs = new Set(changedFiles.map((c2) => path14.resolve(repoPath, c2.path)));
|
|
126473
|
+
const matchesChanged = (p2) => !!p2 && (changedAbs.has(p2) || changedAbs.has(path14.resolve(repoPath, p2)));
|
|
126356
126474
|
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
126357
126475
|
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
126358
126476
|
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
@@ -126469,7 +126587,7 @@ init_dist4();
|
|
|
126469
126587
|
init_paths();
|
|
126470
126588
|
init_registry();
|
|
126471
126589
|
init_helpers();
|
|
126472
|
-
async function runAdd() {
|
|
126590
|
+
async function runAdd(options = {}) {
|
|
126473
126591
|
const repoPath = resolveRepoDir(process.cwd()) ?? process.cwd();
|
|
126474
126592
|
mt("Adding repository to TrueCourse");
|
|
126475
126593
|
O2.step(repoPath);
|
|
@@ -126481,14 +126599,14 @@ async function runAdd() {
|
|
|
126481
126599
|
} else {
|
|
126482
126600
|
O2.success(`Repository "${entry.name}" added.`);
|
|
126483
126601
|
}
|
|
126484
|
-
await promptInstallSkills(repoPath);
|
|
126602
|
+
await promptInstallSkills(repoPath, { install: options.installSkills });
|
|
126485
126603
|
gt("Run `truecourse analyze` to generate analysis data.");
|
|
126486
126604
|
}
|
|
126487
126605
|
|
|
126488
126606
|
// tools/cli/src/commands/analyze.ts
|
|
126489
126607
|
init_dist4();
|
|
126490
126608
|
import { execSync } from "node:child_process";
|
|
126491
|
-
import
|
|
126609
|
+
import path16 from "node:path";
|
|
126492
126610
|
|
|
126493
126611
|
// apps/server/dist/commands/analyze-in-process.js
|
|
126494
126612
|
init_analysis_store();
|
|
@@ -126510,12 +126628,20 @@ init_helpers();
|
|
|
126510
126628
|
|
|
126511
126629
|
// tools/cli/src/commands/llm-prompt.ts
|
|
126512
126630
|
init_dist4();
|
|
126513
|
-
|
|
126631
|
+
init_helpers();
|
|
126632
|
+
async function promptLlmEstimate(estimate, { autoApprove } = {}) {
|
|
126514
126633
|
const totalRules = estimate.uniqueRuleCount ?? estimate.tiers.reduce((s, t2) => s + t2.ruleCount, 0);
|
|
126515
126634
|
const totalFiles = estimate.uniqueFileCount ?? estimate.tiers.reduce((s, t2) => s + t2.fileCount, 0);
|
|
126516
126635
|
const tokens = estimate.totalEstimatedTokens;
|
|
126517
126636
|
const tokenStr = tokens >= 1e6 ? `~${(tokens / 1e6).toFixed(1)}M tokens` : `~${Math.round(tokens / 1e3)}k tokens`;
|
|
126518
126637
|
O2.step(`LLM will analyze ${totalFiles} files with ${totalRules} rules (${tokenStr})`);
|
|
126638
|
+
if (autoApprove) return true;
|
|
126639
|
+
if (!isInteractive()) {
|
|
126640
|
+
O2.error(
|
|
126641
|
+
"Cannot prompt for LLM-rule confirmation non-interactively. Pass --llm to approve the estimate or --no-llm to skip LLM rules."
|
|
126642
|
+
);
|
|
126643
|
+
return false;
|
|
126644
|
+
}
|
|
126519
126645
|
const proceed = await ot2({ message: "Run LLM-powered rules?", initialValue: true });
|
|
126520
126646
|
if (q(proceed)) return false;
|
|
126521
126647
|
if (!proceed) O2.info("Skipping LLM rules.");
|
|
@@ -126524,8 +126650,8 @@ async function promptLlmEstimate(estimate) {
|
|
|
126524
126650
|
|
|
126525
126651
|
// tools/cli/src/telemetry.ts
|
|
126526
126652
|
init_dist4();
|
|
126527
|
-
import
|
|
126528
|
-
import
|
|
126653
|
+
import fs10 from "node:fs";
|
|
126654
|
+
import path15 from "node:path";
|
|
126529
126655
|
import os4 from "node:os";
|
|
126530
126656
|
import crypto from "node:crypto";
|
|
126531
126657
|
var DEFAULT_CONFIG2 = {
|
|
@@ -126534,12 +126660,12 @@ var DEFAULT_CONFIG2 = {
|
|
|
126534
126660
|
noticeShown: false
|
|
126535
126661
|
};
|
|
126536
126662
|
function getTelemetryConfigPath() {
|
|
126537
|
-
return
|
|
126663
|
+
return path15.join(os4.homedir(), ".truecourse", "telemetry.json");
|
|
126538
126664
|
}
|
|
126539
126665
|
function readTelemetryConfig() {
|
|
126540
126666
|
const configPath = getTelemetryConfigPath();
|
|
126541
126667
|
try {
|
|
126542
|
-
const raw =
|
|
126668
|
+
const raw = fs10.readFileSync(configPath, "utf-8");
|
|
126543
126669
|
const parsed = JSON.parse(raw);
|
|
126544
126670
|
const config2 = { ...DEFAULT_CONFIG2, ...parsed };
|
|
126545
126671
|
if (!config2.anonymousId) {
|
|
@@ -126559,17 +126685,17 @@ function readTelemetryConfig() {
|
|
|
126559
126685
|
}
|
|
126560
126686
|
function writeTelemetryConfig(partial) {
|
|
126561
126687
|
const configPath = getTelemetryConfigPath();
|
|
126562
|
-
const dir =
|
|
126563
|
-
|
|
126688
|
+
const dir = path15.dirname(configPath);
|
|
126689
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
126564
126690
|
let current;
|
|
126565
126691
|
try {
|
|
126566
|
-
const raw =
|
|
126692
|
+
const raw = fs10.readFileSync(configPath, "utf-8");
|
|
126567
126693
|
current = { ...DEFAULT_CONFIG2, ...JSON.parse(raw) };
|
|
126568
126694
|
} catch {
|
|
126569
126695
|
current = { ...DEFAULT_CONFIG2 };
|
|
126570
126696
|
}
|
|
126571
126697
|
const merged = { ...current, ...partial };
|
|
126572
|
-
|
|
126698
|
+
fs10.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
126573
126699
|
}
|
|
126574
126700
|
function showFirstRunNotice() {
|
|
126575
126701
|
try {
|
|
@@ -126663,18 +126789,31 @@ function stopSpinner() {
|
|
|
126663
126789
|
spinnerInterval = null;
|
|
126664
126790
|
}
|
|
126665
126791
|
}
|
|
126666
|
-
|
|
126792
|
+
function resolveLlmDecision(options, configDefault) {
|
|
126793
|
+
if (options.llm === true) return { enabled: true, autoApproveEstimate: true };
|
|
126794
|
+
if (options.llm === false) return { enabled: false, autoApproveEstimate: false };
|
|
126795
|
+
if (!isInteractive()) {
|
|
126796
|
+
exitMissingNonInteractiveFlag(
|
|
126797
|
+
"analyze needs a decision on LLM rules before running non-interactively.",
|
|
126798
|
+
"Pass --llm to run with LLM rules (cost) or --no-llm to skip them."
|
|
126799
|
+
);
|
|
126800
|
+
}
|
|
126801
|
+
return { enabled: configDefault, autoApproveEstimate: false };
|
|
126802
|
+
}
|
|
126803
|
+
async function runAnalyze(options = {}) {
|
|
126667
126804
|
mt("Analyzing repository");
|
|
126668
126805
|
ensureClaudeCli();
|
|
126669
126806
|
showFirstRunNotice();
|
|
126670
126807
|
const project = resolveOrInitProject();
|
|
126671
126808
|
O2.step(`Repository: ${project.name}`);
|
|
126809
|
+
await promptInstallSkills(project.path, { install: options.installSkills });
|
|
126672
126810
|
configureLogger({
|
|
126673
|
-
filePath:
|
|
126811
|
+
filePath: path16.join(project.path, ".truecourse/logs/analyze.log")
|
|
126674
126812
|
});
|
|
126675
126813
|
const config2 = readProjectConfig(project.path);
|
|
126676
126814
|
const enabledCategories = config2.enabledCategories ?? void 0;
|
|
126677
|
-
const
|
|
126815
|
+
const llmDecision = resolveLlmDecision(options, config2.enableLlmRules ?? true);
|
|
126816
|
+
const enableLlmRules = llmDecision.enabled;
|
|
126678
126817
|
renderPhase = enableLlmRules ? "pre-llm" : "all";
|
|
126679
126818
|
if (wipeLegacyPostgresData()) {
|
|
126680
126819
|
O2.info("Legacy Postgres data wiped. Re-analyze to repopulate.");
|
|
@@ -126694,7 +126833,9 @@ async function runAnalyze(_options = {}) {
|
|
|
126694
126833
|
enableLlmRulesOverride: enableLlmRules,
|
|
126695
126834
|
onLlmEstimate: async (estimate) => {
|
|
126696
126835
|
stopSpinner();
|
|
126697
|
-
const proceed = await promptLlmEstimate(estimate
|
|
126836
|
+
const proceed = await promptLlmEstimate(estimate, {
|
|
126837
|
+
autoApprove: llmDecision.autoApproveEstimate
|
|
126838
|
+
});
|
|
126698
126839
|
renderPhase = "post-llm";
|
|
126699
126840
|
return proceed;
|
|
126700
126841
|
}
|
|
@@ -126716,7 +126857,7 @@ async function runAnalyze(_options = {}) {
|
|
|
126716
126857
|
await closeLogger();
|
|
126717
126858
|
}
|
|
126718
126859
|
}
|
|
126719
|
-
async function runAnalyzeDiff(
|
|
126860
|
+
async function runAnalyzeDiff(options = {}) {
|
|
126720
126861
|
const { diffInProcess: diffInProcess2 } = await Promise.resolve().then(() => (init_diff_in_process(), diff_in_process_exports));
|
|
126721
126862
|
const { renderDiffResultsSummary: renderDiffResultsSummary2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
126722
126863
|
mt("Running diff check");
|
|
@@ -126724,12 +126865,14 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126724
126865
|
showFirstRunNotice();
|
|
126725
126866
|
const project = resolveOrInitProject();
|
|
126726
126867
|
O2.step(`Repository: ${project.name}`);
|
|
126868
|
+
await promptInstallSkills(project.path, { install: options.installSkills });
|
|
126727
126869
|
configureLogger({
|
|
126728
|
-
filePath:
|
|
126870
|
+
filePath: path16.join(project.path, ".truecourse/logs/analyze.log")
|
|
126729
126871
|
});
|
|
126730
126872
|
const config2 = readProjectConfig(project.path);
|
|
126731
126873
|
const enabledCategories = config2.enabledCategories ?? void 0;
|
|
126732
|
-
const
|
|
126874
|
+
const llmDecision = resolveLlmDecision(options, config2.enableLlmRules ?? true);
|
|
126875
|
+
const enableLlmRules = llmDecision.enabled;
|
|
126733
126876
|
renderPhase = enableLlmRules ? "pre-llm" : "all";
|
|
126734
126877
|
const stepDefs = buildAnalysisSteps(enabledCategories, enableLlmRules);
|
|
126735
126878
|
const tracker = new StepTracker((payload) => {
|
|
@@ -126746,7 +126889,9 @@ async function runAnalyzeDiff(_options = {}) {
|
|
|
126746
126889
|
enableLlmRulesOverride: enableLlmRules,
|
|
126747
126890
|
onLlmEstimate: async (estimate) => {
|
|
126748
126891
|
stopSpinner();
|
|
126749
|
-
const proceed = await promptLlmEstimate(estimate
|
|
126892
|
+
const proceed = await promptLlmEstimate(estimate, {
|
|
126893
|
+
autoApprove: llmDecision.autoApproveEstimate
|
|
126894
|
+
});
|
|
126750
126895
|
renderPhase = "post-llm";
|
|
126751
126896
|
return proceed;
|
|
126752
126897
|
}
|
|
@@ -126781,24 +126926,24 @@ init_paths();
|
|
|
126781
126926
|
init_registry();
|
|
126782
126927
|
init_helpers();
|
|
126783
126928
|
import { spawn as spawn5 } from "node:child_process";
|
|
126784
|
-
import
|
|
126785
|
-
import
|
|
126786
|
-
import { fileURLToPath as
|
|
126929
|
+
import fs15 from "node:fs";
|
|
126930
|
+
import path20 from "node:path";
|
|
126931
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
126787
126932
|
|
|
126788
126933
|
// tools/cli/src/commands/service/macos.ts
|
|
126789
|
-
import
|
|
126790
|
-
import
|
|
126934
|
+
import fs12 from "node:fs";
|
|
126935
|
+
import path17 from "node:path";
|
|
126791
126936
|
import os5 from "node:os";
|
|
126792
126937
|
import { execSync as execSync2 } from "node:child_process";
|
|
126793
126938
|
|
|
126794
126939
|
// tools/cli/src/commands/service/env.ts
|
|
126795
|
-
import
|
|
126940
|
+
import fs11 from "node:fs";
|
|
126796
126941
|
function parseEnvFile(filePath) {
|
|
126797
126942
|
const vars = {};
|
|
126798
|
-
if (!
|
|
126943
|
+
if (!fs11.existsSync(filePath)) {
|
|
126799
126944
|
return vars;
|
|
126800
126945
|
}
|
|
126801
|
-
const content =
|
|
126946
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
126802
126947
|
for (const line of content.split("\n")) {
|
|
126803
126948
|
const trimmed2 = line.trim();
|
|
126804
126949
|
if (!trimmed2 || trimmed2.startsWith("#")) continue;
|
|
@@ -126818,14 +126963,14 @@ function parseEnvFile(filePath) {
|
|
|
126818
126963
|
|
|
126819
126964
|
// tools/cli/src/commands/service/macos.ts
|
|
126820
126965
|
var SERVICE_LABEL = "com.truecourse.server";
|
|
126821
|
-
var PLIST_DIR =
|
|
126822
|
-
var PLIST_PATH =
|
|
126966
|
+
var PLIST_DIR = path17.join(os5.homedir(), "Library", "LaunchAgents");
|
|
126967
|
+
var PLIST_PATH = path17.join(PLIST_DIR, `${SERVICE_LABEL}.plist`);
|
|
126823
126968
|
function escapeXml(s) {
|
|
126824
126969
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
126825
126970
|
}
|
|
126826
126971
|
function buildPlist(serverPath, logPath, envVars) {
|
|
126827
|
-
const stdoutPath =
|
|
126828
|
-
const stderrPath =
|
|
126972
|
+
const stdoutPath = path17.join(path17.dirname(logPath), "truecourse.log");
|
|
126973
|
+
const stderrPath = path17.join(path17.dirname(logPath), "truecourse.error.log");
|
|
126829
126974
|
let envSection = "";
|
|
126830
126975
|
if (Object.keys(envVars).length > 0) {
|
|
126831
126976
|
const entries = Object.entries(envVars).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
@@ -126861,15 +127006,15 @@ ${entries}
|
|
|
126861
127006
|
}
|
|
126862
127007
|
var MacOSService = class {
|
|
126863
127008
|
async install(serverPath, logPath) {
|
|
126864
|
-
const envFile =
|
|
127009
|
+
const envFile = path17.join(os5.homedir(), ".truecourse", ".env");
|
|
126865
127010
|
const envVars = parseEnvFile(envFile);
|
|
126866
127011
|
if (process.env.PATH && !envVars.PATH) {
|
|
126867
127012
|
envVars.PATH = process.env.PATH;
|
|
126868
127013
|
}
|
|
126869
|
-
|
|
126870
|
-
|
|
127014
|
+
fs12.mkdirSync(PLIST_DIR, { recursive: true });
|
|
127015
|
+
fs12.mkdirSync(path17.dirname(logPath), { recursive: true });
|
|
126871
127016
|
const plist = buildPlist(serverPath, logPath, envVars);
|
|
126872
|
-
|
|
127017
|
+
fs12.writeFileSync(PLIST_PATH, plist, "utf-8");
|
|
126873
127018
|
execSync2(`launchctl load -w "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
126874
127019
|
}
|
|
126875
127020
|
async uninstall() {
|
|
@@ -126877,12 +127022,12 @@ var MacOSService = class {
|
|
|
126877
127022
|
execSync2(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
126878
127023
|
} catch {
|
|
126879
127024
|
}
|
|
126880
|
-
if (
|
|
126881
|
-
|
|
127025
|
+
if (fs12.existsSync(PLIST_PATH)) {
|
|
127026
|
+
fs12.unlinkSync(PLIST_PATH);
|
|
126882
127027
|
}
|
|
126883
127028
|
}
|
|
126884
127029
|
async start() {
|
|
126885
|
-
if (!
|
|
127030
|
+
if (!fs12.existsSync(PLIST_PATH)) {
|
|
126886
127031
|
throw new Error("Service is not installed. Run 'truecourse service install' first.");
|
|
126887
127032
|
}
|
|
126888
127033
|
execSync2(`launchctl load -w "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
@@ -126912,21 +127057,21 @@ var MacOSService = class {
|
|
|
126912
127057
|
}
|
|
126913
127058
|
}
|
|
126914
127059
|
async isInstalled() {
|
|
126915
|
-
return
|
|
127060
|
+
return fs12.existsSync(PLIST_PATH);
|
|
126916
127061
|
}
|
|
126917
127062
|
};
|
|
126918
127063
|
|
|
126919
127064
|
// tools/cli/src/commands/service/linux.ts
|
|
126920
|
-
import
|
|
126921
|
-
import
|
|
127065
|
+
import fs13 from "node:fs";
|
|
127066
|
+
import path18 from "node:path";
|
|
126922
127067
|
import os6 from "node:os";
|
|
126923
127068
|
import { execSync as execSync3 } from "node:child_process";
|
|
126924
127069
|
var SERVICE_NAME = "truecourse";
|
|
126925
|
-
var UNIT_DIR =
|
|
126926
|
-
var UNIT_PATH =
|
|
127070
|
+
var UNIT_DIR = path18.join(os6.homedir(), ".config", "systemd", "user");
|
|
127071
|
+
var UNIT_PATH = path18.join(UNIT_DIR, `${SERVICE_NAME}.service`);
|
|
126927
127072
|
function buildUnitFile(serverPath, logPath) {
|
|
126928
|
-
const envFile =
|
|
126929
|
-
const logDir =
|
|
127073
|
+
const envFile = path18.join(os6.homedir(), ".truecourse", ".env");
|
|
127074
|
+
const logDir = path18.dirname(logPath);
|
|
126930
127075
|
return `[Unit]
|
|
126931
127076
|
Description=TrueCourse Server
|
|
126932
127077
|
After=network.target
|
|
@@ -126937,8 +127082,8 @@ ExecStart=${process.execPath} ${serverPath}
|
|
|
126937
127082
|
Restart=on-failure
|
|
126938
127083
|
RestartSec=5
|
|
126939
127084
|
EnvironmentFile=${envFile}
|
|
126940
|
-
StandardOutput=append:${
|
|
126941
|
-
StandardError=append:${
|
|
127085
|
+
StandardOutput=append:${path18.join(logDir, "truecourse.log")}
|
|
127086
|
+
StandardError=append:${path18.join(logDir, "truecourse.error.log")}
|
|
126942
127087
|
|
|
126943
127088
|
[Install]
|
|
126944
127089
|
WantedBy=default.target
|
|
@@ -126946,10 +127091,10 @@ WantedBy=default.target
|
|
|
126946
127091
|
}
|
|
126947
127092
|
var LinuxService = class {
|
|
126948
127093
|
async install(serverPath, logPath) {
|
|
126949
|
-
|
|
126950
|
-
|
|
127094
|
+
fs13.mkdirSync(UNIT_DIR, { recursive: true });
|
|
127095
|
+
fs13.mkdirSync(path18.dirname(logPath), { recursive: true });
|
|
126951
127096
|
const unit = buildUnitFile(serverPath, logPath);
|
|
126952
|
-
|
|
127097
|
+
fs13.writeFileSync(UNIT_PATH, unit, "utf-8");
|
|
126953
127098
|
execSync3("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
126954
127099
|
execSync3(`systemctl --user enable ${SERVICE_NAME}`, { stdio: "pipe" });
|
|
126955
127100
|
}
|
|
@@ -126962,8 +127107,8 @@ var LinuxService = class {
|
|
|
126962
127107
|
execSync3(`systemctl --user disable ${SERVICE_NAME}`, { stdio: "pipe" });
|
|
126963
127108
|
} catch {
|
|
126964
127109
|
}
|
|
126965
|
-
if (
|
|
126966
|
-
|
|
127110
|
+
if (fs13.existsSync(UNIT_PATH)) {
|
|
127111
|
+
fs13.unlinkSync(UNIT_PATH);
|
|
126967
127112
|
}
|
|
126968
127113
|
try {
|
|
126969
127114
|
execSync3("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
@@ -126992,7 +127137,7 @@ var LinuxService = class {
|
|
|
126992
127137
|
}
|
|
126993
127138
|
}
|
|
126994
127139
|
async isInstalled() {
|
|
126995
|
-
return
|
|
127140
|
+
return fs13.existsSync(UNIT_PATH);
|
|
126996
127141
|
}
|
|
126997
127142
|
};
|
|
126998
127143
|
|
|
@@ -127095,72 +127240,72 @@ function getPlatform() {
|
|
|
127095
127240
|
}
|
|
127096
127241
|
|
|
127097
127242
|
// tools/cli/src/commands/service/logs.ts
|
|
127098
|
-
import
|
|
127099
|
-
import
|
|
127243
|
+
import fs14 from "node:fs";
|
|
127244
|
+
import path19 from "node:path";
|
|
127100
127245
|
import os7 from "node:os";
|
|
127101
127246
|
import { spawn as spawn4 } from "node:child_process";
|
|
127102
127247
|
var MAX_LOG_SIZE2 = 10 * 1024 * 1024;
|
|
127103
127248
|
var MAX_LOG_FILES2 = 5;
|
|
127104
127249
|
function getLogDir() {
|
|
127105
|
-
return
|
|
127250
|
+
return path19.join(os7.homedir(), ".truecourse", "logs");
|
|
127106
127251
|
}
|
|
127107
127252
|
function getLogPath() {
|
|
127108
|
-
return
|
|
127253
|
+
return path19.join(getLogDir(), "truecourse.log");
|
|
127109
127254
|
}
|
|
127110
127255
|
function rotateLogs(logDir) {
|
|
127111
|
-
const logFile =
|
|
127112
|
-
if (!
|
|
127113
|
-
const stats =
|
|
127256
|
+
const logFile = path19.join(logDir, "truecourse.log");
|
|
127257
|
+
if (!fs14.existsSync(logFile)) return;
|
|
127258
|
+
const stats = fs14.statSync(logFile);
|
|
127114
127259
|
if (stats.size < MAX_LOG_SIZE2) return;
|
|
127115
127260
|
for (let i = MAX_LOG_FILES2; i >= 1; i--) {
|
|
127116
|
-
const older =
|
|
127261
|
+
const older = path19.join(logDir, `truecourse.log.${i}`);
|
|
127117
127262
|
if (i === MAX_LOG_FILES2) {
|
|
127118
|
-
if (
|
|
127263
|
+
if (fs14.existsSync(older)) fs14.unlinkSync(older);
|
|
127119
127264
|
} else {
|
|
127120
|
-
const newer =
|
|
127121
|
-
if (
|
|
127265
|
+
const newer = path19.join(logDir, `truecourse.log.${i + 1}`);
|
|
127266
|
+
if (fs14.existsSync(older)) fs14.renameSync(older, newer);
|
|
127122
127267
|
}
|
|
127123
127268
|
}
|
|
127124
|
-
|
|
127269
|
+
fs14.renameSync(logFile, path19.join(logDir, "truecourse.log.1"));
|
|
127125
127270
|
}
|
|
127126
127271
|
function rotateErrorLogs(logDir) {
|
|
127127
|
-
const logFile =
|
|
127128
|
-
if (!
|
|
127129
|
-
const stats =
|
|
127272
|
+
const logFile = path19.join(logDir, "truecourse.error.log");
|
|
127273
|
+
if (!fs14.existsSync(logFile)) return;
|
|
127274
|
+
const stats = fs14.statSync(logFile);
|
|
127130
127275
|
if (stats.size < MAX_LOG_SIZE2) return;
|
|
127131
127276
|
for (let i = MAX_LOG_FILES2; i >= 1; i--) {
|
|
127132
|
-
const older =
|
|
127277
|
+
const older = path19.join(logDir, `truecourse.error.log.${i}`);
|
|
127133
127278
|
if (i === MAX_LOG_FILES2) {
|
|
127134
|
-
if (
|
|
127279
|
+
if (fs14.existsSync(older)) fs14.unlinkSync(older);
|
|
127135
127280
|
} else {
|
|
127136
|
-
const newer =
|
|
127137
|
-
if (
|
|
127281
|
+
const newer = path19.join(logDir, `truecourse.error.log.${i + 1}`);
|
|
127282
|
+
if (fs14.existsSync(older)) fs14.renameSync(older, newer);
|
|
127138
127283
|
}
|
|
127139
127284
|
}
|
|
127140
|
-
|
|
127285
|
+
fs14.renameSync(logFile, path19.join(logDir, "truecourse.error.log.1"));
|
|
127141
127286
|
}
|
|
127142
127287
|
function tailLogs() {
|
|
127143
127288
|
const logFile = getLogPath();
|
|
127144
|
-
if (!
|
|
127289
|
+
if (!fs14.existsSync(logFile)) {
|
|
127145
127290
|
console.log("No log file found. Is the service running?");
|
|
127146
127291
|
console.log(`Expected at: ${logFile}`);
|
|
127147
127292
|
return;
|
|
127148
127293
|
}
|
|
127149
127294
|
if (process.platform === "win32") {
|
|
127150
|
-
const content =
|
|
127295
|
+
const content = fs14.readFileSync(logFile, "utf-8");
|
|
127151
127296
|
const lines = content.split("\n");
|
|
127152
127297
|
const tail = lines.slice(-50);
|
|
127153
127298
|
for (const line of tail) {
|
|
127154
127299
|
process.stdout.write(line + "\n");
|
|
127155
127300
|
}
|
|
127156
|
-
let lastSize =
|
|
127157
|
-
|
|
127158
|
-
const newSize =
|
|
127301
|
+
let lastSize = fs14.statSync(logFile).size;
|
|
127302
|
+
fs14.watchFile(logFile, { interval: 500 }, () => {
|
|
127303
|
+
const newSize = fs14.statSync(logFile).size;
|
|
127159
127304
|
if (newSize > lastSize) {
|
|
127160
|
-
const fd =
|
|
127305
|
+
const fd = fs14.openSync(logFile, "r");
|
|
127161
127306
|
const buf = Buffer.alloc(newSize - lastSize);
|
|
127162
|
-
|
|
127163
|
-
|
|
127307
|
+
fs14.readSync(fd, buf, 0, buf.length, lastSize);
|
|
127308
|
+
fs14.closeSync(fd);
|
|
127164
127309
|
process.stdout.write(buf.toString("utf-8"));
|
|
127165
127310
|
lastSize = newSize;
|
|
127166
127311
|
}
|
|
@@ -127181,15 +127326,15 @@ function tailLogs() {
|
|
|
127181
127326
|
}
|
|
127182
127327
|
|
|
127183
127328
|
// tools/cli/src/commands/dashboard.ts
|
|
127184
|
-
var __dirname3 =
|
|
127329
|
+
var __dirname3 = path20.dirname(fileURLToPath5(import.meta.url));
|
|
127185
127330
|
function resolveServerEntry() {
|
|
127186
127331
|
const candidates = [
|
|
127187
127332
|
// Packaged CLI: dist/cli.mjs next to dist/server.mjs
|
|
127188
|
-
|
|
127333
|
+
path20.join(__dirname3, "server.mjs"),
|
|
127189
127334
|
// Source build output: tools/cli/dist → ../../../../dist/server.mjs
|
|
127190
|
-
|
|
127335
|
+
path20.resolve(__dirname3, "..", "..", "..", "..", "dist", "server.mjs")
|
|
127191
127336
|
];
|
|
127192
|
-
return candidates.find((p2) =>
|
|
127337
|
+
return candidates.find((p2) => fs15.existsSync(p2)) ?? null;
|
|
127193
127338
|
}
|
|
127194
127339
|
async function waitForHealth(url, timeoutMs = 3e4) {
|
|
127195
127340
|
const start = Date.now();
|
|
@@ -127282,6 +127427,27 @@ async function runServiceMode(serverEntry) {
|
|
|
127282
127427
|
O2.success(`Dashboard open at ${target}`);
|
|
127283
127428
|
O2.info("Stop the dashboard with: truecourse dashboard stop");
|
|
127284
127429
|
}
|
|
127430
|
+
async function probeHealth(url) {
|
|
127431
|
+
try {
|
|
127432
|
+
const res = await fetch(`${url}/api/health`);
|
|
127433
|
+
return res.ok;
|
|
127434
|
+
} catch {
|
|
127435
|
+
return false;
|
|
127436
|
+
}
|
|
127437
|
+
}
|
|
127438
|
+
async function detectRunningState() {
|
|
127439
|
+
const platform = getPlatform();
|
|
127440
|
+
const url = getServerUrl();
|
|
127441
|
+
const healthy = await probeHealth(url);
|
|
127442
|
+
if (await platform.isInstalled()) {
|
|
127443
|
+
const { running, pid } = await platform.status();
|
|
127444
|
+
if (running) return { mode: "service", pid, healthy };
|
|
127445
|
+
if (healthy) return { mode: "console", healthy: true };
|
|
127446
|
+
return { mode: "none" };
|
|
127447
|
+
}
|
|
127448
|
+
if (healthy) return { mode: "console", healthy: true };
|
|
127449
|
+
return { mode: "none" };
|
|
127450
|
+
}
|
|
127285
127451
|
async function runDashboard(options = {}) {
|
|
127286
127452
|
mt("Opening TrueCourse dashboard");
|
|
127287
127453
|
const serverEntry = resolveServerEntry();
|
|
@@ -127291,10 +127457,35 @@ async function runDashboard(options = {}) {
|
|
|
127291
127457
|
);
|
|
127292
127458
|
process.exit(1);
|
|
127293
127459
|
}
|
|
127294
|
-
const configured =
|
|
127295
|
-
const
|
|
127296
|
-
|
|
127297
|
-
if (
|
|
127460
|
+
const configured = fs15.existsSync(getConfigPath());
|
|
127461
|
+
const needsDecision = !configured || options.reconfigure;
|
|
127462
|
+
let runMode;
|
|
127463
|
+
if (options.mode) {
|
|
127464
|
+
runMode = options.mode;
|
|
127465
|
+
} else if (needsDecision) {
|
|
127466
|
+
if (!isInteractive()) {
|
|
127467
|
+
exitMissingNonInteractiveFlag(
|
|
127468
|
+
"Dashboard run mode is not configured.",
|
|
127469
|
+
"Pass --service for the background service or --console to run in this terminal."
|
|
127470
|
+
);
|
|
127471
|
+
}
|
|
127472
|
+
runMode = await promptRunMode();
|
|
127473
|
+
} else {
|
|
127474
|
+
runMode = readConfig().runMode;
|
|
127475
|
+
}
|
|
127476
|
+
const shouldPersist = needsDecision || options.mode !== void 0;
|
|
127477
|
+
if (runMode !== "service") {
|
|
127478
|
+
const platform = getPlatform();
|
|
127479
|
+
if (await platform.isInstalled()) {
|
|
127480
|
+
const { running } = await platform.status();
|
|
127481
|
+
if (running) {
|
|
127482
|
+
O2.error(
|
|
127483
|
+
"A dashboard service is already installed and running. Stop and remove it first: `truecourse dashboard uninstall`, then rerun `truecourse dashboard`."
|
|
127484
|
+
);
|
|
127485
|
+
process.exit(1);
|
|
127486
|
+
}
|
|
127487
|
+
}
|
|
127488
|
+
}
|
|
127298
127489
|
if (runMode === "service") {
|
|
127299
127490
|
try {
|
|
127300
127491
|
await runServiceMode(serverEntry);
|
|
@@ -127302,70 +127493,68 @@ async function runDashboard(options = {}) {
|
|
|
127302
127493
|
O2.error(`Service mode failed: ${err.message}`);
|
|
127303
127494
|
O2.info("Falling back to console mode.");
|
|
127304
127495
|
await runConsoleMode(serverEntry);
|
|
127496
|
+
if (shouldPersist) writeConfig({ runMode: "console" });
|
|
127497
|
+
return;
|
|
127305
127498
|
}
|
|
127499
|
+
if (shouldPersist) writeConfig({ runMode: "service" });
|
|
127306
127500
|
} else {
|
|
127307
127501
|
await runConsoleMode(serverEntry);
|
|
127502
|
+
if (shouldPersist) writeConfig({ runMode: "console" });
|
|
127308
127503
|
}
|
|
127309
127504
|
}
|
|
127310
127505
|
async function runDashboardStop() {
|
|
127311
|
-
const
|
|
127312
|
-
|
|
127313
|
-
|
|
127314
|
-
|
|
127315
|
-
|
|
127316
|
-
|
|
127317
|
-
|
|
127318
|
-
|
|
127319
|
-
|
|
127320
|
-
|
|
127506
|
+
const state = await detectRunningState();
|
|
127507
|
+
switch (state.mode) {
|
|
127508
|
+
case "service": {
|
|
127509
|
+
O2.step("Stopping dashboard service...");
|
|
127510
|
+
await getPlatform().stop();
|
|
127511
|
+
O2.success("Dashboard service stopped.");
|
|
127512
|
+
return;
|
|
127513
|
+
}
|
|
127514
|
+
case "console": {
|
|
127515
|
+
O2.info(
|
|
127516
|
+
"A dashboard is running in console mode (not managed by the service). Press Ctrl+C in its terminal to stop it."
|
|
127517
|
+
);
|
|
127518
|
+
return;
|
|
127519
|
+
}
|
|
127520
|
+
case "none": {
|
|
127521
|
+
O2.info("Dashboard is not running.");
|
|
127522
|
+
return;
|
|
127523
|
+
}
|
|
127321
127524
|
}
|
|
127322
|
-
O2.step("Stopping dashboard...");
|
|
127323
|
-
await platform.stop();
|
|
127324
|
-
O2.success("Dashboard stopped.");
|
|
127325
127525
|
}
|
|
127326
127526
|
async function runDashboardStatus() {
|
|
127327
|
-
const
|
|
127527
|
+
const state = await detectRunningState();
|
|
127328
127528
|
const url = getServerUrl();
|
|
127329
|
-
|
|
127330
|
-
|
|
127331
|
-
const
|
|
127332
|
-
|
|
127333
|
-
|
|
127529
|
+
switch (state.mode) {
|
|
127530
|
+
case "service": {
|
|
127531
|
+
const pidInfo = state.pid ? ` (PID: ${state.pid})` : "";
|
|
127532
|
+
O2.success(`Dashboard service is running${pidInfo}`);
|
|
127533
|
+
if (state.healthy) {
|
|
127534
|
+
O2.info(`Server is healthy at ${url}`);
|
|
127334
127535
|
} else {
|
|
127335
|
-
O2.
|
|
127536
|
+
O2.warn(`Service process is running but server is not responding at ${url}.`);
|
|
127336
127537
|
}
|
|
127337
|
-
|
|
127338
|
-
O2.info("Dashboard is not running.");
|
|
127538
|
+
return;
|
|
127339
127539
|
}
|
|
127340
|
-
|
|
127341
|
-
|
|
127342
|
-
|
|
127343
|
-
|
|
127344
|
-
|
|
127345
|
-
|
|
127346
|
-
|
|
127347
|
-
|
|
127348
|
-
|
|
127349
|
-
|
|
127350
|
-
|
|
127351
|
-
|
|
127352
|
-
}
|
|
127353
|
-
const pidInfo = pid ? ` (PID: ${pid})` : "";
|
|
127354
|
-
O2.success(`Dashboard service is running${pidInfo}`);
|
|
127355
|
-
try {
|
|
127356
|
-
const res = await fetch(`${url}/api/health`);
|
|
127357
|
-
if (res.ok) {
|
|
127358
|
-
O2.info(`Server is healthy at ${url}`);
|
|
127359
|
-
} else {
|
|
127360
|
-
O2.warn(`Server returned status ${res.status}`);
|
|
127540
|
+
case "console": {
|
|
127541
|
+
O2.success(`Dashboard is running in console mode at ${url}`);
|
|
127542
|
+
return;
|
|
127543
|
+
}
|
|
127544
|
+
case "none": {
|
|
127545
|
+
const platform = getPlatform();
|
|
127546
|
+
if (await platform.isInstalled()) {
|
|
127547
|
+
O2.info("Dashboard service is installed but not running.");
|
|
127548
|
+
} else {
|
|
127549
|
+
O2.info("Dashboard is not running.");
|
|
127550
|
+
}
|
|
127551
|
+
return;
|
|
127361
127552
|
}
|
|
127362
|
-
} catch {
|
|
127363
|
-
O2.warn("Service process is running but server is not responding.");
|
|
127364
127553
|
}
|
|
127365
127554
|
}
|
|
127366
|
-
function runDashboardLogs() {
|
|
127367
|
-
const
|
|
127368
|
-
if (
|
|
127555
|
+
async function runDashboardLogs() {
|
|
127556
|
+
const state = await detectRunningState();
|
|
127557
|
+
if (state.mode === "console") {
|
|
127369
127558
|
O2.info("Dashboard is running in console mode \u2014 logs print to its terminal.");
|
|
127370
127559
|
return;
|
|
127371
127560
|
}
|
|
@@ -127402,27 +127591,38 @@ var SEVERITY_ORDER = {
|
|
|
127402
127591
|
low: 3,
|
|
127403
127592
|
info: 4
|
|
127404
127593
|
};
|
|
127594
|
+
var SEVERITIES = ["critical", "high", "medium", "low", "info"];
|
|
127405
127595
|
function listViolations(repoPath, options = {}) {
|
|
127406
127596
|
const latest = readLatest(repoPath);
|
|
127407
127597
|
if (!latest)
|
|
127408
127598
|
return { violations: [], total: 0 };
|
|
127409
|
-
|
|
127410
|
-
|
|
127599
|
+
let violations;
|
|
127600
|
+
if (!options.analysisId || latest.analysis.id === options.analysisId) {
|
|
127601
|
+
violations = latest.violations;
|
|
127602
|
+
} else {
|
|
127603
|
+
const historical = readActiveViolationsAt(repoPath, options.analysisId);
|
|
127604
|
+
if (!historical)
|
|
127605
|
+
return { violations: [], total: 0 };
|
|
127606
|
+
violations = historical;
|
|
127411
127607
|
}
|
|
127412
127608
|
const statusMode = options.status ?? "active";
|
|
127413
127609
|
let filtered;
|
|
127414
127610
|
if (statusMode === "resolved") {
|
|
127415
|
-
filtered =
|
|
127611
|
+
filtered = violations.filter((v) => v.status === "resolved");
|
|
127416
127612
|
} else if (statusMode === "all") {
|
|
127417
|
-
filtered =
|
|
127613
|
+
filtered = violations;
|
|
127418
127614
|
} else {
|
|
127419
127615
|
const active = ["new", "unchanged"];
|
|
127420
|
-
filtered =
|
|
127616
|
+
filtered = violations.filter((v) => active.includes(v.status));
|
|
127421
127617
|
}
|
|
127422
127618
|
if (options.filePath) {
|
|
127423
127619
|
const absPath = options.filePath.startsWith("/") ? options.filePath : `${repoPath}/${options.filePath}`;
|
|
127424
127620
|
filtered = filtered.filter((v) => v.type === "code" && (v.filePath === absPath || v.filePath === options.filePath));
|
|
127425
127621
|
}
|
|
127622
|
+
if (options.severity !== void 0) {
|
|
127623
|
+
const allowed = (Array.isArray(options.severity) ? options.severity : [options.severity]).map((s) => s.toLowerCase());
|
|
127624
|
+
filtered = filtered.filter((v) => allowed.includes(v.severity.toLowerCase()));
|
|
127625
|
+
}
|
|
127426
127626
|
filtered.sort((a, b) => {
|
|
127427
127627
|
const sa = SEVERITY_ORDER[a.severity] ?? 5;
|
|
127428
127628
|
const sb = SEVERITY_ORDER[b.severity] ?? 5;
|
|
@@ -127436,6 +127636,42 @@ function listViolations(repoPath, options = {}) {
|
|
|
127436
127636
|
const paged = limit > 0 ? filtered.slice(offset, offset + limit) : filtered;
|
|
127437
127637
|
return { violations: paged, total };
|
|
127438
127638
|
}
|
|
127639
|
+
function readActiveViolationsAt(repoPath, analysisId) {
|
|
127640
|
+
const targetFile = findAnalysisFilename(repoPath, analysisId);
|
|
127641
|
+
if (!targetFile)
|
|
127642
|
+
return null;
|
|
127643
|
+
const targetSnap = readAnalysis(repoPath, targetFile);
|
|
127644
|
+
if (!targetSnap)
|
|
127645
|
+
return null;
|
|
127646
|
+
const files = listAnalyses(repoPath);
|
|
127647
|
+
const targetIdx = files.indexOf(targetFile);
|
|
127648
|
+
if (targetIdx === -1)
|
|
127649
|
+
return null;
|
|
127650
|
+
const active = /* @__PURE__ */ new Map();
|
|
127651
|
+
for (let i = 0; i <= targetIdx; i++) {
|
|
127652
|
+
const snap = readAnalysis(repoPath, files[i]);
|
|
127653
|
+
if (!snap)
|
|
127654
|
+
continue;
|
|
127655
|
+
for (const r of snap.violations.resolved)
|
|
127656
|
+
active.delete(r.id);
|
|
127657
|
+
for (const a of snap.violations.added)
|
|
127658
|
+
active.set(a.id, a);
|
|
127659
|
+
}
|
|
127660
|
+
return [...active.values()].map(denormalizeAgainst(targetSnap.graph));
|
|
127661
|
+
}
|
|
127662
|
+
function denormalizeAgainst(graph) {
|
|
127663
|
+
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
127664
|
+
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
127665
|
+
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
127666
|
+
const databaseById = new Map(graph.databases.map((d3) => [d3.id, d3.name]));
|
|
127667
|
+
return (v) => ({
|
|
127668
|
+
...v,
|
|
127669
|
+
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
127670
|
+
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
127671
|
+
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
127672
|
+
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
127673
|
+
});
|
|
127674
|
+
}
|
|
127439
127675
|
function getDiffResult(repoPath) {
|
|
127440
127676
|
const diff = readDiff(repoPath);
|
|
127441
127677
|
if (!diff)
|
|
@@ -127447,19 +127683,37 @@ function getDiffResult(repoPath) {
|
|
|
127447
127683
|
|
|
127448
127684
|
// tools/cli/src/commands/list.ts
|
|
127449
127685
|
init_helpers();
|
|
127450
|
-
async function runList({ limit = 20, offset = 0 } = {}) {
|
|
127686
|
+
async function runList({ limit = 20, offset = 0, severity } = {}) {
|
|
127451
127687
|
mt("Violations");
|
|
127452
127688
|
const repo = requireRegisteredRepo();
|
|
127453
127689
|
const { violations, total } = listViolations(repo.path, {
|
|
127454
127690
|
limit: isFinite(limit) ? limit : 0,
|
|
127455
|
-
offset
|
|
127691
|
+
offset,
|
|
127692
|
+
severity
|
|
127456
127693
|
});
|
|
127457
127694
|
if (total === 0 && violations.length === 0) {
|
|
127458
|
-
|
|
127695
|
+
if (severity && severity.length > 0) {
|
|
127696
|
+
O2.info(`No violations match severity filter: ${severity.join(", ")}.`);
|
|
127697
|
+
} else {
|
|
127698
|
+
O2.info("No violations. Run `truecourse analyze` if you haven't yet.");
|
|
127699
|
+
}
|
|
127459
127700
|
return;
|
|
127460
127701
|
}
|
|
127461
127702
|
renderViolations(violations, { total, offset });
|
|
127462
127703
|
}
|
|
127704
|
+
function parseSeverityFlag(raw) {
|
|
127705
|
+
if (!raw) return void 0;
|
|
127706
|
+
const parts = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
127707
|
+
if (parts.length === 0) return void 0;
|
|
127708
|
+
const invalid = parts.filter((s) => !SEVERITIES.includes(s));
|
|
127709
|
+
if (invalid.length > 0) {
|
|
127710
|
+
console.error(
|
|
127711
|
+
`error: unknown severity value(s): ${invalid.join(", ")}. Valid: ${SEVERITIES.join(", ")}`
|
|
127712
|
+
);
|
|
127713
|
+
process.exit(1);
|
|
127714
|
+
}
|
|
127715
|
+
return parts;
|
|
127716
|
+
}
|
|
127463
127717
|
async function runListDiff() {
|
|
127464
127718
|
mt("Diff check results");
|
|
127465
127719
|
const repo = requireRegisteredRepo();
|
|
@@ -127552,8 +127806,8 @@ async function runRulesLlm(options) {
|
|
|
127552
127806
|
|
|
127553
127807
|
// tools/cli/src/commands/hooks.ts
|
|
127554
127808
|
import { execSync as execSync5 } from "node:child_process";
|
|
127555
|
-
import
|
|
127556
|
-
import
|
|
127809
|
+
import fs16 from "node:fs";
|
|
127810
|
+
import path21 from "node:path";
|
|
127557
127811
|
|
|
127558
127812
|
// node_modules/.pnpm/js-yaml@4.1.1/node_modules/js-yaml/dist/js-yaml.mjs
|
|
127559
127813
|
function isNothing(subject) {
|
|
@@ -130197,22 +130451,22 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
130197
130451
|
function findGitDir(from) {
|
|
130198
130452
|
let dir = from;
|
|
130199
130453
|
while (true) {
|
|
130200
|
-
const gitPath =
|
|
130201
|
-
if (
|
|
130202
|
-
const stat =
|
|
130454
|
+
const gitPath = path21.join(dir, ".git");
|
|
130455
|
+
if (fs16.existsSync(gitPath)) {
|
|
130456
|
+
const stat = fs16.statSync(gitPath);
|
|
130203
130457
|
if (stat.isDirectory()) {
|
|
130204
130458
|
return gitPath;
|
|
130205
130459
|
}
|
|
130206
130460
|
if (stat.isFile()) {
|
|
130207
|
-
const content =
|
|
130461
|
+
const content = fs16.readFileSync(gitPath, "utf-8").trim();
|
|
130208
130462
|
const match2 = content.match(/^gitdir:\s*(.+)$/);
|
|
130209
130463
|
if (match2) {
|
|
130210
|
-
const resolved =
|
|
130464
|
+
const resolved = path21.resolve(dir, match2[1]);
|
|
130211
130465
|
return resolved;
|
|
130212
130466
|
}
|
|
130213
130467
|
}
|
|
130214
130468
|
}
|
|
130215
|
-
const parent =
|
|
130469
|
+
const parent = path21.dirname(dir);
|
|
130216
130470
|
if (parent === dir) return null;
|
|
130217
130471
|
dir = parent;
|
|
130218
130472
|
}
|
|
@@ -130220,10 +130474,10 @@ function findGitDir(from) {
|
|
|
130220
130474
|
function findProjectRoot(from) {
|
|
130221
130475
|
let dir = from;
|
|
130222
130476
|
while (true) {
|
|
130223
|
-
if (
|
|
130477
|
+
if (fs16.existsSync(path21.join(dir, ".git"))) {
|
|
130224
130478
|
return dir;
|
|
130225
130479
|
}
|
|
130226
|
-
const parent =
|
|
130480
|
+
const parent = path21.dirname(dir);
|
|
130227
130481
|
if (parent === dir) return null;
|
|
130228
130482
|
dir = parent;
|
|
130229
130483
|
}
|
|
@@ -130238,10 +130492,10 @@ function parseTimeout(value) {
|
|
|
130238
130492
|
return num * 1e3;
|
|
130239
130493
|
}
|
|
130240
130494
|
function loadConfig(projectRoot) {
|
|
130241
|
-
const configPath =
|
|
130242
|
-
if (!
|
|
130495
|
+
const configPath = path21.join(projectRoot, ".truecourse", "hooks.yaml");
|
|
130496
|
+
if (!fs16.existsSync(configPath)) return {};
|
|
130243
130497
|
try {
|
|
130244
|
-
const raw =
|
|
130498
|
+
const raw = fs16.readFileSync(configPath, "utf-8");
|
|
130245
130499
|
return jsYaml.load(raw) || {};
|
|
130246
130500
|
} catch {
|
|
130247
130501
|
return {};
|
|
@@ -130260,11 +130514,11 @@ function runHooksInstall() {
|
|
|
130260
130514
|
console.error("Error: Not a git repository.");
|
|
130261
130515
|
process.exit(1);
|
|
130262
130516
|
}
|
|
130263
|
-
const hooksDir =
|
|
130264
|
-
|
|
130265
|
-
const hookPath =
|
|
130266
|
-
if (
|
|
130267
|
-
const existing =
|
|
130517
|
+
const hooksDir = path21.join(gitDir, "hooks");
|
|
130518
|
+
fs16.mkdirSync(hooksDir, { recursive: true });
|
|
130519
|
+
const hookPath = path21.join(hooksDir, "pre-commit");
|
|
130520
|
+
if (fs16.existsSync(hookPath)) {
|
|
130521
|
+
const existing = fs16.readFileSync(hookPath, "utf-8");
|
|
130268
130522
|
if (!existing.includes(HOOK_IDENTIFIER)) {
|
|
130269
130523
|
console.error(
|
|
130270
130524
|
"Error: A pre-commit hook already exists and was not installed by TrueCourse."
|
|
@@ -130274,7 +130528,7 @@ function runHooksInstall() {
|
|
|
130274
130528
|
process.exit(1);
|
|
130275
130529
|
}
|
|
130276
130530
|
}
|
|
130277
|
-
|
|
130531
|
+
fs16.writeFileSync(hookPath, HOOK_SCRIPT, { mode: 493 });
|
|
130278
130532
|
console.log("TrueCourse pre-commit hook installed.");
|
|
130279
130533
|
console.log(` ${hookPath}`);
|
|
130280
130534
|
}
|
|
@@ -130284,17 +130538,17 @@ function runHooksUninstall() {
|
|
|
130284
130538
|
console.error("Error: Not a git repository.");
|
|
130285
130539
|
process.exit(1);
|
|
130286
130540
|
}
|
|
130287
|
-
const hookPath =
|
|
130288
|
-
if (!
|
|
130541
|
+
const hookPath = path21.join(gitDir, "hooks", "pre-commit");
|
|
130542
|
+
if (!fs16.existsSync(hookPath)) {
|
|
130289
130543
|
console.log("No pre-commit hook installed.");
|
|
130290
130544
|
return;
|
|
130291
130545
|
}
|
|
130292
|
-
const content =
|
|
130546
|
+
const content = fs16.readFileSync(hookPath, "utf-8");
|
|
130293
130547
|
if (!content.includes(HOOK_IDENTIFIER)) {
|
|
130294
130548
|
console.error("Error: The pre-commit hook was not installed by TrueCourse. Leaving it in place.");
|
|
130295
130549
|
process.exit(1);
|
|
130296
130550
|
}
|
|
130297
|
-
|
|
130551
|
+
fs16.unlinkSync(hookPath);
|
|
130298
130552
|
console.log("TrueCourse pre-commit hook removed.");
|
|
130299
130553
|
}
|
|
130300
130554
|
function runHooksStatus() {
|
|
@@ -130303,8 +130557,8 @@ function runHooksStatus() {
|
|
|
130303
130557
|
console.error("Error: Not a git repository.");
|
|
130304
130558
|
process.exit(1);
|
|
130305
130559
|
}
|
|
130306
|
-
const hookPath =
|
|
130307
|
-
const installed =
|
|
130560
|
+
const hookPath = path21.join(gitDir, "hooks", "pre-commit");
|
|
130561
|
+
const installed = fs16.existsSync(hookPath) && fs16.readFileSync(hookPath, "utf-8").includes(HOOK_IDENTIFIER);
|
|
130308
130562
|
if (installed) {
|
|
130309
130563
|
console.log("TrueCourse pre-commit hook: installed");
|
|
130310
130564
|
console.log(` ${hookPath}`);
|
|
@@ -130314,8 +130568,8 @@ function runHooksStatus() {
|
|
|
130314
130568
|
}
|
|
130315
130569
|
const projectRoot = findProjectRoot(process.cwd());
|
|
130316
130570
|
if (projectRoot) {
|
|
130317
|
-
const configPath =
|
|
130318
|
-
if (
|
|
130571
|
+
const configPath = path21.join(projectRoot, ".truecourse", "hooks.yaml");
|
|
130572
|
+
if (fs16.existsSync(configPath)) {
|
|
130319
130573
|
console.log(`
|
|
130320
130574
|
Config: ${configPath}`);
|
|
130321
130575
|
const config2 = loadConfig(projectRoot);
|
|
@@ -130385,6 +130639,7 @@ async function runHooksRun() {
|
|
|
130385
130639
|
detectLanguage2 = analyzer.detectLanguage;
|
|
130386
130640
|
checkCodeRules2 = analyzer.checkCodeRules;
|
|
130387
130641
|
CODE_RULES2 = analyzer.CODE_RULES;
|
|
130642
|
+
await analyzer.initParsers();
|
|
130388
130643
|
} catch {
|
|
130389
130644
|
console.log(" skipped (analyzer not available)");
|
|
130390
130645
|
process.exit(0);
|
|
@@ -130440,9 +130695,14 @@ async function runHooksRun() {
|
|
|
130440
130695
|
|
|
130441
130696
|
// tools/cli/src/index.ts
|
|
130442
130697
|
var program2 = new Command();
|
|
130443
|
-
program2.name("truecourse").version("0.
|
|
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) => {
|
|
130445
|
-
|
|
130698
|
+
program2.name("truecourse").version("0.5.1").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
130699
|
+
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").option("--service", "Run as a background service (skips mode prompt)").option("--console", "Run in this terminal (skips mode prompt)").action(async (options) => {
|
|
130700
|
+
if (options.service && options.console) {
|
|
130701
|
+
console.error("error: --service and --console are mutually exclusive");
|
|
130702
|
+
process.exit(1);
|
|
130703
|
+
}
|
|
130704
|
+
const mode = options.service ? "service" : options.console ? "console" : void 0;
|
|
130705
|
+
await runDashboard({ reconfigure: options.reconfigure, mode });
|
|
130446
130706
|
});
|
|
130447
130707
|
dashboardCmd.command("stop").description("Stop the dashboard").action(async () => {
|
|
130448
130708
|
await runDashboardStop();
|
|
@@ -130450,29 +130710,40 @@ dashboardCmd.command("stop").description("Stop the dashboard").action(async () =
|
|
|
130450
130710
|
dashboardCmd.command("status").description("Show dashboard status").action(async () => {
|
|
130451
130711
|
await runDashboardStatus();
|
|
130452
130712
|
});
|
|
130453
|
-
dashboardCmd.command("logs").description("Tail dashboard logs (service mode only)").action(() => {
|
|
130454
|
-
runDashboardLogs();
|
|
130713
|
+
dashboardCmd.command("logs").description("Tail dashboard logs (service mode only)").action(async () => {
|
|
130714
|
+
await runDashboardLogs();
|
|
130455
130715
|
});
|
|
130456
130716
|
dashboardCmd.command("uninstall").description("Remove the background service and revert to console mode").action(async () => {
|
|
130457
130717
|
await runDashboardUninstall();
|
|
130458
130718
|
});
|
|
130459
|
-
|
|
130719
|
+
function resolveInstallSkills(options) {
|
|
130720
|
+
if (options.installSkills === true) return true;
|
|
130721
|
+
if (options.skills === false) return false;
|
|
130722
|
+
return void 0;
|
|
130723
|
+
}
|
|
130724
|
+
program2.command("analyze").description("Analyze the current repository").option("--diff", "Run diff check against latest analysis").option("--llm", "Run LLM-powered rules (pre-approves the cost estimate)").option("--no-llm", "Skip LLM-powered rules for this run").option("--install-skills", "Install Claude Code skills without prompting").option("--no-skills", "Skip the Claude Code skills prompt").action(async (options) => {
|
|
130725
|
+
const llm = typeof options.llm === "boolean" ? options.llm : void 0;
|
|
130726
|
+
const installSkills = resolveInstallSkills(options);
|
|
130460
130727
|
if (options.diff) {
|
|
130461
|
-
await runAnalyzeDiff();
|
|
130728
|
+
await runAnalyzeDiff({ llm, installSkills });
|
|
130462
130729
|
} else {
|
|
130463
|
-
await runAnalyze();
|
|
130730
|
+
await runAnalyze({ llm, installSkills });
|
|
130464
130731
|
}
|
|
130465
130732
|
});
|
|
130466
|
-
program2.command("add").description("Register the current directory with TrueCourse").action(async () => {
|
|
130467
|
-
await runAdd();
|
|
130733
|
+
program2.command("add").description("Register the current directory with TrueCourse").option("--install-skills", "Install Claude Code skills without prompting").option("--no-skills", "Skip the Claude Code skills prompt").action(async (options) => {
|
|
130734
|
+
await runAdd({ installSkills: resolveInstallSkills(options) });
|
|
130468
130735
|
});
|
|
130469
|
-
program2.command("list").description("List violations from the latest analysis").option("--diff", "Show diff check results (new and resolved)").option("--limit <n>", "Number of violations to show (default: 20)", parseInt).option("--offset <n>", "Skip first N violations", parseInt).option("--all", "Show all violations").
|
|
130736
|
+
program2.command("list").description("List violations from the latest analysis").option("--diff", "Show diff check results (new and resolved)").option("--limit <n>", "Number of violations to show (default: 20)", parseInt).option("--offset <n>", "Skip first N violations", parseInt).option("--all", "Show all violations").option(
|
|
130737
|
+
"--severity <list>",
|
|
130738
|
+
"Comma-separated severities to include (critical,high,medium,low,info)"
|
|
130739
|
+
).action(async (options) => {
|
|
130470
130740
|
if (options.diff) {
|
|
130471
130741
|
await runListDiff();
|
|
130472
130742
|
} else {
|
|
130473
130743
|
await runList({
|
|
130474
130744
|
limit: options.all ? Infinity : options.limit ?? 20,
|
|
130475
|
-
offset: options.offset ?? 0
|
|
130745
|
+
offset: options.offset ?? 0,
|
|
130746
|
+
severity: parseSeverityFlag(options.severity)
|
|
130476
130747
|
});
|
|
130477
130748
|
}
|
|
130478
130749
|
});
|