nogrep 1.1.1 → 1.2.1

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 (58) hide show
  1. package/README.md +15 -11
  2. package/package.json +2 -2
  3. package/{plugin → plugins/nogrep}/.claude-plugin/plugin.json +0 -1
  4. package/{plugin → plugins/nogrep}/commands/init.md +5 -6
  5. package/{plugin → plugins/nogrep}/commands/off.md +1 -1
  6. package/{plugin → plugins/nogrep}/commands/on.md +1 -1
  7. package/plugins/nogrep/commands/query.md +23 -0
  8. package/{plugin → plugins/nogrep}/commands/status.md +1 -1
  9. package/{plugin → plugins/nogrep}/commands/update.md +3 -3
  10. package/plugins/nogrep/package.json +1 -0
  11. package/plugins/nogrep/scripts/chunk-7D4SUZUM.js +38 -0
  12. package/{plugin/dist/chunk-OJSJ63PH.js → plugins/nogrep/scripts/chunk-ICPV2JWV.js} +1 -1
  13. package/plugins/nogrep/scripts/chunk-ICPV2JWV.js.map +1 -0
  14. package/{plugin/dist → plugins/nogrep/scripts}/query.js +13 -10
  15. package/plugins/nogrep/scripts/query.js.map +1 -0
  16. package/{plugin/dist → plugins/nogrep/scripts}/settings.js +9 -5
  17. package/plugins/nogrep/scripts/settings.js.map +1 -0
  18. package/{plugin/dist → plugins/nogrep/scripts}/signals.js +8 -4
  19. package/plugins/nogrep/scripts/signals.js.map +1 -0
  20. package/{plugin/dist → plugins/nogrep/scripts}/trim.js +2 -0
  21. package/plugins/nogrep/scripts/trim.js.map +1 -0
  22. package/plugins/nogrep/scripts/types.js +8 -0
  23. package/plugins/nogrep/scripts/types.js.map +1 -0
  24. package/plugins/nogrep/scripts/validate.js +9690 -0
  25. package/plugins/nogrep/scripts/validate.js.map +1 -0
  26. package/{plugin/dist → plugins/nogrep/scripts}/write.d.ts +1 -2
  27. package/plugins/nogrep/scripts/write.js +2870 -0
  28. package/plugins/nogrep/scripts/write.js.map +1 -0
  29. package/scripts/query.ts +11 -9
  30. package/scripts/settings.ts +7 -5
  31. package/scripts/signals.ts +6 -4
  32. package/scripts/validate.ts +11 -9
  33. package/scripts/write.ts +8 -50
  34. package/plugin/commands/query.md +0 -13
  35. package/plugin/dist/chunk-OJSJ63PH.js.map +0 -1
  36. package/plugin/dist/query.js.map +0 -1
  37. package/plugin/dist/settings.js.map +0 -1
  38. package/plugin/dist/signals.js.map +0 -1
  39. package/plugin/dist/trim.js.map +0 -1
  40. package/plugin/dist/types.js +0 -7
  41. package/plugin/dist/validate.js +0 -143
  42. package/plugin/dist/validate.js.map +0 -1
  43. package/plugin/dist/write.js +0 -267
  44. package/plugin/dist/write.js.map +0 -1
  45. package/plugin/hooks/hooks.json +0 -53
  46. package/plugin/hooks/pre-tool-use-glob.sh +0 -40
  47. package/plugin/hooks/pre-tool-use-grep.sh +0 -35
  48. package/plugin/hooks/pre-tool-use.sh +0 -37
  49. package/plugin/hooks/prompt-submit.sh +0 -26
  50. package/plugin/hooks/session-start.sh +0 -21
  51. package/plugin/templates/claude-md-patch.md +0 -8
  52. /package/{plugin/dist/types.js.map → plugins/nogrep/scripts/chunk-7D4SUZUM.js.map} +0 -0
  53. /package/{plugin/dist → plugins/nogrep/scripts}/query.d.ts +0 -0
  54. /package/{plugin/dist → plugins/nogrep/scripts}/settings.d.ts +0 -0
  55. /package/{plugin/dist → plugins/nogrep/scripts}/signals.d.ts +0 -0
  56. /package/{plugin/dist → plugins/nogrep/scripts}/trim.d.ts +0 -0
  57. /package/{plugin/dist → plugins/nogrep/scripts}/types.d.ts +0 -0
  58. /package/{plugin/dist → plugins/nogrep/scripts}/validate.d.ts +0 -0
@@ -1,267 +0,0 @@
1
- // scripts/write.ts
2
- import { readFile, writeFile, mkdir } from "fs/promises";
3
- import { join, resolve, dirname } from "path";
4
- import { execFile } from "child_process";
5
- import { promisify } from "util";
6
- import yaml from "js-yaml";
7
- var execFileAsync = promisify(execFile);
8
- function extractManualNotes(content) {
9
- const match = content.match(
10
- /## Manual Notes\n([\s\S]*?)(?=\n## |\n---|\s*$)/
11
- );
12
- return match ? match[1].trim() : "";
13
- }
14
- function buildNodeFrontmatter(node) {
15
- return {
16
- id: node.id,
17
- title: node.title,
18
- category: node.category,
19
- tags: {
20
- domain: node.tags.domain,
21
- layer: node.tags.layer,
22
- tech: node.tags.tech,
23
- concern: node.tags.concern,
24
- type: node.tags.type
25
- },
26
- relates_to: node.relatesTo.map((r) => ({ id: r.id, reason: r.reason })),
27
- inverse_relations: node.inverseRelations.map((r) => ({ id: r.id, reason: r.reason })),
28
- src_paths: node.srcPaths,
29
- keywords: node.keywords,
30
- last_synced: {
31
- commit: node.lastSynced.commit,
32
- timestamp: node.lastSynced.timestamp,
33
- src_hash: node.lastSynced.srcHash
34
- }
35
- };
36
- }
37
- function buildNodeMarkdown(node, manualNotes) {
38
- const fm = buildNodeFrontmatter(node);
39
- const yamlStr = yaml.dump(fm, { lineWidth: -1, quotingType: '"', forceQuotes: false });
40
- const sections = [];
41
- sections.push(`---
42
- ${yamlStr.trimEnd()}
43
- ---`);
44
- sections.push(`
45
- ## Purpose
46
- ${node.purpose}`);
47
- if (node.publicSurface.length > 0) {
48
- sections.push(`
49
- ## Public Surface
50
-
51
- \`\`\`
52
- ${node.publicSurface.join("\n")}
53
- \`\`\``);
54
- }
55
- if (node.doesNotOwn.length > 0) {
56
- sections.push(`
57
- ## Does Not Own
58
- ${node.doesNotOwn.map((d) => `- ${d}`).join("\n")}`);
59
- }
60
- if (node.gotchas.length > 0) {
61
- sections.push(`
62
- ## Gotchas
63
- ${node.gotchas.map((g) => `- ${g}`).join("\n")}`);
64
- }
65
- const notesContent = manualNotes || "_Human annotations. Never overwritten by nogrep update._";
66
- sections.push(`
67
- ## Manual Notes
68
- ${notesContent}`);
69
- return sections.join("\n") + "\n";
70
- }
71
- function categoryDir(category) {
72
- switch (category) {
73
- case "domain":
74
- return "domains";
75
- case "architecture":
76
- return "architecture";
77
- case "flow":
78
- return "flows";
79
- case "entity":
80
- return "entities";
81
- }
82
- }
83
- async function writeContextNodes(nodes, outputDir) {
84
- for (const node of nodes) {
85
- const dir = join(outputDir, categoryDir(node.category));
86
- await mkdir(dir, { recursive: true });
87
- const filePath = join(dir, `${node.id}.md`);
88
- let manualNotes = "";
89
- try {
90
- const existing = await readFile(filePath, "utf-8");
91
- manualNotes = extractManualNotes(existing);
92
- } catch {
93
- }
94
- const content = buildNodeMarkdown(node, manualNotes);
95
- await writeFile(filePath, content, "utf-8");
96
- }
97
- }
98
- function buildIndex(nodes, stack) {
99
- const tags = {};
100
- const keywords = {};
101
- const paths = {};
102
- const inverseMap = /* @__PURE__ */ new Map();
103
- for (const node of nodes) {
104
- for (const rel of node.relatesTo) {
105
- const existing = inverseMap.get(rel.id) ?? [];
106
- existing.push({ fromId: node.id, reason: rel.reason });
107
- inverseMap.set(rel.id, existing);
108
- }
109
- }
110
- for (const node of nodes) {
111
- const contextFile = `.nogrep/${categoryDir(node.category)}/${node.id}.md`;
112
- const inverseEntries = inverseMap.get(node.id) ?? [];
113
- for (const inv of inverseEntries) {
114
- if (!node.inverseRelations.some((r) => r.id === inv.fromId)) {
115
- node.inverseRelations.push({ id: inv.fromId, reason: inv.reason });
116
- }
117
- }
118
- const tagCategories = [
119
- ["domain", node.tags.domain],
120
- ["layer", node.tags.layer],
121
- ["tech", node.tags.tech],
122
- ["concern", node.tags.concern],
123
- ["type", node.tags.type]
124
- ];
125
- const flatTags = [];
126
- for (const [cat, values] of tagCategories) {
127
- for (const val of values) {
128
- const tagKey = `${cat}:${val}`;
129
- flatTags.push(tagKey);
130
- const tagList = tags[tagKey] ?? [];
131
- if (!tagList.includes(contextFile)) {
132
- tagList.push(contextFile);
133
- }
134
- tags[tagKey] = tagList;
135
- }
136
- }
137
- for (const kw of node.keywords) {
138
- const kwList = keywords[kw] ?? [];
139
- if (!kwList.includes(contextFile)) {
140
- kwList.push(contextFile);
141
- }
142
- keywords[kw] = kwList;
143
- }
144
- for (const srcPath of node.srcPaths) {
145
- paths[srcPath] = { context: contextFile, tags: flatTags };
146
- }
147
- }
148
- let commit = "";
149
- try {
150
- } catch {
151
- }
152
- return {
153
- version: "1.0",
154
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
155
- commit,
156
- stack: {
157
- primaryLanguage: stack.primaryLanguage,
158
- frameworks: stack.frameworks,
159
- architecture: stack.architecture
160
- },
161
- tags,
162
- keywords,
163
- paths
164
- };
165
- }
166
- function buildRegistry(nodes) {
167
- const mappings = nodes.flatMap(
168
- (node) => node.srcPaths.map((srcPath) => ({
169
- glob: srcPath,
170
- contextFile: `.nogrep/${categoryDir(node.category)}/${node.id}.md`,
171
- watch: true
172
- }))
173
- );
174
- return { mappings };
175
- }
176
- async function patchClaudeMd(projectRoot) {
177
- const claudeMdPath = join(projectRoot, "CLAUDE.md");
178
- const patchPath = join(dirname(import.meta.url.replace("file://", "")), "..", "templates", "claude-md-patch.md");
179
- let patch;
180
- try {
181
- patch = await readFile(patchPath, "utf-8");
182
- } catch {
183
- patch = [
184
- "<!-- nogrep -->",
185
- "## Code Navigation",
186
- "",
187
- "This project uses [nogrep](https://github.com/techtulp/nogrep).",
188
- "Context files in `.nogrep/` are a navigable index of this codebase.",
189
- "When you see nogrep results injected into your context, trust them \u2014",
190
- "read those files before exploring source.",
191
- "<!-- /nogrep -->"
192
- ].join("\n") + "\n";
193
- }
194
- let existing = "";
195
- try {
196
- existing = await readFile(claudeMdPath, "utf-8");
197
- } catch {
198
- }
199
- if (existing.includes("<!-- nogrep -->")) {
200
- return;
201
- }
202
- const newContent = existing ? existing.trimEnd() + "\n\n" + patch : patch;
203
- await writeFile(claudeMdPath, newContent, "utf-8");
204
- }
205
- async function writeAll(input, projectRoot) {
206
- const outputDir = join(projectRoot, ".nogrep");
207
- await mkdir(outputDir, { recursive: true });
208
- await writeContextNodes(input.nodes, outputDir);
209
- const index = buildIndex(input.nodes, input.stack);
210
- try {
211
- const { stdout } = await execFileAsync("git", ["rev-parse", "--short", "HEAD"], {
212
- cwd: projectRoot
213
- });
214
- index.commit = stdout.trim();
215
- } catch {
216
- }
217
- await writeFile(
218
- join(outputDir, "_index.json"),
219
- JSON.stringify(index, null, 2) + "\n",
220
- "utf-8"
221
- );
222
- const registry = buildRegistry(input.nodes);
223
- await writeFile(
224
- join(outputDir, "_registry.json"),
225
- JSON.stringify(registry, null, 2) + "\n",
226
- "utf-8"
227
- );
228
- await patchClaudeMd(projectRoot);
229
- }
230
- async function main() {
231
- const args = process.argv.slice(2);
232
- let inputFile;
233
- let projectRoot = process.cwd();
234
- for (let i = 0; i < args.length; i++) {
235
- if (args[i] === "--input" && args[i + 1]) {
236
- inputFile = args[i + 1];
237
- i++;
238
- } else if (args[i] === "--root" && args[i + 1]) {
239
- projectRoot = resolve(args[i + 1]);
240
- i++;
241
- }
242
- }
243
- let rawInput;
244
- if (inputFile) {
245
- rawInput = await readFile(resolve(inputFile), "utf-8");
246
- } else {
247
- const chunks = [];
248
- for await (const chunk of process.stdin) {
249
- chunks.push(chunk);
250
- }
251
- rawInput = Buffer.concat(chunks).toString("utf-8");
252
- }
253
- const input = JSON.parse(rawInput);
254
- await writeAll(input, projectRoot);
255
- }
256
- main().catch((err) => {
257
- const message = err instanceof Error ? err.message : String(err);
258
- process.stderr.write(JSON.stringify({ error: message }) + "\n");
259
- process.exitCode = 1;
260
- });
261
- export {
262
- buildIndex,
263
- buildRegistry,
264
- patchClaudeMd,
265
- writeContextNodes
266
- };
267
- //# sourceMappingURL=write.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../scripts/write.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'\nimport { join, resolve, dirname } from 'node:path'\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { createHash } from 'node:crypto'\nimport { glob } from 'glob'\nimport matter from 'gray-matter'\nimport yaml from 'js-yaml'\nimport type {\n NodeResult,\n StackResult,\n IndexJson,\n RegistryJson,\n PathEntry,\n NogrepError,\n} from './types.js'\n\nconst execFileAsync = promisify(execFile)\n\n// --- Manual Notes preservation ---\n\nfunction extractManualNotes(content: string): string {\n const match = content.match(\n /## Manual Notes\\n([\\s\\S]*?)(?=\\n## |\\n---|\\s*$)/,\n )\n return match ? match[1]!.trim() : ''\n}\n\n// --- Context node markdown generation ---\n\nfunction buildNodeFrontmatter(node: NodeResult): Record<string, unknown> {\n return {\n id: node.id,\n title: node.title,\n category: node.category,\n tags: {\n domain: node.tags.domain,\n layer: node.tags.layer,\n tech: node.tags.tech,\n concern: node.tags.concern,\n type: node.tags.type,\n },\n relates_to: node.relatesTo.map(r => ({ id: r.id, reason: r.reason })),\n inverse_relations: node.inverseRelations.map(r => ({ id: r.id, reason: r.reason })),\n src_paths: node.srcPaths,\n keywords: node.keywords,\n last_synced: {\n commit: node.lastSynced.commit,\n timestamp: node.lastSynced.timestamp,\n src_hash: node.lastSynced.srcHash,\n },\n }\n}\n\nfunction buildNodeMarkdown(node: NodeResult, manualNotes: string): string {\n const fm = buildNodeFrontmatter(node)\n const yamlStr = yaml.dump(fm, { lineWidth: -1, quotingType: '\"', forceQuotes: false })\n\n const sections: string[] = []\n sections.push(`---\\n${yamlStr.trimEnd()}\\n---`)\n\n sections.push(`\\n## Purpose\\n${node.purpose}`)\n\n if (node.publicSurface.length > 0) {\n sections.push(`\\n## Public Surface\\n\\n\\`\\`\\`\\n${node.publicSurface.join('\\n')}\\n\\`\\`\\``)\n }\n\n if (node.doesNotOwn.length > 0) {\n sections.push(`\\n## Does Not Own\\n${node.doesNotOwn.map(d => `- ${d}`).join('\\n')}`)\n }\n\n if (node.gotchas.length > 0) {\n sections.push(`\\n## Gotchas\\n${node.gotchas.map(g => `- ${g}`).join('\\n')}`)\n }\n\n const notesContent = manualNotes || '_Human annotations. Never overwritten by nogrep update._'\n sections.push(`\\n## Manual Notes\\n${notesContent}`)\n\n return sections.join('\\n') + '\\n'\n}\n\n// --- Write context files ---\n\nfunction categoryDir(category: NodeResult['category']): string {\n switch (category) {\n case 'domain': return 'domains'\n case 'architecture': return 'architecture'\n case 'flow': return 'flows'\n case 'entity': return 'entities'\n }\n}\n\nexport async function writeContextNodes(\n nodes: NodeResult[],\n outputDir: string,\n): Promise<void> {\n for (const node of nodes) {\n const dir = join(outputDir, categoryDir(node.category))\n await mkdir(dir, { recursive: true })\n\n const filePath = join(dir, `${node.id}.md`)\n\n // Preserve existing manual notes\n let manualNotes = ''\n try {\n const existing = await readFile(filePath, 'utf-8')\n manualNotes = extractManualNotes(existing)\n } catch {\n // File doesn't exist yet\n }\n\n const content = buildNodeMarkdown(node, manualNotes)\n await writeFile(filePath, content, 'utf-8')\n }\n}\n\n// --- Build index ---\n\nexport function buildIndex(\n nodes: NodeResult[],\n stack: Pick<StackResult, 'primaryLanguage' | 'frameworks' | 'architecture'>,\n): IndexJson {\n const tags: Record<string, string[]> = {}\n const keywords: Record<string, string[]> = {}\n const paths: Record<string, PathEntry> = {}\n\n // Populate inverse relations\n const inverseMap = new Map<string, Array<{ fromId: string; reason: string }>>()\n for (const node of nodes) {\n for (const rel of node.relatesTo) {\n const existing = inverseMap.get(rel.id) ?? []\n existing.push({ fromId: node.id, reason: rel.reason })\n inverseMap.set(rel.id, existing)\n }\n }\n\n for (const node of nodes) {\n const contextFile = `.nogrep/${categoryDir(node.category)}/${node.id}.md`\n\n // Merge inverse relations from the map\n const inverseEntries = inverseMap.get(node.id) ?? []\n for (const inv of inverseEntries) {\n if (!node.inverseRelations.some(r => r.id === inv.fromId)) {\n node.inverseRelations.push({ id: inv.fromId, reason: inv.reason })\n }\n }\n\n // Build tag index\n const tagCategories: Array<[string, string[]]> = [\n ['domain', node.tags.domain],\n ['layer', node.tags.layer],\n ['tech', node.tags.tech],\n ['concern', node.tags.concern],\n ['type', node.tags.type],\n ]\n\n const flatTags: string[] = []\n for (const [cat, values] of tagCategories) {\n for (const val of values) {\n const tagKey = `${cat}:${val}`\n flatTags.push(tagKey)\n const tagList = tags[tagKey] ?? []\n if (!tagList.includes(contextFile)) {\n tagList.push(contextFile)\n }\n tags[tagKey] = tagList\n }\n }\n\n // Build keyword index\n for (const kw of node.keywords) {\n const kwList = keywords[kw] ?? []\n if (!kwList.includes(contextFile)) {\n kwList.push(contextFile)\n }\n keywords[kw] = kwList\n }\n\n // Build path index\n for (const srcPath of node.srcPaths) {\n paths[srcPath] = { context: contextFile, tags: flatTags }\n }\n }\n\n let commit = ''\n try {\n // Sync exec not ideal but this is a one-time build step\n // We'll set it from the caller if available\n } catch {\n // no git\n }\n\n return {\n version: '1.0',\n generatedAt: new Date().toISOString(),\n commit,\n stack: {\n primaryLanguage: stack.primaryLanguage,\n frameworks: stack.frameworks,\n architecture: stack.architecture,\n },\n tags,\n keywords,\n paths,\n }\n}\n\n// --- Build registry ---\n\nexport function buildRegistry(nodes: NodeResult[]): RegistryJson {\n const mappings = nodes.flatMap(node =>\n node.srcPaths.map(srcPath => ({\n glob: srcPath,\n contextFile: `.nogrep/${categoryDir(node.category)}/${node.id}.md`,\n watch: true,\n })),\n )\n\n return { mappings }\n}\n\n// --- Patch CLAUDE.md ---\n\nexport async function patchClaudeMd(projectRoot: string): Promise<void> {\n const claudeMdPath = join(projectRoot, 'CLAUDE.md')\n const patchPath = join(dirname(import.meta.url.replace('file://', '')), '..', 'templates', 'claude-md-patch.md')\n\n let patch: string\n try {\n patch = await readFile(patchPath, 'utf-8')\n } catch {\n // Fallback: inline the patch content\n patch = [\n '<!-- nogrep -->',\n '## Code Navigation',\n '',\n 'This project uses [nogrep](https://github.com/techtulp/nogrep).',\n 'Context files in `.nogrep/` are a navigable index of this codebase.',\n 'When you see nogrep results injected into your context, trust them —',\n 'read those files before exploring source.',\n '<!-- /nogrep -->',\n ].join('\\n') + '\\n'\n }\n\n let existing = ''\n try {\n existing = await readFile(claudeMdPath, 'utf-8')\n } catch {\n // File doesn't exist\n }\n\n // Check for existing nogrep marker\n if (existing.includes('<!-- nogrep -->')) {\n return\n }\n\n const newContent = existing\n ? existing.trimEnd() + '\\n\\n' + patch\n : patch\n\n await writeFile(claudeMdPath, newContent, 'utf-8')\n}\n\n// --- Write all outputs ---\n\ninterface WriteInput {\n nodes: NodeResult[]\n stack: Pick<StackResult, 'primaryLanguage' | 'frameworks' | 'architecture'>\n}\n\nasync function writeAll(input: WriteInput, projectRoot: string): Promise<void> {\n const outputDir = join(projectRoot, '.nogrep')\n await mkdir(outputDir, { recursive: true })\n\n // Write context node files\n await writeContextNodes(input.nodes, outputDir)\n\n // Build and write index\n const index = buildIndex(input.nodes, input.stack)\n\n // Try to get current git commit\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', '--short', 'HEAD'], {\n cwd: projectRoot,\n })\n index.commit = stdout.trim()\n } catch {\n // no git\n }\n\n await writeFile(\n join(outputDir, '_index.json'),\n JSON.stringify(index, null, 2) + '\\n',\n 'utf-8',\n )\n\n // Build and write registry\n const registry = buildRegistry(input.nodes)\n await writeFile(\n join(outputDir, '_registry.json'),\n JSON.stringify(registry, null, 2) + '\\n',\n 'utf-8',\n )\n\n // Patch CLAUDE.md\n await patchClaudeMd(projectRoot)\n}\n\n// --- CLI interface ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n let inputFile: string | undefined\n let projectRoot = process.cwd()\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--input' && args[i + 1]) {\n inputFile = args[i + 1]!\n i++\n } else if (args[i] === '--root' && args[i + 1]) {\n projectRoot = resolve(args[i + 1]!)\n i++\n }\n }\n\n let rawInput: string\n if (inputFile) {\n rawInput = await readFile(resolve(inputFile), 'utf-8')\n } else {\n // Read from stdin\n const chunks: Buffer[] = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer)\n }\n rawInput = Buffer.concat(chunks).toString('utf-8')\n }\n\n const input = JSON.parse(rawInput) as WriteInput\n await writeAll(input, projectRoot)\n}\n\nmain().catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(JSON.stringify({ error: message }) + '\\n')\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
@@ -1,53 +0,0 @@
1
- {
2
- "hooks": {
3
- "PreToolUse": [
4
- {
5
- "matcher": "Bash",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use.sh"
10
- }
11
- ]
12
- },
13
- {
14
- "matcher": "Grep",
15
- "hooks": [
16
- {
17
- "type": "command",
18
- "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use-grep.sh"
19
- }
20
- ]
21
- },
22
- {
23
- "matcher": "Glob",
24
- "hooks": [
25
- {
26
- "type": "command",
27
- "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use-glob.sh"
28
- }
29
- ]
30
- }
31
- ],
32
- "SessionStart": [
33
- {
34
- "hooks": [
35
- {
36
- "type": "command",
37
- "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
38
- }
39
- ]
40
- }
41
- ],
42
- "UserPromptSubmit": [
43
- {
44
- "hooks": [
45
- {
46
- "type": "command",
47
- "command": "${CLAUDE_PLUGIN_ROOT}/hooks/prompt-submit.sh"
48
- }
49
- ]
50
- }
51
- ]
52
- }
53
- }
@@ -1,40 +0,0 @@
1
- #!/bin/bash
2
- INPUT=$(cat)
3
-
4
- # Check nogrep is enabled
5
- ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
6
- LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
7
- [ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
8
- [ "$ENABLED" != "true" ] && exit 0
9
-
10
- # Check index exists
11
- [ ! -f ".nogrep/_index.json" ] && exit 0
12
-
13
- # Extract the glob pattern from Glob tool input
14
- PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty')
15
- [ -z "$PATTERN" ] && exit 0
16
-
17
- # Extract meaningful keywords from glob pattern
18
- # e.g. "**/*auth*.ts" -> "auth", "src/**/guard*" -> "guard"
19
- KEYWORDS=$(echo "$PATTERN" \
20
- | sed -E 's/\*\*//g' \
21
- | sed -E 's/\*//g' \
22
- | sed -E 's/[{}(),]/ /g' \
23
- | sed -E 's#/+# #g' \
24
- | sed -E 's/\.[a-z]+$//g' \
25
- | tr -s ' ' \
26
- | xargs)
27
-
28
- [ -z "$KEYWORDS" ] && exit 0
29
-
30
- # Query nogrep
31
- SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
32
- RESULT=$(node "$SCRIPT_DIR/query.js" --keywords "$KEYWORDS" --format summary --limit 3 2>/dev/null)
33
-
34
- if [ -n "$RESULT" ]; then
35
- jq -n \
36
- --arg ctx "nogrep — these context files may help narrow your search:\n\n$RESULT\n\nCheck these files for relevant paths before globbing broadly." \
37
- '{ additionalContext: $ctx }'
38
- fi
39
-
40
- exit 0
@@ -1,35 +0,0 @@
1
- #!/bin/bash
2
- INPUT=$(cat)
3
-
4
- # Check nogrep is enabled
5
- ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
6
- LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
7
- [ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
8
- [ "$ENABLED" != "true" ] && exit 0
9
-
10
- # Check index exists
11
- [ ! -f ".nogrep/_index.json" ] && exit 0
12
-
13
- # Extract the search pattern from Grep tool input
14
- PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty')
15
- [ -z "$PATTERN" ] && exit 0
16
-
17
- # Strip regex syntax to get searchable keywords
18
- KEYWORDS=$(echo "$PATTERN" \
19
- | sed -E 's/[\\^$.*+?{}()\[\]|]/ /g' \
20
- | tr -s ' ' \
21
- | xargs)
22
-
23
- [ -z "$KEYWORDS" ] && exit 0
24
-
25
- # Query nogrep
26
- SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
27
- RESULT=$(node "$SCRIPT_DIR/query.js" --keywords "$KEYWORDS" --format summary --limit 3 2>/dev/null)
28
-
29
- if [ -n "$RESULT" ]; then
30
- jq -n \
31
- --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." \
32
- '{ additionalContext: $ctx }'
33
- fi
34
-
35
- exit 0
@@ -1,37 +0,0 @@
1
- #!/bin/bash
2
- INPUT=$(cat)
3
- COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
4
-
5
- # Only intercept search commands
6
- if ! echo "$COMMAND" | grep -qE '^\s*(grep|find|rg|ag|fd)\s'; then
7
- exit 0
8
- fi
9
-
10
- # Check nogrep is enabled
11
- ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
12
- LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
13
- [ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
14
- [ "$ENABLED" != "true" ] && exit 0
15
-
16
- # Check index exists
17
- [ ! -f ".nogrep/_index.json" ] && exit 0
18
-
19
- # Extract keywords from the grep command
20
- KEYWORDS=$(echo "$COMMAND" \
21
- | sed -E 's/(grep|rg|ag|find)\s+(-[a-zA-Z]+\s+)*//' \
22
- | tr -d '"'"'" \
23
- | awk '{print $1}')
24
-
25
- [ -z "$KEYWORDS" ] && exit 0
26
-
27
- # Query nogrep
28
- SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
29
- RESULT=$(node "$SCRIPT_DIR/query.js" --keywords "$KEYWORDS" --format summary --limit 3 2>/dev/null)
30
-
31
- if [ -n "$RESULT" ]; then
32
- jq -n \
33
- --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." \
34
- '{ additionalContext: $ctx }'
35
- fi
36
-
37
- exit 0
@@ -1,26 +0,0 @@
1
- #!/bin/bash
2
- INPUT=$(cat)
3
- PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty')
4
-
5
- ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
6
- LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
7
- [ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
8
- [ "$ENABLED" != "true" ] && exit 0
9
- [ ! -f ".nogrep/_index.json" ] && exit 0
10
- [ -z "$PROMPT" ] && exit 0
11
-
12
- # Only inject for prompts that seem to be about code navigation
13
- if ! echo "$PROMPT" | grep -qiE '(where|how|which|what|find|look|show|implement|fix|add|change|update|refactor)'; then
14
- exit 0
15
- fi
16
-
17
- SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
18
- RESULT=$(node "$SCRIPT_DIR/query.js" --question "$PROMPT" --format summary --limit 3 2>/dev/null)
19
-
20
- if [ -n "$RESULT" ]; then
21
- jq -n \
22
- --arg ctx "nogrep context for your question:\n\n$RESULT\n\nRead these files first before exploring source." \
23
- '{ additionalContext: $ctx }'
24
- fi
25
-
26
- exit 0
@@ -1,21 +0,0 @@
1
- #!/bin/bash
2
- ENABLED=$(cat .claude/settings.json 2>/dev/null | jq -r '.nogrep.enabled // false')
3
- LOCAL_ENABLED=$(cat .claude/settings.local.json 2>/dev/null | jq -r '.nogrep.enabled // empty')
4
- [ -n "$LOCAL_ENABLED" ] && ENABLED="$LOCAL_ENABLED"
5
- [ "$ENABLED" != "true" ] && exit 0
6
-
7
- if [ ! -f ".nogrep/_index.json" ]; then
8
- jq -n '{ additionalContext: "nogrep is enabled but no index found. Run `/nogrep:init` to generate the codebase index before starting work." }'
9
- exit 0
10
- fi
11
-
12
- SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT}/dist"
13
- STALE=$(node "$SCRIPT_DIR/validate.js" --format json 2>/dev/null | jq -r '.stale[]?.file' | head -3)
14
-
15
- if [ -n "$STALE" ]; then
16
- jq -n \
17
- --arg s "$STALE" \
18
- '{ additionalContext: ("nogrep index may be stale. Consider running `/nogrep:update` before starting.\nStale nodes:\n" + $s) }'
19
- fi
20
-
21
- exit 0
@@ -1,8 +0,0 @@
1
- <!-- nogrep -->
2
- ## Code Navigation
3
-
4
- This project uses [nogrep](https://github.com/alirezanasseh/nogrep).
5
- Context files in `.nogrep/` are a navigable index of this codebase.
6
- When you see nogrep results injected into your context, trust them —
7
- read those files before exploring source.
8
- <!-- /nogrep -->