nogrep 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/docs/ARCHITECTURE.md +0 -239
- package/docs/CLAUDE.md +0 -161
- package/docs/CONVENTIONS.md +0 -162
- package/docs/SPEC.md +0 -803
- package/docs/TASKS.md +0 -216
package/package.json
CHANGED
package/docs/ARCHITECTURE.md
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
# nogrep — Internal Architecture
|
|
2
|
-
|
|
3
|
-
## Module Boundaries
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
┌──────────────────────────────────────────────────────┐
|
|
7
|
-
│ CC Plugin (slash commands) │
|
|
8
|
-
│ /init · /update · /query · /status · /on · /off │
|
|
9
|
-
│ Claude orchestrates — AI work happens here │
|
|
10
|
-
└────────────────────────┬─────────────────────────────┘
|
|
11
|
-
│ calls scripts via Bash
|
|
12
|
-
┌───────────────┼───────────────┐
|
|
13
|
-
▼ ▼ ▼
|
|
14
|
-
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
|
|
15
|
-
│ Signals │ │ Query │ │ Settings │
|
|
16
|
-
│ (collect) │ │ (lookup) │ │ (r/w JSON) │
|
|
17
|
-
└─────────────┘ └──────┬──────┘ └──────────────┘
|
|
18
|
-
│
|
|
19
|
-
┌─────────────┐ ┌──────┴──────┐
|
|
20
|
-
│ Writer │ │ Validator │
|
|
21
|
-
│ (file I/O) │ │ (freshness) │
|
|
22
|
-
└─────────────┘ └─────────────┘
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
No AI client module — Claude IS the AI. The slash commands contain the analysis prompts, and Claude executes them directly during the session.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Module Responsibilities
|
|
30
|
-
|
|
31
|
-
### `commands/` (slash commands)
|
|
32
|
-
Markdown prompts that guide Claude through each operation. `init.md` is the most complex — it orchestrates the full pipeline. Claude reads script output, performs analysis, and writes results.
|
|
33
|
-
|
|
34
|
-
### `scripts/signals.ts`
|
|
35
|
-
Collects language-agnostic signals from the filesystem. Pure data collection — no AI, no writes to `.nogrep/`.
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
collectSignals(root, options) → SignalResult
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### `scripts/write.ts`
|
|
42
|
-
All file I/O for the `.nogrep/` directory. Takes structured JSON input (from Claude's analysis), writes files.
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
writeContextFiles(nodes: NodeResult[], outputDir: string) → void
|
|
46
|
-
buildIndex(nodes: NodeResult[]) → IndexJson
|
|
47
|
-
buildRegistry(nodes: NodeResult[]) → RegistryJson
|
|
48
|
-
patchClaudeMd(projectRoot: string) → void
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### `scripts/query.ts`
|
|
52
|
-
Pure lookup logic. Reads `_index.json`, matches tags/keywords, ranks results. No AI, no file writes. Called by hooks and `/nogrep:query`.
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
extractTerms(question: string, taxonomy: Taxonomy) → { tags, keywords }
|
|
56
|
-
resolve(terms, index) → RankedResult[]
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### `scripts/validate.ts`
|
|
60
|
-
Computes SHA256 of `src_paths` contents, compares to stored `src_hash` in node frontmatter.
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
checkFreshness(node: ContextNode, projectRoot: string) → StaleResult
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### `scripts/settings.ts`
|
|
67
|
-
Read/write `.claude/settings.json` and `.claude/settings.local.json`. Handles merge logic (local takes precedence).
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## Data Flow: `/nogrep:init`
|
|
72
|
-
|
|
73
|
-
> `$PLUGIN` = `${CLAUDE_PLUGIN_ROOT}` — the absolute path to the installed plugin directory.
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
Slash command: init.md (Claude orchestrates)
|
|
77
|
-
│
|
|
78
|
-
├─→ Bash: node $PLUGIN/dist/signals.js → SignalResult (JSON stdout)
|
|
79
|
-
│
|
|
80
|
-
├─→ Claude analyzes signals → StackResult
|
|
81
|
-
│
|
|
82
|
-
├─→ For each cluster:
|
|
83
|
-
│ Claude reads trimmed source → NodeResult
|
|
84
|
-
│
|
|
85
|
-
├─→ Claude detects flows → FlowResult[]
|
|
86
|
-
│
|
|
87
|
-
└─→ Bash: node $PLUGIN/dist/write.js (receives JSON stdin)
|
|
88
|
-
writes .nogrep/domains/*.md etc
|
|
89
|
-
writes .nogrep/_index.json
|
|
90
|
-
writes .nogrep/_registry.json
|
|
91
|
-
patches CLAUDE.md
|
|
92
|
-
writes .claude/settings.json
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Data Flow: Hooks
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
User types prompt
|
|
101
|
-
│
|
|
102
|
-
└─→ prompt-submit.sh
|
|
103
|
-
node $PLUGIN/dist/query.js --question "$PROMPT"
|
|
104
|
-
→ injects additionalContext
|
|
105
|
-
|
|
106
|
-
CC decides to run grep
|
|
107
|
-
│
|
|
108
|
-
└─→ pre-tool-use.sh (PreToolUse hook)
|
|
109
|
-
extracts keywords from grep command
|
|
110
|
-
node $PLUGIN/dist/query.js --keywords "$KEYWORDS"
|
|
111
|
-
→ injects additionalContext
|
|
112
|
-
|
|
113
|
-
CC starts session
|
|
114
|
-
│
|
|
115
|
-
└─→ session-start.sh (SessionStart hook)
|
|
116
|
-
node $PLUGIN/dist/validate.js
|
|
117
|
-
→ injects staleness warning if needed
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Key Types (`scripts/types.ts`)
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
export interface SignalResult {
|
|
126
|
-
directoryTree: DirectoryNode[]
|
|
127
|
-
extensionMap: Record<string, number>
|
|
128
|
-
manifests: ManifestFile[]
|
|
129
|
-
entryPoints: string[]
|
|
130
|
-
gitChurn: ChurnEntry[]
|
|
131
|
-
largeFiles: FileSize[]
|
|
132
|
-
envFiles: string[]
|
|
133
|
-
testFiles: string[]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export interface StackResult {
|
|
137
|
-
primaryLanguage: string
|
|
138
|
-
frameworks: string[]
|
|
139
|
-
architecture: 'monolith' | 'monorepo' | 'multi-repo' | 'microservice' | 'library'
|
|
140
|
-
domainClusters: DomainCluster[]
|
|
141
|
-
conventions: StackConventions
|
|
142
|
-
stackHints: string
|
|
143
|
-
dynamicTaxonomy: { domain: string[]; tech: string[] }
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface DomainCluster {
|
|
147
|
-
name: string
|
|
148
|
-
path: string
|
|
149
|
-
confidence: number
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface NodeResult {
|
|
153
|
-
id: string
|
|
154
|
-
title: string
|
|
155
|
-
category: 'domain' | 'architecture' | 'flow' | 'entity'
|
|
156
|
-
tags: TagSet
|
|
157
|
-
relatesTo: Relation[]
|
|
158
|
-
inverseRelations: Relation[]
|
|
159
|
-
srcPaths: string[]
|
|
160
|
-
keywords: string[]
|
|
161
|
-
lastSynced: SyncMeta
|
|
162
|
-
// content fields
|
|
163
|
-
purpose: string
|
|
164
|
-
publicSurface: string[]
|
|
165
|
-
doesNotOwn: string[]
|
|
166
|
-
externalDeps: ExternalDep[]
|
|
167
|
-
gotchas: string[]
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export interface TagSet {
|
|
171
|
-
domain: string[]
|
|
172
|
-
layer: string[]
|
|
173
|
-
tech: string[]
|
|
174
|
-
concern: string[]
|
|
175
|
-
type: string[]
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export interface IndexJson {
|
|
179
|
-
version: string
|
|
180
|
-
generatedAt: string
|
|
181
|
-
commit: string
|
|
182
|
-
stack: Pick<StackResult, 'primaryLanguage' | 'frameworks' | 'architecture'>
|
|
183
|
-
tags: Record<string, string[]>
|
|
184
|
-
keywords: Record<string, string[]>
|
|
185
|
-
paths: Record<string, PathEntry>
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export interface RankedResult {
|
|
189
|
-
contextFile: string
|
|
190
|
-
score: number
|
|
191
|
-
matchedOn: string[]
|
|
192
|
-
summary: string
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export interface StaleResult {
|
|
196
|
-
file: string
|
|
197
|
-
isStale: boolean
|
|
198
|
-
reason?: string
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## Error Handling Strategy
|
|
205
|
-
|
|
206
|
-
- Scripts: throw typed errors (`NogrepError` with `code` field), exit 1 with JSON error on stderr
|
|
207
|
-
- Hooks: fail silently (exit 0) — never block CC session
|
|
208
|
-
- Never swallow errors silently in scripts
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
export class NogrepError extends Error {
|
|
212
|
-
constructor(
|
|
213
|
-
message: string,
|
|
214
|
-
public code: 'NO_INDEX' | 'NO_GIT' | 'IO_ERROR' | 'STALE'
|
|
215
|
-
) {
|
|
216
|
-
super(message)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Testing Strategy
|
|
224
|
-
|
|
225
|
-
### Unit tests (no filesystem)
|
|
226
|
-
- `query/extractor.test.ts` — NL extraction logic
|
|
227
|
-
- `query/resolver.test.ts` — index lookup ranking
|
|
228
|
-
- `validator/staleness.test.ts` — hash comparison logic
|
|
229
|
-
- `settings/index.test.ts` — merge logic
|
|
230
|
-
|
|
231
|
-
### Integration tests (real filesystem)
|
|
232
|
-
- `signals.test.ts` — run against fixture projects
|
|
233
|
-
- `writer/*.test.ts` — write to temp dir, verify file contents
|
|
234
|
-
|
|
235
|
-
### Fixture projects (`tests/fixtures/`)
|
|
236
|
-
Minimal 5-10 file projects, enough for signal detection:
|
|
237
|
-
- `nestjs-project/` — NestJS with billing + auth modules
|
|
238
|
-
- `django-project/` — Django with users + payments apps
|
|
239
|
-
- `react-project/` — React app with auth + dashboard features
|
package/docs/CLAUDE.md
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# nogrep — Implementation Guide for Claude Code
|
|
2
|
-
|
|
3
|
-
## What You Are Building
|
|
4
|
-
|
|
5
|
-
`nogrep` is a Claude Code plugin that gives AI agents a navigable index of any codebase, so they stop doing blind grep/find exploration.
|
|
6
|
-
|
|
7
|
-
**The one thing it does:** Generate and maintain a structured `.nogrep/` directory with a reverse index (`_index.json`) and thin context nodes (markdown files), so CC can find the right files in 2 reads instead of 20.
|
|
8
|
-
|
|
9
|
-
**What it is NOT:** A documentation generator, a code search engine, a standalone CLI, or a replacement for GSD or Compodoc. It is a navigation layer — intentionally minimal, fully scoped to Claude Code.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Before You Start
|
|
14
|
-
|
|
15
|
-
Read these files in order before writing any code:
|
|
16
|
-
|
|
17
|
-
1. `docs/SPEC.md` — full technical specification, schemas, pipeline details
|
|
18
|
-
2. `docs/ARCHITECTURE.md` — project structure and module boundaries
|
|
19
|
-
3. `docs/CONVENTIONS.md` — code style, naming, patterns to follow
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Project Stack
|
|
24
|
-
|
|
25
|
-
- **Language:** TypeScript (strict mode)
|
|
26
|
-
- **Runtime:** Node.js 20+
|
|
27
|
-
- **Package manager:** npm
|
|
28
|
-
- **Testing:** Vitest
|
|
29
|
-
- **Build:** `tsup`
|
|
30
|
-
- **Distribution:** CC plugin (npm package)
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Repository Structure to Create
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
nogrep/
|
|
38
|
-
├── scripts/
|
|
39
|
-
│ ├── signals.ts # Phase 1: universal signal collection
|
|
40
|
-
│ ├── query.ts # index lookup (called by hooks)
|
|
41
|
-
│ ├── validate.ts # staleness check (called by hooks)
|
|
42
|
-
│ ├── write.ts # structured file writer (context nodes, index, registry)
|
|
43
|
-
│ ├── settings.ts # read/write .claude/settings.json
|
|
44
|
-
│ ├── trim.ts # language-agnostic source trimming
|
|
45
|
-
│ └── types.ts # all shared TypeScript types
|
|
46
|
-
├── commands/
|
|
47
|
-
│ ├── init.md # orchestrates full init pipeline
|
|
48
|
-
│ ├── update.md # diff-based update of stale nodes
|
|
49
|
-
│ ├── query.md # manual index lookup
|
|
50
|
-
│ ├── status.md # coverage + freshness summary
|
|
51
|
-
│ ├── on.md # enable nogrep
|
|
52
|
-
│ └── off.md # disable nogrep
|
|
53
|
-
├── hooks/
|
|
54
|
-
│ ├── pre-tool-use.sh # intercepts grep/find/rg
|
|
55
|
-
│ ├── session-start.sh # checks index freshness on session start
|
|
56
|
-
│ └── prompt-submit.sh # injects context on every user prompt
|
|
57
|
-
├── templates/
|
|
58
|
-
│ └── claude-md-patch.md # snippet appended to target project CLAUDE.md
|
|
59
|
-
├── tests/
|
|
60
|
-
│ ├── fixtures/ # sample mini-projects for testing
|
|
61
|
-
│ │ ├── nestjs-project/
|
|
62
|
-
│ │ ├── django-project/
|
|
63
|
-
│ │ └── react-project/
|
|
64
|
-
│ ├── signals.test.ts
|
|
65
|
-
│ ├── query.test.ts
|
|
66
|
-
│ └── writer.test.ts
|
|
67
|
-
├── docs/
|
|
68
|
-
│ ├── CLAUDE.md # this file
|
|
69
|
-
│ ├── SPEC.md # full technical spec
|
|
70
|
-
│ ├── ARCHITECTURE.md # internal architecture doc
|
|
71
|
-
│ ├── CONVENTIONS.md # coding conventions
|
|
72
|
-
│ └── TASKS.md # implementation tasks
|
|
73
|
-
├── plugin.json
|
|
74
|
-
├── package.json
|
|
75
|
-
├── tsconfig.json
|
|
76
|
-
└── README.md
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Implementation Order
|
|
82
|
-
|
|
83
|
-
Implement in this exact order — each step is testable before moving on:
|
|
84
|
-
|
|
85
|
-
### Step 1 — Scaffold
|
|
86
|
-
- `package.json`, `tsconfig.json`, `tsup.config.ts`
|
|
87
|
-
- `scripts/types.ts` — all types first
|
|
88
|
-
- `plugin.json` — CC plugin manifest
|
|
89
|
-
|
|
90
|
-
### Step 2 — Settings
|
|
91
|
-
- `scripts/settings.ts` — read/write `.claude/settings.json` and `.claude/settings.local.json`
|
|
92
|
-
- `commands/on.md` and `commands/off.md` slash commands
|
|
93
|
-
|
|
94
|
-
### Step 3 — Phase 1: Signals (no AI)
|
|
95
|
-
- `scripts/signals.ts` — universal signal collection
|
|
96
|
-
- Test against the fixtures in `tests/fixtures/`
|
|
97
|
-
|
|
98
|
-
### Step 4 — Source Trimming
|
|
99
|
-
- `scripts/trim.ts` — language-agnostic source trimming
|
|
100
|
-
- Test against TypeScript, Python, Java snippet fixtures
|
|
101
|
-
|
|
102
|
-
### Step 5 — Writers
|
|
103
|
-
- `scripts/write.ts` — context files, index builder, registry, CLAUDE.md patcher
|
|
104
|
-
- `templates/claude-md-patch.md`
|
|
105
|
-
- Test: write to temp dirs, verify file contents
|
|
106
|
-
|
|
107
|
-
### Step 6 — Init Slash Command
|
|
108
|
-
- `commands/init.md` — orchestrates the full pipeline
|
|
109
|
-
- Embeds Phase 2 + Phase 3 prompts (Claude does the AI work)
|
|
110
|
-
- Calls `dist/signals.js` for data, `dist/write.js` for output via `${CLAUDE_PLUGIN_ROOT}`
|
|
111
|
-
|
|
112
|
-
### Step 7 — Query System
|
|
113
|
-
- `scripts/query.ts` — extractor + resolver
|
|
114
|
-
- `commands/query.md` slash command
|
|
115
|
-
|
|
116
|
-
### Step 8 — Validate + Update + Status
|
|
117
|
-
- `scripts/validate.ts` — staleness detection
|
|
118
|
-
- `commands/update.md` — guided incremental update
|
|
119
|
-
- `commands/status.md` — index health summary
|
|
120
|
-
|
|
121
|
-
### Step 9 — Hooks
|
|
122
|
-
- `hooks/pre-tool-use.sh` — intercepts grep/find
|
|
123
|
-
- `hooks/session-start.sh` — freshness check
|
|
124
|
-
- `hooks/prompt-submit.sh` — context injection
|
|
125
|
-
|
|
126
|
-
### Step 10 — README + Distribution
|
|
127
|
-
- `README.md`
|
|
128
|
-
- Verify `npm pack` ships correct files
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Key Decisions Already Made
|
|
133
|
-
|
|
134
|
-
- **No standalone CLI.** Everything runs inside CC via slash commands and hooks.
|
|
135
|
-
- **No AI client / SDK.** Claude IS the AI — slash commands contain the analysis prompts, Claude executes them directly.
|
|
136
|
-
- **No database.** Everything is files. `.nogrep/` lives in the repo.
|
|
137
|
-
- **Scripts are mechanical.** They collect data, write files, query indexes — no AI work.
|
|
138
|
-
- **Nodes are intentionally thin** — 3 sentences of purpose, public surface, does-not-own, gotchas. Not comprehensive docs.
|
|
139
|
-
- **Manual Notes section** in each node is never overwritten by update.
|
|
140
|
-
- **Hooks intercept at tool-call level** — not just CLAUDE.md instructions, so CC cannot bypass.
|
|
141
|
-
- **Per-project settings** — `.claude/settings.json` (team, committed) or `.claude/settings.local.json` (personal, gitignored).
|
|
142
|
-
- **CI is out of scope for v1** — index maintained via `/nogrep:update` during CC sessions.
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## Environment Variables
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
NOGREP_DEBUG # optional, set to 1 for verbose script output
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
No `ANTHROPIC_API_KEY` needed — Claude does the AI work directly.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## Testing Approach
|
|
157
|
-
|
|
158
|
-
- Use **Vitest** for all tests
|
|
159
|
-
- Fixture projects in `tests/fixtures/` are minimal — 5-10 files each, enough to test detection
|
|
160
|
-
- No AI mocking needed — scripts are pure data/IO
|
|
161
|
-
- Run: `npm test`
|
package/docs/CONVENTIONS.md
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# nogrep — Coding Conventions
|
|
2
|
-
|
|
3
|
-
## Language & Runtime
|
|
4
|
-
|
|
5
|
-
- TypeScript strict mode — `"strict": true` in tsconfig
|
|
6
|
-
- Node.js 20+
|
|
7
|
-
- ESM modules (`"type": "module"` in package.json)
|
|
8
|
-
- No `any` — use `unknown` and narrow
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Naming
|
|
13
|
-
|
|
14
|
-
```typescript
|
|
15
|
-
// Files: kebab-case
|
|
16
|
-
signals.ts
|
|
17
|
-
index-builder.ts
|
|
18
|
-
pre-tool-use.sh
|
|
19
|
-
|
|
20
|
-
// Types/Interfaces: PascalCase
|
|
21
|
-
interface StackResult { ... }
|
|
22
|
-
interface NodeResult { ... }
|
|
23
|
-
|
|
24
|
-
// Functions: camelCase, verb-first
|
|
25
|
-
collectSignals()
|
|
26
|
-
buildIndex()
|
|
27
|
-
resolveQuery()
|
|
28
|
-
extractTerms()
|
|
29
|
-
checkFreshness()
|
|
30
|
-
|
|
31
|
-
// Constants: UPPER_SNAKE_CASE
|
|
32
|
-
const MAX_CLUSTER_LINES = 300
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Functions
|
|
38
|
-
|
|
39
|
-
- Small and focused — one responsibility
|
|
40
|
-
- Pure functions where possible (especially in scripts)
|
|
41
|
-
- Prefer named parameters for 3+ args:
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
// ✅
|
|
45
|
-
writeContextFiles({ nodes, outputDir, preserveManualNotes })
|
|
46
|
-
|
|
47
|
-
// ❌
|
|
48
|
-
writeContextFiles(nodes, outputDir, true)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- Always type return values explicitly on exported functions
|
|
52
|
-
- Async/await everywhere — no raw Promise chains
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## Error Handling
|
|
57
|
-
|
|
58
|
-
Always use `NogrepError` with a code for expected failures:
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
// ✅
|
|
62
|
-
throw new NogrepError('No .nogrep/_index.json found. Run /nogrep:init first.', 'NO_INDEX')
|
|
63
|
-
|
|
64
|
-
// ❌
|
|
65
|
-
throw new Error('not found')
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Scripts catch `NogrepError` and output JSON errors to stderr.
|
|
69
|
-
Hooks fail silently (exit 0) — never block the CC session.
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## File I/O
|
|
74
|
-
|
|
75
|
-
- All file paths are absolute — resolve early, pass around as absolute
|
|
76
|
-
- Use `fs/promises` — no sync fs calls
|
|
77
|
-
- Writer functions take `outputDir` as explicit parameter — never hardcode `.nogrep/`
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Script Output
|
|
82
|
-
|
|
83
|
-
Scripts communicate via stdout (JSON for data, plain text for human-readable).
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
# ✅ Good
|
|
87
|
-
{ "nodes": 17, "stale": 1 }
|
|
88
|
-
|
|
89
|
-
# ❌ Too chatty
|
|
90
|
-
🚀 Starting signal collection...
|
|
91
|
-
📁 Found 42 files...
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Verbose output only when `NOGREP_DEBUG=1`, and goes to stderr.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## Package Structure
|
|
99
|
-
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"type": "module",
|
|
103
|
-
"files": ["dist/", "commands/", "hooks/", "templates/", "plugin.json"]
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Dependencies
|
|
110
|
-
|
|
111
|
-
Keep dependencies minimal. Approved list:
|
|
112
|
-
|
|
113
|
-
| Package | Purpose |
|
|
114
|
-
|---------|---------|
|
|
115
|
-
| `glob` | file glob matching |
|
|
116
|
-
| `gray-matter` | frontmatter parsing |
|
|
117
|
-
| `js-yaml` | YAML serialization |
|
|
118
|
-
| `vitest` | testing (dev) |
|
|
119
|
-
| `tsup` | build (dev) |
|
|
120
|
-
| `typescript` | type checking (dev) |
|
|
121
|
-
| `@types/node` | Node type defs (dev) |
|
|
122
|
-
|
|
123
|
-
Do not add new dependencies without a strong reason.
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## tsconfig.json
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"compilerOptions": {
|
|
132
|
-
"target": "ES2022",
|
|
133
|
-
"module": "ESNext",
|
|
134
|
-
"moduleResolution": "bundler",
|
|
135
|
-
"strict": true,
|
|
136
|
-
"noUncheckedIndexedAccess": true,
|
|
137
|
-
"outDir": "./dist",
|
|
138
|
-
"rootDir": "./scripts",
|
|
139
|
-
"declaration": true,
|
|
140
|
-
"sourceMap": true
|
|
141
|
-
},
|
|
142
|
-
"include": ["scripts/**/*"],
|
|
143
|
-
"exclude": ["node_modules", "dist", "tests"]
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Tests
|
|
150
|
-
|
|
151
|
-
- Test files in `tests/` directory
|
|
152
|
-
- Use `describe` + `it` — not `test`
|
|
153
|
-
- Each test file is independent — no shared state between files
|
|
154
|
-
- Use `vitest`'s `vi.mock` sparingly — prefer dependency injection
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// ✅ injectable
|
|
158
|
-
const result = collectSignals('/path/to/fixture', { exclude: [] })
|
|
159
|
-
|
|
160
|
-
// ❌ global mock
|
|
161
|
-
vi.mock('fs/promises')
|
|
162
|
-
```
|