nogrep 1.0.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 +91 -0
- package/commands/init.md +241 -0
- package/commands/off.md +11 -0
- package/commands/on.md +21 -0
- package/commands/query.md +13 -0
- package/commands/status.md +15 -0
- package/commands/update.md +89 -0
- package/dist/chunk-SMUAF6SM.js +12 -0
- package/dist/chunk-SMUAF6SM.js.map +1 -0
- package/dist/query.d.ts +12 -0
- package/dist/query.js +272 -0
- package/dist/query.js.map +1 -0
- package/dist/settings.d.ts +6 -0
- package/dist/settings.js +75 -0
- package/dist/settings.js.map +1 -0
- package/dist/signals.d.ts +9 -0
- package/dist/signals.js +174 -0
- package/dist/signals.js.map +1 -0
- package/dist/trim.d.ts +3 -0
- package/dist/trim.js +266 -0
- package/dist/trim.js.map +1 -0
- package/dist/types.d.ts +141 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +10 -0
- package/dist/validate.js +143 -0
- package/dist/validate.js.map +1 -0
- package/dist/write.d.ts +8 -0
- package/dist/write.js +267 -0
- package/dist/write.js.map +1 -0
- package/docs/ARCHITECTURE.md +239 -0
- package/docs/CLAUDE.md +161 -0
- package/docs/CONVENTIONS.md +162 -0
- package/docs/SPEC.md +803 -0
- package/docs/TASKS.md +216 -0
- package/hooks/hooks.json +35 -0
- package/hooks/pre-tool-use.sh +37 -0
- package/hooks/prompt-submit.sh +26 -0
- package/hooks/session-start.sh +21 -0
- package/package.json +24 -0
- package/scripts/query.ts +290 -0
- package/scripts/settings.ts +98 -0
- package/scripts/signals.ts +237 -0
- package/scripts/trim.ts +379 -0
- package/scripts/types.ts +186 -0
- package/scripts/validate.ts +181 -0
- package/scripts/write.ts +346 -0
- package/templates/claude-md-patch.md +8 -0
package/docs/SPEC.md
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
# nogrep — Technical Specification
|
|
2
|
+
|
|
3
|
+
> A Claude Code plugin + CLI that generates a navigable index for any codebase so AI agents stop doing blind grep/find exploration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Problem](#1-problem)
|
|
10
|
+
2. [The One Thing](#2-the-one-thing)
|
|
11
|
+
3. [Output Structure](#3-output-structure)
|
|
12
|
+
4. [File Schemas](#4-file-schemas)
|
|
13
|
+
5. [CLI Interface](#5-cli-interface)
|
|
14
|
+
6. [Init Pipeline](#6-init-pipeline)
|
|
15
|
+
7. [Update Pipeline](#7-update-pipeline)
|
|
16
|
+
8. [Query System](#8-query-system)
|
|
17
|
+
9. [Tag Taxonomy](#9-tag-taxonomy)
|
|
18
|
+
10. [CC Plugin](#10-cc-plugin)
|
|
19
|
+
11. [Settings](#11-settings)
|
|
20
|
+
12. [CI Integration](#12-ci-integration)
|
|
21
|
+
13. [AI Prompts](#13-ai-prompts)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Problem
|
|
26
|
+
|
|
27
|
+
Claude Code has no navigational index. Every query triggers a full codebase scan:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
find . -name "*.ts" → discovery
|
|
31
|
+
grep -r "keyword" src/ → content scan
|
|
32
|
+
read file → wrong file → repeat
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This is a full table scan on every query — slow, token-expensive, and imprecise.
|
|
36
|
+
|
|
37
|
+
A senior developer doesn't grep. They have a mental map:
|
|
38
|
+
> "Payment issue → src/billing/ → StripeService → webhook idempotency gotcha there"
|
|
39
|
+
|
|
40
|
+
`nogrep` encodes that mental map into a machine-queryable index.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. The One Thing
|
|
45
|
+
|
|
46
|
+
**nogrep does one thing: give AI agents a navigable index so they stop full-scanning codebases.**
|
|
47
|
+
|
|
48
|
+
It is NOT:
|
|
49
|
+
- A documentation generator (that's GSD, Compodoc, JSDoc)
|
|
50
|
+
- A code search engine (that's Sourcegraph, ctags)
|
|
51
|
+
- A runtime service (no servers, no databases)
|
|
52
|
+
- Comprehensive docs — nodes are intentionally minimal
|
|
53
|
+
|
|
54
|
+
It IS:
|
|
55
|
+
- A navigation layer
|
|
56
|
+
- A reverse index
|
|
57
|
+
- A thin context node per domain
|
|
58
|
+
- A CC plugin that intercepts grep at the tool-call level
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 3. Output Structure
|
|
63
|
+
|
|
64
|
+
Generated inside the target project:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
.nogrep/
|
|
68
|
+
├── _index.json # master reverse index — primary lookup file
|
|
69
|
+
├── _registry.json # source path glob → context file mapping (for CI/update)
|
|
70
|
+
├── _taxonomy.json # allowed tags for this project
|
|
71
|
+
├── domains/ # one file per business domain
|
|
72
|
+
│ ├── billing.md
|
|
73
|
+
│ └── auth.md
|
|
74
|
+
├── architecture/ # cross-domain architectural concerns
|
|
75
|
+
│ ├── database.md
|
|
76
|
+
│ └── api-design.md
|
|
77
|
+
├── flows/ # multi-domain business flows
|
|
78
|
+
│ └── checkout.md
|
|
79
|
+
└── entities/ # data models (if applicable)
|
|
80
|
+
└── user.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Also patched in the target project:
|
|
84
|
+
```
|
|
85
|
+
CLAUDE.md # navigation instructions appended
|
|
86
|
+
.claude/settings.json # nogrep.enabled flag
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4. File Schemas
|
|
92
|
+
|
|
93
|
+
### 4.1 Context Node (e.g. `.nogrep/domains/billing.md`)
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
---
|
|
97
|
+
id: billing
|
|
98
|
+
title: Billing & Payments
|
|
99
|
+
category: domain
|
|
100
|
+
tags:
|
|
101
|
+
domain: [billing]
|
|
102
|
+
layer: [business, data, infrastructure]
|
|
103
|
+
tech: [stripe, postgres]
|
|
104
|
+
concern: [error-handling, idempotency]
|
|
105
|
+
type: [module]
|
|
106
|
+
relates_to:
|
|
107
|
+
- id: notifications
|
|
108
|
+
reason: "triggers invoice emails after payment events"
|
|
109
|
+
inverse_relations:
|
|
110
|
+
- id: checkout-flow
|
|
111
|
+
reason: "orchestrates billing as primary step"
|
|
112
|
+
src_paths:
|
|
113
|
+
- src/billing/**
|
|
114
|
+
keywords:
|
|
115
|
+
- stripe
|
|
116
|
+
- webhook
|
|
117
|
+
- invoice
|
|
118
|
+
- retry
|
|
119
|
+
- idempotent
|
|
120
|
+
last_synced:
|
|
121
|
+
commit: abc1234
|
|
122
|
+
timestamp: 2025-03-13T10:00:00Z
|
|
123
|
+
src_hash: sha256:ef9a3c...
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Purpose
|
|
127
|
+
_2-3 sentences of business intent. What this domain exists to do, not how._
|
|
128
|
+
|
|
129
|
+
## Public Surface
|
|
130
|
+
_What other domains call. Exported functions, routes, events._
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
POST /billing/webhook
|
|
134
|
+
BillingService.createSubscription(userId, planId)
|
|
135
|
+
event: billing.invoice.created
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Does Not Own
|
|
139
|
+
- Email delivery → notifications
|
|
140
|
+
- User identity → auth
|
|
141
|
+
|
|
142
|
+
## Gotchas
|
|
143
|
+
- Webhook handler must be idempotent — check event.id before processing
|
|
144
|
+
- All monetary values in cents (integer), never floats
|
|
145
|
+
|
|
146
|
+
## Manual Notes
|
|
147
|
+
_Human annotations. Never overwritten by nogrep update._
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Node design principles:**
|
|
151
|
+
- Purpose: max 3 sentences
|
|
152
|
+
- Gotchas: max 5 bullets
|
|
153
|
+
- If a node is getting long, it's doing too much
|
|
154
|
+
- Nodes answer "should CC look here?" — not "how does this work?"
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 4.2 `_index.json`
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"version": "1.0",
|
|
163
|
+
"generated_at": "2025-03-13T10:00:00Z",
|
|
164
|
+
"commit": "abc1234",
|
|
165
|
+
"stack": {
|
|
166
|
+
"primary_language": "typescript",
|
|
167
|
+
"frameworks": ["nestjs", "react"],
|
|
168
|
+
"architecture": "monolith"
|
|
169
|
+
},
|
|
170
|
+
"tags": {
|
|
171
|
+
"tech:stripe": [
|
|
172
|
+
".nogrep/domains/billing.md",
|
|
173
|
+
".nogrep/flows/checkout.md"
|
|
174
|
+
],
|
|
175
|
+
"tech:redis": [
|
|
176
|
+
".nogrep/domains/auth.md",
|
|
177
|
+
".nogrep/architecture/caching.md"
|
|
178
|
+
],
|
|
179
|
+
"domain:billing": [
|
|
180
|
+
".nogrep/domains/billing.md",
|
|
181
|
+
".nogrep/flows/checkout.md"
|
|
182
|
+
],
|
|
183
|
+
"concern:security": [
|
|
184
|
+
".nogrep/domains/auth.md",
|
|
185
|
+
".nogrep/architecture/api-design.md"
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
"keywords": {
|
|
189
|
+
"webhook": [".nogrep/domains/billing.md"],
|
|
190
|
+
"jwt": [".nogrep/domains/auth.md"],
|
|
191
|
+
"retry": [".nogrep/domains/billing.md", ".nogrep/architecture/event-system.md"]
|
|
192
|
+
},
|
|
193
|
+
"paths": {
|
|
194
|
+
"src/billing/**": {
|
|
195
|
+
"context": ".nogrep/domains/billing.md",
|
|
196
|
+
"tags": ["domain:billing", "tech:stripe", "layer:business"]
|
|
197
|
+
},
|
|
198
|
+
"src/auth/**": {
|
|
199
|
+
"context": ".nogrep/domains/auth.md",
|
|
200
|
+
"tags": ["domain:auth", "concern:security", "tech:redis"]
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### 4.3 `_registry.json`
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"mappings": [
|
|
213
|
+
{
|
|
214
|
+
"glob": "src/billing/**",
|
|
215
|
+
"context_file": ".nogrep/domains/billing.md",
|
|
216
|
+
"watch": true
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"glob": "prisma/schema.prisma",
|
|
220
|
+
"context_file": ".nogrep/architecture/database.md",
|
|
221
|
+
"watch": true
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 4.4 `_taxonomy.json`
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"static": {
|
|
234
|
+
"layer": ["presentation", "business", "data", "infrastructure", "cross-cutting"],
|
|
235
|
+
"concern": ["security", "performance", "caching", "validation", "error-handling", "idempotency", "observability"],
|
|
236
|
+
"type": ["module", "flow", "entity", "integration", "config", "ui", "test"]
|
|
237
|
+
},
|
|
238
|
+
"dynamic": {
|
|
239
|
+
"domain": [],
|
|
240
|
+
"tech": []
|
|
241
|
+
},
|
|
242
|
+
"custom": {}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`dynamic` values are detected per project during init.
|
|
247
|
+
`custom` is user-editable, never overwritten.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### 4.5 `.claude/settings.json` (team-shared)
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"nogrep": {
|
|
256
|
+
"enabled": true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 4.6 `.claude/settings.local.json` (personal, gitignored)
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"nogrep": {
|
|
266
|
+
"enabled": false
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`settings.local.json` takes precedence over `settings.json` for the `enabled` flag.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 5. Plugin Commands
|
|
276
|
+
|
|
277
|
+
All user interaction happens through CC slash commands. No standalone CLI.
|
|
278
|
+
|
|
279
|
+
### Commands
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
/nogrep:init # full init — Claude analyzes project, generates .nogrep/
|
|
283
|
+
/nogrep:update # diff-based update of stale nodes
|
|
284
|
+
/nogrep:query # index lookup
|
|
285
|
+
/nogrep:validate # staleness check
|
|
286
|
+
/nogrep:status # coverage + freshness summary
|
|
287
|
+
/nogrep:on # enable in settings, check index
|
|
288
|
+
/nogrep:off # disable in settings
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Hooks call a thin internal script (`nogrep-run`) for mechanical operations (query lookup, validation). This is not user-facing.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 6. Init Pipeline
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
Phase 1 Phase 2 Phase 3 Phase 4
|
|
299
|
+
Universal → Stack Detection → Deep Analysis → Write
|
|
300
|
+
Signals (Claude analyzes (Claude analyzes .nogrep/
|
|
301
|
+
(script) signals) 1 per cluster) _index.json
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Claude orchestrates the entire pipeline during `/nogrep:init`. Phase 1 runs a script to collect signals. Phases 2-3 are Claude's own analysis. Phase 4 uses writer scripts for structured output.
|
|
305
|
+
|
|
306
|
+
### Phase 1 — Universal Signals (No AI)
|
|
307
|
+
|
|
308
|
+
Collect language-agnostic signals:
|
|
309
|
+
|
|
310
|
+
| Signal | Method |
|
|
311
|
+
|--------|--------|
|
|
312
|
+
| Directory tree | `walk(root, depth=4)`, exclude node_modules/dist/build |
|
|
313
|
+
| File extensions | group files by extension |
|
|
314
|
+
| Dependency manifests | find `package.json`, `requirements.txt`, `pom.xml`, `go.mod`, `Podfile`, `Cargo.toml`, `pubspec.yaml` — note depth (root vs subfolder) |
|
|
315
|
+
| Entry points | find `main.*`, `index.*`, `app.*`, `server.*` |
|
|
316
|
+
| Git churn | `git log --stat` — top 20 most changed files |
|
|
317
|
+
| File size | top 20 largest files |
|
|
318
|
+
| Env files | find `.env*`, `config/**` |
|
|
319
|
+
| Test files | group test files — `*.test.*`, `*.spec.*` |
|
|
320
|
+
|
|
321
|
+
Output: `signals.json` object passed to Phase 2.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
### Phase 2 — Stack Detection (Claude)
|
|
326
|
+
|
|
327
|
+
Input: `signals.json`
|
|
328
|
+
Output: `stack.json`
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
interface StackResult {
|
|
332
|
+
primary_language: string
|
|
333
|
+
frameworks: string[]
|
|
334
|
+
architecture: 'monolith' | 'monorepo' | 'multi-repo' | 'microservice' | 'library'
|
|
335
|
+
domain_clusters: Array<{
|
|
336
|
+
name: string
|
|
337
|
+
path: string
|
|
338
|
+
confidence: number
|
|
339
|
+
}>
|
|
340
|
+
conventions: {
|
|
341
|
+
entry_pattern: string
|
|
342
|
+
test_pattern: string
|
|
343
|
+
config_location: string
|
|
344
|
+
}
|
|
345
|
+
stack_hints: string // reading hints for Phase 3 prompt
|
|
346
|
+
dynamic_taxonomy: {
|
|
347
|
+
domain: string[]
|
|
348
|
+
tech: string[]
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Architecture detection heuristics:**
|
|
354
|
+
- `monolith` — single dependency manifest at root, one primary framework
|
|
355
|
+
- `monorepo` — multiple dependency manifests with shared tooling (nx, turborepo, lerna, workspaces)
|
|
356
|
+
- `multi-repo` — multiple dependency manifests at depth 1, no shared tooling, separate stacks per subfolder (e.g. backend/, frontend/, mobile/)
|
|
357
|
+
- `microservice` — multiple independently deployable services, often with Docker/K8s config
|
|
358
|
+
- `library` — single package, exports API surface, no application entry points
|
|
359
|
+
|
|
360
|
+
See Section 13 for the prompt structure (embedded in `/nogrep:init` slash command).
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### Phase 3 — Deep Analysis (Claude, Per Cluster)
|
|
365
|
+
|
|
366
|
+
For each `domain_cluster` from Phase 2:
|
|
367
|
+
|
|
368
|
+
**Source preparation (language-agnostic trimming):**
|
|
369
|
+
- Keep: file headers, function/method signatures, class/interface declarations, decorators/annotations, exported symbols, inline comments
|
|
370
|
+
- Strip: function bodies, implementation details
|
|
371
|
+
- Target: 100–300 lines per cluster
|
|
372
|
+
|
|
373
|
+
**Output per cluster:** a `ContextNode` object (see types). See Section 13 for the prompt structure.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### Phase 3b — Flow Detection
|
|
378
|
+
|
|
379
|
+
A cluster qualifies as a cross-domain **flow** when:
|
|
380
|
+
- Its import graph touches 3+ distinct domain clusters, OR
|
|
381
|
+
- It is named with flow keywords: `checkout`, `onboarding`, `signup`, `pipeline`, `workflow`, `process`
|
|
382
|
+
|
|
383
|
+
Flows get nodes in `.nogrep/flows/`.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### Phase 4 — Write
|
|
388
|
+
|
|
389
|
+
1. Write all `.md` context node files (with frontmatter + AI content + empty Manual Notes)
|
|
390
|
+
2. Populate `inverse_relations` by scanning all `relates_to` references across nodes
|
|
391
|
+
3. Build `_index.json` aggregating all frontmatter
|
|
392
|
+
4. Build `_registry.json` from `src_paths` in each node
|
|
393
|
+
5. Write `_taxonomy.json` with static + dynamic values
|
|
394
|
+
6. Compute `src_hash` per node (SHA256 of all files matching `src_paths`)
|
|
395
|
+
7. Append navigation instructions to target project `CLAUDE.md`
|
|
396
|
+
8. Write `nogrep.enabled: true` to `.claude/settings.json`
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## 7. Update Pipeline
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
git diff origin/main --name-only
|
|
404
|
+
↓
|
|
405
|
+
Map changed files → affected nodes (via _registry.json globs)
|
|
406
|
+
↓
|
|
407
|
+
For each affected node:
|
|
408
|
+
1. Re-run Phase 1 signals for that cluster only
|
|
409
|
+
2. Re-run Phase 3 deep analysis
|
|
410
|
+
3. Extract and preserve ## Manual Notes
|
|
411
|
+
4. Update src_hash and last_synced
|
|
412
|
+
↓
|
|
413
|
+
Rebuild _index.json
|
|
414
|
+
↓
|
|
415
|
+
(CI: commit .nogrep/ changes)
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Manual Notes Preservation
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
function extractManualNotes(content: string): string {
|
|
422
|
+
const match = content.match(
|
|
423
|
+
/## Manual Notes\n([\s\S]*?)(?=\n## |\n---|\s*$)/
|
|
424
|
+
)
|
|
425
|
+
return match ? match[1].trim() : ''
|
|
426
|
+
}
|
|
427
|
+
// re-inject after AI regeneration
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 8. Query System
|
|
433
|
+
|
|
434
|
+
### Resolution Order
|
|
435
|
+
|
|
436
|
+
```
|
|
437
|
+
1. Keyword match → _index.json .keywords
|
|
438
|
+
2. Tag match → _index.json .tags
|
|
439
|
+
3. Path match → _index.json .paths
|
|
440
|
+
4. NL question → extract keywords/tags → repeat 1-3
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
No AI needed for query. Pure index lookup.
|
|
444
|
+
|
|
445
|
+
### Natural Language Extraction
|
|
446
|
+
|
|
447
|
+
For `--question` input:
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
Question: "how does payment retry work after a failed webhook?"
|
|
451
|
+
|
|
452
|
+
Extracted keywords: [payment, retry, webhook, failed]
|
|
453
|
+
Extracted tags: [domain:billing, concern:error-handling]
|
|
454
|
+
|
|
455
|
+
Lookup: union of all nodes matching any keyword or tag
|
|
456
|
+
Rank: by match count (most relevant first)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Extraction is keyword-based (no AI) — match words in question against taxonomy values and keywords in `_index.json`.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## 9. Tag Taxonomy
|
|
464
|
+
|
|
465
|
+
### Static (universal)
|
|
466
|
+
|
|
467
|
+
**`layer`**
|
|
468
|
+
| Value | Maps to |
|
|
469
|
+
|-------|---------|
|
|
470
|
+
| `presentation` | controllers, views, routes, screens |
|
|
471
|
+
| `business` | services, use-cases, view-models |
|
|
472
|
+
| `data` | repositories, DAOs, stores |
|
|
473
|
+
| `infrastructure` | config, adapters, external clients |
|
|
474
|
+
| `cross-cutting` | middleware, guards, interceptors, hooks |
|
|
475
|
+
|
|
476
|
+
**`concern`**
|
|
477
|
+
`security` · `performance` · `caching` · `validation` · `error-handling` · `idempotency` · `observability`
|
|
478
|
+
|
|
479
|
+
**`type`**
|
|
480
|
+
`module` · `flow` · `entity` · `integration` · `config` · `ui` · `test`
|
|
481
|
+
|
|
482
|
+
### Dynamic (per project)
|
|
483
|
+
|
|
484
|
+
**`domain`** — detected from directory structure in Phase 2
|
|
485
|
+
**`tech`** — detected from dependency manifests in Phase 2
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## 10. CC Plugin
|
|
490
|
+
|
|
491
|
+
This is the **only** interface. Everything runs inside Claude Code.
|
|
492
|
+
|
|
493
|
+
### Plugin Structure
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
nogrep/
|
|
497
|
+
├── plugin.json
|
|
498
|
+
├── commands/
|
|
499
|
+
│ ├── init.md # orchestrates full init pipeline
|
|
500
|
+
│ ├── on.md
|
|
501
|
+
│ ├── off.md
|
|
502
|
+
│ ├── update.md
|
|
503
|
+
│ ├── status.md
|
|
504
|
+
│ └── query.md
|
|
505
|
+
├── hooks/
|
|
506
|
+
│ ├── pre-tool-use.sh
|
|
507
|
+
│ ├── session-start.sh
|
|
508
|
+
│ └── prompt-submit.sh
|
|
509
|
+
└── scripts/
|
|
510
|
+
├── signals.ts # Phase 1 signal collection
|
|
511
|
+
├── query.ts # index lookup (called by hooks)
|
|
512
|
+
├── validate.ts # staleness check (called by hooks)
|
|
513
|
+
├── write.ts # structured file writer
|
|
514
|
+
└── settings.ts # read/write .claude/settings.json
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### `plugin.json`
|
|
518
|
+
|
|
519
|
+
```json
|
|
520
|
+
{
|
|
521
|
+
"name": "nogrep",
|
|
522
|
+
"version": "1.0.0",
|
|
523
|
+
"description": "Navigable codebase index for Claude Code — stop grepping, start navigating",
|
|
524
|
+
"scope": "project",
|
|
525
|
+
"hooks": {
|
|
526
|
+
"PreToolUse": {
|
|
527
|
+
"matcher": "Bash",
|
|
528
|
+
"command": "hooks/pre-tool-use.sh"
|
|
529
|
+
},
|
|
530
|
+
"SessionStart": {
|
|
531
|
+
"command": "hooks/session-start.sh"
|
|
532
|
+
},
|
|
533
|
+
"UserPromptSubmit": {
|
|
534
|
+
"command": "hooks/prompt-submit.sh"
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
### Hook: `pre-tool-use.sh`
|
|
543
|
+
|
|
544
|
+
Intercepts grep/find/rg/ag bash commands and injects nogrep results as `additionalContext` before CC proceeds.
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
#!/bin/bash
|
|
548
|
+
INPUT=$(cat)
|
|
549
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
550
|
+
|
|
551
|
+
# Only intercept search commands
|
|
552
|
+
if ! echo "$COMMAND" | grep -qE '^\s*(grep|find|rg|ag|fd)\s'; then
|
|
553
|
+
exit 0
|
|
554
|
+
fi
|
|
555
|
+
|
|
556
|
+
# Check nogrep is enabled
|
|
557
|
+
ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
|
|
558
|
+
LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
|
|
559
|
+
[ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
|
|
560
|
+
[ "$ENABLED" != "true" ] && exit 0
|
|
561
|
+
|
|
562
|
+
# Check index exists
|
|
563
|
+
[ ! -f ".nogrep/_index.json" ] && exit 0
|
|
564
|
+
|
|
565
|
+
# Extract keywords from the grep command
|
|
566
|
+
KEYWORDS=$(echo "$COMMAND" \
|
|
567
|
+
| sed -E 's/(grep|rg|ag|find)\s+(-[a-zA-Z]+\s+)*//' \
|
|
568
|
+
| tr -d '"'"'" \
|
|
569
|
+
| awk '{print $1}')
|
|
570
|
+
|
|
571
|
+
[ -z "$KEYWORDS" ] && exit 0
|
|
572
|
+
|
|
573
|
+
# Query nogrep
|
|
574
|
+
SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
|
|
575
|
+
RESULT=$(node "$SCRIPT_DIR/query.js" --keywords "$KEYWORDS" --format summary --limit 3 2>/dev/null)
|
|
576
|
+
|
|
577
|
+
if [ -n "$RESULT" ]; then
|
|
578
|
+
jq -n \
|
|
579
|
+
--arg ctx "⚡ nogrep — read these context files before searching:\n\n$RESULT\n\nThese files tell you exactly where to look. Only proceed with the grep if they don't answer your question." \
|
|
580
|
+
'{ additionalContext: $ctx }'
|
|
581
|
+
fi
|
|
582
|
+
|
|
583
|
+
exit 0
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
### Hook: `session-start.sh`
|
|
589
|
+
|
|
590
|
+
Checks index freshness at session start and warns CC.
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
#!/bin/bash
|
|
594
|
+
ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
|
|
595
|
+
LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
|
|
596
|
+
[ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
|
|
597
|
+
[ "$ENABLED" != "true" ] && exit 0
|
|
598
|
+
|
|
599
|
+
if [ ! -f ".nogrep/_index.json" ]; then
|
|
600
|
+
jq -n '{ additionalContext: "⚠️ nogrep is enabled but no index found. Run `/nogrep:init` to generate the codebase index before starting work." }'
|
|
601
|
+
exit 0
|
|
602
|
+
fi
|
|
603
|
+
|
|
604
|
+
SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
|
|
605
|
+
STALE=$(node "$SCRIPT_DIR/validate.js" --format json 2>/dev/null | jq -r '.stale[]?.file' | head -3)
|
|
606
|
+
|
|
607
|
+
if [ -n "$STALE" ]; then
|
|
608
|
+
jq -n \
|
|
609
|
+
--arg s "$STALE" \
|
|
610
|
+
'{ additionalContext: ("⚠️ nogrep index may be stale. Consider running `/nogrep:update` before starting.\nStale nodes:\n" + $s) }'
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
exit 0
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
### Hook: `prompt-submit.sh`
|
|
619
|
+
|
|
620
|
+
Injects relevant context nodes at the moment a user submits a prompt.
|
|
621
|
+
|
|
622
|
+
```bash
|
|
623
|
+
#!/bin/bash
|
|
624
|
+
INPUT=$(cat)
|
|
625
|
+
PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty')
|
|
626
|
+
|
|
627
|
+
ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
|
|
628
|
+
LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
|
|
629
|
+
[ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
|
|
630
|
+
[ "$ENABLED" != "true" ] && exit 0
|
|
631
|
+
[ ! -f ".nogrep/_index.json" ] && exit 0
|
|
632
|
+
[ -z "$PROMPT" ] && exit 0
|
|
633
|
+
|
|
634
|
+
# Only inject for prompts that seem to be about code navigation
|
|
635
|
+
if ! echo "$PROMPT" | grep -qiE '(where|how|which|what|find|look|show|implement|fix|add|change|update|refactor)'; then
|
|
636
|
+
exit 0
|
|
637
|
+
fi
|
|
638
|
+
|
|
639
|
+
SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
|
|
640
|
+
RESULT=$(node "$SCRIPT_DIR/query.js" --question "$PROMPT" --format summary --limit 3 2>/dev/null)
|
|
641
|
+
|
|
642
|
+
if [ -n "$RESULT" ]; then
|
|
643
|
+
jq -n \
|
|
644
|
+
--arg ctx "📍 nogrep context for your question:\n\n$RESULT\n\nRead these files first before exploring source." \
|
|
645
|
+
'{ additionalContext: $ctx }'
|
|
646
|
+
fi
|
|
647
|
+
|
|
648
|
+
exit 0
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### Slash Commands
|
|
654
|
+
|
|
655
|
+
Each command file in `commands/` is a markdown prompt that guides Claude through the operation. Unlike simple shell wrappers, these are rich prompts — Claude does the AI work directly.
|
|
656
|
+
|
|
657
|
+
**`commands/init.md`** — The most important command. Contains:
|
|
658
|
+
- Instructions to run `node "${CLAUDE_PLUGIN_ROOT}/dist/signals.js" --root .` to collect Phase 1 data
|
|
659
|
+
- The Phase 2 prompt (stack detection) — Claude analyzes signals directly
|
|
660
|
+
- The Phase 3 prompt (cluster analysis) — Claude analyzes each cluster
|
|
661
|
+
- Instructions to run `node "${CLAUDE_PLUGIN_ROOT}/dist/write.js"` to generate structured output
|
|
662
|
+
- Instructions to patch CLAUDE.md and write settings
|
|
663
|
+
|
|
664
|
+
**`commands/update.md`** — Guides Claude through:
|
|
665
|
+
- Running `git diff origin/main --name-only` to find changed files
|
|
666
|
+
- Mapping changed files to affected nodes via `_registry.json`
|
|
667
|
+
- Re-analyzing affected clusters
|
|
668
|
+
- Running `node "${CLAUDE_PLUGIN_ROOT}/dist/write.js"` to update nodes (preserving Manual Notes)
|
|
669
|
+
|
|
670
|
+
**`commands/on.md`** — Enable nogrep:
|
|
671
|
+
- Run `node "${CLAUDE_PLUGIN_ROOT}/dist/settings.js" --set enabled=true`
|
|
672
|
+
- Check if `.nogrep/_index.json` exists
|
|
673
|
+
- If missing, suggest running `/nogrep:init`
|
|
674
|
+
|
|
675
|
+
**`commands/off.md`** — Disable nogrep:
|
|
676
|
+
- Run `node "${CLAUDE_PLUGIN_ROOT}/dist/settings.js" --set enabled=false`
|
|
677
|
+
|
|
678
|
+
**`commands/status.md`** — Show index health:
|
|
679
|
+
- Run `node "${CLAUDE_PLUGIN_ROOT}/dist/validate.js" --format text`
|
|
680
|
+
- Show node counts by category, freshness summary
|
|
681
|
+
|
|
682
|
+
**`commands/query.md`** — Manual index lookup:
|
|
683
|
+
- Run `node "${CLAUDE_PLUGIN_ROOT}/dist/query.js" --question "$ARGUMENTS"`
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
## 11. Settings
|
|
688
|
+
|
|
689
|
+
### Resolution Order
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
1. .claude/settings.local.json (personal, gitignored, highest priority)
|
|
693
|
+
2. .claude/settings.json (team, committed to repo)
|
|
694
|
+
3. default: enabled = false
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Settings Script
|
|
698
|
+
|
|
699
|
+
`scripts/settings.ts` handles read/write of settings JSON:
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
// read: merges both files, local takes precedence
|
|
703
|
+
readSettings(projectRoot: string): { enabled: boolean }
|
|
704
|
+
|
|
705
|
+
// write: writes to settings.json by default, settings.local.json with --local flag
|
|
706
|
+
writeSettings(projectRoot: string, settings: { enabled?: boolean }, local?: boolean): void
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## 12. CI Integration
|
|
712
|
+
|
|
713
|
+
Out of scope for v1. The index is maintained via `/nogrep:update` during CC sessions. CI support (validation, auto-update) is a future concern.
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## 13. Prompts (Embedded in Slash Commands)
|
|
718
|
+
|
|
719
|
+
These prompts are embedded in `commands/init.md` and `commands/update.md`. Claude executes them directly — no separate API calls.
|
|
720
|
+
|
|
721
|
+
### Phase 2 — Stack Detection Prompt
|
|
722
|
+
|
|
723
|
+
```
|
|
724
|
+
Analyze this project's signals and return JSON only. No prose, no markdown fences.
|
|
725
|
+
|
|
726
|
+
## Directory tree:
|
|
727
|
+
{directory_tree}
|
|
728
|
+
|
|
729
|
+
## Dependency manifests:
|
|
730
|
+
{manifests}
|
|
731
|
+
|
|
732
|
+
## File extension distribution:
|
|
733
|
+
{extension_map}
|
|
734
|
+
|
|
735
|
+
## Entry point candidates:
|
|
736
|
+
{entry_points}
|
|
737
|
+
|
|
738
|
+
Return this exact shape:
|
|
739
|
+
{
|
|
740
|
+
"primary_language": "typescript",
|
|
741
|
+
"frameworks": ["nestjs", "react"],
|
|
742
|
+
"architecture": "monolith", // or "monorepo", "multi-repo", "microservice", "library"
|
|
743
|
+
"domain_clusters": [
|
|
744
|
+
{ "name": "billing", "path": "src/billing/", "confidence": 0.95 }
|
|
745
|
+
],
|
|
746
|
+
"conventions": {
|
|
747
|
+
"entry_pattern": "*.module.ts",
|
|
748
|
+
"test_pattern": "*.spec.ts",
|
|
749
|
+
"config_location": "src/config/"
|
|
750
|
+
},
|
|
751
|
+
"stack_hints": "NestJS: *.module.ts = module boundary, *.service.ts = business logic, *.controller.ts = HTTP handlers",
|
|
752
|
+
"dynamic_taxonomy": {
|
|
753
|
+
"domain": ["billing", "auth", "users"],
|
|
754
|
+
"tech": ["stripe", "redis", "postgres"]
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
### Phase 3 — Context Node Generation Prompt
|
|
762
|
+
|
|
763
|
+
```
|
|
764
|
+
You are generating a navigation node for an AI coding agent.
|
|
765
|
+
Nodes must be MINIMAL — the agent uses them to decide WHERE to look, not to understand everything.
|
|
766
|
+
|
|
767
|
+
## Project stack:
|
|
768
|
+
{stack_json}
|
|
769
|
+
|
|
770
|
+
## Stack reading hints:
|
|
771
|
+
{stack_hints}
|
|
772
|
+
|
|
773
|
+
## Source files (trimmed to signatures only):
|
|
774
|
+
{trimmed_source}
|
|
775
|
+
|
|
776
|
+
## Allowed tags (use ONLY these exact values — never invent new ones):
|
|
777
|
+
{taxonomy_json}
|
|
778
|
+
|
|
779
|
+
Generate a context node. Return JSON only, no prose, no markdown fences.
|
|
780
|
+
|
|
781
|
+
{
|
|
782
|
+
"purpose": "2-3 sentences MAX. Business intent, not technical description.",
|
|
783
|
+
"public_surface": ["list of exported functions/routes/events other domains use"],
|
|
784
|
+
"does_not_own": ["what this module delegates elsewhere, with → target domain"],
|
|
785
|
+
"external_deps": [{"name": "stripe", "usage": "payment processing"}],
|
|
786
|
+
"tags": {
|
|
787
|
+
"domain": [],
|
|
788
|
+
"layer": [],
|
|
789
|
+
"tech": [],
|
|
790
|
+
"concern": [],
|
|
791
|
+
"type": []
|
|
792
|
+
},
|
|
793
|
+
"keywords": ["terms a developer would search to find this domain"],
|
|
794
|
+
"gotchas": ["max 5 non-obvious behaviors, footguns, or constraints"]
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
Rules:
|
|
798
|
+
- purpose: max 3 sentences
|
|
799
|
+
- gotchas: max 5 items
|
|
800
|
+
- keywords: 5-15 items, think about what words someone would grep for
|
|
801
|
+
- tags: use taxonomy values only, never invent new tag values
|
|
802
|
+
- does_not_own: include explicit redirections ("email delivery → notifications")
|
|
803
|
+
```
|