graphmycode-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # graphmycode-mcp
2
+
3
+ MCP server for codebase structure analysis. Provides dependency graphs, community detection, hotspot identification, and agent context generation directly inside Claude Code.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g graphmycode-mcp
9
+ ```
10
+
11
+ The postinstall script automatically:
12
+ 1. Registers the MCP server in Claude Code (`~/.claude/claude.json`)
13
+ 2. Installs 8 slash commands in `~/.claude/commands/`
14
+
15
+ Restart Claude Code after installing to activate.
16
+
17
+ ---
18
+
19
+ ### If the postinstall was skipped
20
+
21
+ This happens when installing with `--ignore-scripts` or in CI environments. Run setup manually:
22
+
23
+ ```bash
24
+ graphmycode-mcp setup
25
+ ```
26
+
27
+ ---
28
+
29
+ ### Manual registration (clone/dev install)
30
+
31
+ If you cloned the repo instead of installing from npm:
32
+
33
+ ```bash
34
+ npm install
35
+ npm run build
36
+ node_path=$(which node) # get absolute path — e.g. /opt/homebrew/bin/node
37
+ dist_path=$(pwd)/dist/index.js
38
+ claude mcp add -s user graphmycode -- "$node_path" "$dist_path"
39
+ ```
40
+
41
+ Then copy the slash commands:
42
+
43
+ ```bash
44
+ cp commands/*.md ~/.claude/commands/
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Tools
50
+
51
+ | Tool | Description |
52
+ |------|-------------|
53
+ | `analyze_structure` | Full dependency graph with hotspots, dead code, and coupling metrics |
54
+ | `detect_stack` | Languages, frameworks, and project type |
55
+ | `get_file_dependencies` | Imports and importedBy for a specific file |
56
+ | `get_communities` | Module clusters detected via graph analysis |
57
+ | `find_entry_points` | Files with no importers (CLI, main, index) |
58
+ | `query_graph` | Natural language queries over the graph |
59
+ | `export_agent_context` | Generate `CLAUDE.md` / `AGENTS.md` for the codebase |
60
+
61
+ ## Slash Commands
62
+
63
+ Once installed, these commands are available in Claude Code:
64
+
65
+ | Command | Description |
66
+ |---------|-------------|
67
+ | `/graphmycode` | Hotspots, dead code, coupling summary |
68
+ | `/graphmycode-analysis` | Full analysis cycle with improvement plan |
69
+ | `/graphmycode-context` | Generate CLAUDE.md / AGENTS.md for the codebase |
70
+ | `/graphmycode-debt` | Prioritized technical debt backlog (P1/P2/P3) |
71
+ | `/graphmycode-flow` | Execution flow from entry points |
72
+ | `/graphmycode-heatmap` | Circular dependencies and hotspot heatmap |
73
+ | `/graphmycode-layers` | Architectural layers and violations |
74
+ | `/graphmycode-semantic` | Module communities and logical duplication |
75
+ | `/graphmycode-structural` | Structural dependency deep-dive |
76
+
77
+ ## Requirements
78
+
79
+ - Node.js 18+
80
+ - [Claude Code](https://claude.ai/code) CLI installed and configured
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,13 @@
1
+ Run a full graphmycode analysis cycle and generate an improvement plan.
2
+
3
+ 1. Call `analyze_structure` with `path` = current working directory
4
+ 2. Call `get_communities` with `path` = current working directory
5
+ 3. Call `detect_stack` with `path` = current working directory
6
+ 4. Call `query_graph` asking "¿cuales son los archivos más acoplados?"
7
+
8
+ Report:
9
+ - **Current state** — quick metrics snapshot (files, edges, density, avgFanIn/Out)
10
+ - **Top 3 issues** — critical structural problems ranked by impact
11
+ - **Quick wins** — improvements you can make in < 1 hour
12
+ - **Strategic moves** — refactoring priorities for next sprint
13
+ - **Success metrics** — how you'll know the refactoring worked
@@ -0,0 +1,10 @@
1
+ Generate CLAUDE.md and/or AGENTS.md context files for the current codebase.
2
+
3
+ Use the `export_agent_context` tool with:
4
+ - `path` = current working directory
5
+ - `format` = "both"
6
+
7
+ Report:
8
+ - **Files generated** — which context files were created and where
9
+ - **Stack summary** — what the generated context captures about the project
10
+ - **Key sections** — highlight the most useful parts written (entry points, dependencies, architecture)
@@ -0,0 +1,11 @@
1
+ Produce a prioritized technical debt report for the current codebase.
2
+
3
+ 1. Call `analyze_structure` with `path` = current working directory
4
+ 2. Call `get_communities` with `path` = current working directory
5
+ 3. For each hotspot (top 5 by fanIn), call `get_file_dependencies`
6
+
7
+ Debt backlog:
8
+ - **P1 (Critical)** — files with fanIn > 10 or involved in cycles
9
+ - **P2 (High)** — files with fanIn 5-10 or communities > 20 files
10
+ - **P3 (Medium)** — dead code candidates, large unfocused communities
11
+ - Include T-shirt size estimates (S/M/L/XL) per item
@@ -0,0 +1,10 @@
1
+ Trace execution flow from entry points through the current codebase.
2
+
3
+ 1. Call `find_entry_points` with `path` = current working directory
4
+ 2. For each high-confidence entry point, call `get_file_dependencies`
5
+
6
+ Report:
7
+ - **Entry points** — with confidence level and reason
8
+ - **Execution flow** — file chain from each entry point (one level deep)
9
+ - **Dead ends** — entry points that lead to dead code
10
+ - **Flow recommendations** — how to simplify the execution path
@@ -0,0 +1,11 @@
1
+ Detect circular dependencies and dependency hotspots in the current codebase.
2
+
3
+ 1. Call `analyze_structure` with `path` = current working directory
4
+ 2. Call `query_graph` asking "¿hay ciclos de importación?"
5
+ 3. Call `query_graph` asking "¿qué archivos tienen más dependencias?"
6
+
7
+ Report:
8
+ - **Circular imports** — each cycle with full file chain
9
+ - **Hotspot heatmap** — top 10 files by fanIn as ranked list
10
+ - **Risk assessment** — which cycles are most dangerous
11
+ - **Fix recommendations** — concrete steps to break each cycle
@@ -0,0 +1,11 @@
1
+ Analyze the architectural layers of the current codebase.
2
+
3
+ 1. Call `detect_stack` with `path` = current working directory
4
+ 2. Call `get_communities` with `path` = current working directory
5
+ 3. Call `analyze_structure` with `path` = current working directory
6
+
7
+ Report:
8
+ - **Stack summary** — languages, frameworks, project type
9
+ - **Architectural layers** — map communities to layers (presentation/business/data/infrastructure)
10
+ - **Layer violations** — edges crossing architectural boundaries in the wrong direction
11
+ - **Architecture recommendations** — improvements to layering
@@ -0,0 +1,9 @@
1
+ Analyze logical duplication and semantic clustering in the current codebase.
2
+
3
+ 1. Call `get_communities` with `path` = current working directory
4
+ 2. Call `query_graph` asking "what files have the most dependencies?"
5
+
6
+ Report:
7
+ - **Module communities** — each community with its files and inferred purpose
8
+ - **Potential duplication** — communities with similar names or overlapping deps
9
+ - **Semantic recommendations** — suggest consolidations or renames
@@ -0,0 +1,9 @@
1
+ Perform structural dependency analysis of the current codebase.
2
+
3
+ 1. Call `analyze_structure` with `path` = current working directory
4
+ 2. For the top 3 files by fanIn, call `get_file_dependencies`
5
+
6
+ Report:
7
+ - **Most depended-on files** with full import/importedBy lists
8
+ - **Bidirectional dependencies** — files that import each other
9
+ - **Structural recommendations** — how to reduce coupling
@@ -0,0 +1,9 @@
1
+ Analyze the structure of the current codebase using graphmycode MCP.
2
+
3
+ Use the `analyze_structure` tool with `path` set to the current working directory.
4
+
5
+ Report:
6
+ 1. **Hotspots** — files with highest fanIn, with count
7
+ 2. **Dead code candidates** — files with fanIn=0 that are not entry points
8
+ 3. **Coupling summary** — avgFanIn, avgFanOut, total edges
9
+ 4. **Top recommendations** — refactoring priorities based on metrics
@@ -0,0 +1,7 @@
1
+ import type { DependencyGraph } from './graph.js';
2
+ import type { StackInfo } from './stack.js';
3
+ import type { Community } from './communities.js';
4
+ export declare function generateAgentContext(rootPath: string, graph: DependencyGraph, stack: StackInfo, communities: Map<string, Community>, format: 'CLAUDE.md' | 'AGENTS.md' | 'both'): Promise<Array<{
5
+ name: string;
6
+ content: string;
7
+ }>>;
@@ -0,0 +1,99 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ function inferCommands(stack) {
4
+ const pm = stack.packageManager;
5
+ const run = pm === 'yarn' ? 'yarn' : pm === 'pnpm' ? 'pnpm' : pm === 'bun' ? 'bun' : 'npm run';
6
+ if (stack.languages.includes('python')) {
7
+ return { install: 'pip install -r requirements.txt', dev: 'python main.py', build: '# N/A', test: 'pytest' };
8
+ }
9
+ if (stack.languages.includes('php')) {
10
+ return { install: 'composer install', dev: 'php artisan serve', build: '# N/A', test: 'phpunit' };
11
+ }
12
+ return {
13
+ install: pm === 'npm' ? 'npm install' : `${pm} install`,
14
+ dev: `${run} dev`,
15
+ build: `${run} build`,
16
+ test: `${run} test`,
17
+ };
18
+ }
19
+ function buildModuleMap(communities) {
20
+ return [...communities.values()].map(c => {
21
+ const files = c.files.slice(0, 3).map(f => ` - ${f}`).join('\n');
22
+ const more = c.files.length > 3 ? `\n - ...and ${c.files.length - 3} more` : '';
23
+ return `- **${c.label}** (${c.size} files)\n${files}${more}`;
24
+ }).join('\n');
25
+ }
26
+ function buildCriticalEdges(graph) {
27
+ const top = [...graph.nodes.values()]
28
+ .filter(n => n.importedBy.length > 0)
29
+ .sort((a, b) => b.importedBy.length - a.importedBy.length)
30
+ .slice(0, 5);
31
+ return top.length > 0
32
+ ? top.map(n => `- \`${n.id}\` importado por ${n.importedBy.length} archivo(s)`).join('\n')
33
+ : '(none detected)';
34
+ }
35
+ function buildKeySymbols(graph) {
36
+ return [...graph.nodes.values()]
37
+ .sort((a, b) => b.importedBy.length - a.importedBy.length)
38
+ .slice(0, 8)
39
+ .map(n => `- \`${path.basename(n.id, path.extname(n.id))}\` (${n.id})`)
40
+ .join('\n') || '(none detected)';
41
+ }
42
+ async function detectMcpTools(rootPath) {
43
+ try {
44
+ const entries = await fs.readdir(path.join(rootPath, '.claude'), { withFileTypes: true });
45
+ return entries.map(e => e.name);
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ }
51
+ function generateClaudeMdContent(rootPath, graph, stack, communities) {
52
+ const name = path.basename(rootPath);
53
+ const cmds = inferCommands(stack);
54
+ return `# CLAUDE.md
55
+
56
+ ## Purpose
57
+ ${name} — ${stack.projectType} project in ${stack.languages[0] ?? 'unknown'}${stack.frameworks.length ? ` using ${stack.frameworks.slice(0, 2).join(', ')}` : ''}
58
+
59
+ ## Stack
60
+ - Language: ${stack.languages[0] ?? 'unknown'}
61
+ - Frameworks: ${stack.frameworks.join(', ') || 'none detected'}
62
+ - Package manager: ${stack.packageManager}
63
+ - Type: ${stack.projectType}
64
+
65
+ ## Commands
66
+ - Install: \`${cmds.install}\`
67
+ - Dev: \`${cmds.dev}\`
68
+ - Build: \`${cmds.build}\`
69
+ - Test: \`${cmds.test}\`
70
+
71
+ ## Module Map
72
+ ${buildModuleMap(communities)}
73
+
74
+ ## Key Symbols
75
+ ${buildKeySymbols(graph)}
76
+
77
+ ## Critical Edges
78
+ ${buildCriticalEdges(graph)}
79
+ `;
80
+ }
81
+ async function generateAgentsMdContent(rootPath, graph, stack, communities) {
82
+ const base = generateClaudeMdContent(rootPath, graph, stack, communities);
83
+ const tools = await detectMcpTools(rootPath);
84
+ const toolsSection = tools.length > 0
85
+ ? `\n## Available Tools\n${tools.map(t => `- ${t}`).join('\n')}\n`
86
+ : '\n## Available Tools\n(none detected in .claude/)\n';
87
+ return base.replace('# CLAUDE.md', '# AGENTS.md') + toolsSection;
88
+ }
89
+ export async function generateAgentContext(rootPath, graph, stack, communities, format) {
90
+ const results = [];
91
+ if (format === 'CLAUDE.md' || format === 'both') {
92
+ results.push({ name: 'CLAUDE.md', content: generateClaudeMdContent(rootPath, graph, stack, communities) });
93
+ }
94
+ if (format === 'AGENTS.md' || format === 'both') {
95
+ results.push({ name: 'AGENTS.md', content: await generateAgentsMdContent(rootPath, graph, stack, communities) });
96
+ }
97
+ return results;
98
+ }
99
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/analyzer/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { DependencyGraph } from './graph.js';
2
+ export interface Community {
3
+ id: string;
4
+ label: string;
5
+ files: string[];
6
+ size: number;
7
+ }
8
+ export declare function buildCommunities(graph: DependencyGraph): Map<string, Community>;
@@ -0,0 +1,78 @@
1
+ import path from 'path';
2
+ const DENSITY_THRESHOLD = 0.6;
3
+ export function buildCommunities(graph) {
4
+ // Paso 1: agrupar por directorio padre
5
+ const dirGroups = new Map();
6
+ for (const node of graph.nodes.values()) {
7
+ const dir = path.dirname(node.id);
8
+ const key = dir === '.' ? '(root)' : dir;
9
+ const s = dirGroups.get(key) ?? new Set();
10
+ s.add(node.id);
11
+ dirGroups.set(key, s);
12
+ }
13
+ // Fusionar directorios con un solo archivo con su padre
14
+ const groups = [];
15
+ for (const [dir, files] of dirGroups) {
16
+ if (files.size === 1 && dir !== '(root)') {
17
+ const parent = path.dirname(dir);
18
+ const parentKey = parent === '.' ? '(root)' : parent;
19
+ const existing = groups.find(g => g.dir === parentKey);
20
+ if (existing) {
21
+ for (const f of files)
22
+ existing.files.add(f);
23
+ }
24
+ else {
25
+ groups.push({ dir: parentKey, files: new Set(files) });
26
+ }
27
+ }
28
+ else {
29
+ const existing = groups.find(g => g.dir === dir);
30
+ if (existing) {
31
+ for (const f of files)
32
+ existing.files.add(f);
33
+ }
34
+ else {
35
+ groups.push({ dir, files: new Set(files) });
36
+ }
37
+ }
38
+ }
39
+ // Paso 2: refinamiento por densidad de edges
40
+ let changed = true;
41
+ while (changed) {
42
+ changed = false;
43
+ outer: for (let i = 0; i < groups.length; i++) {
44
+ for (let j = i + 1; j < groups.length; j++) {
45
+ const a = groups[i];
46
+ const b = groups[j];
47
+ const merged = new Set([...a.files, ...b.files]);
48
+ let internalEdges = 0;
49
+ let externalEdges = 0;
50
+ for (const edge of graph.edges) {
51
+ const fromIn = merged.has(edge.from);
52
+ const toIn = merged.has(edge.to);
53
+ if (fromIn && toIn)
54
+ internalEdges++;
55
+ else if (fromIn || toIn)
56
+ externalEdges++;
57
+ }
58
+ const total = internalEdges + externalEdges;
59
+ if (total > 0 && internalEdges / total > DENSITY_THRESHOLD) {
60
+ for (const f of b.files)
61
+ a.files.add(f);
62
+ groups.splice(j, 1);
63
+ changed = true;
64
+ break outer;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ const result = new Map();
70
+ groups.forEach(({ dir, files }, i) => {
71
+ const id = `community-${i}`;
72
+ const label = dir === '(root)' ? 'root' : path.basename(dir);
73
+ const fileList = [...files];
74
+ result.set(id, { id, label, files: fileList, size: fileList.length });
75
+ });
76
+ return result;
77
+ }
78
+ //# sourceMappingURL=communities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"communities.js","sourceRoot":"","sources":["../../src/analyzer/communities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export interface FileNode {
2
+ id: string;
3
+ path: string;
4
+ language: string;
5
+ imports: string[];
6
+ importedBy: string[];
7
+ externalDeps: string[];
8
+ }
9
+ export interface DependencyGraph {
10
+ nodes: Map<string, FileNode>;
11
+ edges: Array<{
12
+ from: string;
13
+ to: string;
14
+ }>;
15
+ }
16
+ export interface GraphMetrics {
17
+ fileCount: number;
18
+ edgeCount: number;
19
+ hotspots: Array<{
20
+ file: string;
21
+ fanIn: number;
22
+ }>;
23
+ deadCode: string[];
24
+ avgFanIn: number;
25
+ avgFanOut: number;
26
+ }
27
+ export interface EntryPoint {
28
+ file: string;
29
+ reason: string;
30
+ confidence: 'high' | 'medium' | 'low';
31
+ }
32
+ export declare function buildGraph(rootPath: string): Promise<DependencyGraph>;
33
+ export declare function computeMetrics(graph: DependencyGraph, options?: {
34
+ hotspotThreshold?: number;
35
+ }): GraphMetrics;
36
+ export declare function detectEntryPoints(graph: DependencyGraph, rootPath: string): EntryPoint[];
@@ -0,0 +1,174 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { parseImports, resolveRelativePath } from './parser.js';
4
+ const IGNORED_DIRS = new Set([
5
+ 'node_modules', '.git', 'dist', 'build', '__pycache__',
6
+ 'vendor', '.venv', 'venv', 'out', 'coverage', '.next', '.nuxt',
7
+ ]);
8
+ const LANG_MAP = {
9
+ '.ts': 'typescript', '.tsx': 'typescript',
10
+ '.js': 'javascript', '.jsx': 'javascript',
11
+ '.py': 'python',
12
+ '.php': 'php',
13
+ };
14
+ const ENTRY_NAMES = new Set(['main', 'index', 'app', 'server', 'cli', 'entry', 'bootstrap']);
15
+ const TEST_DIRS = ['tests/', 'test/', '__tests__/', 'spec/', 'specs/'];
16
+ const TEST_SUFFIXES = ['.test.', '.spec.', '_test.', '_spec.'];
17
+ function isDeadCodeCandidate(id) {
18
+ const normalized = id.replace(/\\/g, '/');
19
+ if (TEST_DIRS.some(d => normalized.includes(d)))
20
+ return false;
21
+ if (TEST_SUFFIXES.some(s => normalized.includes(s)))
22
+ return false;
23
+ const base = path.basename(normalized, path.extname(normalized)).toLowerCase();
24
+ if (ENTRY_NAMES.has(base))
25
+ return false;
26
+ return true;
27
+ }
28
+ const ENTRY_PATH_PATTERNS = ['/pages/', '/routes/', '/controllers/', '/app/'];
29
+ const TRY_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.py', '.php'];
30
+ const TRY_INDEX_SUFFIXES = [
31
+ '/index.ts', '/index.js', '/index.tsx', '/index.jsx', '/__init__.py',
32
+ ];
33
+ async function collectFiles(rootPath) {
34
+ const results = [];
35
+ async function walk(dir) {
36
+ try {
37
+ const entries = await fs.readdir(dir, { withFileTypes: true });
38
+ for (const entry of entries) {
39
+ const full = path.join(dir, entry.name);
40
+ if (entry.isDirectory()) {
41
+ if (!IGNORED_DIRS.has(entry.name))
42
+ await walk(full);
43
+ }
44
+ else if (entry.isFile() && LANG_MAP[path.extname(entry.name)]) {
45
+ results.push(full);
46
+ }
47
+ }
48
+ }
49
+ catch {
50
+ // skip unreadable directories
51
+ }
52
+ }
53
+ await walk(rootPath);
54
+ return results;
55
+ }
56
+ function resolveToNode(rawId, nodes) {
57
+ if (nodes.has(rawId))
58
+ return nodes.get(rawId);
59
+ // Try appending extensions directly
60
+ for (const ext of TRY_EXTENSIONS) {
61
+ const candidate = rawId + ext;
62
+ if (nodes.has(candidate))
63
+ return nodes.get(candidate);
64
+ }
65
+ // Strip existing extension and try other extensions (handles .js -> .ts substitution)
66
+ const strippedExt = path.extname(rawId);
67
+ if (strippedExt) {
68
+ const withoutExt = rawId.slice(0, rawId.length - strippedExt.length);
69
+ if (nodes.has(withoutExt))
70
+ return nodes.get(withoutExt);
71
+ for (const ext of TRY_EXTENSIONS) {
72
+ const candidate = withoutExt + ext;
73
+ if (nodes.has(candidate))
74
+ return nodes.get(candidate);
75
+ }
76
+ for (const suffix of TRY_INDEX_SUFFIXES) {
77
+ const candidate = withoutExt + suffix;
78
+ if (nodes.has(candidate))
79
+ return nodes.get(candidate);
80
+ }
81
+ }
82
+ for (const suffix of TRY_INDEX_SUFFIXES) {
83
+ const candidate = rawId + suffix;
84
+ if (nodes.has(candidate))
85
+ return nodes.get(candidate);
86
+ }
87
+ return undefined;
88
+ }
89
+ export async function buildGraph(rootPath) {
90
+ const absRoot = path.resolve(rootPath);
91
+ const files = await collectFiles(absRoot);
92
+ const nodes = new Map();
93
+ for (const filePath of files) {
94
+ const id = path.relative(absRoot, filePath);
95
+ nodes.set(id, {
96
+ id,
97
+ path: filePath,
98
+ language: LANG_MAP[path.extname(filePath)],
99
+ imports: [],
100
+ importedBy: [],
101
+ externalDeps: [],
102
+ });
103
+ }
104
+ const edges = [];
105
+ for (const [id, node] of nodes) {
106
+ let content;
107
+ try {
108
+ content = await fs.readFile(node.path, 'utf-8');
109
+ }
110
+ catch {
111
+ continue;
112
+ }
113
+ const { relative, external } = parseImports(content, node.language);
114
+ node.externalDeps = external;
115
+ for (const rel of relative) {
116
+ const resolved = resolveRelativePath(rel, node.path);
117
+ if (!resolved)
118
+ continue;
119
+ const rawId = path.relative(absRoot, resolved);
120
+ const target = resolveToNode(rawId, nodes);
121
+ if (target) {
122
+ node.imports.push(target.id);
123
+ target.importedBy.push(id);
124
+ edges.push({ from: id, to: target.id });
125
+ }
126
+ }
127
+ }
128
+ return { nodes, edges };
129
+ }
130
+ export function computeMetrics(graph, options = {}) {
131
+ const threshold = options.hotspotThreshold ?? 5;
132
+ const nodeList = [...graph.nodes.values()];
133
+ const fileCount = nodeList.length;
134
+ const edgeCount = graph.edges.length;
135
+ const hotspots = nodeList
136
+ .filter(n => n.importedBy.length > threshold)
137
+ .map(n => ({ file: n.id, fanIn: n.importedBy.length }))
138
+ .sort((a, b) => b.fanIn - a.fanIn);
139
+ const deadCode = nodeList
140
+ .filter(n => n.importedBy.length === 0 && isDeadCodeCandidate(n.id))
141
+ .map(n => n.id);
142
+ const avgFanIn = fileCount > 0
143
+ ? nodeList.reduce((s, n) => s + n.importedBy.length, 0) / fileCount : 0;
144
+ const avgFanOut = fileCount > 0
145
+ ? nodeList.reduce((s, n) => s + n.imports.length, 0) / fileCount : 0;
146
+ return { fileCount, edgeCount, hotspots, deadCode, avgFanIn, avgFanOut };
147
+ }
148
+ export function detectEntryPoints(graph, rootPath) {
149
+ const entries = [];
150
+ const seen = new Set();
151
+ for (const node of graph.nodes.values()) {
152
+ const base = path.basename(node.id, path.extname(node.id)).toLowerCase();
153
+ if (ENTRY_NAMES.has(base)) {
154
+ entries.push({ file: node.id, reason: `filename: ${base}`, confidence: 'high' });
155
+ seen.add(node.id);
156
+ }
157
+ }
158
+ for (const node of graph.nodes.values()) {
159
+ if (seen.has(node.id))
160
+ continue;
161
+ if (ENTRY_PATH_PATTERNS.some(p => node.path.includes(p))) {
162
+ entries.push({ file: node.id, reason: 'path matches routing pattern', confidence: 'medium' });
163
+ seen.add(node.id);
164
+ }
165
+ }
166
+ for (const node of graph.nodes.values()) {
167
+ if (!seen.has(node.id) && node.importedBy.length === 0) {
168
+ entries.push({ file: node.id, reason: 'fanIn=0, no other file imports it', confidence: 'low' });
169
+ seen.add(node.id);
170
+ }
171
+ }
172
+ return entries;
173
+ }
174
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/analyzer/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface ParsedImports {
2
+ relative: string[];
3
+ external: string[];
4
+ }
5
+ export declare function parseImports(content: string, language: string): ParsedImports;
6
+ export declare function resolveRelativePath(importPath: string, fromFile: string): string | null;
@@ -0,0 +1,60 @@
1
+ import path from 'path';
2
+ const JS_TS_IMPORT = /(?:^|[\n;])\s*import\s+(?:type\s+)?(?:[\w*{},\s]+\s+from\s+)?['"]([^'"]+)['"]/gm;
3
+ const JS_TS_REEXPORT = /(?:^|[\n;])\s*export\s+\{[^}]*\}\s+from\s+['"]([^'"]+)['"]/gm;
4
+ const JS_REQUIRE = /(?:^|[\n;])\s*(?:const|let|var)\s+\w+\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/gm;
5
+ const PY_FROM_RELATIVE = /^[ \t]*from\s+(\.+\w*)\s+import/gm;
6
+ const PY_FROM_ABSOLUTE = /^[ \t]*from\s+(\w[\w.]*)\s+import/gm;
7
+ const PY_IMPORT = /^[ \t]*import\s+([\w.]+)/gm;
8
+ const PHP_REQUIRE = /(?:require|require_once|include|include_once)\s*['"(]\s*([^'")\s]+)/gm;
9
+ const PHP_USE = /^[ \t]*use\s+([\w\\]+)/gm;
10
+ function extractMatches(pattern, content) {
11
+ pattern.lastIndex = 0;
12
+ const results = [];
13
+ let m;
14
+ while ((m = pattern.exec(content)) !== null) {
15
+ if (m[1])
16
+ results.push(m[1]);
17
+ }
18
+ return results;
19
+ }
20
+ export function parseImports(content, language) {
21
+ const relative = [];
22
+ const external = [];
23
+ if (language === 'typescript' || language === 'javascript') {
24
+ const all = [
25
+ ...extractMatches(JS_TS_IMPORT, content),
26
+ ...extractMatches(JS_TS_REEXPORT, content),
27
+ ...extractMatches(JS_REQUIRE, content),
28
+ ];
29
+ for (const imp of all) {
30
+ if (imp.startsWith('.')) {
31
+ relative.push(imp);
32
+ }
33
+ else {
34
+ const pkg = imp.startsWith('@')
35
+ ? imp.split('/').slice(0, 2).join('/')
36
+ : imp.split('/')[0];
37
+ if (pkg)
38
+ external.push(pkg);
39
+ }
40
+ }
41
+ }
42
+ else if (language === 'python') {
43
+ relative.push(...extractMatches(PY_FROM_RELATIVE, content));
44
+ external.push(...extractMatches(PY_FROM_ABSOLUTE, content), ...extractMatches(PY_IMPORT, content));
45
+ }
46
+ else if (language === 'php') {
47
+ relative.push(...extractMatches(PHP_REQUIRE, content));
48
+ external.push(...extractMatches(PHP_USE, content));
49
+ }
50
+ return {
51
+ relative: [...new Set(relative)],
52
+ external: [...new Set(external)],
53
+ };
54
+ }
55
+ export function resolveRelativePath(importPath, fromFile) {
56
+ if (!importPath.startsWith('.'))
57
+ return null;
58
+ return path.resolve(path.dirname(fromFile), importPath);
59
+ }
60
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/analyzer/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface StackInfo {
2
+ languages: string[];
3
+ frameworks: string[];
4
+ projectType: 'frontend' | 'backend' | 'fullstack' | 'agent' | 'library';
5
+ packageManager: string;
6
+ runtimes: string[];
7
+ }
8
+ export declare function detectStack(rootPath: string): Promise<StackInfo>;
@@ -0,0 +1,115 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ const FRONTEND = new Set(['react', 'vue', 'svelte', '@angular/core', 'astro', 'next', 'nuxt', 'preact', 'solid-js']);
4
+ const BACKEND = new Set(['express', 'fastify', 'koa', 'hapi', '@nestjs/core', 'nestjs', 'django', 'flask', 'fastapi', 'aiohttp', 'tornado']);
5
+ const AGENT = new Set(['anthropic', '@anthropic-ai/sdk', 'openai', 'langchain', '@langchain/core', 'llamaindex', 'groq']);
6
+ const IGNORED = new Set(['node_modules', '.git', 'dist', 'build', '__pycache__', 'vendor']);
7
+ async function tryReadJson(p) {
8
+ try {
9
+ return JSON.parse(await fs.readFile(p, 'utf-8'));
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ async function tryReadText(p) {
16
+ try {
17
+ return await fs.readFile(p, 'utf-8');
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ async function exists(p) {
24
+ try {
25
+ await fs.access(p);
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ async function countExts(rootPath) {
33
+ const counts = new Map();
34
+ async function walk(dir) {
35
+ try {
36
+ for (const e of await fs.readdir(dir, { withFileTypes: true })) {
37
+ if (e.isDirectory() && !IGNORED.has(e.name))
38
+ await walk(path.join(dir, e.name));
39
+ else if (e.isFile()) {
40
+ const ext = path.extname(e.name);
41
+ counts.set(ext, (counts.get(ext) ?? 0) + 1);
42
+ }
43
+ }
44
+ }
45
+ catch { /* skip */ }
46
+ }
47
+ await walk(rootPath);
48
+ return counts;
49
+ }
50
+ export async function detectStack(rootPath) {
51
+ const abs = path.resolve(rootPath);
52
+ const allDeps = [];
53
+ const runtimes = [];
54
+ let packageManager = 'unknown';
55
+ const pkg = await tryReadJson(path.join(abs, 'package.json'));
56
+ if (pkg) {
57
+ const d = pkg.dependencies;
58
+ const dd = pkg.devDependencies;
59
+ allDeps.push(...Object.keys(d ?? {}), ...Object.keys(dd ?? {}));
60
+ }
61
+ const req = await tryReadText(path.join(abs, 'requirements.txt'));
62
+ if (req) {
63
+ allDeps.push(...req.split('\n').map(l => l.split(/[>=<!]/)[0].trim().toLowerCase()).filter(Boolean));
64
+ }
65
+ if (await exists(path.join(abs, 'Cargo.toml')))
66
+ runtimes.push('rust');
67
+ if (await exists(path.join(abs, 'go.mod')))
68
+ runtimes.push('go');
69
+ if (await exists(path.join(abs, 'composer.json')))
70
+ runtimes.push('php');
71
+ if (await exists(path.join(abs, 'pnpm-lock.yaml')))
72
+ packageManager = 'pnpm';
73
+ else if (await exists(path.join(abs, 'bun.lock')))
74
+ packageManager = 'bun';
75
+ else if (await exists(path.join(abs, 'yarn.lock')))
76
+ packageManager = 'yarn';
77
+ else if (await exists(path.join(abs, 'package-lock.json')))
78
+ packageManager = 'npm';
79
+ else if (pkg)
80
+ packageManager = 'npm';
81
+ const extCounts = await countExts(abs);
82
+ const languages = [];
83
+ if ((extCounts.get('.ts') ?? 0) + (extCounts.get('.tsx') ?? 0) > 0)
84
+ languages.push('typescript');
85
+ if ((extCounts.get('.js') ?? 0) + (extCounts.get('.jsx') ?? 0) > 0)
86
+ languages.push('javascript');
87
+ if ((extCounts.get('.py') ?? 0) > 0)
88
+ languages.push('python');
89
+ if ((extCounts.get('.php') ?? 0) > 0)
90
+ languages.push('php');
91
+ if ((extCounts.get('.rs') ?? 0) > 0)
92
+ languages.push('rust');
93
+ if ((extCounts.get('.go') ?? 0) > 0)
94
+ languages.push('go');
95
+ const depsLow = allDeps.map(d => d.toLowerCase());
96
+ const frameworks = [...new Set([
97
+ ...depsLow.filter(d => FRONTEND.has(d)),
98
+ ...depsLow.filter(d => BACKEND.has(d)),
99
+ ...depsLow.filter(d => AGENT.has(d)),
100
+ ])];
101
+ const isFrontend = depsLow.some(d => FRONTEND.has(d));
102
+ const isBackend = depsLow.some(d => BACKEND.has(d));
103
+ const isAgent = depsLow.some(d => AGENT.has(d));
104
+ let projectType = 'library';
105
+ if (isAgent)
106
+ projectType = 'agent';
107
+ else if (isFrontend && isBackend)
108
+ projectType = 'fullstack';
109
+ else if (isFrontend)
110
+ projectType = 'frontend';
111
+ else if (isBackend)
112
+ projectType = 'backend';
113
+ return { languages, frameworks, projectType, packageManager, runtimes };
114
+ }
115
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../../src/analyzer/stack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { registerTools } from './tools/index.js';
4
+ const server = new McpServer({
5
+ name: 'graphmycode',
6
+ version: '0.1.0',
7
+ });
8
+ registerTools(server);
9
+ const transport = new StdioServerTransport();
10
+ await server.connect(transport);
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEhD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAA;AAEF,aAAa,CAAC,MAAM,CAAC,CAAA;AAErB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerTools(server: McpServer): void;
@@ -0,0 +1,169 @@
1
+ import { z } from 'zod';
2
+ import { buildGraph, computeMetrics, detectEntryPoints } from '../analyzer/graph.js';
3
+ import { detectStack } from '../analyzer/stack.js';
4
+ import { buildCommunities } from '../analyzer/communities.js';
5
+ import { generateAgentContext } from '../analyzer/agent.js';
6
+ function detectCycles(graph) {
7
+ const cycles = [];
8
+ const visited = new Set();
9
+ const stack = new Set();
10
+ const currentPath = [];
11
+ function dfs(id) {
12
+ if (stack.has(id)) {
13
+ const start = currentPath.indexOf(id);
14
+ cycles.push(currentPath.slice(start));
15
+ return;
16
+ }
17
+ if (visited.has(id))
18
+ return;
19
+ visited.add(id);
20
+ stack.add(id);
21
+ currentPath.push(id);
22
+ for (const imp of (graph.nodes.get(id)?.imports ?? []))
23
+ dfs(imp);
24
+ stack.delete(id);
25
+ currentPath.pop();
26
+ }
27
+ for (const id of graph.nodes.keys()) {
28
+ if (!visited.has(id))
29
+ dfs(id);
30
+ }
31
+ return cycles;
32
+ }
33
+ export function registerTools(server) {
34
+ server.tool('analyze_structure', { path: z.string().describe('Absolute path to the codebase root') }, async ({ path }) => {
35
+ const graph = await buildGraph(path);
36
+ const metrics = computeMetrics(graph);
37
+ return {
38
+ content: [{
39
+ type: 'text',
40
+ text: JSON.stringify({
41
+ nodes: [...graph.nodes.values()].map(n => ({
42
+ id: n.id, language: n.language,
43
+ fanIn: n.importedBy.length, fanOut: n.imports.length,
44
+ externalDeps: n.externalDeps,
45
+ })),
46
+ edges: graph.edges,
47
+ metrics: {
48
+ fileCount: metrics.fileCount,
49
+ edgeCount: metrics.edgeCount,
50
+ hotspots: metrics.hotspots,
51
+ deadCode: metrics.deadCode,
52
+ avgFanIn: Math.round(metrics.avgFanIn * 100) / 100,
53
+ avgFanOut: Math.round(metrics.avgFanOut * 100) / 100,
54
+ },
55
+ }, null, 2),
56
+ }],
57
+ };
58
+ });
59
+ server.tool('detect_stack', { path: z.string().describe('Absolute path to the codebase root') }, async ({ path }) => {
60
+ const stack = await detectStack(path);
61
+ return { content: [{ type: 'text', text: JSON.stringify(stack, null, 2) }] };
62
+ });
63
+ server.tool('get_file_dependencies', {
64
+ path: z.string().describe('Absolute path to the codebase root'),
65
+ file: z.string().describe('Relative file path within the codebase (e.g. src/utils.ts)'),
66
+ }, async ({ path, file }) => {
67
+ const graph = await buildGraph(path);
68
+ const node = graph.nodes.get(file);
69
+ if (!node) {
70
+ return {
71
+ content: [{
72
+ type: 'text',
73
+ text: JSON.stringify({ error: `File not found: ${file}`, available: [...graph.nodes.keys()] }),
74
+ }],
75
+ };
76
+ }
77
+ return {
78
+ content: [{
79
+ type: 'text',
80
+ text: JSON.stringify({
81
+ file: node.id,
82
+ imports: node.imports,
83
+ importedBy: node.importedBy,
84
+ externalDeps: node.externalDeps,
85
+ }, null, 2),
86
+ }],
87
+ };
88
+ });
89
+ server.tool('get_communities', { path: z.string().describe('Absolute path to the codebase root') }, async ({ path }) => {
90
+ const graph = await buildGraph(path);
91
+ const communities = buildCommunities(graph);
92
+ return {
93
+ content: [{
94
+ type: 'text',
95
+ text: JSON.stringify({ communities: [...communities.values()] }, null, 2),
96
+ }],
97
+ };
98
+ });
99
+ server.tool('find_entry_points', { path: z.string().describe('Absolute path to the codebase root') }, async ({ path }) => {
100
+ const graph = await buildGraph(path);
101
+ return {
102
+ content: [{
103
+ type: 'text',
104
+ text: JSON.stringify({ entryPoints: detectEntryPoints(graph, path) }, null, 2),
105
+ }],
106
+ };
107
+ });
108
+ server.tool('query_graph', {
109
+ path: z.string().describe('Absolute path to the codebase root'),
110
+ question: z.string().describe('Natural language question about the codebase'),
111
+ }, async ({ path, question }) => {
112
+ const graph = await buildGraph(path);
113
+ const q = question.toLowerCase();
114
+ let answer = '';
115
+ const evidence = [];
116
+ const importerMatch = q.match(/(?:qui[eé]n importa|who imports?|importa)\s+['"]?([^\s'"?]+)['"]?/);
117
+ if (importerMatch) {
118
+ const target = importerMatch[1];
119
+ const found = [...graph.nodes.entries()].find(([id]) => id.includes(target));
120
+ if (found) {
121
+ const [id, node] = found;
122
+ answer = `${node.importedBy.length} file(s) import \`${id}\`: ${node.importedBy.join(', ') || 'none'}`;
123
+ evidence.push({ file: id, metric: 'fanIn', value: node.importedBy.length });
124
+ }
125
+ else {
126
+ answer = `No file matching "${target}" found.`;
127
+ }
128
+ }
129
+ else if (q.includes('ciclo') || q.includes('cycle') || q.includes('circular')) {
130
+ const cycles = detectCycles(graph);
131
+ answer = cycles.length === 0
132
+ ? 'No circular import cycles detected.'
133
+ : `Found ${cycles.length} cycle(s):\n${cycles.map(c => c.join(' → ')).join('\n')}`;
134
+ cycles.forEach(c => evidence.push({ file: c[0], metric: 'cycle', value: c }));
135
+ }
136
+ else if (q.includes('muerto') || q.includes('dead') || q.includes('unused')) {
137
+ const m = computeMetrics(graph);
138
+ answer = m.deadCode.length > 0
139
+ ? `${m.deadCode.length} possibly unused file(s): ${m.deadCode.slice(0, 10).join(', ')}`
140
+ : 'No dead code detected.';
141
+ m.deadCode.forEach(f => evidence.push({ file: f, metric: 'fanIn', value: 0 }));
142
+ }
143
+ else if (q.includes('central') || q.includes('importado') || q.includes('fanin')) {
144
+ const sorted = [...graph.nodes.values()].sort((a, b) => b.importedBy.length - a.importedBy.length).slice(0, 5);
145
+ answer = 'Most central files:\n' + sorted.map(n => `${n.id}: fanIn=${n.importedBy.length}`).join('\n');
146
+ sorted.forEach(n => evidence.push({ file: n.id, metric: 'fanIn', value: n.importedBy.length }));
147
+ }
148
+ else if (q.includes('dependencia') || q.includes('dependencies') || q.includes('fanout')) {
149
+ const sorted = [...graph.nodes.values()].sort((a, b) => b.imports.length - a.imports.length).slice(0, 5);
150
+ answer = 'Files with most dependencies:\n' + sorted.map(n => `${n.id}: fanOut=${n.imports.length}`).join('\n');
151
+ sorted.forEach(n => evidence.push({ file: n.id, metric: 'fanOut', value: n.imports.length }));
152
+ }
153
+ else {
154
+ const m = computeMetrics(graph);
155
+ answer = `Graph: ${m.fileCount} files, ${m.edgeCount} edges, avgFanIn=${m.avgFanIn.toFixed(2)}, avgFanOut=${m.avgFanOut.toFixed(2)}`;
156
+ }
157
+ return { content: [{ type: 'text', text: JSON.stringify({ answer, evidence }, null, 2) }] };
158
+ });
159
+ server.tool('export_agent_context', {
160
+ path: z.string().describe('Absolute path to the codebase root'),
161
+ format: z.enum(['CLAUDE.md', 'AGENTS.md', 'both']).describe('Output format'),
162
+ }, async ({ path, format }) => {
163
+ const [graph, stack] = await Promise.all([buildGraph(path), detectStack(path)]);
164
+ const communities = buildCommunities(graph);
165
+ const files = await generateAgentContext(path, graph, stack, communities, format);
166
+ return { content: [{ type: 'text', text: JSON.stringify({ files }, null, 2) }] };
167
+ });
168
+ }
169
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "graphmycode-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for codebase structure analysis — dependency graphs, communities, hotspots, and agent context generation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "graphmycode-mcp": "scripts/cli.mjs"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "commands",
13
+ "scripts"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "start": "node dist/index.js",
19
+ "test": "vitest run --reporter=verbose",
20
+ "prepublishOnly": "npm run build",
21
+ "postinstall": "node scripts/postinstall.mjs"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "claude",
26
+ "codebase",
27
+ "dependency-graph",
28
+ "code-analysis",
29
+ "claude-code"
30
+ ],
31
+ "author": "Francisco Valero",
32
+ "license": "MIT",
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.12.0",
35
+ "zod": "^3.24.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0",
39
+ "typescript": "^5.7.0",
40
+ "vitest": "^3.1.0"
41
+ }
42
+ }
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ // Punto de entrada CLI: "graphmycode-mcp setup" o el servidor MCP
3
+ if (process.argv[2] === 'setup') {
4
+ const { setup } = await import('./setup.mjs');
5
+ setup();
6
+ } else {
7
+ await import('../dist/index.js');
8
+ }
@@ -0,0 +1,14 @@
1
+ import { setup } from './setup.mjs';
2
+
3
+ // Solo ejecutar en instalación global para no tocar ~/.claude en entornos de desarrollo
4
+ if (process.env.npm_config_global !== 'true') {
5
+ process.exit(0);
6
+ }
7
+
8
+ try {
9
+ setup();
10
+ } catch (e) {
11
+ // Nunca fallar la instalación de npm
12
+ console.warn('\nGraphMyCode: el setup automático falló:', e.message);
13
+ console.warn('Ejecuta el setup manualmente con: graphmycode-mcp setup\n');
14
+ }
@@ -0,0 +1,62 @@
1
+ import { execSync } from 'child_process';
2
+ import { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = path.dirname(__dirname);
9
+ const distPath = path.join(packageRoot, 'dist', 'index.js');
10
+ const nodePath = process.execPath;
11
+ const commandsSource = path.join(packageRoot, 'commands');
12
+ const commandsDest = path.join(os.homedir(), '.claude', 'commands');
13
+
14
+ function registerMcp() {
15
+ try {
16
+ execSync(`claude mcp add -s user graphmycode -- "${nodePath}" "${distPath}"`, {
17
+ stdio: 'pipe',
18
+ });
19
+ console.log('✓ MCP graphmycode registrado en Claude Code');
20
+ } catch (e) {
21
+ const output = (e.stderr?.toString() ?? '') + (e.stdout?.toString() ?? '');
22
+ if (output.includes('already') || output.includes('exists')) {
23
+ // Idempotente: ya estaba registrado, actualizar con remove+add
24
+ try {
25
+ execSync('claude mcp remove graphmycode -s user', { stdio: 'pipe' });
26
+ execSync(`claude mcp add -s user graphmycode -- "${nodePath}" "${distPath}"`, { stdio: 'pipe' });
27
+ console.log('✓ MCP graphmycode actualizado en Claude Code');
28
+ } catch {
29
+ console.log('✓ MCP graphmycode ya estaba registrado');
30
+ }
31
+ } else {
32
+ console.warn('⚠ No se pudo registrar el MCP automáticamente.');
33
+ console.warn(' Ejecuta manualmente:');
34
+ console.warn(` claude mcp add -s user graphmycode -- "${nodePath}" "${distPath}"`);
35
+ }
36
+ }
37
+ }
38
+
39
+ function copyCommands() {
40
+ if (!fs.existsSync(commandsSource)) return;
41
+
42
+ fs.mkdirSync(commandsDest, { recursive: true });
43
+
44
+ const files = fs.readdirSync(commandsSource).filter((f) => f.endsWith('.md'));
45
+ for (const file of files) {
46
+ fs.copyFileSync(path.join(commandsSource, file), path.join(commandsDest, file));
47
+ }
48
+ console.log(`✓ ${files.length} slash commands instalados en ~/.claude/commands/`);
49
+ console.log(' ' + files.map((f) => `/${f.replace('.md', '')}`).join(', '));
50
+ }
51
+
52
+ export function setup() {
53
+ if (!fs.existsSync(distPath)) {
54
+ console.error('Error: dist/index.js no encontrado. El paquete puede estar corrupto.');
55
+ process.exit(1);
56
+ }
57
+
58
+ console.log('\nConfigurando GraphMyCode MCP...\n');
59
+ registerMcp();
60
+ copyCommands();
61
+ console.log('\n¡Listo! Reinicia Claude Code para activar los cambios.\n');
62
+ }