@pimzino/sgrep 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +413 -0
  2. package/build/.claude-plugin/marketplace.json +20 -0
  3. package/build/commands/ask.d.ts +11 -0
  4. package/build/commands/ask.d.ts.map +1 -0
  5. package/build/commands/ask.js +65 -0
  6. package/build/commands/ask.js.map +1 -0
  7. package/build/commands/enhance.d.ts +16 -0
  8. package/build/commands/enhance.d.ts.map +1 -0
  9. package/build/commands/enhance.js +107 -0
  10. package/build/commands/enhance.js.map +1 -0
  11. package/build/commands/search.d.ts +12 -0
  12. package/build/commands/search.d.ts.map +1 -0
  13. package/build/commands/search.js +65 -0
  14. package/build/commands/search.js.map +1 -0
  15. package/build/commands/watch.d.ts +13 -0
  16. package/build/commands/watch.d.ts.map +1 -0
  17. package/build/commands/watch.js +179 -0
  18. package/build/commands/watch.js.map +1 -0
  19. package/build/index.d.ts +3 -0
  20. package/build/index.d.ts.map +1 -0
  21. package/build/index.js +124 -0
  22. package/build/index.js.map +1 -0
  23. package/build/install/claude-code.d.ts +10 -0
  24. package/build/install/claude-code.d.ts.map +1 -0
  25. package/build/install/claude-code.js +255 -0
  26. package/build/install/claude-code.js.map +1 -0
  27. package/build/install/codex.d.ts +4 -0
  28. package/build/install/codex.d.ts.map +1 -0
  29. package/build/install/codex.js +123 -0
  30. package/build/install/codex.js.map +1 -0
  31. package/build/install/cursor.d.ts +4 -0
  32. package/build/install/cursor.d.ts.map +1 -0
  33. package/build/install/cursor.js +91 -0
  34. package/build/install/cursor.js.map +1 -0
  35. package/build/install/droid.d.ts +4 -0
  36. package/build/install/droid.d.ts.map +1 -0
  37. package/build/install/droid.js +226 -0
  38. package/build/install/droid.js.map +1 -0
  39. package/build/install/opencode.d.ts +4 -0
  40. package/build/install/opencode.d.ts.map +1 -0
  41. package/build/install/opencode.js +276 -0
  42. package/build/install/opencode.js.map +1 -0
  43. package/build/plugins/sgrep/.claude-plugin/plugin.json +9 -0
  44. package/build/plugins/sgrep/hooks/hooks.json +39 -0
  45. package/build/plugins/sgrep/hooks/sgrep_enhance.js +160 -0
  46. package/build/plugins/sgrep/hooks/sgrep_watch.js +240 -0
  47. package/build/plugins/sgrep/hooks/sgrep_watch_kill.js +158 -0
  48. package/build/plugins/sgrep/skills/sgrep/SKILL.md +97 -0
  49. package/build/utils/config.d.ts +26 -0
  50. package/build/utils/config.d.ts.map +1 -0
  51. package/build/utils/config.js +106 -0
  52. package/build/utils/config.js.map +1 -0
  53. package/build/utils/context-manager.d.ts +33 -0
  54. package/build/utils/context-manager.d.ts.map +1 -0
  55. package/build/utils/context-manager.js +244 -0
  56. package/build/utils/context-manager.js.map +1 -0
  57. package/build/utils/context.d.ts +25 -0
  58. package/build/utils/context.d.ts.map +1 -0
  59. package/build/utils/context.js +132 -0
  60. package/build/utils/context.js.map +1 -0
  61. package/build/utils/file-walker.d.ts +20 -0
  62. package/build/utils/file-walker.d.ts.map +1 -0
  63. package/build/utils/file-walker.js +239 -0
  64. package/build/utils/file-walker.js.map +1 -0
  65. package/build/utils/output.d.ts +38 -0
  66. package/build/utils/output.d.ts.map +1 -0
  67. package/build/utils/output.js +150 -0
  68. package/build/utils/output.js.map +1 -0
  69. package/build/utils/prompt-parser.d.ts +12 -0
  70. package/build/utils/prompt-parser.d.ts.map +1 -0
  71. package/build/utils/prompt-parser.js +54 -0
  72. package/build/utils/prompt-parser.js.map +1 -0
  73. package/package.json +59 -0
@@ -0,0 +1,160 @@
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
+ function debugLog(message) {
12
+ if (!DEBUG_ENABLED) return;
13
+ try {
14
+ const dir = path.dirname(DEBUG_LOG_FILE);
15
+ if (!fs.existsSync(dir)) {
16
+ fs.mkdirSync(dir, { recursive: true });
17
+ }
18
+ const stamp = new Date().toISOString();
19
+ fs.appendFileSync(DEBUG_LOG_FILE, `[${stamp}] ${message}\n`);
20
+ } catch (e) {
21
+ // Ignore errors
22
+ }
23
+ }
24
+
25
+ function debugLogSection(title, content) {
26
+ debugLog(`\n========== ${title} ==========`);
27
+ debugLog(content);
28
+ debugLog(`========== END ${title} ==========\n`);
29
+ }
30
+
31
+ function readHookInput() {
32
+ return new Promise((resolve) => {
33
+ let data = "";
34
+ process.stdin.setEncoding("utf8");
35
+ process.stdin.on("data", (chunk) => {
36
+ data += chunk;
37
+ });
38
+ process.stdin.on("end", () => {
39
+ if (!data.trim()) {
40
+ debugLog("No input data received from stdin");
41
+ resolve(null);
42
+ return;
43
+ }
44
+ try {
45
+ const parsed = JSON.parse(data);
46
+ debugLogSection("RAW HOOK INPUT", JSON.stringify(parsed, null, 2));
47
+ resolve(parsed);
48
+ } catch (e) {
49
+ debugLog(`Failed to decode JSON: ${e.message}`);
50
+ debugLog(`Raw data: ${data}`);
51
+ resolve(null);
52
+ }
53
+ });
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Determine if enhancement should proceed.
59
+ * Auto-confirms on all platforms (no user prompting).
60
+ */
61
+ function shouldEnhance(prompt) {
62
+ debugLog("Auto-confirming enhancement (prompting disabled on all platforms)");
63
+ return true;
64
+ }
65
+
66
+ async function main() {
67
+ debugLog("\n\n====================================================");
68
+ debugLog("SGREP ENHANCE HOOK TRIGGERED - UserPromptSubmit");
69
+ debugLog("====================================================");
70
+
71
+ const payload = await readHookInput();
72
+ if (!payload) {
73
+ debugLog("ERROR: No payload received - exiting");
74
+ process.exit(0);
75
+ }
76
+
77
+ // Log all payload fields
78
+ debugLog(`Hook Event: ${payload.hook_event_name || 'unknown'}`);
79
+ debugLog(`Session ID: ${payload.session_id || 'unknown'}`);
80
+ debugLog(`CWD: ${payload.cwd || 'unknown'}`);
81
+ debugLog(`Permission Mode: ${payload.permission_mode || 'unknown'}`);
82
+
83
+ const prompt = payload.prompt || "";
84
+ debugLogSection("ORIGINAL PROMPT", prompt);
85
+
86
+ // Skip enhancement for very short prompts or commands
87
+ if (prompt.length < 10) {
88
+ debugLog("SKIP: Prompt too short (< 10 chars)");
89
+ process.exit(0);
90
+ }
91
+ if (prompt.startsWith("/")) {
92
+ debugLog("SKIP: Prompt is a slash command");
93
+ process.exit(0);
94
+ }
95
+
96
+ // Check if enhancement should proceed (auto-confirms on all platforms)
97
+ debugLog("Checking if enhancement should proceed...");
98
+ const shouldProceed = shouldEnhance(prompt);
99
+ debugLog(`Enhancement decision: ${shouldProceed ? 'proceeding' : 'skipped'}`);
100
+
101
+ if (!shouldProceed) {
102
+ debugLog("SKIP: Enhancement not proceeding");
103
+ process.exit(0);
104
+ }
105
+
106
+ try {
107
+ // Call sgrep enhance with JSON output
108
+ debugLog("Calling: sgrep enhance --json \"<prompt>\"");
109
+
110
+ // Escape the prompt for shell
111
+ const escapedPrompt = prompt.replace(/"/g, '\\"').replace(/\n/g, ' ');
112
+
113
+ const result = execSync(`sgrep enhance --json "${escapedPrompt}"`, {
114
+ encoding: "utf8",
115
+ timeout: 60000, // 60 second timeout
116
+ stdio: ["pipe", "pipe", "pipe"],
117
+ });
118
+
119
+ debugLogSection("SGREP ENHANCE RAW RESULT", result);
120
+
121
+ // Parse the JSON result
122
+ const parsed = JSON.parse(result);
123
+ const enhanced = parsed.enhanced || null;
124
+
125
+ if (!enhanced) {
126
+ debugLog("SKIP: No enhanced prompt in result");
127
+ process.exit(0);
128
+ }
129
+
130
+ if (enhanced === prompt) {
131
+ debugLog("SKIP: Enhanced prompt same as original");
132
+ process.exit(0);
133
+ }
134
+
135
+ debugLogSection("ENHANCED PROMPT", enhanced);
136
+
137
+ // Output JSON with additionalContext - this adds context to Claude
138
+ const response = {
139
+ hookSpecificOutput: {
140
+ hookEventName: "UserPromptSubmit",
141
+ additionalContext: `[sgrep enhanced context]\n${enhanced}`
142
+ }
143
+ };
144
+
145
+ debugLogSection("HOOK RESPONSE", JSON.stringify(response, null, 2));
146
+ console.log(JSON.stringify(response));
147
+ debugLog("SUCCESS: Enhancement complete");
148
+ } catch (e) {
149
+ debugLog(`ERROR: Enhancement failed - ${e.message}`);
150
+ if (e.stderr) {
151
+ debugLog(`STDERR: ${e.stderr}`);
152
+ }
153
+ // On any error, just continue without enhancement
154
+ }
155
+
156
+ debugLog("====================================================\n");
157
+ process.exit(0);
158
+ }
159
+
160
+ main();
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawn, 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_WATCH_LOG || path.join(os.tmpdir(), "sgrep-watch.log");
10
+
11
+ function debugLog(message) {
12
+ if (!DEBUG_ENABLED) return;
13
+ try {
14
+ const dir = path.dirname(DEBUG_LOG_FILE);
15
+ if (!fs.existsSync(dir)) {
16
+ fs.mkdirSync(dir, { recursive: true });
17
+ }
18
+ const stamp = new Date().toISOString();
19
+ fs.appendFileSync(DEBUG_LOG_FILE, `[${stamp}] ${message}\n`);
20
+ } catch (e) {
21
+ // Ignore errors
22
+ }
23
+ }
24
+
25
+ function debugLogSection(title, content) {
26
+ debugLog(`\n========== ${title} ==========`);
27
+ debugLog(content);
28
+ debugLog(`========== END ${title} ==========\n`);
29
+ }
30
+
31
+ function readHookInput() {
32
+ return new Promise((resolve) => {
33
+ let data = "";
34
+ process.stdin.setEncoding("utf8");
35
+ process.stdin.on("data", (chunk) => {
36
+ data += chunk;
37
+ });
38
+ process.stdin.on("end", () => {
39
+ if (!data.trim()) {
40
+ debugLog("No input data received from stdin");
41
+ resolve(null);
42
+ return;
43
+ }
44
+ try {
45
+ const parsed = JSON.parse(data);
46
+ debugLogSection("RAW HOOK INPUT", JSON.stringify(parsed, null, 2));
47
+ resolve(parsed);
48
+ } catch (e) {
49
+ debugLog(`Failed to decode JSON: ${e.message}`);
50
+ debugLog(`Raw data: ${data}`);
51
+ resolve(null);
52
+ }
53
+ });
54
+ });
55
+ }
56
+
57
+ async function main() {
58
+ debugLog("\n\n====================================================");
59
+ debugLog("SGREP WATCH HOOK TRIGGERED - SessionStart");
60
+ debugLog("====================================================");
61
+
62
+ const payload = await readHookInput();
63
+ if (!payload) {
64
+ debugLog("ERROR: No payload received - exiting with error");
65
+ process.exit(1);
66
+ }
67
+
68
+ // Log all payload fields for debugging
69
+ debugLog(`Hook Event: ${payload.hook_event_name || 'unknown'}`);
70
+ debugLog(`Session ID: ${payload.session_id || 'unknown'}`);
71
+ debugLog(`Source: ${payload.source || 'unknown'} (startup/resume/clear/compact)`);
72
+ debugLog(`CWD: ${payload.cwd || 'unknown'}`);
73
+ debugLog(`Permission Mode: ${payload.permission_mode || 'unknown'}`);
74
+ debugLog(`Transcript Path: ${payload.transcript_path || 'unknown'}`);
75
+ debugLog(`Model: ${payload.model || 'unknown'}`);
76
+ if (payload.agent_type) {
77
+ debugLog(`Agent Type: ${payload.agent_type}`);
78
+ }
79
+
80
+ const sessionId = payload.session_id || "default";
81
+ const pidFile = path.join(os.tmpdir(), `sgrep-watch-pid-${sessionId}.txt`);
82
+ const logFile = path.join(os.tmpdir(), `sgrep-watch-command-${sessionId}.log`);
83
+
84
+ debugLog(`PID file path: ${pidFile}`);
85
+ debugLog(`Log file path: ${logFile}`);
86
+
87
+ if (fs.existsSync(pidFile)) {
88
+ const existingPid = fs.readFileSync(pidFile, "utf8").trim();
89
+ debugLog(`SKIP: PID file already exists with PID: ${existingPid}`);
90
+ // Check if process is still running
91
+ if (existingPid && existingPid !== "0") {
92
+ try {
93
+ process.kill(parseInt(existingPid, 10), 0); // Signal 0 = check if process exists
94
+ debugLog(`Process ${existingPid} is still running`);
95
+ } catch (e) {
96
+ debugLog(`Process ${existingPid} is not running, will restart`);
97
+ fs.unlinkSync(pidFile);
98
+ // Fall through to start new process
99
+ }
100
+ }
101
+ if (fs.existsSync(pidFile)) {
102
+ // Still exists, so process is running
103
+ const response = {
104
+ hookSpecificOutput: {
105
+ hookEventName: "SessionStart",
106
+ additionalContext: "The sgrep semantic search tool is available. Use `sgrep \"query\"` for semantic code search, `sgrep ask \"question\"` for explanations, and `sgrep enhance \"prompt\"` to improve vague prompts.",
107
+ },
108
+ };
109
+ console.log(JSON.stringify(response));
110
+ process.exit(0);
111
+ }
112
+ }
113
+
114
+ // Start sgrep watch as a detached process
115
+ debugLog("Starting sgrep watch process...");
116
+
117
+ // Check if sgrep is available
118
+ let sgrepPath = "sgrep";
119
+ const isWindows = process.platform === "win32";
120
+
121
+ try {
122
+ // On Windows, we need to find sgrep.cmd specifically
123
+ const whichCmd = isWindows ? "where sgrep.cmd" : "which sgrep";
124
+ const whichResult = execSync(whichCmd, {
125
+ encoding: "utf8",
126
+ timeout: 5000,
127
+ }).trim().split(/\r?\n/)[0];
128
+ sgrepPath = whichResult;
129
+ debugLog(`Found sgrep at: ${sgrepPath}`);
130
+ } catch (e) {
131
+ debugLog(`Could not find sgrep.cmd, trying 'where sgrep'...`);
132
+ // Fallback to finding sgrep without extension
133
+ try {
134
+ const whichResult = execSync(isWindows ? "where sgrep" : "which sgrep", {
135
+ encoding: "utf8",
136
+ timeout: 5000,
137
+ }).trim().split(/\r?\n/)[0];
138
+
139
+ // On Windows, if we got a path without extension, add .cmd
140
+ if (isWindows && !whichResult.toLowerCase().endsWith(".cmd") && !whichResult.toLowerCase().endsWith(".exe")) {
141
+ sgrepPath = whichResult + ".cmd";
142
+ debugLog(`Found sgrep at: ${whichResult}, using: ${sgrepPath}`);
143
+ } else {
144
+ sgrepPath = whichResult;
145
+ debugLog(`Found sgrep at: ${sgrepPath}`);
146
+ }
147
+ } catch (e2) {
148
+ debugLog(`WARNING: Could not find sgrep in PATH, using 'sgrep' directly`);
149
+ }
150
+ }
151
+
152
+ try {
153
+ const out = fs.openSync(logFile, "w");
154
+ const isWindows = process.platform === "win32";
155
+
156
+ // Use --pid-file option to have sgrep write its own PID
157
+ // This gives us the actual Node.js process PID, not the cmd.exe wrapper
158
+ const args = ["watch", "--pid-file", pidFile];
159
+ debugLog(`Spawning: ${sgrepPath} ${args.join(" ")}`);
160
+
161
+ let child;
162
+ if (isWindows) {
163
+ // On Windows, we need to handle .cmd/.bat scripts differently
164
+ const isCmdScript = sgrepPath.toLowerCase().endsWith(".cmd") ||
165
+ sgrepPath.toLowerCase().endsWith(".bat");
166
+
167
+ if (isCmdScript) {
168
+ debugLog(`sgrep is a batch script: ${sgrepPath}`);
169
+ child = spawn("cmd.exe", ["/c", sgrepPath, ...args], {
170
+ detached: true,
171
+ stdio: ["ignore", out, out],
172
+ windowsHide: true,
173
+ });
174
+ } else {
175
+ child = spawn(sgrepPath, args, {
176
+ detached: true,
177
+ stdio: ["ignore", out, out],
178
+ windowsHide: true,
179
+ });
180
+ }
181
+ } else {
182
+ child = spawn(sgrepPath, args, {
183
+ detached: true,
184
+ stdio: ["ignore", out, out],
185
+ });
186
+ }
187
+
188
+ debugLog(`Spawn returned wrapper PID: ${child.pid}`);
189
+
190
+ // Handle any spawn errors
191
+ child.on("error", (err) => {
192
+ debugLog(`Spawn error event: ${err.message}`);
193
+ });
194
+
195
+ // Unref so this script can exit
196
+ child.unref();
197
+
198
+ // Wait a moment for sgrep to write its PID file
199
+ debugLog("Waiting for sgrep to write its PID...");
200
+ let actualPid = null;
201
+ for (let i = 0; i < 20; i++) { // Wait up to 2 seconds
202
+ await new Promise(resolve => setTimeout(resolve, 100));
203
+ if (fs.existsSync(pidFile)) {
204
+ const content = fs.readFileSync(pidFile, "utf8").trim();
205
+ if (content && content !== "0") {
206
+ actualPid = content;
207
+ break;
208
+ }
209
+ }
210
+ }
211
+
212
+ if (actualPid) {
213
+ debugLog(`SUCCESS: sgrep watch started with PID: ${actualPid}`);
214
+ } else {
215
+ debugLog(`WARNING: sgrep watch may not have started properly - no PID file found`);
216
+ }
217
+
218
+ // Log the command output file location
219
+ debugLog(`sgrep watch output will be logged to: ${logFile}`);
220
+ } catch (e) {
221
+ debugLog(`ERROR: Failed to start sgrep watch - ${e.message}`);
222
+ if (e.stack) {
223
+ debugLog(`Stack: ${e.stack}`);
224
+ }
225
+ }
226
+
227
+ const response = {
228
+ hookSpecificOutput: {
229
+ hookEventName: "SessionStart",
230
+ additionalContext: "The sgrep semantic search tool is available. Use `sgrep \"query\"` for semantic code search, `sgrep ask \"question\"` for explanations, and `sgrep enhance \"prompt\"` to improve vague prompts.",
231
+ },
232
+ };
233
+
234
+ debugLogSection("HOOK RESPONSE", JSON.stringify(response, null, 2));
235
+ console.log(JSON.stringify(response));
236
+ debugLog("====================================================\n");
237
+ process.exit(0);
238
+ }
239
+
240
+ main();
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+ const { execSync } = require("child_process");
7
+
8
+ const DEBUG_ENABLED = process.env.SGREP_DEBUG === "1" || process.env.SGREP_DEBUG === "true";
9
+ const DEBUG_LOG_FILE = process.env.SGREP_WATCH_KILL_LOG || path.join(os.tmpdir(), "sgrep-watch-kill.log");
10
+
11
+ function debugLog(message) {
12
+ if (!DEBUG_ENABLED) return;
13
+ try {
14
+ const dir = path.dirname(DEBUG_LOG_FILE);
15
+ if (!fs.existsSync(dir)) {
16
+ fs.mkdirSync(dir, { recursive: true });
17
+ }
18
+ const stamp = new Date().toISOString();
19
+ fs.appendFileSync(DEBUG_LOG_FILE, `[${stamp}] ${message}\n`);
20
+ } catch (e) {
21
+ // Ignore errors
22
+ }
23
+ }
24
+
25
+ function debugLogSection(title, content) {
26
+ debugLog(`\n========== ${title} ==========`);
27
+ debugLog(content);
28
+ debugLog(`========== END ${title} ==========\n`);
29
+ }
30
+
31
+ /**
32
+ * Kill a process by PID, with platform-specific handling
33
+ */
34
+ function killProcess(pid) {
35
+ debugLog(`Attempting to kill process with PID: ${pid}`);
36
+
37
+ // First try Node.js process.kill
38
+ try {
39
+ process.kill(pid, "SIGTERM");
40
+ debugLog(`SUCCESS: Sent SIGTERM to process ${pid}`);
41
+ return true;
42
+ } catch (e) {
43
+ if (e.code === "ESRCH") {
44
+ debugLog(`INFO: Process ${pid} already exited`);
45
+ return true; // Consider success if already gone
46
+ }
47
+ debugLog(`process.kill failed: ${e.message} (code: ${e.code})`);
48
+ }
49
+
50
+ // On Windows, try taskkill as fallback
51
+ if (process.platform === "win32") {
52
+ try {
53
+ execSync(`taskkill /PID ${pid} /F`, {
54
+ encoding: "utf8",
55
+ timeout: 5000,
56
+ stdio: ["pipe", "pipe", "pipe"],
57
+ });
58
+ debugLog(`SUCCESS: Killed process ${pid} using taskkill`);
59
+ return true;
60
+ } catch (e) {
61
+ debugLog(`taskkill failed: ${e.message}`);
62
+ }
63
+ }
64
+
65
+ return false;
66
+ }
67
+
68
+ function readHookInput() {
69
+ return new Promise((resolve) => {
70
+ let data = "";
71
+ process.stdin.setEncoding("utf8");
72
+ process.stdin.on("data", (chunk) => {
73
+ data += chunk;
74
+ });
75
+ process.stdin.on("end", () => {
76
+ if (!data.trim()) {
77
+ debugLog("No input data received from stdin");
78
+ resolve(null);
79
+ return;
80
+ }
81
+ try {
82
+ const parsed = JSON.parse(data);
83
+ debugLogSection("RAW HOOK INPUT", JSON.stringify(parsed, null, 2));
84
+ resolve(parsed);
85
+ } catch (e) {
86
+ debugLog(`Failed to decode JSON: ${e.message}`);
87
+ debugLog(`Raw data: ${data}`);
88
+ resolve(null);
89
+ }
90
+ });
91
+ });
92
+ }
93
+
94
+ async function main() {
95
+ debugLog("\n\n====================================================");
96
+ debugLog("SGREP WATCH KILL HOOK TRIGGERED - SessionEnd");
97
+ debugLog("====================================================");
98
+
99
+ const payload = await readHookInput();
100
+ if (!payload) {
101
+ debugLog("ERROR: No payload received - exiting with error");
102
+ process.exit(1);
103
+ }
104
+
105
+ // Log all payload fields for debugging
106
+ debugLog(`Hook Event: ${payload.hook_event_name || 'unknown'}`);
107
+ debugLog(`Session ID: ${payload.session_id || 'unknown'}`);
108
+ debugLog(`Reason: ${payload.reason || 'unknown'} (clear/logout/prompt_input_exit/other)`);
109
+ debugLog(`CWD: ${payload.cwd || 'unknown'}`);
110
+ debugLog(`Permission Mode: ${payload.permission_mode || 'unknown'}`);
111
+ debugLog(`Transcript Path: ${payload.transcript_path || 'unknown'}`);
112
+
113
+ const sessionId = payload.session_id || "default";
114
+ const pidFile = path.join(os.tmpdir(), `sgrep-watch-pid-${sessionId}.txt`);
115
+
116
+ debugLog(`Looking for PID file: ${pidFile}`);
117
+
118
+ if (!fs.existsSync(pidFile)) {
119
+ debugLog(`SKIP: PID file not found - sgrep watch may not have been started`);
120
+ process.exit(0); // Not an error - process may not have been started
121
+ }
122
+
123
+ let killed = false;
124
+
125
+ try {
126
+ const pidContent = fs.readFileSync(pidFile, "utf8").trim();
127
+ debugLog(`PID file content: "${pidContent}"`);
128
+
129
+ const pid = parseInt(pidContent, 10);
130
+ debugLog(`Parsed PID: ${pid}`);
131
+
132
+ if (!isNaN(pid) && pid > 0) {
133
+ killed = killProcess(pid);
134
+ } else {
135
+ debugLog(`WARNING: Invalid PID (${pid}), cannot kill process`);
136
+ }
137
+ } catch (e) {
138
+ debugLog(`ERROR reading PID file: ${e.message}`);
139
+ }
140
+
141
+ // Clean up PID file (sgrep watch should also clean it up on exit, but be safe)
142
+ try {
143
+ if (fs.existsSync(pidFile)) {
144
+ fs.unlinkSync(pidFile);
145
+ debugLog(`SUCCESS: Removed PID file: ${pidFile}`);
146
+ }
147
+ } catch (cleanupErr) {
148
+ debugLog(`Failed to clean up PID file: ${cleanupErr.message}`);
149
+ }
150
+
151
+ debugLog(`Kill operation completed. Success: ${killed}`);
152
+
153
+
154
+ debugLog("====================================================\n");
155
+ process.exit(0);
156
+ }
157
+
158
+ main();
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: sgrep
3
+ description: "Semantic grep - search codebases by meaning using AI. Use this skill when you need to search for code by functionality, understand codebase architecture, or enhance prompts with codebase context. Replaces pattern-based grep with natural language understanding."
4
+ ---
5
+
6
+ ## Overview
7
+
8
+ sgrep (semantic grep) is a powerful CLI tool that uses AI to search codebases by meaning rather than exact text patterns. It understands what code does, not just what it's named.
9
+
10
+ ## CRITICAL: How to Run sgrep
11
+
12
+ **ALWAYS run sgrep from the project root directory. NEVER use `cd` before running sgrep.**
13
+
14
+ sgrep automatically searches the entire codebase from your current working directory. Using `cd` to navigate into subdirectories before running sgrep will limit your search scope and miss relevant code.
15
+
16
+ ```bash
17
+ # CORRECT - run from project root
18
+ sgrep "authentication middleware"
19
+ sgrep ask "how does routing work?"
20
+
21
+ # WRONG - do NOT cd into subdirectories
22
+ cd src/frontend && sgrep "routing" # BAD: limits search scope
23
+ cd api && sgrep "authentication" # BAD: misses related code elsewhere
24
+ ```
25
+
26
+ ## When to Use This Skill
27
+
28
+ Invoke this skill when:
29
+
30
+ - User asks to search for code by functionality (e.g., "find authentication code")
31
+ - User wants to understand how something works in the codebase
32
+ - User needs to find related files across the codebase
33
+ - User wants to enhance a vague prompt with codebase context
34
+ - You need to explore unfamiliar code before making changes
35
+
36
+ ## Commands
37
+
38
+ ### Search - Find code by meaning
39
+
40
+ ```bash
41
+ sgrep "authentication middleware"
42
+ sgrep "database connection handling"
43
+ sgrep "error handling patterns"
44
+ ```
45
+
46
+ Use natural language to describe what you're looking for. Returns file paths and relevant code snippets from the entire codebase.
47
+
48
+ ### Ask - Get explanations about the codebase
49
+
50
+ ```bash
51
+ sgrep ask "how does the authentication system work?"
52
+ sgrep ask "what patterns are used for API endpoints?"
53
+ ```
54
+
55
+ Ask questions in natural language and get synthesized explanations with code references.
56
+
57
+ ### Enhance - Improve prompts with codebase context
58
+
59
+ ```bash
60
+ sgrep enhance "fix the bug"
61
+ sgrep enhance "add authentication"
62
+ ```
63
+
64
+ Takes a vague prompt and enhances it using codebase context to make it clearer, more specific, and actionable.
65
+
66
+ ## Examples
67
+
68
+ ### Good Usage
69
+
70
+ ```bash
71
+ sgrep "user login handling"
72
+ sgrep "HashRouter configuration"
73
+ sgrep "React Router setup"
74
+ sgrep ask "how does the caching layer work?"
75
+ sgrep ask "what is the routing architecture?"
76
+ ```
77
+
78
+ ### Avoid
79
+
80
+ ```bash
81
+ cd src && sgrep "query" # WRONG: don't cd first
82
+ sgrep "function" # Too vague, be more specific
83
+ sgrep "const" # Use regular grep for exact text matching
84
+ ```
85
+
86
+ ## Tips
87
+
88
+ - **Run from project root** - sgrep searches the entire codebase automatically
89
+ - **Never use cd** - it limits your search and misses related code
90
+ - Use descriptive natural language queries
91
+ - For exact text matching, use regular grep instead
92
+ - Use `sgrep ask` when you need explanations, not just code snippets
93
+
94
+ ## Keywords
95
+
96
+ semantic search, code search, grep, files, local files, codebase, find code,
97
+ search by meaning, natural language search, code understanding, prompt enhancement
@@ -0,0 +1,26 @@
1
+ export interface SgrepConfig {
2
+ defaultDir?: string;
3
+ maxResults?: number;
4
+ outputFormat?: "pretty" | "json";
5
+ noColor?: boolean;
6
+ debug?: boolean;
7
+ }
8
+ /**
9
+ * Loads configuration with cascading priority:
10
+ * 1. CLI flags (handled by caller)
11
+ * 2. Environment variables
12
+ * 3. Local config file (.sgreprc.json)
13
+ * 4. Global config file (~/.config/sgrep/config.json)
14
+ */
15
+ export declare function loadConfig(workingDir?: string): SgrepConfig;
16
+ /**
17
+ * Merges CLI options with config, CLI options take precedence.
18
+ */
19
+ export declare function mergeWithCliOptions(config: SgrepConfig, cliOptions: {
20
+ dir?: string;
21
+ maxResults?: number;
22
+ json?: boolean;
23
+ noColor?: boolean;
24
+ debug?: boolean;
25
+ }): SgrepConfig;
26
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA4CD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,UAAU,GAAE,MAAsB,GAAG,WAAW,CAuC1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE;IACV,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,WAAW,CAoBb"}