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,531 +0,0 @@
1
- # Phase 4 — Task Breakdown
2
-
3
- **Goal**: Broaden language coverage to Rust, Java, and C#; add backend framework adapters (NestJS, Express/Fastify) and the Angular frontend adapter; harden the HTTP transport with authentication; enable remote repository indexing from GitHub; and deliver semantic search — the PRD Phase 3 item that was deferred.
4
-
5
- **Scope rationale**: Phase 3 completed Python, Go, Next.js, HTTP/SSE transport, full-text search, layer violation analysis, and AI summarization expansion. The three items carried forward from the PRD that are still outstanding are: (1) semantic search, (2) authentication for remote HTTP deployments, and (3) extended language/framework coverage. Phase 4 closes all three.
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.
8
-
9
- ---
10
-
11
- ## Task 33: Rust Language Handler
12
-
13
- Add Rust as the first systems language. Rust's rich type system (structs, enums, traits, impls) maps cleanly onto existing `SymbolKind` types, and its `pub` visibility modifier provides a clean export boundary.
14
-
15
- **Deliverables:**
16
- - Download and bundle `grammars/tree-sitter-rust.wasm`
17
- - `src/handlers/rust.ts` — implements `LanguageHandler`
18
- - `extensions()`: `['.rs']`
19
- - `grammarPath()`: path to `tree-sitter-rust.wasm`
20
- - `extractSymbols(tree, source)`:
21
- - `function_item` → kind `'function'`
22
- - `struct_item` → kind `'class'`
23
- - `enum_item` → kind `'enum'`
24
- - `trait_item` → kind `'interface'`
25
- - `impl_item` containing `function_item` children → kind `'method'`, name = `TypeName.method_name`
26
- - `const_item` → kind `'const'`
27
- - `type_item` (type alias) → kind `'type'`
28
- - Only index items with a `pub` visibility_modifier — Rust's single visibility mechanism
29
- - Exception: items inside `impl` blocks for public types may omit `pub` (emit them anyway if parent impl is public)
30
- - `extractImports(tree, source)`:
31
- - `use_declaration` → specifier = the path string (e.g. `std::collections::HashMap`)
32
- - `extern_crate_declaration` → specifier = crate name
33
- - `resolvedPath`: `null` for all (Cargo module system — no local file resolution without Cargo.toml parsing)
34
- - `importedNames`: extract from `use_list` children (e.g. `use foo::{A, B}` → `['A', 'B']`)
35
- - `extractDocstring(node)`:
36
- - Look for `line_comment` nodes with `///` prefix immediately preceding the item
37
- - Also check `doc_comment` nodes (block-style `/** */` is rare in Rust but supported)
38
- - Strip `/// ` prefix, concatenate lines, return first paragraph
39
-
40
- **Signature building:**
41
- - Functions: `pub fn name(params) -> ReturnType`
42
- - Structs: `pub struct Name { field1: Type, ... }` (show first 2 fields; `{ ... }` if more)
43
- - Enums: `pub enum Name { Variant1, Variant2, ... }` (show first 3 variants)
44
- - Traits: `pub trait Name { fn method1(...); }` (show first method signature)
45
- - Constants: `pub const NAME: Type = value`
46
- - Keep signatures under 120 chars
47
-
48
- **Key technical notes:**
49
- - `impl` blocks tie methods to types — the `impl_item` node has a `type` child naming the type
50
- - Method name format: `TypeName.method_name` (consistent with Go handler)
51
- - Trait implementations (`impl Trait for Type`) should use `TypeName.method_name` (not `Trait.method_name`)
52
- - Macro-generated symbols (`derive`, procedural macros) are not visible to the AST parser — skip silently
53
- - Rust has no classes; `struct` → `'class'` is the closest analog (same decision as Go)
54
-
55
- **Verify:** Index `test/fixtures/rust-project/`. Verify exported structs, enums, traits, functions, and impl methods are extracted. Verify unexported items are skipped. Verify `///` docstrings are captured.
56
-
57
- **Tests:** Fixture `.rs` files covering: `pub fn`, private `fn` (skipped), `pub struct`, `pub enum`, `pub trait`, `impl` with methods, `pub const`, type alias, `use` imports, `///` docstring.
58
-
59
- ---
60
-
61
- ## Task 34: Java Language Handler
62
-
63
- Add Java as a statically-typed OO language. Java's structure (classes, interfaces, methods) maps directly onto existing `SymbolKind` types. Only `public` and `protected` symbols are indexed — private/package-private are navigation noise.
64
-
65
- **Deliverables:**
66
- - Download and bundle `grammars/tree-sitter-java.wasm`
67
- - `src/handlers/java.ts` — implements `LanguageHandler`
68
- - `extensions()`: `['.java']`
69
- - `grammarPath()`: path to `tree-sitter-java.wasm`
70
- - `extractSymbols(tree, source)`:
71
- - `class_declaration` → kind `'class'`
72
- - `interface_declaration` → kind `'interface'`
73
- - `enum_declaration` → kind `'enum'`
74
- - `annotation_type_declaration` → kind `'type'`
75
- - `method_declaration` inside a class/interface → kind `'method'`, name = `ClassName.methodName`
76
- - `constructor_declaration` → kind `'method'`, name = `ClassName.ClassName` (constructor)
77
- - `field_declaration` with `static final` modifiers → kind `'const'`
78
- - Visibility filter: only index nodes with `public` or `protected` modifiers
79
- - Top-level class/interface only (not nested private classes)
80
- - `extractImports(tree, source)`:
81
- - `import_declaration` → specifier = the fully-qualified class name (strip `import ` prefix)
82
- - `static` import: `import static foo.Bar.METHOD` → specifier = `foo.Bar`, importedNames = `['METHOD']`
83
- - `resolvedPath`: `null` (Maven/Gradle classpath — no local resolution)
84
- - `importedNames`: `[simpleClassName]` for regular imports
85
- - `extractDocstring(node)`:
86
- - Look for a `block_comment` node immediately preceding the declaration
87
- - Must start with `/**` (Javadoc)
88
- - Strip `/**`, `*/`, and leading ` * ` from each line
89
- - Return first sentence (up to first `.` or 200 chars)
90
-
91
- **Signature building:**
92
- - Classes: `public class Name extends Base implements Interface`
93
- - Interfaces: `public interface Name extends Parent`
94
- - Methods: `public ReturnType methodName(Type param, ...)` (truncate params if > 100 chars)
95
- - Constructors: `public ClassName(Type param, ...)`
96
- - Constants: `public static final Type NAME = value`
97
- - Keep signatures under 120 chars
98
-
99
- **Key technical notes:**
100
- - Java's `@Override`, `@Deprecated`, and other annotations precede methods — extract the first annotation if present as part of the signature context
101
- - Generics (`<T extends Comparable<T>>`) can make signatures very long — truncate aggressively
102
- - Inner classes: only extract `public static` inner classes; skip instance inner classes
103
- - Java enums may have methods — extract them as `EnumName.methodName`
104
-
105
- **Verify:** Index `test/fixtures/java-project/`. Verify public classes, interfaces, methods, and constants are extracted. Verify private/package-private items are skipped. Verify Javadoc comments are captured.
106
-
107
- **Tests:** Fixture `.java` files covering: `public class`, `public interface`, `public enum`, public/private method (private skipped), `public static final` constant, Javadoc comment, `import` and `import static`, generic class.
108
-
109
- ---
110
-
111
- ## Task 35: C# Language Handler
112
-
113
- Add C# as the third statically-typed OO language. C# shares Java's class/interface/method structure but adds properties, namespaces, records, and primary constructors.
114
-
115
- **Deliverables:**
116
- - Download and bundle `grammars/tree-sitter-c-sharp.wasm`
117
- - `src/handlers/csharp.ts` — implements `LanguageHandler`
118
- - `extensions()`: `['.cs']`
119
- - `grammarPath()`: path to `tree-sitter-c-sharp.wasm`
120
- - `extractSymbols(tree, source)`:
121
- - `class_declaration` → kind `'class'`
122
- - `interface_declaration` → kind `'interface'`
123
- - `enum_declaration` → kind `'enum'`
124
- - `struct_declaration` → kind `'class'` (C# structs are value-type analogs of classes)
125
- - `record_declaration` → kind `'class'`
126
- - `method_declaration` inside a class/interface/struct → kind `'method'`
127
- - `constructor_declaration` → kind `'method'`, name = `ClassName.ClassName`
128
- - `property_declaration` → kind `'const'` (properties are named member access points)
129
- - `field_declaration` with `const` or `static readonly` modifiers → kind `'const'`
130
- - Visibility filter: only `public` and `protected` modifiers
131
- - `extractImports(tree, source)`:
132
- - `using_directive` → specifier = the namespace string (strip `using ` prefix)
133
- - `using_static_directive` → specifier = the fully-qualified type
134
- - `resolvedPath`: `null`
135
- - `importedNames`: `[]` (C# using directives bring namespaces into scope, not individual names)
136
- - `extractDocstring(node)`:
137
- - Look for `single_line_doc_comment` nodes (`/// `) immediately preceding the declaration
138
- - Extract the `<summary>` XML content if present, else return the raw text
139
- - Strip `/// ` prefix and XML tags, return first sentence
140
-
141
- **Signature building:**
142
- - Classes: `public class Name : BaseClass, IInterface`
143
- - Interfaces: `public interface IName`
144
- - Methods: `public ReturnType MethodName(Type param, ...)`
145
- - Properties: `public Type PropertyName { get; set; }`
146
- - Constants: `public const Type NAME = value` or `public static readonly Type Name`
147
- - Keep signatures under 120 chars
148
-
149
- **Key technical notes:**
150
- - C# properties look like fields syntactically but are accessors — treat as `'const'` kind (same pattern as Go struct fields)
151
- - Async methods: `public async Task<T> MethodName(...)` — include `async` in signature
152
- - Extension methods: `public static ReturnType MethodName(this Type param, ...)` — include `this` hint
153
- - Nullable annotations (`?`): preserve in signatures (e.g. `string?`)
154
- - Partial classes: multiple `partial class` declarations in different files should all be indexed independently
155
-
156
- **Verify:** Index `test/fixtures/csharp-project/`. Verify public classes, interfaces, methods, and properties extracted. Verify `///` XML doc comments captured.
157
-
158
- **Tests:** Fixture `.cs` files covering: `public class`, `public interface`, `public enum`, `public record`, public/private method, `public` property, `public const`, XML docstring (`/// <summary>`), `using` directive.
159
-
160
- ---
161
-
162
- ## Task 36: Angular Adapter
163
-
164
- Add Angular as the major frontend framework not yet covered. Angular's decorator-based architecture (`@Component`, `@Injectable`, `@NgModule`, `@Directive`, `@Pipe`) maps clearly onto existing symbol kinds.
165
-
166
- **Deliverables:**
167
- - `src/adapters/angular.ts` — implements `FrameworkAdapter`
168
- - `name`: `'angular'`
169
- - `detect(projectRoot)`:
170
- - Check for `@angular/core` in `package.json` dependencies
171
- - `fileFilter(filePath)`:
172
- - `.component.ts`, `.service.ts`, `.module.ts`, `.directive.ts`, `.pipe.ts`, `.guard.ts`, `.resolver.ts`
173
- - Also `*.ts` files that contain `@Component`, `@Injectable`, etc. (check file extension only; the AST check happens in extraction)
174
- - `extractFrameworkSymbols(tree, source, filePath)`:
175
- - Scan for `class_declaration` nodes with decorator children:
176
- - `@Component` → kind `'component'`, extract `selector` from metadata
177
- - `@Injectable` → kind `'class'` with `frameworkMeta.angular_service = true`
178
- - `@NgModule` → kind `'class'` with `frameworkMeta.angular_module = true`
179
- - `@Directive` → kind `'component'` with `frameworkMeta.angular_directive = true`, extract `selector`
180
- - `@Pipe` → kind `'component'` with `frameworkMeta.angular_pipe = true`, extract `name` from metadata
181
- - `@Guard` / class implementing `CanActivate` → kind `'middleware'`
182
- - For each decorated class: set `name` = class name, `signature` = first decorator line + class declaration
183
- - Extract route paths from `RouterModule.forRoot([...])` and `RouterModule.forChild([...])` call expressions → kind `'route'`
184
- - `enrichMetadata(symbol)`:
185
- - Add `frameworkMeta.selector` for components and directives
186
- - Add `frameworkMeta.angular_injectable = true` for services
187
-
188
- **Selector extraction:**
189
- - Parse the decorator metadata object literal: `@Component({ selector: 'app-header', ... })`
190
- - Extract the `selector` property value as a string
191
- - For `@Pipe`, extract the `name` property
192
-
193
- **Route extraction:**
194
- - Find `RouterModule.forRoot` or `RouterModule.forChild` call expressions
195
- - Walk the routes array literal: extract `path` string properties
196
- - Emit one `'route'` symbol per route with `frameworkMeta.router = 'angular'`
197
- - Handle lazy-loaded routes: `loadChildren: () => import(...)`
198
-
199
- **Key technical notes:**
200
- - Angular's `standalone: true` components (Angular 14+) don't require `NgModule` — detect and handle them
201
- - Signals-based components (Angular 17+) look the same as class components from the AST perspective
202
- - Guards may use the newer functional guard pattern (`export const canActivate = () => ...`) in Angular 15+ — emit as `'middleware'` kind with `frameworkMeta.angular_guard = true`
203
- - Resolvers: `@Resolver` (GraphQL, not Angular routing) is NOT the same as `Resolve<T>` — check the interface, not a decorator
204
-
205
- **Verify:** Index `test/fixtures/angular-project/`. Verify components (with selectors), services, modules, and route paths are extracted.
206
-
207
- **Tests:** Component with selector, service with dependency injection, NgModule, standalone component, directive with selector, pipe with name, route path extraction from RouterModule, functional guard.
208
-
209
- ---
210
-
211
- ## Task 37: NestJS Adapter
212
-
213
- Add NestJS as the primary Node.js backend framework adapter. NestJS's decorator-heavy architecture (`@Controller`, `@Get`, `@Post`, `@Injectable`, `@Module`) makes it ideal for structured extraction — routes, services, and modules are all explicitly declared.
214
-
215
- **Deliverables:**
216
- - `src/adapters/nestjs.ts` — implements `FrameworkAdapter`
217
- - `name`: `'nestjs'`
218
- - `detect(projectRoot)`:
219
- - Check for `@nestjs/core` or `@nestjs/common` in `package.json` dependencies
220
- - `fileFilter(filePath)`:
221
- - `.controller.ts`, `.service.ts`, `.module.ts`, `.guard.ts`, `.middleware.ts`, `.interceptor.ts`, `.pipe.ts`, `.decorator.ts`
222
- - `extractFrameworkSymbols(tree, source, filePath)`:
223
- - `@Controller('path')` class → emit a `'route'` symbol per HTTP-method decorator inside:
224
- - `@Get('/sub')` method → route symbol with `name = 'GET /path/sub'`, `frameworkMeta.http_method = 'GET'`
225
- - `@Post(...)`, `@Put(...)`, `@Patch(...)`, `@Delete(...)` similarly
226
- - Combine controller prefix + method path for the full route
227
- - `@Injectable()` class → kind `'class'` with `frameworkMeta.nestjs_provider = true`
228
- - `@Module(...)` class → kind `'class'` with `frameworkMeta.nestjs_module = true`
229
- - `@Guard()` / `CanActivate` implementors → kind `'middleware'` with `frameworkMeta.nestjs_guard = true`
230
- - `@Middleware()` / `NestMiddleware` → kind `'middleware'`
231
- - `@Interceptor()` → kind `'middleware'` with `frameworkMeta.nestjs_interceptor = true`
232
- - Route path merging:
233
- - Controller prefix: `@Controller('users')` → `/users`
234
- - Method decorator: `@Get(':id')` → `:id`
235
- - Combined: `/users/:id`
236
- - Support empty string prefix (root controller)
237
-
238
- **Key technical notes:**
239
- - NestJS uses TypeScript decorators heavily — the TypeScript handler must already parse the file before the adapter enriches it
240
- - `@ApiTags`, `@ApiOperation` (Swagger decorators) should be ignored — they don't affect routing
241
- - Versioning: `@Controller({ path: 'users', version: '1' })` — include version in route if present
242
- - Microservice message patterns (`@MessagePattern`, `@EventPattern`) are not HTTP routes — skip them in Phase 4
243
-
244
- **Verify:** Index `test/fixtures/nestjs-project/`. Verify GET/POST route symbols with full paths, service symbols, and module symbols.
245
-
246
- **Tests:** Controller with base path + GET/POST methods (path combining), empty-path controller, service with `@Injectable`, module, guard, parameterized route (`:id`), nested path.
247
-
248
- ---
249
-
250
- ## Task 38: Express/Fastify Adapter
251
-
252
- Add lightweight adapters for Express and Fastify. Unlike NestJS, these frameworks define routes procedurally — route extraction requires pattern-matching call expressions rather than decorators.
253
-
254
- **Deliverables:**
255
- - `src/adapters/express.ts` — implements `FrameworkAdapter`
256
- - `name`: `'express'`
257
- - `detect(projectRoot)`: check for `express` in `package.json` dependencies
258
- - `fileFilter(filePath)`: `.ts`, `.js` files (broad — filter by content in extraction)
259
- - `extractFrameworkSymbols(tree, source, filePath)`:
260
- - Scan for `call_expression` nodes matching:
261
- - `app.get(path, ...)`, `app.post(...)`, `app.put(...)`, `app.delete(...)`, `app.patch(...)` → kind `'route'`
262
- - `router.get(...)`, `router.post(...)`, etc. → kind `'route'`
263
- - `app.use(path, ...)` → kind `'middleware'` if path is a string literal
264
- - For each matched call: extract the path string literal as `name`, set `frameworkMeta.http_method`
265
- - Skip dynamic path construction (variable paths) — only extract string literal paths
266
- - Skip calls where `app`/`router` is not a recognizable Express variable name (use heuristic: variable assigned from `express()` or `express.Router()`)
267
-
268
- - `src/adapters/fastify.ts` — implements `FrameworkAdapter`
269
- - `name`: `'fastify'`
270
- - `detect(projectRoot)`: check for `fastify` or `@fastify/core` in `package.json` dependencies
271
- - `fileFilter(filePath)`: `.ts`, `.js` files
272
- - `extractFrameworkSymbols(tree, source, filePath)`:
273
- - Scan for `call_expression` nodes matching:
274
- - `fastify.get(path, ...)`, `fastify.post(...)`, etc. → kind `'route'`
275
- - `fastify.register(plugin, { prefix: '...' })` → kind `'middleware'` with route prefix metadata
276
- - Same string-literal-only constraint as Express adapter
277
-
278
- **Key technical notes:**
279
- - Express route definition order matters for middleware but not for symbol extraction
280
- - Router nesting: `app.use('/api', apiRouter)` gives `apiRouter` routes a `/api` prefix — this is hard to resolve statically without full execution; emit routes without the prefix and note it in `frameworkMeta.prefix_unknown = true`
281
- - Fastify plugins: `fastify.register()` with an inline function is equivalent to `router.use()` — extract the `prefix` option if present
282
- - Both adapters: only extract routes where the path is a string literal — dynamic paths (template literals, variables) are too noisy
283
-
284
- **Verify:** Index `test/fixtures/express-project/` and `test/fixtures/fastify-project/`. Verify GET/POST route symbols and middleware symbols are extracted from literal paths.
285
-
286
- **Tests (Express):** `app.get`/`app.post`/`app.use`, `router.get`/`router.post`, dynamic path variable (skipped), non-Express `app` variable (skipped).
287
- **Tests (Fastify):** `fastify.get`/`fastify.post`/`fastify.register` with prefix.
288
-
289
- ---
290
-
291
- ## Task 39: HTTP Transport Authentication
292
-
293
- Add API key authentication to the HTTP transport. Required before the server can safely bind to non-loopback addresses for team/remote deployments.
294
-
295
- **Deliverables:**
296
- - Update `src/config/config-schema.ts`:
297
- ```typescript
298
- interface HttpConfig {
299
- port: number;
300
- host: string;
301
- corsOrigins: string[];
302
- auth: {
303
- /** Enable bearer token authentication. Default: false. */
304
- enabled: boolean;
305
- /**
306
- * Bearer token to require on all /mcp requests.
307
- * Supports ${ENV_VAR} notation for secret management.
308
- * If omitted when enabled=true, generates a random token on startup and logs it.
309
- */
310
- token: string;
311
- };
312
- }
313
- ```
314
- - Update `src/server/http-server.ts`:
315
- - When `auth.enabled = true`: check `Authorization: Bearer <token>` header on all `POST /mcp` requests
316
- - Reject with `401 Unauthorized` + JSON body `{"error":"Unauthorized"}` if missing or wrong
317
- - `/health` endpoint is always public (no auth required)
318
- - `OPTIONS` preflight is always public
319
- - Log a warning on startup if `host !== '127.0.0.1'` and `auth.enabled = false`
320
- - Update `src/config/config-loader.ts`:
321
- - If `auth.enabled = true` and `auth.token` is empty: generate a random 32-byte hex token with `crypto.randomBytes(32).toString('hex')` and log it to stderr at startup: `PureContext MCP auth token: <token>`
322
- - Apply `resolveEnvVar()` to `auth.token`
323
- - Update `src/server/mcp-server.ts`:
324
- - Pass auth config to `startHttpServer`
325
-
326
- **Security notes:**
327
- - The generated token is logged once to stderr on first startup — users should save it and add it to config
328
- - No rate limiting in Phase 4 (add in Phase 5 if remote deployments are common)
329
- - Token comparison must use `crypto.timingSafeEqual()` to prevent timing attacks
330
-
331
- **Verify:** Start server with `auth.enabled = true` and a configured token. Verify that requests without the token return 401. Verify requests with the correct token succeed. Verify `/health` always returns 200.
332
-
333
- **Tests:** No auth header → 401, wrong token → 401, correct token → 200, health check bypasses auth, token generated when not set (logged to stderr).
334
-
335
- ---
336
-
337
- ## Task 40: Remote Repository Indexing
338
-
339
- Add a new `index_repo` tool that clones and indexes a GitHub (or any Git) repository without requiring the user to clone it manually.
340
-
341
- **Deliverables:**
342
- - `src/server/tools/index-repo.ts`
343
- - Tool name: `index_repo`
344
- - Input schema:
345
- ```json
346
- {
347
- "url": { "type": "string", "description": "Git repository URL (https:// or git@)" },
348
- "branch": { "type": "string", "description": "Branch or tag to index (default: default branch)" },
349
- "token": { "type": "string", "description": "GitHub/GitLab personal access token for private repos (optional)" },
350
- "fileLimit": { "type": "number", "default": 5000 }
351
- }
352
- ```
353
- - Implementation:
354
- 1. Validate the URL — reject non-http(s) and non-git@ URLs
355
- 2. Clone the repo to a temp directory under `~/.purecontext/clones/` using `child_process.spawn('git', ['clone', '--depth=1', '--branch', branch, url, targetDir])`
356
- 3. Call `indexFolder(targetDir, { fileLimit })` with the standard pipeline
357
- 4. Store the clone dir path in the repo metadata so the watcher and `reindexFiles` can reference it
358
- 5. Return the same response as `index_folder` (repoId, symbolCount, fileCount, duration)
359
- - Cleanup: cloned repos are persistent (not temp) — agents can call `delete_index` to remove both the DB and the clone dir
360
-
361
- - Add `clonePath` column to `repos` table in `src/core/db/schema.ts` (nullable — null for locally-indexed repos)
362
- - Update `deleteIndex` in `src/core/index-manager.ts`: if `clonePath` is set, `rmSync(clonePath, { recursive: true })` after deleting the DB
363
-
364
- - `src/config/config-schema.ts`: add `clonesDir: string` (default: `~/.purecontext/clones/`)
365
-
366
- **Security notes:**
367
- - Never execute arbitrary git URLs without validation — only `https://`, `http://`, and `git@` schemes allowed
368
- - The `token` parameter is passed as a URL credential (`https://token@host/path`) — never log it
369
- - Clone directory is isolated under `~/.purecontext/clones/` — same security boundary as the index DB
370
-
371
- **Key technical notes:**
372
- - `--depth=1` shallow clone keeps disk usage low for large repos
373
- - Git must be available on `PATH` — check before executing and return a clear error if not found
374
- - Private repos: inject token into URL as `https://<token>@github.com/owner/repo.git`
375
- - Progress: `git clone` output goes to stderr — capture and forward to logger at debug level
376
- - Timeout: kill the git process after 5 minutes to prevent hanging on large repos
377
-
378
- **Verify:** Clone a small public GitHub repository, verify symbols are indexed correctly. Clone with an invalid URL, verify error response.
379
-
380
- **Tests:** Valid public URL (mock `git clone` with a temp dir), invalid URL scheme rejected, git not on PATH returns clear error, `deleteIndex` removes clone dir.
381
-
382
- ---
383
-
384
- ## Task 41: Semantic Search
385
-
386
- Implement semantic (meaning-based) search over indexed symbols. Fills the PRD Phase 3 deliverable that was deferred. Agents can ask "find functions that validate email addresses" and get relevant symbols even when no literal string matches.
387
-
388
- **Deliverables:**
389
-
390
- **Storage:**
391
- - Add `embedding` column (`BLOB`, nullable) to the `symbols` table
392
- - Add `fts_symbols` virtual table using SQLite FTS5 on `name + signature + summary` for fast keyword fallback
393
-
394
- **Embedding generation:**
395
- - `src/summarizer/embeddings.ts`:
396
- ```typescript
397
- export interface EmbeddingProvider {
398
- embed(texts: string[]): Promise<number[][]>;
399
- }
400
- export function createEmbeddingProvider(config: AIConfig): EmbeddingProvider | null;
401
- ```
402
- - Anthropic: use `voyage-code-2` model via `https://api.voyageai.com/v1/embeddings` (Voyage AI is Anthropic's recommended embedding model for code)
403
- - OpenAI-compatible: use `/v1/embeddings` endpoint with the configured model (e.g. `nomic-embed-text` for Ollama)
404
- - Returns `null` if no embedding provider is configured
405
- - Update `src/summarizer/summarizer.ts` (Stage 3.5 — after AI summarization):
406
- - If embedding provider is configured: batch-generate embeddings for all symbols
407
- - Store as 32-bit float array serialized to Buffer (`Float32Array → Buffer`)
408
- - Max batch size: 128 texts per API call
409
-
410
- **Search:**
411
- - `src/server/tools/search-semantic.ts`
412
- - Tool name: `search_semantic`
413
- - Input schema:
414
- ```json
415
- {
416
- "repo": { "type": "string" },
417
- "query": { "type": "string", "description": "Natural language description of what to find" },
418
- "limit": { "type": "number", "default": 20 },
419
- "minScore": { "type": "number", "default": 0.7, "description": "Minimum cosine similarity (0–1)" }
420
- }
421
- ```
422
- - Implementation:
423
- 1. Embed the query using the same provider used during indexing
424
- 2. Load all symbol embeddings from DB for the repo
425
- 3. Compute cosine similarity between query embedding and each symbol embedding
426
- 4. Return top-`limit` results above `minScore`, sorted by similarity descending
427
- 5. If no embeddings are stored (provider not configured): fall back to FTS5 keyword search
428
- - Output: same structure as `search_symbols` — array of SymbolRecord with a `score` field added
429
- - Update `src/config/config-schema.ts`:
430
- - Add `ai.embeddingModel?: string` and `ai.embeddingProvider?: 'voyage' | 'openai-compatible'`
431
-
432
- **Performance:**
433
- - Cosine similarity is O(n) over all symbols — acceptable up to ~50k symbols (~10ms at 100ns/symbol)
434
- - Beyond 50k symbols, consider chunking or approximate nearest-neighbour (HNSW) — defer to Phase 5
435
- - Embeddings are generated once per index and reused — no re-embedding on queries
436
-
437
- **Key technical notes:**
438
- - `Float32Array` embedding vectors are typically 768 or 1536 dimensions — store as raw bytes (4 bytes × dims)
439
- - Cosine similarity: `dot(a, b) / (|a| × |b|)` — normalize vectors at storage time to reduce query cost to a dot product
440
- - FTS5 fallback: when embeddings are absent, use SQLite's built-in full-text search on `name || ' ' || signature || ' ' || summary`
441
- - Voyage AI `voyage-code-2` is specifically trained on code and documentation — preferred over general-purpose models
442
-
443
- **Verify:** Index a project with an embedding provider configured. Query for a natural language description and verify relevant symbols are returned. Query without embeddings configured — verify FTS5 fallback works.
444
-
445
- **Tests:** Cosine similarity function (unit test), embedding provider mock, `search_semantic` returns results above minScore, FTS5 fallback when no embeddings, `minScore` filtering.
446
-
447
- ---
448
-
449
- ## Task 42: Phase 4 Test Fixtures and Integration Tests
450
-
451
- Validate the full Phase 4 pipeline end-to-end.
452
-
453
- **Deliverables:**
454
-
455
- - `test/fixtures/rust-project/` — representative Rust project:
456
- - `src/lib.rs` — public structs, traits, functions, `///` docstrings
457
- - `src/auth.rs` — `pub struct AuthService`, impl block with methods
458
- - `src/models.rs` — `pub struct User`, `pub enum Role`
459
- - `Cargo.toml` — minimal manifest
460
-
461
- - `test/fixtures/java-project/` — representative Java project:
462
- - `src/AuthService.java` — public class with methods, Javadoc
463
- - `src/User.java` — public class with fields and constructors
464
- - `src/IAuthenticator.java` — public interface
465
- - `src/Role.java` — public enum
466
-
467
- - `test/fixtures/csharp-project/` — representative C# project:
468
- - `src/AuthService.cs` — public class with methods, XML doc comments
469
- - `src/User.cs` — public record/class with properties
470
- - `src/IAuthenticator.cs` — public interface
471
-
472
- - `test/fixtures/angular-project/` — minimal Angular project:
473
- - `package.json` with `@angular/core` dep
474
- - `src/app/app.module.ts` — `@NgModule`
475
- - `src/app/app.component.ts` — `@Component` with selector
476
- - `src/app/auth.service.ts` — `@Injectable` service
477
- - `src/app/app-routing.module.ts` — `RouterModule.forRoot` with routes
478
-
479
- - `test/fixtures/nestjs-project/` — minimal NestJS project:
480
- - `package.json` with `@nestjs/core` dep
481
- - `src/users/users.controller.ts` — `@Controller('users')` with `@Get`/`@Post`
482
- - `src/users/users.service.ts` — `@Injectable` service
483
- - `src/app.module.ts` — `@Module`
484
- - `src/auth/auth.guard.ts` — `CanActivate` guard
485
-
486
- - `test/fixtures/express-project/` — minimal Express project:
487
- - `package.json` with `express` dep
488
- - `src/routes/users.ts` — `router.get`/`router.post` with literal paths
489
- - `src/app.ts` — `app.use('/users', usersRouter)`
490
-
491
- - Integration tests `test/integration/phase4.test.ts`:
492
- 1. Rust: public struct, trait, function extracted; private items skipped
493
- 2. Rust: `///` docstring captured; impl method name includes type
494
- 3. Java: public class, interface, method extracted; private method skipped
495
- 4. Java: Javadoc `/** */` comment captured
496
- 5. C#: public class, property, method extracted; `/// <summary>` captured
497
- 6. Angular: `@Component` produces kind `'component'` with selector; `@Injectable` produces kind `'class'`
498
- 7. Angular: `RouterModule.forRoot` routes extracted
499
- 8. NestJS: `@Controller('users')` + `@Get(':id')` produces route `GET /users/:id`
500
- 9. NestJS: `@Injectable` service produces kind `'class'` with `nestjs_provider` metadata
501
- 10. Express: `router.get('/users', ...)` produces route symbol; dynamic path skipped
502
- 11. HTTP auth: request without token returns 401; correct token returns 200
503
- 12. `index_repo`: mock git clone, verify indexing runs on cloned dir
504
- 13. `search_semantic` FTS5 fallback: query returns results matching name/signature keywords
505
- 14. Full suite regression: all Phase 1–3 tests still green
506
-
507
- **Performance benchmark** `test/benchmarks/phase4.bench.ts`:
508
- - Index `rust-project` → record time and symbol count
509
- - Index `java-project` → record time and symbol count
510
- - `search_semantic` with FTS5 on a 500-symbol repo → verify under 50ms
511
-
512
- ---
513
-
514
- ## Order of Execution
515
-
516
- ```
517
- Task 33: Rust language handler ██░░░░░░░░ Languages
518
- Task 34: Java language handler ███░░░░░░░ Languages
519
- Task 35: C# language handler ████░░░░░░ Languages
520
- Task 36: Angular adapter █████░░░░░ Adapters
521
- Task 37: NestJS adapter ██████░░░░ Adapters
522
- Task 38: Express/Fastify adapter ███████░░░ Adapters
523
- Task 39: HTTP auth ████████░░ Security
524
- Task 40: Remote repository indexing █████████░ Tools
525
- Task 41: Semantic search █████████░ Tools
526
- Task 42: Fixtures + integration tests ██████████ Polish
527
- ```
528
-
529
- Tasks 33, 34, and 35 are fully independent — they can be developed in parallel. Task 36 (Angular) depends on the TypeScript handler (Phase 1) but is independent of 33–35. Tasks 37 and 38 (NestJS, Express/Fastify) are also TypeScript-only and independent of each other. Task 39 (HTTP auth) builds directly on the Phase 3 HTTP transport. Task 40 (remote indexing) depends on the core indexing pipeline being stable but is otherwise independent. Task 41 (semantic search) requires the AI summarizer infrastructure from Phase 3 and adds embedding generation as a new stage. Task 42 requires all previous tasks.
530
-
531
- **After Phase 4 is complete**, consider Phase 5: Ruby and PHP language handlers, approximate nearest-neighbour semantic search for large repos (>50k symbols), rate limiting and multi-tenant auth for hosted deployments, and a web UI for exploring the symbol graph.