@aashwalayan/repolens 1.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/LICENSE +0 -0
- package/README.md +3 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +89 -0
- package/dist/cli.js.map +1 -0
- package/dist/cliConfig.d.ts +8 -0
- package/dist/cliConfig.d.ts.map +1 -0
- package/dist/cliConfig.js +31 -0
- package/dist/cliConfig.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/chainTracer.d.ts +20 -0
- package/dist/lib/chainTracer.d.ts.map +1 -0
- package/dist/lib/chainTracer.js +51 -0
- package/dist/lib/chainTracer.js.map +1 -0
- package/dist/lib/entryPoints.d.ts +4 -0
- package/dist/lib/entryPoints.d.ts.map +1 -0
- package/dist/lib/entryPoints.js +78 -0
- package/dist/lib/entryPoints.js.map +1 -0
- package/dist/lib/graphBuilder.d.ts +7 -0
- package/dist/lib/graphBuilder.d.ts.map +1 -0
- package/dist/lib/graphBuilder.js +131 -0
- package/dist/lib/graphBuilder.js.map +1 -0
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +43 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/render.d.ts +3 -0
- package/dist/lib/render.d.ts.map +1 -0
- package/dist/lib/render.js +66 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/types.d.ts +20 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/push.d.ts +8 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +40 -0
- package/dist/push.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +31 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/getRepoContext.d.ts +15 -0
- package/dist/tools/getRepoContext.d.ts.map +1 -0
- package/dist/tools/getRepoContext.js +33 -0
- package/dist/tools/getRepoContext.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
File without changes
|
package/README.md
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { resolveConfig } from './cliConfig.js';
|
|
4
|
+
import { pushOnce } from './push.js';
|
|
5
|
+
const command = process.argv[2];
|
|
6
|
+
async function runPush() {
|
|
7
|
+
const config = resolveConfig();
|
|
8
|
+
console.log(`Pushing ${config.repoName}...`);
|
|
9
|
+
const result = await pushOnce(config);
|
|
10
|
+
if (result.ok) {
|
|
11
|
+
console.log(`✓ Pushed. Download: ${result.downloadUrl}`);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
console.error(`✗ Push failed: ${result.error}`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function runWatch() {
|
|
19
|
+
const config = resolveConfig();
|
|
20
|
+
console.log(`Watching ${config.repoName} for changes (Ctrl+C to stop)...`);
|
|
21
|
+
let pending = false;
|
|
22
|
+
let running = false;
|
|
23
|
+
async function triggerPush() {
|
|
24
|
+
if (running) {
|
|
25
|
+
pending = true; // a change came in while a push was in flight — run once more after
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
running = true;
|
|
29
|
+
const result = await pushOnce(config);
|
|
30
|
+
if (result.ok) {
|
|
31
|
+
console.log(`✓ Pushed (${new Date().toLocaleTimeString()}). Download: ${result.downloadUrl}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.error(`✗ Push failed: ${result.error}`);
|
|
35
|
+
}
|
|
36
|
+
running = false;
|
|
37
|
+
if (pending) {
|
|
38
|
+
pending = false;
|
|
39
|
+
triggerPush();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const watcher = chokidar.watch(config.rootDir, {
|
|
43
|
+
ignored: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
|
|
44
|
+
ignoreInitial: true,
|
|
45
|
+
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
|
|
46
|
+
});
|
|
47
|
+
watcher.on('change', () => triggerPush());
|
|
48
|
+
// initial push on startup so the map is fresh before the first edit
|
|
49
|
+
triggerPush();
|
|
50
|
+
}
|
|
51
|
+
async function runMcp() {
|
|
52
|
+
try {
|
|
53
|
+
// Dynamic imports here — these pull in @modelcontextprotocol/sdk, which
|
|
54
|
+
// push/watch users don't need, so keep it out of the eagerly-loaded path.
|
|
55
|
+
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
56
|
+
const { createServer } = await import('./server.js');
|
|
57
|
+
const server = createServer();
|
|
58
|
+
const transport = new StdioServerTransport();
|
|
59
|
+
await server.connect(transport);
|
|
60
|
+
console.error('RepoLens MCP server running on stdio');
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error('[repolens mcp] Fatal error during startup:', err);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
process.on('uncaughtException', (err) => {
|
|
68
|
+
console.error('[repolens mcp] Uncaught exception:', err);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
71
|
+
process.on('unhandledRejection', (err) => {
|
|
72
|
+
console.error('[repolens mcp] Unhandled rejection:', err);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
});
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'push':
|
|
77
|
+
runPush();
|
|
78
|
+
break;
|
|
79
|
+
case 'watch':
|
|
80
|
+
runWatch();
|
|
81
|
+
break;
|
|
82
|
+
case 'mcp':
|
|
83
|
+
runMcp();
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
console.log('Usage: repolens <push|watch|mcp>');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliConfig.d.ts","sourceRoot":"","sources":["../src/cliConfig.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAeD,wBAAgB,aAAa,CAAC,OAAO,GAAE,MAAsB,GAAG,SAAS,CAexE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { basename, join } from 'path';
|
|
3
|
+
function detectRepoName(rootDir) {
|
|
4
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
5
|
+
if (existsSync(pkgPath)) {
|
|
6
|
+
try {
|
|
7
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
8
|
+
if (typeof pkg.name === 'string' && pkg.name.trim())
|
|
9
|
+
return pkg.name;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// fall through to folder name
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return basename(rootDir);
|
|
16
|
+
}
|
|
17
|
+
export function resolveConfig(rootDir = process.cwd()) {
|
|
18
|
+
const apiKey = process.env.REPOLENS_KEY;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
console.error('Missing REPOLENS_KEY. Set it with:\n $env:REPOLENS_KEY = "your-key-here" (PowerShell)\n export REPOLENS_KEY=your-key-here (bash/zsh)');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const apiUrl = process.env.REPOLENS_API_URL || 'https://repolensmcp-production.up.railway.app';
|
|
24
|
+
return {
|
|
25
|
+
apiKey,
|
|
26
|
+
apiUrl,
|
|
27
|
+
repoName: detectRepoName(rootDir),
|
|
28
|
+
rootDir,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=cliConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliConfig.js","sourceRoot":"","sources":["../src/cliConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAStC,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YACvD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,OAAO,GAAG,CAAC,IAAI,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAO,GAAW,OAAO,CAAC,GAAG,EAAE;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,8IAA8I,CAAC,CAAC;QAC9J,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,+CAA+C,CAAC;IAE/F,OAAO;QACL,MAAM;QACN,MAAM;QACN,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC;QACjC,OAAO;KACR,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
async function main() {
|
|
4
|
+
const server = createServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
console.error("RepoLensMCP running on stdio");
|
|
8
|
+
}
|
|
9
|
+
main().catch((err) => {
|
|
10
|
+
console.error("Fatal error starting RepoLensMCP:", err);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAChD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Edge } from './types.js';
|
|
2
|
+
export interface ChainStep {
|
|
3
|
+
file: string;
|
|
4
|
+
symbol: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns every root-to-leaf path starting from `startFile`/`startSymbol`,
|
|
8
|
+
* following edges where fromFile+fromSymbol matches the current step.
|
|
9
|
+
* Cross-file edges only by default (see `crossFileOnly`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function traceChains(startFile: string, startSymbol: string, edges: Edge[], crossFileOnly?: boolean): ChainStep[][];
|
|
12
|
+
/**
|
|
13
|
+
* Symbols called from 3+ distinct locations — worth expanding even if
|
|
14
|
+
* they aren't entry points, since they're structurally significant "hubs".
|
|
15
|
+
*/
|
|
16
|
+
export declare function findHubSymbols(edges: Edge[], minCallers?: number): {
|
|
17
|
+
file: string;
|
|
18
|
+
symbol: string;
|
|
19
|
+
}[];
|
|
20
|
+
//# sourceMappingURL=chainTracer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chainTracer.d.ts","sourceRoot":"","sources":["../../src/lib/chainTracer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,UAAO,GACnB,SAAS,EAAE,EAAE,CAmCf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,SAAI,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAchG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const MAX_DEPTH = 6;
|
|
2
|
+
/**
|
|
3
|
+
* Returns every root-to-leaf path starting from `startFile`/`startSymbol`,
|
|
4
|
+
* following edges where fromFile+fromSymbol matches the current step.
|
|
5
|
+
* Cross-file edges only by default (see `crossFileOnly`).
|
|
6
|
+
*/
|
|
7
|
+
export function traceChains(startFile, startSymbol, edges, crossFileOnly = true) {
|
|
8
|
+
function walk(file, symbol, visited, depth) {
|
|
9
|
+
const key = `${file}#${symbol}`;
|
|
10
|
+
if (visited.has(key) || depth > MAX_DEPTH) {
|
|
11
|
+
return [[{ file, symbol: `${symbol} (stopped: cycle or depth limit)` }]];
|
|
12
|
+
}
|
|
13
|
+
const nextVisited = new Set(visited);
|
|
14
|
+
nextVisited.add(key);
|
|
15
|
+
let outgoing = edges.filter((e) => e.fromFile === file && e.fromSymbol === symbol);
|
|
16
|
+
if (crossFileOnly) {
|
|
17
|
+
outgoing = outgoing.filter((e) => e.toFile !== file);
|
|
18
|
+
}
|
|
19
|
+
if (outgoing.length === 0) {
|
|
20
|
+
return [[{ file, symbol }]];
|
|
21
|
+
}
|
|
22
|
+
const results = [];
|
|
23
|
+
for (const edge of outgoing) {
|
|
24
|
+
const subChains = walk(edge.toFile, edge.toSymbol, nextVisited, depth + 1);
|
|
25
|
+
for (const sub of subChains) {
|
|
26
|
+
results.push([{ file, symbol }, ...sub]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
return walk(startFile, startSymbol, new Set(), 0);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Symbols called from 3+ distinct locations — worth expanding even if
|
|
35
|
+
* they aren't entry points, since they're structurally significant "hubs".
|
|
36
|
+
*/
|
|
37
|
+
export function findHubSymbols(edges, minCallers = 3) {
|
|
38
|
+
const counts = new Map();
|
|
39
|
+
for (const edge of edges) {
|
|
40
|
+
const key = `${edge.toFile}#${edge.toSymbol}`;
|
|
41
|
+
const existing = counts.get(key);
|
|
42
|
+
if (existing) {
|
|
43
|
+
existing.count++;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
counts.set(key, { file: edge.toFile, symbol: edge.toSymbol, count: 1 });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return [...counts.values()].filter((c) => c.count >= minCallers).map(({ file, symbol }) => ({ file, symbol }));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=chainTracer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chainTracer.js","sourceRoot":"","sources":["../../src/lib/chainTracer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entryPoints.d.ts","sourceRoot":"","sources":["../../src/lib/entryPoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAc,MAAM,UAAU,CAAC;AAG/C,OAAQ,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgD1D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,IAAI,EAAE,GACZ,gBAAgB,EAAE,CAmCpB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
function findUnimportedFiles(files, edges) {
|
|
5
|
+
const imported = new Set(edges.map((e) => e.toFile));
|
|
6
|
+
return files.filter((f) => !imported.has(f));
|
|
7
|
+
}
|
|
8
|
+
function getConventionalEntryPoints(rootDir) {
|
|
9
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
10
|
+
if (!existsSync(pkgPath))
|
|
11
|
+
return [];
|
|
12
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
13
|
+
const entries = [];
|
|
14
|
+
if (typeof pkg.main === 'string')
|
|
15
|
+
entries.push(pkg.main);
|
|
16
|
+
if (pkg.bin) {
|
|
17
|
+
const bins = typeof pkg.bin === 'string' ? [pkg.bin] : Object.values(pkg.bin);
|
|
18
|
+
entries.push(...bins);
|
|
19
|
+
}
|
|
20
|
+
if (pkg.exports && typeof pkg.exports === 'object') {
|
|
21
|
+
for (const v of Object.values(pkg.exports)) {
|
|
22
|
+
if (typeof v === 'string')
|
|
23
|
+
entries.push(v);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Normalize to absolute-ish paths matching what ts-morph reports
|
|
27
|
+
return entries.map((e) => join(rootDir, e.replace(/^\.\//, '')));
|
|
28
|
+
}
|
|
29
|
+
function hasTopLevelSideEffect(file) {
|
|
30
|
+
const SIDE_EFFECT_PATTERN = /\.(listen|connect|start)\s*\(/;
|
|
31
|
+
return file.getStatements().some((stmt) => {
|
|
32
|
+
if (stmt.getKind() !== SyntaxKind.ExpressionStatement)
|
|
33
|
+
return false;
|
|
34
|
+
return SIDE_EFFECT_PATTERN.test(stmt.getText());
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function loadManualOverrides(rootDir) {
|
|
38
|
+
const configPath = join(rootDir, '.repolens.json');
|
|
39
|
+
if (!existsSync(configPath))
|
|
40
|
+
return [];
|
|
41
|
+
try {
|
|
42
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
43
|
+
return (config.entryPoints ?? []).map((p) => join(rootDir, p));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function detectEntryPoints(project, rootDir, files, edges) {
|
|
50
|
+
const unimported = findUnimportedFiles(files, edges);
|
|
51
|
+
const conventional = new Set(getConventionalEntryPoints(rootDir));
|
|
52
|
+
const sideEffecting = new Set(project.getSourceFiles().filter(hasTopLevelSideEffect).map((f) => f.getFilePath().toString()));
|
|
53
|
+
const manual = new Set(loadManualOverrides(rootDir));
|
|
54
|
+
const candidates = new Set([...unimported, ...manual]);
|
|
55
|
+
const scored = [...candidates].map((file) => {
|
|
56
|
+
const reasons = [];
|
|
57
|
+
let score = 0;
|
|
58
|
+
if (unimported.includes(file)) {
|
|
59
|
+
score += 1;
|
|
60
|
+
reasons.push('not imported by any other file');
|
|
61
|
+
}
|
|
62
|
+
if (conventional.has(file)) {
|
|
63
|
+
score += 2;
|
|
64
|
+
reasons.push('listed in package.json (main/bin/exports)');
|
|
65
|
+
}
|
|
66
|
+
if (sideEffecting.has(file)) {
|
|
67
|
+
score += 2;
|
|
68
|
+
reasons.push('has a top-level side effect (e.g. .listen/.start)');
|
|
69
|
+
}
|
|
70
|
+
if (manual.has(file)) {
|
|
71
|
+
score += 3;
|
|
72
|
+
reasons.push('manually specified in .repolens.json');
|
|
73
|
+
}
|
|
74
|
+
return { file, score, reasons };
|
|
75
|
+
});
|
|
76
|
+
return scored.sort((a, b) => b.score - a.score);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=entryPoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entryPoints.js","sourceRoot":"","sources":["../../src/lib/entryPoints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../src/lib/graphBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAgC,MAAM,UAAU,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAa,MAAM,YAAY,CAAC;AAwElD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAyC/E"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Project, SourceFile, SyntaxKind, Node } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Finds the nearest enclosing function/method/class name for a node,
|
|
4
|
+
* so a usage can be attributed to "who is doing the calling", not just
|
|
5
|
+
* "which file". Falls back to '' for top-level (module-scope) code.
|
|
6
|
+
*/
|
|
7
|
+
function getEnclosingSymbolName(node) {
|
|
8
|
+
const fn = node.getFirstAncestor((a) => a.getKind() === SyntaxKind.FunctionDeclaration ||
|
|
9
|
+
a.getKind() === SyntaxKind.MethodDeclaration ||
|
|
10
|
+
a.getKind() === SyntaxKind.ArrowFunction ||
|
|
11
|
+
a.getKind() === SyntaxKind.FunctionExpression);
|
|
12
|
+
if (!fn)
|
|
13
|
+
return '';
|
|
14
|
+
if (Node.isFunctionDeclaration(fn) || Node.isMethodDeclaration(fn)) {
|
|
15
|
+
return fn.getName() ?? '(anonymous)';
|
|
16
|
+
}
|
|
17
|
+
// Arrow function / function expression: try to grab the variable it's assigned to
|
|
18
|
+
const varDecl = fn.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
19
|
+
return varDecl?.getName() ?? '(anonymous)';
|
|
20
|
+
}
|
|
21
|
+
function classifyUsage(node) {
|
|
22
|
+
const parent = node.getParent();
|
|
23
|
+
if (!parent)
|
|
24
|
+
return null;
|
|
25
|
+
if (Node.isCallExpression(parent) && parent.getExpression() === node)
|
|
26
|
+
return 'call';
|
|
27
|
+
if (Node.isNewExpression(parent) && parent.getExpression() === node)
|
|
28
|
+
return 'instantiation';
|
|
29
|
+
return 'reference';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a CommonJS require() specifier (e.g. './foo', '../lib/bar') to an
|
|
33
|
+
* actual SourceFile already in the project. Node's require() resolution tries,
|
|
34
|
+
* in order: exact match, +.js, +.jsx, +.ts, +.tsx, then an /index file of each.
|
|
35
|
+
* Only handles relative specifiers — bare specifiers ('express') are external
|
|
36
|
+
* packages and skipped, same as ES imports.
|
|
37
|
+
*/
|
|
38
|
+
function resolveRequireTarget(fromFile, specifier, project) {
|
|
39
|
+
if (!specifier.startsWith('.'))
|
|
40
|
+
return undefined; // external package
|
|
41
|
+
const dir = fromFile.getDirectoryPath();
|
|
42
|
+
const base = `${dir}/${specifier}`.replace(/\/\.\//g, '/');
|
|
43
|
+
const candidates = [
|
|
44
|
+
base,
|
|
45
|
+
`${base}.js`, `${base}.jsx`, `${base}.ts`, `${base}.tsx`,
|
|
46
|
+
`${base}/index.js`, `${base}/index.jsx`, `${base}/index.ts`, `${base}/index.tsx`,
|
|
47
|
+
];
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
const found = project.getSourceFile((f) => f.getFilePath() === candidate);
|
|
50
|
+
if (found)
|
|
51
|
+
return found;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The names a require() call actually binds, given how its result is used. */
|
|
56
|
+
function getRequiredNames(callExpr) {
|
|
57
|
+
const varDecl = callExpr.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
58
|
+
if (!varDecl)
|
|
59
|
+
return []; // bare `require('./x')` with no binding — side-effect only, no symbol usage to trace
|
|
60
|
+
const nameNode = varDecl.getNameNode();
|
|
61
|
+
if (Node.isObjectBindingPattern(nameNode)) {
|
|
62
|
+
// const { a, b } = require('./x')
|
|
63
|
+
return nameNode.getElements().map((el) => el.getName());
|
|
64
|
+
}
|
|
65
|
+
// const x = require('./x')
|
|
66
|
+
return [varDecl.getName()];
|
|
67
|
+
}
|
|
68
|
+
export function buildGraph(project) {
|
|
69
|
+
const edges = [];
|
|
70
|
+
const sourceFiles = project.getSourceFiles();
|
|
71
|
+
const files = sourceFiles.map((f) => f.getFilePath());
|
|
72
|
+
for (const file of sourceFiles) {
|
|
73
|
+
// --- ES module imports ---
|
|
74
|
+
for (const importDecl of file.getImportDeclarations()) {
|
|
75
|
+
const resolved = importDecl.getModuleSpecifierSourceFile();
|
|
76
|
+
if (!resolved)
|
|
77
|
+
continue; // external package (node_modules) — skip for now
|
|
78
|
+
const namedImports = importDecl.getNamedImports().map((n) => n.getName());
|
|
79
|
+
const defaultImport = importDecl.getDefaultImport()?.getText();
|
|
80
|
+
const importedNames = [...namedImports, ...(defaultImport ? [defaultImport] : [])];
|
|
81
|
+
for (const name of importedNames) {
|
|
82
|
+
recordUsages(file, name, resolved, edges);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// --- CommonJS require() calls ---
|
|
86
|
+
file.forEachDescendant((node) => {
|
|
87
|
+
if (!Node.isCallExpression(node))
|
|
88
|
+
return;
|
|
89
|
+
const expr = node.getExpression();
|
|
90
|
+
if (!Node.isIdentifier(expr) || expr.getText() !== 'require')
|
|
91
|
+
return;
|
|
92
|
+
const args = node.getArguments();
|
|
93
|
+
if (args.length !== 1 || !Node.isStringLiteral(args[0]))
|
|
94
|
+
return;
|
|
95
|
+
const specifier = args[0].getLiteralValue();
|
|
96
|
+
const resolved = resolveRequireTarget(file, specifier, project);
|
|
97
|
+
if (!resolved)
|
|
98
|
+
return; // external package or unresolvable — skip
|
|
99
|
+
const requiredNames = getRequiredNames(node);
|
|
100
|
+
for (const name of requiredNames) {
|
|
101
|
+
recordUsages(file, name, resolved, edges);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return { files, edges };
|
|
106
|
+
}
|
|
107
|
+
/** Finds every usage of `name` in `file` (excluding the import/require site itself) and records an edge for each. */
|
|
108
|
+
function recordUsages(file, name, resolved, edges) {
|
|
109
|
+
file.forEachDescendant((node) => {
|
|
110
|
+
if (!Node.isIdentifier(node) || node.getText() !== name)
|
|
111
|
+
return;
|
|
112
|
+
// skip the import declaration / require call itself
|
|
113
|
+
if (node.getFirstAncestorByKind(SyntaxKind.ImportDeclaration))
|
|
114
|
+
return;
|
|
115
|
+
const varDecl = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
|
|
116
|
+
if (varDecl && varDecl.getInitializer()?.getText().includes('require('))
|
|
117
|
+
return;
|
|
118
|
+
const usageType = classifyUsage(node);
|
|
119
|
+
if (!usageType)
|
|
120
|
+
return;
|
|
121
|
+
edges.push({
|
|
122
|
+
fromFile: file.getFilePath(),
|
|
123
|
+
fromSymbol: getEnclosingSymbolName(node),
|
|
124
|
+
toFile: resolved.getFilePath(),
|
|
125
|
+
toSymbol: name,
|
|
126
|
+
usageType,
|
|
127
|
+
line: node.getStartLineNumber(),
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=graphBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphBuilder.js","sourceRoot":"","sources":["../../src/lib/graphBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA4ChE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
import { writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { buildGraph } from './graphBuilder.js';
|
|
5
|
+
import { detectEntryPoints } from './entryPoints.js';
|
|
6
|
+
import { renderMarkdown } from './render.js';
|
|
7
|
+
export function generateRepoMap(options) {
|
|
8
|
+
const { rootDir, include = ['**/*.{ts,tsx,js,jsx}'], outFile } = options;
|
|
9
|
+
const project = new Project({
|
|
10
|
+
skipAddingFilesFromTsConfig: true,
|
|
11
|
+
...(options.tsConfigFilePath ? { tsConfigFilePath: options.tsConfigFilePath } : {}),
|
|
12
|
+
compilerOptions: {
|
|
13
|
+
// Most JS projects have no tsconfig.json at all, and even ones that do
|
|
14
|
+
// may not set allowJs — force it so .js/.jsx files actually get parsed
|
|
15
|
+
// instead of silently skipped. checkJs stays off: we only need the
|
|
16
|
+
// syntax tree (imports, functions, calls), not real type-checking.
|
|
17
|
+
allowJs: true,
|
|
18
|
+
checkJs: false,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
for (const pattern of include) {
|
|
22
|
+
project.addSourceFilesAtPaths(join(rootDir, pattern));
|
|
23
|
+
}
|
|
24
|
+
// Strip anything that got swept in from node_modules — addSourceFilesAtPaths
|
|
25
|
+
// globs the filesystem directly and doesn't respect .gitignore or tsconfig excludes.
|
|
26
|
+
for (const file of project.getSourceFiles()) {
|
|
27
|
+
const path = file.getFilePath();
|
|
28
|
+
if (path.includes('node_modules') || path.includes('/dist/') || path.includes('\\dist\\')) {
|
|
29
|
+
project.removeSourceFile(file);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
console.log(`[repolens] scanning ${project.getSourceFiles().length} files after exclusions`);
|
|
33
|
+
const { files, edges } = buildGraph(project);
|
|
34
|
+
const entryPoints = detectEntryPoints(project, rootDir, files, edges);
|
|
35
|
+
const graph = { files, edges, entryPoints };
|
|
36
|
+
const markdown = renderMarkdown(rootDir, graph);
|
|
37
|
+
console.log(`[repolens] generated markdown: ${markdown.length} chars, ${files.length} files, ${edges.length} edges`);
|
|
38
|
+
if (outFile) {
|
|
39
|
+
writeFileSync(outFile, markdown, 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
return markdown;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAU7C,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEzE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;QAC1B,2BAA2B,EAAE,IAAI;QACjC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,eAAe,EAAE;YACf,uEAAuE;YACvE,uEAAuE;YACvE,mEAAmE;YACnE,mEAAmE;YACnE,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,KAAK;SACf;KACF,CAAC,CAAC;IAEH,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,6EAA6E;IAC7E,qFAAqF;IACrF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1F,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,yBAAyB,CAAC,CAAC;IAE7F,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtE,MAAM,KAAK,GAAc,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IACvD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,kCAAkC,QAAQ,CAAC,MAAM,WAAW,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;IAErH,IAAI,OAAO,EAAE,CAAC;QACZ,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAY5C,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CA+DxE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { relative } from 'path';
|
|
2
|
+
import { traceChains, findHubSymbols } from './chainTracer.js';
|
|
3
|
+
function rel(rootDir, file) {
|
|
4
|
+
return relative(rootDir, file).replace(/\\/g, '/'); // normalize Windows paths
|
|
5
|
+
}
|
|
6
|
+
function renderChain(rootDir, chain) {
|
|
7
|
+
return chain.map((step) => `${step.symbol || '(module)'} (${rel(rootDir, step.file)})`).join(' → ');
|
|
8
|
+
}
|
|
9
|
+
export function renderMarkdown(rootDir, graph) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push(`# Repo Map`);
|
|
12
|
+
lines.push('');
|
|
13
|
+
lines.push(`Generated from ${graph.files.length} files, ${graph.edges.length} tracked connections.`);
|
|
14
|
+
lines.push('');
|
|
15
|
+
// --- Entry points ---
|
|
16
|
+
lines.push('## Entry points');
|
|
17
|
+
lines.push('');
|
|
18
|
+
const topEntries = graph.entryPoints.filter((e) => e.score > 0).slice(0, 10);
|
|
19
|
+
if (topEntries.length === 0) {
|
|
20
|
+
lines.push('_No confident entry points detected — consider adding a `.repolens.json` override._');
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
for (const entry of topEntries) {
|
|
24
|
+
lines.push(`- \`${rel(rootDir, entry.file)}\` (score: ${entry.score}) — ${entry.reasons.join('; ')}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
lines.push('');
|
|
28
|
+
// --- Direct connections, grouped by file ---
|
|
29
|
+
lines.push('## Direct connections');
|
|
30
|
+
lines.push('');
|
|
31
|
+
const byFile = new Map();
|
|
32
|
+
for (const edge of graph.edges) {
|
|
33
|
+
const list = byFile.get(edge.fromFile) ?? [];
|
|
34
|
+
list.push(edge);
|
|
35
|
+
byFile.set(edge.fromFile, list);
|
|
36
|
+
}
|
|
37
|
+
for (const [file, edges] of byFile) {
|
|
38
|
+
lines.push(`### \`${rel(rootDir, file)}\``);
|
|
39
|
+
for (const edge of edges) {
|
|
40
|
+
const who = edge.fromSymbol ? `\`${edge.fromSymbol}\`` : 'module top-level';
|
|
41
|
+
lines.push(`- ${who} ${edge.usageType}s \`${edge.toSymbol}\` from \`${rel(rootDir, edge.toFile)}\` (line ${edge.line})`);
|
|
42
|
+
}
|
|
43
|
+
lines.push('');
|
|
44
|
+
}
|
|
45
|
+
// --- Transitive chains from entry points + hub symbols ---
|
|
46
|
+
lines.push('## Call chains from entry points');
|
|
47
|
+
lines.push('');
|
|
48
|
+
const hubs = findHubSymbols(graph.edges);
|
|
49
|
+
const startingPoints = [
|
|
50
|
+
...topEntries.slice(0, 5).map((e) => ({ file: e.file, symbol: '' })),
|
|
51
|
+
...hubs,
|
|
52
|
+
];
|
|
53
|
+
for (const start of startingPoints) {
|
|
54
|
+
const chains = traceChains(start.file, start.symbol, graph.edges);
|
|
55
|
+
const meaningful = chains.filter((c) => c.length > 1);
|
|
56
|
+
if (meaningful.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
lines.push(`### ${start.symbol || '(module)'} — \`${rel(rootDir, start.file)}\``);
|
|
59
|
+
for (const chain of meaningful) {
|
|
60
|
+
lines.push(`- ${renderChain(rootDir, chain)}`);
|
|
61
|
+
}
|
|
62
|
+
lines.push('');
|
|
63
|
+
}
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type UsageType = 'call' | 'instantiation' | 'reference';
|
|
2
|
+
export interface Edge {
|
|
3
|
+
fromFile: string;
|
|
4
|
+
fromSymbol: string;
|
|
5
|
+
toFile: string;
|
|
6
|
+
toSymbol: string;
|
|
7
|
+
usageType: UsageType;
|
|
8
|
+
line: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ScoredEntryPoint {
|
|
11
|
+
file: string;
|
|
12
|
+
score: number;
|
|
13
|
+
reasons: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface RepoGraph {
|
|
16
|
+
files: string[];
|
|
17
|
+
edges: Edge[];
|
|
18
|
+
entryPoints: ScoredEntryPoint[];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,eAAe,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,IAAI;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
|
package/dist/push.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAsCtE"}
|
package/dist/push.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { generateRepoMap } from './lib/index.js';
|
|
4
|
+
export async function pushOnce(config) {
|
|
5
|
+
const tsConfigPath = join(config.rootDir, 'tsconfig.json');
|
|
6
|
+
const markdown = generateRepoMap({
|
|
7
|
+
rootDir: config.rootDir,
|
|
8
|
+
...(existsSync(tsConfigPath) ? { tsConfigFilePath: tsConfigPath } : {}),
|
|
9
|
+
});
|
|
10
|
+
const fileCount = (markdown.match(/^### `/gm) ?? []).length; // rough count from render.ts's output shape
|
|
11
|
+
try {
|
|
12
|
+
const res = await fetch(`${config.apiUrl}/api/push`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
repoName: config.repoName,
|
|
20
|
+
markdown,
|
|
21
|
+
fileCount,
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const body = (await res.json().catch(() => ({})));
|
|
26
|
+
return { ok: false, error: `${res.status} ${res.statusText}: ${body.error ?? 'unknown error'}` };
|
|
27
|
+
}
|
|
28
|
+
const body = (await res.json());
|
|
29
|
+
return { ok: true, downloadUrl: `${config.apiUrl}${body.downloadUrl}` };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const cause = err instanceof Error && 'cause' in err ? err.cause : undefined;
|
|
33
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
34
|
+
console.error('[repolens push] Raw fetch error:', err);
|
|
35
|
+
if (cause)
|
|
36
|
+
console.error('[repolens push] Cause:', cause);
|
|
37
|
+
return { ok: false, error: cause ? `${detail} (${String(cause)})` : detail };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=push.js.map
|
package/dist/push.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAcjD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAiB;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAE3D,MAAM,QAAQ,GAAG,eAAe,CAAC;QAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxE,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,4CAA4C;IAEzG,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,WAAW,EAAE;YACnD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ;gBACR,SAAS;aACV,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAiB,CAAC;YAClE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,KAAK,IAAI,eAAe,EAAE,EAAE,CAAC;QACnG,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAiB,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,IAAI,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;QACvD,IAAI,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/E,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,YAAY,cAmC3B"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getRepoContext } from "./tools/getRepoContext.js";
|
|
4
|
+
export function createServer() {
|
|
5
|
+
const server = new McpServer({
|
|
6
|
+
name: "RepoLensMCP",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
});
|
|
9
|
+
server.registerTool("getRepoContext", {
|
|
10
|
+
title: "Get Repo Context",
|
|
11
|
+
description: "Returns a structured markdown map of a repo you've previously pushed with " +
|
|
12
|
+
"`repolens push` — includes entry points, file relationships, and call chains " +
|
|
13
|
+
"so an LLM can understand the codebase before reading individual files.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
repoName: z.string().describe("The repo name as it appears in your pushed repos (matches package.json's 'name' field, or the folder name)"),
|
|
16
|
+
},
|
|
17
|
+
}, async ({ repoName }) => {
|
|
18
|
+
const result = await getRepoContext({ repoName });
|
|
19
|
+
if (!result.success) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: result.markdown ?? "" }],
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
return server;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,4EAA4E;YAC5E,+EAA+E;YAC/E,wEAAwE;QAC1E,WAAW,EAAE;YACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4GAA4G,CAAC;SAC5I;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAElD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC3D,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface GetRepoContextInput {
|
|
2
|
+
repoName: string;
|
|
3
|
+
}
|
|
4
|
+
export interface GetRepoContextResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
markdown?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The MCP tool's core logic. Fetches the ALREADY-GENERATED repo map from the
|
|
11
|
+
* backend (pushed there earlier by `repolens push`/`watch`) — this tool does
|
|
12
|
+
* NOT clone or analyze anything itself anymore.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getRepoContext(input: GetRepoContextInput): Promise<GetRepoContextResult>;
|
|
15
|
+
//# sourceMappingURL=getRepoContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRepoContext.d.ts","sourceRoot":"","sources":["../../src/tools/getRepoContext.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0B9F"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const API_URL = process.env.REPOLENS_API_URL || 'https://repolensmcp-production.up.railway.app';
|
|
2
|
+
const API_KEY = process.env.REPOLENS_KEY;
|
|
3
|
+
/**
|
|
4
|
+
* The MCP tool's core logic. Fetches the ALREADY-GENERATED repo map from the
|
|
5
|
+
* backend (pushed there earlier by `repolens push`/`watch`) — this tool does
|
|
6
|
+
* NOT clone or analyze anything itself anymore.
|
|
7
|
+
*/
|
|
8
|
+
export async function getRepoContext(input) {
|
|
9
|
+
if (!API_KEY) {
|
|
10
|
+
return {
|
|
11
|
+
success: false,
|
|
12
|
+
error: 'REPOLENS_KEY is not set. Configure it in your MCP server environment.',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(`${API_URL}/api/context/${encodeURIComponent(input.repoName)}`, {
|
|
17
|
+
headers: { Authorization: `Bearer ${API_KEY}` },
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const body = (await res.json().catch(() => ({})));
|
|
21
|
+
return { success: false, error: body.error ?? `Request failed with status ${res.status}` };
|
|
22
|
+
}
|
|
23
|
+
const body = (await res.json());
|
|
24
|
+
return { success: true, markdown: body.markdown };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: err instanceof Error ? err.message : 'Unknown error fetching repo context',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=getRepoContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRepoContext.js","sourceRoot":"","sources":["../../src/tools/getRepoContext.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,+CAA+C,CAAC;AAChG,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAA0B;IAC7D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,uEAAuE;SAC/E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,gBAAgB,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE;YACtF,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,EAAE,EAAE;SAChD,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAuB,CAAC;YACxE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,8BAA8B,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QAC7F,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;QACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,qCAAqC;SAClF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aashwalayan/repolens",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Turn any repo into an LLM-readable map — entry points, call chains, and cross-file relationships, via CLI or MCP.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"bin": {
|
|
8
|
+
"repolens": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"dev": "tsx watch src/server.ts",
|
|
17
|
+
"repolens": "tsx src/cli.ts"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"llm",
|
|
22
|
+
"code-analysis",
|
|
23
|
+
"repo-map"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
28
|
+
"chokidar": "^5.0.0",
|
|
29
|
+
"dotenv": "^17.4.2",
|
|
30
|
+
"ts-morph": "^28.0.0",
|
|
31
|
+
"zod": "^4.5.4"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^26.5.0",
|
|
35
|
+
"tsx": "^4.23.13",
|
|
36
|
+
"typescript": "^7.0.2"
|
|
37
|
+
}
|
|
38
|
+
}
|