@reactgraph/cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +319 -0
- package/bun.lock +527 -0
- package/dist/cli/components/IndexProgress.d.ts +18 -0
- package/dist/cli/components/IndexProgress.d.ts.map +1 -0
- package/dist/cli/components/IndexProgress.js +26 -0
- package/dist/cli/components/IndexProgress.js.map +1 -0
- package/dist/cli/components/InitResult.d.ts +7 -0
- package/dist/cli/components/InitResult.d.ts.map +1 -0
- package/dist/cli/components/InitResult.js +6 -0
- package/dist/cli/components/InitResult.js.map +1 -0
- package/dist/cli/index-cmd.d.ts +7 -0
- package/dist/cli/index-cmd.d.ts.map +1 -0
- package/dist/cli/index-cmd.js +28 -0
- package/dist/cli/index-cmd.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +81 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +8 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +77 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +28 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/unused.d.ts +2 -0
- package/dist/cli/unused.d.ts.map +1 -0
- package/dist/cli/unused.js +56 -0
- package/dist/cli/unused.js.map +1 -0
- package/dist/graph/graph.d.ts +30 -0
- package/dist/graph/graph.d.ts.map +1 -0
- package/dist/graph/graph.js +166 -0
- package/dist/graph/graph.js.map +1 -0
- package/dist/graph/index.d.ts +5 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +5 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/schema.d.ts +33 -0
- package/dist/graph/schema.d.ts.map +1 -0
- package/dist/graph/schema.js +3 -0
- package/dist/graph/schema.js.map +1 -0
- package/dist/graph/serialize.d.ts +7 -0
- package/dist/graph/serialize.d.ts.map +1 -0
- package/dist/graph/serialize.js +39 -0
- package/dist/graph/serialize.js.map +1 -0
- package/dist/graph/traverse.d.ts +14 -0
- package/dist/graph/traverse.d.ts.map +1 -0
- package/dist/graph/traverse.js +50 -0
- package/dist/graph/traverse.js.map +1 -0
- package/dist/mcp/formatter.d.ts +26 -0
- package/dist/mcp/formatter.d.ts.map +1 -0
- package/dist/mcp/formatter.js +691 -0
- package/dist/mcp/formatter.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +45 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +136 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/output/ai-context.d.ts +7 -0
- package/dist/output/ai-context.d.ts.map +1 -0
- package/dist/output/ai-context.js +26 -0
- package/dist/output/ai-context.js.map +1 -0
- package/dist/parser/extractors/api-calls.d.ts +15 -0
- package/dist/parser/extractors/api-calls.d.ts.map +1 -0
- package/dist/parser/extractors/api-calls.js +168 -0
- package/dist/parser/extractors/api-calls.js.map +1 -0
- package/dist/parser/extractors/components.d.ts +5 -0
- package/dist/parser/extractors/components.d.ts.map +1 -0
- package/dist/parser/extractors/components.js +236 -0
- package/dist/parser/extractors/components.js.map +1 -0
- package/dist/parser/extractors/context.d.ts +14 -0
- package/dist/parser/extractors/context.d.ts.map +1 -0
- package/dist/parser/extractors/context.js +196 -0
- package/dist/parser/extractors/context.js.map +1 -0
- package/dist/parser/extractors/effects.d.ts +14 -0
- package/dist/parser/extractors/effects.d.ts.map +1 -0
- package/dist/parser/extractors/effects.js +175 -0
- package/dist/parser/extractors/effects.js.map +1 -0
- package/dist/parser/extractors/hooks.d.ts +5 -0
- package/dist/parser/extractors/hooks.d.ts.map +1 -0
- package/dist/parser/extractors/hooks.js +242 -0
- package/dist/parser/extractors/hooks.js.map +1 -0
- package/dist/parser/extractors/imports.d.ts +6 -0
- package/dist/parser/extractors/imports.d.ts.map +1 -0
- package/dist/parser/extractors/imports.js +148 -0
- package/dist/parser/extractors/imports.js.map +1 -0
- package/dist/parser/extractors/index.d.ts +12 -0
- package/dist/parser/extractors/index.d.ts.map +1 -0
- package/dist/parser/extractors/index.js +11 -0
- package/dist/parser/extractors/index.js.map +1 -0
- package/dist/parser/extractors/jsx-tree.d.ts +5 -0
- package/dist/parser/extractors/jsx-tree.d.ts.map +1 -0
- package/dist/parser/extractors/jsx-tree.js +226 -0
- package/dist/parser/extractors/jsx-tree.js.map +1 -0
- package/dist/parser/extractors/routes.d.ts +13 -0
- package/dist/parser/extractors/routes.d.ts.map +1 -0
- package/dist/parser/extractors/routes.js +275 -0
- package/dist/parser/extractors/routes.js.map +1 -0
- package/dist/parser/extractors/state.d.ts +14 -0
- package/dist/parser/extractors/state.d.ts.map +1 -0
- package/dist/parser/extractors/state.js +368 -0
- package/dist/parser/extractors/state.js.map +1 -0
- package/dist/parser/extractors/types.d.ts +22 -0
- package/dist/parser/extractors/types.d.ts.map +1 -0
- package/dist/parser/extractors/types.js +51 -0
- package/dist/parser/extractors/types.js.map +1 -0
- package/dist/parser/indexer.d.ts +14 -0
- package/dist/parser/indexer.d.ts.map +1 -0
- package/dist/parser/indexer.js +167 -0
- package/dist/parser/indexer.js.map +1 -0
- package/dist/parser/pipeline.d.ts +16 -0
- package/dist/parser/pipeline.d.ts.map +1 -0
- package/dist/parser/pipeline.js +63 -0
- package/dist/parser/pipeline.js.map +1 -0
- package/dist/parser/setup.d.ts +4 -0
- package/dist/parser/setup.d.ts.map +1 -0
- package/dist/parser/setup.js +29 -0
- package/dist/parser/setup.js.map +1 -0
- package/dist/parser/walker.d.ts +6 -0
- package/dist/parser/walker.d.ts.map +1 -0
- package/dist/parser/walker.js +45 -0
- package/dist/parser/walker.js.map +1 -0
- package/dist/watcher.d.ts +12 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +72 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +51 -0
- package/src/cli/components/IndexProgress.tsx +79 -0
- package/src/cli/components/InitResult.tsx +28 -0
- package/src/cli/index-cmd.ts +41 -0
- package/src/cli/index.ts +92 -0
- package/src/cli/init.ts +97 -0
- package/src/cli/serve.ts +29 -0
- package/src/cli/unused.ts +88 -0
- package/src/graph/graph.ts +179 -0
- package/src/graph/index.ts +4 -0
- package/src/graph/schema.ts +68 -0
- package/src/graph/serialize.ts +40 -0
- package/src/graph/traverse.ts +66 -0
- package/src/mcp/formatter.ts +757 -0
- package/src/mcp/server.ts +59 -0
- package/src/mcp/tools.ts +154 -0
- package/src/output/ai-context.ts +29 -0
- package/src/parser/extractors/api-calls.ts +192 -0
- package/src/parser/extractors/components.ts +273 -0
- package/src/parser/extractors/context.ts +216 -0
- package/src/parser/extractors/effects.ts +205 -0
- package/src/parser/extractors/hooks.ts +268 -0
- package/src/parser/extractors/imports.ts +192 -0
- package/src/parser/extractors/index.ts +11 -0
- package/src/parser/extractors/jsx-tree.ts +271 -0
- package/src/parser/extractors/routes.ts +331 -0
- package/src/parser/extractors/state.ts +392 -0
- package/src/parser/extractors/types.ts +71 -0
- package/src/parser/indexer.ts +197 -0
- package/src/parser/pipeline.ts +89 -0
- package/src/parser/setup.ts +33 -0
- package/src/parser/walker.ts +61 -0
- package/src/watcher.ts +91 -0
- package/templates/CLAUDE.md +7 -0
- package/tests/extractors.test.ts +164 -0
- package/tests/fixtures/basic/src/App.tsx +12 -0
- package/tests/fixtures/basic/src/components/Dashboard.tsx +24 -0
- package/tests/fixtures/basic/src/components/MetricsCard.tsx +15 -0
- package/tests/fixtures/basic/src/components/Sidebar.tsx +20 -0
- package/tests/fixtures/basic/src/contexts/ThemeContext.tsx +16 -0
- package/tests/fixtures/basic/src/hooks/useAuth.ts +25 -0
- package/tests/fixtures/basic/src/stores/authStore.ts +15 -0
- package/tests/fixtures/basic/src/utils.ts +7 -0
- package/tests/graph.test.ts +91 -0
- package/tests/phase2.test.ts +309 -0
- package/tests/smoke.test.ts +77 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/parser/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GraphNode, GraphEdge } from '../graph/schema.js';
|
|
2
|
+
export interface PipelineResult {
|
|
3
|
+
nodes: GraphNode[];
|
|
4
|
+
edges: GraphEdge[];
|
|
5
|
+
filePath: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Run all extractors on a single file in order.
|
|
9
|
+
*
|
|
10
|
+
* @param globalNodes - Nodes from ALL previously-processed files.
|
|
11
|
+
* Cross-file extractors (state, context, hooks, jsx-tree) use this
|
|
12
|
+
* to resolve references to stores, contexts, and components defined
|
|
13
|
+
* in other files.
|
|
14
|
+
*/
|
|
15
|
+
export declare function processFile(relativePath: string, projectDir: string, globalNodes?: GraphNode[]): Promise<PipelineResult>;
|
|
16
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/parser/pipeline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAa/D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,WAAW,GAAE,SAAS,EAAO,GAC5B,OAAO,CAAC,cAAc,CAAC,CAuDzB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { parseFile } from './setup.js';
|
|
4
|
+
import { extractImports } from './extractors/imports.js';
|
|
5
|
+
import { extractComponents } from './extractors/components.js';
|
|
6
|
+
import { extractHooks } from './extractors/hooks.js';
|
|
7
|
+
import { extractJSXTree } from './extractors/jsx-tree.js';
|
|
8
|
+
import { extractContext } from './extractors/context.js';
|
|
9
|
+
import { extractState } from './extractors/state.js';
|
|
10
|
+
import { extractApiCalls } from './extractors/api-calls.js';
|
|
11
|
+
import { extractRoutes } from './extractors/routes.js';
|
|
12
|
+
import { extractEffects } from './extractors/effects.js';
|
|
13
|
+
/**
|
|
14
|
+
* Run all extractors on a single file in order.
|
|
15
|
+
*
|
|
16
|
+
* @param globalNodes - Nodes from ALL previously-processed files.
|
|
17
|
+
* Cross-file extractors (state, context, hooks, jsx-tree) use this
|
|
18
|
+
* to resolve references to stores, contexts, and components defined
|
|
19
|
+
* in other files.
|
|
20
|
+
*/
|
|
21
|
+
export async function processFile(relativePath, projectDir, globalNodes = []) {
|
|
22
|
+
const absolutePath = resolve(projectDir, relativePath);
|
|
23
|
+
const sourceCode = await readFile(absolutePath, 'utf-8');
|
|
24
|
+
const tree = parseFile(relativePath, sourceCode);
|
|
25
|
+
if (!tree) {
|
|
26
|
+
return { nodes: [], edges: [], filePath: relativePath };
|
|
27
|
+
}
|
|
28
|
+
// allNodes = this file's nodes as they accumulate through extractors
|
|
29
|
+
const allNodes = [];
|
|
30
|
+
const allEdges = [];
|
|
31
|
+
// visibleNodes = this file's nodes + global nodes from other files
|
|
32
|
+
// Extractors see both when resolving cross-file references
|
|
33
|
+
function visibleNodes() {
|
|
34
|
+
return [...globalNodes, ...allNodes];
|
|
35
|
+
}
|
|
36
|
+
function run(extractor) {
|
|
37
|
+
const result = extractor(tree, relativePath, sourceCode, visibleNodes());
|
|
38
|
+
allNodes.push(...result.nodes);
|
|
39
|
+
allEdges.push(...result.edges);
|
|
40
|
+
}
|
|
41
|
+
// 1. Imports (creates Module nodes + import edges)
|
|
42
|
+
const importResult = extractImports(tree, relativePath, sourceCode, visibleNodes(), projectDir);
|
|
43
|
+
allNodes.push(...importResult.nodes);
|
|
44
|
+
allEdges.push(...importResult.edges);
|
|
45
|
+
// 2. Components
|
|
46
|
+
run(extractComponents);
|
|
47
|
+
// 3. Hooks
|
|
48
|
+
run(extractHooks);
|
|
49
|
+
// 4. JSX render tree
|
|
50
|
+
run(extractJSXTree);
|
|
51
|
+
// 5. Context (needs components/hooks for edge sources)
|
|
52
|
+
run(extractContext);
|
|
53
|
+
// 6. State management (needs components/hooks + cross-file stores)
|
|
54
|
+
run(extractState);
|
|
55
|
+
// 7. API calls (needs components/hooks for edge sources)
|
|
56
|
+
run(extractApiCalls);
|
|
57
|
+
// 8. Routes (needs components for linking)
|
|
58
|
+
run(extractRoutes);
|
|
59
|
+
// 9. Effects (annotates existing nodes — must run last)
|
|
60
|
+
run(extractEffects);
|
|
61
|
+
return { nodes: allNodes, edges: allEdges, filePath: relativePath };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/parser/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AASzD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,YAAoB,EACpB,UAAkB,EAClB,cAA2B,EAAE;IAE7B,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAEjD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;IAC1D,CAAC;IAED,qEAAqE;IACrE,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAgB,EAAE,CAAC;IAEjC,mEAAmE;IACnE,2DAA2D;IAC3D,SAAS,YAAY;QACnB,OAAO,CAAC,GAAG,WAAW,EAAE,GAAG,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,SAAS,GAAG,CAAC,SAAuF;QAClG,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;QACzE,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,mDAAmD;IACnD,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC;IAChG,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAErC,gBAAgB;IAChB,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEvB,WAAW;IACX,GAAG,CAAC,YAAY,CAAC,CAAC;IAElB,qBAAqB;IACrB,GAAG,CAAC,cAAc,CAAC,CAAC;IAEpB,uDAAuD;IACvD,GAAG,CAAC,cAAc,CAAC,CAAC;IAEpB,mEAAmE;IACnE,GAAG,CAAC,YAAY,CAAC,CAAC;IAElB,yDAAyD;IACzD,GAAG,CAAC,eAAe,CAAC,CAAC;IAErB,2CAA2C;IAC3C,GAAG,CAAC,aAAa,CAAC,CAAC;IAEnB,wDAAwD;IACxD,GAAG,CAAC,cAAc,CAAC,CAAC;IAEpB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/parser/setup.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAuBjC,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,CAIlF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Parser from 'tree-sitter';
|
|
2
|
+
// @ts-expect-error no declaration file for tree-sitter grammars
|
|
3
|
+
import TypeScript from 'tree-sitter-typescript/typescript';
|
|
4
|
+
// @ts-expect-error no declaration file for tree-sitter grammars
|
|
5
|
+
import TSX from 'tree-sitter-typescript/tsx';
|
|
6
|
+
import JavaScript from 'tree-sitter-javascript';
|
|
7
|
+
const tsParser = new Parser();
|
|
8
|
+
tsParser.setLanguage(TypeScript);
|
|
9
|
+
const tsxParser = new Parser();
|
|
10
|
+
tsxParser.setLanguage(TSX);
|
|
11
|
+
const jsParser = new Parser();
|
|
12
|
+
jsParser.setLanguage(JavaScript);
|
|
13
|
+
const EXT_MAP = {
|
|
14
|
+
'.ts': tsParser,
|
|
15
|
+
'.tsx': tsxParser,
|
|
16
|
+
'.js': jsParser,
|
|
17
|
+
'.jsx': tsxParser, // JSX uses TSX grammar
|
|
18
|
+
};
|
|
19
|
+
export function getParser(filePath) {
|
|
20
|
+
const ext = filePath.slice(filePath.lastIndexOf('.'));
|
|
21
|
+
return EXT_MAP[ext] ?? null;
|
|
22
|
+
}
|
|
23
|
+
export function parseFile(filePath, sourceCode) {
|
|
24
|
+
const parser = getParser(filePath);
|
|
25
|
+
if (!parser)
|
|
26
|
+
return null;
|
|
27
|
+
return parser.parse(sourceCode);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/parser/setup.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,gEAAgE;AAChE,OAAO,UAAU,MAAM,mCAAmC,CAAC;AAC3D,gEAAgE;AAChE,OAAO,GAAG,MAAM,4BAA4B,CAAC;AAC7C,OAAO,UAAU,MAAM,wBAAwB,CAAC;AAEhD,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;AAC9B,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;AAEjC,MAAM,SAAS,GAAG,IAAI,MAAM,EAAE,CAAC;AAC/B,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AAE3B,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;AAC9B,QAAQ,CAAC,WAAW,CAAC,UAAwC,CAAC,CAAC;AAE/D,MAAM,OAAO,GAA2B;IACtC,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,SAAS,EAAE,uBAAuB;CAC3C,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,UAAkB;IAC5D,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walker.d.ts","sourceRoot":"","sources":["../../src/parser/walker.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,MAAM,EAAO,GAC1B,OAAO,CAAC,UAAU,EAAE,CAAC,CAOvB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import ignore from 'ignore';
|
|
4
|
+
const ALWAYS_SKIP = new Set(['node_modules', '.next', 'dist', 'build', '.reactgraph', '.git']);
|
|
5
|
+
const EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
|
|
6
|
+
export async function walkProject(projectDir, extraExclude = []) {
|
|
7
|
+
const ig = await loadGitignore(projectDir);
|
|
8
|
+
for (const pat of extraExclude)
|
|
9
|
+
ig.add(pat);
|
|
10
|
+
const files = [];
|
|
11
|
+
await walk(projectDir, projectDir, ig, files);
|
|
12
|
+
return files;
|
|
13
|
+
}
|
|
14
|
+
async function loadGitignore(projectDir) {
|
|
15
|
+
const ig = ignore();
|
|
16
|
+
try {
|
|
17
|
+
const content = await readFile(join(projectDir, '.gitignore'), 'utf-8');
|
|
18
|
+
ig.add(content);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// no .gitignore, that's fine
|
|
22
|
+
}
|
|
23
|
+
return ig;
|
|
24
|
+
}
|
|
25
|
+
async function walk(dir, projectDir, ig, files) {
|
|
26
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
if (ALWAYS_SKIP.has(entry.name))
|
|
29
|
+
continue;
|
|
30
|
+
const abs = join(dir, entry.name);
|
|
31
|
+
const rel = relative(projectDir, abs);
|
|
32
|
+
if (ig.ignores(rel))
|
|
33
|
+
continue;
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
await walk(abs, projectDir, ig, files);
|
|
36
|
+
}
|
|
37
|
+
else if (entry.isFile()) {
|
|
38
|
+
const ext = entry.name.slice(entry.name.lastIndexOf('.'));
|
|
39
|
+
if (EXTENSIONS.has(ext)) {
|
|
40
|
+
files.push({ absolutePath: abs, relativePath: rel });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=walker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walker.js","sourceRoot":"","sources":["../../src/parser/walker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAQ,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,MAAuB,MAAM,QAAQ,CAAC;AAE7C,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/F,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAO3D,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAAkB,EAClB,eAAyB,EAAE;IAE3B,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,YAAY;QAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,UAAkB;IAC7C,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,GAAW,EACX,UAAkB,EAClB,EAAU,EACV,KAAmB;IAEnB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAE1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEtC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,SAAS;QAE9B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1D,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactGraph } from './graph/graph.js';
|
|
2
|
+
export interface WatcherOptions {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
graph: ReactGraph;
|
|
5
|
+
onUpdate?: (file: string, stats: {
|
|
6
|
+
nodes: number;
|
|
7
|
+
edges: number;
|
|
8
|
+
}) => void;
|
|
9
|
+
onError?: (file: string, error: Error) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function startWatcher(opts: WatcherOptions): () => void;
|
|
12
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ9C,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAChD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,IAAI,CAuE7D"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { watch } from 'chokidar';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { relative } from 'node:path';
|
|
5
|
+
import { saveGraph } from './graph/serialize.js';
|
|
6
|
+
import { processFile } from './parser/pipeline.js';
|
|
7
|
+
import { generateAIContext } from './output/ai-context.js';
|
|
8
|
+
const EXTENSIONS = /\.(tsx?|jsx?)$/;
|
|
9
|
+
const IGNORE = ['**/node_modules/**', '**/.next/**', '**/dist/**', '**/build/**', '**/.reactgraph/**'];
|
|
10
|
+
export function startWatcher(opts) {
|
|
11
|
+
const { projectDir, graph, onUpdate, onError } = opts;
|
|
12
|
+
const fileHashes = new Map();
|
|
13
|
+
// Initialize hashes for current graph files
|
|
14
|
+
// (We'll compute them on first change detection)
|
|
15
|
+
const watcher = watch(projectDir, {
|
|
16
|
+
ignored: IGNORE,
|
|
17
|
+
ignoreInitial: true,
|
|
18
|
+
persistent: true,
|
|
19
|
+
});
|
|
20
|
+
const handleChange = async (absPath) => {
|
|
21
|
+
if (!EXTENSIONS.test(absPath))
|
|
22
|
+
return;
|
|
23
|
+
const relPath = relative(projectDir, absPath).replace(/\\/g, '/');
|
|
24
|
+
try {
|
|
25
|
+
const content = await readFile(absPath, 'utf-8');
|
|
26
|
+
const hash = createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
27
|
+
// Skip if unchanged
|
|
28
|
+
if (fileHashes.get(relPath) === hash)
|
|
29
|
+
return;
|
|
30
|
+
fileHashes.set(relPath, hash);
|
|
31
|
+
// Remove old nodes/edges for this file
|
|
32
|
+
graph.removeNodesByFile(relPath);
|
|
33
|
+
// Re-process file
|
|
34
|
+
const result = await processFile(relPath, projectDir);
|
|
35
|
+
for (const node of result.nodes)
|
|
36
|
+
graph.addNode(node);
|
|
37
|
+
for (const edge of result.edges)
|
|
38
|
+
graph.addEdge(edge);
|
|
39
|
+
// Persist
|
|
40
|
+
await saveGraph(graph, projectDir);
|
|
41
|
+
await generateAIContext(graph, projectDir);
|
|
42
|
+
onUpdate?.(relPath, {
|
|
43
|
+
nodes: graph.stats().totalNodes,
|
|
44
|
+
edges: graph.stats().totalEdges,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
onError?.(relPath, err);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const handleDelete = async (absPath) => {
|
|
52
|
+
if (!EXTENSIONS.test(absPath))
|
|
53
|
+
return;
|
|
54
|
+
const relPath = relative(projectDir, absPath).replace(/\\/g, '/');
|
|
55
|
+
graph.removeNodesByFile(relPath);
|
|
56
|
+
fileHashes.delete(relPath);
|
|
57
|
+
await saveGraph(graph, projectDir);
|
|
58
|
+
await generateAIContext(graph, projectDir);
|
|
59
|
+
onUpdate?.(relPath, {
|
|
60
|
+
nodes: graph.stats().totalNodes,
|
|
61
|
+
edges: graph.stats().totalEdges,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
watcher.on('change', handleChange);
|
|
65
|
+
watcher.on('add', handleChange);
|
|
66
|
+
watcher.on('unlink', handleDelete);
|
|
67
|
+
// Return cleanup function
|
|
68
|
+
return () => {
|
|
69
|
+
watcher.close();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reactgraph/cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Pre-computed React project graph for AI-assisted development",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"reactgraph": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "bun run --bun node_modules/.bin/tsc",
|
|
13
|
+
"dev": "bun run src/cli/index.ts",
|
|
14
|
+
"test": "bun test",
|
|
15
|
+
"lint": "bun run --bun node_modules/.bin/tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react",
|
|
19
|
+
"graph",
|
|
20
|
+
"mcp",
|
|
21
|
+
"claude",
|
|
22
|
+
"ai",
|
|
23
|
+
"ast",
|
|
24
|
+
"tree-sitter"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
32
|
+
"chokidar": "^4.0.3",
|
|
33
|
+
"commander": "^13.1.0",
|
|
34
|
+
"ignore": "^7.0.4",
|
|
35
|
+
"ink": "^5.2.0",
|
|
36
|
+
"react": "^18.3.1",
|
|
37
|
+
"tree-sitter": "^0.22.4",
|
|
38
|
+
"tree-sitter-javascript": "^0.23.1",
|
|
39
|
+
"tree-sitter-typescript": "^0.23.2"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.13.14",
|
|
43
|
+
"@types/react": "^18.3.18",
|
|
44
|
+
"tsx": "^4.19.3",
|
|
45
|
+
"typescript": "^5.8.2",
|
|
46
|
+
"vitest": "^3.0.9"
|
|
47
|
+
},
|
|
48
|
+
"trustedDependencies": [
|
|
49
|
+
"tree-sitter-javascript"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
|
|
4
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
5
|
+
|
|
6
|
+
function useSpinner() {
|
|
7
|
+
const [frame, setFrame] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const timer = setInterval(() => {
|
|
10
|
+
setFrame(f => (f + 1) % SPINNER_FRAMES.length);
|
|
11
|
+
}, 80);
|
|
12
|
+
return () => clearInterval(timer);
|
|
13
|
+
}, []);
|
|
14
|
+
return SPINNER_FRAMES[frame];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface IndexProgressProps {
|
|
18
|
+
currentFile: string;
|
|
19
|
+
current: number;
|
|
20
|
+
total: number;
|
|
21
|
+
done: boolean;
|
|
22
|
+
stats?: {
|
|
23
|
+
totalFiles: number;
|
|
24
|
+
parsedFiles: number;
|
|
25
|
+
skippedFiles: number;
|
|
26
|
+
nodes: number;
|
|
27
|
+
edges: number;
|
|
28
|
+
durationMs: number;
|
|
29
|
+
};
|
|
30
|
+
graphStats?: Record<string, number>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function IndexProgress({ currentFile, current, total, done, stats, graphStats }: IndexProgressProps) {
|
|
34
|
+
const spinner = useSpinner();
|
|
35
|
+
|
|
36
|
+
if (done && stats && graphStats) {
|
|
37
|
+
return (
|
|
38
|
+
<Box flexDirection="column" paddingTop={1}>
|
|
39
|
+
<Text color="green" bold>✓ Indexing complete</Text>
|
|
40
|
+
<Box paddingLeft={2} flexDirection="column" marginTop={1}>
|
|
41
|
+
<Text dimColor>Files: {stats.totalFiles} found, {stats.parsedFiles} parsed, {stats.skippedFiles} cached</Text>
|
|
42
|
+
<Text dimColor>Time: {(stats.durationMs / 1000).toFixed(2)}s</Text>
|
|
43
|
+
<Text dimColor>Nodes: {stats.nodes}</Text>
|
|
44
|
+
<Text dimColor>Edges: {stats.edges}</Text>
|
|
45
|
+
</Box>
|
|
46
|
+
<Box paddingLeft={2} flexDirection="column" marginTop={1}>
|
|
47
|
+
{graphStats.Component !== undefined && <Text> Components: {graphStats.Component}</Text>}
|
|
48
|
+
{graphStats.Hook !== undefined && <Text> Hooks: {graphStats.Hook}</Text>}
|
|
49
|
+
{graphStats.Module !== undefined && <Text> Modules: {graphStats.Module}</Text>}
|
|
50
|
+
</Box>
|
|
51
|
+
<Box marginTop={1}>
|
|
52
|
+
<Text color="cyan">→ .reactgraph/ai-context.md generated</Text>
|
|
53
|
+
</Box>
|
|
54
|
+
</Box>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const pct = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
59
|
+
const barWidth = 30;
|
|
60
|
+
const filled = Math.round((pct / 100) * barWidth);
|
|
61
|
+
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Box flexDirection="column">
|
|
65
|
+
<Box>
|
|
66
|
+
<Text color="yellow">{spinner}</Text>
|
|
67
|
+
<Text> Indexing... </Text>
|
|
68
|
+
<Text dimColor>{current}/{total}</Text>
|
|
69
|
+
</Box>
|
|
70
|
+
<Box paddingLeft={2}>
|
|
71
|
+
<Text color="cyan">{bar}</Text>
|
|
72
|
+
<Text> {pct}%</Text>
|
|
73
|
+
</Box>
|
|
74
|
+
<Box paddingLeft={2}>
|
|
75
|
+
<Text dimColor>{currentFile}</Text>
|
|
76
|
+
</Box>
|
|
77
|
+
</Box>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import type { InitResult as InitResultType } from '../init.js';
|
|
4
|
+
|
|
5
|
+
interface InitResultProps {
|
|
6
|
+
result: InitResultType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function InitResultView({ result }: InitResultProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Box flexDirection="column" paddingTop={1}>
|
|
12
|
+
<Text color="green" bold>✓ ReactGraph initialized</Text>
|
|
13
|
+
<Box paddingLeft={2} flexDirection="column" marginTop={1}>
|
|
14
|
+
<Text>Framework: <Text color="cyan">{result.framework}</Text></Text>
|
|
15
|
+
<Text>Source dirs: <Text color="cyan">{result.srcDirs.join(', ')}</Text></Text>
|
|
16
|
+
<Text>Config: <Text dimColor>.reactgraph/config.json</Text></Text>
|
|
17
|
+
{result.gitignoreUpdated && (
|
|
18
|
+
<Text dimColor>Added .reactgraph/ to .gitignore</Text>
|
|
19
|
+
)}
|
|
20
|
+
</Box>
|
|
21
|
+
<Box marginTop={1} flexDirection="column">
|
|
22
|
+
<Text color="yellow">Next steps:</Text>
|
|
23
|
+
<Text> 1. Run <Text color="cyan" bold>reactgraph index</Text> to build the graph</Text>
|
|
24
|
+
<Text> 2. Add to CLAUDE.md: <Text dimColor>"Read .reactgraph/ai-context.md at session start"</Text></Text>
|
|
25
|
+
</Box>
|
|
26
|
+
</Box>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { ReactGraphConfig } from '../graph/schema.js';
|
|
4
|
+
import { indexProject, type IndexStats } from '../parser/indexer.js';
|
|
5
|
+
import { generateAIContext } from '../output/ai-context.js';
|
|
6
|
+
|
|
7
|
+
export interface IndexResult {
|
|
8
|
+
stats: IndexStats;
|
|
9
|
+
graphStats: Record<string, number>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function runIndex(
|
|
13
|
+
projectDir: string,
|
|
14
|
+
onProgress?: (file: string, current: number, total: number) => void,
|
|
15
|
+
): Promise<IndexResult> {
|
|
16
|
+
// Load config
|
|
17
|
+
const config = loadConfig(projectDir);
|
|
18
|
+
|
|
19
|
+
// Run indexer
|
|
20
|
+
const { graph, stats } = await indexProject(projectDir, config.exclude, onProgress);
|
|
21
|
+
|
|
22
|
+
// Always generate ai-context.md
|
|
23
|
+
await generateAIContext(graph, projectDir);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
stats,
|
|
27
|
+
graphStats: graph.stats(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loadConfig(projectDir: string): ReactGraphConfig {
|
|
32
|
+
const configPath = join(projectDir, '.reactgraph', 'config.json');
|
|
33
|
+
if (existsSync(configPath)) {
|
|
34
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
srcDirs: ['src'],
|
|
38
|
+
framework: 'unknown',
|
|
39
|
+
exclude: ['**/*.test.*', '**/*.spec.*'],
|
|
40
|
+
};
|
|
41
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { render } from 'ink';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { runInit } from './init.js';
|
|
7
|
+
import { runIndex } from './index-cmd.js';
|
|
8
|
+
import { runServe } from './serve.js';
|
|
9
|
+
import { runUnused } from './unused.js';
|
|
10
|
+
import { InitResultView } from './components/InitResult.js';
|
|
11
|
+
import { IndexProgress } from './components/IndexProgress.js';
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name('reactgraph')
|
|
17
|
+
.description('Pre-computed React project graph for AI-assisted development')
|
|
18
|
+
.version('0.1.0');
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command('init')
|
|
22
|
+
.description('Initialize ReactGraph in the current project')
|
|
23
|
+
.action(() => {
|
|
24
|
+
const projectDir = process.cwd();
|
|
25
|
+
const result = runInit(projectDir);
|
|
26
|
+
const { unmount } = render(React.createElement(InitResultView, { result }));
|
|
27
|
+
setTimeout(() => unmount(), 100);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command('index')
|
|
32
|
+
.description('Build the project graph')
|
|
33
|
+
.action(async () => {
|
|
34
|
+
const projectDir = process.cwd();
|
|
35
|
+
|
|
36
|
+
let currentFile = '';
|
|
37
|
+
let current = 0;
|
|
38
|
+
let total = 0;
|
|
39
|
+
let done = false;
|
|
40
|
+
let stats: any = null;
|
|
41
|
+
let graphStats: any = null;
|
|
42
|
+
|
|
43
|
+
const App = () => React.createElement(IndexProgress, {
|
|
44
|
+
currentFile,
|
|
45
|
+
current,
|
|
46
|
+
total,
|
|
47
|
+
done,
|
|
48
|
+
stats,
|
|
49
|
+
graphStats,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const { rerender, unmount } = render(React.createElement(App));
|
|
53
|
+
|
|
54
|
+
const onProgress = (file: string, cur: number, tot: number) => {
|
|
55
|
+
currentFile = file;
|
|
56
|
+
current = cur;
|
|
57
|
+
total = tot;
|
|
58
|
+
rerender(React.createElement(App));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const result = await runIndex(projectDir, onProgress);
|
|
63
|
+
done = true;
|
|
64
|
+
stats = result.stats;
|
|
65
|
+
graphStats = result.graphStats;
|
|
66
|
+
rerender(React.createElement(App));
|
|
67
|
+
setTimeout(() => unmount(), 200);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
unmount();
|
|
70
|
+
console.error('Index failed:', err);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
program
|
|
76
|
+
.command('serve')
|
|
77
|
+
.description('Start the MCP server')
|
|
78
|
+
.option('--watch', 'Watch for file changes and update graph incrementally')
|
|
79
|
+
.action(async (opts) => {
|
|
80
|
+
const projectDir = process.cwd();
|
|
81
|
+
await runServe(projectDir, opts.watch ?? false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
program
|
|
85
|
+
.command('unused')
|
|
86
|
+
.description('Find orphan components, unused hooks, and dead exports')
|
|
87
|
+
.action(async () => {
|
|
88
|
+
const projectDir = process.cwd();
|
|
89
|
+
await runUnused(projectDir);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
program.parse();
|