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.
- package/package.json +1 -1
- package/docs/dev/API_STABILITY.md +0 -319
- package/docs/dev/DECISIONS.md +0 -22
- package/docs/dev/DOCUMENTATION_PLAN.md +0 -113
- package/docs/dev/PHASE10_TASKS.md +0 -476
- package/docs/dev/PHASE11_TASKS.md +0 -385
- package/docs/dev/PHASE12_TASKS.md +0 -335
- package/docs/dev/PHASE13_TASKS.md +0 -381
- package/docs/dev/PHASE14_TASKS.md +0 -371
- package/docs/dev/PHASE15_TASKS.md +0 -256
- package/docs/dev/PHASE16_TASKS.md +0 -314
- package/docs/dev/PHASE17_TASKS.md +0 -321
- package/docs/dev/PHASE18_TASKS.md +0 -345
- package/docs/dev/PHASE19_TASKS.md +0 -261
- package/docs/dev/PHASE1_TASKS.md +0 -443
- package/docs/dev/PHASE20_TASKS.md +0 -280
- package/docs/dev/PHASE21_TASKS.md +0 -355
- package/docs/dev/PHASE22_TASKS.md +0 -371
- package/docs/dev/PHASE23_TASKS.md +0 -274
- package/docs/dev/PHASE24_TASKS.md +0 -326
- package/docs/dev/PHASE25_TASKS.md +0 -452
- package/docs/dev/PHASE26_TASKS.md +0 -253
- package/docs/dev/PHASE27_TASKS.md +0 -410
- package/docs/dev/PHASE2_TASKS.md +0 -328
- package/docs/dev/PHASE3_TASKS.md +0 -571
- package/docs/dev/PHASE4_TASKS.md +0 -531
- package/docs/dev/PHASE5_TASKS.md +0 -835
- package/docs/dev/PHASE6_TASKS.md +0 -347
- package/docs/dev/PHASE7_TASKS.md +0 -257
- package/docs/dev/PHASE8_TASKS.md +0 -299
- package/docs/dev/PHASE9_TASKS.md +0 -320
- package/docs/dev/PureContext_MCP_PRD_v1.0.docx +0 -0
- package/docs/dev/SELF_HOSTING.md +0 -142
- package/docs/dev/TEAM_SETUP.md +0 -316
- package/docs/dev/TELEMETRY.md +0 -99
- package/docs/dev/feature-analysis.md +0 -305
- package/docs/dev/phase-1-notes.md +0 -3
package/docs/dev/PHASE5_TASKS.md
DELETED
|
@@ -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.
|