purecontext-mcp 1.1.1 → 1.1.2

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/docs/dev/API_STABILITY.md +0 -319
  3. package/docs/dev/DECISIONS.md +0 -22
  4. package/docs/dev/DOCUMENTATION_PLAN.md +0 -113
  5. package/docs/dev/PHASE10_TASKS.md +0 -476
  6. package/docs/dev/PHASE11_TASKS.md +0 -385
  7. package/docs/dev/PHASE12_TASKS.md +0 -335
  8. package/docs/dev/PHASE13_TASKS.md +0 -381
  9. package/docs/dev/PHASE14_TASKS.md +0 -371
  10. package/docs/dev/PHASE15_TASKS.md +0 -256
  11. package/docs/dev/PHASE16_TASKS.md +0 -314
  12. package/docs/dev/PHASE17_TASKS.md +0 -321
  13. package/docs/dev/PHASE18_TASKS.md +0 -345
  14. package/docs/dev/PHASE19_TASKS.md +0 -261
  15. package/docs/dev/PHASE1_TASKS.md +0 -443
  16. package/docs/dev/PHASE20_TASKS.md +0 -280
  17. package/docs/dev/PHASE21_TASKS.md +0 -355
  18. package/docs/dev/PHASE22_TASKS.md +0 -371
  19. package/docs/dev/PHASE23_TASKS.md +0 -274
  20. package/docs/dev/PHASE24_TASKS.md +0 -326
  21. package/docs/dev/PHASE25_TASKS.md +0 -452
  22. package/docs/dev/PHASE26_TASKS.md +0 -253
  23. package/docs/dev/PHASE27_TASKS.md +0 -410
  24. package/docs/dev/PHASE2_TASKS.md +0 -328
  25. package/docs/dev/PHASE3_TASKS.md +0 -571
  26. package/docs/dev/PHASE4_TASKS.md +0 -531
  27. package/docs/dev/PHASE5_TASKS.md +0 -835
  28. package/docs/dev/PHASE6_TASKS.md +0 -347
  29. package/docs/dev/PHASE7_TASKS.md +0 -257
  30. package/docs/dev/PHASE8_TASKS.md +0 -299
  31. package/docs/dev/PHASE9_TASKS.md +0 -320
  32. package/docs/dev/PureContext_MCP_PRD_v1.0.docx +0 -0
  33. package/docs/dev/SELF_HOSTING.md +0 -142
  34. package/docs/dev/TEAM_SETUP.md +0 -316
  35. package/docs/dev/TELEMETRY.md +0 -99
  36. package/docs/dev/feature-analysis.md +0 -305
  37. package/docs/dev/phase-1-notes.md +0 -3
@@ -1,835 +0,0 @@
1
- # Phase 5 — Task Breakdown
2
-
3
- **Goal**: Deliver framework adapters for Python and Go (whose handlers already exist), then add four new language handlers — PHP, Ruby, Kotlin, and C — with their accompanying framework adapters. This phase brings PureContext to near-parity with jCodeMunch's language breadth for the most widely-used web and systems languages.
4
-
5
- **Scope rationale**: Phase 4 completed Rust, Java, and C# handlers along with Angular, NestJS, Express, and Fastify adapters. Python and Go handlers were completed in Phase 3 but their framework ecosystems (Flask, Django, FastAPI, Gin, Echo, Fiber) were left unaddressed. PHP, Ruby, and Kotlin are the next three languages on jCodeMunch's list that PureContext is missing. C is a systems-language quick win — its grammar is official and stable, its AST is flat and straightforward. Delivering all of these together in Phase 5 closes the largest competitive gap in a single phase.
6
-
7
- **Approach**: Tasks are sequenced so each one builds on the previous. Complete them in order. Each task should be independently testable before moving to the next. Framework adapter tasks for Python and Go come first — they have no handler dependency and deliver immediate value. Language handler tasks are sequenced from simplest (C) to most complex (Ruby, Kotlin). Each handler is paired immediately with its framework adapters before moving to the next language.
8
-
9
- ---
10
-
11
- ## Task 43: Extend SymbolKind for New Languages
12
-
13
- Extend the `SymbolKind` union type in `src/core/types.ts` before any new handler can be compiled. All downstream type checks depend on this union being complete.
14
-
15
- **Deliverables:**
16
- - Update `src/core/types.ts` — extend the `SymbolKind` union:
17
- ```typescript
18
- export type SymbolKind =
19
- // Existing (unchanged)
20
- | 'function' | 'class' | 'method' | 'const' | 'type'
21
- | 'interface' | 'enum' | 'component' | 'composable'
22
- | 'hook' | 'route' | 'decorator' | 'middleware'
23
- // Phase 5 additions
24
- | 'model' // ORM models: Django Model, Rails ActiveRecord, Laravel Eloquent
25
- | 'view' // Django views, Rails controllers (request handler entry points)
26
- | 'struct' // C/C++ struct — distinct from class; plain data container
27
- | 'macro' // C/C++ #define object-like macros; distinct from const
28
- | 'signal'; // Django signals, framework event emitters
29
- ```
30
-
31
- **Rationale for each addition:**
32
- - `model`: AI agents frequently navigate to ORM models by name. Using `class` loses the distinction in projects where every file is a class.
33
- - `view`: Django views and Rails controllers are the request-handler entry points agents most often need. A dedicated kind avoids grep over all classes.
34
- - `struct`: C (and later C++) projects have a clear semantic distinction between `struct` (pure data) and `class` (data + behaviour). Collapsing them into `class` confuses AI navigation.
35
- - `macro`: C `#define NAME value` is not a constant in the language-theoretic sense. A separate kind prevents AI agents from conflating `#define MAX_SIZE 1024` with `const int maxSize = 1024`.
36
- - `signal`: Django signal receivers and similar event hooks are a frequent query target; using `function` or `decorator` is ambiguous.
37
-
38
- **Backward-compatibility:** All five additions are appended to the union. Existing tests reference specific kind values and will not break. The SQLite `kind` column stores TEXT — no migration needed; existing indexed repos simply won't have the new kinds until re-indexed.
39
-
40
- **Verify:** `npm run build` succeeds with no type errors after the change. All existing tests still pass.
41
-
42
- **Tests:** Add a type-level test asserting all new kinds are accepted by functions that take `SymbolKind`. No runtime tests needed — this is a compile-time change.
43
-
44
- ---
45
-
46
- ## Task 44: Flask Adapter
47
-
48
- Extract route and view symbols from Flask applications. Flask's route registration uses Python function decorators, which are already parsed cleanly by the Python handler from Phase 3.
49
-
50
- **Deliverables:**
51
- - `src/adapters/flask.ts` — implements `FrameworkAdapter`
52
- - `name`: `'flask'`
53
- - `extensions()`: `[]` — `.py` files are already handled by the Python handler
54
- - `detect(projectRoot)`:
55
- - Read `requirements.txt` if present — check for a line matching `/^\s*[Ff]lask[>=\s]/`
56
- - Read `pyproject.toml` if present — check `project.dependencies` or `tool.poetry.dependencies` array for `"flask"` (case-insensitive)
57
- - Return `true` if either check matches
58
- - `fileFilter(filePath)`: returns `true` for `.py` files only
59
- - `extractFrameworkSymbols(tree, source, filePath)`:
60
- - Scan for `decorated_definition` nodes whose decorator chain includes a `call` node matching `/^(app|bp|blueprint)\.(route|get|post|put|delete|patch|head|options)\s*\(/`
61
- - For each matched decorated function:
62
- - Emit kind `'route'`
63
- - Name: the decorated function name
64
- - Extract the route path string literal from the first decorator argument
65
- - Extract HTTP methods from the `methods=[...]` keyword argument if present; default to `['GET']` if absent
66
- - `frameworkMeta.http_methods`: string array of methods
67
- - `frameworkMeta.route_path`: the extracted path string
68
- - `frameworkMeta.flask_route: true`
69
- - Scan for `class_definition` nodes whose base classes include `MethodView` or `View` (Flask class-based views):
70
- - Emit kind `'view'`
71
- - `frameworkMeta.flask_class_view: true`
72
- - `enrichMetadata(symbol)`: no-op (route metadata is set during extraction)
73
- - Register via `registerAdapter(flaskAdapter)` at module level
74
-
75
- **Key technical notes:**
76
- - Flask supports both `@app.route('/path')` and shorthand `@app.get('/path')`, `@app.post('/path')` — both patterns must be detected
77
- - Blueprint routes use a variable name that may not be `bp` — detect by checking if the variable was assigned from `Blueprint(...)` call in the same file. As a simpler heuristic: match any decorator whose method part matches `route|get|post|put|delete|patch`
78
- - Path parameters use `<type:name>` syntax (e.g. `<int:user_id>`) — emit the path as-is in `frameworkMeta.route_path`; normalise to `:name` format for the symbol `name` field
79
-
80
- **Verify:** Index `test/fixtures/flask-project/` (created in Task 60). Verify `@app.route('/users')` produces a `route` symbol. Verify `@app.get('/users/<int:id>')` produces a route. Verify a plain function without decorator is not extracted.
81
-
82
- **Tests:** `@app.route` with GET (default), `@app.route` with explicit methods, `@app.get` / `@app.post` shorthand, blueprint route, class-based view, non-route function (not extracted).
83
-
84
- ---
85
-
86
- ## Task 45: FastAPI Adapter
87
-
88
- Extract route symbols from FastAPI applications. FastAPI's decorator API is nearly identical to Flask's shorthand decorators but detected from a different dependency.
89
-
90
- **Deliverables:**
91
- - `src/adapters/fastapi.ts` — implements `FrameworkAdapter`
92
- - `name`: `'fastapi'`
93
- - `extensions()`: `[]`
94
- - `detect(projectRoot)`:
95
- - Read `requirements.txt` — check for `/^\s*fastapi[>=\s]/i`
96
- - Read `pyproject.toml` — check dependencies for `"fastapi"` (case-insensitive)
97
- - `fileFilter(filePath)`: `.py` files only
98
- - `extractFrameworkSymbols(tree, source, filePath)`:
99
- - Scan for `decorated_definition` nodes whose decorator matches `/^(app|router|api)\.(get|post|put|delete|patch|options|head)\s*\(/`
100
- - For each matched function:
101
- - Emit kind `'route'`
102
- - Extract path from first positional argument
103
- - `frameworkMeta.http_method`: uppercase method name
104
- - `frameworkMeta.route_path`: extracted path
105
- - `frameworkMeta.fastapi_route: true`
106
- - Scan for `class_definition` nodes inheriting from `APIRouter` — emit kind `'class'` with `frameworkMeta.fastapi_router: true`
107
- - `enrichMetadata(symbol)`: no-op
108
- - Register via `registerAdapter(fastapiAdapter)` at module level
109
-
110
- **Key technical notes:**
111
- - FastAPI uses `APIRouter` for route grouping (analogous to Flask blueprints) — detect `router.get(...)` patterns with the same regex
112
- - FastAPI path parameters use `{param_name}` syntax (not Flask's `<type:name>`) — preserve as-is
113
- - Response models and dependencies (`Depends(...)`) are not extracted — they are type annotations, not symbols
114
-
115
- **Verify:** Index `test/fixtures/fastapi-project/`. Verify `@app.get('/items/{item_id}')` produces a route. Verify `APIRouter` usage produces routes. Verify async functions are handled identically to sync functions.
116
-
117
- **Tests:** `@app.get`, `@app.post`, `@router.get` (APIRouter), async route function, dependency injection (not extracted), non-route function (not extracted).
118
-
119
- ---
120
-
121
- ## Task 46: Django Adapter
122
-
123
- Extract models, views, and URL routes from Django applications. Django's conventions are file-path-based (`models.py`, `views.py`, `urls.py`) making the adapter more file-aware than Flask/FastAPI.
124
-
125
- **Deliverables:**
126
- - `src/adapters/django.ts` — implements `FrameworkAdapter`
127
- - `name`: `'django'`
128
- - `extensions()`: `[]`
129
- - `detect(projectRoot)`:
130
- - Check `requirements.txt` or `pyproject.toml` for `django` or `Django`
131
- - OR check for the presence of `manage.py` at the project root (Django's canonical marker file)
132
- - `fileFilter(filePath)`:
133
- - Returns `true` for any `.py` file (broad filter; extraction is scoped by content/path)
134
- - `extractFrameworkSymbols(tree, source, filePath)`:
135
-
136
- **Models** (any `.py` file):
137
- - Scan for `class_definition` nodes whose base class list includes `models.Model`, `Model`, or `BaseModel` (DRF)
138
- - Emit kind `'model'`
139
- - `frameworkMeta.django_model: true`
140
-
141
- **Views** (any `.py` file):
142
- - Function-based views: `decorated_definition` or `function_definition` nodes where the decorator includes `login_required`, `permission_required`, or `api_view` — emit kind `'view'` with `frameworkMeta.django_fbv: true`
143
- - Class-based views: `class_definition` nodes inheriting from `View`, `APIView`, `ModelViewSet`, `ViewSet`, `GenericAPIView`, `ListAPIView`, `CreateAPIView`, `RetrieveAPIView`, `UpdateAPIView`, `DestroyAPIView`, or `ListCreateAPIView` — emit kind `'view'` with `frameworkMeta.django_cbv: true`
144
-
145
- **URL patterns** (files named `urls.py`):
146
- - Scan for `call` nodes matching `path(...)` or `re_path(...)` or `url(...)` at module level
147
- - For each call: extract the first argument (route string) and second argument (view name)
148
- - Emit kind `'route'`
149
- - `frameworkMeta.django_url_pattern: true`, `frameworkMeta.route_path`, `frameworkMeta.view_name`
150
-
151
- **Signal receivers** (any `.py` file):
152
- - Scan for `decorated_definition` nodes whose decorator matches `receiver(...)` from `django.dispatch`
153
- - Emit kind `'signal'`
154
- - `frameworkMeta.django_signal: true`
155
-
156
- - `enrichMetadata(symbol)`: no-op
157
- - Register via `registerAdapter(djangoAdapter)` at module level
158
-
159
- **Key decisions:**
160
- - Model detection via inheritance is more reliable than filename detection (`models.py` is a convention, not a requirement)
161
- - URL patterns are only extracted from files literally named `urls.py` (or matching `*/urls.py`) to avoid false positives
162
- - DRF (Django REST Framework) viewsets and generic views are included in CBV detection — they are the dominant Django API pattern
163
-
164
- **Verify:** Index `test/fixtures/django-project/`. Verify `User(models.Model)` produces kind `'model'`. Verify a CBV inheriting `APIView` produces kind `'view'`. Verify `path('/users/', UserListView.as_view())` in `urls.py` produces kind `'route'`.
165
-
166
- **Tests:** `models.Model` subclass (model), `APIView` subclass (CBV view), `@login_required` function (FBV view), `path(...)` in urls.py (route), `@receiver(post_save)` (signal), plain class not inheriting Model (not extracted as model).
167
-
168
- ---
169
-
170
- ## Task 47: Gin Adapter
171
-
172
- Extract route symbols from Gin web framework applications. Go's procedural routing uses method calls on the router, not decorators — the same call-expression scanning approach used by the Express adapter.
173
-
174
- **Deliverables:**
175
- - `src/adapters/gin.ts` — implements `FrameworkAdapter`
176
- - `name`: `'gin'`
177
- - `extensions()`: `[]` — `.go` files already handled by the Go handler
178
- - `detect(projectRoot)`:
179
- - Read `go.mod` if present — check for a line containing `github.com/gin-gonic/gin`
180
- - `fileFilter(filePath)`: `.go` files only
181
- - `extractFrameworkSymbols(tree, source, filePath)`:
182
- - Scan raw source bytes for call expressions matching the pattern:
183
- `/<identifier>\.(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|Any)\s*\(/gi`
184
- - For each match:
185
- - Extract the route path string literal from the first argument (next string after the opening paren)
186
- - Skip if the path is not a string literal (variable or expression — too dynamic)
187
- - Emit kind `'route'`
188
- - `frameworkMeta.http_method`: matched method name (uppercase)
189
- - `frameworkMeta.route_path`: extracted path string
190
- - `frameworkMeta.gin_route: true`
191
- - Scan for `router.Group(...)` calls — extract group prefix string, apply as prefix to child routes in the same scope
192
- - `enrichMetadata(symbol)`: no-op
193
- - Register via `registerAdapter(ginAdapter)` at module level
194
-
195
- **Key technical notes:**
196
- - Go does not use decorators — route registration is via method calls on a `*gin.Engine` or `*gin.RouterGroup` variable
197
- - Route groups: `r.Group("/api")` returns a sub-router; routes registered on it are prefixed. Static detection of group nesting is approximated by proximity in source; don't over-engineer
198
- - Middleware registration: `r.Use(authMiddleware)` — emit kind `'middleware'` with `frameworkMeta.gin_middleware: true` only if the argument is a named function reference (not an inline function literal)
199
-
200
- **Verify:** Index `test/fixtures/gin-project/`. Verify `r.GET("/users", handler)` produces a route symbol. Verify `r.POST("/users", ...)` produces a route. Verify a non-route method call is not extracted.
201
-
202
- **Tests:** `r.GET`, `r.POST`, `r.PUT`, `r.DELETE`, `router.GET` (alternate variable name), dynamic path (`"/users/:id"`), group prefix, `r.Use` middleware.
203
-
204
- ---
205
-
206
- ## Task 48: Echo Adapter
207
-
208
- Extract route symbols from Echo web framework applications. Structurally identical to the Gin adapter with different detection and method names.
209
-
210
- **Deliverables:**
211
- - `src/adapters/echo.ts` — implements `FrameworkAdapter`
212
- - `name`: `'echo'`
213
- - `extensions()`: `[]`
214
- - `detect(projectRoot)`:
215
- - Read `go.mod` — check for `github.com/labstack/echo`
216
- - `fileFilter(filePath)`: `.go` files only
217
- - `extractFrameworkSymbols(tree, source, filePath)`:
218
- - Same regex approach as the Gin adapter: match `<identifier>.(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|Any)\s*\(`
219
- - Extract path from first string argument
220
- - Emit kind `'route'`
221
- - `frameworkMeta.http_method`, `frameworkMeta.route_path`, `frameworkMeta.echo_route: true`
222
- - Echo groups: `e.Group("/api")` — detect and apply prefix
223
- - `enrichMetadata(symbol)`: no-op
224
- - Register via `registerAdapter(echoAdapter)` at module level
225
-
226
- **Key technical notes:**
227
- - Echo's API is nearly identical to Gin's — the adapter is a thin wrapper around the same extraction logic. Extract the shared route-scanning logic into `src/adapters/go-router-utils.ts` if duplication becomes significant.
228
- - Echo middleware: `e.Use(middleware.Logger())` — same pattern as Gin middleware extraction
229
-
230
- **Verify:** Index `test/fixtures/echo-project/`. Verify `e.GET("/users", handler)` produces a route.
231
-
232
- **Tests:** Same cases as Gin adapter but with Echo variable names and import detection.
233
-
234
- ---
235
-
236
- ## Task 49: Fiber Adapter
237
-
238
- Extract route symbols from Fiber web framework applications. Fiber's API mirrors Express.js closely but is written in Go.
239
-
240
- **Deliverables:**
241
- - `src/adapters/fiber.ts` — implements `FrameworkAdapter`
242
- - `name`: `'fiber'`
243
- - `extensions()`: `[]`
244
- - `detect(projectRoot)`:
245
- - Read `go.mod` — check for `github.com/gofiber/fiber`
246
- - `fileFilter(filePath)`: `.go` files only
247
- - `extractFrameworkSymbols(tree, source, filePath)`:
248
- - Match `<identifier>.(Get|Post|Put|Delete|Patch|Head|Options|All)\s*\(` (Fiber uses title-case method names)
249
- - Extract path string, emit kind `'route'`
250
- - `frameworkMeta.http_method`, `frameworkMeta.route_path`, `frameworkMeta.fiber_route: true`
251
- - Fiber groups: `app.Group("/api")` — detect prefix
252
- - `enrichMetadata(symbol)`: no-op
253
- - Register via `registerAdapter(fiberAdapter)` at module level
254
-
255
- **Key technical notes:**
256
- - Fiber's method names are title-case (`Get`, `Post`) unlike Gin/Echo (all-caps `GET`, `POST`) — regex must match both forms or be case-insensitive
257
- - Fiber middleware: `app.Use(...)` — same extraction pattern
258
-
259
- **Verify:** Index `test/fixtures/fiber-project/`. Verify `app.Get("/users", handler)` produces a route.
260
-
261
- **Tests:** `app.Get`, `app.Post`, `app.Group` prefix, middleware via `app.Use`.
262
-
263
- ---
264
-
265
- ## Task 50: PHP Language Handler
266
-
267
- Add PHP as the first non-Go/Python new language handler in Phase 5. PHP's official tree-sitter grammar is mature and well-maintained.
268
-
269
- **Deliverables:**
270
- - Download and bundle `grammars/tree-sitter-php.wasm`
271
- - Source: `tree-sitter-php` npm package
272
- - Build: `npx tree-sitter build-wasm node_modules/tree-sitter-php` → copy output to `grammars/`
273
- - `src/handlers/php.ts` — implements `LanguageHandler`
274
- - `extensions()`: `['.php']`
275
- - `grammarPath()`: path to `tree-sitter-php.wasm`
276
- - `extractSymbols(tree, source)`:
277
- - PHP files begin with a `php_tag` node — skip it and iterate remaining top-level children
278
- - `function_definition` → kind `'function'`
279
- - `class_declaration` → kind `'class'`
280
- - `interface_declaration` → kind `'interface'`
281
- - `trait_declaration` → kind `'interface'` (PHP traits are closest to interfaces for navigation)
282
- - `enum_declaration` → kind `'enum'` (PHP 8.1+)
283
- - `method_declaration` inside `declaration_list` of a class/interface/trait → kind `'method'`, name = `ClassName::methodName`
284
- - `const_declaration` → kind `'const'`
285
- - `namespace_definition` → recurse into its body, applying namespace prefix to all extracted symbol names
286
- - Visibility filter: `method_declaration` nodes — check `visibility_modifier` child for `public` or `protected`; skip `private`
287
- - `extractImports(tree, source)`:
288
- - `use_declaration` nodes → iterate `namespace_use_clause` children
289
- - Each clause has a `qualified_name` child (the imported class path) and optionally a `namespace_aliasing_clause` (the alias after `as`)
290
- - `specifier`: the qualified name text (e.g. `App\Http\Controllers\Controller`)
291
- - `importedNames`: the simple class name (last segment of the qualified name)
292
- - `resolvedPath`: `null` (Composer autoload — no local path resolution)
293
- - `extractDocstring(node)`:
294
- - Walk `previousNamedSibling` chain to find a `comment` node
295
- - Must start with `/**` (PHP docblock)
296
- - Strip `/**`, `*/`, and leading ` * ` from each line
297
- - Return first sentence (up to first `.` or 200 chars)
298
- - Update `src/handlers/handler-registry.ts` — add entry: `'php' → ['.php']`
299
- - Update `src/index.ts` — call `registerHandler(new PhpHandler())` in bootstrap
300
-
301
- **Signature building:**
302
- - Functions: `function name(params): returnType`
303
- - Classes: `class Name extends Base implements Interface`
304
- - Methods: `public/protected function name(params): returnType`
305
- - Constants: `const NAME = value` or `public const NAME: Type = value`
306
- - Keep signatures under 120 chars; truncate parameter lists with `...` if needed
307
-
308
- **Key technical notes:**
309
- - PHP files may have content before `<?php` (e.g. HTML) — the grammar models this as `text` nodes before `php_tag`. Skip all nodes before and including the first `php_tag`.
310
- - Namespaces: `namespace App\Http\Controllers;` changes the fully-qualified name of all subsequent declarations. Track the current namespace string and prefix symbol names accordingly (e.g. `App\Http\Controllers\UserController`)
311
- - PHP 8 attributes (`#[Attribute]`) appear as `attribute_list` nodes preceding declarations — include the first attribute in the signature string but do not emit them as separate symbols
312
- - PHP 8.1 readonly properties and constructor promotion are inside `method_declaration` — handle `constructor_promotion` parameter nodes when extracting constructor signatures
313
-
314
- **Verify:** Index `test/fixtures/php-project/`. Verify classes, interfaces, methods (with visibility), constants, and namespace-qualified names are extracted correctly. Verify PHPDoc `/** */` docstrings are captured.
315
-
316
- **Tests:** Top-level function, class with public/protected/private methods (private skipped), interface, trait, enum, `const`, namespace declaration qualifying names, `use` import with alias, PHPDoc docstring.
317
-
318
- ---
319
-
320
- ## Task 51: Laravel Adapter
321
-
322
- Extract routes, controllers, and Eloquent models from Laravel applications.
323
-
324
- **Deliverables:**
325
- - `src/adapters/laravel.ts` — implements `FrameworkAdapter`
326
- - `name`: `'laravel'`
327
- - `extensions()`: `[]`
328
- - `detect(projectRoot)`:
329
- - Read `composer.json` — check `require` object for key `"laravel/framework"`
330
- - `fileFilter(filePath)`:
331
- - `routes/web.php`, `routes/api.php`, `routes/*.php` → route files
332
- - `app/Http/Controllers/**/*.php` → controllers
333
- - `app/Models/**/*.php` → Eloquent models
334
- - `app/Http/Middleware/**/*.php` → middleware
335
- - `extractFrameworkSymbols(tree, source, filePath)`:
336
-
337
- **Route files** (`routes/*.php`):
338
- - Scan for `call` nodes matching `Route::(get|post|put|delete|patch|options|any)\s*\(`
339
- - Extract path string literal (first argument) and handler (second argument — string or array or closure)
340
- - Emit kind `'route'`
341
- - `frameworkMeta.http_method`, `frameworkMeta.route_path`, `frameworkMeta.laravel_route: true`
342
- - Also detect `Route::resource(name, Controller::class)` → emit multiple route symbols (index, show, store, update, destroy)
343
- - Detect `Route::group(['prefix' => '/api'], ...)` — apply prefix to child routes
344
-
345
- **Controllers** (`app/Http/Controllers/**`):
346
- - Class inheriting `Controller` → emit kind `'class'` with `frameworkMeta.laravel_controller: true`
347
- - Public methods inside controllers → emit kind `'view'` (they are request handlers)
348
- - `frameworkMeta.laravel_action: true`
349
-
350
- **Models** (`app/Models/**`):
351
- - Class inheriting `Model` or `Illuminate\Database\Eloquent\Model` → emit kind `'model'`
352
- - `frameworkMeta.laravel_model: true`
353
-
354
- **Middleware** (`app/Http/Middleware/**`):
355
- - Class with a `handle(Request $request, Closure $next)` method → emit kind `'middleware'`
356
- - `frameworkMeta.laravel_middleware: true`
357
-
358
- - `enrichMetadata(symbol)`: no-op
359
- - Register via `registerAdapter(laravelAdapter)` at module level
360
-
361
- **Verify:** Index `test/fixtures/laravel-project/`. Verify `Route::get('/users', ...)` produces a route. Verify `User extends Model` produces kind `'model'`. Verify a controller's public methods produce kind `'view'`.
362
-
363
- **Tests:** `Route::get`, `Route::post`, `Route::resource` (multiple routes), route group with prefix, Eloquent model detection, controller action as `'view'`, middleware detection.
364
-
365
- ---
366
-
367
- ## Task 52: Symfony Adapter
368
-
369
- Extract routes and controllers from Symfony applications. Symfony supports both annotation/attribute-based routing and YAML/XML config routing.
370
-
371
- **Deliverables:**
372
- - `src/adapters/symfony.ts` — implements `FrameworkAdapter`
373
- - `name`: `'symfony'`
374
- - `extensions()`: `[]`
375
- - `detect(projectRoot)`:
376
- - Read `composer.json` — check `require` for `"symfony/framework-bundle"` or `"symfony/symfony"`
377
- - `fileFilter(filePath)`: `src/**/*.php` (Symfony sources live in `src/`)
378
- - `extractFrameworkSymbols(tree, source, filePath)`:
379
- - **Attribute-based routes** (PHP 8.0+ `#[Route(...)]`):
380
- - Scan for `attribute_list` nodes containing `Route` attribute
381
- - Extract `path` argument from the attribute
382
- - Extract `methods` argument if present
383
- - Emit kind `'route'` for each method in `methods` (or one route for no restriction)
384
- - `frameworkMeta.symfony_route: true`, `frameworkMeta.route_path`, `frameworkMeta.http_method`
385
- - **Annotation-based routes** (older `@Route(...)` in docblock):
386
- - Scan `/** */` comment nodes for `@Route` annotation
387
- - Same extraction as above but from the comment string
388
- - **Services / Controllers**:
389
- - Class annotated with `#[AsController]` or inheriting `AbstractController` → emit kind `'class'` with `frameworkMeta.symfony_controller: true`
390
- - `enrichMetadata(symbol)`: no-op
391
- - Register via `registerAdapter(symfonyAdapter)` at module level
392
-
393
- **Key technical notes:**
394
- - Symfony's YAML/XML route config files are out of scope for Phase 5 — only PHP attribute and annotation routing
395
- - Symfony 6+ strongly prefers PHP attributes over annotations — annotations remain supported for backward compat
396
- - Entity classes (`@ORM\Entity` or `#[ORM\Entity]`) could be emitted as `'model'` — defer to a future task; not enough ROI to add in Phase 5
397
-
398
- **Verify:** Index `test/fixtures/symfony-project/`. Verify `#[Route('/users', methods: ['GET'])]` produces a route symbol. Verify a class annotated `#[AsController]` produces a controller symbol.
399
-
400
- **Tests:** `#[Route]` with path only, `#[Route]` with methods array, `@Route` annotation in docblock, `#[AsController]` class, non-controller class (not extracted as controller).
401
-
402
- ---
403
-
404
- ## Task 53: Ruby Language Handler
405
-
406
- Add Ruby as the fourth new language handler. Ruby's expression-based AST requires context tracking to distinguish top-level `def` from class-level `def`.
407
-
408
- **Deliverables:**
409
- - Download and bundle `grammars/tree-sitter-ruby.wasm`
410
- - Source: `tree-sitter-ruby` npm package (official tree-sitter grammar)
411
- - Build: `npx tree-sitter build-wasm node_modules/tree-sitter-ruby`
412
- - `src/handlers/ruby.ts` — implements `LanguageHandler`
413
- - `extensions()`: `['.rb']`
414
- - `grammarPath()`: path to `tree-sitter-ruby.wasm`
415
- - `extractSymbols(tree, source)`:
416
- - Use a recursive `walkNode(node, context: { className: string | null })` helper
417
- - `method` node at root scope (no class ancestor) → kind `'function'`
418
- - `method` node inside a `class` or `module` body → kind `'method'`, name = `ClassName#methodName`
419
- - `singleton_method` node (e.g. `def self.foo`) → kind `'method'`, name = `ClassName.methodName`
420
- - `class` node → kind `'class'`, recurse into body with updated className context
421
- - `module` node → kind `'type'`, recurse into body with updated className context (modules are namespaces + mixins)
422
- - Constants: `assignment` nodes at class/module scope where the LHS is a `constant` node (uppercase identifier) → kind `'const'`
423
- - Skip `alias` nodes, `attr_accessor`/`attr_reader`/`attr_writer` call nodes (too noisy, not true definitions)
424
- - `extractImports(tree, source)`:
425
- - Scan for `call` nodes at root scope where the `method` child text is `require` or `require_relative`
426
- - First argument must be a `string` node
427
- - `specifier`: the string value (strip quotes)
428
- - `resolvedPath`: for `require_relative` apply relative resolution against the file path; for `require` return `null` (gem or stdlib)
429
- - `importedNames`: `[]` (Ruby `require` brings the whole file into scope, not named symbols)
430
- - `isTypeOnly`: always `false`
431
- - `extractDocstring(node)`:
432
- - Walk `previousNamedSibling` chain to find consecutive `comment` nodes (`#` prefix)
433
- - Strip `# ` prefix, concatenate, return first paragraph
434
-
435
- **Signature building:**
436
- - Top-level methods: `def name(params)`
437
- - Class methods: `def self.name(params)`
438
- - Instance methods: `def name(params)`
439
- - Classes: `class Name < SuperClass` or `class Name`
440
- - Modules: `module Name`
441
- - Constants: `NAME = value` (truncate if > 80 chars)
442
- - Keep signatures under 120 chars
443
-
444
- **Key technical notes:**
445
- - `method` node has a `name` child (identifier) and optionally `method_parameters`
446
- - `singleton_method` has an `object` child (the receiver, typically `self`) and a `name` child
447
- - Context tracking: when entering a `class` or `module` node, push its name to a context stack; pop when leaving. The class name for method qualification is the top of the stack.
448
- - Ruby does not have access modifiers like Java/PHP. `public`, `private`, and `protected` are method calls that affect subsequent method definitions — do not attempt to track visibility in Phase 5; extract all `method` nodes
449
- - `module_function` pattern: some modules use `module_function` to make methods available as both instance and module methods — treat as a regular method
450
-
451
- **Verify:** Index `test/fixtures/ruby-project/`. Verify top-level `def` is kind `'function'`, class-level `def` is kind `'method'`, `def self.foo` is kind `'method'` with name `ClassName.foo`. Verify `# comment` preceding a method is captured as docstring.
452
-
453
- **Tests:** Top-level `def`, class with instance methods, `def self.` singleton method, module with methods, constant assignment, `require` import, `require_relative` import, Ruby comment docstring, nested class method name qualification.
454
-
455
- ---
456
-
457
- ## Task 54: Rails Adapter
458
-
459
- Extract models, controllers, and routes from Ruby on Rails applications. Rails follows strict file-path conventions making detection straightforward.
460
-
461
- **Deliverables:**
462
- - `src/adapters/rails.ts` — implements `FrameworkAdapter`
463
- - `name`: `'rails'`
464
- - `extensions()`: `[]`
465
- - `detect(projectRoot)`:
466
- - Read `Gemfile` — check for line matching `/^\s*gem\s+['"]rails['"]/m`
467
- - `fileFilter(filePath)`:
468
- - `app/models/**/*.rb` → models
469
- - `app/controllers/**/*.rb` → controllers
470
- - `app/jobs/**/*.rb` → background jobs
471
- - `config/routes.rb` → routes
472
- - `extractFrameworkSymbols(tree, source, filePath)`:
473
-
474
- **Models** (`app/models/**`):
475
- - Class inheriting from `ApplicationRecord`, `ActiveRecord::Base`, or `ActiveModel::Model` → emit kind `'model'`
476
- - `frameworkMeta.rails_model: true`
477
- - Detect `has_many`, `belongs_to`, `has_one` call nodes → `frameworkMeta.associations: string[]`
478
-
479
- **Controllers** (`app/controllers/**`):
480
- - Class inheriting from `ApplicationController` or `ActionController::Base` → emit kind `'class'` with `frameworkMeta.rails_controller: true`
481
- - Public `method` nodes inside the controller → emit kind `'view'` (Rails actions are request handlers)
482
- - `frameworkMeta.rails_action: true`
483
-
484
- **Routes** (`config/routes.rb`):
485
- - Scan for `call` nodes matching `(get|post|put|delete|patch)\s*\(` → emit kind `'route'`
486
- - Scan for `resources :name` and `resource :name` → emit `route` symbols for each REST action
487
- - Scan for `namespace :name` → apply prefix to child routes
488
- - `frameworkMeta.rails_route: true`, `frameworkMeta.route_path`, `frameworkMeta.http_method`
489
-
490
- **Jobs** (`app/jobs/**`):
491
- - Class inheriting `ApplicationJob` or `ActiveJob::Base` → emit kind `'class'` with `frameworkMeta.rails_job: true`
492
-
493
- - `enrichMetadata(symbol)`: no-op
494
- - Register via `registerAdapter(railsAdapter)` at module level
495
-
496
- **Verify:** Index `test/fixtures/rails-project/`. Verify `User < ApplicationRecord` produces kind `'model'`. Verify `UsersController < ApplicationController` produces kind `'class'` with controller metadata. Verify controller public methods produce kind `'view'`. Verify `get '/users', to: 'users#index'` in `routes.rb` produces a route.
497
-
498
- **Tests:** Model with `has_many`, controller with actions, `get`/`post` route, `resources :users` (generates CRUD routes), `namespace` prefix.
499
-
500
- ---
501
-
502
- ## Task 55: Sinatra Adapter
503
-
504
- Extract route symbols from Sinatra applications. Sinatra's DSL is closer to Flask/Express than to Rails.
505
-
506
- **Deliverables:**
507
- - `src/adapters/sinatra.ts` — implements `FrameworkAdapter`
508
- - `name`: `'sinatra'`
509
- - `extensions()`: `[]`
510
- - `detect(projectRoot)`:
511
- - Read `Gemfile` — check for `/gem\s+['"]sinatra['"]/`
512
- - OR check for `require 'sinatra'` / `require "sinatra"` in any `.rb` file at root level (shallow scan)
513
- - `fileFilter(filePath)`: `.rb` files only
514
- - `extractFrameworkSymbols(tree, source, filePath)`:
515
- - Scan for `call` nodes matching the Sinatra route DSL:
516
- - `(get|post|put|delete|patch)\s+\(?\s*['"]` at top-level or inside a class body
517
- - Extract the path string literal
518
- - Emit kind `'route'`
519
- - `frameworkMeta.http_method`, `frameworkMeta.route_path`, `frameworkMeta.sinatra_route: true`
520
- - `enrichMetadata(symbol)`: no-op
521
- - Register via `registerAdapter(sinatraAdapter)` at module level
522
-
523
- **Key technical notes:**
524
- - Sinatra routes can be at top-level (classic style) or inside a class inheriting `Sinatra::Base` (modular style) — the regex captures both
525
- - Sinatra path parameters use `:param` syntax, same as Express — preserve as-is
526
-
527
- **Verify:** Index `test/fixtures/sinatra-project/` (a minimal fixture with classic and modular style). Verify `get '/users' do` produces a route.
528
-
529
- **Tests:** Classic style `get`, modular style `post` inside class, dynamic path (`:id`), non-route call (not extracted).
530
-
531
- ---
532
-
533
- ## Task 56: Kotlin Language Handler
534
-
535
- Add Kotlin as the fifth new language handler. Kotlin's grammar is community-maintained but stable and widely used (Neovim, VS Code Kotlin plugins).
536
-
537
- **Deliverables:**
538
- - Download and bundle `grammars/tree-sitter-kotlin.wasm`
539
- - Source: `tree-sitter-kotlin` npm package
540
- - Build: `npx tree-sitter build-wasm node_modules/tree-sitter-kotlin`
541
- - `src/handlers/kotlin.ts` — implements `LanguageHandler`
542
- - `extensions()`: `['.kt', '.kts']`
543
- - `grammarPath()`: path to `tree-sitter-kotlin.wasm`
544
- - `extractSymbols(tree, source)`:
545
- - `function_declaration` at top-level → kind `'function'`
546
- - `class_declaration` → kind `'class'`
547
- - `data class`, `sealed class`, `abstract class` are all `class_declaration` nodes — check `class_modifiers` child for distinguishing modifiers; emit all as `'class'`
548
- - `interface_declaration` or `class_declaration` with `interface` keyword → kind `'interface'`
549
- - `object_declaration` (Kotlin singleton) → kind `'class'` with `frameworkMeta.kotlin_object: true`
550
- - `enum_class_declaration` → kind `'enum'`
551
- - `type_alias` → kind `'type'`
552
- - `function_declaration` inside `class_body` or `object_declaration` body → kind `'method'`, name = `ClassName.methodName`
553
- - `companion_object` body `function_declaration` → kind `'method'`, name = `ClassName.methodName` (or `ClassName.Companion.methodName` if companion is named)
554
- - `property_declaration` at top-level with `val` and uppercase name → kind `'const'`
555
- - Visibility: extract `visibility_modifier` child from `modifiers` node; skip `private` and `internal`; include `public`, `protected`, and declarations with no modifier (Kotlin default is `public`)
556
- - `extractImports(tree, source)`:
557
- - `import_header` nodes under `source_file`
558
- - `specifier`: the full qualified import path (e.g. `io.ktor.server.application.Application`)
559
- - `importedNames`: last segment (simple class/function name)
560
- - `isTypeOnly`: `false` (Kotlin has no type-only imports)
561
- - `resolvedPath`: `null` (Gradle/Maven classpath)
562
- - `extractDocstring(node)`:
563
- - Look for `multiline_comment` node immediately preceding the declaration starting with `/**` (KDoc)
564
- - OR `line_comment` nodes (`//`) directly preceding — less common in Kotlin but valid
565
- - Strip `/**`, `*/`, and ` * ` prefixes; return first paragraph
566
-
567
- **Signature building:**
568
- - Functions: `fun name(params): ReturnType`
569
- - Classes: `[data/sealed/abstract] class Name(primaryConstructorParams) : SuperType`
570
- - Interfaces: `interface Name`
571
- - Object declarations: `object Name : SuperType`
572
- - Methods: `fun name(params): ReturnType` (class context implied by name prefix)
573
- - Type aliases: `typealias Name = Type`
574
- - Constants: `val NAME: Type = value`
575
- - Keep signatures under 120 chars
576
-
577
- **Key technical notes:**
578
- - Kotlin primary constructors are part of the `class_declaration` header (not a separate node) — extract constructor params for the class signature
579
- - Annotations appear as `annotation` children inside `modifiers` — include the first annotation in the signature (e.g. `@Component class UserService`)
580
- - Extension functions: `fun ReceiverType.methodName(...)` — the receiver type is a `user_type` child before the function name; emit as `'function'` with name `ReceiverType.methodName`
581
- - `suspend` functions: `suspend` is a `function_modifier` inside `modifiers` — include `suspend` in the signature
582
-
583
- **Verify:** Index `test/fixtures/kotlin-project/`. Verify `fun topLevel()` is kind `'function'`, class method is kind `'method'`, data class is kind `'class'`, companion object function is kind `'method'`. Verify KDoc comment is captured.
584
-
585
- **Tests:** Top-level fun, class with methods, data class, sealed class, object declaration, companion object with function, extension function, `typealias`, import declaration, KDoc comment.
586
-
587
- ---
588
-
589
- ## Task 57: Ktor Adapter
590
-
591
- Extract route symbols from Ktor server applications. Ktor uses a Kotlin DSL for routing — structured function calls that define route hierarchies.
592
-
593
- **Deliverables:**
594
- - `src/adapters/ktor.ts` — implements `FrameworkAdapter`
595
- - `name`: `'ktor'`
596
- - `extensions()`: `[]`
597
- - `detect(projectRoot)`:
598
- - Read `build.gradle` or `build.gradle.kts` — check for string `io.ktor`
599
- - Read `pom.xml` if present — check for `<groupId>io.ktor</groupId>`
600
- - `fileFilter(filePath)`: `.kt` files only
601
- - `extractFrameworkSymbols(tree, source, filePath)`:
602
- - Scan for `call_expression` nodes matching Ktor routing DSL:
603
- - `route(path) { ... }` → container, extract path, apply as prefix to nested routes
604
- - `get(path) { ... }`, `post(path) { ... }`, `put(...)`, `delete(...)`, `patch(...)`, `head(...)`, `options(...)` → emit kind `'route'`
605
- - `authenticate { ... }` blocks — recurse to extract nested routes, add `frameworkMeta.authenticated: true`
606
- - Extract path string from first argument of each HTTP method call
607
- - `frameworkMeta.http_method`, `frameworkMeta.route_path`, `frameworkMeta.ktor_route: true`
608
- - `enrichMetadata(symbol)`: no-op
609
- - Register via `registerAdapter(ktorAdapter)` at module level
610
-
611
- **Key technical notes:**
612
- - Ktor routes are defined inside `routing { ... }` blocks (an `Application` extension function call) — the DSL is nested via trailing lambdas
613
- - Path parameters use `{param}` syntax — preserve as-is
614
- - Ktor 2.x introduced a type-safe routing API — out of scope for Phase 5; handle only string-literal routes
615
-
616
- **Verify:** Index `test/fixtures/ktor-project/`. Verify `get("/users") { ... }` produces a route. Verify nested `route("/api") { get("/users") ... }` produces a route with combined path `/api/users`.
617
-
618
- **Tests:** `get`/`post` with path, nested `route` prefix, `authenticate` block wrapper, non-route call (not extracted).
619
-
620
- ---
621
-
622
- ## Task 58: Spring Kotlin Adapter
623
-
624
- Extract routes, controllers, and services from Spring Boot applications written in Kotlin. Spring uses the same decorator-based model as its Java equivalent.
625
-
626
- **Deliverables:**
627
- - `src/adapters/spring-kotlin.ts` — implements `FrameworkAdapter`
628
- - `name`: `'spring-kotlin'`
629
- - `extensions()`: `[]`
630
- - `detect(projectRoot)`:
631
- - Read `build.gradle` or `build.gradle.kts` — check for `org.springframework.boot`
632
- - OR read `pom.xml` — check for `<artifactId>spring-boot-starter</artifactId>`
633
- - `fileFilter(filePath)`: `.kt` files only
634
- - `extractFrameworkSymbols(tree, source, filePath)`:
635
- - Scan for `class_declaration` nodes with annotations:
636
- - `@RestController` or `@Controller` → emit kind `'class'` with `frameworkMeta.spring_controller: true`
637
- - Scan class body for `function_declaration` nodes with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping` annotations → emit kind `'route'`
638
- - Extract path from annotation argument; combine with class-level `@RequestMapping` prefix if present
639
- - `@Service` → emit kind `'class'` with `frameworkMeta.spring_service: true`
640
- - `@Component` → emit kind `'class'` with `frameworkMeta.spring_component: true`
641
- - `@Repository` → emit kind `'class'` with `frameworkMeta.spring_repository: true`
642
- - `frameworkMeta.http_method`, `frameworkMeta.route_path` for route symbols
643
- - `enrichMetadata(symbol)`: no-op
644
- - Register via `registerAdapter(springKotlinAdapter)` at module level
645
-
646
- **Key technical notes:**
647
- - Spring Kotlin supports both annotation-style and functional routing DSL — only annotation style in Phase 5
648
- - `@RequestMapping` at class level provides a base path prefix — combine with method-level mapping for full path
649
- - `@PathVariable` and `@RequestParam` are parameter annotations, not route symbols — ignore
650
-
651
- **Verify:** Index `test/fixtures/spring-kotlin-project/`. Verify `@RestController` with `@GetMapping("/users")` produces a route symbol. Verify `@Service` class produces kind `'class'` with service metadata.
652
-
653
- **Tests:** `@RestController` with `@GetMapping`, class-level `@RequestMapping` prefix combined with method-level mapping, `@Service`, `@Repository`, non-Spring class (no Spring metadata).
654
-
655
- ---
656
-
657
- ## Task 59: C Language Handler
658
-
659
- Add C as the sixth new language handler in Phase 5. C's grammar is official and its AST is flat — the simplest handler in this phase.
660
-
661
- **Deliverables:**
662
- - Download and bundle `grammars/tree-sitter-c.wasm`
663
- - Source: `tree-sitter-c` npm package (official tree-sitter grammar)
664
- - Build: `npx tree-sitter build-wasm node_modules/tree-sitter-c`
665
- - `src/handlers/c.ts` — implements `LanguageHandler`
666
- - `extensions()`: `['.c', '.h']`
667
- - `grammarPath()`: path to `tree-sitter-c.wasm`
668
- - `extractSymbols(tree, source)`:
669
- - `function_definition` → kind `'function'`
670
- - Extract name by unwrapping `function_declarator` → may be nested inside `pointer_declarator`; recurse to find the `identifier` leaf
671
- - Skip if `declaration_specifiers` contains `storage_class_specifier` with text `static`
672
- - `declaration` with `function_declarator` (forward declarations in headers) → kind `'function'`
673
- - `type_definition` containing `struct_specifier` → kind `'struct'`
674
- - Name: the identifier after the closing `}` (typedef alias) or inside the struct tag
675
- - `type_definition` containing `union_specifier` → kind `'struct'` (union is a data-layout variant)
676
- - `type_definition` containing `enum_specifier` → kind `'enum'`
677
- - `type_definition` with bare `type_identifier` → kind `'type'` (typedef alias)
678
- - `preproc_def` (object-like `#define NAME value`) → kind `'macro'`
679
- - Skip `preproc_function_def` (function-like macros — `#define MAX(a,b) ...`) — too noisy
680
- - Skip macros with no value (header guards like `#define HEADER_H`)
681
- - `struct_specifier` at top-level without typedef → kind `'struct'` (anonymous or tagged struct)
682
- - `enum_specifier` at top-level → kind `'enum'`
683
- - `extractImports(tree, source)`:
684
- - `preproc_include` nodes → specifier = the included path (strip `<>` or `""`)
685
- - `resolvedPath`: for `"local.h"` (double-quoted) attempt relative resolution; for `<system.h>` (angle-bracket) return `null`
686
- - `importedNames`: `[]` (C `#include` brings the whole header into scope)
687
- - `isTypeOnly`: `false`
688
- - `extractDocstring(node)`:
689
- - Walk `previousNamedSibling` to find a `comment` node
690
- - Handle both `//` and `/* */` style
691
- - Strip prefixes, return first sentence
692
-
693
- **Signature building:**
694
- - Functions: `returnType name(params)` — extract from `declaration_specifiers` + `function_declarator` source slice
695
- - Structs: `struct Name { field1; field2; ... }` (show first 2 fields; `{ ... }` if more)
696
- - Enums: `enum Name { CONSTANT1, CONSTANT2, ... }` (show first 3 constants)
697
- - Typedefs: `typedef originalType Name`
698
- - Macros: `#define NAME value`
699
- - Keep signatures under 120 chars
700
-
701
- **Key technical notes:**
702
- - C function names can be nested inside pointer declarators: `int *(*func_ptr)(int)` — the `extractFunctionName` helper must recursively unwrap `pointer_declarator` and `parenthesized_declarator` nodes to reach the `identifier`
703
- - Header files (`.h`): forward declarations are common and should be indexed (they define the public API). Do not skip `.h` files.
704
- - `static` functions are internal to the translation unit — skip them (they are implementation details, not API surface)
705
- - Variadic functions (`...` parameter) — handle in signature building; the AST has a `...` child inside the parameter list
706
-
707
- **Verify:** Index `test/fixtures/c-project/`. Verify non-static functions, typedef structs, enums, and object-like macros are extracted. Verify static functions are skipped. Verify `#include "local.h"` resolves locally.
708
-
709
- **Tests:** Non-static function, static function (skipped), pointer-return function (name extraction), typedef struct, anonymous struct, typedef enum, `#define` object macro, `#define` function macro (skipped), `#include` local and system.
710
-
711
- ---
712
-
713
- ## Task 60: Phase 5 Test Fixtures and Integration Tests
714
-
715
- Validate the full Phase 5 pipeline end-to-end across all new handlers, adapters, and the SymbolKind extension.
716
-
717
- **Deliverables:**
718
-
719
- - `test/fixtures/flask-project/` — minimal Flask application:
720
- - `requirements.txt` with `Flask>=3.0`
721
- - `app.py` — `@app.route`, `@app.get`, `@app.post` routes, one class-based view
722
- - `blueprints/users.py` — Blueprint with routes
723
- - `models/user.py` — plain Python class (not extracted as model — no ORM)
724
-
725
- - `test/fixtures/fastapi-project/` — minimal FastAPI application:
726
- - `requirements.txt` with `fastapi>=0.100`
727
- - `main.py` — `@app.get`, `@app.post`, `@router.get` (APIRouter)
728
- - `models.py` — Pydantic models (BaseModel subclasses, not ORM)
729
-
730
- - `test/fixtures/django-project/` — minimal Django application:
731
- - `requirements.txt` with `Django>=4.2`
732
- - `manage.py` (empty — triggers detection)
733
- - `myapp/models.py` — `User(models.Model)`, `Post(models.Model)`
734
- - `myapp/views.py` — function-based view with `@login_required`, class-based `APIView`
735
- - `myapp/urls.py` — `path(...)` route definitions
736
- - `myapp/signals.py` — `@receiver(post_save)` signal handler
737
-
738
- - `test/fixtures/gin-project/` — minimal Gin application:
739
- - `go.mod` with `require github.com/gin-gonic/gin v1.9.1`
740
- - `main.go` — `r.GET`, `r.POST`, `r.Group` routes
741
-
742
- - `test/fixtures/php-project/` — representative PHP project:
743
- - `src/Controller/UserController.php` — class with public methods, namespace
744
- - `src/Model/User.php` — plain PHP class
745
- - `src/Service/AuthService.php` — interface + implementation
746
- - `tests/UserControllerTest.php` — test class (lower priority)
747
-
748
- - `test/fixtures/laravel-project/` — minimal Laravel application:
749
- - `composer.json` with `laravel/framework`
750
- - `routes/web.php` — `Route::get`, `Route::resource`
751
- - `app/Models/User.php` — `User extends Model`
752
- - `app/Http/Controllers/UserController.php` — extends `Controller`
753
-
754
- - `test/fixtures/ruby-project/` — representative Ruby project:
755
- - `lib/auth_service.rb` — class with instance and singleton methods, docstrings
756
- - `lib/user.rb` — class with constants
757
- - `lib/utils.rb` — top-level functions, `require_relative`
758
-
759
- - `test/fixtures/rails-project/` — minimal Rails application:
760
- - `Gemfile` with `gem 'rails'`
761
- - `app/models/user.rb` — `User < ApplicationRecord` with `has_many`
762
- - `app/controllers/users_controller.rb` — `UsersController < ApplicationController`
763
- - `config/routes.rb` — `get`, `resources`
764
-
765
- - `test/fixtures/kotlin-project/` — representative Kotlin project:
766
- - `src/main/kotlin/AuthService.kt` — class with methods, KDoc
767
- - `src/main/kotlin/models/User.kt` — data class
768
- - `src/main/kotlin/utils/Extensions.kt` — extension functions
769
-
770
- - `test/fixtures/ktor-project/` — minimal Ktor application:
771
- - `build.gradle.kts` with `io.ktor:ktor-server-core`
772
- - `src/main/kotlin/Application.kt` — `get`, `post`, `route` DSL
773
-
774
- - `test/fixtures/c-project/` — representative C project:
775
- - `src/auth.c` + `include/auth.h` — functions, structs, forward declarations
776
- - `src/utils.c` — static functions (should be skipped), non-static helpers
777
- - `include/constants.h` — `#define` macros, typedef structs, enums
778
-
779
- - Integration tests `test/integration/phase5.test.ts`:
780
- 1. Flask: `@app.route('/users')` produces route, `@app.get('/users/<int:id>')` produces route with path
781
- 2. Flask: plain function without decorator not extracted as route
782
- 3. FastAPI: `@app.get('/items/{id}')` produces route, async route handled
783
- 4. Django: `User(models.Model)` produces kind `'model'`, `@login_required` FBV produces kind `'view'`
784
- 5. Django: `path('/users/', ...)` in `urls.py` produces kind `'route'`
785
- 6. Django: `@receiver(post_save)` produces kind `'signal'`
786
- 7. Gin: `r.GET("/users", handler)` produces route, non-route call not extracted
787
- 8. PHP handler: class with public/protected/private methods — private skipped
788
- 9. PHP handler: namespace prefix applied to class names
789
- 10. Laravel: `Route::get('/users', ...)` produces route, `User extends Model` produces `'model'`
790
- 11. Ruby handler: top-level `def` is kind `'function'`, class-level `def` is kind `'method'`
791
- 12. Ruby handler: `# comment` docstring captured
792
- 13. Rails: `User < ApplicationRecord` produces kind `'model'`, controller action produces kind `'view'`
793
- 14. Kotlin handler: `data class` produces kind `'class'`, companion object function produces kind `'method'`
794
- 15. Ktor: `get("/users") { }` produces route, nested `route` prefix combined
795
- 16. C handler: non-static function extracted, static function skipped, `#define` macro produces kind `'macro'`
796
- 17. C handler: typedef struct produces kind `'struct'`
797
- 18. SymbolKind: `search-symbols` with `kind: 'model'` returns only model symbols across all fixtures
798
- 19. SymbolKind: `search-symbols` with `kind: 'macro'` returns only C macros
799
- 20. Full suite regression: all Phase 1–4 tests still green
800
-
801
- - **Performance benchmark** `test/benchmarks/phase5.bench.ts`:
802
- - Index each language fixture, record time and symbol count
803
- - Compare against jCodeMunch baseline on Express.js repo using same 5 queries from `reference/jcodemunch-mcp/benchmarks/tasks.json`
804
- - Report token efficiency ratio: PureContext search tokens vs. baseline full-file tokens
805
-
806
- **Verify:** `npm run test` passes entirely — all Phase 1–5 tests green. No regressions.
807
-
808
- ---
809
-
810
- ## Order of Execution
811
-
812
- ```
813
- Task 43: Extend SymbolKind ██░░░░░░░░ Foundation
814
- Task 44: Flask adapter ███░░░░░░░ Python/Go frameworks
815
- Task 45: FastAPI adapter ███░░░░░░░ Python/Go frameworks
816
- Task 46: Django adapter ████░░░░░░ Python/Go frameworks
817
- Task 47: Gin adapter ████░░░░░░ Python/Go frameworks
818
- Task 48: Echo adapter ████░░░░░░ Python/Go frameworks
819
- Task 49: Fiber adapter █████░░░░░ Python/Go frameworks
820
- Task 50: PHP language handler ██████░░░░ PHP
821
- Task 51: Laravel adapter ███████░░░ PHP
822
- Task 52: Symfony adapter ███████░░░ PHP
823
- Task 53: Ruby language handler ████████░░ Ruby
824
- Task 54: Rails adapter █████████░ Ruby
825
- Task 55: Sinatra adapter █████████░ Ruby
826
- Task 56: Kotlin language handler █████████░ Kotlin
827
- Task 57: Ktor adapter ██████████ Kotlin
828
- Task 58: Spring Kotlin adapter ██████████ Kotlin
829
- Task 59: C language handler ██████████ C
830
- Task 60: Fixtures + integration tests ██████████ Polish
831
- ```
832
-
833
- Tasks 44–49 (Python/Go framework adapters) are fully independent of each other and of the new handlers — they can be developed in parallel or in any order. Tasks 50–52 (PHP + adapters) form a dependency chain: 50 must be complete before 51 and 52. The same applies to 53–55 (Ruby) and 56–58 (Kotlin). Task 59 (C) is independent of all language groups and can be worked at any point after Task 43. Task 60 requires all previous tasks.
834
-
835
- **After Phase 5 is complete**, move to Phase 6: C++ handler, Lua handler, Dart handler, and Flutter adapter.