Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5bcca41
feat(webview): add hub score visual indicator to ReactFlow file nodes
magic5644 Jun 27, 2026
0d361a6
feat(shared): add GraphNodeMetadata and nodeMetadata to GraphData (F2)
magic5644 Jun 29, 2026
72e8ace
feat(cli): add HTML vis.js export command
magic5644 Jun 29, 2026
99b986a
feat(analyzer): add Louvain community detection with webview legend
magic5644 Jun 30, 2026
5d4e27a
feat(repl): add /export command for HTML vis.js export
magic5644 Jun 30, 2026
84f36ed
feat(F4): add community legend to webview and HTML export, fix REPL /…
magic5644 Jun 30, 2026
4ac7f3b
fix(tests): normalize path assertions in ExportHtmlCommand.test.ts fo…
magic5644 Jun 30, 2026
04ba364
fix(repl): accept /export without --format html, simplify help text
magic5644 Jun 30, 2026
5ec55fb
feat(export): scope HTML export to current REPL path context or posit…
magic5644 Jun 30, 2026
fa13bfa
feat(export): improve vis.js graph layout, add toolbar with Fit/Physi…
magic5644 Jun 30, 2026
c252ddc
feat(F4): replace Louvain with path-based community detection, legend…
magic5644 Jun 30, 2026
ce80a55
fix(F4): community domain = first non-umbrella subdir (src/analyzer →…
magic5644 Jun 30, 2026
dac09c7
feat(F4): thread workspaceRoot into community detection for VS Code e…
magic5644 Jun 30, 2026
141a397
fix(F4): use path.relative(workspaceRoot) for community legend labels
magic5644 Jun 30, 2026
9de7a86
fix(repl): /export respects /path workspace narrowing as default scope
magic5644 Jun 30, 2026
edff13c
fix(tests/Windows): normalizePath on outputPath + assertions (Règle 03)
magic5644 Jun 30, 2026
4b9c9fe
feat(F4): add user stories for community detection feature
magic5644 Jun 30, 2026
4cac21f
docs(v1.11.0): document F4 community detection, HTML export, REPL /ex…
magic5644 Jun 30, 2026
9833592
fix(tests/Windows): normalizePath on path.join assertion (Règle 03)
magic5644 Jun 30, 2026
0c8ce1a
feat(export): refactor HTML export logic and enhance path normalization
magic5644 Jun 30, 2026
a7e98fb
feat(mcp): wire communityId + hubScore into crawl_dependency_graph nodes
magic5644 Jul 1, 2026
dbf063c
tests(mcp): cover buildNodeInfo metadata + crawl communityId/hubScore
magic5644 Jul 1, 2026
410bb9c
fix(mcp/gate6): normalize node keys for metadata lookup + doc MCP com…
magic5644 Jul 1, 2026
79d58ab
fix(community): handle monorepo nested source roots (vue/src/, angula…
magic5644 Jul 1, 2026
97b2417
fix(community): split nested source roots by last umbrella dir
magic5644 Jul 1, 2026
264ec5b
fix(repl): add /export to slash command autocomplete registry
magic5644 Jul 1, 2026
46cdd5d
feat(webview): add showCommunities setting to toggle community display
magic5644 Jul 2, 2026
18a7edf
test(analyzer): crash-safe sweep of temp_*.py python fixtures
magic5644 Jul 2, 2026
d435bbc
fix(gitignore): add .tokensave directory to ignore list
magic5644 Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ _bmad-output

# VS Code settings (may contain user-specific or machine-specific settings)
.vscode/settings.json

.tokensave/
.serena

/graphify-out/
Expand All @@ -228,3 +228,6 @@ graphify-out/cost.json # local only
# graphify-out/cache/ # optional: commit for speed, skip to keep repo small
# Generated wiki (graph-it wiki output)
/wiki/

# Temp fixtures generated by PythonSymbolAnalyzer property tests (crash orphans)
/tests/fixtures/python-project/temp_*.py
2 changes: 2 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ tsconfig.tsbuildinfo
meta.json
metadata.json
DEVELOPMENT.md
CLAUDE.md
.claude/**

# Source code (bundled in dist/)
src/**
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ graph-it wiki # Generate a navigable markdown wiki from the ca
graph-it serve # Launch MCP stdio server (for AI clients)
graph-it tool --list # List all 22 CLI analysis tools
graph-it tool <mcp-tool> [args] # Run any MCP tool directly from the terminal
graph-it export [path] [--format html] # Export graph as standalone HTML (vis.js)
graph-it update # Update graph-it to the latest version
graph-it install # Symlink the binary into your system PATH (opt-in)
```
Expand Down Expand Up @@ -448,7 +449,7 @@ This output can be pasted directly into any Markdown renderer (GitHub, Notion, V

**Use as MCP server (no VS Code):** Run `graph-it serve` and point your AI client at it — see [Manual MCP Server Configuration](#manual-mcp-server-configuration).

**Interactive REPL:** Run `graph-it` with no arguments to enter interactive mode. Type `/query how does X work` to ask natural language questions about your codebase (no quotes needed). Use `/wiki` to generate a navigable markdown wiki from the call graph. Other slash commands: `/trace`, `/summary`, `/architecture`, `/check`, `/cycles`, `/format`, `/help`.
**Interactive REPL:** Run `graph-it` with no arguments to enter interactive mode. Type `/query how does X work` to ask natural language questions about your codebase (no quotes needed). Use `/wiki` to generate a navigable markdown wiki from the call graph. Other slash commands: `/trace`, `/summary`, `/architecture`, `/check`, `/cycles`, `/export` (export dependency graph as standalone HTML), `/format`, `/help`.

**Full CLI reference:** See **[docs/CLI.md](docs/CLI.md)** for complete documentation on every command, all options, output format examples, advanced workflows, and the full MCP tools reference.

Expand Down
23 changes: 23 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## v1.11.0

### Added — Path-based community detection (F4)

- **`PathCommunityDetector`**: Replaces Louvain algorithm. Groups files by first non-umbrella subdirectory (e.g. `src/analyzer/Spider.ts` → community `analyzer`). Umbrella dirs skipped: `src`, `tests`, `test`, `lib`, `app`, `packages`, `dist`, `out`.
- **`Spider.workspaceRoot` getter**: Exposes `config.rootDir` for downstream consumers. Threaded from `Spider` → `GraphViewService` → `computeNodeMetadata` → `detectPathCommunities`.
- **Community legend in webview**: Header "Import clusters", subtitle "Groups of closely connected files". `buildCommunityLegend` uses `path.relative(workspaceRoot, filePath)` for correct labels on scoped exports.

### Added — HTML vis.js export

- **`graph-it export --format html` / REPL `/export`**: Exports an interactive vis.js graph. Toolbar with Fit / Physics / Reset buttons. Physics disabled after stabilization so nodes stay when dragged. Community legend overlay (bottom-left) with domain names and palette colors.
- **REPL `/export` command**: Standalone (no `--format html` needed). Optional `--output <path>` / `-o <path>` flag. Respects `/path` workspace narrowing as default scope; falls back to `state.lastFile` → `state.workspaceRoot` → full workspace.

### Added — MCP `crawl_dependency_graph`

- **`hubScore` + `communityId` on each node**: `crawl_dependency_graph` now populates both fields using the same `PathCommunityDetector` logic as the VS Code extension and CLI. All three surfaces are aligned.

### Fixed

- **Community label showed workspace dir instead of functional domain**: Shallow files reduced `workspacePrefixLen` cap incorrectly — fixed with `path.relative(workspaceRoot)` in `buildCommunityLegend`.
- **`normalizePath` applied to `outputPath`**: Cross-platform path compliance (Règle 03).
- **Windows CI path assertions in `ExportHtmlCommand` tests**: Test comparisons now use `normalizePath` to handle Windows separators.

## v1.10.0

### Added — graph-it wiki
Expand Down
7 changes: 6 additions & 1 deletion docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ graph-it [options]
| `/check` | Find unused exports |
| `/format` | Set preferred output format for the session |
| `/command` | Run a raw CLI command line inside REPL |
| `/export` | Export dependency graph as standalone HTML (vis.js). Optional `--output <path>` / `-o <path>`. Scope: active file context → workspace scope (set via `/path`) → full workspace |
| `/help` | Show REPL command help |
| `/quit` | Exit interactive mode |

Expand Down Expand Up @@ -968,7 +969,11 @@ graph-it tool crawl_dependency_graph --entryFile=/abs/path/to/index.ts
graph-it tool crawl_dependency_graph --entryFile=/abs/path/to/index.ts --maxDepth=5
```

**Output fields:** `files[]`, `edges[]`, `cycles[]`, `totalFiles`, `totalEdges`
**Output fields:** `nodes[]`, `edges[]`, `circularDependencies[]`, `nodeCount`, `edgeCount`

Each node in `nodes[]` includes:
- `hubScore` (0–1): proportion of workspace files that import this node
- `communityId` (0+): path-based functional cluster — 0 = isolated, 1+ = first non-umbrella subdirectory group (e.g. files under `src/analyzer/` → same `communityId`)

---

Expand Down
57 changes: 57 additions & 0 deletions docs/architecture/ADR-F2-01-hubscore-source-of-truth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ADR-F2-01 : Source de vérité hubScore

## Status

Proposed

## Context

Story F2 ajoute `nodeMetadata?: Record<string, GraphNodeMetadata>` à `GraphData` (src/shared/graph-types.ts).
`GraphNodeMetadata` inclut `hubScore: number`.

`parentCounts?: Record<string, number>` existe déjà dans `GraphData` et est transmis au webview.
F1 (mergée) a implémenté `computeHubScores(visibleNodes, parentCounts)` dans `src/webview/components/reactflow/buildGraph.ts` — normalise sur les noeuds visibles uniquement.
`src/analyzer/wiki/WikiGenerator.ts` calcule déjà un `hubScore` workspace-wide pour les articles wiki.

Consommateurs qui ont besoin d'un hubScore stable : MCP (`graphitlive_analyze_dependencies` retourne `GraphData` sérialisé), CLI, export HTML F3, community detection F4 (Louvain).

## Decision

**Option A retenue : Spider (analyzer layer) est la seule source de vérité pour `hubScore`.**

Spider calcule `hubScore = parentCounts[file] / max(parentCounts)` normalisé [0-1] sur le workspace entier, lors du full-crawl uniquement (pas de mise à jour incrémentale partielle pour éviter un `maxParentCount` sous-estimé). Le résultat est stocké dans `nodeMetadata` de `GraphData`.

`computeHubScores` de F1 est remplacé par une lecture directe de `nodeMetadata[path].hubScore` dans `buildGraph.ts`.

Le calcul `hubScore` de `WikiGenerator.ts` est unifié : WikiGenerator lit `nodeMetadata.hubScore` depuis `GraphData` au lieu de recalculer.

## Consequences

**Positives**
- Source unique : un seul endroit à maintenir, cohérence garantie entre MCP, CLI, export HTML (F3) et webview.
- F3 (export HTML) et F4 (Louvain) lisent `nodeMetadata` sans recalcul.
- Conforme Règle 01 : pur Node.js dans l'analyzer, zéro import vscode.
- Suppression de duplication : calcul Wiki absorbé, `computeHubScores` webview supprimé.

**Negatives**
- Hub local dans un sous-graphe filtré peut afficher un score faible (exemple : noeud central d'un sous-graphe de 10 fichiers sur un workspace de 1000). Accepté : la vue webview gagne en cohérence avec MCP et CLI au détriment de la relativité locale.
- `nodeMetadata` doit etre versionné avant F4 : si Louvain modifie la sémantique de `hubScore`, un champ `hubScoreVersion?: string` ou une migration breaking sera nécessaire.

**Contrats modifiés**
- `src/shared/graph-types.ts` : ajout `GraphNodeMetadata` + `nodeMetadata?` sur `GraphData`.
- `src/analyzer/Spider.ts` (ou `GraphCrawler`) : calcul `hubScore` sur full-crawl, guard `max === 0 → score = 0`.
- `src/analyzer/wiki/WikiGenerator.ts` : lecture `nodeMetadata.hubScore` au lieu du calcul interne.
- `src/webview/components/reactflow/buildGraph.ts` : suppression `computeHubScores`, lecture `nodeMetadata[path]?.hubScore ?? 0`.
- `src/mcp/` : Zod schema `graphitlive_analyze_dependencies` étendu avec `nodeMetadata` optionnel.

## Alternatives rejetées

**Option B — buildGraph.ts (webview) uniquement** : score instable (change à chaque filtre de vue), absent de MCP et CLI. `parentCounts` brut transmis au webview ne suffit pas : MCP consomme `GraphData` sérialisé sans passer par le webview, et F3/F4 opèrent hors bundle webview.

**Option C — Double calcul (global + local)** : rejetée sur recommandation de Marine. Deux sources sans règle de priorité formelle = comportement indéfini selon le chemin d'exécution (webview vs MCP vs export). Duplication de logique de normalisation dans deux bundles séparés (`dist/extension.js` et `dist/webview.js`) : toute évolution de formule (F4 Louvain) doit être propagée manuellement — risque de régression silencieux.

---

Date : 2026-06-27
Auteur : Antoine (architecte système)
Review : Marine (devil's advocate)
22 changes: 22 additions & 0 deletions docs/architecture/ADR-F3-01-visjs-inlining-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ADR-F3-01 — Stratégie d'inlining vis.js dans le HTML autoportant

**Date** : 2026-06-29
**Statut** : Accepted

## Contexte
Story F3 produit un HTML autoportant (zéro dépendance réseau) avec vis-network (~1.5MB minifié). CLI distribué via `npm install -g graph-it-live` ; `vis-network` est dans `dependencies`, présent dans le `node_modules` global du package.

## Décision
Lecture au runtime via `createRequire(import.meta.url)` (compatible ESM) + `fs.readFileSync` sur le bundle `vis-network/standalone/umd/vis-network.min.js`. Inlining dans `<script>` du HTML généré.

## Conséquences
+ HTML totalement autoportant, consultable hors ligne
+ `require.resolve` depuis le module CLI résout dans le bon `node_modules` global
+ Erreur explicite si `vis-network` absent (`MODULE_NOT_FOUND`)
− HTML généré ~1.5MB — acceptable pour un artefact de visualisation one-shot
− I/O synchrone ~1.5MB par export — acceptable pour CLI non-interactif

## Alternatives rejetées
- CDN (unpkg) : rompt l'exigence autoportant
- Bundle dans `dist/graph-it.js` via esbuild : gonfle le CLI de 1.5MB, risque .map dans .vsix
- Cache `~/.cache/graph-it/` : complexité injection/invalidation injustifiée
20 changes: 20 additions & 0 deletions docs/architecture/ADR-F3-02-vis-network-dependency-position.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ADR-F3-02 — `vis-network` : dependencies vs devDependencies

**Date** : 2026-06-29
**Statut** : Accepted

## Contexte
`vis-network` est lu à runtime par le CLI via `fs.readFileSync` (pas importé statiquement). Position dans `package.json` détermine présence en install production et impact sur bundle esbuild.

## Décision
`dependencies` (pas `devDependencies`).

## Conséquences
+ `npm install -g graph-it-live` installe `vis-network` → `require.resolve` fonctionne
+ Pas d'import statique → esbuild ne bundle pas vis-network → zéro .map dans .vsix (règle 02 ✓)
− `node_modules` global +~8MB — acceptable pour dépendance runtime légitime
− Utilisateurs n'utilisant pas `--format html` portent le poids

## Alternatives rejetées
- `devDependencies` : absent en `npm install --production` et CI Docker → `MODULE_NOT_FOUND` en production
- `optionalDependencies` : sémantique incorrecte (requis pour la feature, pas optionnel)
59 changes: 59 additions & 0 deletions docs/architecture/ADR-F4-01-community-detection-algo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# ADR-F4-01 : Algorithme de community detection (Louvain)

## Status

Proposed

## Context

Story F4 ajoute la community detection au graphe de dépendances (50-500 noeuds, arêtes dirigées import → dépendance). Le résultat alimente le colour-coding des noeuds dans le webview ReactFlow, les exports HTML (F3), et les outils MCP. L'algo doit opérer dans `src/analyzer/` (Règle 01 : pur Node.js, NO vscode imports). Le bundle CLI `dist/graph-it.js` pèse actuellement 10.4 MB (WASM sql.js inclus) — toute dépendance npm s'additionne à ce poids via esbuild CJS.

## Decision

**Option A retenue : implémentation maison Louvain phase-1 uniquement (~150 lignes TypeScript, zéro dépendance npm).**

Justification par critère :

1. **Qualité algorithmique à l'échelle cible.** Louvain phase-1 (optimisation locale de la modularité) converge vers une partition de qualité acceptable sur des graphes < 500 noeuds. La phase-2 (agrégation hiérarchique) apporte un gain marginal à cette échelle — elle devient utile au-delà de ~5 000 noeuds selon la littérature. Les graphes de dépendances TypeScript typiques de Graph-It-Live restent en dessous de ce seuil.

2. **Impact bundle CLI.** `graphology` dépaqueté = 2.73 MB ; après tree-shaking esbuild le résidu reste estimé à 300-500 KB (graphology expose de nombreux modules non-tree-shakable en CJS). Sur un bundle de 10.4 MB, l'ajout de 400 KB est acceptable en absolu, mais introduit quatre dépendances transitives (`graphology-indices`, `graphology-utils`, `mnemonist`, `pandemonium`) dont la compatibilité CJS/ESM avec esbuild n'est pas vérifiée — risque de conflit de build non justifié pour un gain de qualité nul à < 500 noeuds.

3. **Déterminisme.** L'implémentation maison utilise un Fisher-Yates shuffle avec seed fixe, garantissant une partition reproductible entre runs. `graphology-communities-louvain` propose `randomWalk: false` mais ne documente pas de seed externe — le déterminisme est un contrat nécessaire pour la stabilité des exports HTML et des réponses MCP.

4. **Arêtes dirigées.** Louvain maison traite les arêtes dirigées comme non-dirigées (somme in+out pour le calcul de modularité), cohérent avec la sémantique de couplage structurel entre fichiers. graphology-communities-louvain supporte nativement le mode `directed` mais sa modularité dirigée produit des communautés plus fragmentées sur des DAGs d'import — comportement moins lisible pour l'utilisateur final.

5. **Maintenance.** 150 lignes TypeScript internes, sans surface d'API tierce à maintenir. Aucune montée de version à tracker.

## Consequences

**Positives**
- Zéro ajout au bundle CLI et .vsix — Règle 02 inchangée, pas de .map supplémentaire.
- Déterminisme garanti par seed explicite (Fisher-Yates) : exports HTML et réponses MCP stables entre runs.
- Aucune dépendance transitive (graphology-indices, mnemonist, pandemonium) à auditer pour Règle 01 et compatibilité esbuild CJS.
- Implémentation localisée dans `src/analyzer/callgraph/` ou `src/analyzer/communityDetection.ts` — testable unitairement sans mock npm.

**Negatives**
- Phase-2 absente : sur des workspaces exceptionnellement denses (> 500 noeuds, graphes monorepo), la qualité de partition peut être inférieure à graphology. Accepté : hors du profil cible actuel.
- Code métier à maintenir en interne. Si l'algorithme évolue (Leiden, SBM), la migration sera manuelle. Mitigé : l'interface de sortie (`Map<string, number>` fileId → communityId) est stable et remplaçable sans impact sur les couches supérieures.
- Risque d'implémentation incorrecte du calcul de modularité (delta Q). Mitigé : tests unitaires obligatoires sur graphes de référence avec partition connue (triangle, bipartite, chaîne).

**Contrats modifiés**
- `src/shared/graph-types.ts` : ajout `communityId?: number` dans `GraphNodeMetadata` (déjà prévu par ADR-F2-01).
- `src/analyzer/communityDetection.ts` (nouveau fichier) : export `detectCommunities(nodes: string[], edges: [string, string][]): Map<string, number>` — pur Node.js, NO vscode.
- `src/analyzer/Spider.ts` (ou `GraphCrawler`) : appel `detectCommunities` après full-crawl, résultat écrit dans `nodeMetadata[path].communityId`.
- `src/webview/components/reactflow/buildGraph.ts` : lecture `nodeMetadata[path]?.communityId` pour la couleur de noeud.
- `src/mcp/types.ts` : Zod schema `nodeMetadata` étendu avec `communityId?: number`.

## Alternatives rejetées

**Option B — `graphology-communities-louvain` v2.0.2 + `graphology` v0.26.0** : rejetée. Gain de qualité nul à < 500 noeuds. Ajout estimé 300-500 KB au bundle CJS post-esbuild. Quatre dépendances transitives dont la compatibilité CJS/ESM avec esbuild bundler n'est pas vérifiée (risque de build cassé). Déterminisme par seed externe non documenté. Rapport coût/bénéfice défavorable au profil cible.

**Option C — Algorithme de Leiden (état de l'art post-Louvain)** : rejetée. Aucune implémentation TypeScript mature disponible sans dépendances lourdes. Complexité d'implémentation maison élevée (correction des coupures arbitraires de Louvain). Hors scope F4.

**Option D — Label Propagation (LPA)** : rejetée. Convergence non garantie, résultat non déterministe sans mécanisme de tie-breaking complexe. Qualité inférieure à Louvain sur graphes structurés en modules denses.

---

Date : 2026-06-29
Auteur : Antoine (architecte système)
Review : Marine (devil's advocate) — à compléter avant GATE-5
Loading
Loading