@sashabogi/argus-mcp 1.2.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -156
- package/dist/cli.mjs +286 -45
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +70 -1
- package/dist/index.mjs +252 -0
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.mjs +1080 -35
- package/dist/mcp.mjs.map +1 -1
- package/package.json +5 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -10,9 +16,15 @@ var __export = (target, all) => {
|
|
|
10
16
|
};
|
|
11
17
|
|
|
12
18
|
// node_modules/tsup/assets/esm_shims.js
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
import path from "path";
|
|
21
|
+
var getFilename, getDirname, __dirname;
|
|
13
22
|
var init_esm_shims = __esm({
|
|
14
23
|
"node_modules/tsup/assets/esm_shims.js"() {
|
|
15
24
|
"use strict";
|
|
25
|
+
getFilename = () => fileURLToPath(import.meta.url);
|
|
26
|
+
getDirname = () => path.dirname(getFilename());
|
|
27
|
+
__dirname = /* @__PURE__ */ getDirname();
|
|
16
28
|
}
|
|
17
29
|
});
|
|
18
30
|
|
|
@@ -511,14 +523,14 @@ var init_onboarding_ui = __esm({
|
|
|
511
523
|
});
|
|
512
524
|
|
|
513
525
|
// src/core/onboarding.ts
|
|
514
|
-
function detectPotentialKeyFiles(projectPath, userPatterns, fs2,
|
|
526
|
+
function detectPotentialKeyFiles(projectPath, userPatterns, fs2, path3) {
|
|
515
527
|
const detected = [];
|
|
516
528
|
function checkFile(filePath, relativePath) {
|
|
517
529
|
try {
|
|
518
530
|
const stats = fs2.statSync(filePath);
|
|
519
531
|
if (!stats.isFile()) return;
|
|
520
|
-
const fileName =
|
|
521
|
-
const ext =
|
|
532
|
+
const fileName = path3.basename(filePath).toLowerCase();
|
|
533
|
+
const ext = path3.extname(filePath).toLowerCase();
|
|
522
534
|
if (![".md", ".txt", ".org", ""].includes(ext)) return;
|
|
523
535
|
let reason = "";
|
|
524
536
|
let matchedPattern;
|
|
@@ -587,13 +599,13 @@ function detectPotentialKeyFiles(projectPath, userPatterns, fs2, path2) {
|
|
|
587
599
|
}
|
|
588
600
|
if (relativePath.split("/").filter(Boolean).length < 2) {
|
|
589
601
|
scanDir(
|
|
590
|
-
|
|
602
|
+
path3.join(dirPath, entry.name),
|
|
591
603
|
relativePath ? `${relativePath}/${entry.name}` : entry.name
|
|
592
604
|
);
|
|
593
605
|
}
|
|
594
606
|
} else {
|
|
595
607
|
checkFile(
|
|
596
|
-
|
|
608
|
+
path3.join(dirPath, entry.name),
|
|
597
609
|
relativePath ? `${relativePath}/${entry.name}` : entry.name
|
|
598
610
|
);
|
|
599
611
|
}
|
|
@@ -624,12 +636,12 @@ async function runGlobalOnboarding() {
|
|
|
624
636
|
console.log(` Key patterns: ${config.globalKeyPatterns.join(", ")}`);
|
|
625
637
|
return config;
|
|
626
638
|
}
|
|
627
|
-
async function runProjectOnboarding(projectPath, globalConfig, fs2,
|
|
628
|
-
const projectName =
|
|
639
|
+
async function runProjectOnboarding(projectPath, globalConfig, fs2, path3) {
|
|
640
|
+
const projectName = path3.basename(projectPath);
|
|
629
641
|
console.log(`
|
|
630
642
|
\u{1F4C2} Scanning project: ${projectName}
|
|
631
643
|
`);
|
|
632
|
-
const detected = detectPotentialKeyFiles(projectPath, globalConfig.globalKeyPatterns, fs2,
|
|
644
|
+
const detected = detectPotentialKeyFiles(projectPath, globalConfig.globalKeyPatterns, fs2, path3);
|
|
633
645
|
if (globalConfig.experienceLevel === "beginner") {
|
|
634
646
|
const autoSelected = detected.filter((d) => d.matchedPattern).map((d) => d.path);
|
|
635
647
|
if (autoSelected.length > 0) {
|
|
@@ -703,9 +715,9 @@ import { Command } from "commander";
|
|
|
703
715
|
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, statSync as statSync2, unlinkSync, readdirSync as readdirSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
704
716
|
import * as fs from "fs";
|
|
705
717
|
import { homedir as homedir2 } from "os";
|
|
706
|
-
import { join as join4, resolve, basename } from "path";
|
|
707
|
-
import * as
|
|
708
|
-
import { execSync } from "child_process";
|
|
718
|
+
import { join as join4, resolve, basename as basename2 } from "path";
|
|
719
|
+
import * as path2 from "path";
|
|
720
|
+
import { execSync as execSync2 } from "child_process";
|
|
709
721
|
|
|
710
722
|
// src/core/config.ts
|
|
711
723
|
init_esm_shims();
|
|
@@ -945,7 +957,8 @@ function createSnapshot(projectPath, outputPath, options = {}) {
|
|
|
945
957
|
// src/core/enhanced-snapshot.ts
|
|
946
958
|
init_esm_shims();
|
|
947
959
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
948
|
-
import { join as join3, dirname, extname as extname2 } from "path";
|
|
960
|
+
import { join as join3, dirname, extname as extname2, basename } from "path";
|
|
961
|
+
import { execSync } from "child_process";
|
|
949
962
|
function parseImports(content, filePath) {
|
|
950
963
|
const imports = [];
|
|
951
964
|
const lines = content.split("\n");
|
|
@@ -1082,6 +1095,114 @@ function parseExports(content, filePath) {
|
|
|
1082
1095
|
}
|
|
1083
1096
|
return exports;
|
|
1084
1097
|
}
|
|
1098
|
+
function calculateComplexity(content) {
|
|
1099
|
+
const patterns = [
|
|
1100
|
+
/\bif\s*\(/g,
|
|
1101
|
+
/\belse\s+if\s*\(/g,
|
|
1102
|
+
/\bwhile\s*\(/g,
|
|
1103
|
+
/\bfor\s*\(/g,
|
|
1104
|
+
/\bcase\s+/g,
|
|
1105
|
+
/\?\s*.*\s*:/g,
|
|
1106
|
+
/\&\&/g,
|
|
1107
|
+
/\|\|/g,
|
|
1108
|
+
/\bcatch\s*\(/g
|
|
1109
|
+
];
|
|
1110
|
+
let complexity = 1;
|
|
1111
|
+
for (const pattern of patterns) {
|
|
1112
|
+
const matches = content.match(pattern);
|
|
1113
|
+
if (matches) complexity += matches.length;
|
|
1114
|
+
}
|
|
1115
|
+
return complexity;
|
|
1116
|
+
}
|
|
1117
|
+
function getComplexityLevel(score) {
|
|
1118
|
+
if (score <= 10) return "low";
|
|
1119
|
+
if (score <= 20) return "medium";
|
|
1120
|
+
return "high";
|
|
1121
|
+
}
|
|
1122
|
+
function mapTestFiles(files) {
|
|
1123
|
+
const testMap = {};
|
|
1124
|
+
const testPatterns = [
|
|
1125
|
+
// Same directory patterns
|
|
1126
|
+
(src) => src.replace(/\.tsx?$/, ".test.ts"),
|
|
1127
|
+
(src) => src.replace(/\.tsx?$/, ".test.tsx"),
|
|
1128
|
+
(src) => src.replace(/\.tsx?$/, ".spec.ts"),
|
|
1129
|
+
(src) => src.replace(/\.tsx?$/, ".spec.tsx"),
|
|
1130
|
+
(src) => src.replace(/\.jsx?$/, ".test.js"),
|
|
1131
|
+
(src) => src.replace(/\.jsx?$/, ".test.jsx"),
|
|
1132
|
+
(src) => src.replace(/\.jsx?$/, ".spec.js"),
|
|
1133
|
+
(src) => src.replace(/\.jsx?$/, ".spec.jsx"),
|
|
1134
|
+
// __tests__ directory pattern
|
|
1135
|
+
(src) => {
|
|
1136
|
+
const dir = dirname(src);
|
|
1137
|
+
const base = basename(src).replace(/\.(tsx?|jsx?)$/, "");
|
|
1138
|
+
return join3(dir, "__tests__", `${base}.test.ts`);
|
|
1139
|
+
},
|
|
1140
|
+
(src) => {
|
|
1141
|
+
const dir = dirname(src);
|
|
1142
|
+
const base = basename(src).replace(/\.(tsx?|jsx?)$/, "");
|
|
1143
|
+
return join3(dir, "__tests__", `${base}.test.tsx`);
|
|
1144
|
+
},
|
|
1145
|
+
// test/ directory pattern
|
|
1146
|
+
(src) => src.replace(/^src\//, "test/").replace(/\.(tsx?|jsx?)$/, ".test.ts"),
|
|
1147
|
+
(src) => src.replace(/^src\//, "tests/").replace(/\.(tsx?|jsx?)$/, ".test.ts")
|
|
1148
|
+
];
|
|
1149
|
+
const fileSet = new Set(files);
|
|
1150
|
+
for (const file of files) {
|
|
1151
|
+
if (file.includes(".test.") || file.includes(".spec.") || file.includes("__tests__")) continue;
|
|
1152
|
+
if (!/\.(tsx?|jsx?)$/.test(file)) continue;
|
|
1153
|
+
const tests = [];
|
|
1154
|
+
for (const pattern of testPatterns) {
|
|
1155
|
+
const testPath = pattern(file);
|
|
1156
|
+
if (testPath !== file && fileSet.has(testPath)) {
|
|
1157
|
+
tests.push(testPath);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (tests.length > 0) {
|
|
1161
|
+
testMap[file] = [...new Set(tests)];
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return testMap;
|
|
1165
|
+
}
|
|
1166
|
+
function getRecentChanges(projectPath) {
|
|
1167
|
+
try {
|
|
1168
|
+
execSync("git rev-parse --git-dir", { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
|
|
1169
|
+
const output = execSync(
|
|
1170
|
+
'git log --since="7 days ago" --name-only --format="COMMIT_AUTHOR:%an" --diff-filter=ACMR',
|
|
1171
|
+
{ cwd: projectPath, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024, stdio: "pipe" }
|
|
1172
|
+
);
|
|
1173
|
+
if (!output.trim()) return [];
|
|
1174
|
+
const fileStats = {};
|
|
1175
|
+
let currentAuthor = "";
|
|
1176
|
+
let currentCommitId = 0;
|
|
1177
|
+
for (const line of output.split("\n")) {
|
|
1178
|
+
const trimmed = line.trim();
|
|
1179
|
+
if (!trimmed) {
|
|
1180
|
+
currentCommitId++;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (trimmed.startsWith("COMMIT_AUTHOR:")) {
|
|
1184
|
+
currentAuthor = trimmed.replace("COMMIT_AUTHOR:", "");
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
const file = trimmed;
|
|
1188
|
+
if (!fileStats[file]) {
|
|
1189
|
+
fileStats[file] = { commits: /* @__PURE__ */ new Set(), authors: /* @__PURE__ */ new Set() };
|
|
1190
|
+
}
|
|
1191
|
+
fileStats[file].commits.add(`${currentCommitId}`);
|
|
1192
|
+
if (currentAuthor) {
|
|
1193
|
+
fileStats[file].authors.add(currentAuthor);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
const result = Object.entries(fileStats).map(([file, stats]) => ({
|
|
1197
|
+
file,
|
|
1198
|
+
commits: stats.commits.size,
|
|
1199
|
+
authors: stats.authors.size
|
|
1200
|
+
})).sort((a, b) => b.commits - a.commits);
|
|
1201
|
+
return result;
|
|
1202
|
+
} catch {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1085
1206
|
function resolveImportPath(importPath, fromFile, projectFiles) {
|
|
1086
1207
|
if (!importPath.startsWith(".")) return void 0;
|
|
1087
1208
|
const fromDir = dirname(fromFile);
|
|
@@ -1151,6 +1272,23 @@ function createEnhancedSnapshot(projectPath, outputPath, options = {}) {
|
|
|
1151
1272
|
symbolIndex[exp.symbol].push(exp.file);
|
|
1152
1273
|
}
|
|
1153
1274
|
}
|
|
1275
|
+
const complexityScores = [];
|
|
1276
|
+
for (const [relPath, metadata] of Object.entries(fileIndex)) {
|
|
1277
|
+
const fullPath = join3(projectPath, relPath);
|
|
1278
|
+
try {
|
|
1279
|
+
const content = readFileSync3(fullPath, "utf-8");
|
|
1280
|
+
const score = calculateComplexity(content);
|
|
1281
|
+
complexityScores.push({
|
|
1282
|
+
file: relPath,
|
|
1283
|
+
score,
|
|
1284
|
+
level: getComplexityLevel(score)
|
|
1285
|
+
});
|
|
1286
|
+
} catch {
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
complexityScores.sort((a, b) => b.score - a.score);
|
|
1290
|
+
const testFileMap = mapTestFiles(baseResult.files);
|
|
1291
|
+
const recentChanges = getRecentChanges(projectPath);
|
|
1154
1292
|
const metadataSection = `
|
|
1155
1293
|
|
|
1156
1294
|
================================================================================
|
|
@@ -1174,6 +1312,25 @@ METADATA: WHO IMPORTS WHOM
|
|
|
1174
1312
|
================================================================================
|
|
1175
1313
|
${Object.entries(exportGraph).map(([file, importers]) => `${file} is imported by:
|
|
1176
1314
|
${importers.map((i) => ` \u2190 ${i}`).join("\n")}`).join("\n\n")}
|
|
1315
|
+
|
|
1316
|
+
================================================================================
|
|
1317
|
+
METADATA: COMPLEXITY SCORES
|
|
1318
|
+
================================================================================
|
|
1319
|
+
${complexityScores.map((c) => `${c.file}: ${c.score} (${c.level})`).join("\n")}
|
|
1320
|
+
|
|
1321
|
+
================================================================================
|
|
1322
|
+
METADATA: TEST COVERAGE MAP
|
|
1323
|
+
================================================================================
|
|
1324
|
+
${Object.entries(testFileMap).length > 0 ? Object.entries(testFileMap).map(([src, tests]) => `${src} -> ${tests.join(", ")}`).join("\n") : "(no test file mappings found)"}
|
|
1325
|
+
${baseResult.files.filter(
|
|
1326
|
+
(f) => /\.(tsx?|jsx?)$/.test(f) && !f.includes(".test.") && !f.includes(".spec.") && !f.includes("__tests__") && !testFileMap[f]
|
|
1327
|
+
).map((f) => `${f} -> (no tests)`).join("\n")}
|
|
1328
|
+
${recentChanges !== null ? `
|
|
1329
|
+
|
|
1330
|
+
================================================================================
|
|
1331
|
+
METADATA: RECENT CHANGES (last 7 days)
|
|
1332
|
+
================================================================================
|
|
1333
|
+
${recentChanges.length > 0 ? recentChanges.map((c) => `${c.file}: ${c.commits} commit${c.commits !== 1 ? "s" : ""}, ${c.authors} author${c.authors !== 1 ? "s" : ""}`).join("\n") : "(no changes in the last 7 days)"}` : ""}
|
|
1177
1334
|
`;
|
|
1178
1335
|
const existingContent = readFileSync3(outputPath, "utf-8");
|
|
1179
1336
|
writeFileSync3(outputPath, existingContent + metadataSection);
|
|
@@ -1185,7 +1342,10 @@ ${importers.map((i) => ` \u2190 ${i}`).join("\n")}`).join("\n\n")}
|
|
|
1185
1342
|
fileIndex,
|
|
1186
1343
|
importGraph,
|
|
1187
1344
|
exportGraph,
|
|
1188
|
-
symbolIndex
|
|
1345
|
+
symbolIndex,
|
|
1346
|
+
complexityScores,
|
|
1347
|
+
testFileMap,
|
|
1348
|
+
recentChanges
|
|
1189
1349
|
}
|
|
1190
1350
|
};
|
|
1191
1351
|
}
|
|
@@ -2055,7 +2215,7 @@ program.command("init").description("Interactive setup wizard").action(async ()
|
|
|
2055
2215
|
program.command("update").description("Update Argus to the latest version").action(() => {
|
|
2056
2216
|
console.log("\n\u{1F504} Updating Argus...\n");
|
|
2057
2217
|
try {
|
|
2058
|
-
|
|
2218
|
+
execSync2("npm install -g https://github.com/sashabogi/argus/tarball/main", { stdio: "inherit" });
|
|
2059
2219
|
console.log("\n\u2705 Argus updated successfully!");
|
|
2060
2220
|
console.log("\nRun `argus --version` to check the new version.");
|
|
2061
2221
|
} catch (error) {
|
|
@@ -2064,7 +2224,7 @@ program.command("update").description("Update Argus to the latest version").acti
|
|
|
2064
2224
|
process.exit(1);
|
|
2065
2225
|
}
|
|
2066
2226
|
});
|
|
2067
|
-
program.command("analyze <path> <query>").description("Analyze a codebase or snapshot with AI").option("-p, --provider <provider>", "Override default provider").option("-t, --max-turns <n>", "Maximum reasoning turns", "15").option("-v, --verbose", "Show detailed execution logs").action(async (
|
|
2227
|
+
program.command("analyze <path> <query>").description("Analyze a codebase or snapshot with AI").option("-p, --provider <provider>", "Override default provider").option("-t, --max-turns <n>", "Maximum reasoning turns", "15").option("-v, --verbose", "Show detailed execution logs").action(async (path3, query, opts) => {
|
|
2068
2228
|
const config = loadConfig();
|
|
2069
2229
|
if (opts.provider) {
|
|
2070
2230
|
config.provider = opts.provider;
|
|
@@ -2076,7 +2236,7 @@ program.command("analyze <path> <query>").description("Analyze a codebase or sna
|
|
|
2076
2236
|
console.error("\nRun `argus init` to configure.");
|
|
2077
2237
|
process.exit(1);
|
|
2078
2238
|
}
|
|
2079
|
-
const resolvedPath = resolve(
|
|
2239
|
+
const resolvedPath = resolve(path3);
|
|
2080
2240
|
if (!existsSync4(resolvedPath)) {
|
|
2081
2241
|
console.error(`File not found: ${resolvedPath}`);
|
|
2082
2242
|
process.exit(1);
|
|
@@ -2088,11 +2248,11 @@ program.command("analyze <path> <query>").description("Analyze a codebase or sna
|
|
|
2088
2248
|
console.log("\u{1F4F8} Creating snapshot of codebase...");
|
|
2089
2249
|
snapshotPath = join4(homedir2(), ".argus", `temp-${Date.now()}.txt`);
|
|
2090
2250
|
ensureConfigDir();
|
|
2091
|
-
const result =
|
|
2251
|
+
const result = createEnhancedSnapshot(resolvedPath, snapshotPath, {
|
|
2092
2252
|
extensions: config.defaults.snapshotExtensions,
|
|
2093
2253
|
excludePatterns: config.defaults.excludePatterns
|
|
2094
2254
|
});
|
|
2095
|
-
console.log(` ${result.fileCount} files, ${formatSize(result.totalSize)}`);
|
|
2255
|
+
console.log(` ${result.fileCount} files, ${formatSize(result.totalSize)} (enhanced)`);
|
|
2096
2256
|
tempSnapshot = true;
|
|
2097
2257
|
}
|
|
2098
2258
|
console.log(`
|
|
@@ -2131,29 +2291,31 @@ program.command("analyze <path> <query>").description("Analyze a codebase or sna
|
|
|
2131
2291
|
}
|
|
2132
2292
|
}
|
|
2133
2293
|
});
|
|
2134
|
-
program.command("snapshot <path>").description("Create a codebase snapshot for analysis").option("-o, --output <file>", "Output file path").option("-e, --extensions <exts>", "File extensions to include (comma-separated)").option("--exclude <patterns>", "Patterns to exclude (comma-separated)").option("--
|
|
2294
|
+
program.command("snapshot <path>").description("Create a codebase snapshot for analysis").option("-o, --output <file>", "Output file path").option("-e, --extensions <exts>", "File extensions to include (comma-separated)").option("--exclude <patterns>", "Patterns to exclude (comma-separated)").option("--basic", "Skip structural metadata (faster, smaller snapshot)").action((path3, opts) => {
|
|
2135
2295
|
const config = loadConfig();
|
|
2136
|
-
const resolvedPath = resolve(
|
|
2296
|
+
const resolvedPath = resolve(path3);
|
|
2137
2297
|
if (!existsSync4(resolvedPath)) {
|
|
2138
2298
|
console.error(`Path not found: ${resolvedPath}`);
|
|
2139
2299
|
process.exit(1);
|
|
2140
2300
|
}
|
|
2141
|
-
const outputPath = opts.output || `${
|
|
2301
|
+
const outputPath = opts.output || `${basename2(resolvedPath)}-snapshot.txt`;
|
|
2142
2302
|
console.log("\u{1F4F8} Creating codebase snapshot...");
|
|
2143
2303
|
console.log(` Source: ${resolvedPath}`);
|
|
2144
2304
|
console.log(` Output: ${outputPath}`);
|
|
2145
2305
|
const extensions = opts.extensions ? opts.extensions.split(",").map((e) => e.trim()) : config.defaults.snapshotExtensions;
|
|
2146
2306
|
const excludePatterns = opts.exclude ? opts.exclude.split(",").map((p) => p.trim()) : config.defaults.excludePatterns;
|
|
2147
|
-
if (opts.
|
|
2307
|
+
if (opts.basic) {
|
|
2308
|
+
console.log(" Mode: Basic (no structural metadata)");
|
|
2309
|
+
} else {
|
|
2148
2310
|
console.log(" Mode: Enhanced (with import graph & exports index)");
|
|
2149
2311
|
}
|
|
2150
|
-
const result = opts.
|
|
2312
|
+
const result = opts.basic ? createSnapshot(resolvedPath, outputPath, { extensions, excludePatterns }) : createEnhancedSnapshot(resolvedPath, outputPath, { extensions, excludePatterns });
|
|
2151
2313
|
console.log(`
|
|
2152
2314
|
\u2705 Snapshot created!`);
|
|
2153
2315
|
console.log(` Files: ${result.fileCount}`);
|
|
2154
2316
|
console.log(` Lines: ${result.totalLines.toLocaleString()}`);
|
|
2155
2317
|
console.log(` Size: ${formatSize(result.totalSize)}`);
|
|
2156
|
-
if (opts.
|
|
2318
|
+
if (!opts.basic && "metadata" in result) {
|
|
2157
2319
|
const meta = result.metadata;
|
|
2158
2320
|
console.log(`
|
|
2159
2321
|
\u{1F4CA} Structural Metadata:`);
|
|
@@ -2194,8 +2356,8 @@ program.command("search <snapshot> <pattern>").description("Fast grep search (no
|
|
|
2194
2356
|
console.log(`${match.lineNum}: ${match.line.trim()}`);
|
|
2195
2357
|
}
|
|
2196
2358
|
});
|
|
2197
|
-
program.command("status [path]").description("Check if snapshot is up to date").option("-s, --snapshot <file>", "Snapshot file to check", ".argus/snapshot.txt").action((
|
|
2198
|
-
const projectPath =
|
|
2359
|
+
program.command("status [path]").description("Check if snapshot is up to date").option("-s, --snapshot <file>", "Snapshot file to check", ".argus/snapshot.txt").action((path3, opts) => {
|
|
2360
|
+
const projectPath = path3 ? resolve(path3) : process.cwd();
|
|
2199
2361
|
const snapshotPath = resolve(projectPath, opts.snapshot);
|
|
2200
2362
|
console.log("\u{1F4CA} Argus Status\n");
|
|
2201
2363
|
if (!existsSync4(snapshotPath)) {
|
|
@@ -2310,8 +2472,8 @@ exec argus-mcp "$@"
|
|
|
2310
2472
|
ensureConfigDir();
|
|
2311
2473
|
writeFileSync4(wrapperPath, wrapperScript, { mode: 493 });
|
|
2312
2474
|
try {
|
|
2313
|
-
|
|
2314
|
-
|
|
2475
|
+
execSync2(`claude mcp remove argus -s user 2>/dev/null || true`, { stdio: "ignore" });
|
|
2476
|
+
execSync2(`claude mcp add argus -s user -- "${wrapperPath}"`, { stdio: "inherit" });
|
|
2315
2477
|
console.log("\n\u2705 Argus MCP server installed for Claude Code!");
|
|
2316
2478
|
} catch {
|
|
2317
2479
|
console.log("\n\u26A0\uFE0F Could not automatically add to Claude Code.");
|
|
@@ -2343,7 +2505,7 @@ exec argus-mcp "$@"
|
|
|
2343
2505
|
});
|
|
2344
2506
|
mcpCommand.command("uninstall").description("Remove Argus from Claude Code").action(() => {
|
|
2345
2507
|
try {
|
|
2346
|
-
|
|
2508
|
+
execSync2("claude mcp remove argus -s user", { stdio: "inherit" });
|
|
2347
2509
|
console.log("\n\u2705 Argus MCP server removed from Claude Code.");
|
|
2348
2510
|
} catch {
|
|
2349
2511
|
console.log("\n\u26A0\uFE0F Could not remove from Claude Code.");
|
|
@@ -2352,7 +2514,7 @@ mcpCommand.command("uninstall").description("Remove Argus from Claude Code").act
|
|
|
2352
2514
|
}
|
|
2353
2515
|
});
|
|
2354
2516
|
var contextCommand = program.command("context").description("Generate architectural context for CLAUDE.md (survives compaction)");
|
|
2355
|
-
contextCommand.command("generate <path>").description("Generate architecture summary for a project").option("-o, --output <file>", "Output file (default: stdout)").option("-f, --format <format>", "Output format: markdown, json", "markdown").action(async (
|
|
2517
|
+
contextCommand.command("generate <path>").description("Generate architecture summary for a project").option("-o, --output <file>", "Output file (default: stdout)").option("-f, --format <format>", "Output format: markdown, json", "markdown").action(async (path3, opts) => {
|
|
2356
2518
|
const config = loadConfig();
|
|
2357
2519
|
const errors = validateConfig(config);
|
|
2358
2520
|
if (errors.length > 0) {
|
|
@@ -2360,7 +2522,7 @@ contextCommand.command("generate <path>").description("Generate architecture sum
|
|
|
2360
2522
|
errors.forEach((e) => console.error(` - ${e}`));
|
|
2361
2523
|
process.exit(1);
|
|
2362
2524
|
}
|
|
2363
|
-
const resolvedPath = resolve(
|
|
2525
|
+
const resolvedPath = resolve(path3);
|
|
2364
2526
|
if (!existsSync4(resolvedPath)) {
|
|
2365
2527
|
console.error(`Path not found: ${resolvedPath}`);
|
|
2366
2528
|
process.exit(1);
|
|
@@ -2368,11 +2530,11 @@ contextCommand.command("generate <path>").description("Generate architecture sum
|
|
|
2368
2530
|
console.error("\u{1F4F8} Creating snapshot...");
|
|
2369
2531
|
const snapshotPath = join4(homedir2(), ".argus", `context-${Date.now()}.txt`);
|
|
2370
2532
|
ensureConfigDir();
|
|
2371
|
-
const snapshotResult =
|
|
2533
|
+
const snapshotResult = createEnhancedSnapshot(resolvedPath, snapshotPath, {
|
|
2372
2534
|
extensions: config.defaults.snapshotExtensions,
|
|
2373
2535
|
excludePatterns: config.defaults.excludePatterns
|
|
2374
2536
|
});
|
|
2375
|
-
console.error(` ${snapshotResult.fileCount} files, ${formatSize(snapshotResult.totalSize)}`);
|
|
2537
|
+
console.error(` ${snapshotResult.fileCount} files, ${formatSize(snapshotResult.totalSize)} (enhanced)`);
|
|
2376
2538
|
console.error("\u{1F9E0} Analyzing architecture...\n");
|
|
2377
2539
|
try {
|
|
2378
2540
|
const provider = createProvider(config);
|
|
@@ -2408,7 +2570,7 @@ List with file paths and one-line descriptions.`;
|
|
|
2408
2570
|
}
|
|
2409
2571
|
});
|
|
2410
2572
|
console.error("\n");
|
|
2411
|
-
const projectName =
|
|
2573
|
+
const projectName = basename2(resolvedPath);
|
|
2412
2574
|
const output = generateContextMarkdown(projectName, {
|
|
2413
2575
|
modules: moduleResult.answer || "Unable to analyze modules",
|
|
2414
2576
|
patterns: patternResult.answer || "Unable to analyze patterns",
|
|
@@ -2428,13 +2590,13 @@ List with file paths and one-line descriptions.`;
|
|
|
2428
2590
|
}
|
|
2429
2591
|
}
|
|
2430
2592
|
});
|
|
2431
|
-
contextCommand.command("inject <path>").description("Add/update architecture section in CLAUDE.md").action(async (
|
|
2432
|
-
const resolvedPath = resolve(
|
|
2593
|
+
contextCommand.command("inject <path>").description("Add/update architecture section in CLAUDE.md").action(async (path3) => {
|
|
2594
|
+
const resolvedPath = resolve(path3);
|
|
2433
2595
|
const claudeMdPath = join4(resolvedPath, "CLAUDE.md");
|
|
2434
2596
|
console.error("Generating context...\n");
|
|
2435
|
-
const { execSync:
|
|
2597
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
2436
2598
|
try {
|
|
2437
|
-
const contextOutput =
|
|
2599
|
+
const contextOutput = execSync3(
|
|
2438
2600
|
`argus context generate "${resolvedPath}"`,
|
|
2439
2601
|
{ encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
|
|
2440
2602
|
);
|
|
@@ -2464,10 +2626,10 @@ ${endMarker}`;
|
|
|
2464
2626
|
process.exit(1);
|
|
2465
2627
|
}
|
|
2466
2628
|
});
|
|
2467
|
-
contextCommand.command("refresh <path>").description("Regenerate architecture context (run after major changes)").action(async (
|
|
2468
|
-
const resolvedPath = resolve(
|
|
2629
|
+
contextCommand.command("refresh <path>").description("Regenerate architecture context (run after major changes)").action(async (path3) => {
|
|
2630
|
+
const resolvedPath = resolve(path3);
|
|
2469
2631
|
console.log("Refreshing codebase context...\n");
|
|
2470
|
-
|
|
2632
|
+
execSync2(`argus context inject "${resolvedPath}"`, { stdio: "inherit" });
|
|
2471
2633
|
});
|
|
2472
2634
|
function generateContextMarkdown(projectName, data) {
|
|
2473
2635
|
return `## Codebase Intelligence (Auto-generated by Argus)
|
|
@@ -2698,7 +2860,7 @@ program.command("setup [path]").description("Set up Argus for a project (snapsho
|
|
|
2698
2860
|
let projectConfig = onboardingConfig.projects[projectPath];
|
|
2699
2861
|
if (!projectConfig && opts.onboarding !== false) {
|
|
2700
2862
|
try {
|
|
2701
|
-
projectConfig = await runProjectOnboarding(projectPath, onboardingConfig, fs,
|
|
2863
|
+
projectConfig = await runProjectOnboarding(projectPath, onboardingConfig, fs, path2);
|
|
2702
2864
|
config.onboarding = config.onboarding || DEFAULT_ONBOARDING_CONFIG;
|
|
2703
2865
|
config.onboarding.projects[projectPath] = projectConfig;
|
|
2704
2866
|
saveConfig(config);
|
|
@@ -2708,7 +2870,7 @@ program.command("setup [path]").description("Set up Argus for a project (snapsho
|
|
|
2708
2870
|
}
|
|
2709
2871
|
} catch {
|
|
2710
2872
|
console.log("\n\u26A0\uFE0F Interactive selection skipped (non-interactive terminal)");
|
|
2711
|
-
const detected = detectPotentialKeyFiles(projectPath, onboardingConfig.globalKeyPatterns, fs,
|
|
2873
|
+
const detected = detectPotentialKeyFiles(projectPath, onboardingConfig.globalKeyPatterns, fs, path2);
|
|
2712
2874
|
projectConfig = {
|
|
2713
2875
|
keyFiles: detected.filter((d) => d.matchedPattern).map((d) => d.path),
|
|
2714
2876
|
customPatterns: [],
|
|
@@ -2719,12 +2881,15 @@ program.command("setup [path]").description("Set up Argus for a project (snapsho
|
|
|
2719
2881
|
console.log(`\u2713 Using existing project configuration (${projectConfig.keyFiles.length} key files)`);
|
|
2720
2882
|
}
|
|
2721
2883
|
const snapshotPath = join4(argusDir, "snapshot.txt");
|
|
2722
|
-
console.log("\n\u{1F4F8} Creating codebase snapshot...");
|
|
2723
|
-
const result =
|
|
2884
|
+
console.log("\n\u{1F4F8} Creating codebase snapshot (enhanced)...");
|
|
2885
|
+
const result = createEnhancedSnapshot(projectPath, snapshotPath, {
|
|
2724
2886
|
extensions: config.defaults.snapshotExtensions,
|
|
2725
2887
|
excludePatterns: config.defaults.excludePatterns
|
|
2726
2888
|
});
|
|
2727
2889
|
console.log(`\u2705 Snapshot created: ${result.fileCount} files, ${result.totalLines.toLocaleString()} lines`);
|
|
2890
|
+
if ("metadata" in result) {
|
|
2891
|
+
console.log(` Imports: ${result.metadata.imports.length} | Exports: ${result.metadata.exports.length} | Symbols: ${Object.keys(result.metadata.symbolIndex).length}`);
|
|
2892
|
+
}
|
|
2728
2893
|
if (projectConfig && projectConfig.keyFiles.length > 0) {
|
|
2729
2894
|
const keyFilesPath = join4(argusDir, "key-files.json");
|
|
2730
2895
|
writeFileSync4(keyFilesPath, JSON.stringify({
|
|
@@ -2792,5 +2957,81 @@ ${CLAUDE_MD_ARGUS_SECTION}`;
|
|
|
2792
2957
|
}
|
|
2793
2958
|
}
|
|
2794
2959
|
});
|
|
2960
|
+
program.command("ui").description("Open the Argus web UI for codebase visualization").option("-p, --port <port>", "Port to serve on", "3333").option("--no-open", "Do not open browser automatically").action(async (opts) => {
|
|
2961
|
+
const uiPath = join4(__dirname, "..", "packages", "ui");
|
|
2962
|
+
if (!existsSync4(join4(uiPath, "package.json"))) {
|
|
2963
|
+
console.error("Argus UI package not found.");
|
|
2964
|
+
console.error("\nThe UI package needs to be installed separately:");
|
|
2965
|
+
console.error(" cd packages/ui && npm install && npm run build");
|
|
2966
|
+
process.exit(1);
|
|
2967
|
+
}
|
|
2968
|
+
const distPath = join4(uiPath, "dist");
|
|
2969
|
+
const hasBuiltUI = existsSync4(distPath);
|
|
2970
|
+
console.log("Starting Argus UI...\n");
|
|
2971
|
+
try {
|
|
2972
|
+
if (hasBuiltUI) {
|
|
2973
|
+
console.log(` Serving built UI from ${distPath}`);
|
|
2974
|
+
console.log(` Open http://localhost:${opts.port} in your browser`);
|
|
2975
|
+
const http = await import("http");
|
|
2976
|
+
const mimeTypes = {
|
|
2977
|
+
".html": "text/html",
|
|
2978
|
+
".js": "text/javascript",
|
|
2979
|
+
".css": "text/css",
|
|
2980
|
+
".json": "application/json",
|
|
2981
|
+
".png": "image/png",
|
|
2982
|
+
".svg": "image/svg+xml"
|
|
2983
|
+
};
|
|
2984
|
+
const server = http.createServer((req, res) => {
|
|
2985
|
+
let filePath = join4(distPath, req.url === "/" ? "index.html" : req.url || "");
|
|
2986
|
+
if (!existsSync4(filePath) && !filePath.includes(".")) {
|
|
2987
|
+
filePath = join4(distPath, "index.html");
|
|
2988
|
+
}
|
|
2989
|
+
if (existsSync4(filePath)) {
|
|
2990
|
+
const ext = path2.extname(filePath);
|
|
2991
|
+
const contentType = mimeTypes[ext] || "application/octet-stream";
|
|
2992
|
+
const content = readFileSync5(filePath);
|
|
2993
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
2994
|
+
res.end(content);
|
|
2995
|
+
} else {
|
|
2996
|
+
res.writeHead(404);
|
|
2997
|
+
res.end("Not found");
|
|
2998
|
+
}
|
|
2999
|
+
});
|
|
3000
|
+
const port = parseInt(opts.port, 10);
|
|
3001
|
+
server.listen(port, () => {
|
|
3002
|
+
console.log(`
|
|
3003
|
+
Argus UI running at http://localhost:${port}`);
|
|
3004
|
+
if (opts.open !== false) {
|
|
3005
|
+
const { spawn } = __require("child_process");
|
|
3006
|
+
const openUrl = `http://localhost:${port}`;
|
|
3007
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
3008
|
+
spawn(openCmd, [openUrl], { detached: true, stdio: "ignore" }).unref();
|
|
3009
|
+
}
|
|
3010
|
+
});
|
|
3011
|
+
process.on("SIGINT", () => {
|
|
3012
|
+
console.log("\n\nShutting down Argus UI...");
|
|
3013
|
+
server.close();
|
|
3014
|
+
process.exit(0);
|
|
3015
|
+
});
|
|
3016
|
+
} else {
|
|
3017
|
+
console.log(` Running development server...`);
|
|
3018
|
+
console.log(` Port: ${opts.port}`);
|
|
3019
|
+
const { spawn } = __require("child_process");
|
|
3020
|
+
const vite = spawn("npm", ["run", "dev", "--", "--port", opts.port], {
|
|
3021
|
+
cwd: uiPath,
|
|
3022
|
+
stdio: "inherit"
|
|
3023
|
+
});
|
|
3024
|
+
process.on("SIGINT", () => {
|
|
3025
|
+
vite.kill();
|
|
3026
|
+
process.exit(0);
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
} catch (error) {
|
|
3030
|
+
console.error("Failed to start UI server:", error);
|
|
3031
|
+
console.error("\nTry building the UI first:");
|
|
3032
|
+
console.error(" cd packages/ui && npm install && npm run build");
|
|
3033
|
+
process.exit(1);
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
2795
3036
|
program.parse();
|
|
2796
3037
|
//# sourceMappingURL=cli.mjs.map
|