@contextstream/mcp-server 0.4.50 → 0.4.53

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,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
4
10
  var __esm = (fn, res) => function __init() {
5
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
12
  };
@@ -28,6 +34,7 @@ __export(hooks_config_exports, {
28
34
  getClineHooksDir: () => getClineHooksDir,
29
35
  getCursorHooksConfigPath: () => getCursorHooksConfigPath,
30
36
  getCursorHooksDir: () => getCursorHooksDir,
37
+ getHookCommand: () => getHookCommand,
31
38
  getHooksDir: () => getHooksDir,
32
39
  getIndexStatusPath: () => getIndexStatusPath,
33
40
  getKiloCodeHooksDir: () => getKiloCodeHooksDir,
@@ -53,6 +60,23 @@ __export(hooks_config_exports, {
53
60
  import * as fs from "node:fs/promises";
54
61
  import * as path from "node:path";
55
62
  import { homedir } from "node:os";
63
+ import { fileURLToPath } from "node:url";
64
+ function getHookCommand(hookName) {
65
+ const fs3 = __require("node:fs");
66
+ const binaryPath = "/usr/local/bin/contextstream-mcp";
67
+ if (fs3.existsSync(binaryPath)) {
68
+ return `${binaryPath} hook ${hookName}`;
69
+ }
70
+ try {
71
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
72
+ const indexPath = path.join(__dirname, "index.js");
73
+ if (fs3.existsSync(indexPath)) {
74
+ return `node ${indexPath} hook ${hookName}`;
75
+ }
76
+ } catch {
77
+ }
78
+ return `npx @contextstream/mcp-server hook ${hookName}`;
79
+ }
56
80
  function getClaudeSettingsPath(scope, projectPath) {
57
81
  if (scope === "user") {
58
82
  return path.join(homedir(), ".claude", "settings.json");
@@ -72,19 +96,31 @@ function buildHooksConfig(options) {
72
96
  hooks: [
73
97
  {
74
98
  type: "command",
75
- command: "npx @contextstream/mcp-server hook user-prompt-submit",
99
+ command: getHookCommand("user-prompt-submit"),
76
100
  timeout: 5
77
101
  }
78
102
  ]
79
103
  }
80
104
  ];
105
+ if (options?.includeOnSaveIntent !== false) {
106
+ userPromptHooks.push({
107
+ matcher: "*",
108
+ hooks: [
109
+ {
110
+ type: "command",
111
+ command: getHookCommand("on-save-intent"),
112
+ timeout: 5
113
+ }
114
+ ]
115
+ });
116
+ }
81
117
  if (options?.includeMediaAware !== false) {
82
118
  userPromptHooks.push({
83
119
  matcher: "*",
84
120
  hooks: [
85
121
  {
86
122
  type: "command",
87
- command: "npx @contextstream/mcp-server hook media-aware",
123
+ command: getHookCommand("media-aware"),
88
124
  timeout: 5
89
125
  }
90
126
  ]
@@ -97,7 +133,7 @@ function buildHooksConfig(options) {
97
133
  hooks: [
98
134
  {
99
135
  type: "command",
100
- command: "npx @contextstream/mcp-server hook pre-tool-use",
136
+ command: getHookCommand("pre-tool-use"),
101
137
  timeout: 5
102
138
  }
103
139
  ]
@@ -108,12 +144,39 @@ function buildHooksConfig(options) {
108
144
  if (options?.includePreCompact !== false) {
109
145
  config.PreCompact = [
110
146
  {
111
- // Match both manual (/compact) and automatic compaction
112
147
  matcher: "*",
113
148
  hooks: [
114
149
  {
115
150
  type: "command",
116
- command: "npx @contextstream/mcp-server hook pre-compact",
151
+ command: getHookCommand("pre-compact"),
152
+ timeout: 10
153
+ }
154
+ ]
155
+ }
156
+ ];
157
+ }
158
+ if (options?.includeSessionInit !== false) {
159
+ config.SessionStart = [
160
+ {
161
+ matcher: "*",
162
+ hooks: [
163
+ {
164
+ type: "command",
165
+ command: getHookCommand("session-init"),
166
+ timeout: 10
167
+ }
168
+ ]
169
+ }
170
+ ];
171
+ }
172
+ if (options?.includeSessionEnd !== false) {
173
+ config.Stop = [
174
+ {
175
+ matcher: "*",
176
+ hooks: [
177
+ {
178
+ type: "command",
179
+ command: getHookCommand("session-end"),
117
180
  timeout: 10
118
181
  }
119
182
  ]
@@ -127,7 +190,7 @@ function buildHooksConfig(options) {
127
190
  hooks: [
128
191
  {
129
192
  type: "command",
130
- command: "npx @contextstream/mcp-server hook post-write",
193
+ command: getHookCommand("post-write"),
131
194
  timeout: 10
132
195
  }
133
196
  ]
@@ -139,12 +202,60 @@ function buildHooksConfig(options) {
139
202
  hooks: [
140
203
  {
141
204
  type: "command",
142
- command: "npx @contextstream/mcp-server hook auto-rules",
205
+ command: getHookCommand("auto-rules"),
143
206
  timeout: 15
144
207
  }
145
208
  ]
146
209
  });
147
210
  }
211
+ if (options?.includeOnBash !== false) {
212
+ postToolUseHooks.push({
213
+ matcher: "Bash",
214
+ hooks: [
215
+ {
216
+ type: "command",
217
+ command: getHookCommand("on-bash"),
218
+ timeout: 5
219
+ }
220
+ ]
221
+ });
222
+ }
223
+ if (options?.includeOnTask !== false) {
224
+ postToolUseHooks.push({
225
+ matcher: "Task",
226
+ hooks: [
227
+ {
228
+ type: "command",
229
+ command: getHookCommand("on-task"),
230
+ timeout: 5
231
+ }
232
+ ]
233
+ });
234
+ }
235
+ if (options?.includeOnRead !== false) {
236
+ postToolUseHooks.push({
237
+ matcher: "Read|Glob|Grep",
238
+ hooks: [
239
+ {
240
+ type: "command",
241
+ command: getHookCommand("on-read"),
242
+ timeout: 5
243
+ }
244
+ ]
245
+ });
246
+ }
247
+ if (options?.includeOnWeb !== false) {
248
+ postToolUseHooks.push({
249
+ matcher: "WebFetch|WebSearch",
250
+ hooks: [
251
+ {
252
+ type: "command",
253
+ command: getHookCommand("on-web"),
254
+ timeout: 5
255
+ }
256
+ ]
257
+ });
258
+ }
148
259
  if (postToolUseHooks.length > 0) {
149
260
  config.PostToolUse = postToolUseHooks;
150
261
  }
@@ -154,17 +265,17 @@ async function installHookScripts(options) {
154
265
  const hooksDir = getHooksDir();
155
266
  await fs.mkdir(hooksDir, { recursive: true });
156
267
  const result = {
157
- preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
158
- userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
268
+ preToolUse: getHookCommand("pre-tool-use"),
269
+ userPrompt: getHookCommand("user-prompt-submit")
159
270
  };
160
271
  if (options?.includePreCompact !== false) {
161
- result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
272
+ result.preCompact = getHookCommand("pre-compact");
162
273
  }
163
274
  if (options?.includeMediaAware !== false) {
164
- result.mediaAware = "npx @contextstream/mcp-server hook media-aware";
275
+ result.mediaAware = getHookCommand("media-aware");
165
276
  }
166
277
  if (options?.includeAutoRules !== false) {
167
- result.autoRules = "npx @contextstream/mcp-server hook auto-rules";
278
+ result.autoRules = getHookCommand("auto-rules");
168
279
  }
169
280
  return result;
170
281
  }
@@ -200,20 +311,20 @@ function mergeHooksIntoSettings(existingSettings, newHooks) {
200
311
  async function installClaudeCodeHooks(options) {
201
312
  const result = { scripts: [], settings: [] };
202
313
  result.scripts.push(
203
- "npx @contextstream/mcp-server hook pre-tool-use",
204
- "npx @contextstream/mcp-server hook user-prompt-submit"
314
+ getHookCommand("pre-tool-use"),
315
+ getHookCommand("user-prompt-submit")
205
316
  );
206
317
  if (options.includePreCompact !== false) {
207
- result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
318
+ result.scripts.push(getHookCommand("pre-compact"));
208
319
  }
209
320
  if (options.includeMediaAware !== false) {
210
- result.scripts.push("npx @contextstream/mcp-server hook media-aware");
321
+ result.scripts.push(getHookCommand("media-aware"));
211
322
  }
212
323
  if (options.includePostWrite !== false) {
213
- result.scripts.push("npx @contextstream/mcp-server hook post-write");
324
+ result.scripts.push(getHookCommand("post-write"));
214
325
  }
215
326
  if (options.includeAutoRules !== false) {
216
- result.scripts.push("npx @contextstream/mcp-server hook auto-rules");
327
+ result.scripts.push(getHookCommand("auto-rules"));
217
328
  }
218
329
  const hooksConfig = buildHooksConfig({
219
330
  includePreCompact: options.includePreCompact,
@@ -491,6 +602,8 @@ async function installCursorHookScripts(options) {
491
602
  };
492
603
  const filteredPreToolUse = filterContextStreamHooks(existingConfig.hooks.preToolUse);
493
604
  const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
605
+ const preToolUseCommand = getHookCommand("pre-tool-use");
606
+ const userPromptCommand = getHookCommand("user-prompt-submit");
494
607
  const config = {
495
608
  version: 1,
496
609
  hooks: {
@@ -498,7 +611,7 @@ async function installCursorHookScripts(options) {
498
611
  preToolUse: [
499
612
  ...filteredPreToolUse,
500
613
  {
501
- command: "npx @contextstream/mcp-server hook pre-tool-use",
614
+ command: preToolUseCommand,
502
615
  type: "command",
503
616
  timeout: 5,
504
617
  matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
@@ -507,7 +620,7 @@ async function installCursorHookScripts(options) {
507
620
  beforeSubmitPrompt: [
508
621
  ...filteredBeforeSubmit,
509
622
  {
510
- command: "npx @contextstream/mcp-server hook user-prompt-submit",
623
+ command: userPromptCommand,
511
624
  type: "command",
512
625
  timeout: 5
513
626
  }
@@ -517,8 +630,8 @@ async function installCursorHookScripts(options) {
517
630
  await writeCursorHooksConfig(config, options.scope, options.projectPath);
518
631
  const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
519
632
  return {
520
- preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
521
- beforeSubmitPrompt: "npx @contextstream/mcp-server hook user-prompt-submit",
633
+ preToolUse: preToolUseCommand,
634
+ beforeSubmitPrompt: userPromptCommand,
522
635
  config: configPath
523
636
  };
524
637
  }
@@ -1327,11 +1440,13 @@ esac
1327
1440
 
1328
1441
  exit 0
1329
1442
  `;
1330
- CLINE_HOOK_WRAPPER = (hookName) => `#!/bin/bash
1443
+ CLINE_HOOK_WRAPPER = (hookName) => {
1444
+ const command = getHookCommand(hookName);
1445
+ return `#!/bin/bash
1331
1446
  # ContextStream ${hookName} Hook Wrapper for Cline/Roo/Kilo Code
1332
- # Calls the Node.js hook via npx
1333
- exec npx @contextstream/mcp-server hook ${hookName}
1447
+ exec ${command}
1334
1448
  `;
1449
+ };
1335
1450
  CURSOR_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
1336
1451
  """
1337
1452
  ContextStream PreToolUse Hook for Cursor
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/on-bash.ts
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { homedir } from "node:os";
7
+ var ENABLED = process.env.CONTEXTSTREAM_BASH_HOOK_ENABLED !== "false";
8
+ var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
9
+ var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
10
+ var WORKSPACE_ID = null;
11
+ function loadConfigFromMcpJson(cwd) {
12
+ let searchDir = path.resolve(cwd);
13
+ for (let i = 0; i < 5; i++) {
14
+ if (!API_KEY) {
15
+ const mcpPath = path.join(searchDir, ".mcp.json");
16
+ if (fs.existsSync(mcpPath)) {
17
+ try {
18
+ const content = fs.readFileSync(mcpPath, "utf-8");
19
+ const config = JSON.parse(content);
20
+ const csEnv = config.mcpServers?.contextstream?.env;
21
+ if (csEnv?.CONTEXTSTREAM_API_KEY) {
22
+ API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
23
+ }
24
+ if (csEnv?.CONTEXTSTREAM_API_URL) {
25
+ API_URL = csEnv.CONTEXTSTREAM_API_URL;
26
+ }
27
+ } catch {
28
+ }
29
+ }
30
+ }
31
+ if (!WORKSPACE_ID) {
32
+ const csConfigPath = path.join(searchDir, ".contextstream", "config.json");
33
+ if (fs.existsSync(csConfigPath)) {
34
+ try {
35
+ const content = fs.readFileSync(csConfigPath, "utf-8");
36
+ const csConfig = JSON.parse(content);
37
+ if (csConfig.workspace_id) {
38
+ WORKSPACE_ID = csConfig.workspace_id;
39
+ }
40
+ } catch {
41
+ }
42
+ }
43
+ }
44
+ const parentDir = path.dirname(searchDir);
45
+ if (parentDir === searchDir) break;
46
+ searchDir = parentDir;
47
+ }
48
+ if (!API_KEY) {
49
+ const homeMcpPath = path.join(homedir(), ".mcp.json");
50
+ if (fs.existsSync(homeMcpPath)) {
51
+ try {
52
+ const content = fs.readFileSync(homeMcpPath, "utf-8");
53
+ const config = JSON.parse(content);
54
+ const csEnv = config.mcpServers?.contextstream?.env;
55
+ if (csEnv?.CONTEXTSTREAM_API_KEY) {
56
+ API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
57
+ }
58
+ if (csEnv?.CONTEXTSTREAM_API_URL) {
59
+ API_URL = csEnv.CONTEXTSTREAM_API_URL;
60
+ }
61
+ } catch {
62
+ }
63
+ }
64
+ }
65
+ }
66
+ async function captureCommand(command, output, exitCode, isError, sessionId) {
67
+ if (!API_KEY) return;
68
+ const payload = {
69
+ event_type: isError ? "bash_error" : "bash_command",
70
+ title: isError ? `Bash Error: ${command.slice(0, 50)}...` : `Command: ${command.slice(0, 50)}...`,
71
+ content: JSON.stringify({
72
+ command,
73
+ output: output.slice(0, 2e3),
74
+ exit_code: exitCode,
75
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
76
+ }),
77
+ importance: isError ? "high" : "low",
78
+ tags: isError ? ["bash", "error", "command"] : ["bash", "command"],
79
+ source_type: "hook",
80
+ session_id: sessionId
81
+ };
82
+ if (WORKSPACE_ID) {
83
+ payload.workspace_id = WORKSPACE_ID;
84
+ }
85
+ try {
86
+ const controller = new AbortController();
87
+ const timeoutId = setTimeout(() => controller.abort(), 3e3);
88
+ await fetch(`${API_URL}/api/v1/memory/events`, {
89
+ method: "POST",
90
+ headers: {
91
+ "Content-Type": "application/json",
92
+ "X-API-Key": API_KEY
93
+ },
94
+ body: JSON.stringify(payload),
95
+ signal: controller.signal
96
+ });
97
+ clearTimeout(timeoutId);
98
+ } catch {
99
+ }
100
+ }
101
+ async function suggestLesson(command, error) {
102
+ const errorPatterns = [
103
+ {
104
+ pattern: /command not found/i,
105
+ lesson: `The command "${command.split(" ")[0]}" is not installed. Check if the package needs to be installed first.`
106
+ },
107
+ {
108
+ pattern: /permission denied/i,
109
+ lesson: "Permission denied. May need sudo or to check file permissions."
110
+ },
111
+ {
112
+ pattern: /no such file or directory/i,
113
+ lesson: "Path does not exist. Verify the file/directory path before running commands."
114
+ },
115
+ {
116
+ pattern: /EADDRINUSE|address already in use/i,
117
+ lesson: "Port is already in use. Kill the existing process or use a different port."
118
+ },
119
+ {
120
+ pattern: /npm ERR!|ERESOLVE/i,
121
+ lesson: "npm dependency conflict. Try `npm install --legacy-peer-deps` or check package versions."
122
+ },
123
+ {
124
+ pattern: /ENOENT.*package\.json/i,
125
+ lesson: "No package.json found. Make sure you're in the right directory or run `npm init`."
126
+ },
127
+ {
128
+ pattern: /git.*not a git repository/i,
129
+ lesson: "Not in a git repository. Run `git init` or navigate to a git repo."
130
+ }
131
+ ];
132
+ for (const { pattern, lesson } of errorPatterns) {
133
+ if (pattern.test(error)) {
134
+ return lesson;
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+ async function runOnBashHook() {
140
+ if (!ENABLED) {
141
+ process.exit(0);
142
+ }
143
+ let inputData = "";
144
+ for await (const chunk of process.stdin) {
145
+ inputData += chunk;
146
+ }
147
+ if (!inputData.trim()) {
148
+ process.exit(0);
149
+ }
150
+ let input;
151
+ try {
152
+ input = JSON.parse(inputData);
153
+ } catch {
154
+ process.exit(0);
155
+ }
156
+ if (input.tool_name !== "Bash") {
157
+ process.exit(0);
158
+ }
159
+ const cwd = input.cwd || process.cwd();
160
+ loadConfigFromMcpJson(cwd);
161
+ const command = input.tool_input?.command || "";
162
+ const output = input.tool_result?.output || input.tool_result?.error || "";
163
+ const exitCode = input.tool_result?.exit_code ?? 0;
164
+ const sessionId = input.session_id || "unknown";
165
+ const isError = exitCode !== 0 || !!input.tool_result?.error;
166
+ captureCommand(command, output, exitCode, isError, sessionId).catch(() => {
167
+ });
168
+ if (isError) {
169
+ const lesson = await suggestLesson(command, output);
170
+ if (lesson) {
171
+ console.log(
172
+ JSON.stringify({
173
+ hookSpecificOutput: {
174
+ hookEventName: "PostToolUse",
175
+ additionalContext: `[ContextStream Insight] ${lesson}`
176
+ }
177
+ })
178
+ );
179
+ process.exit(0);
180
+ }
181
+ }
182
+ process.exit(0);
183
+ }
184
+ var isDirectRun = process.argv[1]?.includes("on-bash") || process.argv[2] === "on-bash";
185
+ if (isDirectRun) {
186
+ runOnBashHook().catch(() => process.exit(0));
187
+ }
188
+ export {
189
+ runOnBashHook
190
+ };
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/on-read.ts
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { homedir } from "node:os";
7
+ var ENABLED = process.env.CONTEXTSTREAM_READ_HOOK_ENABLED !== "false";
8
+ var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
9
+ var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
10
+ var WORKSPACE_ID = null;
11
+ var recentCaptures = /* @__PURE__ */ new Set();
12
+ var CAPTURE_WINDOW_MS = 6e4;
13
+ function loadConfigFromMcpJson(cwd) {
14
+ let searchDir = path.resolve(cwd);
15
+ for (let i = 0; i < 5; i++) {
16
+ if (!API_KEY) {
17
+ const mcpPath = path.join(searchDir, ".mcp.json");
18
+ if (fs.existsSync(mcpPath)) {
19
+ try {
20
+ const content = fs.readFileSync(mcpPath, "utf-8");
21
+ const config = JSON.parse(content);
22
+ const csEnv = config.mcpServers?.contextstream?.env;
23
+ if (csEnv?.CONTEXTSTREAM_API_KEY) {
24
+ API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
25
+ }
26
+ if (csEnv?.CONTEXTSTREAM_API_URL) {
27
+ API_URL = csEnv.CONTEXTSTREAM_API_URL;
28
+ }
29
+ } catch {
30
+ }
31
+ }
32
+ }
33
+ if (!WORKSPACE_ID) {
34
+ const csConfigPath = path.join(searchDir, ".contextstream", "config.json");
35
+ if (fs.existsSync(csConfigPath)) {
36
+ try {
37
+ const content = fs.readFileSync(csConfigPath, "utf-8");
38
+ const csConfig = JSON.parse(content);
39
+ if (csConfig.workspace_id) {
40
+ WORKSPACE_ID = csConfig.workspace_id;
41
+ }
42
+ } catch {
43
+ }
44
+ }
45
+ }
46
+ const parentDir = path.dirname(searchDir);
47
+ if (parentDir === searchDir) break;
48
+ searchDir = parentDir;
49
+ }
50
+ if (!API_KEY) {
51
+ const homeMcpPath = path.join(homedir(), ".mcp.json");
52
+ if (fs.existsSync(homeMcpPath)) {
53
+ try {
54
+ const content = fs.readFileSync(homeMcpPath, "utf-8");
55
+ const config = JSON.parse(content);
56
+ const csEnv = config.mcpServers?.contextstream?.env;
57
+ if (csEnv?.CONTEXTSTREAM_API_KEY) {
58
+ API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
59
+ }
60
+ if (csEnv?.CONTEXTSTREAM_API_URL) {
61
+ API_URL = csEnv.CONTEXTSTREAM_API_URL;
62
+ }
63
+ } catch {
64
+ }
65
+ }
66
+ }
67
+ }
68
+ async function captureExploration(toolName, target, resultSummary, sessionId) {
69
+ if (!API_KEY) return;
70
+ const cacheKey = `${toolName}:${target}`;
71
+ if (recentCaptures.has(cacheKey)) {
72
+ return;
73
+ }
74
+ recentCaptures.add(cacheKey);
75
+ setTimeout(() => recentCaptures.delete(cacheKey), CAPTURE_WINDOW_MS);
76
+ const payload = {
77
+ event_type: "file_exploration",
78
+ title: `${toolName}: ${target.slice(0, 50)}`,
79
+ content: JSON.stringify({
80
+ tool: toolName,
81
+ target,
82
+ result_summary: resultSummary.slice(0, 500),
83
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
84
+ }),
85
+ importance: "low",
86
+ tags: ["exploration", toolName.toLowerCase()],
87
+ source_type: "hook",
88
+ session_id: sessionId
89
+ };
90
+ if (WORKSPACE_ID) {
91
+ payload.workspace_id = WORKSPACE_ID;
92
+ }
93
+ try {
94
+ const controller = new AbortController();
95
+ const timeoutId = setTimeout(() => controller.abort(), 3e3);
96
+ await fetch(`${API_URL}/api/v1/memory/events`, {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ "X-API-Key": API_KEY
101
+ },
102
+ body: JSON.stringify(payload),
103
+ signal: controller.signal
104
+ });
105
+ clearTimeout(timeoutId);
106
+ } catch {
107
+ }
108
+ }
109
+ async function runOnReadHook() {
110
+ if (!ENABLED) {
111
+ process.exit(0);
112
+ }
113
+ let inputData = "";
114
+ for await (const chunk of process.stdin) {
115
+ inputData += chunk;
116
+ }
117
+ if (!inputData.trim()) {
118
+ process.exit(0);
119
+ }
120
+ let input;
121
+ try {
122
+ input = JSON.parse(inputData);
123
+ } catch {
124
+ process.exit(0);
125
+ }
126
+ const toolName = input.tool_name || "";
127
+ if (!["Read", "Glob", "Grep"].includes(toolName)) {
128
+ process.exit(0);
129
+ }
130
+ const cwd = input.cwd || process.cwd();
131
+ loadConfigFromMcpJson(cwd);
132
+ const sessionId = input.session_id || "unknown";
133
+ let target = "";
134
+ let resultSummary = "";
135
+ switch (toolName) {
136
+ case "Read":
137
+ target = input.tool_input?.file_path || "";
138
+ resultSummary = `Read file: ${target}`;
139
+ break;
140
+ case "Glob":
141
+ target = input.tool_input?.pattern || "";
142
+ const globFiles = input.tool_result?.files || [];
143
+ resultSummary = `Found ${globFiles.length} files matching ${target}`;
144
+ break;
145
+ case "Grep":
146
+ target = input.tool_input?.pattern || "";
147
+ const matches = input.tool_result?.matches || 0;
148
+ resultSummary = `Found ${matches} matches for "${target}"`;
149
+ break;
150
+ }
151
+ if (target) {
152
+ captureExploration(toolName, target, resultSummary, sessionId).catch(() => {
153
+ });
154
+ }
155
+ process.exit(0);
156
+ }
157
+ var isDirectRun = process.argv[1]?.includes("on-read") || process.argv[2] === "on-read";
158
+ if (isDirectRun) {
159
+ runOnReadHook().catch(() => process.exit(0));
160
+ }
161
+ export {
162
+ runOnReadHook
163
+ };