@contextstream/mcp-server 0.4.82 → 1.0.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/NOTICE +25 -0
- package/README.md +174 -290
- package/assets/contextstream-logo.png +0 -0
- package/npm/bin/contextstream-hook.mjs +9 -0
- package/npm/bin/contextstream-mcp.mjs +9 -0
- package/npm/bin/mcp-server.mjs +9 -0
- package/npm/launcher.mjs +390 -0
- package/package.json +28 -72
- package/server.json +43 -0
- package/dist/hooks/auto-rules.js +0 -3529
- package/dist/hooks/media-aware.js +0 -103
- package/dist/hooks/notification.js +0 -162
- package/dist/hooks/on-bash.js +0 -190
- package/dist/hooks/on-read.js +0 -321
- package/dist/hooks/on-save-intent.js +0 -132
- package/dist/hooks/on-task.js +0 -139
- package/dist/hooks/on-web.js +0 -155
- package/dist/hooks/permission-request.js +0 -174
- package/dist/hooks/post-compact.js +0 -172
- package/dist/hooks/post-tool-use-failure.js +0 -213
- package/dist/hooks/post-write.js +0 -1023
- package/dist/hooks/pre-compact.js +0 -317
- package/dist/hooks/pre-tool-use.js +0 -553
- package/dist/hooks/runner.js +0 -5826
- package/dist/hooks/session-end.js +0 -285
- package/dist/hooks/session-init.js +0 -1880
- package/dist/hooks/stop.js +0 -171
- package/dist/hooks/subagent-start.js +0 -195
- package/dist/hooks/subagent-stop.js +0 -301
- package/dist/hooks/task-completed.js +0 -260
- package/dist/hooks/teammate-idle.js +0 -200
- package/dist/hooks/user-prompt-submit.js +0 -866
- package/dist/index.js +0 -41985
- package/dist/test-server.js +0 -4363
- package/scripts/postinstall.js +0 -56
- package/scripts/registration-smoke.mts +0 -45
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/hooks/pre-compact.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_PRECOMPACT_ENABLED !== "false";
|
|
8
|
-
var AUTO_SAVE = process.env.CONTEXTSTREAM_PRECOMPACT_AUTO_SAVE !== "false";
|
|
9
|
-
var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
10
|
-
var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
11
|
-
var WORKSPACE_ID = null;
|
|
12
|
-
function loadConfigFromMcpJson(cwd) {
|
|
13
|
-
let searchDir = path.resolve(cwd);
|
|
14
|
-
for (let i = 0; i < 5; i++) {
|
|
15
|
-
if (!API_KEY) {
|
|
16
|
-
const mcpPath = path.join(searchDir, ".mcp.json");
|
|
17
|
-
if (fs.existsSync(mcpPath)) {
|
|
18
|
-
try {
|
|
19
|
-
const content = fs.readFileSync(mcpPath, "utf-8");
|
|
20
|
-
const config = JSON.parse(content);
|
|
21
|
-
const csEnv = config.mcpServers?.contextstream?.env;
|
|
22
|
-
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
23
|
-
API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
|
|
24
|
-
}
|
|
25
|
-
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
26
|
-
API_URL = csEnv.CONTEXTSTREAM_API_URL;
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (!WORKSPACE_ID) {
|
|
33
|
-
const csConfigPath = path.join(searchDir, ".contextstream", "config.json");
|
|
34
|
-
if (fs.existsSync(csConfigPath)) {
|
|
35
|
-
try {
|
|
36
|
-
const content = fs.readFileSync(csConfigPath, "utf-8");
|
|
37
|
-
const csConfig = JSON.parse(content);
|
|
38
|
-
if (csConfig.workspace_id) {
|
|
39
|
-
WORKSPACE_ID = csConfig.workspace_id;
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const parentDir = path.dirname(searchDir);
|
|
46
|
-
if (parentDir === searchDir) break;
|
|
47
|
-
searchDir = parentDir;
|
|
48
|
-
}
|
|
49
|
-
if (!API_KEY) {
|
|
50
|
-
const homeMcpPath = path.join(homedir(), ".mcp.json");
|
|
51
|
-
if (fs.existsSync(homeMcpPath)) {
|
|
52
|
-
try {
|
|
53
|
-
const content = fs.readFileSync(homeMcpPath, "utf-8");
|
|
54
|
-
const config = JSON.parse(content);
|
|
55
|
-
const csEnv = config.mcpServers?.contextstream?.env;
|
|
56
|
-
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
57
|
-
API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
|
|
58
|
-
}
|
|
59
|
-
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
60
|
-
API_URL = csEnv.CONTEXTSTREAM_API_URL;
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function parseTranscript(transcriptPath) {
|
|
68
|
-
const activeFiles = /* @__PURE__ */ new Set();
|
|
69
|
-
const recentMessages = [];
|
|
70
|
-
const toolCalls = [];
|
|
71
|
-
const messages = [];
|
|
72
|
-
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
73
|
-
let firstTimestamp = true;
|
|
74
|
-
try {
|
|
75
|
-
const content = fs.readFileSync(transcriptPath, "utf-8");
|
|
76
|
-
const lines = content.split("\n");
|
|
77
|
-
for (const line of lines) {
|
|
78
|
-
if (!line.trim()) continue;
|
|
79
|
-
try {
|
|
80
|
-
const entry = JSON.parse(line);
|
|
81
|
-
const msgType = entry.type || "";
|
|
82
|
-
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
83
|
-
if (firstTimestamp && entry.timestamp) {
|
|
84
|
-
startedAt = entry.timestamp;
|
|
85
|
-
firstTimestamp = false;
|
|
86
|
-
}
|
|
87
|
-
if (msgType === "tool_use") {
|
|
88
|
-
const toolName = entry.name || "";
|
|
89
|
-
const toolInput = entry.input || {};
|
|
90
|
-
toolCalls.push({ name: toolName, input: toolInput });
|
|
91
|
-
if (["Read", "Write", "Edit", "NotebookEdit"].includes(toolName)) {
|
|
92
|
-
const filePath = toolInput.file_path || toolInput.notebook_path;
|
|
93
|
-
if (filePath) {
|
|
94
|
-
activeFiles.add(filePath);
|
|
95
|
-
}
|
|
96
|
-
} else if (toolName === "Glob") {
|
|
97
|
-
const pattern = toolInput.pattern;
|
|
98
|
-
if (pattern) {
|
|
99
|
-
activeFiles.add(`[glob:${pattern}]`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
messages.push({
|
|
103
|
-
role: "assistant",
|
|
104
|
-
content: `[Tool: ${toolName}]`,
|
|
105
|
-
timestamp,
|
|
106
|
-
tool_calls: { name: toolName, input: toolInput }
|
|
107
|
-
});
|
|
108
|
-
} else if (msgType === "tool_result") {
|
|
109
|
-
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
110
|
-
messages.push({
|
|
111
|
-
role: "tool",
|
|
112
|
-
content: resultContent,
|
|
113
|
-
timestamp,
|
|
114
|
-
tool_results: { name: entry.name }
|
|
115
|
-
});
|
|
116
|
-
} else if (msgType === "user" || entry.role === "user") {
|
|
117
|
-
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
118
|
-
if (userContent) {
|
|
119
|
-
messages.push({
|
|
120
|
-
role: "user",
|
|
121
|
-
content: userContent,
|
|
122
|
-
timestamp
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
126
|
-
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
127
|
-
if (assistantContent) {
|
|
128
|
-
messages.push({
|
|
129
|
-
role: "assistant",
|
|
130
|
-
content: assistantContent,
|
|
131
|
-
timestamp
|
|
132
|
-
});
|
|
133
|
-
if (assistantContent.length > 50) {
|
|
134
|
-
recentMessages.push(assistantContent.slice(0, 500));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} catch {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch {
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
activeFiles: Array.from(activeFiles).slice(-20),
|
|
146
|
-
// Last 20 files
|
|
147
|
-
toolCallCount: toolCalls.length,
|
|
148
|
-
messageCount: messages.length,
|
|
149
|
-
lastTools: toolCalls.slice(-10).map((t) => t.name),
|
|
150
|
-
// Last 10 tool names
|
|
151
|
-
messages,
|
|
152
|
-
startedAt
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
156
|
-
if (!API_KEY) {
|
|
157
|
-
return { success: false, message: "No API key configured" };
|
|
158
|
-
}
|
|
159
|
-
if (transcriptData.messages.length === 0) {
|
|
160
|
-
return { success: false, message: "No messages to save" };
|
|
161
|
-
}
|
|
162
|
-
const payload = {
|
|
163
|
-
session_id: sessionId,
|
|
164
|
-
messages: transcriptData.messages,
|
|
165
|
-
started_at: transcriptData.startedAt,
|
|
166
|
-
source_type: "pre_compact",
|
|
167
|
-
title: `Pre-compaction save (${trigger})`,
|
|
168
|
-
metadata: {
|
|
169
|
-
trigger,
|
|
170
|
-
active_files: transcriptData.activeFiles,
|
|
171
|
-
tool_call_count: transcriptData.toolCallCount
|
|
172
|
-
},
|
|
173
|
-
tags: ["pre_compaction", trigger]
|
|
174
|
-
};
|
|
175
|
-
if (WORKSPACE_ID) {
|
|
176
|
-
payload.workspace_id = WORKSPACE_ID;
|
|
177
|
-
}
|
|
178
|
-
try {
|
|
179
|
-
const controller = new AbortController();
|
|
180
|
-
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
181
|
-
const response = await fetch(`${API_URL}/api/v1/transcripts`, {
|
|
182
|
-
method: "POST",
|
|
183
|
-
headers: {
|
|
184
|
-
"Content-Type": "application/json",
|
|
185
|
-
"X-API-Key": API_KEY
|
|
186
|
-
},
|
|
187
|
-
body: JSON.stringify(payload),
|
|
188
|
-
signal: controller.signal
|
|
189
|
-
});
|
|
190
|
-
clearTimeout(timeoutId);
|
|
191
|
-
if (response.ok) {
|
|
192
|
-
return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
|
|
193
|
-
}
|
|
194
|
-
return { success: false, message: `API error: ${response.status}` };
|
|
195
|
-
} catch (error) {
|
|
196
|
-
return { success: false, message: String(error) };
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
200
|
-
if (!API_KEY) {
|
|
201
|
-
return { success: false, message: "No API key configured" };
|
|
202
|
-
}
|
|
203
|
-
const snapshotContent = {
|
|
204
|
-
session_id: sessionId,
|
|
205
|
-
trigger,
|
|
206
|
-
captured_at: null,
|
|
207
|
-
// API will set timestamp
|
|
208
|
-
active_files: transcriptData.activeFiles,
|
|
209
|
-
tool_call_count: transcriptData.toolCallCount,
|
|
210
|
-
last_tools: transcriptData.lastTools,
|
|
211
|
-
auto_captured: true
|
|
212
|
-
};
|
|
213
|
-
const payload = {
|
|
214
|
-
event_type: "session_snapshot",
|
|
215
|
-
title: `Auto Pre-compaction Snapshot (${trigger})`,
|
|
216
|
-
content: JSON.stringify(snapshotContent),
|
|
217
|
-
importance: "high",
|
|
218
|
-
tags: ["session_snapshot", "pre_compaction", "auto_captured"],
|
|
219
|
-
source_type: "hook"
|
|
220
|
-
};
|
|
221
|
-
if (WORKSPACE_ID) {
|
|
222
|
-
payload.workspace_id = WORKSPACE_ID;
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
const controller = new AbortController();
|
|
226
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
227
|
-
const response = await fetch(`${API_URL}/api/v1/memory/events`, {
|
|
228
|
-
method: "POST",
|
|
229
|
-
headers: {
|
|
230
|
-
"Content-Type": "application/json",
|
|
231
|
-
"X-API-Key": API_KEY
|
|
232
|
-
},
|
|
233
|
-
body: JSON.stringify(payload),
|
|
234
|
-
signal: controller.signal
|
|
235
|
-
});
|
|
236
|
-
clearTimeout(timeoutId);
|
|
237
|
-
if (response.ok) {
|
|
238
|
-
return { success: true, message: "Snapshot saved" };
|
|
239
|
-
}
|
|
240
|
-
return { success: false, message: `API error: ${response.status}` };
|
|
241
|
-
} catch (error) {
|
|
242
|
-
return { success: false, message: String(error) };
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
async function runPreCompactHook() {
|
|
246
|
-
if (!ENABLED) {
|
|
247
|
-
process.exit(0);
|
|
248
|
-
}
|
|
249
|
-
let inputData = "";
|
|
250
|
-
for await (const chunk of process.stdin) {
|
|
251
|
-
inputData += chunk;
|
|
252
|
-
}
|
|
253
|
-
if (!inputData.trim()) {
|
|
254
|
-
process.exit(0);
|
|
255
|
-
}
|
|
256
|
-
let input;
|
|
257
|
-
try {
|
|
258
|
-
input = JSON.parse(inputData);
|
|
259
|
-
} catch {
|
|
260
|
-
process.exit(0);
|
|
261
|
-
}
|
|
262
|
-
const cwd = input.cwd || process.cwd();
|
|
263
|
-
loadConfigFromMcpJson(cwd);
|
|
264
|
-
const sessionId = input.session_id || "unknown";
|
|
265
|
-
const transcriptPath = input.transcript_path || "";
|
|
266
|
-
const trigger = input.trigger || "unknown";
|
|
267
|
-
const customInstructions = input.custom_instructions || "";
|
|
268
|
-
let transcriptData = {
|
|
269
|
-
activeFiles: [],
|
|
270
|
-
toolCallCount: 0,
|
|
271
|
-
messageCount: 0,
|
|
272
|
-
lastTools: [],
|
|
273
|
-
messages: [],
|
|
274
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
275
|
-
};
|
|
276
|
-
if (transcriptPath && fs.existsSync(transcriptPath)) {
|
|
277
|
-
transcriptData = parseTranscript(transcriptPath);
|
|
278
|
-
}
|
|
279
|
-
let autoSaveStatus = "";
|
|
280
|
-
if (AUTO_SAVE && API_KEY) {
|
|
281
|
-
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
282
|
-
if (transcriptResult.success) {
|
|
283
|
-
autoSaveStatus = `
|
|
284
|
-
[ContextStream: ${transcriptResult.message}]`;
|
|
285
|
-
} else {
|
|
286
|
-
const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
|
|
287
|
-
if (success) {
|
|
288
|
-
autoSaveStatus = `
|
|
289
|
-
[ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
|
|
290
|
-
} else {
|
|
291
|
-
autoSaveStatus = `
|
|
292
|
-
[ContextStream: Auto-save failed - ${message}]`;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
|
|
297
|
-
const context = `[CONTEXT COMPACTION - ${trigger.toUpperCase()}]${autoSaveStatus}
|
|
298
|
-
|
|
299
|
-
Active files detected: ${filesList}
|
|
300
|
-
Tool calls in session: ${transcriptData.toolCallCount}
|
|
301
|
-
|
|
302
|
-
After compaction, call session_init(is_post_compact=true) to restore context.${customInstructions ? `
|
|
303
|
-
User instructions: ${customInstructions}` : ""}`;
|
|
304
|
-
console.log(
|
|
305
|
-
JSON.stringify({
|
|
306
|
-
additionalContext: context
|
|
307
|
-
})
|
|
308
|
-
);
|
|
309
|
-
process.exit(0);
|
|
310
|
-
}
|
|
311
|
-
var isDirectRun = process.argv[1]?.includes("pre-compact") || process.argv[2] === "pre-compact";
|
|
312
|
-
if (isDirectRun) {
|
|
313
|
-
runPreCompactHook().catch(() => process.exit(0));
|
|
314
|
-
}
|
|
315
|
-
export {
|
|
316
|
-
runPreCompactHook
|
|
317
|
-
};
|