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.
- package/README.md +149 -90
- package/dist/analyzers/angular/index.d.ts.map +1 -1
- package/dist/analyzers/angular/index.js +85 -39
- package/dist/analyzers/angular/index.js.map +1 -1
- package/dist/analyzers/generic/index.d.ts.map +1 -1
- package/dist/analyzers/generic/index.js +5 -4
- package/dist/analyzers/generic/index.js.map +1 -1
- package/dist/cli-formatters.d.ts +47 -0
- package/dist/cli-formatters.d.ts.map +1 -0
- package/dist/cli-formatters.js +803 -0
- package/dist/cli-formatters.js.map +1 -0
- package/dist/cli-memory.d.ts +5 -0
- package/dist/cli-memory.d.ts.map +1 -0
- package/dist/cli-memory.js +218 -0
- package/dist/cli-memory.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +168 -178
- package/dist/cli.js.map +1 -1
- package/dist/core/auto-refresh.d.ts +16 -0
- package/dist/core/auto-refresh.d.ts.map +1 -0
- package/dist/core/auto-refresh.js +25 -0
- package/dist/core/auto-refresh.js.map +1 -0
- package/dist/core/file-watcher.d.ts +15 -0
- package/dist/core/file-watcher.d.ts.map +1 -0
- package/dist/core/file-watcher.js +59 -0
- package/dist/core/file-watcher.js.map +1 -0
- package/dist/core/index-meta.d.ts +3 -0
- package/dist/core/index-meta.d.ts.map +1 -1
- package/dist/core/index-meta.js +9 -1
- package/dist/core/index-meta.js.map +1 -1
- package/dist/core/indexer.d.ts.map +1 -1
- package/dist/core/indexer.js +74 -15
- package/dist/core/indexer.js.map +1 -1
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +3 -0
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/search-quality.js +2 -2
- package/dist/core/search-quality.js.map +1 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +20 -7
- package/dist/core/search.js.map +1 -1
- package/dist/core/symbol-references.d.ts +2 -3
- package/dist/core/symbol-references.d.ts.map +1 -1
- package/dist/core/symbol-references.js +111 -16
- package/dist/core/symbol-references.js.map +1 -1
- package/dist/embeddings/index.d.ts +8 -0
- package/dist/embeddings/index.d.ts.map +1 -1
- package/dist/embeddings/index.js +17 -2
- package/dist/embeddings/index.js.map +1 -1
- package/dist/embeddings/openai.d.ts +1 -1
- package/dist/embeddings/openai.d.ts.map +1 -1
- package/dist/embeddings/openai.js +3 -1
- package/dist/embeddings/openai.js.map +1 -1
- package/dist/embeddings/transformers.d.ts +6 -0
- package/dist/embeddings/transformers.d.ts.map +1 -1
- package/dist/embeddings/transformers.js +12 -5
- package/dist/embeddings/transformers.js.map +1 -1
- package/dist/embeddings/types.d.ts +1 -0
- package/dist/embeddings/types.d.ts.map +1 -1
- package/dist/embeddings/types.js +7 -1
- package/dist/embeddings/types.js.map +1 -1
- package/dist/grammars/manifest.d.ts.map +1 -1
- package/dist/grammars/manifest.js +2 -1
- package/dist/grammars/manifest.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -3
- package/dist/index.js.map +1 -1
- package/dist/patterns/semantics.d.ts +2 -1
- package/dist/patterns/semantics.d.ts.map +1 -1
- package/dist/patterns/semantics.js +0 -2
- package/dist/patterns/semantics.js.map +1 -1
- package/dist/storage/index.d.ts +4 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/lancedb.d.ts +2 -0
- package/dist/storage/lancedb.d.ts.map +1 -1
- package/dist/storage/lancedb.js +20 -4
- package/dist/storage/lancedb.js.map +1 -1
- package/dist/storage/types.d.ts +4 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/tools/detect-circular-dependencies.d.ts.map +1 -1
- package/dist/tools/detect-circular-dependencies.js.map +1 -1
- package/dist/tools/get-team-patterns.d.ts.map +1 -1
- package/dist/tools/get-team-patterns.js +30 -14
- package/dist/tools/get-team-patterns.js.map +1 -1
- package/dist/tools/search-codebase.d.ts.map +1 -1
- package/dist/tools/search-codebase.js +296 -189
- package/dist/tools/search-codebase.js.map +1 -1
- package/dist/tools/types.d.ts +193 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +73 -11
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/language-detection.d.ts.map +1 -1
- package/dist/utils/language-detection.js +6 -1
- package/dist/utils/language-detection.js.map +1 -1
- package/dist/utils/tree-sitter.d.ts +11 -0
- package/dist/utils/tree-sitter.d.ts.map +1 -1
- package/dist/utils/tree-sitter.js +111 -0
- package/dist/utils/tree-sitter.js.map +1 -1
- package/dist/utils/usage-tracker.d.ts +30 -40
- package/dist/utils/usage-tracker.d.ts.map +1 -1
- package/dist/utils/usage-tracker.js +66 -8
- package/dist/utils/usage-tracker.js.map +1 -1
- package/docs/capabilities.md +22 -8
- package/docs/cli.md +196 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/package.json +6 -4
- package/dist/tools/get-component-usage.d.ts +0 -5
- package/dist/tools/get-component-usage.d.ts.map +0 -1
- package/dist/tools/get-component-usage.js +0 -83
- 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
|
|
3
|
+
## Local-first second brain for AI agents working on your codebase
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/codebase-context) [](./LICENSE) [](./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
|
-
|
|
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
|
<!--  -->
|
|
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
|
-
|
|
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
|
-
|
|
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` (
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
258
|
-
| ------------------------ |
|
|
259
|
-
| `EMBEDDING_PROVIDER` | `transformers`
|
|
260
|
-
| `OPENAI_API_KEY` | -
|
|
261
|
-
| `CODEBASE_ROOT` | -
|
|
262
|
-
| `CODEBASE_CONTEXT_DEBUG` | -
|
|
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
|
-
##
|
|
389
|
+
## What to add to your CLAUDE.md / AGENTS.md
|
|
338
390
|
|
|
339
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
347
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
466
|
-
|
|
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) =>
|
|
483
|
-
|
|
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 || '
|
|
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 &&
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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:
|
|
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) =>
|
|
518
|
-
|
|
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 &&
|
|
528
|
-
|
|
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
|
|
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
|
|
824
|
-
const outputs = metadata?.outputs
|
|
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
|
|
847
|
-
const declarations = metadata?.declarations
|
|
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':
|