nogrep 1.0.8 → 1.1.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/package.json +2 -2
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/{dist/chunk-SMUAF6SM.js → plugin/dist/chunk-OJSJ63PH.js} +1 -1
- package/plugin/dist/chunk-OJSJ63PH.js.map +1 -0
- package/{dist → plugin/dist}/query.js +1 -1
- package/plugin/dist/query.js.map +1 -0
- package/plugin/dist/settings.js.map +1 -0
- package/plugin/dist/signals.js.map +1 -0
- package/plugin/dist/trim.js.map +1 -0
- package/plugin/dist/types.js +7 -0
- package/{dist → plugin/dist}/validate.js +1 -1
- package/plugin/dist/validate.js.map +1 -0
- package/plugin/dist/write.js.map +1 -0
- package/dist/chunk-SMUAF6SM.js.map +0 -1
- package/dist/query.js.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/signals.js.map +0 -1
- package/dist/trim.js.map +0 -1
- package/dist/types.js +0 -7
- package/dist/validate.js.map +0 -1
- package/dist/write.js.map +0 -1
- /package/{commands → plugin/commands}/init.md +0 -0
- /package/{commands → plugin/commands}/off.md +0 -0
- /package/{commands → plugin/commands}/on.md +0 -0
- /package/{commands → plugin/commands}/query.md +0 -0
- /package/{commands → plugin/commands}/status.md +0 -0
- /package/{commands → plugin/commands}/update.md +0 -0
- /package/{dist → plugin/dist}/query.d.ts +0 -0
- /package/{dist → plugin/dist}/settings.d.ts +0 -0
- /package/{dist → plugin/dist}/settings.js +0 -0
- /package/{dist → plugin/dist}/signals.d.ts +0 -0
- /package/{dist → plugin/dist}/signals.js +0 -0
- /package/{dist → plugin/dist}/trim.d.ts +0 -0
- /package/{dist → plugin/dist}/trim.js +0 -0
- /package/{dist → plugin/dist}/types.d.ts +0 -0
- /package/{dist → plugin/dist}/types.js.map +0 -0
- /package/{dist → plugin/dist}/validate.d.ts +0 -0
- /package/{dist → plugin/dist}/write.d.ts +0 -0
- /package/{dist → plugin/dist}/write.js +0 -0
- /package/{hooks → plugin/hooks}/hooks.json +0 -0
- /package/{hooks → plugin/hooks}/pre-tool-use-glob.sh +0 -0
- /package/{hooks → plugin/hooks}/pre-tool-use-grep.sh +0 -0
- /package/{hooks → plugin/hooks}/pre-tool-use.sh +0 -0
- /package/{hooks → plugin/hooks}/prompt-submit.sh +0 -0
- /package/{hooks → plugin/hooks}/session-start.sh +0 -0
- /package/{templates → plugin/templates}/claude-md-patch.md +0 -0
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nogrep",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Navigable codebase index for Claude Code — stop grepping, start navigating",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/alirezanasseh/nogrep"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
|
-
"files": ["dist", "commands", "hooks", "
|
|
10
|
+
"files": ["plugin/dist", "plugin/commands", "plugin/hooks", "plugin/templates", "plugin/.claude-plugin", "scripts"],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsup",
|
|
13
13
|
"prepublishOnly": "npm run build",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/types.ts"],"sourcesContent":["// --- Directory / File types ---\n\nexport interface DirectoryNode {\n name: string\n path: string\n type: 'file' | 'directory'\n children?: DirectoryNode[]\n}\n\nexport interface ManifestFile {\n path: string\n type: string\n depth: number\n}\n\nexport interface ChurnEntry {\n path: string\n changes: number\n}\n\nexport interface FileSize {\n path: string\n bytes: number\n}\n\n// --- Signal collection ---\n\nexport interface SignalResult {\n directoryTree: DirectoryNode[]\n extensionMap: Record<string, number>\n manifests: ManifestFile[]\n entryPoints: string[]\n gitChurn: ChurnEntry[]\n largeFiles: FileSize[]\n envFiles: string[]\n testFiles: string[]\n}\n\n// --- Stack detection ---\n\nexport interface StackConventions {\n entryPattern: string\n testPattern: string\n configLocation: string\n}\n\nexport interface DomainCluster {\n name: string\n path: string\n confidence: number\n}\n\nexport interface StackResult {\n primaryLanguage: string\n frameworks: string[]\n architecture: 'monolith' | 'monorepo' | 'multi-repo' | 'microservice' | 'library'\n domainClusters: DomainCluster[]\n conventions: StackConventions\n stackHints: string\n dynamicTaxonomy: { domain: string[]; tech: string[] }\n}\n\n// --- Tags ---\n\nexport interface TagSet {\n domain: string[]\n layer: string[]\n tech: string[]\n concern: string[]\n type: string[]\n}\n\nexport interface Taxonomy {\n static: {\n layer: string[]\n concern: string[]\n type: string[]\n }\n dynamic: {\n domain: string[]\n tech: string[]\n }\n custom: Record<string, string[]>\n}\n\n// --- Relations ---\n\nexport interface Relation {\n id: string\n reason: string\n}\n\nexport interface ExternalDep {\n name: string\n usage: string\n}\n\nexport interface SyncMeta {\n commit: string\n timestamp: string\n srcHash: string\n}\n\n// --- Context nodes ---\n\nexport interface NodeResult {\n id: string\n title: string\n category: 'domain' | 'architecture' | 'flow' | 'entity'\n tags: TagSet\n relatesTo: Relation[]\n inverseRelations: Relation[]\n srcPaths: string[]\n keywords: string[]\n lastSynced: SyncMeta\n purpose: string\n publicSurface: string[]\n doesNotOwn: string[]\n externalDeps: ExternalDep[]\n gotchas: string[]\n}\n\n// --- Index ---\n\nexport interface PathEntry {\n context: string\n tags: string[]\n}\n\nexport interface IndexJson {\n version: string\n generatedAt: string\n commit: string\n stack: Pick<StackResult, 'primaryLanguage' | 'frameworks' | 'architecture'>\n tags: Record<string, string[]>\n keywords: Record<string, string[]>\n paths: Record<string, PathEntry>\n}\n\n// --- Registry ---\n\nexport interface RegistryMapping {\n glob: string\n contextFile: string\n watch: boolean\n}\n\nexport interface RegistryJson {\n mappings: RegistryMapping[]\n}\n\n// --- Query ---\n\nexport interface RankedResult {\n contextFile: string\n score: number\n matchedOn: string[]\n summary: string\n}\n\n// --- Validation ---\n\nexport interface StaleResult {\n file: string\n isStale: boolean\n reason?: string\n}\n\n// --- Settings ---\n\nexport interface NogrepSettings {\n enabled: boolean\n}\n\n// --- Errors ---\n\nexport type NogrepErrorCode = 'NO_INDEX' | 'NO_GIT' | 'IO_ERROR' | 'STALE'\n\nexport class NogrepError extends Error {\n constructor(\n message: string,\n public code: NogrepErrorCode,\n ) {\n super(message)\n }\n}\n"],"mappings":";AAkLO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACE,SACO,MACP;AACA,UAAM,OAAO;AAFN;AAAA,EAGT;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/query.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { join, resolve as resolvePath } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { IndexJson, RankedResult, Taxonomy } from './types.js'\nimport { NogrepError } from './types.js'\n\n// --- Term extraction ---\n\nexport function extractTerms(\n question: string,\n taxonomy: Taxonomy,\n): { tags: string[]; keywords: string[] } {\n const words = question\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, ' ')\n .split(/\\s+/)\n .filter(w => w.length > 1)\n\n const tags: string[] = []\n const keywords: string[] = []\n\n // Collect all taxonomy values for matching\n const tagLookup = new Map<string, string>()\n\n for (const val of taxonomy.static.layer) {\n tagLookup.set(val.toLowerCase(), `layer:${val}`)\n }\n for (const val of taxonomy.static.concern) {\n tagLookup.set(val.toLowerCase(), `concern:${val}`)\n }\n for (const val of taxonomy.static.type) {\n tagLookup.set(val.toLowerCase(), `type:${val}`)\n }\n for (const val of taxonomy.dynamic.domain) {\n tagLookup.set(val.toLowerCase(), `domain:${val}`)\n }\n for (const val of taxonomy.dynamic.tech) {\n tagLookup.set(val.toLowerCase(), `tech:${val}`)\n }\n for (const [cat, values] of Object.entries(taxonomy.custom)) {\n for (const val of values) {\n tagLookup.set(val.toLowerCase(), `${cat}:${val}`)\n }\n }\n\n // Stop words to skip as keywords\n const stopWords = new Set([\n 'the', 'is', 'at', 'in', 'of', 'on', 'to', 'a', 'an', 'and', 'or',\n 'for', 'it', 'do', 'does', 'how', 'what', 'where', 'which', 'when',\n 'who', 'why', 'this', 'that', 'with', 'from', 'by', 'be', 'as',\n 'are', 'was', 'were', 'been', 'has', 'have', 'had', 'not', 'but',\n 'if', 'my', 'our', 'its', 'can', 'will', 'should', 'would', 'could',\n 'about', 'after', 'work', 'works', 'use', 'uses', 'used',\n ])\n\n for (const word of words) {\n const tag = tagLookup.get(word)\n if (tag && !tags.includes(tag)) {\n tags.push(tag)\n }\n\n // Also check hyphenated compound matches (e.g. \"error-handling\")\n if (!tag && !stopWords.has(word)) {\n keywords.push(word)\n }\n }\n\n // Check for multi-word tag matches (e.g. \"error handling\" → \"error-handling\")\n const questionLower = question.toLowerCase()\n for (const [val, tag] of tagLookup.entries()) {\n if (val.includes('-')) {\n const spacedVersion = val.replace(/-/g, ' ')\n if (questionLower.includes(spacedVersion) && !tags.includes(tag)) {\n tags.push(tag)\n }\n if (questionLower.includes(val) && !tags.includes(tag)) {\n tags.push(tag)\n }\n }\n }\n\n return { tags, keywords }\n}\n\n// --- Resolution ---\n\nexport function resolveQuery(\n terms: { tags: string[]; keywords: string[] },\n index: IndexJson,\n limit = 5,\n): RankedResult[] {\n const scoreMap = new Map<string, { score: number; matchedOn: string[] }>()\n\n function addMatch(contextFile: string, score: number, matchLabel: string): void {\n const existing = scoreMap.get(contextFile)\n if (existing) {\n existing.score += score\n existing.matchedOn.push(matchLabel)\n } else {\n scoreMap.set(contextFile, { score, matchedOn: [matchLabel] })\n }\n }\n\n // Tag matching: +2 per match\n for (const tag of terms.tags) {\n const files = index.tags[tag]\n if (files) {\n for (const file of files) {\n addMatch(file, 2, `tag:${tag}`)\n }\n }\n }\n\n // Keyword matching: +1 per match\n for (const kw of terms.keywords) {\n const kwLower = kw.toLowerCase()\n\n // Direct keyword lookup\n const files = index.keywords[kwLower]\n if (files) {\n for (const file of files) {\n addMatch(file, 1, `keyword:${kwLower}`)\n }\n }\n\n // Also search all index keywords for partial matches\n for (const [indexKw, kwFiles] of Object.entries(index.keywords)) {\n if (indexKw === kwLower) continue // Already handled\n if (indexKw.includes(kwLower) || kwLower.includes(indexKw)) {\n for (const file of kwFiles) {\n addMatch(file, 1, `keyword:${indexKw}`)\n }\n }\n }\n }\n\n // Sort by score descending, then alphabetically for ties\n const results: RankedResult[] = [...scoreMap.entries()]\n .sort((a, b) => b[1].score - a[1].score || a[0].localeCompare(b[0]))\n .slice(0, limit)\n .map(([contextFile, { score, matchedOn }]) => ({\n contextFile,\n score,\n matchedOn: [...new Set(matchedOn)],\n summary: `Matched: ${[...new Set(matchedOn)].join(', ')}`,\n }))\n\n return results\n}\n\n// --- Index + taxonomy loading ---\n\nasync function loadIndex(projectRoot: string): Promise<IndexJson> {\n const indexPath = join(projectRoot, '.nogrep', '_index.json')\n try {\n const content = await readFile(indexPath, 'utf-8')\n return JSON.parse(content) as IndexJson\n } catch {\n throw new NogrepError(\n 'No .nogrep/_index.json found. Run /nogrep:init first.',\n 'NO_INDEX',\n )\n }\n}\n\nasync function loadTaxonomy(projectRoot: string): Promise<Taxonomy> {\n const taxonomyPath = join(projectRoot, '.nogrep', '_taxonomy.json')\n try {\n const content = await readFile(taxonomyPath, 'utf-8')\n return JSON.parse(content) as Taxonomy\n } catch {\n // Return default taxonomy if file doesn't exist\n return {\n static: {\n layer: ['presentation', 'business', 'data', 'infrastructure', 'cross-cutting'],\n concern: ['security', 'performance', 'caching', 'validation', 'error-handling', 'idempotency', 'observability'],\n type: ['module', 'flow', 'entity', 'integration', 'config', 'ui', 'test'],\n },\n dynamic: { domain: [], tech: [] },\n custom: {},\n }\n }\n}\n\nfunction buildTaxonomyFromIndex(index: IndexJson, baseTaxonomy: Taxonomy): Taxonomy {\n // Extract dynamic domain and tech values from the index tags\n const domains = new Set<string>(baseTaxonomy.dynamic.domain)\n const techs = new Set<string>(baseTaxonomy.dynamic.tech)\n\n for (const tagKey of Object.keys(index.tags)) {\n const [category, value] = tagKey.split(':')\n if (!category || !value) continue\n if (category === 'domain') domains.add(value)\n if (category === 'tech') techs.add(value)\n }\n\n return {\n ...baseTaxonomy,\n dynamic: {\n domain: [...domains],\n tech: [...techs],\n },\n }\n}\n\n// --- Formatting ---\n\nfunction formatPaths(results: RankedResult[]): string {\n return results.map(r => r.contextFile).join('\\n')\n}\n\nfunction formatJson(results: RankedResult[]): string {\n return JSON.stringify(results, null, 2)\n}\n\nfunction formatSummary(results: RankedResult[]): string {\n if (results.length === 0) return 'No matching context files found.'\n return results\n .map(r => `- ${r.contextFile} (score: ${r.score}) — ${r.summary}`)\n .join('\\n')\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n tags: { type: 'string' },\n keywords: { type: 'string' },\n question: { type: 'string' },\n format: { type: 'string', default: 'json' },\n limit: { type: 'string', default: '5' },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = resolvePath(values.root ?? process.cwd())\n const limit = parseInt(values.limit ?? '5', 10)\n const format = values.format ?? 'json'\n\n const index = await loadIndex(root)\n const baseTaxonomy = await loadTaxonomy(root)\n const taxonomy = buildTaxonomyFromIndex(index, baseTaxonomy)\n\n let terms: { tags: string[]; keywords: string[] }\n\n if (values.question) {\n terms = extractTerms(values.question, taxonomy)\n } else if (values.tags || values.keywords) {\n const tags = values.tags\n ? values.tags.split(',').map(t => t.trim()).filter(Boolean)\n : []\n const keywords = values.keywords\n ? values.keywords.split(',').map(k => k.trim()).filter(Boolean)\n : []\n terms = { tags, keywords }\n } else {\n process.stderr.write(\n JSON.stringify({ error: 'Usage: node query.js --tags <tags> | --keywords <words> | --question <text> [--format paths|json|summary] [--limit N]' }) + '\\n',\n )\n process.exitCode = 1\n return\n }\n\n const results = resolveQuery(terms, index, limit)\n\n switch (format) {\n case 'paths':\n process.stdout.write(formatPaths(results) + '\\n')\n break\n case 'summary':\n process.stdout.write(formatSummary(results) + '\\n')\n break\n case 'json':\n default:\n process.stdout.write(formatJson(results) + '\\n')\n break\n }\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof NogrepError) {\n process.stderr.write(JSON.stringify({ error: err.message, code: err.code }) + '\\n')\n } else {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(JSON.stringify({ error: message }) + '\\n')\n }\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/settings.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { NogrepSettings } from './types.js'\n\nconst SETTINGS_FILE = '.claude/settings.json'\nconst SETTINGS_LOCAL_FILE = '.claude/settings.local.json'\n\ninterface SettingsJson {\n nogrep?: Partial<NogrepSettings>\n [key: string]: unknown\n}\n\nasync function readJsonFile(path: string): Promise<SettingsJson> {\n try {\n const content = await readFile(path, 'utf-8')\n return JSON.parse(content) as SettingsJson\n } catch {\n return {}\n }\n}\n\nasync function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\nexport async function readSettings(projectRoot: string): Promise<NogrepSettings> {\n const sharedPath = join(projectRoot, SETTINGS_FILE)\n const localPath = join(projectRoot, SETTINGS_LOCAL_FILE)\n\n const shared = await readJsonFile(sharedPath)\n const local = await readJsonFile(localPath)\n\n const enabled =\n local.nogrep?.enabled ?? shared.nogrep?.enabled ?? false\n\n return { enabled }\n}\n\nexport async function writeSettings(\n projectRoot: string,\n settings: Partial<NogrepSettings>,\n local?: boolean,\n): Promise<void> {\n const filePath = join(\n projectRoot,\n local ? SETTINGS_LOCAL_FILE : SETTINGS_FILE,\n )\n\n await ensureDir(join(projectRoot, '.claude'))\n\n const existing = await readJsonFile(filePath)\n existing.nogrep = { ...existing.nogrep, ...settings }\n\n await writeFile(filePath, JSON.stringify(existing, null, 2) + '\\n', 'utf-8')\n}\n\n// CLI interface\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n set: { type: 'string' },\n get: { type: 'boolean', default: false },\n local: { type: 'boolean', default: false },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = values.root ?? process.cwd()\n\n if (values.get) {\n const settings = await readSettings(root)\n process.stdout.write(JSON.stringify(settings, null, 2) + '\\n')\n return\n }\n\n if (values.set) {\n const [key, value] = values.set.split('=')\n if (key === 'enabled') {\n const enabled = value === 'true'\n await writeSettings(root, { enabled }, values.local)\n } else {\n process.stderr.write(JSON.stringify({ error: `Unknown setting: ${key}` }) + '\\n')\n process.exitCode = 1\n }\n return\n }\n\n process.stderr.write(JSON.stringify({ error: 'Usage: node settings.js --set enabled=true [--local] | --get' }) + '\\n')\n process.exitCode = 1\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":";AAAA,SAAS,UAAU,WAAW,aAAa;AAC3C,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAG1B,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAO5B,eAAe,aAAa,MAAqC;AAC/D,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,UAAU,KAA4B;AACnD,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACtC;AAEA,eAAsB,aAAa,aAA8C;AAC/E,QAAM,aAAa,KAAK,aAAa,aAAa;AAClD,QAAM,YAAY,KAAK,aAAa,mBAAmB;AAEvD,QAAM,SAAS,MAAM,aAAa,UAAU;AAC5C,QAAM,QAAQ,MAAM,aAAa,SAAS;AAE1C,QAAM,UACJ,MAAM,QAAQ,WAAW,OAAO,QAAQ,WAAW;AAErD,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,cACpB,aACA,UACA,OACe;AACf,QAAM,WAAW;AAAA,IACf;AAAA,IACA,QAAQ,sBAAsB;AAAA,EAChC;AAEA,QAAM,UAAU,KAAK,aAAa,SAAS,CAAC;AAE5C,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,WAAS,SAAS,EAAE,GAAG,SAAS,QAAQ,GAAG,SAAS;AAEpD,QAAM,UAAU,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO;AAC7E;AAGA,eAAe,OAAsB;AACnC,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,SAAS;AAAA,MACP,KAAK,EAAE,MAAM,SAAS;AAAA,MACtB,KAAK,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACvC,OAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACzC,MAAM,EAAE,MAAM,UAAU,SAAS,QAAQ,IAAI,EAAE;AAAA,IACjD;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,OAAO,OAAO,QAAQ,QAAQ,IAAI;AAExC,MAAI,OAAO,KAAK;AACd,UAAM,WAAW,MAAM,aAAa,IAAI;AACxC,YAAQ,OAAO,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAC7D;AAAA,EACF;AAEA,MAAI,OAAO,KAAK;AACd,UAAM,CAAC,KAAK,KAAK,IAAI,OAAO,IAAI,MAAM,GAAG;AACzC,QAAI,QAAQ,WAAW;AACrB,YAAM,UAAU,UAAU;AAC1B,YAAM,cAAc,MAAM,EAAE,QAAQ,GAAG,OAAO,KAAK;AAAA,IACrD,OAAO;AACL,cAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,oBAAoB,GAAG,GAAG,CAAC,IAAI,IAAI;AAChF,cAAQ,WAAW;AAAA,IACrB;AACA;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,+DAA+D,CAAC,IAAI,IAAI;AACrH,UAAQ,WAAW;AACrB;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC,IAAI,IAAI;AAC9D,UAAQ,WAAW;AACrB,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/signals.ts"],"sourcesContent":["import { readdir, stat, readFile } from 'fs/promises'\nimport { join, extname, relative, resolve } from 'path'\nimport { execFile } from 'child_process'\nimport { promisify } from 'util'\nimport type { SignalResult, DirectoryNode, ManifestFile, ChurnEntry, FileSize } from './types.js'\n\nconst execFileAsync = promisify(execFile)\n\nconst SKIP_DIRS = new Set([\n 'node_modules', 'dist', 'build', '.git', 'coverage',\n '.next', '.nuxt', '__pycache__', '.venv', 'venv',\n '.idea', '.vscode', '.nogrep',\n])\n\nconst MANIFEST_NAMES: Record<string, string> = {\n 'package.json': 'npm',\n 'requirements.txt': 'pip',\n 'pom.xml': 'maven',\n 'go.mod': 'go',\n 'Podfile': 'cocoapods',\n 'Cargo.toml': 'cargo',\n 'pubspec.yaml': 'flutter',\n 'composer.json': 'composer',\n}\n\nconst ENTRY_NAMES = new Set(['main', 'index', 'app', 'server'])\n\nconst TEST_PATTERNS = [\n /\\.test\\.\\w+$/,\n /\\.spec\\.\\w+$/,\n /_test\\.\\w+$/,\n /^test_.*\\.py$/,\n]\n\ninterface CollectOptions {\n exclude?: string[]\n maxDepth?: number\n}\n\nexport async function collectSignals(\n root: string,\n options: CollectOptions = {},\n): Promise<SignalResult> {\n const absRoot = resolve(root)\n const maxDepth = options.maxDepth ?? 4\n const extraSkip = new Set(options.exclude ?? [])\n\n const allFiles: { path: string; bytes: number }[] = []\n const extensionMap: Record<string, number> = {}\n const manifests: ManifestFile[] = []\n const entryPoints: string[] = []\n const envFiles: string[] = []\n const testFiles: string[] = []\n\n const directoryTree = await walkDirectory(absRoot, absRoot, 0, maxDepth, extraSkip, {\n allFiles,\n extensionMap,\n manifests,\n entryPoints,\n envFiles,\n testFiles,\n })\n\n const gitChurn = await collectGitChurn(absRoot)\n\n const largeFiles = allFiles\n .sort((a, b) => b.bytes - a.bytes)\n .slice(0, 20)\n .map(f => ({ path: f.path, bytes: f.bytes }))\n\n return {\n directoryTree,\n extensionMap,\n manifests,\n entryPoints,\n gitChurn,\n largeFiles,\n envFiles,\n testFiles,\n }\n}\n\ninterface Collectors {\n allFiles: { path: string; bytes: number }[]\n extensionMap: Record<string, number>\n manifests: ManifestFile[]\n entryPoints: string[]\n envFiles: string[]\n testFiles: string[]\n}\n\nasync function walkDirectory(\n dir: string,\n root: string,\n depth: number,\n maxDepth: number,\n extraSkip: Set<string>,\n collectors: Collectors,\n): Promise<DirectoryNode[]> {\n if (depth > maxDepth) return []\n\n let entries\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n return []\n }\n\n const nodes: DirectoryNode[] = []\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n const relPath = relative(root, fullPath)\n\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name) || extraSkip.has(entry.name)) continue\n\n const children = await walkDirectory(fullPath, root, depth + 1, maxDepth, extraSkip, collectors)\n nodes.push({ name: entry.name, path: relPath, type: 'directory', children })\n } else if (entry.isFile()) {\n nodes.push({ name: entry.name, path: relPath, type: 'file' })\n\n let fileBytes = 0\n try {\n const s = await stat(fullPath)\n fileBytes = s.size\n } catch {\n // skip\n }\n\n collectors.allFiles.push({ path: relPath, bytes: fileBytes })\n\n const ext = extname(entry.name)\n if (ext) {\n collectors.extensionMap[ext] = (collectors.extensionMap[ext] ?? 0) + 1\n }\n\n // Manifest check\n if (entry.name in MANIFEST_NAMES) {\n collectors.manifests.push({\n path: relPath,\n type: MANIFEST_NAMES[entry.name]!,\n depth,\n })\n }\n\n // Entry point check — root or src/ level\n if (depth <= 1 || (depth === 2 && dir.endsWith('/src'))) {\n const nameWithoutExt = entry.name.replace(/\\.\\w+$/, '')\n if (ENTRY_NAMES.has(nameWithoutExt)) {\n collectors.entryPoints.push(relPath)\n }\n }\n\n // Env files\n if (entry.name.startsWith('.env')) {\n collectors.envFiles.push(relPath)\n }\n\n // Config directories are handled at directory level\n // But we also detect config files at root\n if (depth === 0 && entry.name.match(/^config\\./)) {\n collectors.envFiles.push(relPath)\n }\n\n // Test files\n const fileName = entry.name\n if (TEST_PATTERNS.some(p => p.test(fileName))) {\n collectors.testFiles.push(relPath)\n }\n }\n }\n\n // Check if this directory is a config directory\n const dirName = dir.split('/').pop()\n if (dirName === 'config' && depth <= 2) {\n collectors.envFiles.push(relative(root, dir))\n }\n\n return nodes\n}\n\nasync function collectGitChurn(root: string): Promise<ChurnEntry[]> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['log', '--stat', '--oneline', '-50', '--pretty=format:'],\n { cwd: root, maxBuffer: 1024 * 1024 },\n )\n\n const changeCounts: Record<string, number> = {}\n\n for (const line of stdout.split('\\n')) {\n // Match lines like: src/billing/service.ts | 42 +++---\n const match = line.match(/^\\s+(.+?)\\s+\\|\\s+(\\d+)/)\n if (match) {\n const filePath = match[1]!.trim()\n const changes = parseInt(match[2]!, 10)\n changeCounts[filePath] = (changeCounts[filePath] ?? 0) + changes\n }\n }\n\n return Object.entries(changeCounts)\n .sort(([, a], [, b]) => b - a)\n .slice(0, 20)\n .map(([path, changes]) => ({ path, changes }))\n } catch {\n // No git or git log fails — return empty\n return []\n }\n}\n\n// --- CLI interface ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n let root = '.'\n const exclude: string[] = []\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--root' && args[i + 1]) {\n root = args[i + 1]!\n i++\n } else if (args[i] === '--exclude' && args[i + 1]) {\n exclude.push(...args[i + 1]!.split(','))\n i++\n }\n }\n\n const result = await collectSignals(root, { exclude })\n process.stdout.write(JSON.stringify(result, null, 2))\n}\n\nmain().catch(err => {\n process.stderr.write(JSON.stringify({ error: String(err) }))\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/trim.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { resolve, extname, basename } from 'path'\n\nconst MAX_CLUSTER_LINES = 300\n\ninterface TrimOptions {\n maxLines?: number\n}\n\n// Language-agnostic regex patterns for stripping function/method bodies\n// Strategy: find opening braces after signatures, track depth, remove body content\n\nfunction trimTypeScript(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let braceDepth = 0\n let inBody = false\n let bodyStartDepth = 0\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Always keep: empty lines at top level, imports, type/interface, decorators, exports of types\n if (braceDepth === 0 || !inBody) {\n if (\n trimmed === '' ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('export type ') ||\n trimmed.startsWith('export interface ') ||\n trimmed.startsWith('export enum ') ||\n trimmed.startsWith('export const ') ||\n trimmed.startsWith('type ') ||\n trimmed.startsWith('interface ') ||\n trimmed.startsWith('enum ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('//') ||\n trimmed.startsWith('/*') ||\n trimmed.startsWith('*') ||\n trimmed.startsWith('declare ')\n ) {\n result.push(line)\n // Count braces even in kept lines\n braceDepth += countChar(trimmed, '{') - countChar(trimmed, '}')\n continue\n }\n }\n\n const openBraces = countChar(trimmed, '{')\n const closeBraces = countChar(trimmed, '}')\n\n if (!inBody) {\n // Detect function/method signature — line with opening brace\n if (isSignatureLine(trimmed) && openBraces > closeBraces) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n inBody = true\n bodyStartDepth = braceDepth\n continue\n }\n\n // Class/interface declaration — keep but don't treat as body\n if (isClassOrInterfaceLine(trimmed)) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n continue\n }\n\n // Keep the line (top-level statement, property declaration, etc.)\n result.push(line)\n braceDepth += openBraces - closeBraces\n } else {\n // Inside a function body — skip lines\n braceDepth += openBraces - closeBraces\n\n // Check if we've closed back to where the body started\n if (braceDepth < bodyStartDepth) {\n // Add closing brace\n result.push(line)\n inBody = false\n }\n }\n }\n\n return result.join('\\n')\n}\n\nfunction trimPython(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let skipIndent = -1\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n const trimmed = line.trim()\n const indent = line.length - line.trimStart().length\n\n // If we're skipping a body and this line is still indented deeper, skip it\n if (skipIndent >= 0) {\n if (trimmed === '' || indent > skipIndent) {\n continue\n }\n // We've exited the body\n skipIndent = -1\n }\n\n // Always keep: comments, imports, class defs, decorators, type hints, module-level assignments\n if (\n trimmed === '' ||\n trimmed.startsWith('#') ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('from ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('class ') ||\n /^[A-Z_][A-Z_0-9]*\\s*=/.test(trimmed)\n ) {\n result.push(line)\n continue\n }\n\n // Function/method definition — keep signature, skip body\n if (trimmed.startsWith('def ') || trimmed.startsWith('async def ')) {\n result.push(line)\n // If the next non-empty line has docstring, keep it\n const docIdx = findDocstring(lines, i + 1, indent)\n if (docIdx > i) {\n for (let j = i + 1; j <= docIdx; j++) {\n result.push(lines[j]!)\n }\n }\n skipIndent = indent\n continue\n }\n\n // Keep everything else at module/class level\n result.push(line)\n }\n\n return result.join('\\n')\n}\n\nfunction trimJava(content: string): string {\n // Java/Kotlin — very similar to TypeScript brace-matching\n const lines = content.split('\\n')\n const result: string[] = []\n let braceDepth = 0\n let inBody = false\n let bodyStartDepth = 0\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (braceDepth === 0 || !inBody) {\n if (\n trimmed === '' ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('package ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('//') ||\n trimmed.startsWith('/*') ||\n trimmed.startsWith('*') ||\n trimmed.startsWith('public interface ') ||\n trimmed.startsWith('interface ') ||\n trimmed.startsWith('public enum ') ||\n trimmed.startsWith('enum ')\n ) {\n result.push(line)\n braceDepth += countChar(trimmed, '{') - countChar(trimmed, '}')\n continue\n }\n }\n\n const openBraces = countChar(trimmed, '{')\n const closeBraces = countChar(trimmed, '}')\n\n if (!inBody) {\n if (isJavaMethodSignature(trimmed) && openBraces > closeBraces) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n inBody = true\n bodyStartDepth = braceDepth\n continue\n }\n\n if (isJavaClassLine(trimmed)) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n continue\n }\n\n result.push(line)\n braceDepth += openBraces - closeBraces\n } else {\n braceDepth += openBraces - closeBraces\n if (braceDepth < bodyStartDepth) {\n result.push(line)\n inBody = false\n }\n }\n }\n\n return result.join('\\n')\n}\n\nfunction trimGeneric(content: string): string {\n // For unknown languages, just return as-is (truncation handles size)\n return content\n}\n\n// --- Helpers ---\n\nfunction countChar(s: string, ch: string): number {\n let count = 0\n let inString = false\n let stringChar = ''\n for (let i = 0; i < s.length; i++) {\n const c = s[i]!\n if (inString) {\n if (c === stringChar && s[i - 1] !== '\\\\') inString = false\n } else if (c === '\"' || c === \"'\" || c === '`') {\n inString = true\n stringChar = c\n } else if (c === ch) {\n count++\n }\n }\n return count\n}\n\nfunction isSignatureLine(trimmed: string): boolean {\n return /^(export\\s+)?(async\\s+)?function\\s/.test(trimmed) ||\n /^(public|private|protected|static|async|get|set|\\*)\\s/.test(trimmed) ||\n /^(readonly\\s+)?[a-zA-Z_$][a-zA-Z0-9_$]*\\s*\\(/.test(trimmed) ||\n /^(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?\\(/.test(trimmed) ||\n /^(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?function/.test(trimmed) ||\n // Arrow function assigned at class level\n /^[a-zA-Z_$][a-zA-Z0-9_$]*\\s*=\\s*(async\\s+)?\\(/.test(trimmed)\n}\n\nfunction isClassOrInterfaceLine(trimmed: string): boolean {\n return /^(export\\s+)?(abstract\\s+)?(class|interface|enum)\\s/.test(trimmed) ||\n /^(export\\s+)?namespace\\s/.test(trimmed)\n}\n\nfunction isJavaMethodSignature(trimmed: string): boolean {\n return /^(public|private|protected|static|final|abstract|synchronized|native)\\s/.test(trimmed) &&\n /\\(/.test(trimmed)\n}\n\nfunction isJavaClassLine(trimmed: string): boolean {\n return /^(public|private|protected)?\\s*(abstract\\s+)?(class|interface|enum)\\s/.test(trimmed)\n}\n\nfunction findDocstring(lines: string[], startIdx: number, defIndent: number): number {\n // Find Python docstring (triple-quoted) after a def\n for (let i = startIdx; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (trimmed === '') continue\n if (trimmed.startsWith('\"\"\"') || trimmed.startsWith(\"'''\")) {\n const quote = trimmed.slice(0, 3)\n // Single-line docstring\n if (trimmed.length > 3 && trimmed.endsWith(quote)) return i\n // Multi-line docstring — find closing\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j]!.trim().endsWith(quote)) return j\n }\n return i\n }\n // First non-empty line after def is not a docstring\n return startIdx - 1\n }\n return startIdx - 1\n}\n\nfunction getTrimmer(filePath: string): (content: string) => string {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.ts':\n case '.tsx':\n case '.js':\n case '.jsx':\n case '.mjs':\n case '.cjs':\n return trimTypeScript\n case '.py':\n return trimPython\n case '.java':\n case '.kt':\n case '.kts':\n case '.scala':\n case '.groovy':\n return trimJava\n case '.go':\n case '.rs':\n case '.c':\n case '.cpp':\n case '.h':\n case '.hpp':\n case '.cs':\n case '.swift':\n case '.dart':\n return trimJava // brace-based languages use same strategy\n default:\n return trimGeneric\n }\n}\n\nexport async function trimCluster(paths: string[], projectRoot: string): Promise<string> {\n const results: Array<{ path: string; content: string; lines: number }> = []\n\n for (const filePath of paths) {\n const absPath = resolve(projectRoot, filePath)\n try {\n const raw = await readFile(absPath, 'utf-8')\n const trimmer = getTrimmer(filePath)\n const trimmed = trimmer(raw)\n results.push({\n path: filePath,\n content: trimmed,\n lines: trimmed.split('\\n').length,\n })\n } catch {\n // Skip files that can't be read\n if (process.env['NOGREP_DEBUG'] === '1') {\n process.stderr.write(`[nogrep] Could not read: ${absPath}\\n`)\n }\n }\n }\n\n // Sort by line count descending — truncate least important (largest) files first\n results.sort((a, b) => a.lines - b.lines)\n\n const output: string[] = []\n let totalLines = 0\n const maxLines = MAX_CLUSTER_LINES\n\n for (const file of results) {\n const header = `// === ${file.path} ===`\n const fileLines = file.content.split('\\n')\n const available = maxLines - totalLines - 2 // header + separator\n\n if (available <= 0) break\n\n output.push(header)\n if (fileLines.length <= available) {\n output.push(file.content)\n } else {\n output.push(fileLines.slice(0, available).join('\\n'))\n output.push(`// ... truncated (${fileLines.length - available} more lines)`)\n }\n output.push('')\n\n totalLines += Math.min(fileLines.length, available) + 2\n }\n\n return output.join('\\n')\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n\n if (args.length === 0) {\n process.stderr.write('Usage: node trim.js <path1> <path2> ...\\n')\n process.exit(1)\n }\n\n const projectRoot = process.cwd()\n const result = await trimCluster(args, projectRoot)\n process.stdout.write(result)\n}\n\nconst isDirectRun = process.argv[1]?.endsWith('trim.js') || process.argv[1]?.endsWith('trim.ts')\nif (isDirectRun) {\n main().catch((err: unknown) => {\n process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n })\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../scripts/validate.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { join, resolve as resolvePath } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { parseArgs } from 'node:util'\nimport { glob } from 'glob'\nimport matter from 'gray-matter'\nimport type { StaleResult } from './types.js'\nimport { NogrepError } from './types.js'\n\n// --- Freshness check ---\n\nexport async function checkFreshness(\n nodeFile: string,\n projectRoot: string,\n): Promise<StaleResult> {\n let content: string\n try {\n content = await readFile(join(projectRoot, nodeFile), 'utf-8')\n } catch {\n return { file: nodeFile, isStale: true, reason: 'context file not found' }\n }\n\n const parsed = matter(content)\n const srcPaths: string[] = parsed.data.src_paths ?? []\n const lastSynced = parsed.data.last_synced as\n | { src_hash?: string; commit?: string; timestamp?: string }\n | undefined\n\n if (!lastSynced?.src_hash) {\n return { file: nodeFile, isStale: true, reason: 'no src_hash in frontmatter' }\n }\n\n if (srcPaths.length === 0) {\n return { file: nodeFile, isStale: false }\n }\n\n // Glob all matching source files\n const allFiles: string[] = []\n for (const pattern of srcPaths) {\n const matches = await glob(pattern, {\n cwd: projectRoot,\n nodir: true,\n ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'coverage/**'],\n })\n allFiles.push(...matches)\n }\n\n allFiles.sort()\n\n if (allFiles.length === 0) {\n return { file: nodeFile, isStale: true, reason: 'no source files match src_paths' }\n }\n\n // Compute SHA256 of all file contents concatenated\n const hash = createHash('sha256')\n for (const file of allFiles) {\n try {\n const fileContent = await readFile(join(projectRoot, file))\n hash.update(fileContent)\n } catch {\n // File unreadable — skip\n }\n }\n const currentHash = `sha256:${hash.digest('hex').slice(0, 12)}`\n\n if (currentHash !== lastSynced.src_hash) {\n return {\n file: nodeFile,\n isStale: true,\n reason: `hash mismatch: expected ${lastSynced.src_hash}, got ${currentHash}`,\n }\n }\n\n return { file: nodeFile, isStale: false }\n}\n\n// --- Discover all context nodes ---\n\nasync function discoverNodes(projectRoot: string): Promise<string[]> {\n const nogrepDir = join(projectRoot, '.nogrep')\n const patterns = [\n 'domains/*.md',\n 'architecture/*.md',\n 'flows/*.md',\n 'entities/*.md',\n ]\n\n const files: string[] = []\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: nogrepDir, nodir: true })\n files.push(...matches.map(m => `.nogrep/${m}`))\n }\n\n return files.sort()\n}\n\n// --- Validate all nodes ---\n\nexport async function validateAll(\n projectRoot: string,\n): Promise<{ total: number; fresh: StaleResult[]; stale: StaleResult[] }> {\n const indexPath = join(projectRoot, '.nogrep', '_index.json')\n try {\n await readFile(indexPath, 'utf-8')\n } catch {\n throw new NogrepError(\n 'No .nogrep/_index.json found. Run /nogrep:init first.',\n 'NO_INDEX',\n )\n }\n\n const nodeFiles = await discoverNodes(projectRoot)\n const results = await Promise.all(\n nodeFiles.map(f => checkFreshness(f, projectRoot)),\n )\n\n const fresh = results.filter(r => !r.isStale)\n const stale = results.filter(r => r.isStale)\n\n return { total: results.length, fresh, stale }\n}\n\n// --- Formatting ---\n\nfunction formatText(result: { total: number; fresh: StaleResult[]; stale: StaleResult[] }): string {\n const lines: string[] = []\n lines.push(`nogrep index: ${result.total} nodes`)\n lines.push(` Fresh: ${result.fresh.length}`)\n lines.push(` Stale: ${result.stale.length}`)\n\n if (result.stale.length > 0) {\n lines.push('')\n lines.push('Stale nodes:')\n for (const s of result.stale) {\n lines.push(` - ${s.file}: ${s.reason}`)\n }\n }\n\n return lines.join('\\n')\n}\n\nfunction formatJson(result: { total: number; fresh: StaleResult[]; stale: StaleResult[] }): string {\n return JSON.stringify(result, null, 2)\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n format: { type: 'string', default: 'text' },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = resolvePath(values.root ?? process.cwd())\n const format = values.format ?? 'text'\n\n const result = await validateAll(root)\n\n switch (format) {\n case 'json':\n process.stdout.write(formatJson(result) + '\\n')\n break\n case 'text':\n default:\n process.stdout.write(formatText(result) + '\\n')\n break\n }\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof NogrepError) {\n process.stderr.write(JSON.stringify({ error: err.message, code: err.code }) + '\\n')\n } else {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(JSON.stringify({ error: message }) + '\\n')\n }\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/types.ts"],"sourcesContent":["// --- Directory / File types ---\n\nexport interface DirectoryNode {\n name: string\n path: string\n type: 'file' | 'directory'\n children?: DirectoryNode[]\n}\n\nexport interface ManifestFile {\n path: string\n type: string\n depth: number\n}\n\nexport interface ChurnEntry {\n path: string\n changes: number\n}\n\nexport interface FileSize {\n path: string\n bytes: number\n}\n\n// --- Signal collection ---\n\nexport interface SignalResult {\n directoryTree: DirectoryNode[]\n extensionMap: Record<string, number>\n manifests: ManifestFile[]\n entryPoints: string[]\n gitChurn: ChurnEntry[]\n largeFiles: FileSize[]\n envFiles: string[]\n testFiles: string[]\n}\n\n// --- Stack detection ---\n\nexport interface StackConventions {\n entryPattern: string\n testPattern: string\n configLocation: string\n}\n\nexport interface DomainCluster {\n name: string\n path: string\n confidence: number\n}\n\nexport interface StackResult {\n primaryLanguage: string\n frameworks: string[]\n architecture: 'monolith' | 'monorepo' | 'multi-repo' | 'microservice' | 'library'\n domainClusters: DomainCluster[]\n conventions: StackConventions\n stackHints: string\n dynamicTaxonomy: { domain: string[]; tech: string[] }\n}\n\n// --- Tags ---\n\nexport interface TagSet {\n domain: string[]\n layer: string[]\n tech: string[]\n concern: string[]\n type: string[]\n}\n\nexport interface Taxonomy {\n static: {\n layer: string[]\n concern: string[]\n type: string[]\n }\n dynamic: {\n domain: string[]\n tech: string[]\n }\n custom: Record<string, string[]>\n}\n\n// --- Relations ---\n\nexport interface Relation {\n id: string\n reason: string\n}\n\nexport interface ExternalDep {\n name: string\n usage: string\n}\n\nexport interface SyncMeta {\n commit: string\n timestamp: string\n srcHash: string\n}\n\n// --- Context nodes ---\n\nexport interface NodeResult {\n id: string\n title: string\n category: 'domain' | 'architecture' | 'flow' | 'entity'\n tags: TagSet\n relatesTo: Relation[]\n inverseRelations: Relation[]\n srcPaths: string[]\n keywords: string[]\n lastSynced: SyncMeta\n purpose: string\n publicSurface: string[]\n doesNotOwn: string[]\n externalDeps: ExternalDep[]\n gotchas: string[]\n}\n\n// --- Index ---\n\nexport interface PathEntry {\n context: string\n tags: string[]\n}\n\nexport interface IndexJson {\n version: string\n generatedAt: string\n commit: string\n stack: Pick<StackResult, 'primaryLanguage' | 'frameworks' | 'architecture'>\n tags: Record<string, string[]>\n keywords: Record<string, string[]>\n paths: Record<string, PathEntry>\n}\n\n// --- Registry ---\n\nexport interface RegistryMapping {\n glob: string\n contextFile: string\n watch: boolean\n}\n\nexport interface RegistryJson {\n mappings: RegistryMapping[]\n}\n\n// --- Query ---\n\nexport interface RankedResult {\n contextFile: string\n score: number\n matchedOn: string[]\n summary: string\n}\n\n// --- Validation ---\n\nexport interface StaleResult {\n file: string\n isStale: boolean\n reason?: string\n}\n\n// --- Settings ---\n\nexport interface NogrepSettings {\n enabled: boolean\n}\n\n// --- Errors ---\n\nexport type NogrepErrorCode = 'NO_INDEX' | 'NO_GIT' | 'IO_ERROR' | 'STALE'\n\nexport class NogrepError extends Error {\n constructor(\n message: string,\n public code: NogrepErrorCode,\n ) {\n super(message)\n }\n}\n"],"mappings":";AAkLO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACE,SACO,MACP;AACA,UAAM,OAAO;AAFN;AAAA,EAGT;AACF;","names":[]}
|
package/dist/query.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/query.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { join, resolve as resolvePath } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { IndexJson, RankedResult, Taxonomy } from './types.js'\nimport { NogrepError } from './types.js'\n\n// --- Term extraction ---\n\nexport function extractTerms(\n question: string,\n taxonomy: Taxonomy,\n): { tags: string[]; keywords: string[] } {\n const words = question\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, ' ')\n .split(/\\s+/)\n .filter(w => w.length > 1)\n\n const tags: string[] = []\n const keywords: string[] = []\n\n // Collect all taxonomy values for matching\n const tagLookup = new Map<string, string>()\n\n for (const val of taxonomy.static.layer) {\n tagLookup.set(val.toLowerCase(), `layer:${val}`)\n }\n for (const val of taxonomy.static.concern) {\n tagLookup.set(val.toLowerCase(), `concern:${val}`)\n }\n for (const val of taxonomy.static.type) {\n tagLookup.set(val.toLowerCase(), `type:${val}`)\n }\n for (const val of taxonomy.dynamic.domain) {\n tagLookup.set(val.toLowerCase(), `domain:${val}`)\n }\n for (const val of taxonomy.dynamic.tech) {\n tagLookup.set(val.toLowerCase(), `tech:${val}`)\n }\n for (const [cat, values] of Object.entries(taxonomy.custom)) {\n for (const val of values) {\n tagLookup.set(val.toLowerCase(), `${cat}:${val}`)\n }\n }\n\n // Stop words to skip as keywords\n const stopWords = new Set([\n 'the', 'is', 'at', 'in', 'of', 'on', 'to', 'a', 'an', 'and', 'or',\n 'for', 'it', 'do', 'does', 'how', 'what', 'where', 'which', 'when',\n 'who', 'why', 'this', 'that', 'with', 'from', 'by', 'be', 'as',\n 'are', 'was', 'were', 'been', 'has', 'have', 'had', 'not', 'but',\n 'if', 'my', 'our', 'its', 'can', 'will', 'should', 'would', 'could',\n 'about', 'after', 'work', 'works', 'use', 'uses', 'used',\n ])\n\n for (const word of words) {\n const tag = tagLookup.get(word)\n if (tag && !tags.includes(tag)) {\n tags.push(tag)\n }\n\n // Also check hyphenated compound matches (e.g. \"error-handling\")\n if (!tag && !stopWords.has(word)) {\n keywords.push(word)\n }\n }\n\n // Check for multi-word tag matches (e.g. \"error handling\" → \"error-handling\")\n const questionLower = question.toLowerCase()\n for (const [val, tag] of tagLookup.entries()) {\n if (val.includes('-')) {\n const spacedVersion = val.replace(/-/g, ' ')\n if (questionLower.includes(spacedVersion) && !tags.includes(tag)) {\n tags.push(tag)\n }\n if (questionLower.includes(val) && !tags.includes(tag)) {\n tags.push(tag)\n }\n }\n }\n\n return { tags, keywords }\n}\n\n// --- Resolution ---\n\nexport function resolveQuery(\n terms: { tags: string[]; keywords: string[] },\n index: IndexJson,\n limit = 5,\n): RankedResult[] {\n const scoreMap = new Map<string, { score: number; matchedOn: string[] }>()\n\n function addMatch(contextFile: string, score: number, matchLabel: string): void {\n const existing = scoreMap.get(contextFile)\n if (existing) {\n existing.score += score\n existing.matchedOn.push(matchLabel)\n } else {\n scoreMap.set(contextFile, { score, matchedOn: [matchLabel] })\n }\n }\n\n // Tag matching: +2 per match\n for (const tag of terms.tags) {\n const files = index.tags[tag]\n if (files) {\n for (const file of files) {\n addMatch(file, 2, `tag:${tag}`)\n }\n }\n }\n\n // Keyword matching: +1 per match\n for (const kw of terms.keywords) {\n const kwLower = kw.toLowerCase()\n\n // Direct keyword lookup\n const files = index.keywords[kwLower]\n if (files) {\n for (const file of files) {\n addMatch(file, 1, `keyword:${kwLower}`)\n }\n }\n\n // Also search all index keywords for partial matches\n for (const [indexKw, kwFiles] of Object.entries(index.keywords)) {\n if (indexKw === kwLower) continue // Already handled\n if (indexKw.includes(kwLower) || kwLower.includes(indexKw)) {\n for (const file of kwFiles) {\n addMatch(file, 1, `keyword:${indexKw}`)\n }\n }\n }\n }\n\n // Sort by score descending, then alphabetically for ties\n const results: RankedResult[] = [...scoreMap.entries()]\n .sort((a, b) => b[1].score - a[1].score || a[0].localeCompare(b[0]))\n .slice(0, limit)\n .map(([contextFile, { score, matchedOn }]) => ({\n contextFile,\n score,\n matchedOn: [...new Set(matchedOn)],\n summary: `Matched: ${[...new Set(matchedOn)].join(', ')}`,\n }))\n\n return results\n}\n\n// --- Index + taxonomy loading ---\n\nasync function loadIndex(projectRoot: string): Promise<IndexJson> {\n const indexPath = join(projectRoot, '.nogrep', '_index.json')\n try {\n const content = await readFile(indexPath, 'utf-8')\n return JSON.parse(content) as IndexJson\n } catch {\n throw new NogrepError(\n 'No .nogrep/_index.json found. Run /nogrep:init first.',\n 'NO_INDEX',\n )\n }\n}\n\nasync function loadTaxonomy(projectRoot: string): Promise<Taxonomy> {\n const taxonomyPath = join(projectRoot, '.nogrep', '_taxonomy.json')\n try {\n const content = await readFile(taxonomyPath, 'utf-8')\n return JSON.parse(content) as Taxonomy\n } catch {\n // Return default taxonomy if file doesn't exist\n return {\n static: {\n layer: ['presentation', 'business', 'data', 'infrastructure', 'cross-cutting'],\n concern: ['security', 'performance', 'caching', 'validation', 'error-handling', 'idempotency', 'observability'],\n type: ['module', 'flow', 'entity', 'integration', 'config', 'ui', 'test'],\n },\n dynamic: { domain: [], tech: [] },\n custom: {},\n }\n }\n}\n\nfunction buildTaxonomyFromIndex(index: IndexJson, baseTaxonomy: Taxonomy): Taxonomy {\n // Extract dynamic domain and tech values from the index tags\n const domains = new Set<string>(baseTaxonomy.dynamic.domain)\n const techs = new Set<string>(baseTaxonomy.dynamic.tech)\n\n for (const tagKey of Object.keys(index.tags)) {\n const [category, value] = tagKey.split(':')\n if (!category || !value) continue\n if (category === 'domain') domains.add(value)\n if (category === 'tech') techs.add(value)\n }\n\n return {\n ...baseTaxonomy,\n dynamic: {\n domain: [...domains],\n tech: [...techs],\n },\n }\n}\n\n// --- Formatting ---\n\nfunction formatPaths(results: RankedResult[]): string {\n return results.map(r => r.contextFile).join('\\n')\n}\n\nfunction formatJson(results: RankedResult[]): string {\n return JSON.stringify(results, null, 2)\n}\n\nfunction formatSummary(results: RankedResult[]): string {\n if (results.length === 0) return 'No matching context files found.'\n return results\n .map(r => `- ${r.contextFile} (score: ${r.score}) — ${r.summary}`)\n .join('\\n')\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n tags: { type: 'string' },\n keywords: { type: 'string' },\n question: { type: 'string' },\n format: { type: 'string', default: 'json' },\n limit: { type: 'string', default: '5' },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = resolvePath(values.root ?? process.cwd())\n const limit = parseInt(values.limit ?? '5', 10)\n const format = values.format ?? 'json'\n\n const index = await loadIndex(root)\n const baseTaxonomy = await loadTaxonomy(root)\n const taxonomy = buildTaxonomyFromIndex(index, baseTaxonomy)\n\n let terms: { tags: string[]; keywords: string[] }\n\n if (values.question) {\n terms = extractTerms(values.question, taxonomy)\n } else if (values.tags || values.keywords) {\n const tags = values.tags\n ? values.tags.split(',').map(t => t.trim()).filter(Boolean)\n : []\n const keywords = values.keywords\n ? values.keywords.split(',').map(k => k.trim()).filter(Boolean)\n : []\n terms = { tags, keywords }\n } else {\n process.stderr.write(\n JSON.stringify({ error: 'Usage: node query.js --tags <tags> | --keywords <words> | --question <text> [--format paths|json|summary] [--limit N]' }) + '\\n',\n )\n process.exitCode = 1\n return\n }\n\n const results = resolveQuery(terms, index, limit)\n\n switch (format) {\n case 'paths':\n process.stdout.write(formatPaths(results) + '\\n')\n break\n case 'summary':\n process.stdout.write(formatSummary(results) + '\\n')\n break\n case 'json':\n default:\n process.stdout.write(formatJson(results) + '\\n')\n break\n }\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof NogrepError) {\n process.stderr.write(JSON.stringify({ error: err.message, code: err.code }) + '\\n')\n } else {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(JSON.stringify({ error: message }) + '\\n')\n }\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
|
package/dist/settings.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/settings.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { NogrepSettings } from './types.js'\n\nconst SETTINGS_FILE = '.claude/settings.json'\nconst SETTINGS_LOCAL_FILE = '.claude/settings.local.json'\n\ninterface SettingsJson {\n nogrep?: Partial<NogrepSettings>\n [key: string]: unknown\n}\n\nasync function readJsonFile(path: string): Promise<SettingsJson> {\n try {\n const content = await readFile(path, 'utf-8')\n return JSON.parse(content) as SettingsJson\n } catch {\n return {}\n }\n}\n\nasync function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\nexport async function readSettings(projectRoot: string): Promise<NogrepSettings> {\n const sharedPath = join(projectRoot, SETTINGS_FILE)\n const localPath = join(projectRoot, SETTINGS_LOCAL_FILE)\n\n const shared = await readJsonFile(sharedPath)\n const local = await readJsonFile(localPath)\n\n const enabled =\n local.nogrep?.enabled ?? shared.nogrep?.enabled ?? false\n\n return { enabled }\n}\n\nexport async function writeSettings(\n projectRoot: string,\n settings: Partial<NogrepSettings>,\n local?: boolean,\n): Promise<void> {\n const filePath = join(\n projectRoot,\n local ? SETTINGS_LOCAL_FILE : SETTINGS_FILE,\n )\n\n await ensureDir(join(projectRoot, '.claude'))\n\n const existing = await readJsonFile(filePath)\n existing.nogrep = { ...existing.nogrep, ...settings }\n\n await writeFile(filePath, JSON.stringify(existing, null, 2) + '\\n', 'utf-8')\n}\n\n// CLI interface\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n set: { type: 'string' },\n get: { type: 'boolean', default: false },\n local: { type: 'boolean', default: false },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = values.root ?? process.cwd()\n\n if (values.get) {\n const settings = await readSettings(root)\n process.stdout.write(JSON.stringify(settings, null, 2) + '\\n')\n return\n }\n\n if (values.set) {\n const [key, value] = values.set.split('=')\n if (key === 'enabled') {\n const enabled = value === 'true'\n await writeSettings(root, { enabled }, values.local)\n } else {\n process.stderr.write(JSON.stringify({ error: `Unknown setting: ${key}` }) + '\\n')\n process.exitCode = 1\n }\n return\n }\n\n process.stderr.write(JSON.stringify({ error: 'Usage: node settings.js --set enabled=true [--local] | --get' }) + '\\n')\n process.exitCode = 1\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":";AAAA,SAAS,UAAU,WAAW,aAAa;AAC3C,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAG1B,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAO5B,eAAe,aAAa,MAAqC;AAC/D,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,UAAU,KAA4B;AACnD,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACtC;AAEA,eAAsB,aAAa,aAA8C;AAC/E,QAAM,aAAa,KAAK,aAAa,aAAa;AAClD,QAAM,YAAY,KAAK,aAAa,mBAAmB;AAEvD,QAAM,SAAS,MAAM,aAAa,UAAU;AAC5C,QAAM,QAAQ,MAAM,aAAa,SAAS;AAE1C,QAAM,UACJ,MAAM,QAAQ,WAAW,OAAO,QAAQ,WAAW;AAErD,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,cACpB,aACA,UACA,OACe;AACf,QAAM,WAAW;AAAA,IACf;AAAA,IACA,QAAQ,sBAAsB;AAAA,EAChC;AAEA,QAAM,UAAU,KAAK,aAAa,SAAS,CAAC;AAE5C,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,WAAS,SAAS,EAAE,GAAG,SAAS,QAAQ,GAAG,SAAS;AAEpD,QAAM,UAAU,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO;AAC7E;AAGA,eAAe,OAAsB;AACnC,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,SAAS;AAAA,MACP,KAAK,EAAE,MAAM,SAAS;AAAA,MACtB,KAAK,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACvC,OAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACzC,MAAM,EAAE,MAAM,UAAU,SAAS,QAAQ,IAAI,EAAE;AAAA,IACjD;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,OAAO,OAAO,QAAQ,QAAQ,IAAI;AAExC,MAAI,OAAO,KAAK;AACd,UAAM,WAAW,MAAM,aAAa,IAAI;AACxC,YAAQ,OAAO,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAC7D;AAAA,EACF;AAEA,MAAI,OAAO,KAAK;AACd,UAAM,CAAC,KAAK,KAAK,IAAI,OAAO,IAAI,MAAM,GAAG;AACzC,QAAI,QAAQ,WAAW;AACrB,YAAM,UAAU,UAAU;AAC1B,YAAM,cAAc,MAAM,EAAE,QAAQ,GAAG,OAAO,KAAK;AAAA,IACrD,OAAO;AACL,cAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,oBAAoB,GAAG,GAAG,CAAC,IAAI,IAAI;AAChF,cAAQ,WAAW;AAAA,IACrB;AACA;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,+DAA+D,CAAC,IAAI,IAAI;AACrH,UAAQ,WAAW;AACrB;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC,IAAI,IAAI;AAC9D,UAAQ,WAAW;AACrB,CAAC;","names":[]}
|
package/dist/signals.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/signals.ts"],"sourcesContent":["import { readdir, stat, readFile } from 'fs/promises'\nimport { join, extname, relative, resolve } from 'path'\nimport { execFile } from 'child_process'\nimport { promisify } from 'util'\nimport type { SignalResult, DirectoryNode, ManifestFile, ChurnEntry, FileSize } from './types.js'\n\nconst execFileAsync = promisify(execFile)\n\nconst SKIP_DIRS = new Set([\n 'node_modules', 'dist', 'build', '.git', 'coverage',\n '.next', '.nuxt', '__pycache__', '.venv', 'venv',\n '.idea', '.vscode', '.nogrep',\n])\n\nconst MANIFEST_NAMES: Record<string, string> = {\n 'package.json': 'npm',\n 'requirements.txt': 'pip',\n 'pom.xml': 'maven',\n 'go.mod': 'go',\n 'Podfile': 'cocoapods',\n 'Cargo.toml': 'cargo',\n 'pubspec.yaml': 'flutter',\n 'composer.json': 'composer',\n}\n\nconst ENTRY_NAMES = new Set(['main', 'index', 'app', 'server'])\n\nconst TEST_PATTERNS = [\n /\\.test\\.\\w+$/,\n /\\.spec\\.\\w+$/,\n /_test\\.\\w+$/,\n /^test_.*\\.py$/,\n]\n\ninterface CollectOptions {\n exclude?: string[]\n maxDepth?: number\n}\n\nexport async function collectSignals(\n root: string,\n options: CollectOptions = {},\n): Promise<SignalResult> {\n const absRoot = resolve(root)\n const maxDepth = options.maxDepth ?? 4\n const extraSkip = new Set(options.exclude ?? [])\n\n const allFiles: { path: string; bytes: number }[] = []\n const extensionMap: Record<string, number> = {}\n const manifests: ManifestFile[] = []\n const entryPoints: string[] = []\n const envFiles: string[] = []\n const testFiles: string[] = []\n\n const directoryTree = await walkDirectory(absRoot, absRoot, 0, maxDepth, extraSkip, {\n allFiles,\n extensionMap,\n manifests,\n entryPoints,\n envFiles,\n testFiles,\n })\n\n const gitChurn = await collectGitChurn(absRoot)\n\n const largeFiles = allFiles\n .sort((a, b) => b.bytes - a.bytes)\n .slice(0, 20)\n .map(f => ({ path: f.path, bytes: f.bytes }))\n\n return {\n directoryTree,\n extensionMap,\n manifests,\n entryPoints,\n gitChurn,\n largeFiles,\n envFiles,\n testFiles,\n }\n}\n\ninterface Collectors {\n allFiles: { path: string; bytes: number }[]\n extensionMap: Record<string, number>\n manifests: ManifestFile[]\n entryPoints: string[]\n envFiles: string[]\n testFiles: string[]\n}\n\nasync function walkDirectory(\n dir: string,\n root: string,\n depth: number,\n maxDepth: number,\n extraSkip: Set<string>,\n collectors: Collectors,\n): Promise<DirectoryNode[]> {\n if (depth > maxDepth) return []\n\n let entries\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n return []\n }\n\n const nodes: DirectoryNode[] = []\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n const relPath = relative(root, fullPath)\n\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name) || extraSkip.has(entry.name)) continue\n\n const children = await walkDirectory(fullPath, root, depth + 1, maxDepth, extraSkip, collectors)\n nodes.push({ name: entry.name, path: relPath, type: 'directory', children })\n } else if (entry.isFile()) {\n nodes.push({ name: entry.name, path: relPath, type: 'file' })\n\n let fileBytes = 0\n try {\n const s = await stat(fullPath)\n fileBytes = s.size\n } catch {\n // skip\n }\n\n collectors.allFiles.push({ path: relPath, bytes: fileBytes })\n\n const ext = extname(entry.name)\n if (ext) {\n collectors.extensionMap[ext] = (collectors.extensionMap[ext] ?? 0) + 1\n }\n\n // Manifest check\n if (entry.name in MANIFEST_NAMES) {\n collectors.manifests.push({\n path: relPath,\n type: MANIFEST_NAMES[entry.name]!,\n depth,\n })\n }\n\n // Entry point check — root or src/ level\n if (depth <= 1 || (depth === 2 && dir.endsWith('/src'))) {\n const nameWithoutExt = entry.name.replace(/\\.\\w+$/, '')\n if (ENTRY_NAMES.has(nameWithoutExt)) {\n collectors.entryPoints.push(relPath)\n }\n }\n\n // Env files\n if (entry.name.startsWith('.env')) {\n collectors.envFiles.push(relPath)\n }\n\n // Config directories are handled at directory level\n // But we also detect config files at root\n if (depth === 0 && entry.name.match(/^config\\./)) {\n collectors.envFiles.push(relPath)\n }\n\n // Test files\n const fileName = entry.name\n if (TEST_PATTERNS.some(p => p.test(fileName))) {\n collectors.testFiles.push(relPath)\n }\n }\n }\n\n // Check if this directory is a config directory\n const dirName = dir.split('/').pop()\n if (dirName === 'config' && depth <= 2) {\n collectors.envFiles.push(relative(root, dir))\n }\n\n return nodes\n}\n\nasync function collectGitChurn(root: string): Promise<ChurnEntry[]> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['log', '--stat', '--oneline', '-50', '--pretty=format:'],\n { cwd: root, maxBuffer: 1024 * 1024 },\n )\n\n const changeCounts: Record<string, number> = {}\n\n for (const line of stdout.split('\\n')) {\n // Match lines like: src/billing/service.ts | 42 +++---\n const match = line.match(/^\\s+(.+?)\\s+\\|\\s+(\\d+)/)\n if (match) {\n const filePath = match[1]!.trim()\n const changes = parseInt(match[2]!, 10)\n changeCounts[filePath] = (changeCounts[filePath] ?? 0) + changes\n }\n }\n\n return Object.entries(changeCounts)\n .sort(([, a], [, b]) => b - a)\n .slice(0, 20)\n .map(([path, changes]) => ({ path, changes }))\n } catch {\n // No git or git log fails — return empty\n return []\n }\n}\n\n// --- CLI interface ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n let root = '.'\n const exclude: string[] = []\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--root' && args[i + 1]) {\n root = args[i + 1]!\n i++\n } else if (args[i] === '--exclude' && args[i + 1]) {\n exclude.push(...args[i + 1]!.split(','))\n i++\n }\n }\n\n const result = await collectSignals(root, { exclude })\n process.stdout.write(JSON.stringify(result, null, 2))\n}\n\nmain().catch(err => {\n process.stderr.write(JSON.stringify({ error: String(err) }))\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
package/dist/trim.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/trim.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { resolve, extname, basename } from 'path'\n\nconst MAX_CLUSTER_LINES = 300\n\ninterface TrimOptions {\n maxLines?: number\n}\n\n// Language-agnostic regex patterns for stripping function/method bodies\n// Strategy: find opening braces after signatures, track depth, remove body content\n\nfunction trimTypeScript(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let braceDepth = 0\n let inBody = false\n let bodyStartDepth = 0\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Always keep: empty lines at top level, imports, type/interface, decorators, exports of types\n if (braceDepth === 0 || !inBody) {\n if (\n trimmed === '' ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('export type ') ||\n trimmed.startsWith('export interface ') ||\n trimmed.startsWith('export enum ') ||\n trimmed.startsWith('export const ') ||\n trimmed.startsWith('type ') ||\n trimmed.startsWith('interface ') ||\n trimmed.startsWith('enum ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('//') ||\n trimmed.startsWith('/*') ||\n trimmed.startsWith('*') ||\n trimmed.startsWith('declare ')\n ) {\n result.push(line)\n // Count braces even in kept lines\n braceDepth += countChar(trimmed, '{') - countChar(trimmed, '}')\n continue\n }\n }\n\n const openBraces = countChar(trimmed, '{')\n const closeBraces = countChar(trimmed, '}')\n\n if (!inBody) {\n // Detect function/method signature — line with opening brace\n if (isSignatureLine(trimmed) && openBraces > closeBraces) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n inBody = true\n bodyStartDepth = braceDepth\n continue\n }\n\n // Class/interface declaration — keep but don't treat as body\n if (isClassOrInterfaceLine(trimmed)) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n continue\n }\n\n // Keep the line (top-level statement, property declaration, etc.)\n result.push(line)\n braceDepth += openBraces - closeBraces\n } else {\n // Inside a function body — skip lines\n braceDepth += openBraces - closeBraces\n\n // Check if we've closed back to where the body started\n if (braceDepth < bodyStartDepth) {\n // Add closing brace\n result.push(line)\n inBody = false\n }\n }\n }\n\n return result.join('\\n')\n}\n\nfunction trimPython(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let skipIndent = -1\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n const trimmed = line.trim()\n const indent = line.length - line.trimStart().length\n\n // If we're skipping a body and this line is still indented deeper, skip it\n if (skipIndent >= 0) {\n if (trimmed === '' || indent > skipIndent) {\n continue\n }\n // We've exited the body\n skipIndent = -1\n }\n\n // Always keep: comments, imports, class defs, decorators, type hints, module-level assignments\n if (\n trimmed === '' ||\n trimmed.startsWith('#') ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('from ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('class ') ||\n /^[A-Z_][A-Z_0-9]*\\s*=/.test(trimmed)\n ) {\n result.push(line)\n continue\n }\n\n // Function/method definition — keep signature, skip body\n if (trimmed.startsWith('def ') || trimmed.startsWith('async def ')) {\n result.push(line)\n // If the next non-empty line has docstring, keep it\n const docIdx = findDocstring(lines, i + 1, indent)\n if (docIdx > i) {\n for (let j = i + 1; j <= docIdx; j++) {\n result.push(lines[j]!)\n }\n }\n skipIndent = indent\n continue\n }\n\n // Keep everything else at module/class level\n result.push(line)\n }\n\n return result.join('\\n')\n}\n\nfunction trimJava(content: string): string {\n // Java/Kotlin — very similar to TypeScript brace-matching\n const lines = content.split('\\n')\n const result: string[] = []\n let braceDepth = 0\n let inBody = false\n let bodyStartDepth = 0\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (braceDepth === 0 || !inBody) {\n if (\n trimmed === '' ||\n trimmed.startsWith('import ') ||\n trimmed.startsWith('package ') ||\n trimmed.startsWith('@') ||\n trimmed.startsWith('//') ||\n trimmed.startsWith('/*') ||\n trimmed.startsWith('*') ||\n trimmed.startsWith('public interface ') ||\n trimmed.startsWith('interface ') ||\n trimmed.startsWith('public enum ') ||\n trimmed.startsWith('enum ')\n ) {\n result.push(line)\n braceDepth += countChar(trimmed, '{') - countChar(trimmed, '}')\n continue\n }\n }\n\n const openBraces = countChar(trimmed, '{')\n const closeBraces = countChar(trimmed, '}')\n\n if (!inBody) {\n if (isJavaMethodSignature(trimmed) && openBraces > closeBraces) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n inBody = true\n bodyStartDepth = braceDepth\n continue\n }\n\n if (isJavaClassLine(trimmed)) {\n result.push(line)\n braceDepth += openBraces - closeBraces\n continue\n }\n\n result.push(line)\n braceDepth += openBraces - closeBraces\n } else {\n braceDepth += openBraces - closeBraces\n if (braceDepth < bodyStartDepth) {\n result.push(line)\n inBody = false\n }\n }\n }\n\n return result.join('\\n')\n}\n\nfunction trimGeneric(content: string): string {\n // For unknown languages, just return as-is (truncation handles size)\n return content\n}\n\n// --- Helpers ---\n\nfunction countChar(s: string, ch: string): number {\n let count = 0\n let inString = false\n let stringChar = ''\n for (let i = 0; i < s.length; i++) {\n const c = s[i]!\n if (inString) {\n if (c === stringChar && s[i - 1] !== '\\\\') inString = false\n } else if (c === '\"' || c === \"'\" || c === '`') {\n inString = true\n stringChar = c\n } else if (c === ch) {\n count++\n }\n }\n return count\n}\n\nfunction isSignatureLine(trimmed: string): boolean {\n return /^(export\\s+)?(async\\s+)?function\\s/.test(trimmed) ||\n /^(public|private|protected|static|async|get|set|\\*)\\s/.test(trimmed) ||\n /^(readonly\\s+)?[a-zA-Z_$][a-zA-Z0-9_$]*\\s*\\(/.test(trimmed) ||\n /^(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?\\(/.test(trimmed) ||\n /^(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?function/.test(trimmed) ||\n // Arrow function assigned at class level\n /^[a-zA-Z_$][a-zA-Z0-9_$]*\\s*=\\s*(async\\s+)?\\(/.test(trimmed)\n}\n\nfunction isClassOrInterfaceLine(trimmed: string): boolean {\n return /^(export\\s+)?(abstract\\s+)?(class|interface|enum)\\s/.test(trimmed) ||\n /^(export\\s+)?namespace\\s/.test(trimmed)\n}\n\nfunction isJavaMethodSignature(trimmed: string): boolean {\n return /^(public|private|protected|static|final|abstract|synchronized|native)\\s/.test(trimmed) &&\n /\\(/.test(trimmed)\n}\n\nfunction isJavaClassLine(trimmed: string): boolean {\n return /^(public|private|protected)?\\s*(abstract\\s+)?(class|interface|enum)\\s/.test(trimmed)\n}\n\nfunction findDocstring(lines: string[], startIdx: number, defIndent: number): number {\n // Find Python docstring (triple-quoted) after a def\n for (let i = startIdx; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (trimmed === '') continue\n if (trimmed.startsWith('\"\"\"') || trimmed.startsWith(\"'''\")) {\n const quote = trimmed.slice(0, 3)\n // Single-line docstring\n if (trimmed.length > 3 && trimmed.endsWith(quote)) return i\n // Multi-line docstring — find closing\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j]!.trim().endsWith(quote)) return j\n }\n return i\n }\n // First non-empty line after def is not a docstring\n return startIdx - 1\n }\n return startIdx - 1\n}\n\nfunction getTrimmer(filePath: string): (content: string) => string {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.ts':\n case '.tsx':\n case '.js':\n case '.jsx':\n case '.mjs':\n case '.cjs':\n return trimTypeScript\n case '.py':\n return trimPython\n case '.java':\n case '.kt':\n case '.kts':\n case '.scala':\n case '.groovy':\n return trimJava\n case '.go':\n case '.rs':\n case '.c':\n case '.cpp':\n case '.h':\n case '.hpp':\n case '.cs':\n case '.swift':\n case '.dart':\n return trimJava // brace-based languages use same strategy\n default:\n return trimGeneric\n }\n}\n\nexport async function trimCluster(paths: string[], projectRoot: string): Promise<string> {\n const results: Array<{ path: string; content: string; lines: number }> = []\n\n for (const filePath of paths) {\n const absPath = resolve(projectRoot, filePath)\n try {\n const raw = await readFile(absPath, 'utf-8')\n const trimmer = getTrimmer(filePath)\n const trimmed = trimmer(raw)\n results.push({\n path: filePath,\n content: trimmed,\n lines: trimmed.split('\\n').length,\n })\n } catch {\n // Skip files that can't be read\n if (process.env['NOGREP_DEBUG'] === '1') {\n process.stderr.write(`[nogrep] Could not read: ${absPath}\\n`)\n }\n }\n }\n\n // Sort by line count descending — truncate least important (largest) files first\n results.sort((a, b) => a.lines - b.lines)\n\n const output: string[] = []\n let totalLines = 0\n const maxLines = MAX_CLUSTER_LINES\n\n for (const file of results) {\n const header = `// === ${file.path} ===`\n const fileLines = file.content.split('\\n')\n const available = maxLines - totalLines - 2 // header + separator\n\n if (available <= 0) break\n\n output.push(header)\n if (fileLines.length <= available) {\n output.push(file.content)\n } else {\n output.push(fileLines.slice(0, available).join('\\n'))\n output.push(`// ... truncated (${fileLines.length - available} more lines)`)\n }\n output.push('')\n\n totalLines += Math.min(fileLines.length, available) + 2\n }\n\n return output.join('\\n')\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n\n if (args.length === 0) {\n process.stderr.write('Usage: node trim.js <path1> <path2> ...\\n')\n process.exit(1)\n }\n\n const projectRoot = process.cwd()\n const result = await trimCluster(args, projectRoot)\n process.stdout.write(result)\n}\n\nconst isDirectRun = process.argv[1]?.endsWith('trim.js') || process.argv[1]?.endsWith('trim.ts')\nif (isDirectRun) {\n main().catch((err: unknown) => {\n process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n })\n}\n"],"mappings":"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","names":[]}
|
package/dist/types.js
DELETED
package/dist/validate.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../scripts/validate.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { join, resolve as resolvePath } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { parseArgs } from 'node:util'\nimport { glob } from 'glob'\nimport matter from 'gray-matter'\nimport type { StaleResult } from './types.js'\nimport { NogrepError } from './types.js'\n\n// --- Freshness check ---\n\nexport async function checkFreshness(\n nodeFile: string,\n projectRoot: string,\n): Promise<StaleResult> {\n let content: string\n try {\n content = await readFile(join(projectRoot, nodeFile), 'utf-8')\n } catch {\n return { file: nodeFile, isStale: true, reason: 'context file not found' }\n }\n\n const parsed = matter(content)\n const srcPaths: string[] = parsed.data.src_paths ?? []\n const lastSynced = parsed.data.last_synced as\n | { src_hash?: string; commit?: string; timestamp?: string }\n | undefined\n\n if (!lastSynced?.src_hash) {\n return { file: nodeFile, isStale: true, reason: 'no src_hash in frontmatter' }\n }\n\n if (srcPaths.length === 0) {\n return { file: nodeFile, isStale: false }\n }\n\n // Glob all matching source files\n const allFiles: string[] = []\n for (const pattern of srcPaths) {\n const matches = await glob(pattern, {\n cwd: projectRoot,\n nodir: true,\n ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'coverage/**'],\n })\n allFiles.push(...matches)\n }\n\n allFiles.sort()\n\n if (allFiles.length === 0) {\n return { file: nodeFile, isStale: true, reason: 'no source files match src_paths' }\n }\n\n // Compute SHA256 of all file contents concatenated\n const hash = createHash('sha256')\n for (const file of allFiles) {\n try {\n const fileContent = await readFile(join(projectRoot, file))\n hash.update(fileContent)\n } catch {\n // File unreadable — skip\n }\n }\n const currentHash = `sha256:${hash.digest('hex').slice(0, 12)}`\n\n if (currentHash !== lastSynced.src_hash) {\n return {\n file: nodeFile,\n isStale: true,\n reason: `hash mismatch: expected ${lastSynced.src_hash}, got ${currentHash}`,\n }\n }\n\n return { file: nodeFile, isStale: false }\n}\n\n// --- Discover all context nodes ---\n\nasync function discoverNodes(projectRoot: string): Promise<string[]> {\n const nogrepDir = join(projectRoot, '.nogrep')\n const patterns = [\n 'domains/*.md',\n 'architecture/*.md',\n 'flows/*.md',\n 'entities/*.md',\n ]\n\n const files: string[] = []\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: nogrepDir, nodir: true })\n files.push(...matches.map(m => `.nogrep/${m}`))\n }\n\n return files.sort()\n}\n\n// --- Validate all nodes ---\n\nexport async function validateAll(\n projectRoot: string,\n): Promise<{ total: number; fresh: StaleResult[]; stale: StaleResult[] }> {\n const indexPath = join(projectRoot, '.nogrep', '_index.json')\n try {\n await readFile(indexPath, 'utf-8')\n } catch {\n throw new NogrepError(\n 'No .nogrep/_index.json found. Run /nogrep:init first.',\n 'NO_INDEX',\n )\n }\n\n const nodeFiles = await discoverNodes(projectRoot)\n const results = await Promise.all(\n nodeFiles.map(f => checkFreshness(f, projectRoot)),\n )\n\n const fresh = results.filter(r => !r.isStale)\n const stale = results.filter(r => r.isStale)\n\n return { total: results.length, fresh, stale }\n}\n\n// --- Formatting ---\n\nfunction formatText(result: { total: number; fresh: StaleResult[]; stale: StaleResult[] }): string {\n const lines: string[] = []\n lines.push(`nogrep index: ${result.total} nodes`)\n lines.push(` Fresh: ${result.fresh.length}`)\n lines.push(` Stale: ${result.stale.length}`)\n\n if (result.stale.length > 0) {\n lines.push('')\n lines.push('Stale nodes:')\n for (const s of result.stale) {\n lines.push(` - ${s.file}: ${s.reason}`)\n }\n }\n\n return lines.join('\\n')\n}\n\nfunction formatJson(result: { total: number; fresh: StaleResult[]; stale: StaleResult[] }): string {\n return JSON.stringify(result, null, 2)\n}\n\n// --- CLI ---\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n format: { type: 'string', default: 'text' },\n root: { type: 'string', default: process.cwd() },\n },\n strict: true,\n })\n\n const root = resolvePath(values.root ?? process.cwd())\n const format = values.format ?? 'text'\n\n const result = await validateAll(root)\n\n switch (format) {\n case 'json':\n process.stdout.write(formatJson(result) + '\\n')\n break\n case 'text':\n default:\n process.stdout.write(formatText(result) + '\\n')\n break\n }\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof NogrepError) {\n process.stderr.write(JSON.stringify({ error: err.message, code: err.code }) + '\\n')\n } else {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(JSON.stringify({ error: message }) + '\\n')\n }\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
|
package/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":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|