@contextstream/mcp-server 0.4.55 → 0.4.56
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/dist/hooks/runner.js +474 -45
- package/dist/hooks/session-end.js +111 -17
- package/dist/hooks/session-init.js +381 -9
- package/dist/hooks/user-prompt-submit.js +453 -29
- package/dist/index.js +990 -271
- package/dist/test-server.js +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,6 +38,388 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
38
38
|
mod
|
|
39
39
|
));
|
|
40
40
|
|
|
41
|
+
// src/version.ts
|
|
42
|
+
import { createRequire } from "module";
|
|
43
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
44
|
+
import { homedir, platform } from "os";
|
|
45
|
+
import { join } from "path";
|
|
46
|
+
import { spawn } from "child_process";
|
|
47
|
+
function getVersion() {
|
|
48
|
+
if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
|
|
49
|
+
return __CONTEXTSTREAM_VERSION__;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const require2 = createRequire(import.meta.url);
|
|
53
|
+
const pkg = require2("../package.json");
|
|
54
|
+
const version = pkg?.version;
|
|
55
|
+
if (typeof version === "string" && version.trim()) return version.trim();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
return "unknown";
|
|
59
|
+
}
|
|
60
|
+
function compareVersions(v1, v2) {
|
|
61
|
+
const parts1 = v1.split(".").map(Number);
|
|
62
|
+
const parts2 = v2.split(".").map(Number);
|
|
63
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
64
|
+
const p1 = parts1[i] ?? 0;
|
|
65
|
+
const p2 = parts2[i] ?? 0;
|
|
66
|
+
if (p1 < p2) return -1;
|
|
67
|
+
if (p1 > p2) return 1;
|
|
68
|
+
}
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
function getCacheFilePath() {
|
|
72
|
+
return join(homedir(), ".contextstream", "version-cache.json");
|
|
73
|
+
}
|
|
74
|
+
function readCache() {
|
|
75
|
+
try {
|
|
76
|
+
const cacheFile = getCacheFilePath();
|
|
77
|
+
if (!existsSync(cacheFile)) return null;
|
|
78
|
+
const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
|
|
79
|
+
if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
|
|
80
|
+
return data;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function writeCache(latestVersion) {
|
|
86
|
+
try {
|
|
87
|
+
const configDir = join(homedir(), ".contextstream");
|
|
88
|
+
if (!existsSync(configDir)) {
|
|
89
|
+
mkdirSync(configDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
const cacheFile = getCacheFilePath();
|
|
92
|
+
writeFileSync(
|
|
93
|
+
cacheFile,
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
latestVersion,
|
|
96
|
+
checkedAt: Date.now()
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function fetchLatestVersion() {
|
|
103
|
+
try {
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
106
|
+
const response = await fetch(NPM_LATEST_URL, {
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
headers: { Accept: "application/json" }
|
|
109
|
+
});
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
if (!response.ok) return null;
|
|
112
|
+
const data = await response.json();
|
|
113
|
+
return typeof data.version === "string" ? data.version : null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function resolveLatestVersion() {
|
|
119
|
+
const cached = readCache();
|
|
120
|
+
if (cached) return cached.latestVersion;
|
|
121
|
+
if (!latestVersionPromise) {
|
|
122
|
+
latestVersionPromise = fetchLatestVersion().finally(() => {
|
|
123
|
+
latestVersionPromise = null;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const latestVersion = await latestVersionPromise;
|
|
127
|
+
if (latestVersion) {
|
|
128
|
+
writeCache(latestVersion);
|
|
129
|
+
}
|
|
130
|
+
return latestVersion;
|
|
131
|
+
}
|
|
132
|
+
async function checkForUpdates() {
|
|
133
|
+
const notice = await getUpdateNotice();
|
|
134
|
+
if (notice?.behind) {
|
|
135
|
+
showUpdateWarning(notice.current, notice.latest);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function showUpdateWarning(currentVersion, latestVersion) {
|
|
139
|
+
console.error("");
|
|
140
|
+
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
141
|
+
console.error(`\u26A0\uFE0F Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
142
|
+
console.error("");
|
|
143
|
+
console.error(` Run: ${UPGRADE_COMMAND}`);
|
|
144
|
+
console.error("");
|
|
145
|
+
console.error(" Then restart your AI tool to use the new version.");
|
|
146
|
+
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
147
|
+
console.error("");
|
|
148
|
+
}
|
|
149
|
+
async function getUpdateNotice() {
|
|
150
|
+
const currentVersion = VERSION;
|
|
151
|
+
if (currentVersion === "unknown") return null;
|
|
152
|
+
try {
|
|
153
|
+
const latestVersion = await resolveLatestVersion();
|
|
154
|
+
if (!latestVersion) return null;
|
|
155
|
+
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
156
|
+
return {
|
|
157
|
+
current: currentVersion,
|
|
158
|
+
latest: latestVersion,
|
|
159
|
+
behind: true,
|
|
160
|
+
upgrade_command: UPGRADE_COMMAND
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
function getVersionsBehind(current, latest) {
|
|
168
|
+
try {
|
|
169
|
+
const currentParts = current.split(".").map(Number);
|
|
170
|
+
const latestParts = latest.split(".").map(Number);
|
|
171
|
+
if ((latestParts[0] ?? 0) > (currentParts[0] ?? 0)) {
|
|
172
|
+
return 10 + ((latestParts[1] ?? 0) - (currentParts[1] ?? 0));
|
|
173
|
+
}
|
|
174
|
+
const minorDiff = (latestParts[1] ?? 0) - (currentParts[1] ?? 0);
|
|
175
|
+
const patchDiff = (latestParts[2] ?? 0) - (currentParts[2] ?? 0);
|
|
176
|
+
if (minorDiff > 0) {
|
|
177
|
+
return minorDiff;
|
|
178
|
+
} else if (minorDiff === 0 && patchDiff > 0) {
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
return 0;
|
|
182
|
+
} catch {
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function getVersionWarning(notice) {
|
|
187
|
+
if (!notice?.behind) return null;
|
|
188
|
+
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
189
|
+
if (versionsBehind >= 3) {
|
|
190
|
+
return `[VERSION_CRITICAL] MCP server is ${versionsBehind} versions behind (${notice.current} \u2192 ${notice.latest}). Update immediately: ${notice.upgrade_command}`;
|
|
191
|
+
} else if (versionsBehind >= 1) {
|
|
192
|
+
return `[VERSION_NOTICE] MCP server update available (${notice.current} \u2192 ${notice.latest}). Run: ${notice.upgrade_command}`;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
function getVersionInstructions(notice) {
|
|
197
|
+
if (!notice?.behind) return null;
|
|
198
|
+
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
199
|
+
if (versionsBehind >= 5) {
|
|
200
|
+
return `\u26A0\uFE0F CRITICAL: User's MCP is ${versionsBehind} versions behind. Mention update in EVERY response until updated.`;
|
|
201
|
+
} else if (versionsBehind >= 3) {
|
|
202
|
+
return `\u26A0\uFE0F User's MCP is ${versionsBehind} versions behind. Remind about update every 2-3 messages.`;
|
|
203
|
+
} else if (versionsBehind >= 1) {
|
|
204
|
+
return `Note: MCP update available (${notice.current}\u2192${notice.latest}). Mention once at session start.`;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
function getVersionNoticeForHook(notice) {
|
|
209
|
+
if (!notice?.behind) return null;
|
|
210
|
+
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
211
|
+
const severity = versionsBehind >= 5 ? "CRITICAL" : versionsBehind >= 3 ? "IMPORTANT" : "INFO";
|
|
212
|
+
let frequency = "";
|
|
213
|
+
let whatTheyAreMissing = "";
|
|
214
|
+
if (versionsBehind >= 5) {
|
|
215
|
+
frequency = "Mention this update in EVERY response until the user updates.";
|
|
216
|
+
whatTheyAreMissing = `The user is missing out on many new features, important bug fixes, and possible security patches. Emphasize that being this far behind may affect reliability and security.`;
|
|
217
|
+
} else if (versionsBehind >= 3) {
|
|
218
|
+
frequency = "Remind the user about this update every 2-3 messages.";
|
|
219
|
+
whatTheyAreMissing = `The user is missing out on several new features and important fixes that improve reliability and functionality.`;
|
|
220
|
+
} else {
|
|
221
|
+
frequency = "Mention this update once at session start, then only if user asks.";
|
|
222
|
+
whatTheyAreMissing = `Minor update with improvements and fixes available.`;
|
|
223
|
+
}
|
|
224
|
+
return `[VERSION_${severity}] MCP server is ${versionsBehind > 0 ? versionsBehind + " version(s)" : "a version"} behind (${notice.current} \u2192 ${notice.latest}).
|
|
225
|
+
|
|
226
|
+
${whatTheyAreMissing}
|
|
227
|
+
|
|
228
|
+
${frequency}
|
|
229
|
+
|
|
230
|
+
When mentioning the update, provide these commands (user can choose their preferred method):
|
|
231
|
+
- macOS/Linux: curl -fsSL https://contextstream.io/scripts/setup.sh | bash
|
|
232
|
+
- Windows: irm https://contextstream.io/scripts/setup.ps1 | iex
|
|
233
|
+
- npm: npm install -g @contextstream/mcp-server@latest
|
|
234
|
+
|
|
235
|
+
Be helpful but not annoying - frame it positively as access to new capabilities rather than criticism.`;
|
|
236
|
+
}
|
|
237
|
+
function isAutoUpdateEnabled() {
|
|
238
|
+
if (process.env.CONTEXTSTREAM_AUTO_UPDATE === "false") {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const configPath = join(homedir(), ".contextstream", "config.json");
|
|
243
|
+
if (existsSync(configPath)) {
|
|
244
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
245
|
+
if (config.auto_update === false) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
function setAutoUpdatePreference(enabled) {
|
|
254
|
+
try {
|
|
255
|
+
const configDir = join(homedir(), ".contextstream");
|
|
256
|
+
const configPath = join(configDir, "config.json");
|
|
257
|
+
let config = {};
|
|
258
|
+
if (existsSync(configPath)) {
|
|
259
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
260
|
+
} else {
|
|
261
|
+
if (!existsSync(configDir)) {
|
|
262
|
+
mkdirSync(configDir, { recursive: true });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
config.auto_update = enabled;
|
|
266
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async function attemptAutoUpdate() {
|
|
271
|
+
const currentVersion = VERSION;
|
|
272
|
+
if (!isAutoUpdateEnabled()) {
|
|
273
|
+
return {
|
|
274
|
+
attempted: false,
|
|
275
|
+
success: false,
|
|
276
|
+
previousVersion: currentVersion,
|
|
277
|
+
newVersion: null,
|
|
278
|
+
error: "Auto-update disabled"
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const notice = await getUpdateNotice();
|
|
282
|
+
if (!notice?.behind) {
|
|
283
|
+
return {
|
|
284
|
+
attempted: false,
|
|
285
|
+
success: true,
|
|
286
|
+
previousVersion: currentVersion,
|
|
287
|
+
newVersion: null
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const updateMethod = detectUpdateMethod();
|
|
291
|
+
try {
|
|
292
|
+
await runUpdate(updateMethod);
|
|
293
|
+
writeUpdateMarker(currentVersion, notice.latest);
|
|
294
|
+
return {
|
|
295
|
+
attempted: true,
|
|
296
|
+
success: true,
|
|
297
|
+
previousVersion: currentVersion,
|
|
298
|
+
newVersion: notice.latest
|
|
299
|
+
};
|
|
300
|
+
} catch (err) {
|
|
301
|
+
return {
|
|
302
|
+
attempted: true,
|
|
303
|
+
success: false,
|
|
304
|
+
previousVersion: currentVersion,
|
|
305
|
+
newVersion: notice.latest,
|
|
306
|
+
error: err instanceof Error ? err.message : "Update failed"
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function detectUpdateMethod() {
|
|
311
|
+
const execPath = process.argv[1] || "";
|
|
312
|
+
if (execPath.includes("node_modules") || execPath.includes("npm")) {
|
|
313
|
+
return "npm";
|
|
314
|
+
}
|
|
315
|
+
const os = platform();
|
|
316
|
+
if (os === "win32") {
|
|
317
|
+
return "powershell";
|
|
318
|
+
}
|
|
319
|
+
return "curl";
|
|
320
|
+
}
|
|
321
|
+
async function runUpdate(method) {
|
|
322
|
+
return new Promise((resolve16, reject) => {
|
|
323
|
+
let command;
|
|
324
|
+
let args;
|
|
325
|
+
let shell;
|
|
326
|
+
switch (method) {
|
|
327
|
+
case "npm":
|
|
328
|
+
command = "npm";
|
|
329
|
+
args = ["install", "-g", "@contextstream/mcp-server@latest"];
|
|
330
|
+
shell = false;
|
|
331
|
+
break;
|
|
332
|
+
case "curl":
|
|
333
|
+
command = "bash";
|
|
334
|
+
args = ["-c", "curl -fsSL https://contextstream.io/scripts/setup.sh | bash"];
|
|
335
|
+
shell = false;
|
|
336
|
+
break;
|
|
337
|
+
case "powershell":
|
|
338
|
+
command = "powershell";
|
|
339
|
+
args = ["-Command", "irm https://contextstream.io/scripts/setup.ps1 | iex"];
|
|
340
|
+
shell = false;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
const proc = spawn(command, args, {
|
|
344
|
+
shell,
|
|
345
|
+
stdio: "ignore",
|
|
346
|
+
detached: true
|
|
347
|
+
});
|
|
348
|
+
proc.on("error", (err) => {
|
|
349
|
+
reject(err);
|
|
350
|
+
});
|
|
351
|
+
proc.on("close", (code) => {
|
|
352
|
+
if (code === 0) {
|
|
353
|
+
resolve16();
|
|
354
|
+
} else {
|
|
355
|
+
reject(new Error(`Update process exited with code ${code}`));
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
proc.unref();
|
|
359
|
+
setTimeout(() => resolve16(), 1e3);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function writeUpdateMarker(previousVersion, newVersion) {
|
|
363
|
+
try {
|
|
364
|
+
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
365
|
+
const configDir = join(homedir(), ".contextstream");
|
|
366
|
+
if (!existsSync(configDir)) {
|
|
367
|
+
mkdirSync(configDir, { recursive: true });
|
|
368
|
+
}
|
|
369
|
+
writeFileSync(markerPath, JSON.stringify({
|
|
370
|
+
previousVersion,
|
|
371
|
+
newVersion,
|
|
372
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
373
|
+
}));
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function checkUpdateMarker() {
|
|
378
|
+
try {
|
|
379
|
+
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
380
|
+
if (!existsSync(markerPath)) return null;
|
|
381
|
+
const marker = JSON.parse(readFileSync(markerPath, "utf-8"));
|
|
382
|
+
const updatedAt = new Date(marker.updatedAt);
|
|
383
|
+
const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
|
|
384
|
+
if (updatedAt < hourAgo) {
|
|
385
|
+
try {
|
|
386
|
+
__require("fs").unlinkSync(markerPath);
|
|
387
|
+
} catch {
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
return { previousVersion: marker.previousVersion, newVersion: marker.newVersion };
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function clearUpdateMarker() {
|
|
397
|
+
try {
|
|
398
|
+
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
399
|
+
if (existsSync(markerPath)) {
|
|
400
|
+
__require("fs").unlinkSync(markerPath);
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
var NPM_LATEST_URL, AUTO_UPDATE_ENABLED, UPDATE_COMMANDS, UPGRADE_COMMAND, VERSION, CACHE_TTL_MS, latestVersionPromise;
|
|
406
|
+
var init_version = __esm({
|
|
407
|
+
"src/version.ts"() {
|
|
408
|
+
"use strict";
|
|
409
|
+
NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
410
|
+
AUTO_UPDATE_ENABLED = process.env.CONTEXTSTREAM_AUTO_UPDATE !== "false";
|
|
411
|
+
UPDATE_COMMANDS = {
|
|
412
|
+
npm: "npm install -g @contextstream/mcp-server@latest",
|
|
413
|
+
macLinux: "curl -fsSL https://contextstream.io/scripts/setup.sh | bash",
|
|
414
|
+
windows: "irm https://contextstream.io/scripts/setup.ps1 | iex"
|
|
415
|
+
};
|
|
416
|
+
UPGRADE_COMMAND = UPDATE_COMMANDS.npm;
|
|
417
|
+
VERSION = getVersion();
|
|
418
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
419
|
+
latestVersionPromise = null;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
41
423
|
// node_modules/ignore/index.js
|
|
42
424
|
var require_ignore = __commonJS({
|
|
43
425
|
"node_modules/ignore/index.js"(exports, module) {
|
|
@@ -2772,46 +3154,291 @@ function loadConfigFromMcpJson(cwd) {
|
|
|
2772
3154
|
}
|
|
2773
3155
|
}
|
|
2774
3156
|
}
|
|
3157
|
+
function readTranscriptFile(transcriptPath) {
|
|
3158
|
+
try {
|
|
3159
|
+
const content = fs10.readFileSync(transcriptPath, "utf-8");
|
|
3160
|
+
const lines = content.trim().split("\n");
|
|
3161
|
+
const messages = [];
|
|
3162
|
+
for (const line of lines) {
|
|
3163
|
+
try {
|
|
3164
|
+
const entry = JSON.parse(line);
|
|
3165
|
+
if (entry.type === "user" || entry.type === "assistant") {
|
|
3166
|
+
const msg = entry.message;
|
|
3167
|
+
if (msg?.role && msg?.content) {
|
|
3168
|
+
let textContent = "";
|
|
3169
|
+
if (typeof msg.content === "string") {
|
|
3170
|
+
textContent = msg.content;
|
|
3171
|
+
} else if (Array.isArray(msg.content)) {
|
|
3172
|
+
textContent = msg.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
|
|
3173
|
+
}
|
|
3174
|
+
if (textContent) {
|
|
3175
|
+
messages.push({ role: msg.role, content: textContent });
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
} catch {
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
return messages;
|
|
3183
|
+
} catch {
|
|
3184
|
+
return [];
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
function extractLastExchange(input, editorFormat) {
|
|
3188
|
+
try {
|
|
3189
|
+
if (editorFormat === "claude" && input.transcript_path) {
|
|
3190
|
+
const messages = readTranscriptFile(input.transcript_path);
|
|
3191
|
+
if (messages.length < 2) return null;
|
|
3192
|
+
let lastAssistantIdx = -1;
|
|
3193
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3194
|
+
if (messages[i].role === "assistant") {
|
|
3195
|
+
lastAssistantIdx = i;
|
|
3196
|
+
break;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if (lastAssistantIdx < 1) return null;
|
|
3200
|
+
let lastUserIdx = -1;
|
|
3201
|
+
for (let i = lastAssistantIdx - 1; i >= 0; i--) {
|
|
3202
|
+
if (messages[i].role === "user") {
|
|
3203
|
+
lastUserIdx = i;
|
|
3204
|
+
break;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
if (lastUserIdx < 0) return null;
|
|
3208
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3209
|
+
return {
|
|
3210
|
+
userMessage: { role: "user", content: messages[lastUserIdx].content, timestamp: now },
|
|
3211
|
+
assistantMessage: { role: "assistant", content: messages[lastAssistantIdx].content, timestamp: now },
|
|
3212
|
+
sessionId: input.session_id
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
if (editorFormat === "claude" && input.session?.messages) {
|
|
3216
|
+
const messages = input.session.messages;
|
|
3217
|
+
if (messages.length < 2) return null;
|
|
3218
|
+
let lastAssistantIdx = -1;
|
|
3219
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3220
|
+
if (messages[i].role === "assistant") {
|
|
3221
|
+
lastAssistantIdx = i;
|
|
3222
|
+
break;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
if (lastAssistantIdx < 1) return null;
|
|
3226
|
+
let lastUserIdx = -1;
|
|
3227
|
+
for (let i = lastAssistantIdx - 1; i >= 0; i--) {
|
|
3228
|
+
if (messages[i].role === "user") {
|
|
3229
|
+
lastUserIdx = i;
|
|
3230
|
+
break;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
if (lastUserIdx < 0) return null;
|
|
3234
|
+
const userMsg = messages[lastUserIdx];
|
|
3235
|
+
const assistantMsg = messages[lastAssistantIdx];
|
|
3236
|
+
const extractContent = (content) => {
|
|
3237
|
+
if (typeof content === "string") return content;
|
|
3238
|
+
if (Array.isArray(content)) {
|
|
3239
|
+
return content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
|
|
3240
|
+
}
|
|
3241
|
+
return "";
|
|
3242
|
+
};
|
|
3243
|
+
const userContent = extractContent(userMsg.content);
|
|
3244
|
+
const assistantContent = extractContent(assistantMsg.content);
|
|
3245
|
+
if (!userContent || !assistantContent) return null;
|
|
3246
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3247
|
+
return {
|
|
3248
|
+
userMessage: { role: "user", content: userContent, timestamp: now },
|
|
3249
|
+
assistantMessage: { role: "assistant", content: assistantContent, timestamp: now },
|
|
3250
|
+
sessionId: input.session_id
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
if ((editorFormat === "cursor" || editorFormat === "antigravity") && input.history) {
|
|
3254
|
+
const history = input.history;
|
|
3255
|
+
if (history.length < 2) return null;
|
|
3256
|
+
let lastAssistantIdx = -1;
|
|
3257
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
3258
|
+
if (history[i].role === "assistant") {
|
|
3259
|
+
lastAssistantIdx = i;
|
|
3260
|
+
break;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
if (lastAssistantIdx < 1) return null;
|
|
3264
|
+
let lastUserIdx = -1;
|
|
3265
|
+
for (let i = lastAssistantIdx - 1; i >= 0; i--) {
|
|
3266
|
+
if (history[i].role === "user") {
|
|
3267
|
+
lastUserIdx = i;
|
|
3268
|
+
break;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
if (lastUserIdx < 0) return null;
|
|
3272
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3273
|
+
return {
|
|
3274
|
+
userMessage: { role: "user", content: history[lastUserIdx].content, timestamp: now },
|
|
3275
|
+
assistantMessage: { role: "assistant", content: history[lastAssistantIdx].content, timestamp: now },
|
|
3276
|
+
sessionId: input.conversationId || input.session_id
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
return null;
|
|
3280
|
+
} catch {
|
|
3281
|
+
return null;
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
async function saveLastExchange(exchange, cwd, clientName) {
|
|
3285
|
+
if (!API_KEY2) return;
|
|
3286
|
+
const sessionId = exchange.sessionId || `hook-${Buffer.from(cwd).toString("base64").slice(0, 16)}`;
|
|
3287
|
+
const payload = {
|
|
3288
|
+
session_id: sessionId,
|
|
3289
|
+
user_message: exchange.userMessage.content,
|
|
3290
|
+
assistant_message: exchange.assistantMessage.content,
|
|
3291
|
+
client_name: clientName
|
|
3292
|
+
};
|
|
3293
|
+
if (WORKSPACE_ID) {
|
|
3294
|
+
payload.workspace_id = WORKSPACE_ID;
|
|
3295
|
+
}
|
|
3296
|
+
if (PROJECT_ID) {
|
|
3297
|
+
payload.project_id = PROJECT_ID;
|
|
3298
|
+
}
|
|
3299
|
+
try {
|
|
3300
|
+
const controller = new AbortController();
|
|
3301
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
3302
|
+
await fetch(`${API_URL2}/api/v1/transcripts/exchange`, {
|
|
3303
|
+
method: "POST",
|
|
3304
|
+
headers: {
|
|
3305
|
+
"Content-Type": "application/json",
|
|
3306
|
+
"X-API-Key": API_KEY2
|
|
3307
|
+
},
|
|
3308
|
+
body: JSON.stringify(payload),
|
|
3309
|
+
signal: controller.signal
|
|
3310
|
+
});
|
|
3311
|
+
clearTimeout(timeoutId);
|
|
3312
|
+
} catch {
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
2775
3315
|
async function fetchSessionContext() {
|
|
2776
3316
|
if (!API_KEY2) return null;
|
|
2777
3317
|
try {
|
|
2778
3318
|
const controller = new AbortController();
|
|
2779
3319
|
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
2780
|
-
const url =
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
const response = await fetch(url
|
|
2789
|
-
method: "
|
|
3320
|
+
const url = `${API_URL2}/api/v1/context/smart`;
|
|
3321
|
+
const body = {
|
|
3322
|
+
user_message: "hook context fetch",
|
|
3323
|
+
max_tokens: 200,
|
|
3324
|
+
format: "readable"
|
|
3325
|
+
};
|
|
3326
|
+
if (WORKSPACE_ID) body.workspace_id = WORKSPACE_ID;
|
|
3327
|
+
if (PROJECT_ID) body.project_id = PROJECT_ID;
|
|
3328
|
+
const response = await fetch(url, {
|
|
3329
|
+
method: "POST",
|
|
2790
3330
|
headers: {
|
|
2791
|
-
"X-API-Key": API_KEY2
|
|
3331
|
+
"X-API-Key": API_KEY2,
|
|
3332
|
+
"Content-Type": "application/json"
|
|
2792
3333
|
},
|
|
3334
|
+
body: JSON.stringify(body),
|
|
2793
3335
|
signal: controller.signal
|
|
2794
3336
|
});
|
|
2795
3337
|
clearTimeout(timeoutId);
|
|
2796
3338
|
if (response.ok) {
|
|
2797
|
-
|
|
3339
|
+
const data = await response.json();
|
|
3340
|
+
return transformSmartContextResponse(data);
|
|
2798
3341
|
}
|
|
2799
3342
|
return null;
|
|
2800
3343
|
} catch {
|
|
2801
3344
|
return null;
|
|
2802
3345
|
}
|
|
2803
3346
|
}
|
|
2804
|
-
function
|
|
3347
|
+
function transformSmartContextResponse(data) {
|
|
3348
|
+
try {
|
|
3349
|
+
const response = data;
|
|
3350
|
+
const result = {};
|
|
3351
|
+
if (response.data?.warnings && response.data.warnings.length > 0) {
|
|
3352
|
+
result.lessons = response.data.warnings.map((w) => ({
|
|
3353
|
+
title: "Lesson",
|
|
3354
|
+
trigger: "",
|
|
3355
|
+
prevention: w.replace(/^\[LESSONS_WARNING\]\s*/, "")
|
|
3356
|
+
}));
|
|
3357
|
+
}
|
|
3358
|
+
if (response.data?.items) {
|
|
3359
|
+
for (const item of response.data.items) {
|
|
3360
|
+
if (item.item_type === "preference") {
|
|
3361
|
+
if (!result.preferences) result.preferences = [];
|
|
3362
|
+
result.preferences.push({
|
|
3363
|
+
title: item.title,
|
|
3364
|
+
content: item.content,
|
|
3365
|
+
importance: item.metadata?.importance || "medium"
|
|
3366
|
+
});
|
|
3367
|
+
} else if (item.item_type === "plan") {
|
|
3368
|
+
if (!result.active_plans) result.active_plans = [];
|
|
3369
|
+
result.active_plans.push({
|
|
3370
|
+
title: item.title,
|
|
3371
|
+
status: "active"
|
|
3372
|
+
});
|
|
3373
|
+
} else if (item.item_type === "task") {
|
|
3374
|
+
if (!result.pending_tasks) result.pending_tasks = [];
|
|
3375
|
+
result.pending_tasks.push({
|
|
3376
|
+
title: item.title,
|
|
3377
|
+
status: "pending"
|
|
3378
|
+
});
|
|
3379
|
+
} else if (item.item_type === "reminder") {
|
|
3380
|
+
if (!result.reminders) result.reminders = [];
|
|
3381
|
+
result.reminders.push({
|
|
3382
|
+
title: item.title,
|
|
3383
|
+
content: item.content
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
return result;
|
|
3389
|
+
} catch {
|
|
3390
|
+
return null;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
function buildClaudeReminder(ctx, versionNotice) {
|
|
3394
|
+
const parts = [];
|
|
3395
|
+
if (versionNotice?.behind) {
|
|
3396
|
+
const versionInfo = getVersionNoticeForHook(versionNotice);
|
|
3397
|
+
if (versionInfo) {
|
|
3398
|
+
parts.push(versionInfo);
|
|
3399
|
+
parts.push("");
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
|
|
3403
|
+
if (highImportancePrefs.length > 0) {
|
|
3404
|
+
parts.push(`[USER PREFERENCES - Always respect these]`);
|
|
3405
|
+
for (const pref of highImportancePrefs.slice(0, 5)) {
|
|
3406
|
+
parts.push(`\u2022 ${pref.title}: ${pref.content}`);
|
|
3407
|
+
}
|
|
3408
|
+
parts.push("");
|
|
3409
|
+
}
|
|
3410
|
+
parts.push(REMINDER);
|
|
3411
|
+
return parts.join("\n");
|
|
3412
|
+
}
|
|
3413
|
+
function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
2805
3414
|
const parts = [ENHANCED_REMINDER_HEADER];
|
|
3415
|
+
if (versionNotice?.behind) {
|
|
3416
|
+
const versionInfo = getVersionNoticeForHook(versionNotice);
|
|
3417
|
+
if (versionInfo) {
|
|
3418
|
+
parts.push(`## \u{1F504} UPDATE AVAILABLE
|
|
3419
|
+
`);
|
|
3420
|
+
parts.push(versionInfo);
|
|
3421
|
+
parts.push("");
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
2806
3424
|
if (isNewSession2) {
|
|
2807
3425
|
parts.push(`## \u{1F680} NEW SESSION DETECTED
|
|
2808
3426
|
1. Call \`init(folder_path="...")\` - this triggers project indexing
|
|
2809
3427
|
2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
|
|
2810
|
-
3.
|
|
2811
|
-
4.
|
|
3428
|
+
3. Generate a unique session_id (e.g., "session-" + timestamp or UUID) - use this for ALL context() calls
|
|
3429
|
+
4. Call \`context(user_message="...", save_exchange=true, session_id="<your-session-id>")\` for task-specific context
|
|
3430
|
+
5. Use \`search(mode="hybrid")\` for code discovery (not Glob/Grep/Read)
|
|
2812
3431
|
|
|
2813
3432
|
`);
|
|
2814
3433
|
}
|
|
3434
|
+
const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
|
|
3435
|
+
if (highImportancePrefs.length > 0) {
|
|
3436
|
+
parts.push(`## \u2699\uFE0F USER PREFERENCES - Always respect these`);
|
|
3437
|
+
for (const pref of highImportancePrefs.slice(0, 5)) {
|
|
3438
|
+
parts.push(`- **${pref.title}**: ${pref.content}`);
|
|
3439
|
+
}
|
|
3440
|
+
parts.push("");
|
|
3441
|
+
}
|
|
2815
3442
|
if (ctx?.lessons && ctx.lessons.length > 0) {
|
|
2816
3443
|
parts.push(`## \u26A0\uFE0F LESSONS FROM PAST MISTAKES`);
|
|
2817
3444
|
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
@@ -2929,20 +3556,26 @@ async function runUserPromptSubmitHook() {
|
|
|
2929
3556
|
}
|
|
2930
3557
|
const editorFormat = detectEditorFormat2(input);
|
|
2931
3558
|
const cwd = input.cwd || process.cwd();
|
|
3559
|
+
loadConfigFromMcpJson(cwd);
|
|
3560
|
+
const versionNoticePromise = getUpdateNotice();
|
|
3561
|
+
const lastExchange = extractLastExchange(input, editorFormat);
|
|
3562
|
+
const clientName = editorFormat === "claude" ? "claude-code" : editorFormat;
|
|
3563
|
+
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
2932
3564
|
if (editorFormat === "claude") {
|
|
3565
|
+
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
3566
|
+
const claudeReminder = buildClaudeReminder(ctx, versionNotice);
|
|
2933
3567
|
console.log(
|
|
2934
3568
|
JSON.stringify({
|
|
2935
3569
|
hookSpecificOutput: {
|
|
2936
3570
|
hookEventName: "UserPromptSubmit",
|
|
2937
|
-
additionalContext:
|
|
3571
|
+
additionalContext: claudeReminder
|
|
2938
3572
|
}
|
|
2939
3573
|
})
|
|
2940
3574
|
);
|
|
2941
3575
|
} else if (editorFormat === "cline") {
|
|
2942
|
-
loadConfigFromMcpJson(cwd);
|
|
2943
3576
|
const newSession = isNewSession(input, editorFormat);
|
|
2944
|
-
const ctx = await fetchSessionContext();
|
|
2945
|
-
const enhancedReminder = buildEnhancedReminder(ctx, newSession);
|
|
3577
|
+
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
3578
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
2946
3579
|
console.log(
|
|
2947
3580
|
JSON.stringify({
|
|
2948
3581
|
cancel: false,
|
|
@@ -2950,10 +3583,12 @@ async function runUserPromptSubmitHook() {
|
|
|
2950
3583
|
})
|
|
2951
3584
|
);
|
|
2952
3585
|
} else if (editorFormat === "cursor") {
|
|
2953
|
-
loadConfigFromMcpJson(cwd);
|
|
2954
3586
|
const newSession = isNewSession(input, editorFormat);
|
|
2955
|
-
const ctx = await fetchSessionContext();
|
|
2956
|
-
|
|
3587
|
+
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
3588
|
+
let cursorReminder = ctx?.lessons?.length ? `[CONTEXTSTREAM] \u26A0\uFE0F ${ctx.lessons.length} lessons from past mistakes. Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="hybrid") before Glob/Grep. After file edits: project(action="index").` : `[CONTEXTSTREAM] Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="hybrid") before Glob/Grep/Read. After file edits: project(action="index").`;
|
|
3589
|
+
if (versionNotice?.behind) {
|
|
3590
|
+
cursorReminder += ` [UPDATE v${versionNotice.current}\u2192${versionNotice.latest}]`;
|
|
3591
|
+
}
|
|
2957
3592
|
console.log(
|
|
2958
3593
|
JSON.stringify({
|
|
2959
3594
|
continue: true,
|
|
@@ -2961,10 +3596,9 @@ async function runUserPromptSubmitHook() {
|
|
|
2961
3596
|
})
|
|
2962
3597
|
);
|
|
2963
3598
|
} else if (editorFormat === "antigravity") {
|
|
2964
|
-
loadConfigFromMcpJson(cwd);
|
|
2965
3599
|
const newSession = isNewSession(input, editorFormat);
|
|
2966
|
-
const ctx = await fetchSessionContext();
|
|
2967
|
-
const enhancedReminder = buildEnhancedReminder(ctx, newSession);
|
|
3600
|
+
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
3601
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
2968
3602
|
console.log(
|
|
2969
3603
|
JSON.stringify({
|
|
2970
3604
|
cancel: false,
|
|
@@ -2978,6 +3612,7 @@ var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, ENHANCED_R
|
|
|
2978
3612
|
var init_user_prompt_submit = __esm({
|
|
2979
3613
|
"src/hooks/user-prompt-submit.ts"() {
|
|
2980
3614
|
"use strict";
|
|
3615
|
+
init_version();
|
|
2981
3616
|
ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
|
|
2982
3617
|
API_URL2 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
2983
3618
|
API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
@@ -2985,9 +3620,11 @@ var init_user_prompt_submit = __esm({
|
|
|
2985
3620
|
PROJECT_ID = null;
|
|
2986
3621
|
REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
|
|
2987
3622
|
|
|
2988
|
-
1. FIRST: Call mcp__contextstream__context(user_message="...") before ANY other tool
|
|
3623
|
+
1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
|
|
2989
3624
|
- Returns: dynamic rules, lessons from past mistakes, relevant context
|
|
2990
3625
|
- Check response for: [LESSONS_WARNING], [RULES_NOTICE], preferences
|
|
3626
|
+
- save_exchange=true saves each conversation turn for later retrieval
|
|
3627
|
+
- Use a consistent session_id for the entire conversation (generate once on first message)
|
|
2991
3628
|
|
|
2992
3629
|
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
2993
3630
|
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
@@ -3014,7 +3651,7 @@ var init_user_prompt_submit = __esm({
|
|
|
3014
3651
|
|
|
3015
3652
|
6. SKIP CONTEXTSTREAM: If user preference says "skip contextstream", use local tools instead
|
|
3016
3653
|
[END]`;
|
|
3017
|
-
ENHANCED_REMINDER_HEADER =
|
|
3654
|
+
ENHANCED_REMINDER_HEADER = `\u2B21 ContextStream \u2014 Smart Context & Memory
|
|
3018
3655
|
|
|
3019
3656
|
`;
|
|
3020
3657
|
isDirectRun3 = process.argv[1]?.includes("user-prompt-submit") || process.argv[2] === "user-prompt-submit";
|
|
@@ -4559,13 +5196,44 @@ async function fetchSessionContext2() {
|
|
|
4559
5196
|
return null;
|
|
4560
5197
|
}
|
|
4561
5198
|
}
|
|
4562
|
-
function formatContext(ctx) {
|
|
5199
|
+
function formatContext(ctx, options = {}) {
|
|
5200
|
+
const parts = ["\u2B21 ContextStream \u2014 Smart Context & Memory", ""];
|
|
5201
|
+
if (options.updateMarker) {
|
|
5202
|
+
parts.push(`
|
|
5203
|
+
## \u{1F389} UPDATE COMPLETE - RESTART CHAT SESSION`);
|
|
5204
|
+
parts.push(`ContextStream was auto-updated from v${options.updateMarker.previousVersion} to v${options.updateMarker.newVersion}.`);
|
|
5205
|
+
parts.push(`**Tell the user:** "ContextStream has been updated! Please start a new chat session (or restart your AI tool) to use the new version with the latest features and fixes."`);
|
|
5206
|
+
parts.push("");
|
|
5207
|
+
} else if (options.autoUpdateResult?.attempted) {
|
|
5208
|
+
if (options.autoUpdateResult.success) {
|
|
5209
|
+
parts.push(`
|
|
5210
|
+
## \u{1F504} AUTO-UPDATE INITIATED`);
|
|
5211
|
+
parts.push(`ContextStream is updating from v${options.autoUpdateResult.previousVersion} to v${options.autoUpdateResult.newVersion}.`);
|
|
5212
|
+
parts.push(`**Tell the user:** "ContextStream is updating in the background. Please start a new chat session (or restart your AI tool) in a moment to use the new version."`);
|
|
5213
|
+
parts.push("");
|
|
5214
|
+
} else if (options.autoUpdateResult.error) {
|
|
5215
|
+
parts.push(`
|
|
5216
|
+
## \u26A0\uFE0F AUTO-UPDATE FAILED`);
|
|
5217
|
+
parts.push(`Automatic update failed: ${options.autoUpdateResult.error}`);
|
|
5218
|
+
const versionInfo = getVersionNoticeForHook(options.versionNotice || null);
|
|
5219
|
+
if (versionInfo) {
|
|
5220
|
+
parts.push(versionInfo);
|
|
5221
|
+
}
|
|
5222
|
+
parts.push("");
|
|
5223
|
+
}
|
|
5224
|
+
} else if (options.versionNotice?.behind && !isAutoUpdateEnabled()) {
|
|
5225
|
+
const versionInfo = getVersionNoticeForHook(options.versionNotice);
|
|
5226
|
+
if (versionInfo) {
|
|
5227
|
+
parts.push(`
|
|
5228
|
+
## \u{1F504} UPDATE AVAILABLE (auto-update disabled)`);
|
|
5229
|
+
parts.push(versionInfo);
|
|
5230
|
+
parts.push("");
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
4563
5233
|
if (!ctx) {
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
No stored context found. Call \`mcp__contextstream__context(user_message="starting new session")\` to initialize.`;
|
|
5234
|
+
parts.push('\nNo stored context found. Call `mcp__contextstream__context(user_message="starting new session")` to initialize.');
|
|
5235
|
+
return parts.join("\n");
|
|
4567
5236
|
}
|
|
4568
|
-
const parts = ["[ContextStream Session Start]"];
|
|
4569
5237
|
if (ctx.lessons && ctx.lessons.length > 0) {
|
|
4570
5238
|
parts.push("\n## \u26A0\uFE0F Lessons from Past Mistakes");
|
|
4571
5239
|
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
@@ -4613,8 +5281,21 @@ async function runSessionInitHook() {
|
|
|
4613
5281
|
}
|
|
4614
5282
|
const cwd = input.cwd || process.cwd();
|
|
4615
5283
|
loadConfigFromMcpJson8(cwd);
|
|
4616
|
-
const
|
|
4617
|
-
|
|
5284
|
+
const updateMarker = checkUpdateMarker();
|
|
5285
|
+
if (updateMarker) {
|
|
5286
|
+
clearUpdateMarker();
|
|
5287
|
+
}
|
|
5288
|
+
const [context, autoUpdateResult, versionNotice] = await Promise.all([
|
|
5289
|
+
fetchSessionContext2(),
|
|
5290
|
+
updateMarker ? Promise.resolve(null) : attemptAutoUpdate(),
|
|
5291
|
+
// Skip if already updated
|
|
5292
|
+
getUpdateNotice()
|
|
5293
|
+
]);
|
|
5294
|
+
const formattedContext = formatContext(context, {
|
|
5295
|
+
autoUpdateResult,
|
|
5296
|
+
versionNotice,
|
|
5297
|
+
updateMarker
|
|
5298
|
+
});
|
|
4618
5299
|
console.log(
|
|
4619
5300
|
JSON.stringify({
|
|
4620
5301
|
hookSpecificOutput: {
|
|
@@ -4629,6 +5310,7 @@ var ENABLED12, API_URL10, API_KEY10, WORKSPACE_ID8, PROJECT_ID2, isDirectRun12;
|
|
|
4629
5310
|
var init_session_init = __esm({
|
|
4630
5311
|
"src/hooks/session-init.ts"() {
|
|
4631
5312
|
"use strict";
|
|
5313
|
+
init_version();
|
|
4632
5314
|
ENABLED12 = process.env.CONTEXTSTREAM_SESSION_INIT_ENABLED !== "false";
|
|
4633
5315
|
API_URL10 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4634
5316
|
API_KEY10 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
@@ -4669,15 +5351,18 @@ function loadConfigFromMcpJson9(cwd) {
|
|
|
4669
5351
|
}
|
|
4670
5352
|
}
|
|
4671
5353
|
}
|
|
4672
|
-
if (!WORKSPACE_ID9) {
|
|
5354
|
+
if (!WORKSPACE_ID9 || !PROJECT_ID3) {
|
|
4673
5355
|
const csConfigPath = path20.join(searchDir, ".contextstream", "config.json");
|
|
4674
5356
|
if (fs19.existsSync(csConfigPath)) {
|
|
4675
5357
|
try {
|
|
4676
5358
|
const content = fs19.readFileSync(csConfigPath, "utf-8");
|
|
4677
5359
|
const csConfig = JSON.parse(content);
|
|
4678
|
-
if (csConfig.workspace_id) {
|
|
5360
|
+
if (csConfig.workspace_id && !WORKSPACE_ID9) {
|
|
4679
5361
|
WORKSPACE_ID9 = csConfig.workspace_id;
|
|
4680
5362
|
}
|
|
5363
|
+
if (csConfig.project_id && !PROJECT_ID3) {
|
|
5364
|
+
PROJECT_ID3 = csConfig.project_id;
|
|
5365
|
+
}
|
|
4681
5366
|
} catch {
|
|
4682
5367
|
}
|
|
4683
5368
|
}
|
|
@@ -4709,7 +5394,9 @@ function parseTranscriptStats(transcriptPath) {
|
|
|
4709
5394
|
messageCount: 0,
|
|
4710
5395
|
toolCallCount: 0,
|
|
4711
5396
|
duration: 0,
|
|
4712
|
-
filesModified: []
|
|
5397
|
+
filesModified: [],
|
|
5398
|
+
messages: [],
|
|
5399
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4713
5400
|
};
|
|
4714
5401
|
if (!transcriptPath || !fs19.existsSync(transcriptPath)) {
|
|
4715
5402
|
return stats;
|
|
@@ -4724,26 +5411,63 @@ function parseTranscriptStats(transcriptPath) {
|
|
|
4724
5411
|
if (!line.trim()) continue;
|
|
4725
5412
|
try {
|
|
4726
5413
|
const entry = JSON.parse(line);
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
} else if (entry.type === "tool_use") {
|
|
4730
|
-
stats.toolCallCount++;
|
|
4731
|
-
if (["Write", "Edit", "NotebookEdit"].includes(entry.name || "")) {
|
|
4732
|
-
const filePath = entry.input?.file_path;
|
|
4733
|
-
if (filePath) {
|
|
4734
|
-
modifiedFiles.add(filePath);
|
|
4735
|
-
}
|
|
4736
|
-
}
|
|
4737
|
-
}
|
|
5414
|
+
const msgType = entry.type || "";
|
|
5415
|
+
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
4738
5416
|
if (entry.timestamp) {
|
|
4739
5417
|
const ts = new Date(entry.timestamp);
|
|
4740
5418
|
if (!firstTimestamp || ts < firstTimestamp) {
|
|
4741
5419
|
firstTimestamp = ts;
|
|
5420
|
+
stats.startedAt = entry.timestamp;
|
|
4742
5421
|
}
|
|
4743
5422
|
if (!lastTimestamp || ts > lastTimestamp) {
|
|
4744
5423
|
lastTimestamp = ts;
|
|
4745
5424
|
}
|
|
4746
5425
|
}
|
|
5426
|
+
if (msgType === "user" || entry.role === "user") {
|
|
5427
|
+
stats.messageCount++;
|
|
5428
|
+
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
5429
|
+
if (userContent) {
|
|
5430
|
+
stats.messages.push({
|
|
5431
|
+
role: "user",
|
|
5432
|
+
content: userContent,
|
|
5433
|
+
timestamp
|
|
5434
|
+
});
|
|
5435
|
+
}
|
|
5436
|
+
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
5437
|
+
stats.messageCount++;
|
|
5438
|
+
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
5439
|
+
if (assistantContent) {
|
|
5440
|
+
stats.messages.push({
|
|
5441
|
+
role: "assistant",
|
|
5442
|
+
content: assistantContent,
|
|
5443
|
+
timestamp
|
|
5444
|
+
});
|
|
5445
|
+
}
|
|
5446
|
+
} else if (msgType === "tool_use") {
|
|
5447
|
+
stats.toolCallCount++;
|
|
5448
|
+
const toolName = entry.name || "";
|
|
5449
|
+
const toolInput = entry.input || {};
|
|
5450
|
+
if (["Write", "Edit", "NotebookEdit"].includes(toolName)) {
|
|
5451
|
+
const filePath = toolInput.file_path || toolInput.notebook_path;
|
|
5452
|
+
if (filePath) {
|
|
5453
|
+
modifiedFiles.add(filePath);
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
stats.messages.push({
|
|
5457
|
+
role: "assistant",
|
|
5458
|
+
content: `[Tool: ${toolName}]`,
|
|
5459
|
+
timestamp,
|
|
5460
|
+
tool_calls: { name: toolName, input: toolInput }
|
|
5461
|
+
});
|
|
5462
|
+
} else if (msgType === "tool_result") {
|
|
5463
|
+
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
5464
|
+
stats.messages.push({
|
|
5465
|
+
role: "tool",
|
|
5466
|
+
content: resultContent,
|
|
5467
|
+
timestamp,
|
|
5468
|
+
tool_results: { name: entry.name }
|
|
5469
|
+
});
|
|
5470
|
+
}
|
|
4747
5471
|
} catch {
|
|
4748
5472
|
continue;
|
|
4749
5473
|
}
|
|
@@ -4756,10 +5480,61 @@ function parseTranscriptStats(transcriptPath) {
|
|
|
4756
5480
|
}
|
|
4757
5481
|
return stats;
|
|
4758
5482
|
}
|
|
5483
|
+
async function saveFullTranscript2(sessionId, stats, reason) {
|
|
5484
|
+
if (!API_KEY11) {
|
|
5485
|
+
return { success: false, message: "No API key configured" };
|
|
5486
|
+
}
|
|
5487
|
+
if (stats.messages.length === 0) {
|
|
5488
|
+
return { success: false, message: "No messages to save" };
|
|
5489
|
+
}
|
|
5490
|
+
const payload = {
|
|
5491
|
+
session_id: sessionId,
|
|
5492
|
+
messages: stats.messages,
|
|
5493
|
+
started_at: stats.startedAt,
|
|
5494
|
+
source_type: "session_end",
|
|
5495
|
+
title: `Session transcript (${reason})`,
|
|
5496
|
+
metadata: {
|
|
5497
|
+
reason,
|
|
5498
|
+
tool_call_count: stats.toolCallCount,
|
|
5499
|
+
files_modified: stats.filesModified.slice(0, 20),
|
|
5500
|
+
duration_seconds: stats.duration
|
|
5501
|
+
},
|
|
5502
|
+
tags: ["session_end", reason]
|
|
5503
|
+
};
|
|
5504
|
+
if (WORKSPACE_ID9) {
|
|
5505
|
+
payload.workspace_id = WORKSPACE_ID9;
|
|
5506
|
+
}
|
|
5507
|
+
if (PROJECT_ID3) {
|
|
5508
|
+
payload.project_id = PROJECT_ID3;
|
|
5509
|
+
}
|
|
5510
|
+
try {
|
|
5511
|
+
const controller = new AbortController();
|
|
5512
|
+
const timeoutId = setTimeout(() => controller.abort(), 15e3);
|
|
5513
|
+
const response = await fetch(`${API_URL11}/api/v1/transcripts`, {
|
|
5514
|
+
method: "POST",
|
|
5515
|
+
headers: {
|
|
5516
|
+
"Content-Type": "application/json",
|
|
5517
|
+
"X-API-Key": API_KEY11
|
|
5518
|
+
},
|
|
5519
|
+
body: JSON.stringify(payload),
|
|
5520
|
+
signal: controller.signal
|
|
5521
|
+
});
|
|
5522
|
+
clearTimeout(timeoutId);
|
|
5523
|
+
if (response.ok) {
|
|
5524
|
+
return { success: true, message: `Transcript saved (${stats.messages.length} messages)` };
|
|
5525
|
+
}
|
|
5526
|
+
return { success: false, message: `API error: ${response.status}` };
|
|
5527
|
+
} catch (error) {
|
|
5528
|
+
return { success: false, message: String(error) };
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
4759
5531
|
async function finalizeSession(sessionId, stats, reason) {
|
|
4760
5532
|
if (!API_KEY11) return;
|
|
5533
|
+
if (SAVE_TRANSCRIPT && stats.messages.length > 0) {
|
|
5534
|
+
await saveFullTranscript2(sessionId, stats, reason);
|
|
5535
|
+
}
|
|
4761
5536
|
const payload = {
|
|
4762
|
-
event_type: "
|
|
5537
|
+
event_type: "manual_note",
|
|
4763
5538
|
title: `Session Ended: ${reason}`,
|
|
4764
5539
|
content: JSON.stringify({
|
|
4765
5540
|
session_id: sessionId,
|
|
@@ -4775,8 +5550,7 @@ async function finalizeSession(sessionId, stats, reason) {
|
|
|
4775
5550
|
}),
|
|
4776
5551
|
importance: "low",
|
|
4777
5552
|
tags: ["session", "end", reason],
|
|
4778
|
-
source_type: "hook"
|
|
4779
|
-
session_id: sessionId
|
|
5553
|
+
source_type: "hook"
|
|
4780
5554
|
};
|
|
4781
5555
|
if (WORKSPACE_ID9) {
|
|
4782
5556
|
payload.workspace_id = WORKSPACE_ID9;
|
|
@@ -4823,14 +5597,16 @@ async function runSessionEndHook() {
|
|
|
4823
5597
|
await finalizeSession(sessionId, stats, reason);
|
|
4824
5598
|
process.exit(0);
|
|
4825
5599
|
}
|
|
4826
|
-
var ENABLED13, API_URL11, API_KEY11, WORKSPACE_ID9, isDirectRun13;
|
|
5600
|
+
var ENABLED13, SAVE_TRANSCRIPT, API_URL11, API_KEY11, WORKSPACE_ID9, PROJECT_ID3, isDirectRun13;
|
|
4827
5601
|
var init_session_end = __esm({
|
|
4828
5602
|
"src/hooks/session-end.ts"() {
|
|
4829
5603
|
"use strict";
|
|
4830
5604
|
ENABLED13 = process.env.CONTEXTSTREAM_SESSION_END_ENABLED !== "false";
|
|
5605
|
+
SAVE_TRANSCRIPT = process.env.CONTEXTSTREAM_SESSION_END_SAVE_TRANSCRIPT !== "false";
|
|
4831
5606
|
API_URL11 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4832
5607
|
API_KEY11 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4833
5608
|
WORKSPACE_ID9 = null;
|
|
5609
|
+
PROJECT_ID3 = null;
|
|
4834
5610
|
isDirectRun13 = process.argv[1]?.includes("session-end") || process.argv[2] === "session-end";
|
|
4835
5611
|
if (isDirectRun13) {
|
|
4836
5612
|
runSessionEndHook().catch(() => process.exit(0));
|
|
@@ -4988,10 +5764,11 @@ import * as fs20 from "node:fs";
|
|
|
4988
5764
|
import * as path21 from "node:path";
|
|
4989
5765
|
import { homedir as homedir18 } from "node:os";
|
|
4990
5766
|
function maskApiKey2(key) {
|
|
4991
|
-
if (!key || key.length <
|
|
4992
|
-
const
|
|
4993
|
-
const
|
|
4994
|
-
|
|
5767
|
+
if (!key || key.length < 8) return "***";
|
|
5768
|
+
const prefixMatch = key.match(/^([a-z]{2,3}_)/i);
|
|
5769
|
+
const prefix = prefixMatch ? prefixMatch[1] : "";
|
|
5770
|
+
const suffix = key.slice(-3);
|
|
5771
|
+
return `${prefix}***...${suffix}`;
|
|
4995
5772
|
}
|
|
4996
5773
|
function extractFromMcpConfig(config) {
|
|
4997
5774
|
if (!config.mcpServers) return {};
|
|
@@ -5016,10 +5793,10 @@ function extractFromMcpConfig(config) {
|
|
|
5016
5793
|
return {};
|
|
5017
5794
|
}
|
|
5018
5795
|
function getClaudeDesktopConfigPath() {
|
|
5019
|
-
const
|
|
5020
|
-
if (
|
|
5796
|
+
const platform2 = process.platform;
|
|
5797
|
+
if (platform2 === "darwin") {
|
|
5021
5798
|
return path21.join(homedir18(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5022
|
-
} else if (
|
|
5799
|
+
} else if (platform2 === "win32") {
|
|
5023
5800
|
return path21.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
5024
5801
|
} else {
|
|
5025
5802
|
return path21.join(homedir18(), ".config", "Claude", "claude_desktop_config.json");
|
|
@@ -5174,6 +5951,7 @@ async function validateApiKey(apiKey, apiUrl) {
|
|
|
5174
5951
|
return {
|
|
5175
5952
|
valid: true,
|
|
5176
5953
|
masked_key: maskApiKey2(apiKey),
|
|
5954
|
+
// lgtm[js/clear-text-logging]
|
|
5177
5955
|
email: data.email,
|
|
5178
5956
|
name: data.name || data.full_name,
|
|
5179
5957
|
plan: data.plan_name || data.plan || "free",
|
|
@@ -5183,12 +5961,14 @@ async function validateApiKey(apiKey, apiUrl) {
|
|
|
5183
5961
|
return {
|
|
5184
5962
|
valid: false,
|
|
5185
5963
|
masked_key: maskApiKey2(apiKey),
|
|
5964
|
+
// lgtm[js/clear-text-logging]
|
|
5186
5965
|
error: "Invalid or expired API key"
|
|
5187
5966
|
};
|
|
5188
5967
|
} else {
|
|
5189
5968
|
return {
|
|
5190
5969
|
valid: false,
|
|
5191
5970
|
masked_key: maskApiKey2(apiKey),
|
|
5971
|
+
// lgtm[js/clear-text-logging]
|
|
5192
5972
|
error: `API error: ${response.status}`
|
|
5193
5973
|
};
|
|
5194
5974
|
}
|
|
@@ -9300,179 +10080,8 @@ var coerce = {
|
|
|
9300
10080
|
};
|
|
9301
10081
|
var NEVER = INVALID;
|
|
9302
10082
|
|
|
9303
|
-
// src/version.ts
|
|
9304
|
-
import { createRequire } from "module";
|
|
9305
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
9306
|
-
import { homedir } from "os";
|
|
9307
|
-
import { join } from "path";
|
|
9308
|
-
var UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
|
|
9309
|
-
var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
9310
|
-
function getVersion() {
|
|
9311
|
-
if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
|
|
9312
|
-
return __CONTEXTSTREAM_VERSION__;
|
|
9313
|
-
}
|
|
9314
|
-
try {
|
|
9315
|
-
const require2 = createRequire(import.meta.url);
|
|
9316
|
-
const pkg = require2("../package.json");
|
|
9317
|
-
const version = pkg?.version;
|
|
9318
|
-
if (typeof version === "string" && version.trim()) return version.trim();
|
|
9319
|
-
} catch {
|
|
9320
|
-
}
|
|
9321
|
-
return "unknown";
|
|
9322
|
-
}
|
|
9323
|
-
var VERSION = getVersion();
|
|
9324
|
-
function compareVersions(v1, v2) {
|
|
9325
|
-
const parts1 = v1.split(".").map(Number);
|
|
9326
|
-
const parts2 = v2.split(".").map(Number);
|
|
9327
|
-
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
9328
|
-
const p1 = parts1[i] ?? 0;
|
|
9329
|
-
const p2 = parts2[i] ?? 0;
|
|
9330
|
-
if (p1 < p2) return -1;
|
|
9331
|
-
if (p1 > p2) return 1;
|
|
9332
|
-
}
|
|
9333
|
-
return 0;
|
|
9334
|
-
}
|
|
9335
|
-
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9336
|
-
var latestVersionPromise = null;
|
|
9337
|
-
function getCacheFilePath() {
|
|
9338
|
-
return join(homedir(), ".contextstream", "version-cache.json");
|
|
9339
|
-
}
|
|
9340
|
-
function readCache() {
|
|
9341
|
-
try {
|
|
9342
|
-
const cacheFile = getCacheFilePath();
|
|
9343
|
-
if (!existsSync(cacheFile)) return null;
|
|
9344
|
-
const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
|
|
9345
|
-
if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
|
|
9346
|
-
return data;
|
|
9347
|
-
} catch {
|
|
9348
|
-
return null;
|
|
9349
|
-
}
|
|
9350
|
-
}
|
|
9351
|
-
function writeCache(latestVersion) {
|
|
9352
|
-
try {
|
|
9353
|
-
const configDir = join(homedir(), ".contextstream");
|
|
9354
|
-
if (!existsSync(configDir)) {
|
|
9355
|
-
mkdirSync(configDir, { recursive: true });
|
|
9356
|
-
}
|
|
9357
|
-
const cacheFile = getCacheFilePath();
|
|
9358
|
-
writeFileSync(
|
|
9359
|
-
cacheFile,
|
|
9360
|
-
JSON.stringify({
|
|
9361
|
-
latestVersion,
|
|
9362
|
-
checkedAt: Date.now()
|
|
9363
|
-
})
|
|
9364
|
-
);
|
|
9365
|
-
} catch {
|
|
9366
|
-
}
|
|
9367
|
-
}
|
|
9368
|
-
async function fetchLatestVersion() {
|
|
9369
|
-
try {
|
|
9370
|
-
const controller = new AbortController();
|
|
9371
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
9372
|
-
const response = await fetch(NPM_LATEST_URL, {
|
|
9373
|
-
signal: controller.signal,
|
|
9374
|
-
headers: { Accept: "application/json" }
|
|
9375
|
-
});
|
|
9376
|
-
clearTimeout(timeout);
|
|
9377
|
-
if (!response.ok) return null;
|
|
9378
|
-
const data = await response.json();
|
|
9379
|
-
return typeof data.version === "string" ? data.version : null;
|
|
9380
|
-
} catch {
|
|
9381
|
-
return null;
|
|
9382
|
-
}
|
|
9383
|
-
}
|
|
9384
|
-
async function resolveLatestVersion() {
|
|
9385
|
-
const cached = readCache();
|
|
9386
|
-
if (cached) return cached.latestVersion;
|
|
9387
|
-
if (!latestVersionPromise) {
|
|
9388
|
-
latestVersionPromise = fetchLatestVersion().finally(() => {
|
|
9389
|
-
latestVersionPromise = null;
|
|
9390
|
-
});
|
|
9391
|
-
}
|
|
9392
|
-
const latestVersion = await latestVersionPromise;
|
|
9393
|
-
if (latestVersion) {
|
|
9394
|
-
writeCache(latestVersion);
|
|
9395
|
-
}
|
|
9396
|
-
return latestVersion;
|
|
9397
|
-
}
|
|
9398
|
-
async function checkForUpdates() {
|
|
9399
|
-
const notice = await getUpdateNotice();
|
|
9400
|
-
if (notice?.behind) {
|
|
9401
|
-
showUpdateWarning(notice.current, notice.latest);
|
|
9402
|
-
}
|
|
9403
|
-
}
|
|
9404
|
-
function showUpdateWarning(currentVersion, latestVersion) {
|
|
9405
|
-
console.error("");
|
|
9406
|
-
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
9407
|
-
console.error(`\u26A0\uFE0F Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
9408
|
-
console.error("");
|
|
9409
|
-
console.error(` Run: ${UPGRADE_COMMAND}`);
|
|
9410
|
-
console.error("");
|
|
9411
|
-
console.error(" Then restart your AI tool to use the new version.");
|
|
9412
|
-
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
9413
|
-
console.error("");
|
|
9414
|
-
}
|
|
9415
|
-
async function getUpdateNotice() {
|
|
9416
|
-
const currentVersion = VERSION;
|
|
9417
|
-
if (currentVersion === "unknown") return null;
|
|
9418
|
-
try {
|
|
9419
|
-
const latestVersion = await resolveLatestVersion();
|
|
9420
|
-
if (!latestVersion) return null;
|
|
9421
|
-
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
9422
|
-
return {
|
|
9423
|
-
current: currentVersion,
|
|
9424
|
-
latest: latestVersion,
|
|
9425
|
-
behind: true,
|
|
9426
|
-
upgrade_command: UPGRADE_COMMAND
|
|
9427
|
-
};
|
|
9428
|
-
}
|
|
9429
|
-
} catch {
|
|
9430
|
-
}
|
|
9431
|
-
return null;
|
|
9432
|
-
}
|
|
9433
|
-
function getVersionsBehind(current, latest) {
|
|
9434
|
-
try {
|
|
9435
|
-
const currentParts = current.split(".").map(Number);
|
|
9436
|
-
const latestParts = latest.split(".").map(Number);
|
|
9437
|
-
if ((latestParts[0] ?? 0) > (currentParts[0] ?? 0)) {
|
|
9438
|
-
return 10 + ((latestParts[1] ?? 0) - (currentParts[1] ?? 0));
|
|
9439
|
-
}
|
|
9440
|
-
const minorDiff = (latestParts[1] ?? 0) - (currentParts[1] ?? 0);
|
|
9441
|
-
const patchDiff = (latestParts[2] ?? 0) - (currentParts[2] ?? 0);
|
|
9442
|
-
if (minorDiff > 0) {
|
|
9443
|
-
return minorDiff;
|
|
9444
|
-
} else if (minorDiff === 0 && patchDiff > 0) {
|
|
9445
|
-
return 1;
|
|
9446
|
-
}
|
|
9447
|
-
return 0;
|
|
9448
|
-
} catch {
|
|
9449
|
-
return 0;
|
|
9450
|
-
}
|
|
9451
|
-
}
|
|
9452
|
-
function getVersionWarning(notice) {
|
|
9453
|
-
if (!notice?.behind) return null;
|
|
9454
|
-
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
9455
|
-
if (versionsBehind >= 3) {
|
|
9456
|
-
return `[VERSION_CRITICAL] MCP server is ${versionsBehind} versions behind (${notice.current} \u2192 ${notice.latest}). Update immediately: ${notice.upgrade_command}`;
|
|
9457
|
-
} else if (versionsBehind >= 1) {
|
|
9458
|
-
return `[VERSION_NOTICE] MCP server update available (${notice.current} \u2192 ${notice.latest}). Run: ${notice.upgrade_command}`;
|
|
9459
|
-
}
|
|
9460
|
-
return null;
|
|
9461
|
-
}
|
|
9462
|
-
function getVersionInstructions(notice) {
|
|
9463
|
-
if (!notice?.behind) return null;
|
|
9464
|
-
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
9465
|
-
if (versionsBehind >= 5) {
|
|
9466
|
-
return `\u26A0\uFE0F CRITICAL: User's MCP is ${versionsBehind} versions behind. Mention update in EVERY response until updated.`;
|
|
9467
|
-
} else if (versionsBehind >= 3) {
|
|
9468
|
-
return `\u26A0\uFE0F User's MCP is ${versionsBehind} versions behind. Remind about update every 2-3 messages.`;
|
|
9469
|
-
} else if (versionsBehind >= 1) {
|
|
9470
|
-
return `Note: MCP update available (${notice.current}\u2192${notice.latest}). Mention once at session start.`;
|
|
9471
|
-
}
|
|
9472
|
-
return null;
|
|
9473
|
-
}
|
|
9474
|
-
|
|
9475
10083
|
// src/config.ts
|
|
10084
|
+
init_version();
|
|
9476
10085
|
var DEFAULT_API_URL = "https://api.contextstream.io";
|
|
9477
10086
|
function parseBooleanEnv(value) {
|
|
9478
10087
|
if (value === void 0) return void 0;
|
|
@@ -10331,6 +10940,7 @@ var CacheKeys = {
|
|
|
10331
10940
|
var globalCache = new MemoryCache();
|
|
10332
10941
|
|
|
10333
10942
|
// src/client.ts
|
|
10943
|
+
init_version();
|
|
10334
10944
|
var uuidSchema = external_exports.string().uuid();
|
|
10335
10945
|
function unwrapApiResponse(result) {
|
|
10336
10946
|
if (!result || typeof result !== "object") return result;
|
|
@@ -12647,7 +13257,8 @@ var ContextStreamClient = class {
|
|
|
12647
13257
|
// Transcript save parameters
|
|
12648
13258
|
...params.save_exchange !== void 0 && { save_exchange: params.save_exchange },
|
|
12649
13259
|
...params.session_id !== void 0 && { session_id: params.session_id },
|
|
12650
|
-
...params.client_name !== void 0 && { client_name: params.client_name }
|
|
13260
|
+
...params.client_name !== void 0 && { client_name: params.client_name },
|
|
13261
|
+
...params.assistant_message !== void 0 && { assistant_message: params.assistant_message }
|
|
12651
13262
|
}
|
|
12652
13263
|
});
|
|
12653
13264
|
const data = unwrapApiResponse(apiResult);
|
|
@@ -12664,10 +13275,17 @@ var ContextStreamClient = class {
|
|
|
12664
13275
|
if (versionWarning2) {
|
|
12665
13276
|
warnings2.push(versionWarning2);
|
|
12666
13277
|
}
|
|
13278
|
+
const CS_HEADER2 = "\u2B21 ContextStream \u2014 Smart Context & Memory";
|
|
12667
13279
|
let context2 = String(data?.context ?? "");
|
|
12668
13280
|
const versionInstructions = getVersionInstructions(versionNotice2);
|
|
12669
13281
|
if (versionInstructions) {
|
|
12670
|
-
context2 = `${
|
|
13282
|
+
context2 = `${CS_HEADER2}
|
|
13283
|
+
|
|
13284
|
+
${versionInstructions}
|
|
13285
|
+
|
|
13286
|
+
${context2}`;
|
|
13287
|
+
} else {
|
|
13288
|
+
context2 = `${CS_HEADER2}
|
|
12671
13289
|
|
|
12672
13290
|
${context2}`;
|
|
12673
13291
|
}
|
|
@@ -12825,11 +13443,13 @@ ${context2}`;
|
|
|
12825
13443
|
let charsUsed = 0;
|
|
12826
13444
|
const maxChars = maxTokens * 4;
|
|
12827
13445
|
let candidateContext;
|
|
13446
|
+
const CS_HEADER = "\u2B21 ContextStream \u2014 Smart Context & Memory";
|
|
13447
|
+
const CS_HEADER_MINIFIED = "CS:active";
|
|
12828
13448
|
const CS_FIRST_RULE = "\u26A0\uFE0F STOP: Before Glob/Grep/Read \u2192 search(mode=hybrid) FIRST. Local tools ONLY if 0 results.";
|
|
12829
13449
|
const CS_FIRST_RULE_MINIFIED = "R:CS-first|Idx:project.index_status->ingest|NoLocalScanUnlessCSempty";
|
|
12830
13450
|
if (format === "minified") {
|
|
12831
|
-
const parts = [CS_FIRST_RULE_MINIFIED];
|
|
12832
|
-
charsUsed = CS_FIRST_RULE_MINIFIED.length +
|
|
13451
|
+
const parts = [CS_HEADER_MINIFIED, CS_FIRST_RULE_MINIFIED];
|
|
13452
|
+
charsUsed = CS_HEADER_MINIFIED.length + CS_FIRST_RULE_MINIFIED.length + 2;
|
|
12833
13453
|
for (const item of items) {
|
|
12834
13454
|
const entry = `${item.type}:${item.value}`;
|
|
12835
13455
|
if (charsUsed + entry.length + 1 > maxChars) break;
|
|
@@ -12837,12 +13457,12 @@ ${context2}`;
|
|
|
12837
13457
|
charsUsed += entry.length + 1;
|
|
12838
13458
|
}
|
|
12839
13459
|
context = parts.join("|");
|
|
12840
|
-
candidateContext = [CS_FIRST_RULE_MINIFIED, ...items.map((i) => `${i.type}:${i.value}`)].join(
|
|
13460
|
+
candidateContext = [CS_HEADER_MINIFIED, CS_FIRST_RULE_MINIFIED, ...items.map((i) => `${i.type}:${i.value}`)].join(
|
|
12841
13461
|
"|"
|
|
12842
13462
|
);
|
|
12843
13463
|
} else if (format === "structured") {
|
|
12844
|
-
const grouped = { R: [CS_FIRST_RULE] };
|
|
12845
|
-
charsUsed = CS_FIRST_RULE.length +
|
|
13464
|
+
const grouped = { _: [CS_HEADER], R: [CS_FIRST_RULE] };
|
|
13465
|
+
charsUsed = CS_HEADER.length + CS_FIRST_RULE.length + 15;
|
|
12846
13466
|
for (const item of items) {
|
|
12847
13467
|
if (charsUsed > maxChars) break;
|
|
12848
13468
|
if (!grouped[item.type]) grouped[item.type] = [];
|
|
@@ -12850,15 +13470,15 @@ ${context2}`;
|
|
|
12850
13470
|
charsUsed += item.value.length + 5;
|
|
12851
13471
|
}
|
|
12852
13472
|
context = JSON.stringify(grouped);
|
|
12853
|
-
const candidateGrouped = { R: [CS_FIRST_RULE] };
|
|
13473
|
+
const candidateGrouped = { _: [CS_HEADER], R: [CS_FIRST_RULE] };
|
|
12854
13474
|
for (const item of items) {
|
|
12855
13475
|
if (!candidateGrouped[item.type]) candidateGrouped[item.type] = [];
|
|
12856
13476
|
candidateGrouped[item.type].push(item.value);
|
|
12857
13477
|
}
|
|
12858
13478
|
candidateContext = JSON.stringify(candidateGrouped);
|
|
12859
13479
|
} else {
|
|
12860
|
-
const lines = [CS_FIRST_RULE, "", "[CTX]"];
|
|
12861
|
-
charsUsed = CS_FIRST_RULE.length +
|
|
13480
|
+
const lines = [CS_HEADER, "", CS_FIRST_RULE, "", "[CTX]"];
|
|
13481
|
+
charsUsed = CS_HEADER.length + CS_FIRST_RULE.length + 15;
|
|
12862
13482
|
for (const item of items) {
|
|
12863
13483
|
const line = `${item.type}:${item.value}`;
|
|
12864
13484
|
if (charsUsed + line.length + 1 > maxChars) break;
|
|
@@ -12867,7 +13487,7 @@ ${context2}`;
|
|
|
12867
13487
|
}
|
|
12868
13488
|
lines.push("[/CTX]");
|
|
12869
13489
|
context = lines.join("\n");
|
|
12870
|
-
const candidateLines = [CS_FIRST_RULE, "", "[CTX]"];
|
|
13490
|
+
const candidateLines = [CS_HEADER, "", CS_FIRST_RULE, "", "[CTX]"];
|
|
12871
13491
|
for (const item of items) {
|
|
12872
13492
|
candidateLines.push(`${item.type}:${item.value}`);
|
|
12873
13493
|
}
|
|
@@ -12876,7 +13496,9 @@ ${context2}`;
|
|
|
12876
13496
|
}
|
|
12877
13497
|
if (context.length === 0 && withDefaults.workspace_id) {
|
|
12878
13498
|
const wsHint = items.find((i) => i.type === "W")?.value || withDefaults.workspace_id;
|
|
12879
|
-
context = format === "minified" ? `${CS_FIRST_RULE_MINIFIED}|W:${wsHint}|[NO_MATCHES]` : `${
|
|
13499
|
+
context = format === "minified" ? `${CS_HEADER_MINIFIED}|${CS_FIRST_RULE_MINIFIED}|W:${wsHint}|[NO_MATCHES]` : `${CS_HEADER}
|
|
13500
|
+
|
|
13501
|
+
${CS_FIRST_RULE}
|
|
12880
13502
|
|
|
12881
13503
|
[CTX]
|
|
12882
13504
|
W:${wsHint}
|
|
@@ -14306,6 +14928,7 @@ import * as path6 from "node:path";
|
|
|
14306
14928
|
import { homedir as homedir3 } from "node:os";
|
|
14307
14929
|
|
|
14308
14930
|
// src/rules-templates.ts
|
|
14931
|
+
init_version();
|
|
14309
14932
|
var DEFAULT_CLAUDE_MCP_SERVER_NAME = "contextstream";
|
|
14310
14933
|
var RULES_VERSION = VERSION === "unknown" ? "0.0.0" : VERSION;
|
|
14311
14934
|
var CONTEXTSTREAM_TOOL_NAMES = [
|
|
@@ -14354,7 +14977,7 @@ var CONTEXTSTREAM_RULES_BOOTSTRAP = `
|
|
|
14354
14977
|
|
|
14355
14978
|
**Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
|
|
14356
14979
|
|
|
14357
|
-
**Notices:** [LESSONS_WARNING] \u2192
|
|
14980
|
+
**Notices:** [LESSONS_WARNING] \u2192 apply lessons | [PREFERENCE] \u2192 follow user preferences | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE/CRITICAL] \u2192 tell user about update
|
|
14358
14981
|
|
|
14359
14982
|
v${RULES_VERSION}
|
|
14360
14983
|
`.trim();
|
|
@@ -14395,6 +15018,7 @@ var CONTEXTSTREAM_RULES_FULL = `
|
|
|
14395
15018
|
## Handle Notices from context()
|
|
14396
15019
|
|
|
14397
15020
|
- **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
|
|
15021
|
+
- **[PREFERENCE]** \u2192 Follow user preferences (high-priority user memories)
|
|
14398
15022
|
- **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
|
|
14399
15023
|
- **[VERSION_NOTICE]** \u2192 Tell user to update MCP
|
|
14400
15024
|
|
|
@@ -14621,11 +15245,14 @@ If context still feels missing, use \`session(action="recall", query="...")\` fo
|
|
|
14621
15245
|
|
|
14622
15246
|
**[RULES_NOTICE]** - Update rules via \`generate_rules()\` (or rerun setup).
|
|
14623
15247
|
|
|
14624
|
-
**[VERSION_NOTICE]** - You **MUST
|
|
14625
|
-
1. A new MCP server version is available
|
|
14626
|
-
2.
|
|
14627
|
-
|
|
14628
|
-
|
|
15248
|
+
**[VERSION_NOTICE]** or **[VERSION_CRITICAL]** - You **MUST** tell the user (be helpful, not annoying):
|
|
15249
|
+
1. A new MCP server version is available with improvements
|
|
15250
|
+
2. Provide update commands (user chooses preferred method):
|
|
15251
|
+
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
|
|
15252
|
+
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
|
|
15253
|
+
- npm: \`npm install -g @contextstream/mcp-server@latest\`
|
|
15254
|
+
3. Restart AI tool after updating
|
|
15255
|
+
**Mention once** - don't nag. Users often miss stderr warnings so this helps.
|
|
14629
15256
|
|
|
14630
15257
|
**[LESSONS_WARNING]** - You **MUST** before proceeding:
|
|
14631
15258
|
1. Read all lessons listed
|
|
@@ -14637,9 +15264,17 @@ If context still feels missing, use \`session(action="recall", query="...")\` fo
|
|
|
14637
15264
|
|
|
14638
15265
|
### Preferences & Lessons (Use Early)
|
|
14639
15266
|
|
|
14640
|
-
|
|
15267
|
+
**Preferences ([PREFERENCE] in context response):**
|
|
15268
|
+
- High-priority user memories that should guide your behavior
|
|
15269
|
+
- Surfaced automatically via \`context()\` warnings field
|
|
15270
|
+
- To save: \`session(action="remember", content="...")\`
|
|
15271
|
+
- To retrieve explicitly: \`session(action="user_context")\`
|
|
15272
|
+
|
|
15273
|
+
**Lessons ([LESSONS_WARNING] in context response):**
|
|
15274
|
+
- Past mistakes to avoid - apply prevention steps
|
|
15275
|
+
- Surfaced automatically via \`context()\` warnings field
|
|
14641
15276
|
- Before risky changes: \`session(action="get_lessons", query="<topic>")\`
|
|
14642
|
-
- On
|
|
15277
|
+
- On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
|
|
14643
15278
|
|
|
14644
15279
|
---
|
|
14645
15280
|
|
|
@@ -14933,6 +15568,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
14933
15568
|
## Handle Notices from context()
|
|
14934
15569
|
|
|
14935
15570
|
- **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
|
|
15571
|
+
- **[PREFERENCE]** \u2192 Follow user preferences (high-priority user memories)
|
|
14936
15572
|
- **[RULES_NOTICE]** \u2192 Run \`generate_rules()\`
|
|
14937
15573
|
- **[VERSION_NOTICE]** \u2192 Tell user to update MCP
|
|
14938
15574
|
|
|
@@ -14974,9 +15610,13 @@ To disable automatic restoration:
|
|
|
14974
15610
|
|
|
14975
15611
|
### Notices - MUST HANDLE IMMEDIATELY
|
|
14976
15612
|
|
|
14977
|
-
- **[VERSION_NOTICE]**: Tell
|
|
15613
|
+
- **[VERSION_NOTICE]** or **[VERSION_CRITICAL]**: Tell user about the update in a helpful, non-annoying way. Provide update commands:
|
|
15614
|
+
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
|
|
15615
|
+
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
|
|
15616
|
+
- npm: \`npm install -g @contextstream/mcp-server@latest\`
|
|
14978
15617
|
- **[RULES_NOTICE]**: Run \`generate_rules(overwrite_existing=true)\` to update
|
|
14979
15618
|
- **[LESSONS_WARNING]**: Read lessons, tell user about them, explain how you'll avoid past mistakes
|
|
15619
|
+
- **[PREFERENCE]**: Follow user preferences - these are high-priority user memories that should guide your behavior
|
|
14980
15620
|
|
|
14981
15621
|
### Plans & Tasks
|
|
14982
15622
|
|
|
@@ -15015,12 +15655,42 @@ You MUST follow these rules manually - there is no automatic enforcement.
|
|
|
15015
15655
|
- Check response for \`indexing_status\`
|
|
15016
15656
|
- If \`"started"\` or \`"refreshing"\`: wait before searching
|
|
15017
15657
|
|
|
15018
|
-
2. **
|
|
15019
|
-
-
|
|
15658
|
+
2. **Generate a unique session_id** (e.g., \`"session-" + timestamp\` or a UUID)
|
|
15659
|
+
- Use this SAME session_id for ALL context() calls in this conversation
|
|
15660
|
+
- This groups all turns together in the transcript
|
|
15661
|
+
|
|
15662
|
+
3. **Call \`context(user_message="<first_message>", save_exchange=true, session_id="<your-session-id>")\`**
|
|
15663
|
+
- Gets task-specific rules, lessons, and preferences
|
|
15020
15664
|
- Check for [LESSONS_WARNING] - past mistakes to avoid
|
|
15665
|
+
- Check for [PREFERENCE] - user preferences to follow
|
|
15021
15666
|
- Check for [RULES_NOTICE] - update rules if needed
|
|
15667
|
+
- **save_exchange=true** saves each conversation turn for later retrieval
|
|
15668
|
+
|
|
15669
|
+
4. **NEVER skip init/context** - you will miss critical context
|
|
15670
|
+
|
|
15671
|
+
---
|
|
15672
|
+
|
|
15673
|
+
## \u{1F4BE} AUTOMATIC TRANSCRIPT SAVING (CRITICAL)
|
|
15674
|
+
|
|
15675
|
+
**This editor does NOT have hooks to auto-save transcripts.**
|
|
15676
|
+
You MUST save each conversation turn manually:
|
|
15677
|
+
|
|
15678
|
+
### On EVERY message (including the first):
|
|
15679
|
+
\`\`\`
|
|
15680
|
+
context(user_message="<user's message>", save_exchange=true, session_id="<session-id>")
|
|
15681
|
+
\`\`\`
|
|
15022
15682
|
|
|
15023
|
-
|
|
15683
|
+
### Why save_exchange matters:
|
|
15684
|
+
- Transcripts enable searching past conversations
|
|
15685
|
+
- Allows context restoration after compaction
|
|
15686
|
+
- Provides conversation history for debugging
|
|
15687
|
+
- Required for the Transcripts page in the dashboard
|
|
15688
|
+
|
|
15689
|
+
### Session ID Guidelines:
|
|
15690
|
+
- Generate ONCE at the start of the conversation
|
|
15691
|
+
- Use a unique identifier: \`"session-" + Date.now()\` or a UUID
|
|
15692
|
+
- Keep the SAME session_id for ALL context() calls in this session
|
|
15693
|
+
- Different sessions = different transcripts
|
|
15024
15694
|
|
|
15025
15695
|
---
|
|
15026
15696
|
|
|
@@ -15130,6 +15800,39 @@ memory(action="create_task", title="...", plan_id="...")
|
|
|
15130
15800
|
|
|
15131
15801
|
\u274C DO NOT use built-in plan mode or write plans to markdown files.
|
|
15132
15802
|
|
|
15803
|
+
---
|
|
15804
|
+
|
|
15805
|
+
## \u{1F504} VERSION UPDATES (Check Periodically)
|
|
15806
|
+
|
|
15807
|
+
**This editor does NOT have hooks to check for updates automatically.**
|
|
15808
|
+
You should check for updates using \`help(action="version")\` periodically (e.g., at session start).
|
|
15809
|
+
|
|
15810
|
+
### If the response includes [VERSION_NOTICE] or [VERSION_CRITICAL]:
|
|
15811
|
+
|
|
15812
|
+
**Tell the user** about the available update in a helpful, non-annoying way:
|
|
15813
|
+
- Frame it as "new features and improvements available"
|
|
15814
|
+
- Provide the update commands (user can choose their preferred method)
|
|
15815
|
+
- Don't nag repeatedly - mention once, then only if user asks
|
|
15816
|
+
|
|
15817
|
+
### Update Commands (provide all options):
|
|
15818
|
+
|
|
15819
|
+
**macOS/Linux:**
|
|
15820
|
+
\`\`\`bash
|
|
15821
|
+
curl -fsSL https://contextstream.io/scripts/setup.sh | bash
|
|
15822
|
+
\`\`\`
|
|
15823
|
+
|
|
15824
|
+
**Windows (PowerShell):**
|
|
15825
|
+
\`\`\`powershell
|
|
15826
|
+
irm https://contextstream.io/scripts/setup.ps1 | iex
|
|
15827
|
+
\`\`\`
|
|
15828
|
+
|
|
15829
|
+
**npm (requires Node.js 18+):**
|
|
15830
|
+
\`\`\`bash
|
|
15831
|
+
npm install -g @contextstream/mcp-server@latest
|
|
15832
|
+
\`\`\`
|
|
15833
|
+
|
|
15834
|
+
After updating, user should restart their AI tool.
|
|
15835
|
+
|
|
15133
15836
|
---
|
|
15134
15837
|
`;
|
|
15135
15838
|
var NO_HOOKS_EDITORS = ["codex", "aider", "antigravity"];
|
|
@@ -15238,6 +15941,9 @@ function generateAllRuleFiles(options) {
|
|
|
15238
15941
|
}).filter((r) => r !== null);
|
|
15239
15942
|
}
|
|
15240
15943
|
|
|
15944
|
+
// src/tools.ts
|
|
15945
|
+
init_version();
|
|
15946
|
+
|
|
15241
15947
|
// src/tool-catalog.ts
|
|
15242
15948
|
var TOOL_CATALOG = [
|
|
15243
15949
|
{
|
|
@@ -17241,12 +17947,6 @@ function registerTools(server, client, sessionManager) {
|
|
|
17241
17947
|
function getToolAccessTier(toolName) {
|
|
17242
17948
|
return proTools.has(toolName) ? "pro" : "free";
|
|
17243
17949
|
}
|
|
17244
|
-
function getToolAccessLabel(toolName) {
|
|
17245
|
-
const graphTier = graphToolTiers.get(toolName);
|
|
17246
|
-
if (graphTier === "lite") return "Pro (Graph-Lite)";
|
|
17247
|
-
if (graphTier === "full") return "Elite/Team (Full Graph)";
|
|
17248
|
-
return getToolAccessTier(toolName) === "pro" ? "PRO" : "Free";
|
|
17249
|
-
}
|
|
17250
17950
|
async function gateIfProTool(toolName) {
|
|
17251
17951
|
if (getToolAccessTier(toolName) !== "pro") return null;
|
|
17252
17952
|
const planName = await client.getPlanName();
|
|
@@ -17542,22 +18242,18 @@ function registerTools(server, client, sessionManager) {
|
|
|
17542
18242
|
};
|
|
17543
18243
|
}
|
|
17544
18244
|
function actuallyRegisterTool(name, config, handler) {
|
|
17545
|
-
const accessLabel = getToolAccessLabel(name);
|
|
17546
|
-
const showUpgrade = accessLabel !== "Free";
|
|
17547
18245
|
let finalDescription;
|
|
17548
18246
|
let finalSchema;
|
|
17549
18247
|
if (COMPACT_SCHEMA_ENABLED) {
|
|
17550
18248
|
finalDescription = compactifyDescription(config.description);
|
|
17551
18249
|
finalSchema = config.inputSchema ? applyCompactParamDescriptions(config.inputSchema) : void 0;
|
|
17552
18250
|
} else {
|
|
17553
|
-
finalDescription =
|
|
17554
|
-
|
|
17555
|
-
Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl2})` : ""}`;
|
|
18251
|
+
finalDescription = config.description;
|
|
17556
18252
|
finalSchema = config.inputSchema ? applyParamDescriptions(config.inputSchema) : void 0;
|
|
17557
18253
|
}
|
|
17558
18254
|
const labeledConfig = {
|
|
17559
18255
|
...config,
|
|
17560
|
-
title:
|
|
18256
|
+
title: config.title,
|
|
17561
18257
|
description: finalDescription
|
|
17562
18258
|
};
|
|
17563
18259
|
const annotatedConfig = {
|
|
@@ -20935,7 +21631,8 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
20935
21631
|
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)"),
|
|
20936
21632
|
save_exchange: external_exports.boolean().optional().describe("Save this exchange to the transcript for later search (background task)"),
|
|
20937
21633
|
session_id: external_exports.string().optional().describe("Session ID for transcript association (required if save_exchange is true)"),
|
|
20938
|
-
client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')")
|
|
21634
|
+
client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')"),
|
|
21635
|
+
assistant_message: external_exports.string().optional().describe("Previous assistant response to save along with user message (for complete exchange capture)")
|
|
20939
21636
|
})
|
|
20940
21637
|
},
|
|
20941
21638
|
async (input) => {
|
|
@@ -21031,7 +21728,8 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
21031
21728
|
context_threshold: contextThreshold,
|
|
21032
21729
|
save_exchange: input.save_exchange,
|
|
21033
21730
|
session_id: sessionId,
|
|
21034
|
-
client_name: clientName
|
|
21731
|
+
client_name: clientName,
|
|
21732
|
+
assistant_message: input.assistant_message
|
|
21035
21733
|
});
|
|
21036
21734
|
if (sessionManager && result.token_estimate) {
|
|
21037
21735
|
sessionManager.addTokens(result.token_estimate);
|
|
@@ -27117,7 +27815,7 @@ var SessionManager = class _SessionManager {
|
|
|
27117
27815
|
buildContextSummary(context) {
|
|
27118
27816
|
const parts = [];
|
|
27119
27817
|
parts.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
27120
|
-
parts.push("\u2B21
|
|
27818
|
+
parts.push("\u2B21 ContextStream \u2014 Smart Context & Memory");
|
|
27121
27819
|
parts.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
27122
27820
|
if (context.status === "requires_workspace_name") {
|
|
27123
27821
|
parts.push("");
|
|
@@ -27345,6 +28043,7 @@ import { createServer } from "node:http";
|
|
|
27345
28043
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
27346
28044
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27347
28045
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
28046
|
+
init_version();
|
|
27348
28047
|
var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
|
|
27349
28048
|
var PORT = Number.parseInt(process.env.MCP_HTTP_PORT || "8787", 10);
|
|
27350
28049
|
var MCP_PATH = process.env.MCP_HTTP_PATH || "/mcp";
|
|
@@ -27623,6 +28322,7 @@ async function runHttpGateway() {
|
|
|
27623
28322
|
}
|
|
27624
28323
|
|
|
27625
28324
|
// src/index.ts
|
|
28325
|
+
init_version();
|
|
27626
28326
|
import { existsSync as existsSync17, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
27627
28327
|
import { homedir as homedir19 } from "os";
|
|
27628
28328
|
import { join as join23 } from "path";
|
|
@@ -27633,6 +28333,7 @@ import * as path8 from "node:path";
|
|
|
27633
28333
|
import { homedir as homedir5 } from "node:os";
|
|
27634
28334
|
import { stdin, stdout } from "node:process";
|
|
27635
28335
|
import { createInterface } from "node:readline/promises";
|
|
28336
|
+
init_version();
|
|
27636
28337
|
|
|
27637
28338
|
// src/credentials.ts
|
|
27638
28339
|
import * as fs6 from "node:fs/promises";
|
|
@@ -27783,7 +28484,8 @@ var CONTEXTSTREAM_PREAMBLE_PATTERNS2 = [
|
|
|
27783
28484
|
/^#\s+cline rules$/i,
|
|
27784
28485
|
/^#\s+kilo code rules$/i,
|
|
27785
28486
|
/^#\s+roo code rules$/i,
|
|
27786
|
-
/^#\s+aider configuration$/i
|
|
28487
|
+
/^#\s+aider configuration$/i,
|
|
28488
|
+
/^#\s+antigravity agent rules$/i
|
|
27787
28489
|
];
|
|
27788
28490
|
function wrapWithMarkers2(content) {
|
|
27789
28491
|
return `${CONTEXTSTREAM_START_MARKER2}
|
|
@@ -28191,6 +28893,9 @@ function claudeDesktopConfigPath() {
|
|
|
28191
28893
|
const appData = process.env.APPDATA || path8.join(home, "AppData", "Roaming");
|
|
28192
28894
|
return path8.join(appData, "Claude", "claude_desktop_config.json");
|
|
28193
28895
|
}
|
|
28896
|
+
if (process.platform === "linux") {
|
|
28897
|
+
return path8.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
28898
|
+
}
|
|
28194
28899
|
return null;
|
|
28195
28900
|
}
|
|
28196
28901
|
async function upsertCodexTomlConfig(filePath, params) {
|
|
@@ -28576,6 +29281,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
28576
29281
|
const contextPackEnabled = isPro;
|
|
28577
29282
|
const showTiming = false;
|
|
28578
29283
|
const restoreContextEnabled = true;
|
|
29284
|
+
console.log("\nAuto-Update:");
|
|
29285
|
+
console.log(" When enabled, ContextStream will automatically update to the latest version");
|
|
29286
|
+
console.log(" on new sessions (checks daily). You can disable this if you prefer manual updates.");
|
|
29287
|
+
const currentAutoUpdate = isAutoUpdateEnabled();
|
|
29288
|
+
const autoUpdateChoice = normalizeInput(
|
|
29289
|
+
await rl.question(`Enable auto-update? [${currentAutoUpdate ? "Y/n" : "y/N"}]: `)
|
|
29290
|
+
).toLowerCase();
|
|
29291
|
+
const autoUpdateEnabled = autoUpdateChoice === "" ? currentAutoUpdate : autoUpdateChoice === "y" || autoUpdateChoice === "yes";
|
|
29292
|
+
setAutoUpdatePreference(autoUpdateEnabled);
|
|
29293
|
+
if (autoUpdateEnabled) {
|
|
29294
|
+
console.log(" \u2713 Auto-update enabled (disable anytime with CONTEXTSTREAM_AUTO_UPDATE=false)");
|
|
29295
|
+
} else {
|
|
29296
|
+
console.log(" \u2717 Auto-update disabled");
|
|
29297
|
+
}
|
|
28579
29298
|
const editors = [
|
|
28580
29299
|
"codex",
|
|
28581
29300
|
"claude",
|