@contextstream/mcp-server 0.4.63 → 0.4.64

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.
@@ -994,6 +994,7 @@ function detectLanguage(filePath) {
994
994
  swift: "swift",
995
995
  scala: "scala",
996
996
  sql: "sql",
997
+ dart: "dart",
997
998
  md: "markdown",
998
999
  json: "json",
999
1000
  yaml: "yaml",
@@ -1105,7 +1106,9 @@ var init_files = __esm({
1105
1106
  // Other
1106
1107
  "graphql",
1107
1108
  "proto",
1108
- "dockerfile"
1109
+ "dockerfile",
1110
+ // Dart/Flutter
1111
+ "dart"
1109
1112
  ]);
1110
1113
  IGNORE_DIRS = /* @__PURE__ */ new Set([
1111
1114
  "node_modules",
@@ -1264,7 +1267,7 @@ function buildHooksConfig(options) {
1264
1267
  ]
1265
1268
  });
1266
1269
  }
1267
- if (options?.includeMediaAware !== false) {
1270
+ if (options?.includeMediaAware === true) {
1268
1271
  userPromptHooks.push({
1269
1272
  matcher: "*",
1270
1273
  hooks: [
@@ -1279,7 +1282,7 @@ function buildHooksConfig(options) {
1279
1282
  const config = {
1280
1283
  PreToolUse: [
1281
1284
  {
1282
- matcher: "Glob|Grep|Search|Task|EnterPlanMode",
1285
+ matcher: "*",
1283
1286
  hooks: [
1284
1287
  {
1285
1288
  type: "command",
@@ -1308,11 +1311,11 @@ function buildHooksConfig(options) {
1308
1311
  if (options?.includeSessionInit !== false) {
1309
1312
  config.SessionStart = [
1310
1313
  {
1311
- matcher: "*",
1314
+ matcher: "startup|resume|compact",
1312
1315
  hooks: [
1313
1316
  {
1314
1317
  type: "command",
1315
- command: getHookCommand("session-init"),
1318
+ command: getHookCommand("session-start"),
1316
1319
  timeout: 10
1317
1320
  }
1318
1321
  ]
@@ -1321,6 +1324,18 @@ function buildHooksConfig(options) {
1321
1324
  }
1322
1325
  if (options?.includeSessionEnd !== false) {
1323
1326
  config.Stop = [
1327
+ {
1328
+ matcher: "*",
1329
+ hooks: [
1330
+ {
1331
+ type: "command",
1332
+ command: getHookCommand("stop"),
1333
+ timeout: 15
1334
+ }
1335
+ ]
1336
+ }
1337
+ ];
1338
+ config.SessionEnd = [
1324
1339
  {
1325
1340
  matcher: "*",
1326
1341
  hooks: [
@@ -1346,7 +1361,7 @@ function buildHooksConfig(options) {
1346
1361
  ]
1347
1362
  });
1348
1363
  }
1349
- if (options?.includeAutoRules !== false) {
1364
+ if (options?.includeAutoRules === true) {
1350
1365
  postToolUseHooks.push({
1351
1366
  matcher: "mcp__contextstream__init|mcp__contextstream__context",
1352
1367
  hooks: [
@@ -1358,7 +1373,7 @@ function buildHooksConfig(options) {
1358
1373
  ]
1359
1374
  });
1360
1375
  }
1361
- if (options?.includeOnBash !== false) {
1376
+ if (options?.includeOnBash === true) {
1362
1377
  postToolUseHooks.push({
1363
1378
  matcher: "Bash",
1364
1379
  hooks: [
@@ -1370,7 +1385,7 @@ function buildHooksConfig(options) {
1370
1385
  ]
1371
1386
  });
1372
1387
  }
1373
- if (options?.includeOnTask !== false) {
1388
+ if (options?.includeOnTask === true) {
1374
1389
  postToolUseHooks.push({
1375
1390
  matcher: "Task",
1376
1391
  hooks: [
@@ -1382,7 +1397,7 @@ function buildHooksConfig(options) {
1382
1397
  ]
1383
1398
  });
1384
1399
  }
1385
- if (options?.includeOnRead !== false) {
1400
+ if (options?.includeOnRead === true) {
1386
1401
  postToolUseHooks.push({
1387
1402
  matcher: "Read|Glob|Grep",
1388
1403
  hooks: [
@@ -1394,7 +1409,7 @@ function buildHooksConfig(options) {
1394
1409
  ]
1395
1410
  });
1396
1411
  }
1397
- if (options?.includeOnWeb !== false) {
1412
+ if (options?.includeOnWeb === true) {
1398
1413
  postToolUseHooks.push({
1399
1414
  matcher: "WebFetch|WebSearch",
1400
1415
  hooks: [
@@ -1409,6 +1424,48 @@ function buildHooksConfig(options) {
1409
1424
  if (postToolUseHooks.length > 0) {
1410
1425
  config.PostToolUse = postToolUseHooks;
1411
1426
  }
1427
+ config.PostToolUseFailure = [
1428
+ {
1429
+ matcher: "*",
1430
+ hooks: [{ type: "command", command: getHookCommand("post-tool-use-failure"), timeout: 10 }]
1431
+ }
1432
+ ];
1433
+ config.SubagentStart = [
1434
+ {
1435
+ matcher: "Explore|Plan|general-purpose|custom",
1436
+ hooks: [{ type: "command", command: getHookCommand("subagent-start"), timeout: 10 }]
1437
+ }
1438
+ ];
1439
+ config.SubagentStop = [
1440
+ {
1441
+ matcher: "Plan",
1442
+ hooks: [{ type: "command", command: getHookCommand("subagent-stop"), timeout: 15 }]
1443
+ }
1444
+ ];
1445
+ config.TaskCompleted = [
1446
+ {
1447
+ matcher: "*",
1448
+ hooks: [{ type: "command", command: getHookCommand("task-completed"), timeout: 10 }]
1449
+ }
1450
+ ];
1451
+ config.TeammateIdle = [
1452
+ {
1453
+ matcher: "*",
1454
+ hooks: [{ type: "command", command: getHookCommand("teammate-idle"), timeout: 10 }]
1455
+ }
1456
+ ];
1457
+ config.Notification = [
1458
+ {
1459
+ matcher: "*",
1460
+ hooks: [{ type: "command", command: getHookCommand("notification"), timeout: 10 }]
1461
+ }
1462
+ ];
1463
+ config.PermissionRequest = [
1464
+ {
1465
+ matcher: "*",
1466
+ hooks: [{ type: "command", command: getHookCommand("permission-request"), timeout: 10 }]
1467
+ }
1468
+ ];
1412
1469
  return config;
1413
1470
  }
1414
1471
  async function installHookScripts(options) {
@@ -1462,18 +1519,29 @@ async function installClaudeCodeHooks(options) {
1462
1519
  const result = { scripts: [], settings: [] };
1463
1520
  result.scripts.push(
1464
1521
  getHookCommand("pre-tool-use"),
1465
- getHookCommand("user-prompt-submit")
1522
+ getHookCommand("user-prompt-submit"),
1523
+ getHookCommand("on-save-intent"),
1524
+ getHookCommand("session-start"),
1525
+ getHookCommand("stop"),
1526
+ getHookCommand("session-end"),
1527
+ getHookCommand("post-tool-use-failure"),
1528
+ getHookCommand("subagent-start"),
1529
+ getHookCommand("subagent-stop"),
1530
+ getHookCommand("task-completed"),
1531
+ getHookCommand("teammate-idle"),
1532
+ getHookCommand("notification"),
1533
+ getHookCommand("permission-request")
1466
1534
  );
1467
1535
  if (options.includePreCompact !== false) {
1468
1536
  result.scripts.push(getHookCommand("pre-compact"));
1469
1537
  }
1470
- if (options.includeMediaAware !== false) {
1538
+ if (options.includeMediaAware === true) {
1471
1539
  result.scripts.push(getHookCommand("media-aware"));
1472
1540
  }
1473
1541
  if (options.includePostWrite !== false) {
1474
1542
  result.scripts.push(getHookCommand("post-write"));
1475
1543
  }
1476
- if (options.includeAutoRules !== false) {
1544
+ if (options.includeAutoRules === true) {
1477
1545
  result.scripts.push(getHookCommand("auto-rules"));
1478
1546
  }
1479
1547
  const hooksConfig = buildHooksConfig({
@@ -1511,8 +1579,8 @@ All hooks run via Node.js - no Python dependency required.
1511
1579
 
1512
1580
  ### PreToolUse Hook
1513
1581
  - **Command:** \`npx @contextstream/mcp-server hook pre-tool-use\`
1514
- - **Purpose:** Blocks Glob/Grep/Search/EnterPlanMode and redirects to ContextStream
1515
- - **Blocked tools:** Glob, Grep, Search, Task(Explore), Task(Plan), EnterPlanMode
1582
+ - **Purpose:** Blocks Glob/Grep/Search/Explore/Task/EnterPlanMode and redirects to ContextStream
1583
+ - **Blocked tools:** Glob, Grep, Search, Explore, Task(Explore), Task(Plan), EnterPlanMode
1516
1584
  - **Disable:** Set \`CONTEXTSTREAM_HOOK_ENABLED=false\` environment variable
1517
1585
 
1518
1586
  ### UserPromptSubmit Hook
@@ -1565,7 +1633,7 @@ If you prefer to configure manually, add to \`~/.claude/settings.json\`:
1565
1633
  {
1566
1634
  "hooks": {
1567
1635
  "PreToolUse": [{
1568
- "matcher": "Glob|Grep|Search|Task|EnterPlanMode",
1636
+ "matcher": "Glob|Grep|Search|Explore|Task|EnterPlanMode",
1569
1637
  "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook pre-tool-use"}]
1570
1638
  }],
1571
1639
  "UserPromptSubmit": [
@@ -1790,6 +1858,7 @@ async function installCursorHookScripts(options) {
1790
1858
  const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
1791
1859
  const preToolUseCommand = getHookCommand("pre-tool-use");
1792
1860
  const userPromptCommand = getHookCommand("user-prompt-submit");
1861
+ const saveIntentCommand = getHookCommand("on-save-intent");
1793
1862
  const config = {
1794
1863
  version: 1,
1795
1864
  hooks: {
@@ -1799,8 +1868,7 @@ async function installCursorHookScripts(options) {
1799
1868
  {
1800
1869
  command: preToolUseCommand,
1801
1870
  type: "command",
1802
- timeout: 5,
1803
- matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
1871
+ timeout: 5
1804
1872
  }
1805
1873
  ],
1806
1874
  beforeSubmitPrompt: [
@@ -1809,6 +1877,11 @@ async function installCursorHookScripts(options) {
1809
1877
  command: userPromptCommand,
1810
1878
  type: "command",
1811
1879
  timeout: 5
1880
+ },
1881
+ {
1882
+ command: saveIntentCommand,
1883
+ type: "command",
1884
+ timeout: 5
1812
1885
  }
1813
1886
  ]
1814
1887
  }
@@ -1817,7 +1890,7 @@ async function installCursorHookScripts(options) {
1817
1890
  const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
1818
1891
  return {
1819
1892
  preToolUse: preToolUseCommand,
1820
- beforeSubmitPrompt: userPromptCommand,
1893
+ beforeSubmitPrompt: `${userPromptCommand}, ${saveIntentCommand}`,
1821
1894
  config: configPath
1822
1895
  };
1823
1896
  }
@@ -1973,7 +2046,7 @@ var init_hooks_config = __esm({
1973
2046
  PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
1974
2047
  """
1975
2048
  ContextStream PreToolUse Hook for Claude Code
1976
- Blocks Grep/Glob/Search/Task(Explore)/EnterPlanMode and redirects to ContextStream.
2049
+ Blocks Grep/Glob/Search/Explore/Task(Explore|Plan)/EnterPlanMode and redirects to ContextStream.
1977
2050
 
1978
2051
  Only blocks if the current project is indexed in ContextStream.
1979
2052
  If not indexed, allows local tools through with a suggestion to index.
@@ -2092,12 +2165,18 @@ def main():
2092
2165
  print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
2093
2166
  sys.exit(2)
2094
2167
 
2168
+ elif tool == "Explore":
2169
+ print("STOP: Use mcp__contextstream__search(mode=\\"auto\\", output_format=\\"paths\\") instead of Explore.", file=sys.stderr)
2170
+ sys.exit(2)
2171
+
2095
2172
  elif tool == "Task":
2096
- if inp.get("subagent_type", "").lower() == "explore":
2173
+ subagent = inp.get("subagent_type", "") or inp.get("subagentType", "")
2174
+ subagent = subagent.lower()
2175
+ if "explore" in subagent:
2097
2176
  print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
2098
2177
  sys.exit(2)
2099
- if inp.get("subagent_type", "").lower() == "plan":
2100
- print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
2178
+ if "plan" in subagent:
2179
+ print("STOP: For planning, use mcp__contextstream__search(mode=\\"auto\\", output_format=\\"paths\\") for discovery and mcp__contextstream__session(action=\\"capture_plan\\") for persistence.", file=sys.stderr)
2101
2180
  sys.exit(2)
2102
2181
 
2103
2182
  elif tool == "EnterPlanMode":
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/common.ts
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { homedir } from "node:os";
7
+ var DEFAULT_API_URL = "https://api.contextstream.io";
8
+ function readHookInput() {
9
+ try {
10
+ return JSON.parse(fs.readFileSync(0, "utf8"));
11
+ } catch {
12
+ return {};
13
+ }
14
+ }
15
+ function writeHookOutput(output) {
16
+ const payload = output && (output.additionalContext || output.blocked || output.reason) ? {
17
+ hookSpecificOutput: output.additionalContext ? {
18
+ hookEventName: output.hookEventName,
19
+ additionalContext: output.additionalContext
20
+ } : void 0,
21
+ additionalContext: output.additionalContext,
22
+ blocked: output.blocked,
23
+ reason: output.reason
24
+ } : {};
25
+ console.log(JSON.stringify(payload));
26
+ }
27
+ function extractCwd(input) {
28
+ const cwd = typeof input.cwd === "string" && input.cwd.trim() ? input.cwd.trim() : process.cwd();
29
+ return cwd;
30
+ }
31
+ function loadHookConfig(cwd) {
32
+ let apiUrl = process.env.CONTEXTSTREAM_API_URL || DEFAULT_API_URL;
33
+ let apiKey = process.env.CONTEXTSTREAM_API_KEY || "";
34
+ let jwt = process.env.CONTEXTSTREAM_JWT || "";
35
+ let workspaceId = process.env.CONTEXTSTREAM_WORKSPACE_ID || null;
36
+ let projectId = process.env.CONTEXTSTREAM_PROJECT_ID || null;
37
+ let searchDir = path.resolve(cwd);
38
+ for (let i = 0; i < 6; i++) {
39
+ if (!apiKey && !jwt) {
40
+ const mcpPath = path.join(searchDir, ".mcp.json");
41
+ if (fs.existsSync(mcpPath)) {
42
+ try {
43
+ const config = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
44
+ const env = config.mcpServers?.contextstream?.env;
45
+ if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
46
+ if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
47
+ if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
48
+ if (env?.CONTEXTSTREAM_WORKSPACE_ID && !workspaceId) workspaceId = env.CONTEXTSTREAM_WORKSPACE_ID;
49
+ if (env?.CONTEXTSTREAM_PROJECT_ID && !projectId) projectId = env.CONTEXTSTREAM_PROJECT_ID;
50
+ } catch {
51
+ }
52
+ }
53
+ }
54
+ if (!workspaceId || !projectId) {
55
+ const localConfigPath = path.join(searchDir, ".contextstream", "config.json");
56
+ if (fs.existsSync(localConfigPath)) {
57
+ try {
58
+ const localConfig = JSON.parse(fs.readFileSync(localConfigPath, "utf8"));
59
+ if (localConfig.workspace_id && !workspaceId) workspaceId = localConfig.workspace_id;
60
+ if (localConfig.project_id && !projectId) projectId = localConfig.project_id;
61
+ } catch {
62
+ }
63
+ }
64
+ }
65
+ const parentDir = path.dirname(searchDir);
66
+ if (parentDir === searchDir) break;
67
+ searchDir = parentDir;
68
+ }
69
+ if (!apiKey && !jwt) {
70
+ const homeMcpPath = path.join(homedir(), ".mcp.json");
71
+ if (fs.existsSync(homeMcpPath)) {
72
+ try {
73
+ const config = JSON.parse(fs.readFileSync(homeMcpPath, "utf8"));
74
+ const env = config.mcpServers?.contextstream?.env;
75
+ if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
76
+ if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
77
+ if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
78
+ } catch {
79
+ }
80
+ }
81
+ }
82
+ return { apiUrl, apiKey, jwt, workspaceId, projectId };
83
+ }
84
+ function isConfigured(config) {
85
+ return Boolean(config.apiKey || config.jwt);
86
+ }
87
+ function authHeaders(config) {
88
+ if (config.apiKey) {
89
+ return { "X-API-Key": config.apiKey };
90
+ }
91
+ if (config.jwt) {
92
+ return { Authorization: `Bearer ${config.jwt}` };
93
+ }
94
+ return {};
95
+ }
96
+ async function apiRequest(config, apiPath, init = {}) {
97
+ const response = await fetch(`${config.apiUrl}${apiPath}`, {
98
+ method: init.method || "GET",
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ ...authHeaders(config)
102
+ },
103
+ body: init.body !== void 0 ? JSON.stringify(init.body) : void 0
104
+ });
105
+ if (!response.ok) {
106
+ throw new Error(`${response.status} ${response.statusText}`);
107
+ }
108
+ const json = await response.json();
109
+ if (json && typeof json === "object" && "data" in json) {
110
+ return json.data;
111
+ }
112
+ return json;
113
+ }
114
+ async function postMemoryEvent(config, title, content, tags, eventType = "operation") {
115
+ if (!isConfigured(config) || !config.workspaceId) return;
116
+ await apiRequest(config, "/memory/events", {
117
+ method: "POST",
118
+ body: {
119
+ workspace_id: config.workspaceId,
120
+ project_id: config.projectId || void 0,
121
+ event_type: eventType,
122
+ title,
123
+ content: typeof content === "string" ? content : JSON.stringify(content),
124
+ metadata: {
125
+ tags,
126
+ source: "mcp_hook",
127
+ captured_at: (/* @__PURE__ */ new Date()).toISOString()
128
+ }
129
+ }
130
+ });
131
+ }
132
+
133
+ // src/hooks/notification.ts
134
+ async function runNotificationHook() {
135
+ const input = readHookInput();
136
+ const cwd = extractCwd(input);
137
+ const config = loadHookConfig(cwd);
138
+ if (isConfigured(config)) {
139
+ const message = typeof input.title === "string" && input.title || typeof input.message === "string" && input.message || "Notification event";
140
+ await postMemoryEvent(
141
+ config,
142
+ `Notification: ${message}`,
143
+ {
144
+ notification: input,
145
+ captured_at: (/* @__PURE__ */ new Date()).toISOString()
146
+ },
147
+ ["hook", "notification"]
148
+ ).catch(() => {
149
+ });
150
+ }
151
+ writeHookOutput();
152
+ }
153
+ var isDirectRun = process.argv[1]?.includes("notification") || process.argv[2] === "notification";
154
+ if (isDirectRun) {
155
+ runNotificationHook().catch(() => process.exit(0));
156
+ }
157
+ export {
158
+ runNotificationHook
159
+ };
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/common.ts
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { homedir } from "node:os";
7
+ var DEFAULT_API_URL = "https://api.contextstream.io";
8
+ function readHookInput() {
9
+ try {
10
+ return JSON.parse(fs.readFileSync(0, "utf8"));
11
+ } catch {
12
+ return {};
13
+ }
14
+ }
15
+ function writeHookOutput(output) {
16
+ const payload = output && (output.additionalContext || output.blocked || output.reason) ? {
17
+ hookSpecificOutput: output.additionalContext ? {
18
+ hookEventName: output.hookEventName,
19
+ additionalContext: output.additionalContext
20
+ } : void 0,
21
+ additionalContext: output.additionalContext,
22
+ blocked: output.blocked,
23
+ reason: output.reason
24
+ } : {};
25
+ console.log(JSON.stringify(payload));
26
+ }
27
+ function extractCwd(input) {
28
+ const cwd = typeof input.cwd === "string" && input.cwd.trim() ? input.cwd.trim() : process.cwd();
29
+ return cwd;
30
+ }
31
+ function loadHookConfig(cwd) {
32
+ let apiUrl = process.env.CONTEXTSTREAM_API_URL || DEFAULT_API_URL;
33
+ let apiKey = process.env.CONTEXTSTREAM_API_KEY || "";
34
+ let jwt = process.env.CONTEXTSTREAM_JWT || "";
35
+ let workspaceId = process.env.CONTEXTSTREAM_WORKSPACE_ID || null;
36
+ let projectId = process.env.CONTEXTSTREAM_PROJECT_ID || null;
37
+ let searchDir = path.resolve(cwd);
38
+ for (let i = 0; i < 6; i++) {
39
+ if (!apiKey && !jwt) {
40
+ const mcpPath = path.join(searchDir, ".mcp.json");
41
+ if (fs.existsSync(mcpPath)) {
42
+ try {
43
+ const config = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
44
+ const env = config.mcpServers?.contextstream?.env;
45
+ if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
46
+ if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
47
+ if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
48
+ if (env?.CONTEXTSTREAM_WORKSPACE_ID && !workspaceId) workspaceId = env.CONTEXTSTREAM_WORKSPACE_ID;
49
+ if (env?.CONTEXTSTREAM_PROJECT_ID && !projectId) projectId = env.CONTEXTSTREAM_PROJECT_ID;
50
+ } catch {
51
+ }
52
+ }
53
+ }
54
+ if (!workspaceId || !projectId) {
55
+ const localConfigPath = path.join(searchDir, ".contextstream", "config.json");
56
+ if (fs.existsSync(localConfigPath)) {
57
+ try {
58
+ const localConfig = JSON.parse(fs.readFileSync(localConfigPath, "utf8"));
59
+ if (localConfig.workspace_id && !workspaceId) workspaceId = localConfig.workspace_id;
60
+ if (localConfig.project_id && !projectId) projectId = localConfig.project_id;
61
+ } catch {
62
+ }
63
+ }
64
+ }
65
+ const parentDir = path.dirname(searchDir);
66
+ if (parentDir === searchDir) break;
67
+ searchDir = parentDir;
68
+ }
69
+ if (!apiKey && !jwt) {
70
+ const homeMcpPath = path.join(homedir(), ".mcp.json");
71
+ if (fs.existsSync(homeMcpPath)) {
72
+ try {
73
+ const config = JSON.parse(fs.readFileSync(homeMcpPath, "utf8"));
74
+ const env = config.mcpServers?.contextstream?.env;
75
+ if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
76
+ if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
77
+ if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
78
+ } catch {
79
+ }
80
+ }
81
+ }
82
+ return { apiUrl, apiKey, jwt, workspaceId, projectId };
83
+ }
84
+ function isConfigured(config) {
85
+ return Boolean(config.apiKey || config.jwt);
86
+ }
87
+ function authHeaders(config) {
88
+ if (config.apiKey) {
89
+ return { "X-API-Key": config.apiKey };
90
+ }
91
+ if (config.jwt) {
92
+ return { Authorization: `Bearer ${config.jwt}` };
93
+ }
94
+ return {};
95
+ }
96
+ async function apiRequest(config, apiPath, init = {}) {
97
+ const response = await fetch(`${config.apiUrl}${apiPath}`, {
98
+ method: init.method || "GET",
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ ...authHeaders(config)
102
+ },
103
+ body: init.body !== void 0 ? JSON.stringify(init.body) : void 0
104
+ });
105
+ if (!response.ok) {
106
+ throw new Error(`${response.status} ${response.statusText}`);
107
+ }
108
+ const json = await response.json();
109
+ if (json && typeof json === "object" && "data" in json) {
110
+ return json.data;
111
+ }
112
+ return json;
113
+ }
114
+ async function postMemoryEvent(config, title, content, tags, eventType = "operation") {
115
+ if (!isConfigured(config) || !config.workspaceId) return;
116
+ await apiRequest(config, "/memory/events", {
117
+ method: "POST",
118
+ body: {
119
+ workspace_id: config.workspaceId,
120
+ project_id: config.projectId || void 0,
121
+ event_type: eventType,
122
+ title,
123
+ content: typeof content === "string" ? content : JSON.stringify(content),
124
+ metadata: {
125
+ tags,
126
+ source: "mcp_hook",
127
+ captured_at: (/* @__PURE__ */ new Date()).toISOString()
128
+ }
129
+ }
130
+ });
131
+ }
132
+
133
+ // src/hooks/permission-request.ts
134
+ function isHighRiskCommand(command) {
135
+ const lower = command.toLowerCase();
136
+ return ["rm -rf", "git reset --hard", "mkfs", "dd if=", "shutdown", "reboot"].some(
137
+ (pattern) => lower.includes(pattern)
138
+ );
139
+ }
140
+ async function runPermissionRequestHook() {
141
+ const input = readHookInput();
142
+ const cwd = extractCwd(input);
143
+ const config = loadHookConfig(cwd);
144
+ const command = typeof input.command === "string" && input.command || typeof input.cmd === "string" && input.cmd || "";
145
+ if (isConfigured(config)) {
146
+ await postMemoryEvent(
147
+ config,
148
+ "Permission request",
149
+ {
150
+ request: input,
151
+ captured_at: (/* @__PURE__ */ new Date()).toISOString()
152
+ },
153
+ ["hook", "permission_request"]
154
+ ).catch(() => {
155
+ });
156
+ }
157
+ if (isHighRiskCommand(command)) {
158
+ writeHookOutput({
159
+ additionalContext: "High-risk command detected. Confirm scope and prefer least-privilege execution."
160
+ });
161
+ return;
162
+ }
163
+ writeHookOutput();
164
+ }
165
+ var isDirectRun = process.argv[1]?.includes("permission-request") || process.argv[2] === "permission-request";
166
+ if (isDirectRun) {
167
+ runPermissionRequestHook().catch(() => process.exit(0));
168
+ }
169
+ export {
170
+ runPermissionRequestHook
171
+ };