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.
- package/README.md +15 -11
- package/package.json +2 -2
- package/{plugin → plugins/nogrep}/.claude-plugin/plugin.json +0 -1
- package/{plugin → plugins/nogrep}/commands/init.md +5 -6
- package/{plugin → plugins/nogrep}/commands/off.md +1 -1
- package/{plugin → plugins/nogrep}/commands/on.md +1 -1
- package/plugins/nogrep/commands/query.md +23 -0
- package/{plugin → plugins/nogrep}/commands/status.md +1 -1
- package/{plugin → plugins/nogrep}/commands/update.md +3 -3
- package/plugins/nogrep/package.json +1 -0
- package/plugins/nogrep/scripts/chunk-7D4SUZUM.js +38 -0
- package/{plugin/dist/chunk-OJSJ63PH.js → plugins/nogrep/scripts/chunk-ICPV2JWV.js} +1 -1
- package/plugins/nogrep/scripts/chunk-ICPV2JWV.js.map +1 -0
- package/{plugin/dist → plugins/nogrep/scripts}/query.js +13 -10
- package/plugins/nogrep/scripts/query.js.map +1 -0
- package/{plugin/dist → plugins/nogrep/scripts}/settings.js +9 -5
- package/plugins/nogrep/scripts/settings.js.map +1 -0
- package/{plugin/dist → plugins/nogrep/scripts}/signals.js +8 -4
- package/plugins/nogrep/scripts/signals.js.map +1 -0
- package/{plugin/dist → plugins/nogrep/scripts}/trim.js +2 -0
- package/plugins/nogrep/scripts/trim.js.map +1 -0
- package/plugins/nogrep/scripts/types.js +8 -0
- package/plugins/nogrep/scripts/types.js.map +1 -0
- package/plugins/nogrep/scripts/validate.js +9690 -0
- package/plugins/nogrep/scripts/validate.js.map +1 -0
- package/{plugin/dist → plugins/nogrep/scripts}/write.d.ts +1 -2
- package/plugins/nogrep/scripts/write.js +2870 -0
- package/plugins/nogrep/scripts/write.js.map +1 -0
- package/scripts/query.ts +11 -9
- package/scripts/settings.ts +7 -5
- package/scripts/signals.ts +6 -4
- package/scripts/validate.ts +11 -9
- package/scripts/write.ts +8 -50
- package/plugin/commands/query.md +0 -13
- package/plugin/dist/chunk-OJSJ63PH.js.map +0 -1
- package/plugin/dist/query.js.map +0 -1
- package/plugin/dist/settings.js.map +0 -1
- package/plugin/dist/signals.js.map +0 -1
- package/plugin/dist/trim.js.map +0 -1
- package/plugin/dist/types.js +0 -7
- package/plugin/dist/validate.js +0 -143
- package/plugin/dist/validate.js.map +0 -1
- package/plugin/dist/write.js +0 -267
- package/plugin/dist/write.js.map +0 -1
- package/plugin/hooks/hooks.json +0 -53
- package/plugin/hooks/pre-tool-use-glob.sh +0 -40
- package/plugin/hooks/pre-tool-use-grep.sh +0 -35
- package/plugin/hooks/pre-tool-use.sh +0 -37
- package/plugin/hooks/prompt-submit.sh +0 -26
- package/plugin/hooks/session-start.sh +0 -21
- package/plugin/templates/claude-md-patch.md +0 -8
- /package/{plugin/dist/types.js.map → plugins/nogrep/scripts/chunk-7D4SUZUM.js.map} +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/query.d.ts +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/settings.d.ts +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/signals.d.ts +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/trim.d.ts +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/types.d.ts +0 -0
- /package/{plugin/dist → plugins/nogrep/scripts}/validate.d.ts +0 -0
package/plugin/dist/write.js
DELETED
|
@@ -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
|
package/plugin/dist/write.js.map
DELETED
|
@@ -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":[]}
|
package/plugin/hooks/hooks.json
DELETED
|
@@ -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 -->
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|