archtracker-mcp 0.6.0 → 0.7.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.ja.md +344 -0
- package/README.md +16 -321
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/cli/index.js +507 -1689
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +79 -17
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +507 -81
- package/dist/mcp/index.js.map +1 -1
- package/dist/web/viewer.js +1572 -0
- package/package.json +5 -3
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,12 @@ interface ArchSnapshot {
|
|
|
56
56
|
graph: DependencyGraph;
|
|
57
57
|
/** Present only when layers.json was used */
|
|
58
58
|
multiLayer?: MultiLayerGraph;
|
|
59
|
+
/** Analysis options used to generate this snapshot (for diff consistency checks) */
|
|
60
|
+
analysisOptions?: {
|
|
61
|
+
targetDir?: string;
|
|
62
|
+
language?: string;
|
|
63
|
+
exclude?: string[];
|
|
64
|
+
};
|
|
59
65
|
}
|
|
60
66
|
/** Diff result between two snapshots */
|
|
61
67
|
interface ArchDiff {
|
|
@@ -156,7 +162,7 @@ declare function formatAnalysisReport(graph: DependencyGraph, options?: {
|
|
|
156
162
|
* Each layer is analyzed independently via analyzeProject().
|
|
157
163
|
* Results are merged into a unified graph with layer-prefixed node paths.
|
|
158
164
|
*/
|
|
159
|
-
declare function analyzeMultiLayer(projectRoot: string, layerDefs: LayerDefinition[]): Promise<MultiLayerGraph>;
|
|
165
|
+
declare function analyzeMultiLayer(projectRoot: string, layerDefs: LayerDefinition[], globalExclude?: string[]): Promise<MultiLayerGraph>;
|
|
160
166
|
/**
|
|
161
167
|
* Auto-detect cross-layer connections by scanning file contents
|
|
162
168
|
* for references to identifiers defined in other layers.
|
|
@@ -180,7 +186,11 @@ declare function detectCrossLayerConnections(layers: Record<string, DependencyGr
|
|
|
180
186
|
* Creates .archtracker/ directory if it doesn't exist.
|
|
181
187
|
* Writes snapshot.json with schema version and timestamp.
|
|
182
188
|
*/
|
|
183
|
-
declare function saveSnapshot(projectRoot: string, graph: DependencyGraph, multiLayer?: MultiLayerGraph
|
|
189
|
+
declare function saveSnapshot(projectRoot: string, graph: DependencyGraph, multiLayer?: MultiLayerGraph, analysisOptions?: {
|
|
190
|
+
targetDir?: string;
|
|
191
|
+
language?: string;
|
|
192
|
+
exclude?: string[];
|
|
193
|
+
}): Promise<ArchSnapshot>;
|
|
184
194
|
/**
|
|
185
195
|
* Load the most recent snapshot from .archtracker/snapshot.json.
|
|
186
196
|
*
|
package/dist/index.js
CHANGED
|
@@ -1564,6 +1564,8 @@ var en = {
|
|
|
1564
1564
|
"diff.reasonRemoved": 'Dependency "{file}" was removed',
|
|
1565
1565
|
"diff.reasonModified": 'Dependency "{file}" had its dependencies changed',
|
|
1566
1566
|
"diff.reasonAdded": 'New dependency "{file}" was added',
|
|
1567
|
+
"diff.testSummary": " ... and {count} test/fixture file(s)",
|
|
1568
|
+
"diff.testAffectedSummary": " ... and {count} test/fixture-related review(s)",
|
|
1567
1569
|
// Search
|
|
1568
1570
|
"search.pathMatch": 'Path matches "{pattern}"',
|
|
1569
1571
|
"search.affected": 'May be affected by changes to "{file}" (via: {via})',
|
|
@@ -1625,6 +1627,12 @@ var en = {
|
|
|
1625
1627
|
"web.watching": "Watching {dir}/ for changes...",
|
|
1626
1628
|
"web.reloading": "File change detected, reloading...",
|
|
1627
1629
|
"web.reloaded": "Graph reloaded",
|
|
1630
|
+
// History
|
|
1631
|
+
"history.title": "# Snapshot History\n",
|
|
1632
|
+
"history.empty": "No snapshots found. Run `archtracker init` to create one.",
|
|
1633
|
+
"history.entry": " {ts} | {files} files, {edges} edges, {circular} circular{layers}",
|
|
1634
|
+
"history.count": "{count} snapshot(s) recorded",
|
|
1635
|
+
"history.snapshotNotFound": "Snapshot not found for timestamp: {ts}",
|
|
1628
1636
|
// Errors
|
|
1629
1637
|
"error.analyzer": "[Analysis Error] {message}",
|
|
1630
1638
|
"error.storage": "[Storage Error] {message}",
|
|
@@ -1657,6 +1665,8 @@ var ja = {
|
|
|
1657
1665
|
"diff.reasonRemoved": '\u4F9D\u5B58\u5148 "{file}" \u304C\u524A\u9664\u3055\u308C\u307E\u3057\u305F',
|
|
1658
1666
|
"diff.reasonModified": '\u4F9D\u5B58\u5148 "{file}" \u306E\u4F9D\u5B58\u95A2\u4FC2\u304C\u5909\u66F4\u3055\u308C\u307E\u3057\u305F',
|
|
1659
1667
|
"diff.reasonAdded": '\u65B0\u3057\u3044\u4F9D\u5B58\u5148 "{file}" \u304C\u8FFD\u52A0\u3055\u308C\u307E\u3057\u305F',
|
|
1668
|
+
"diff.testSummary": " ... \u4ED6 {count}\u4EF6\u306E\u30C6\u30B9\u30C8/\u30D5\u30A3\u30AF\u30B9\u30C1\u30E3\u30D5\u30A1\u30A4\u30EB",
|
|
1669
|
+
"diff.testAffectedSummary": " ... \u4ED6 {count}\u4EF6\u306E\u30C6\u30B9\u30C8/\u30D5\u30A3\u30AF\u30B9\u30C1\u30E3\u95A2\u9023\u306E\u78BA\u8A8D\u9805\u76EE",
|
|
1660
1670
|
// Search
|
|
1661
1671
|
"search.pathMatch": '\u30D1\u30B9\u304C "{pattern}" \u306B\u30DE\u30C3\u30C1',
|
|
1662
1672
|
"search.affected": '"{file}" \u306E\u5909\u66F4\u306B\u3088\u308A\u5F71\u97FF\u3092\u53D7\u3051\u308B\u53EF\u80FD\u6027\uFF08\u7D4C\u7531: {via}\uFF09',
|
|
@@ -1718,6 +1728,12 @@ var ja = {
|
|
|
1718
1728
|
"web.watching": "{dir}/ \u3092\u76E3\u8996\u4E2D...",
|
|
1719
1729
|
"web.reloading": "\u30D5\u30A1\u30A4\u30EB\u5909\u66F4\u3092\u691C\u51FA\u3001\u30EA\u30ED\u30FC\u30C9\u4E2D...",
|
|
1720
1730
|
"web.reloaded": "\u30B0\u30E9\u30D5\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
|
|
1731
|
+
// History
|
|
1732
|
+
"history.title": "# \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5C65\u6B74\n",
|
|
1733
|
+
"history.empty": "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002`archtracker init` \u3067\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
1734
|
+
"history.entry": " {ts} | {files}\u30D5\u30A1\u30A4\u30EB, {edges}\u30A8\u30C3\u30B8, \u5FAA\u74B0{circular}\u4EF6{layers}",
|
|
1735
|
+
"history.count": "{count}\u4EF6\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092\u8A18\u9332",
|
|
1736
|
+
"history.snapshotNotFound": "\u6307\u5B9A\u306E\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: {ts}",
|
|
1721
1737
|
// Errors
|
|
1722
1738
|
"error.analyzer": "[\u89E3\u6790\u30A8\u30E9\u30FC] {message}",
|
|
1723
1739
|
"error.storage": "[\u30B9\u30C8\u30EC\u30FC\u30B8\u30A8\u30E9\u30FC] {message}",
|
|
@@ -1802,14 +1818,14 @@ var LAYER_COLORS = [
|
|
|
1802
1818
|
"#ffa657",
|
|
1803
1819
|
"#7ee787"
|
|
1804
1820
|
];
|
|
1805
|
-
async function analyzeMultiLayer(projectRoot, layerDefs) {
|
|
1821
|
+
async function analyzeMultiLayer(projectRoot, layerDefs, globalExclude) {
|
|
1806
1822
|
const layers = {};
|
|
1807
1823
|
const layerMetadata = [];
|
|
1808
1824
|
for (let idx = 0; idx < layerDefs.length; idx++) {
|
|
1809
1825
|
const def = layerDefs[idx];
|
|
1810
1826
|
const targetDir = resolve4(projectRoot, def.targetDir);
|
|
1811
1827
|
const graph = await analyzeProject(targetDir, {
|
|
1812
|
-
exclude: def.exclude,
|
|
1828
|
+
exclude: def.exclude ?? globalExclude,
|
|
1813
1829
|
language: def.language
|
|
1814
1830
|
});
|
|
1815
1831
|
const language = def.language ?? await detectLanguage(targetDir) ?? "javascript";
|
|
@@ -2241,12 +2257,19 @@ function isNodeError(error) {
|
|
|
2241
2257
|
return error instanceof Error && "code" in error;
|
|
2242
2258
|
}
|
|
2243
2259
|
|
|
2260
|
+
// src/storage/graph-cache.ts
|
|
2261
|
+
import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, stat as stat4 } from "fs/promises";
|
|
2262
|
+
import { join as join6, resolve as resolve5 } from "path";
|
|
2263
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
2264
|
+
import { createHash } from "crypto";
|
|
2265
|
+
|
|
2244
2266
|
// src/storage/snapshot.ts
|
|
2245
|
-
import { mkdir as
|
|
2246
|
-
import { join as
|
|
2267
|
+
import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile4, readdir as readdir4, access } from "fs/promises";
|
|
2268
|
+
import { join as join7 } from "path";
|
|
2247
2269
|
import { z as z2 } from "zod";
|
|
2248
2270
|
var ARCHTRACKER_DIR2 = ".archtracker";
|
|
2249
2271
|
var SNAPSHOT_FILE = "snapshot.json";
|
|
2272
|
+
var HISTORY_DIR = "history";
|
|
2250
2273
|
var FileNodeSchema = z2.object({
|
|
2251
2274
|
path: z2.string(),
|
|
2252
2275
|
exists: z2.boolean(),
|
|
@@ -2271,25 +2294,34 @@ var SnapshotSchema = z2.object({
|
|
|
2271
2294
|
rootDir: z2.string(),
|
|
2272
2295
|
graph: DependencyGraphSchema
|
|
2273
2296
|
});
|
|
2274
|
-
async function saveSnapshot(projectRoot, graph, multiLayer) {
|
|
2275
|
-
const dirPath =
|
|
2276
|
-
const filePath =
|
|
2297
|
+
async function saveSnapshot(projectRoot, graph, multiLayer, analysisOptions) {
|
|
2298
|
+
const dirPath = join7(projectRoot, ARCHTRACKER_DIR2);
|
|
2299
|
+
const filePath = join7(dirPath, SNAPSHOT_FILE);
|
|
2277
2300
|
const snapshot = {
|
|
2278
2301
|
version: SCHEMA_VERSION,
|
|
2279
2302
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2280
2303
|
rootDir: graph.rootDir,
|
|
2281
2304
|
graph,
|
|
2282
|
-
...multiLayer ? { multiLayer } : {}
|
|
2305
|
+
...multiLayer ? { multiLayer } : {},
|
|
2306
|
+
...analysisOptions ? { analysisOptions } : {}
|
|
2283
2307
|
};
|
|
2284
|
-
await
|
|
2285
|
-
|
|
2308
|
+
await mkdir3(dirPath, { recursive: true });
|
|
2309
|
+
const json = JSON.stringify(snapshot, null, 2);
|
|
2310
|
+
await writeFile3(filePath, json, "utf-8");
|
|
2311
|
+
try {
|
|
2312
|
+
const historyPath = join7(dirPath, HISTORY_DIR);
|
|
2313
|
+
await mkdir3(historyPath, { recursive: true });
|
|
2314
|
+
const safeTs = snapshot.timestamp.replace(/[:.]/g, "-");
|
|
2315
|
+
await writeFile3(join7(historyPath, `${safeTs}.json`), json, "utf-8");
|
|
2316
|
+
} catch {
|
|
2317
|
+
}
|
|
2286
2318
|
return snapshot;
|
|
2287
2319
|
}
|
|
2288
2320
|
async function loadSnapshot(projectRoot) {
|
|
2289
|
-
const filePath =
|
|
2321
|
+
const filePath = join7(projectRoot, ARCHTRACKER_DIR2, SNAPSHOT_FILE);
|
|
2290
2322
|
let raw;
|
|
2291
2323
|
try {
|
|
2292
|
-
raw = await
|
|
2324
|
+
raw = await readFile4(filePath, "utf-8");
|
|
2293
2325
|
} catch (error) {
|
|
2294
2326
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
2295
2327
|
return null;
|
|
@@ -2318,7 +2350,7 @@ async function loadSnapshot(projectRoot) {
|
|
|
2318
2350
|
}
|
|
2319
2351
|
async function hasArchtrackerDir(projectRoot) {
|
|
2320
2352
|
try {
|
|
2321
|
-
await access(
|
|
2353
|
+
await access(join7(projectRoot, ARCHTRACKER_DIR2));
|
|
2322
2354
|
return true;
|
|
2323
2355
|
} catch {
|
|
2324
2356
|
return false;
|
|
@@ -2385,6 +2417,17 @@ function computeDiff(oldGraph, newGraph) {
|
|
|
2385
2417
|
}
|
|
2386
2418
|
return { added, removed, modified, affectedDependents };
|
|
2387
2419
|
}
|
|
2420
|
+
function isTestOrFixture(path) {
|
|
2421
|
+
return /(__fixtures__|__tests__|__mocks__|\.test\.|\.spec\.|\.e2e\.)/.test(path);
|
|
2422
|
+
}
|
|
2423
|
+
function partition(arr, pred) {
|
|
2424
|
+
const yes = [];
|
|
2425
|
+
const no = [];
|
|
2426
|
+
for (const item of arr) {
|
|
2427
|
+
(pred(item) ? yes : no).push(item);
|
|
2428
|
+
}
|
|
2429
|
+
return [yes, no];
|
|
2430
|
+
}
|
|
2388
2431
|
function formatDiffReport(diff) {
|
|
2389
2432
|
const lines = [];
|
|
2390
2433
|
lines.push(t("diff.title"));
|
|
@@ -2393,32 +2436,51 @@ function formatDiffReport(diff) {
|
|
|
2393
2436
|
return lines.join("\n");
|
|
2394
2437
|
}
|
|
2395
2438
|
if (diff.added.length > 0) {
|
|
2439
|
+
const [testFiles, srcFiles] = partition(diff.added, isTestOrFixture);
|
|
2396
2440
|
lines.push(t("diff.added", { count: diff.added.length }));
|
|
2397
|
-
for (const f of
|
|
2441
|
+
for (const f of srcFiles) {
|
|
2398
2442
|
lines.push(` + ${f}`);
|
|
2399
2443
|
}
|
|
2444
|
+
if (testFiles.length > 0) {
|
|
2445
|
+
lines.push(t("diff.testSummary", { count: testFiles.length }));
|
|
2446
|
+
}
|
|
2400
2447
|
lines.push("");
|
|
2401
2448
|
}
|
|
2402
2449
|
if (diff.removed.length > 0) {
|
|
2450
|
+
const [testFiles, srcFiles] = partition(diff.removed, isTestOrFixture);
|
|
2403
2451
|
lines.push(t("diff.removed", { count: diff.removed.length }));
|
|
2404
|
-
for (const f of
|
|
2452
|
+
for (const f of srcFiles) {
|
|
2405
2453
|
lines.push(` - ${f}`);
|
|
2406
2454
|
}
|
|
2455
|
+
if (testFiles.length > 0) {
|
|
2456
|
+
lines.push(t("diff.testSummary", { count: testFiles.length }));
|
|
2457
|
+
}
|
|
2407
2458
|
lines.push("");
|
|
2408
2459
|
}
|
|
2409
2460
|
if (diff.modified.length > 0) {
|
|
2461
|
+
const [testFiles, srcFiles] = partition(diff.modified, isTestOrFixture);
|
|
2410
2462
|
lines.push(t("diff.modified", { count: diff.modified.length }));
|
|
2411
|
-
for (const f of
|
|
2463
|
+
for (const f of srcFiles) {
|
|
2412
2464
|
lines.push(` ~ ${f}`);
|
|
2413
2465
|
}
|
|
2466
|
+
if (testFiles.length > 0) {
|
|
2467
|
+
lines.push(t("diff.testSummary", { count: testFiles.length }));
|
|
2468
|
+
}
|
|
2414
2469
|
lines.push("");
|
|
2415
2470
|
}
|
|
2416
2471
|
if (diff.affectedDependents.length > 0) {
|
|
2472
|
+
const [testEntries, srcEntries] = partition(
|
|
2473
|
+
diff.affectedDependents,
|
|
2474
|
+
(a) => isTestOrFixture(a.file) || isTestOrFixture(a.dependsOn)
|
|
2475
|
+
);
|
|
2417
2476
|
lines.push(t("diff.affected", { count: diff.affectedDependents.length }));
|
|
2418
|
-
for (const a of
|
|
2477
|
+
for (const a of srcEntries) {
|
|
2419
2478
|
lines.push(` ! ${a.file}`);
|
|
2420
2479
|
lines.push(` ${a.reason}`);
|
|
2421
2480
|
}
|
|
2481
|
+
if (testEntries.length > 0) {
|
|
2482
|
+
lines.push(t("diff.testAffectedSummary", { count: testEntries.length }));
|
|
2483
|
+
}
|
|
2422
2484
|
lines.push("");
|
|
2423
2485
|
}
|
|
2424
2486
|
return lines.join("\n");
|