@pimzino/sgrep 1.3.39 → 1.3.44

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.
@@ -1,188 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require("fs");
4
- const path = require("path");
5
- const { execSync } = require("child_process");
6
- const os = require("os");
7
-
8
- const DEBUG_ENABLED = process.env.SGREP_DEBUG === "1" || process.env.SGREP_DEBUG === "true";
9
- const DEBUG_LOG_FILE = process.env.SGREP_ENHANCE_LOG || path.join(os.tmpdir(), "sgrep-enhance.log");
10
-
11
- // Lightweight reminder injected with every prompt (exploits recency bias)
12
- const SGREP_REMINDER = `<tool-guidance scope="local-codebase-only">
13
- <condition>Question is about code in THIS repository</condition>
14
- <action>Load sgrep skill via Skill tool, then execute sgrep commands</action>
15
- <condition>Question is about external APIs, libraries, documentation, or general knowledge</condition>
16
- <action>Use WebSearch or WebFetch instead - sgrep cannot answer these</action>
17
- </tool-guidance>`;
18
-
19
- function debugLog(message) {
20
- if (!DEBUG_ENABLED) return;
21
- try {
22
- const dir = path.dirname(DEBUG_LOG_FILE);
23
- if (!fs.existsSync(dir)) {
24
- fs.mkdirSync(dir, { recursive: true });
25
- }
26
- const stamp = new Date().toISOString();
27
- fs.appendFileSync(DEBUG_LOG_FILE, `[${stamp}] ${message}\n`);
28
- } catch (e) {
29
- // Ignore errors
30
- }
31
- }
32
-
33
- function debugLogSection(title, content) {
34
- debugLog(`\n========== ${title} ==========`);
35
- debugLog(content);
36
- debugLog(`========== END ${title} ==========\n`);
37
- }
38
-
39
- function readHookInput() {
40
- return new Promise((resolve) => {
41
- let data = "";
42
- process.stdin.setEncoding("utf8");
43
- process.stdin.on("data", (chunk) => {
44
- data += chunk;
45
- });
46
- process.stdin.on("end", () => {
47
- if (!data.trim()) {
48
- debugLog("No input data received from stdin");
49
- resolve(null);
50
- return;
51
- }
52
- try {
53
- const parsed = JSON.parse(data);
54
- debugLogSection("RAW HOOK INPUT", JSON.stringify(parsed, null, 2));
55
- resolve(parsed);
56
- } catch (e) {
57
- debugLog(`Failed to decode JSON: ${e.message}`);
58
- debugLog(`Raw data: ${data}`);
59
- resolve(null);
60
- }
61
- });
62
- });
63
- }
64
-
65
- /**
66
- * Determine if enhancement should proceed.
67
- * Auto-confirms on all platforms (no user prompting).
68
- */
69
- function shouldEnhance(prompt) {
70
- debugLog("Auto-confirming enhancement (prompting disabled on all platforms)");
71
- return true;
72
- }
73
-
74
- /**
75
- * Output the hook response with optional enhanced context and mandatory reminder.
76
- * Always includes the sgrep reminder to exploit recency bias.
77
- */
78
- function outputResponse(enhancedContext = null) {
79
- const parts = [];
80
-
81
- if (enhancedContext) {
82
- parts.push(`[sgrep enhanced context]\n${enhancedContext}`);
83
- }
84
-
85
- // Always append the reminder (exploits recency bias - appears fresh with every prompt)
86
- parts.push(SGREP_REMINDER);
87
-
88
- const response = {
89
- hookSpecificOutput: {
90
- hookEventName: "UserPromptSubmit",
91
- additionalContext: parts.join("\n\n")
92
- }
93
- };
94
-
95
- debugLogSection("HOOK RESPONSE", JSON.stringify(response, null, 2));
96
- console.log(JSON.stringify(response));
97
- }
98
-
99
- async function main() {
100
- debugLog("\n\n====================================================");
101
- debugLog("SGREP ENHANCE HOOK TRIGGERED - UserPromptSubmit");
102
- debugLog("====================================================");
103
-
104
- const payload = await readHookInput();
105
- if (!payload) {
106
- debugLog("ERROR: No payload received - exiting");
107
- process.exit(0);
108
- }
109
-
110
- // Log all payload fields
111
- debugLog(`Hook Event: ${payload.hook_event_name || 'unknown'}`);
112
- debugLog(`Session ID: ${payload.session_id || 'unknown'}`);
113
- debugLog(`CWD: ${payload.cwd || 'unknown'}`);
114
- debugLog(`Permission Mode: ${payload.permission_mode || 'unknown'}`);
115
-
116
- const prompt = payload.prompt || "";
117
- debugLogSection("ORIGINAL PROMPT", prompt);
118
-
119
- // Skip enhancement for very short prompts or slash commands, but still inject reminder
120
- if (prompt.length < 10) {
121
- debugLog("SKIP enhancement: Prompt too short (< 10 chars), but injecting reminder");
122
- outputResponse(null);
123
- debugLog("====================================================\n");
124
- process.exit(0);
125
- }
126
- if (prompt.startsWith("/")) {
127
- debugLog("SKIP enhancement: Prompt is a slash command, but injecting reminder");
128
- outputResponse(null);
129
- debugLog("====================================================\n");
130
- process.exit(0);
131
- }
132
-
133
- // Check if enhancement should proceed (auto-confirms on all platforms)
134
- debugLog("Checking if enhancement should proceed...");
135
- const shouldProceed = shouldEnhance(prompt);
136
- debugLog(`Enhancement decision: ${shouldProceed ? 'proceeding' : 'skipped'}`);
137
-
138
- if (!shouldProceed) {
139
- debugLog("SKIP enhancement: Not proceeding, but injecting reminder");
140
- outputResponse(null);
141
- debugLog("====================================================\n");
142
- process.exit(0);
143
- }
144
-
145
- let enhancedContext = null;
146
-
147
- try {
148
- // Call sgrep enhance with JSON output
149
- debugLog("Calling: sgrep enhance --json \"<prompt>\"");
150
-
151
- // Escape the prompt for shell
152
- const escapedPrompt = prompt.replace(/"/g, '\\"').replace(/\n/g, ' ');
153
-
154
- const result = execSync(`sgrep enhance --json "${escapedPrompt}"`, {
155
- encoding: "utf8",
156
- timeout: 60000, // 60 second timeout
157
- stdio: ["pipe", "pipe", "pipe"],
158
- });
159
-
160
- debugLogSection("SGREP ENHANCE RAW RESULT", result);
161
-
162
- // Parse the JSON result
163
- const parsed = JSON.parse(result);
164
- const enhanced = parsed.enhanced || null;
165
-
166
- if (enhanced && enhanced !== prompt) {
167
- debugLogSection("ENHANCED PROMPT", enhanced);
168
- enhancedContext = enhanced;
169
- debugLog("SUCCESS: Enhancement complete");
170
- } else {
171
- debugLog("SKIP: No enhanced prompt or same as original");
172
- }
173
- } catch (e) {
174
- debugLog(`ERROR: Enhancement failed - ${e.message}`);
175
- if (e.stderr) {
176
- debugLog(`STDERR: ${e.stderr}`);
177
- }
178
- // Continue to output reminder even on error
179
- }
180
-
181
- // Always output response with reminder (and enhanced context if available)
182
- outputResponse(enhancedContext);
183
-
184
- debugLog("====================================================\n");
185
- process.exit(0);
186
- }
187
-
188
- main();
@@ -1,236 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * sgrep_project_summary.js - Injects project context into Claude Code sessions
5
- *
6
- * Runs on SessionStart (startup, compact) to provide Claude with immediate
7
- * understanding of the project's tech stack, architecture, and key patterns.
8
- *
9
- * Features:
10
- * - Runs `sgrep ask` with a structured prompt
11
- * - Falls back to instructions if query fails
12
- * - Waits for index to build before querying
13
- */
14
-
15
- const fs = require("fs");
16
- const path = require("path");
17
- const os = require("os");
18
- const { execSync } = require("child_process");
19
-
20
- // Configuration
21
- const QUERY_TIMEOUT_MS = 25000; // 25 seconds
22
- const WATCH_READY_WAIT_MS = 10000; // Wait up to 10s for watch to be ready
23
- const INDEX_READY_WAIT_MS = 60000; // Wait up to 60s for index to be ready
24
-
25
- const DEBUG_ENABLED = process.env.SGREP_DEBUG === "1" || process.env.SGREP_DEBUG === "true";
26
- const DEBUG_LOG_FILE = process.env.SGREP_PROJECT_LOG || path.join(os.tmpdir(), "sgrep-project-summary.log");
27
-
28
- // Structured prompt optimized for agentic LLM context
29
- const PROJECT_SUMMARY_PROMPT = `You are briefing an AI coding agent that is about to work on this codebase for the first time. Provide the information that will save it the most tool calls and prevent mistakes. Be concise and specific — no filler.
30
-
31
- FORMAT YOUR RESPONSE EXACTLY AS:
32
-
33
- **What This Project Does:** [1-2 sentences: the domain problem, who uses it, what it produces. NOT the tech stack.]
34
-
35
- **Core Concepts:** [The 3-6 key domain abstractions/entities and how they relate. e.g., "Users own Workspaces, which contain Projects. Projects have Deployments."]
36
-
37
- **How It's Wired Together:**
38
- - Entry point(s): [exact file paths]
39
- - Request/data flow: [e.g., "Routes → Controllers → Services → Repositories" or "CLI parses commands → each command module handles execution"]
40
- - State management: [where state lives, how it flows]
41
-
42
- **Conventions (match these when writing code):**
43
- - File naming: [e.g., kebab-case, PascalCase for components]
44
- - File organization: [e.g., co-located tests, barrel exports, feature folders]
45
- - Import style: [e.g., path aliases like @/, relative imports, barrel re-exports]
46
- - Error handling pattern: [e.g., Result types, thrown exceptions, error boundaries]
47
- - Any other strong patterns the agent should follow
48
-
49
- **Commands:**
50
- - Build: [exact command]
51
- - Test: [exact command, plus how to run a single test]
52
- - Dev/Run: [exact command]
53
- - Lint/Format: [exact command, if applicable]
54
-
55
- **Gotchas:** [Non-obvious things that would trip up a newcomer: required env vars, unusual build steps, files that are auto-generated and shouldn't be edited, quirky patterns, etc.]
56
-
57
- Only include sections where you have concrete information. Omit rather than guess.`;
58
-
59
- function debugLog(message) {
60
- if (!DEBUG_ENABLED) return;
61
- try {
62
- const dir = path.dirname(DEBUG_LOG_FILE);
63
- if (!fs.existsSync(dir)) {
64
- fs.mkdirSync(dir, { recursive: true });
65
- }
66
- const stamp = new Date().toISOString();
67
- fs.appendFileSync(DEBUG_LOG_FILE, `[${stamp}] ${message}\n`);
68
- } catch (e) {
69
- // Ignore errors
70
- }
71
- }
72
-
73
- function readHookInput() {
74
- return new Promise((resolve) => {
75
- let data = "";
76
- process.stdin.setEncoding("utf8");
77
- process.stdin.on("data", (chunk) => {
78
- data += chunk;
79
- });
80
- process.stdin.on("end", () => {
81
- if (!data.trim()) {
82
- debugLog("No input data received from stdin");
83
- resolve(null);
84
- return;
85
- }
86
- try {
87
- const parsed = JSON.parse(data);
88
- debugLog(`Received hook input: ${JSON.stringify(parsed, null, 2)}`);
89
- resolve(parsed);
90
- } catch (e) {
91
- debugLog(`Failed to decode JSON: ${e.message}`);
92
- resolve(null);
93
- }
94
- });
95
- });
96
- }
97
-
98
- function waitForWatchReady(sessionId) {
99
- const pidFile = path.join(os.tmpdir(), `sgrep-watch-pid-${sessionId}.txt`);
100
- debugLog(`Waiting for watch process (PID file: ${pidFile})`);
101
-
102
- const startTime = Date.now();
103
- while (Date.now() - startTime < WATCH_READY_WAIT_MS) {
104
- if (fs.existsSync(pidFile)) {
105
- const pid = fs.readFileSync(pidFile, "utf8").trim();
106
- if (pid && pid !== "0") {
107
- debugLog(`Watch process ready with PID: ${pid}`);
108
- return true;
109
- }
110
- }
111
- // Small sync sleep
112
- const waitUntil = Date.now() + 200;
113
- while (Date.now() < waitUntil) {
114
- // Busy wait (not ideal but works for short durations)
115
- }
116
- }
117
-
118
- debugLog("Watch process not ready within timeout");
119
- return false;
120
- }
121
-
122
- function waitForIndexReady(sessionId) {
123
- // Watch writes a ready marker file when indexing completes
124
- // The file is named sgrep-watch-ready-{sessionId}.txt (derived from PID file)
125
- const readyFile = path.join(os.tmpdir(), `sgrep-watch-ready-${sessionId}.txt`);
126
- debugLog(`Waiting for index ready (ready file: ${readyFile})`);
127
-
128
- const startTime = Date.now();
129
-
130
- while (Date.now() - startTime < INDEX_READY_WAIT_MS) {
131
- if (fs.existsSync(readyFile)) {
132
- try {
133
- const content = fs.readFileSync(readyFile, "utf8").trim();
134
- debugLog(`Index ready! Files indexed: ${content}`);
135
- return true;
136
- } catch (e) {
137
- debugLog(`Error reading ready file: ${e.message}`);
138
- }
139
- }
140
-
141
- // Small sync sleep - check every 200ms
142
- const waitUntil = Date.now() + 200;
143
- while (Date.now() < waitUntil) {
144
- // Busy wait
145
- }
146
- }
147
-
148
- debugLog("Index ready timeout - proceeding anyway");
149
- return false;
150
- }
151
-
152
- function runSgrepAsk(cwd) {
153
- debugLog("Running sgrep ask for project summary");
154
-
155
- try {
156
- // Find sgrep executable
157
- const isWindows = process.platform === "win32";
158
- let sgrepPath = "sgrep";
159
-
160
- try {
161
- const whichCmd = isWindows ? "where sgrep" : "which sgrep";
162
- sgrepPath = execSync(whichCmd, { encoding: "utf8", timeout: 5000 })
163
- .trim()
164
- .split(/\r?\n/)[0];
165
- debugLog(`Found sgrep at: ${sgrepPath}`);
166
- } catch (e) {
167
- debugLog("Could not find sgrep in PATH, using 'sgrep' directly");
168
- }
169
-
170
- // Run sgrep ask with the structured prompt
171
- const result = execSync(`"${sgrepPath}" ask "${PROJECT_SUMMARY_PROMPT.replace(/"/g, '\\"')}"`, {
172
- encoding: "utf8",
173
- timeout: QUERY_TIMEOUT_MS,
174
- cwd,
175
- stdio: ["pipe", "pipe", "pipe"],
176
- });
177
-
178
- debugLog(`sgrep ask completed, output length: ${result.length}`);
179
- return result.trim();
180
- } catch (e) {
181
- debugLog(`sgrep ask failed: ${e.message}`);
182
- return null;
183
- }
184
- }
185
-
186
- function getFallbackInstruction() {
187
- return `<action>Execute via Bash: sgrep "What is the tech stack, frameworks, and architecture of this project?"</action>`;
188
- }
189
-
190
- async function main() {
191
- debugLog("\n====================================================");
192
- debugLog("SGREP PROJECT SUMMARY HOOK TRIGGERED");
193
- debugLog("====================================================");
194
-
195
- const payload = await readHookInput();
196
- if (!payload) {
197
- debugLog("ERROR: No payload received - exiting with error");
198
- process.exit(1);
199
- }
200
-
201
- const eventName = payload.hook_event_name || "unknown";
202
- const source = payload.source || "unknown";
203
- const cwd = payload.cwd || process.cwd();
204
- const sessionId = payload.session_id || "default";
205
-
206
- debugLog(`Event: ${eventName}, Source: ${source}`);
207
- debugLog(`CWD: ${cwd}`);
208
-
209
- // Wait for watch process to be ready
210
- const watchReady = waitForWatchReady(sessionId);
211
- if (!watchReady) {
212
- debugLog("Watch not ready, outputting fallback instruction");
213
- console.log(getFallbackInstruction());
214
- process.exit(0);
215
- }
216
-
217
- // Wait for index to be built by monitoring watch output
218
- const indexReady = waitForIndexReady(sessionId);
219
- if (!indexReady) {
220
- debugLog("Index ready timeout, but proceeding with query anyway");
221
- }
222
-
223
- // Run sgrep ask
224
- const summary = runSgrepAsk(cwd);
225
- if (summary) {
226
- console.log(`[Project Context]\n${summary}`);
227
- } else {
228
- debugLog("Query failed, outputting fallback instruction");
229
- console.log(getFallbackInstruction());
230
- }
231
-
232
- debugLog("====================================================\n");
233
- process.exit(0);
234
- }
235
-
236
- main();
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * sgrep_tool_reminder.js - Friendly reminder when search tools are used
5
- *
6
- * Runs on PreToolUse for Grep and Glob to nudge the agent toward using
7
- * the sgrep skill for codebase navigation and understanding.
8
- *
9
- * Does NOT block the tool - just injects a reminder as context.
10
- */
11
-
12
- const REMINDER = [
13
- "Tip: For codebase navigation and understanding, the sgrep skill provides",
14
- "semantic search that finds code by meaning rather than patterns.",
15
- "Use the Skill tool with skill=\"sgrep\" for questions about architecture,",
16
- "finding implementations, or understanding how systems work.",
17
- "Grep/Glob are fine for exact-match lookups - but sgrep saves time",
18
- "when you're exploring or need to understand functionality."
19
- ].join(" ");
20
-
21
- function main() {
22
- let data = "";
23
- process.stdin.setEncoding("utf8");
24
- process.stdin.on("data", (chunk) => {
25
- data += chunk;
26
- });
27
- process.stdin.on("end", () => {
28
- // Output reason without decision - tool proceeds, model sees the hint
29
- console.log(JSON.stringify({ reason: REMINDER }));
30
- process.exit(0);
31
- });
32
- }
33
-
34
- main();