codebase-context 1.7.0 → 1.8.0

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 (117) hide show
  1. package/README.md +149 -90
  2. package/dist/analyzers/angular/index.d.ts.map +1 -1
  3. package/dist/analyzers/angular/index.js +85 -39
  4. package/dist/analyzers/angular/index.js.map +1 -1
  5. package/dist/analyzers/generic/index.d.ts.map +1 -1
  6. package/dist/analyzers/generic/index.js +5 -4
  7. package/dist/analyzers/generic/index.js.map +1 -1
  8. package/dist/cli-formatters.d.ts +47 -0
  9. package/dist/cli-formatters.d.ts.map +1 -0
  10. package/dist/cli-formatters.js +803 -0
  11. package/dist/cli-formatters.js.map +1 -0
  12. package/dist/cli-memory.d.ts +5 -0
  13. package/dist/cli-memory.d.ts.map +1 -0
  14. package/dist/cli-memory.js +218 -0
  15. package/dist/cli-memory.js.map +1 -0
  16. package/dist/cli.d.ts +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +168 -178
  19. package/dist/cli.js.map +1 -1
  20. package/dist/core/auto-refresh.d.ts +16 -0
  21. package/dist/core/auto-refresh.d.ts.map +1 -0
  22. package/dist/core/auto-refresh.js +25 -0
  23. package/dist/core/auto-refresh.js.map +1 -0
  24. package/dist/core/file-watcher.d.ts +15 -0
  25. package/dist/core/file-watcher.d.ts.map +1 -0
  26. package/dist/core/file-watcher.js +59 -0
  27. package/dist/core/file-watcher.js.map +1 -0
  28. package/dist/core/index-meta.d.ts +3 -0
  29. package/dist/core/index-meta.d.ts.map +1 -1
  30. package/dist/core/index-meta.js +9 -1
  31. package/dist/core/index-meta.js.map +1 -1
  32. package/dist/core/indexer.d.ts.map +1 -1
  33. package/dist/core/indexer.js +74 -15
  34. package/dist/core/indexer.js.map +1 -1
  35. package/dist/core/reranker.d.ts.map +1 -1
  36. package/dist/core/reranker.js +3 -0
  37. package/dist/core/reranker.js.map +1 -1
  38. package/dist/core/search-quality.js +2 -2
  39. package/dist/core/search-quality.js.map +1 -1
  40. package/dist/core/search.d.ts.map +1 -1
  41. package/dist/core/search.js +20 -7
  42. package/dist/core/search.js.map +1 -1
  43. package/dist/core/symbol-references.d.ts +2 -3
  44. package/dist/core/symbol-references.d.ts.map +1 -1
  45. package/dist/core/symbol-references.js +111 -16
  46. package/dist/core/symbol-references.js.map +1 -1
  47. package/dist/embeddings/index.d.ts +8 -0
  48. package/dist/embeddings/index.d.ts.map +1 -1
  49. package/dist/embeddings/index.js +17 -2
  50. package/dist/embeddings/index.js.map +1 -1
  51. package/dist/embeddings/openai.d.ts +1 -1
  52. package/dist/embeddings/openai.d.ts.map +1 -1
  53. package/dist/embeddings/openai.js +3 -1
  54. package/dist/embeddings/openai.js.map +1 -1
  55. package/dist/embeddings/transformers.d.ts +6 -0
  56. package/dist/embeddings/transformers.d.ts.map +1 -1
  57. package/dist/embeddings/transformers.js +12 -5
  58. package/dist/embeddings/transformers.js.map +1 -1
  59. package/dist/embeddings/types.d.ts +1 -0
  60. package/dist/embeddings/types.d.ts.map +1 -1
  61. package/dist/embeddings/types.js +7 -1
  62. package/dist/embeddings/types.js.map +1 -1
  63. package/dist/grammars/manifest.d.ts.map +1 -1
  64. package/dist/grammars/manifest.js +2 -1
  65. package/dist/grammars/manifest.js.map +1 -1
  66. package/dist/index.d.ts +5 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +46 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/patterns/semantics.d.ts +2 -1
  71. package/dist/patterns/semantics.d.ts.map +1 -1
  72. package/dist/patterns/semantics.js +0 -2
  73. package/dist/patterns/semantics.js.map +1 -1
  74. package/dist/storage/index.d.ts +4 -1
  75. package/dist/storage/index.d.ts.map +1 -1
  76. package/dist/storage/index.js +2 -2
  77. package/dist/storage/index.js.map +1 -1
  78. package/dist/storage/lancedb.d.ts +2 -0
  79. package/dist/storage/lancedb.d.ts.map +1 -1
  80. package/dist/storage/lancedb.js +20 -4
  81. package/dist/storage/lancedb.js.map +1 -1
  82. package/dist/storage/types.d.ts +4 -1
  83. package/dist/storage/types.d.ts.map +1 -1
  84. package/dist/storage/types.js.map +1 -1
  85. package/dist/tools/detect-circular-dependencies.d.ts.map +1 -1
  86. package/dist/tools/detect-circular-dependencies.js.map +1 -1
  87. package/dist/tools/get-team-patterns.d.ts.map +1 -1
  88. package/dist/tools/get-team-patterns.js +30 -14
  89. package/dist/tools/get-team-patterns.js.map +1 -1
  90. package/dist/tools/search-codebase.d.ts.map +1 -1
  91. package/dist/tools/search-codebase.js +296 -189
  92. package/dist/tools/search-codebase.js.map +1 -1
  93. package/dist/tools/types.d.ts +193 -1
  94. package/dist/tools/types.d.ts.map +1 -1
  95. package/dist/types/index.d.ts +73 -11
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/index.js +0 -1
  98. package/dist/types/index.js.map +1 -1
  99. package/dist/utils/language-detection.d.ts.map +1 -1
  100. package/dist/utils/language-detection.js +6 -1
  101. package/dist/utils/language-detection.js.map +1 -1
  102. package/dist/utils/tree-sitter.d.ts +11 -0
  103. package/dist/utils/tree-sitter.d.ts.map +1 -1
  104. package/dist/utils/tree-sitter.js +111 -0
  105. package/dist/utils/tree-sitter.js.map +1 -1
  106. package/dist/utils/usage-tracker.d.ts +30 -40
  107. package/dist/utils/usage-tracker.d.ts.map +1 -1
  108. package/dist/utils/usage-tracker.js +66 -8
  109. package/dist/utils/usage-tracker.js.map +1 -1
  110. package/docs/capabilities.md +22 -8
  111. package/docs/cli.md +196 -0
  112. package/grammars/tree-sitter-kotlin.wasm +0 -0
  113. package/package.json +6 -4
  114. package/dist/tools/get-component-usage.d.ts +0 -5
  115. package/dist/tools/get-component-usage.d.ts.map +0 -1
  116. package/dist/tools/get-component-usage.js +0 -83
  117. package/dist/tools/get-component-usage.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # codebase-context
2
2
 
3
- ## Local-first second brain for AI Agents working on your codebase
3
+ ## Local-first second brain for AI agents working on your codebase
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/codebase-context)](https://www.npmjs.com/package/codebase-context) [![license](https://img.shields.io/npm/l/codebase-context)](./LICENSE) [![node](https://img.shields.io/node/v/codebase-context)](./package.json)
6
6
 
@@ -16,11 +16,13 @@ Here's what codebase-context does:
16
16
 
17
17
  **Remembers across sessions** - Decisions, failures, workarounds that look wrong but exist for a reason - the battle scars that aren't in the comments. Recorded once, surfaced automatically so the agent doesn't "clean up" something you spent a week getting right. Conventional git commits (`refactor:`, `migrate:`, `fix:`) auto-extract into memory with zero effort. Stale memories decay and get flagged instead of blindly trusted.
18
18
 
19
- **Checks before editing** - Before editing something, you get a decision card showing whether there's enough evidence to proceed. If a symbol has four callers and only two appear in your search results, the card shows that coverage gap. If coverage is low, `whatWouldHelp` lists the specific searches to run before you touch anything. When code, team memories, and patterns contradict each other, it tells you to look deeper instead of guessing.
19
+ **Checks before editing** - Before editing something, you get a decision card showing whether there's enough evidence to proceed. If a symbol has four callers (files that import or reference it) and only two appear in your search results, the card shows that coverage gap. If coverage is low, `whatWouldHelp` lists the specific searches to run before you touch anything. When code, team memories, and patterns contradict each other, it tells you to look deeper instead of guessing.
20
20
 
21
- One tool call returns all of it. Local-first - your code never leaves your machine.
21
+ One tool call returns all of it. Local-first - your code never leaves your machine by default. Opt into `EMBEDDING_PROVIDER=openai` for cloud speed, but then code is sent externally.
22
22
 
23
- <!-- TODO: Add demo GIF: search_codebase("How does this app attach the auth token to outgoing API calls?") AuthInterceptor top result + preflight + agent proceeds or asks -->
23
+ The index auto-refreshes as you edit - a file watcher triggers incremental reindex in the background when the MCP server is running. No stale context between tool calls.
24
+
25
+ <!-- TODO: Add demo GIF: search_codebase("How does this app attach the auth token to outgoing API calls?") -> AuthInterceptor top result + preflight + agent proceeds or asks -->
24
26
  <!-- ![Demo](./docs/assets/demo.gif) -->
25
27
 
26
28
  ## Quick Start
@@ -57,7 +59,7 @@ Add `.vscode/mcp.json` to your project root:
57
59
  "servers": {
58
60
  "codebase-context": {
59
61
  "command": "npx",
60
- "args": ["-y", "codebase-context", "/path/to/your/project"] // Or "${workspaceFolder}"if your workspace is one project only
62
+ "args": ["-y", "codebase-context", "/path/to/your/project"] // Or "${workspaceFolder}" if your workspace is one project only
61
63
  }
62
64
  }
63
65
  }
@@ -93,9 +95,92 @@ Open Settings > MCP and add:
93
95
  }
94
96
  ```
95
97
 
96
- ## Codex
98
+ ### Codex
99
+
100
+ ```bash
101
+ codex mcp add codebase-context npx -y codebase-context "/path/to/your/project"
102
+ ```
103
+
104
+ ## New to this codebase?
105
+
106
+ Three commands to get what usually takes a new developer weeks to piece together:
107
+
108
+ ```bash
109
+ # What tech stack, architecture, and file count?
110
+ npx -y codebase-context metadata
111
+
112
+ # What does the team actually code like right now?
113
+ npx -y codebase-context patterns
114
+
115
+ # What team decisions were made (and why)?
116
+ npx -y codebase-context memory list
117
+ ```
118
+
119
+ This is also what your AI agent consumes automatically via MCP tools; the CLI is the human-readable version.
120
+
121
+ ### CLI preview
122
+
123
+ ```text
124
+ $ npx -y codebase-context patterns
125
+ ┌─ Team Patterns ──────────────────────────────────────────────────────┐
126
+ │ │
127
+ │ UNIT TEST FRAMEWORK │
128
+ │ USE: Vitest – 96% adoption │
129
+ │ alt CAUTION: Jest – 4% minority pattern │
130
+ │ │
131
+ │ STATE MANAGEMENT │
132
+ │ PREFER: RxJS – 63% adoption │
133
+ │ alt Redux-style store – 25% │
134
+ │ │
135
+ └──────────────────────────────────────────────────────────────────────┘
136
+ ```
137
+
138
+ ```text
139
+ $ npx -y codebase-context search --query "file watcher" --intent edit --limit 1
140
+ ┌─ Search: "file watcher" ─── intent: edit ────────────────────────────┐
141
+ │ Quality: ok (1.00) │
142
+ │ Ready to edit: YES │
143
+ │ │
144
+ │ Best example: index.ts │
145
+ └──────────────────────────────────────────────────────────────────────┘
146
+ ```
97
147
 
98
- Run codex mcp add codebase-context npx -y codebase-context "/path/to/your/project"
148
+ ```text
149
+ $ npx -y codebase-context metadata
150
+ ┌─ codebase-context [monorepo] ────────────────────────────────────────┐
151
+ │ │
152
+ │ Framework: Angular unknown Architecture: mixed │
153
+ │ 130 files · 24,211 lines · 1077 components │
154
+ │ │
155
+ │ Dependencies: @huggingface/transformers · @lancedb/lancedb · │
156
+ │ @modelcontextprotocol/sdk · @typescript-eslint/typescript-estree · │
157
+ │ chokidar · fuse.js (+14 more) │
158
+ │ │
159
+ └──────────────────────────────────────────────────────────────────────┘
160
+ ```
161
+
162
+ ```text
163
+ $ npx -y codebase-context refs --symbol "startFileWatcher"
164
+ ┌─ startFileWatcher ─── 11 references ─── static analysis ─────────────┐
165
+ │ │
166
+ │ startFileWatcher │
167
+ │ │ │
168
+ │ ├─ file-watcher.test.ts:5 │
169
+ │ │ import { startFileWatcher } from '../src/core/file-watcher.... │
170
+ │ │
171
+ └──────────────────────────────────────────────────────────────────────┘
172
+ ```
173
+
174
+ ```text
175
+ $ npx -y codebase-context cycles
176
+ ┌─ Circular Dependencies ──────────────────────────────────────────────┐
177
+ │ │
178
+ │ No cycles found · 98 files · 260 edges · 2.7 avg deps │
179
+ │ │
180
+ └──────────────────────────────────────────────────────────────────────┘
181
+ ```
182
+
183
+ See `docs/cli.md` for the full CLI gallery.
99
184
 
100
185
  ## What It Actually Does
101
186
 
@@ -122,7 +207,7 @@ This is where it all comes together. One call returns:
122
207
  - **Relationships** per result: `importedByCount` and `hasTests` (condensed) + **hints** (capped ranked callers, consumers, tests) — so you see suggested next reads and know what you haven't looked at yet
123
208
  - **Related memories**: up to 3 team decisions, gotchas, and failures matched to the query
124
209
  - **Search quality**: `ok` or `low_confidence` with confidence score and `hint` when low
125
- - **Preflight**: `ready` (boolean) with decision card when `intent="edit"|"refactor"|"migrate"`. Shows `nextAction` (if not ready), `warnings`, `patterns` (do/avoid), `bestExample`, `impact` (caller coverage), and `whatWouldHelp` (next steps). If search quality is low, `ready` is always `false`.
210
+ - **Preflight**: `ready` (boolean) with decision card when `intent="edit"|"refactor"|"migrate"`. Shows `nextAction` (if not ready), `warnings`, `patterns` (do/avoid), `bestExample`, `impact` (import-graph coverage — how many files that import or reference the result are in your search), and `whatWouldHelp` (next steps). If search quality is low, `ready` is always `false`.
126
211
 
127
212
  Snippets are optional (`includeSnippets: true`). When enabled, snippets that have symbol metadata (e.g. from the Generic analyzer's AST chunking or Angular component chunks) start with a scope header so you know where the code lives (e.g. `// AuthService.getToken()` or `// SpotifyApiService`). Example:
128
213
 
@@ -135,43 +220,8 @@ getToken(): string {
135
220
 
136
221
  Default output is lean — if the agent wants code, it calls `read_file`.
137
222
 
138
- ```json
139
- {
140
- "searchQuality": { "status": "ok", "confidence": 0.72 },
141
- "preflight": {
142
- "ready": false,
143
- "nextAction": "2 of 5 callers aren't in results — search for src/app.module.ts",
144
- "patterns": {
145
- "do": ["HttpInterceptorFn — 97%", "standalone components — 84%"],
146
- "avoid": ["constructor injection — 3% (declining)"]
147
- },
148
- "bestExample": "src/auth/auth.interceptor.ts",
149
- "impact": {
150
- "coverage": "3/5 callers in results",
151
- "files": ["src/app.module.ts", "src/boot.ts"]
152
- },
153
- "whatWouldHelp": [
154
- "Search for src/app.module.ts to cover the main caller",
155
- "Call get_team_patterns for auth/ injection patterns"
156
- ]
157
- },
158
- "results": [
159
- {
160
- "file": "src/auth/auth.interceptor.ts:1-20",
161
- "summary": "HTTP interceptor that attaches auth token to outgoing requests",
162
- "score": 0.72,
163
- "type": "service:core",
164
- "trend": "Rising",
165
- "relationships": { "importedByCount": 4, "hasTests": true },
166
- "hints": {
167
- "callers": ["src/app.module.ts", "src/boot.ts"],
168
- "tests": ["src/auth/auth.interceptor.spec.ts"]
169
- }
170
- }
171
- ],
172
- "relatedMemories": ["Always use HttpInterceptorFn (0.97)"]
173
- }
174
- ```
223
+ For scripting and automation, every CLI command accepts `--json` for machine output (stdout = JSON; logs/errors go to stderr).
224
+ See `docs/capabilities.md` for the field reference.
175
225
 
176
226
  Lean enough to fit on one screen. If search quality is low, preflight blocks edits instead of faking confidence.
177
227
 
@@ -194,18 +244,18 @@ Record a decision once. It surfaces automatically in search results and prefligh
194
244
 
195
245
  ### All Tools
196
246
 
197
- | Tool | What it does |
198
- | ------------------------------ | ------------------------------------------------------------------------------------------- |
199
- | `search_codebase` | Hybrid search + decision card. Pass `intent="edit"` to get `ready`, `nextAction`, patterns, caller coverage, and `whatWouldHelp`. |
200
- | `get_team_patterns` | Pattern frequencies, golden files, conflict detection |
247
+ | Tool | What it does |
248
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
249
+ | `search_codebase` | Hybrid search + decision card. Pass `intent="edit"` to get `ready`, `nextAction`, patterns, import-graph coverage, and `whatWouldHelp`. |
250
+ | `get_team_patterns` | Pattern frequencies, golden files, conflict detection |
201
251
  | `get_symbol_references` | Find concrete references to a symbol (usageCount + top snippets). `confidence: "syntactic"` = static/source-based only; no runtime or dynamic dispatch. |
202
- | `remember` | Record a convention, decision, gotcha, or failure |
203
- | `get_memory` | Query team memory with confidence decay scoring |
204
- | `get_codebase_metadata` | Project structure, frameworks, dependencies |
205
- | `get_style_guide` | Style guide rules for the current project |
206
- | `detect_circular_dependencies` | Import cycles between files |
207
- | `refresh_index` | Re-index (full or incremental) + extract git memories |
208
- | `get_indexing_status` | Progress and stats for the current index |
252
+ | `remember` | Record a convention, decision, gotcha, or failure |
253
+ | `get_memory` | Query team memory with confidence decay scoring |
254
+ | `get_codebase_metadata` | Project structure, frameworks, dependencies |
255
+ | `get_style_guide` | Style guide rules for the current project |
256
+ | `detect_circular_dependencies` | Import cycles between files |
257
+ | `refresh_index` | Re-index (full or incremental) + extract git memories |
258
+ | `get_indexing_status` | Progress and stats for the current index |
209
259
 
210
260
  ## Evaluation Harness (`npm run eval`)
211
261
 
@@ -229,7 +279,7 @@ npm run eval -- tests/fixtures/codebases/eval-controlled tests/fixtures/codebase
229
279
 
230
280
  The retrieval pipeline is designed around one goal: give the agent the right context, not just any file that matches.
231
281
 
232
- - **Definition-first ranking** - for exact-name lookups (e.g. a symbol name), the file that *defines* the symbol ranks above files that only use it.
282
+ - **Definition-first ranking** - for exact-name lookups (e.g. a symbol name), the file that _defines_ the symbol ranks above files that only use it.
233
283
  - **Intent classification** - knows whether "AuthService" is a name lookup or "how does auth work" is conceptual. Adjusts keyword/semantic weights accordingly.
234
284
  - **Hybrid fusion (RRF)** - combines keyword and semantic search using Reciprocal Rank Fusion instead of brittle score averaging.
235
285
  - **Query expansion** - conceptual queries automatically expand with domain-relevant terms (auth → login, token, session, guard).
@@ -254,12 +304,13 @@ Structured filters available: `framework`, `language`, `componentType`, `layer`
254
304
 
255
305
  ## Configuration
256
306
 
257
- | Variable | Default | Description |
258
- | ------------------------ | -------------- | --------------------------------------------------------- |
259
- | `EMBEDDING_PROVIDER` | `transformers` | `openai` (fast, cloud) or `transformers` (local, private) |
260
- | `OPENAI_API_KEY` | - | Required only if using `openai` provider |
261
- | `CODEBASE_ROOT` | - | Project root (CLI arg takes precedence) |
262
- | `CODEBASE_CONTEXT_DEBUG` | - | Set to `1` for verbose logging |
307
+ | Variable | Default | Description |
308
+ | ------------------------ | -------------------------- | --------------------------------------------------------------------------------------------- |
309
+ | `EMBEDDING_PROVIDER` | `transformers` | `openai` (fast, cloud) or `transformers` (local, private) |
310
+ | `OPENAI_API_KEY` | - | Required only if using `openai` provider |
311
+ | `CODEBASE_ROOT` | - | Project root (CLI arg takes precedence) |
312
+ | `CODEBASE_CONTEXT_DEBUG` | - | Set to `1` for verbose logging |
313
+ | `EMBEDDING_MODEL` | `Xenova/bge-small-en-v1.5` | Local embedding model override (e.g. `onnx-community/granite-embedding-small-english-r2-ONNX` for Granite) |
263
314
 
264
315
  ## Performance
265
316
 
@@ -289,64 +340,72 @@ Structured filters available: `framework`, `language`, `componentType`, `layer`
289
340
 
290
341
  ## CLI Reference
291
342
 
292
- All MCP tools are available as CLI commands — no AI agent required. Useful for scripting, debugging, and CI workflows.
343
+ All MCP tools are available as CLI commands — no AI agent required. Useful for onboarding, scripting, debugging, and CI workflows.
344
+ For formatted examples and “money shots”, see `docs/cli.md`.
293
345
 
294
346
  Set `CODEBASE_ROOT` to your project root, or run from the project directory.
295
347
 
296
348
  ```bash
297
349
  # Search the indexed codebase
298
- npx codebase-context search --query "authentication middleware"
299
- npx codebase-context search --query "auth" --intent edit --limit 5
350
+ npx -y codebase-context search --query "authentication middleware"
351
+ npx -y codebase-context search --query "auth" --intent edit --limit 5
300
352
 
301
353
  # Project structure, frameworks, and dependencies
302
- npx codebase-context metadata
354
+ npx -y codebase-context metadata
303
355
 
304
356
  # Index state and progress
305
- npx codebase-context status
357
+ npx -y codebase-context status
306
358
 
307
359
  # Re-index the codebase
308
- npx codebase-context reindex
309
- npx codebase-context reindex --incremental --reason "added new service"
360
+ npx -y codebase-context reindex
361
+ npx -y codebase-context reindex --incremental --reason "added new service"
310
362
 
311
363
  # Style guide rules
312
- npx codebase-context style-guide
313
- npx codebase-context style-guide --query "naming" --category patterns
364
+ npx -y codebase-context style-guide
365
+ npx -y codebase-context style-guide --query "naming" --category patterns
314
366
 
315
367
  # Team patterns (DI, state, testing, etc.)
316
- npx codebase-context patterns
317
- npx codebase-context patterns --category testing
368
+ npx -y codebase-context patterns
369
+ npx -y codebase-context patterns --category testing
318
370
 
319
371
  # Symbol references
320
- npx codebase-context refs --symbol "UserService"
321
- npx codebase-context refs --symbol "handleLogin" --limit 20
372
+ npx -y codebase-context refs --symbol "UserService"
373
+ npx -y codebase-context refs --symbol "handleLogin" --limit 20
322
374
 
323
375
  # Circular dependency detection
324
- npx codebase-context cycles
325
- npx codebase-context cycles --scope src/features
376
+ npx -y codebase-context cycles
377
+ npx -y codebase-context cycles --scope src/features
326
378
 
327
379
  # Memory management
328
- npx codebase-context memory list
329
- npx codebase-context memory list --category conventions --type convention
330
- npx codebase-context memory list --query "auth" --json
331
- npx codebase-context memory add --type convention --category tooling --memory "Use pnpm, not npm" --reason "Workspace support and speed"
332
- npx codebase-context memory remove <id>
380
+ npx -y codebase-context memory list
381
+ npx -y codebase-context memory list --category conventions --type convention
382
+ npx -y codebase-context memory list --query "auth" --json
383
+ npx -y codebase-context memory add --type convention --category tooling --memory "Use pnpm, not npm" --reason "Workspace support and speed"
384
+ npx -y codebase-context memory remove <id>
333
385
  ```
334
386
 
335
387
  All commands accept `--json` for raw JSON output suitable for piping and scripting.
336
388
 
337
- ## Tip: Ensuring your AI Agent recalls memory:
389
+ ## What to add to your CLAUDE.md / AGENTS.md
338
390
 
339
- Add this to `.cursorrules`, `CLAUDE.md`, or `AGENTS.md`:
391
+ Paste this into `.cursorrules`, `CLAUDE.md`, `AGENTS.md`, or wherever your AI reads project instructions:
340
392
 
341
- ```
342
- ## Codebase Context
393
+ ```markdown
394
+ ## Codebase Context (MCP)
395
+
396
+ **Start of every task:** Call `get_memory` to load team conventions before writing any code.
343
397
 
344
- **At start of each task:** Call `get_memory` to load team conventions.
398
+ **Before editing existing code:** Call `search_codebase` with `intent: "edit"`. If the preflight card says `ready: false`, read the listed files before touching anything.
345
399
 
346
- **When user says "remember this" or "record this":**
347
- - Call `remember` tool IMMEDIATELY before doing anything else.
400
+ **Before writing new code:** Call `get_team_patterns` to check how the team handles DI, state, testing, and library wrappers — don't introduce a new pattern if one already exists.
401
+
402
+ **When asked to "remember" or "record" something:** Call `remember` immediately, before doing anything else.
403
+
404
+ **When adding imports that cross module boundaries:** Call `detect_circular_dependencies` with the relevant scope after adding the import.
348
405
  ```
349
406
 
407
+ These are the behaviors that make the most difference day-to-day. Copy, trim what doesn't apply to your stack, and add it once.
408
+
350
409
  ## Links
351
410
 
352
411
  - [Motivation](./MOTIVATION.md) - Research and design rationale
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/angular/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,SAAS,EAKV,MAAM,sBAAsB,CAAC;AAS9B,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,mBAAmB,WAA8D;IAC1F,QAAQ,CAAC,QAAQ,OAAO;;IAQxB,OAAO,CAAC,eAAe,CAYrB;IAEF,OAAO,CAAC,uBAAuB,CAM7B;IAEF,OAAO,CAAC,qBAAqB,CAa3B;IAEF,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAqBjD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;YA0B3D,qBAAqB;IAuNnC;;OAEG;IACH,OAAO,CAAC,2BAA2B;YAYrB,uBAAuB;IAmHrC,OAAO,CAAC,wBAAwB;IAgChC,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,aAAa;IA4CrB,OAAO,CAAC,cAAc;YAyCR,mBAAmB;YAiCnB,gBAAgB;IAwB9B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,cAAc;IA0DtB,OAAO,CAAC,oBAAoB;IA2BtB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8JzE;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM;IA2FnC,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,gBAAgB;IAOxB,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAqBtC;IAEF,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/angular/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,SAAS,EAMV,MAAM,sBAAsB,CAAC;AA6B9B,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,mBAAmB,WAA8D;IAC1F,QAAQ,CAAC,QAAQ,OAAO;;IAQxB,OAAO,CAAC,eAAe,CAYrB;IAEF,OAAO,CAAC,uBAAuB,CAM7B;IAEF,OAAO,CAAC,qBAAqB,CAa3B;IAEF,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAqBjD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;YA0B3D,qBAAqB;IA0NnC;;OAEG;IACH,OAAO,CAAC,2BAA2B;YAYrB,uBAAuB;IAyHrC,OAAO,CAAC,wBAAwB;IAiChC,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,uBAAuB;IA0B/B,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAC,cAAc;YAoDR,mBAAmB;YAiCnB,gBAAgB;IAwB9B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,cAAc;IA0DtB,OAAO,CAAC,oBAAoB;IA2BtB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgKzE;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM;IA6FnC,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,gBAAgB;IAOxB,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAqBtC;IAEF,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGjE"}
@@ -3,7 +3,6 @@
3
3
  * Understands components, services, directives, pipes, modules, guards, interceptors, etc.
4
4
  * Detects state management patterns, architectural layers, and Angular-specific patterns
5
5
  */
6
- /* eslint-disable @typescript-eslint/no-explicit-any */
7
6
  import { promises as fs } from 'fs';
8
7
  import path from 'path';
9
8
  import { parse } from '@typescript-eslint/typescript-estree';
@@ -117,7 +116,8 @@ export class AngularAnalyzer {
117
116
  return 'default';
118
117
  if (s.type === 'ImportNamespaceSpecifier')
119
118
  return '*';
120
- return s.imported?.name || s.local.name;
119
+ const specifier = s;
120
+ return specifier.imported.name || specifier.local.name;
121
121
  }),
122
122
  isDefault: node.specifiers.some((s) => s.type === 'ImportDefaultSpecifier'),
123
123
  isDynamic: false,
@@ -290,11 +290,16 @@ export class AngularAnalyzer {
290
290
  return detected;
291
291
  }
292
292
  async extractAngularComponent(classNode, content) {
293
- if (!classNode.decorators || classNode.decorators.length === 0) {
293
+ if (!classNode.id || !classNode.decorators || classNode.decorators.length === 0) {
294
294
  return null;
295
295
  }
296
296
  const decorator = classNode.decorators[0];
297
- const decoratorName = decorator.expression.callee?.name || decorator.expression.name;
297
+ const expr = decorator.expression;
298
+ const decoratorName = expr.type === 'CallExpression' && expr.callee.type === 'Identifier'
299
+ ? expr.callee.name
300
+ : expr.type === 'Identifier'
301
+ ? expr.name
302
+ : '';
298
303
  let componentType;
299
304
  let angularType;
300
305
  // Determine Angular component type
@@ -403,23 +408,26 @@ export class AngularAnalyzer {
403
408
  extractDecoratorMetadata(decorator) {
404
409
  const metadata = {};
405
410
  try {
406
- if (decorator.expression.arguments && decorator.expression.arguments[0]) {
411
+ if (decorator.expression.type === 'CallExpression' && decorator.expression.arguments[0]) {
407
412
  const arg = decorator.expression.arguments[0];
408
413
  if (arg.type === 'ObjectExpression') {
409
414
  for (const prop of arg.properties) {
410
- if (prop.key && prop.value) {
411
- const key = prop.key.name || prop.key.value;
412
- if (prop.value.type === 'Literal') {
413
- metadata[key] = prop.value.value;
414
- }
415
- else if (prop.value.type === 'ArrayExpression') {
416
- metadata[key] = prop.value.elements
417
- .map((el) => (el.type === 'Literal' ? el.value : null))
418
- .filter(Boolean);
419
- }
420
- else if (prop.value.type === 'Identifier') {
421
- metadata[key] = prop.value.name;
422
- }
415
+ if (prop.type !== 'Property')
416
+ continue;
417
+ const keyNode = prop.key;
418
+ const key = keyNode.name ?? String(keyNode.value ?? '');
419
+ if (!key)
420
+ continue;
421
+ if (prop.value.type === 'Literal') {
422
+ metadata[key] = prop.value.value;
423
+ }
424
+ else if (prop.value.type === 'ArrayExpression') {
425
+ metadata[key] = prop.value.elements
426
+ .map((el) => (el && el.type === 'Literal' ? el.value : null))
427
+ .filter(Boolean);
428
+ }
429
+ else if (prop.value.type === 'Identifier') {
430
+ metadata[key] = prop.value.name;
423
431
  }
424
432
  }
425
433
  }
@@ -444,7 +452,7 @@ export class AngularAnalyzer {
444
452
  ];
445
453
  if (classNode.body && classNode.body.body) {
446
454
  for (const member of classNode.body.body) {
447
- if (member.type === 'MethodDefinition' && member.key) {
455
+ if (member.type === 'MethodDefinition' && member.key && member.key.type === 'Identifier') {
448
456
  const methodName = member.key.name;
449
457
  if (lifecycleHooks.includes(methodName)) {
450
458
  hooks.push(methodName);
@@ -462,8 +470,13 @@ export class AngularAnalyzer {
462
470
  if (member.type === 'MethodDefinition' && member.kind === 'constructor') {
463
471
  if (member.value.params) {
464
472
  for (const param of member.value.params) {
465
- if (param.typeAnnotation?.typeAnnotation?.typeName) {
466
- services.push(param.typeAnnotation.typeAnnotation.typeName.name);
473
+ const typedParam = param;
474
+ if (typedParam.typeAnnotation?.typeAnnotation?.type === 'TSTypeReference') {
475
+ const typeRef = typedParam.typeAnnotation
476
+ .typeAnnotation;
477
+ if (typeRef.typeName.type === 'Identifier') {
478
+ services.push(typeRef.typeName.name);
479
+ }
467
480
  }
468
481
  }
469
482
  }
@@ -479,26 +492,46 @@ export class AngularAnalyzer {
479
492
  if (member.type === 'PropertyDefinition') {
480
493
  // Check for decorator-based @Input()
481
494
  if (member.decorators) {
482
- const hasInput = member.decorators.some((d) => d.expression?.callee?.name === 'Input' || d.expression?.name === 'Input');
483
- if (hasInput && member.key) {
495
+ const hasInput = member.decorators.some((d) => {
496
+ const expr = d.expression;
497
+ return ((expr.type === 'CallExpression' &&
498
+ expr.callee.type === 'Identifier' &&
499
+ expr.callee.name === 'Input') ||
500
+ (expr.type === 'Identifier' && expr.name === 'Input'));
501
+ });
502
+ if (hasInput && member.key && 'name' in member.key) {
484
503
  inputs.push({
485
504
  name: member.key.name,
486
- type: member.typeAnnotation?.typeAnnotation?.type || 'any',
505
+ type: member.typeAnnotation?.typeAnnotation?.type || 'unknown',
487
506
  style: 'decorator'
488
507
  });
489
508
  }
490
509
  }
491
510
  // Check for signal-based input() (Angular v17.1+)
492
- if (member.value && member.key) {
493
- const valueStr = member.value.type === 'CallExpression'
494
- ? member.value.callee?.name || member.value.callee?.object?.name
495
- : null;
511
+ if (member.value &&
512
+ member.key &&
513
+ 'name' in member.key &&
514
+ member.value.type === 'CallExpression') {
515
+ const callee = member.value.callee;
516
+ let valueStr = null;
517
+ let isRequired = false;
518
+ if (callee.type === 'Identifier') {
519
+ valueStr = callee.name;
520
+ }
521
+ else if (callee.type === 'MemberExpression') {
522
+ if (callee.object.type === 'Identifier') {
523
+ valueStr = callee.object.name;
524
+ }
525
+ if (callee.property.type === 'Identifier') {
526
+ isRequired = callee.property.name === 'required';
527
+ }
528
+ }
496
529
  if (valueStr === 'input') {
497
530
  inputs.push({
498
531
  name: member.key.name,
499
532
  type: 'InputSignal',
500
533
  style: 'signal',
501
- required: member.value.callee?.property?.name === 'required'
534
+ required: isRequired
502
535
  });
503
536
  }
504
537
  }
@@ -514,8 +547,14 @@ export class AngularAnalyzer {
514
547
  if (member.type === 'PropertyDefinition') {
515
548
  // Check for decorator-based @Output()
516
549
  if (member.decorators) {
517
- const hasOutput = member.decorators.some((d) => d.expression?.callee?.name === 'Output' || d.expression?.name === 'Output');
518
- if (hasOutput && member.key) {
550
+ const hasOutput = member.decorators.some((d) => {
551
+ const expr = d.expression;
552
+ return ((expr.type === 'CallExpression' &&
553
+ expr.callee.type === 'Identifier' &&
554
+ expr.callee.name === 'Output') ||
555
+ (expr.type === 'Identifier' && expr.name === 'Output'));
556
+ });
557
+ if (hasOutput && member.key && 'name' in member.key) {
519
558
  outputs.push({
520
559
  name: member.key.name,
521
560
  type: 'EventEmitter',
@@ -524,8 +563,12 @@ export class AngularAnalyzer {
524
563
  }
525
564
  }
526
565
  // Check for signal-based output() (Angular v17.1+)
527
- if (member.value && member.key) {
528
- const valueStr = member.value.type === 'CallExpression' ? member.value.callee?.name : null;
566
+ if (member.value &&
567
+ member.key &&
568
+ 'name' in member.key &&
569
+ member.value.type === 'CallExpression') {
570
+ const callee = member.value.callee;
571
+ const valueStr = callee.type === 'Identifier' ? callee.name : null;
529
572
  if (valueStr === 'output') {
530
573
  outputs.push({
531
574
  name: member.key.name,
@@ -769,11 +812,12 @@ export class AngularAnalyzer {
769
812
  if (Array.isArray(parsed)) {
770
813
  return metadata;
771
814
  }
772
- const chunks = parsed && Array.isArray(parsed.chunks) ? parsed.chunks : null;
815
+ const parsedObj = parsed;
816
+ const chunks = parsedObj && Array.isArray(parsedObj.chunks) ? parsedObj.chunks : null;
773
817
  if (Array.isArray(chunks) && chunks.length > 0) {
774
818
  console.error(`Loading statistics from ${indexPath}: ${chunks.length} chunks`);
775
819
  metadata.statistics.totalFiles = new Set(chunks.map((c) => c.filePath)).size;
776
- metadata.statistics.totalLines = chunks.reduce((sum, c) => sum + (c.endLine - c.startLine + 1), 0);
820
+ metadata.statistics.totalLines = chunks.reduce((sum, c) => sum + ((c.endLine ?? 0) - (c.startLine ?? 0) + 1), 0);
777
821
  // Count components by type
778
822
  const componentCounts = {};
779
823
  const layerCounts = {
@@ -820,8 +864,8 @@ export class AngularAnalyzer {
820
864
  switch (componentType) {
821
865
  case 'component': {
822
866
  const selector = metadata?.selector || 'unknown';
823
- const inputs = metadata?.inputs?.length || 0;
824
- const outputs = metadata?.outputs?.length || 0;
867
+ const inputs = Array.isArray(metadata?.inputs) ? metadata.inputs.length : 0;
868
+ const outputs = Array.isArray(metadata?.outputs) ? metadata.outputs.length : 0;
825
869
  const lifecycle = this.extractLifecycleMethods(content);
826
870
  return `Angular component '${className}' (selector: ${selector})${lifecycle ? ` with ${lifecycle}` : ''}${inputs ? `, ${inputs} inputs` : ''}${outputs ? `, ${outputs} outputs` : ''}.`;
827
871
  }
@@ -843,8 +887,10 @@ export class AngularAnalyzer {
843
887
  return `Angular pipe '${className}' (name: ${pipeName}) for data transformation.`;
844
888
  }
845
889
  case 'module': {
846
- const imports = metadata?.imports?.length || 0;
847
- const declarations = metadata?.declarations?.length || 0;
890
+ const imports = Array.isArray(metadata?.imports) ? metadata.imports.length : 0;
891
+ const declarations = Array.isArray(metadata?.declarations)
892
+ ? metadata.declarations.length
893
+ : 0;
848
894
  return `Angular module '${className}' with ${declarations} declarations and ${imports} imports.`;
849
895
  }
850
896
  case 'interceptor':