Skip to content

Commit 4ed8fc1

Browse files
committed
Adding C++ extractor + documentation
1 parent 7ec3c45 commit 4ed8fc1

12 files changed

Lines changed: 2107 additions & 5 deletions

File tree

ARCHITECTURE.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ File Walker ──▶ Extractors ──▶ Fact Store ──▶ Cross-Repo Linke
111111
(apply (Go, Kotlin, (indexed by (only with 2+ (bidirectional)
112112
ignore Python, TS, kind / file / repos loaded) │
113113
globs) Swift, Ruby, name / repo) ▼
114-
OpenAPI) Explainers
114+
C++, OpenAPI) Explainers
115115
(cycles, layers,
116116
crossrepo)
117117
@@ -321,7 +321,7 @@ The bundled [`mcp-arch.yaml`](mcp-arch.yaml) ships a much fuller `ignore` list (
321321
|-------|-------------|---------|
322322
| `repo` | Repository root path | `"."` |
323323
| `ignore` | Glob patterns for files/dirs to skip | vendor, node_modules, .git, tests, build dirs, docs, config data, … |
324-
| `extractors` | Enabled extractors | `["go", "kotlin", "openapi", "python", "typescript", "swift", "ruby"]` |
324+
| `extractors` | Enabled extractors | `["cpp", "go", "kotlin", "openapi", "python", "typescript", "swift", "ruby"]` |
325325
| `explainers` | Enabled explainers | `["cycles", "layers", "crossrepo"]` |
326326
| `renderers` | Enabled renderers | `["llm_context"]` |
327327
| `output.dir` | Output directory for artifacts | `".enola"` |
@@ -341,6 +341,7 @@ Each extractor is detected by characteristic project files and then parses what
341341
| TypeScript | tree-sitter | `tsconfig.json`, `tsconfig.base.json`, or `package.json` with TypeScript (root or one level deep) |
342342
| Swift | tree-sitter | `Package.swift`, `.xcodeproj`, or `.xcworkspace` present |
343343
| Ruby | regex scanner | `Gemfile` present |
344+
| C++ | tree-sitter | a C++ source (`.cpp`/`.cc`/`.cxx`/`.hpp`/...) present, or a build file (`CMakeLists.txt`/`Makefile`/`meson.build`/`*.vcxproj`) plus any header |
344345
| OpenAPI | YAML/JSON scanner| any file containing `openapi:` or `swagger:` |
345346

346347
**Go** uses the standard-library parser directly, so symbols, methods, interfaces, imports, and call edges are exact.
@@ -357,6 +358,8 @@ Each extractor is detected by characteristic project files and then parses what
357358

358359
**Ruby** is Rails-aware: ActiveRecord models (`has_many`, `belongs_to`, scopes, table inference), the route DSL in `config/routes.rb` (resources, namespaces, member/collection blocks), and Packwerk package boundaries (`package.yml` dependency enforcement). It also tracks modules, classes, methods with visibility, mixins (`include`/`extend`/`prepend`), `ActiveSupport::Concern`, constants, and attributes.
359360

361+
**C++** is parsed with tree-sitter and handles the header/source split that defines the language. Classes, structs, unions, enums (incl. `enum class`), namespaces, free functions and methods, data members, and `typedef`/`using` aliases become symbol facts named `<dir>.<ns1::ns2::Class::member>` — enola's `<dir>.` module convention on the outside, native C++ `::` scope inside. Because an out-of-line definition `Class::method` (parsed from a `qualified_identifier`) yields the same canonical name as its in-class declaration, a dedup pass **merges a header's method prototype with its `.cpp` definition** into a single symbol (the definition wins for file/line and carries the call-graph edges). Base classes become `implements` edges; method bodies are walked for `calls`/`instantiates` edges; quoted `#include "x.h"` becomes a `dependency` resolved to the declaring module, while system `<...>` includes are skipped. Templates are unwrapped to their inner declaration and flagged `templated`, and the walker descends through `#if`/`#ifdef` preprocessor guards (so code wrapped in `#if defined(HAVE_*)` and headers behind include guards are still extracted). *Limitation:* header/source merging relies on the `.h` and `.cpp` living in the same directory (the common layout); split `include/` + `src/` trees are not merged.
362+
360363
---
361364

362365
## Output artifacts

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,22 @@ Binaries are published for Linux, macOS (amd64/arm64), and Windows (amd64). You
101101

102102
### Connect it to your agent
103103

104-
Add enola to your MCP client configuration. For example, in Cursor's `mcp.json`:
104+
**Claude Code** — register enola as an MCP server with one command. This assumes the `enola` binary is on your `PATH` (the install script above puts it in `~/.local/bin`):
105+
106+
```bash
107+
claude mcp add enola enola /path/to/enola/mcp-arch.yaml
108+
```
109+
110+
The shape is `claude mcp add <name> <command> [args…]`: the first `enola` names the server, the second is the binary, and the trailing path is the config it reads. That config's `repo:` is only the *default* repository — you can still snapshot any repo by passing `repo_path` to `generate_snapshot`. Verify it registered with `claude mcp list`, then start Claude Code and ask it to generate a snapshot.
111+
112+
**Cursor / other MCP clients** — add enola to your client's MCP configuration. For example, in Cursor's `mcp.json`:
105113

106114
```json
107115
{
108116
"mcpServers": {
109117
"enola": {
110-
"command": "enola"
118+
"command": "enola",
119+
"args": ["/path/to/enola/mcp-arch.yaml"]
111120
}
112121
}
113122
}
@@ -149,6 +158,7 @@ Working across several repos? Generate the first, then add the rest with append
149158
| Kotlin | `build.gradle(.kts)` with Kotlin/Android (Compose / Hilt / Room aware) |
150159
| Swift | `Package.swift`, `.xcodeproj`, `.xcworkspace` (SwiftUI / UIKit aware) |
151160
| Ruby | `Gemfile` (Rails / ActiveRecord / Packwerk aware) |
161+
| C++ | `.cpp`/`.hpp`/… source or `CMakeLists.txt`/`Makefile` + header (header/source method merging, namespaces, templates) |
152162
| OpenAPI | any spec with an `openapi:` / `swagger:` key |
153163

154164
Framework- and platform-specific detection for each language is described in **[ARCHITECTURE.md → Supported languages](ARCHITECTURE.md#supported-languages)**.

examples/cpp.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# enola configuration for a C++ project.
2+
#
3+
# Detection: The C++ extractor activates when an unambiguous C++ source
4+
# (.cpp/.cc/.cxx/.hpp/...) is present, or a build file
5+
# (CMakeLists.txt/Makefile/meson.build/*.vcxproj) plus any header.
6+
# Features: Classes, structs, unions, enums (incl. enum class), free functions
7+
# and methods (inline AND out-of-line "Class::method" definitions,
8+
# merged into one symbol), namespaces (qualified "<dir>.ns::Class::member"
9+
# naming), inheritance edges (base classes), a call graph
10+
# (calls / instantiations), and #include dependency edges. Templates are
11+
# unwrapped and flagged; system <...> includes are skipped.
12+
13+
repo: "."
14+
ignore:
15+
# VCS and tooling
16+
- ".git/**"
17+
- ".enola/**"
18+
# CMake / build output (at any depth)
19+
- "build/**"
20+
- "**/build/**"
21+
- "cmake-build-*/**"
22+
- "**/cmake-build-*/**"
23+
- "CMakeFiles/**"
24+
- "**/CMakeFiles/**"
25+
- "**/*.o"
26+
- "**/*.obj"
27+
- "**/*.a"
28+
- "**/*.so"
29+
- "**/*.dylib"
30+
# Vendored third-party code
31+
- "contrib/**"
32+
- "third_party/**"
33+
- "extern/**"
34+
# Pure C sources (handled by a future C extractor, not this one)
35+
- "**/*.c"
36+
# Documentation / config / data
37+
- "**/*.md"
38+
- "**/*.yml"
39+
- "**/*.yaml"
40+
- "**/*.json"
41+
extractors:
42+
- cpp
43+
explainers:
44+
- cycles
45+
- layers
46+
- crossrepo
47+
renderers:
48+
- llm_context
49+
output:
50+
dir: ".enola"
51+
max_context_tokens: 16000

examples/full.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# - typescript (detection: tsconfig.json or package.json with TypeScript)
1111
# - swift (detection: Package.swift, .xcodeproj, or .xcworkspace)
1212
# - ruby (detection: Gemfile)
13+
# - cpp (detection: .cpp/.hpp/... or CMakeLists.txt/Makefile + header)
1314

1415
repo: "."
1516
ignore:
@@ -83,6 +84,7 @@ ignore:
8384
- "**/.env*"
8485

8586
extractors:
87+
- cpp
8688
- go
8789
- kotlin
8890
- typescript

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/modelcontextprotocol/go-sdk v1.3.0
77
github.com/tree-sitter-grammars/tree-sitter-kotlin v1.1.0
88
github.com/tree-sitter/go-tree-sitter v0.24.0
9+
github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148
910
github.com/tree-sitter/tree-sitter-python v0.23.6
1011
github.com/tree-sitter/tree-sitter-typescript v0.23.2
1112
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ github.com/modelcontextprotocol/go-sdk v1.3.0 h1:gMfZkv3DzQF5q/DcQePo5rahEY+sguy
1212
github.com/modelcontextprotocol/go-sdk v1.3.0/go.mod h1:AnQ//Qc6+4nIyyrB4cxBU7UW9VibK4iOZBeyP/rF1IE=
1313
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1414
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15+
github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8 h1:DxgjlvWYsb80WEN2Zv3WqJFAg2DKjUQJO6URGdf1x6Y=
16+
github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE=
1517
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
1618
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
1719
github.com/tree-sitter-grammars/tree-sitter-kotlin v1.1.0 h1:SWIUDASa+WPhDDem1U5IJpYwQEezkqXrUI61OcnORzM=

internal/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func Default() *Config {
5757
"**/Pods/**",
5858
"**/.gradle/**",
5959
},
60-
Extractors: []string{"go", "kotlin", "openapi", "python", "typescript", "swift", "ruby"},
60+
Extractors: []string{"cpp", "go", "kotlin", "openapi", "python", "typescript", "swift", "ruby"},
6161
Explainers: []string{"cycles", "layers", "crossrepo"},
6262
Renderers: []string{"llm_context"},
6363
Output: OutputConfig{

0 commit comments

Comments
 (0)