@contextstream/mcp-server 0.4.82 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +25 -0
- package/README.md +77 -353
- 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 +27 -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,1880 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
// src/hooks/session-init.ts
|
|
10
|
-
import * as fs2 from "node:fs";
|
|
11
|
-
import * as path2 from "node:path";
|
|
12
|
-
import { homedir as homedir3 } from "node:os";
|
|
13
|
-
|
|
14
|
-
// src/version.ts
|
|
15
|
-
import { createRequire } from "module";
|
|
16
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
17
|
-
import { homedir, platform } from "os";
|
|
18
|
-
import { join } from "path";
|
|
19
|
-
import { spawn } from "child_process";
|
|
20
|
-
var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
21
|
-
var AUTO_UPDATE_ENABLED = process.env.CONTEXTSTREAM_AUTO_UPDATE !== "false";
|
|
22
|
-
var UPDATE_COMMANDS = {
|
|
23
|
-
npm: "npm install -g @contextstream/mcp-server@latest",
|
|
24
|
-
macLinux: "curl -fsSL https://contextstream.io/scripts/setup.sh | bash",
|
|
25
|
-
windows: "irm https://contextstream.io/scripts/setup.ps1 | iex"
|
|
26
|
-
};
|
|
27
|
-
var UPGRADE_COMMAND = UPDATE_COMMANDS.npm;
|
|
28
|
-
function getVersion() {
|
|
29
|
-
if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
|
|
30
|
-
return __CONTEXTSTREAM_VERSION__;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const require2 = createRequire(import.meta.url);
|
|
34
|
-
const pkg = require2("../package.json");
|
|
35
|
-
const version = pkg?.version;
|
|
36
|
-
if (typeof version === "string" && version.trim()) return version.trim();
|
|
37
|
-
} catch {
|
|
38
|
-
}
|
|
39
|
-
return "unknown";
|
|
40
|
-
}
|
|
41
|
-
var VERSION = getVersion();
|
|
42
|
-
function compareVersions(v1, v2) {
|
|
43
|
-
const parts1 = v1.split(".").map(Number);
|
|
44
|
-
const parts2 = v2.split(".").map(Number);
|
|
45
|
-
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
46
|
-
const p1 = parts1[i] ?? 0;
|
|
47
|
-
const p2 = parts2[i] ?? 0;
|
|
48
|
-
if (p1 < p2) return -1;
|
|
49
|
-
if (p1 > p2) return 1;
|
|
50
|
-
}
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
var CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
54
|
-
var latestVersionPromise = null;
|
|
55
|
-
function getCacheFilePath() {
|
|
56
|
-
return join(homedir(), ".contextstream", "version-cache.json");
|
|
57
|
-
}
|
|
58
|
-
function readCache() {
|
|
59
|
-
try {
|
|
60
|
-
const cacheFile = getCacheFilePath();
|
|
61
|
-
if (!existsSync(cacheFile)) return null;
|
|
62
|
-
const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
|
|
63
|
-
if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
|
|
64
|
-
return data;
|
|
65
|
-
} catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function writeCache(latestVersion) {
|
|
70
|
-
try {
|
|
71
|
-
const configDir = join(homedir(), ".contextstream");
|
|
72
|
-
if (!existsSync(configDir)) {
|
|
73
|
-
mkdirSync(configDir, { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
const cacheFile = getCacheFilePath();
|
|
76
|
-
writeFileSync(
|
|
77
|
-
cacheFile,
|
|
78
|
-
JSON.stringify({
|
|
79
|
-
latestVersion,
|
|
80
|
-
checkedAt: Date.now()
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
} catch {
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
async function fetchLatestVersion() {
|
|
87
|
-
try {
|
|
88
|
-
const controller = new AbortController();
|
|
89
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
90
|
-
const response = await fetch(NPM_LATEST_URL, {
|
|
91
|
-
signal: controller.signal,
|
|
92
|
-
headers: { Accept: "application/json" }
|
|
93
|
-
});
|
|
94
|
-
clearTimeout(timeout);
|
|
95
|
-
if (!response.ok) return null;
|
|
96
|
-
const data = await response.json();
|
|
97
|
-
return typeof data.version === "string" ? data.version : null;
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async function resolveLatestVersion() {
|
|
103
|
-
const cached = readCache();
|
|
104
|
-
if (cached) return cached.latestVersion;
|
|
105
|
-
if (!latestVersionPromise) {
|
|
106
|
-
latestVersionPromise = fetchLatestVersion().finally(() => {
|
|
107
|
-
latestVersionPromise = null;
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
const latestVersion = await latestVersionPromise;
|
|
111
|
-
if (latestVersion) {
|
|
112
|
-
writeCache(latestVersion);
|
|
113
|
-
}
|
|
114
|
-
return latestVersion;
|
|
115
|
-
}
|
|
116
|
-
async function getUpdateNotice() {
|
|
117
|
-
const currentVersion = VERSION;
|
|
118
|
-
if (currentVersion === "unknown") return null;
|
|
119
|
-
try {
|
|
120
|
-
const latestVersion = await resolveLatestVersion();
|
|
121
|
-
if (!latestVersion) return null;
|
|
122
|
-
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
123
|
-
return {
|
|
124
|
-
current: currentVersion,
|
|
125
|
-
latest: latestVersion,
|
|
126
|
-
behind: true,
|
|
127
|
-
upgrade_command: UPGRADE_COMMAND
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
} catch {
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
function getVersionsBehind(current, latest) {
|
|
135
|
-
try {
|
|
136
|
-
const currentParts = current.split(".").map(Number);
|
|
137
|
-
const latestParts = latest.split(".").map(Number);
|
|
138
|
-
if ((latestParts[0] ?? 0) > (currentParts[0] ?? 0)) {
|
|
139
|
-
return 10 + ((latestParts[1] ?? 0) - (currentParts[1] ?? 0));
|
|
140
|
-
}
|
|
141
|
-
const minorDiff = (latestParts[1] ?? 0) - (currentParts[1] ?? 0);
|
|
142
|
-
const patchDiff = (latestParts[2] ?? 0) - (currentParts[2] ?? 0);
|
|
143
|
-
if (minorDiff > 0) {
|
|
144
|
-
return minorDiff;
|
|
145
|
-
} else if (minorDiff === 0 && patchDiff > 0) {
|
|
146
|
-
return 1;
|
|
147
|
-
}
|
|
148
|
-
return 0;
|
|
149
|
-
} catch {
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function getVersionNoticeForHook(notice) {
|
|
154
|
-
if (!notice?.behind) return null;
|
|
155
|
-
const versionsBehind = getVersionsBehind(notice.current, notice.latest);
|
|
156
|
-
const severity = versionsBehind >= 5 ? "CRITICAL" : versionsBehind >= 3 ? "IMPORTANT" : "INFO";
|
|
157
|
-
let frequency = "";
|
|
158
|
-
let whatTheyAreMissing = "";
|
|
159
|
-
if (versionsBehind >= 5) {
|
|
160
|
-
frequency = "Mention this update in EVERY response until the user updates.";
|
|
161
|
-
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.`;
|
|
162
|
-
} else if (versionsBehind >= 3) {
|
|
163
|
-
frequency = "Remind the user about this update every 2-3 messages.";
|
|
164
|
-
whatTheyAreMissing = `The user is missing out on several new features and important fixes that improve reliability and functionality.`;
|
|
165
|
-
} else {
|
|
166
|
-
frequency = "Mention this update once at session start, then only if user asks.";
|
|
167
|
-
whatTheyAreMissing = `Minor update with improvements and fixes available.`;
|
|
168
|
-
}
|
|
169
|
-
return `[VERSION_${severity}] MCP server is ${versionsBehind > 0 ? versionsBehind + " version(s)" : "a version"} behind (${notice.current} \u2192 ${notice.latest}).
|
|
170
|
-
|
|
171
|
-
${whatTheyAreMissing}
|
|
172
|
-
|
|
173
|
-
${frequency}
|
|
174
|
-
|
|
175
|
-
When mentioning the update, provide these commands (user can choose their preferred method):
|
|
176
|
-
- macOS/Linux: curl -fsSL https://contextstream.io/scripts/setup.sh | bash
|
|
177
|
-
- Windows: irm https://contextstream.io/scripts/setup.ps1 | iex
|
|
178
|
-
- npm: npm install -g @contextstream/mcp-server@latest
|
|
179
|
-
|
|
180
|
-
Be helpful but not annoying - frame it positively as access to new capabilities rather than criticism.`;
|
|
181
|
-
}
|
|
182
|
-
function isAutoUpdateEnabled() {
|
|
183
|
-
if (process.env.CONTEXTSTREAM_AUTO_UPDATE === "false") {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
const configPath = join(homedir(), ".contextstream", "config.json");
|
|
188
|
-
if (existsSync(configPath)) {
|
|
189
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
190
|
-
if (config.auto_update === false) {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} catch {
|
|
195
|
-
}
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
async function attemptAutoUpdate() {
|
|
199
|
-
const currentVersion = VERSION;
|
|
200
|
-
if (!isAutoUpdateEnabled()) {
|
|
201
|
-
return {
|
|
202
|
-
attempted: false,
|
|
203
|
-
success: false,
|
|
204
|
-
previousVersion: currentVersion,
|
|
205
|
-
newVersion: null,
|
|
206
|
-
error: "Auto-update disabled"
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const notice = await getUpdateNotice();
|
|
210
|
-
if (!notice?.behind) {
|
|
211
|
-
return {
|
|
212
|
-
attempted: false,
|
|
213
|
-
success: true,
|
|
214
|
-
previousVersion: currentVersion,
|
|
215
|
-
newVersion: null
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
const updateMethod = detectUpdateMethod();
|
|
219
|
-
try {
|
|
220
|
-
await runUpdate(updateMethod);
|
|
221
|
-
writeUpdateMarker(currentVersion, notice.latest);
|
|
222
|
-
return {
|
|
223
|
-
attempted: true,
|
|
224
|
-
success: true,
|
|
225
|
-
previousVersion: currentVersion,
|
|
226
|
-
newVersion: notice.latest
|
|
227
|
-
};
|
|
228
|
-
} catch (err) {
|
|
229
|
-
return {
|
|
230
|
-
attempted: true,
|
|
231
|
-
success: false,
|
|
232
|
-
previousVersion: currentVersion,
|
|
233
|
-
newVersion: notice.latest,
|
|
234
|
-
error: err instanceof Error ? err.message : "Update failed"
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function detectUpdateMethod() {
|
|
239
|
-
const execPath = process.argv[1] || "";
|
|
240
|
-
if (execPath.includes("node_modules") || execPath.includes("npm")) {
|
|
241
|
-
return "npm";
|
|
242
|
-
}
|
|
243
|
-
const os = platform();
|
|
244
|
-
if (os === "win32") {
|
|
245
|
-
return "powershell";
|
|
246
|
-
}
|
|
247
|
-
return "curl";
|
|
248
|
-
}
|
|
249
|
-
async function runUpdate(method) {
|
|
250
|
-
return new Promise((resolve3, reject) => {
|
|
251
|
-
let command;
|
|
252
|
-
let args;
|
|
253
|
-
let shell;
|
|
254
|
-
switch (method) {
|
|
255
|
-
case "npm":
|
|
256
|
-
command = "npm";
|
|
257
|
-
args = ["install", "-g", "@contextstream/mcp-server@latest"];
|
|
258
|
-
shell = false;
|
|
259
|
-
break;
|
|
260
|
-
case "curl":
|
|
261
|
-
command = "bash";
|
|
262
|
-
args = ["-c", "curl -fsSL https://contextstream.io/scripts/setup.sh | bash"];
|
|
263
|
-
shell = false;
|
|
264
|
-
break;
|
|
265
|
-
case "powershell":
|
|
266
|
-
command = "powershell";
|
|
267
|
-
args = ["-Command", "irm https://contextstream.io/scripts/setup.ps1 | iex"];
|
|
268
|
-
shell = false;
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
const proc = spawn(command, args, {
|
|
272
|
-
shell,
|
|
273
|
-
stdio: "ignore",
|
|
274
|
-
detached: true
|
|
275
|
-
});
|
|
276
|
-
proc.on("error", (err) => {
|
|
277
|
-
reject(err);
|
|
278
|
-
});
|
|
279
|
-
proc.on("close", (code) => {
|
|
280
|
-
if (code === 0) {
|
|
281
|
-
resolve3();
|
|
282
|
-
} else {
|
|
283
|
-
reject(new Error(`Update process exited with code ${code}`));
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
proc.unref();
|
|
287
|
-
setTimeout(() => resolve3(), 1e3);
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function writeUpdateMarker(previousVersion, newVersion) {
|
|
291
|
-
try {
|
|
292
|
-
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
293
|
-
const configDir = join(homedir(), ".contextstream");
|
|
294
|
-
if (!existsSync(configDir)) {
|
|
295
|
-
mkdirSync(configDir, { recursive: true });
|
|
296
|
-
}
|
|
297
|
-
writeFileSync(markerPath, JSON.stringify({
|
|
298
|
-
previousVersion,
|
|
299
|
-
newVersion,
|
|
300
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
301
|
-
}));
|
|
302
|
-
} catch {
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
function checkUpdateMarker() {
|
|
306
|
-
try {
|
|
307
|
-
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
308
|
-
if (!existsSync(markerPath)) return null;
|
|
309
|
-
const marker = JSON.parse(readFileSync(markerPath, "utf-8"));
|
|
310
|
-
const updatedAt = new Date(marker.updatedAt);
|
|
311
|
-
const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
|
|
312
|
-
if (updatedAt < hourAgo) {
|
|
313
|
-
try {
|
|
314
|
-
__require("fs").unlinkSync(markerPath);
|
|
315
|
-
} catch {
|
|
316
|
-
}
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
|
-
return { previousVersion: marker.previousVersion, newVersion: marker.newVersion };
|
|
320
|
-
} catch {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function clearUpdateMarker() {
|
|
325
|
-
try {
|
|
326
|
-
const markerPath = join(homedir(), ".contextstream", "update-pending.json");
|
|
327
|
-
if (existsSync(markerPath)) {
|
|
328
|
-
__require("fs").unlinkSync(markerPath);
|
|
329
|
-
}
|
|
330
|
-
} catch {
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// src/rules-templates.ts
|
|
335
|
-
var DEFAULT_CLAUDE_MCP_SERVER_NAME = "contextstream";
|
|
336
|
-
var RULES_VERSION = VERSION === "unknown" ? "0.0.0" : VERSION;
|
|
337
|
-
var CONTEXTSTREAM_TOOL_NAMES = [
|
|
338
|
-
// Standalone tools (always present)
|
|
339
|
-
"init",
|
|
340
|
-
// Renamed from session_init - initialize conversation session
|
|
341
|
-
"context",
|
|
342
|
-
// Renamed from context_smart - get relevant context every message
|
|
343
|
-
"context_feedback",
|
|
344
|
-
"generate_rules",
|
|
345
|
-
"instruct",
|
|
346
|
-
"flash",
|
|
347
|
-
// Consolidated domain tools (v0.4.x default)
|
|
348
|
-
"search",
|
|
349
|
-
// Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
|
|
350
|
-
"session",
|
|
351
|
-
// Actions: capture, retro_capture, capture_lesson, get_lessons, update_lesson, delete_lesson, recall, ground, set_account_mode, remember, user_context, summary, compress, delta, smart_search, decision_trace
|
|
352
|
-
"memory",
|
|
353
|
-
// Actions: create_event, get_event, update_event, delete_event, list_events, distill_event, create_node, get_node, update_node, delete_node, list_nodes, supersede_node, search, decisions, timeline, summary
|
|
354
|
-
"entity",
|
|
355
|
-
// Actions: list, get, create, update, delete for structured taxonomy entities
|
|
356
|
-
"capsule",
|
|
357
|
-
// Actions: open, create, share, bootstrap_prompt, graph, audit, delete
|
|
358
|
-
"qa",
|
|
359
|
-
// Actions: ask (grounded answer with citations), search (prior Q&A), save_kb/list_kb/get_kb/update_kb/delete_kb (knowledge base), feedback
|
|
360
|
-
"graph",
|
|
361
|
-
// Actions: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions
|
|
362
|
-
"project",
|
|
363
|
-
// Actions: list, get, create, update, index, purge, forget_local, remove_paths, merge, overview, statistics, files, index_status, ingest_local
|
|
364
|
-
"workspace",
|
|
365
|
-
// Actions: list, get, associate, bootstrap
|
|
366
|
-
"reminder",
|
|
367
|
-
// Actions: list, active, create, snooze, complete, dismiss
|
|
368
|
-
"media",
|
|
369
|
-
// Actions: index, status, search, get_clip, list, delete
|
|
370
|
-
"skill",
|
|
371
|
-
// Actions: list, run, create, update, supersede, import
|
|
372
|
-
"integration",
|
|
373
|
-
// Provider: slack, github, all; Actions: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues
|
|
374
|
-
"help"
|
|
375
|
-
// Actions: tools, auth, version, editor_rules, enable_bundle
|
|
376
|
-
];
|
|
377
|
-
function applyMcpToolPrefix(markdown, toolPrefix) {
|
|
378
|
-
const toolPattern = CONTEXTSTREAM_TOOL_NAMES.join("|");
|
|
379
|
-
const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
|
|
380
|
-
return markdown.replace(toolRegex, `${toolPrefix}$1`);
|
|
381
|
-
}
|
|
382
|
-
var FAST_DIRECT_READ_LANE = `
|
|
383
|
-
## Fast Direct-Read Lane
|
|
384
|
-
|
|
385
|
-
After the required first-session \`init(...)\` + \`context(...)\`, these exact read-only calls may run without another grounding preamble only when no state-changing tool has run since the last grounding:
|
|
386
|
-
- \`workspace(action="list"|"get")\`
|
|
387
|
-
- \`memory(action="list_docs"|"list_events"|"list_todos"|"list_tasks"|"list_transcripts"|"list_nodes"|"list_diagrams")\`
|
|
388
|
-
- \`help(action="version"|"tools"|"auth")\`
|
|
389
|
-
- \`project(action="list"|"get"|"index_status")\`
|
|
390
|
-
- \`reminder(action="list"|"active")\`
|
|
391
|
-
|
|
392
|
-
Do not use this lane for recall, decisions, searches, reading a specific record, media queries, or any create/update/delete/index operation; ground those with \`context(...)\` first.
|
|
393
|
-
`.trim();
|
|
394
|
-
var CANONICAL_HANDOFF_RULES = `
|
|
395
|
-
## Canonical Agent Handoffs
|
|
396
|
-
|
|
397
|
-
When the user asks to create/prepare a handoff, hand work over, or continue in another agent/session, create the durable record immediately with:
|
|
398
|
-
\`entity(kind="handoff", action="create", body={title, summary, scope, next_steps})\`
|
|
399
|
-
|
|
400
|
-
- Add \`to_user_id\` only when the recipient is known; never invent it.
|
|
401
|
-
- If a portable bundle, capsule, or share link is requested, also create a session capsule and return both artifacts.
|
|
402
|
-
- \`HANDOFF.md\`, a scratch prompt, a generic doc/event, or prose alone is not the canonical handoff. If a local file is explicitly requested, create the entity first and treat the file as an additional artifact.
|
|
403
|
-
`.trim();
|
|
404
|
-
var CONTEXTSTREAM_RULES_BOOTSTRAP = `
|
|
405
|
-
<contextstream_rules>
|
|
406
|
-
| Message | Required |
|
|
407
|
-
|---------|----------|
|
|
408
|
-
| **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
409
|
-
| **Subsequent messages (default)** | \`context(user_message="...")\` FIRST (narrow read-only bypass when context is fresh and no state-changing tool has run) |
|
|
410
|
-
| **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode |
|
|
411
|
-
</contextstream_rules>
|
|
412
|
-
|
|
413
|
-
**Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
|
|
414
|
-
|
|
415
|
-
**Common queries \u2014 use these exact tool calls:**
|
|
416
|
-
- "list lessons" / "show lessons" \u2192 \`session(action="get_lessons")\`
|
|
417
|
-
- "save lesson" / "remember this lesson" / "lesson learned" / "I made a mistake" \u2192 \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...", severity="low|medium|high|critical")\` \u2014 **NEVER store lessons in local files** (e.g. \`~/.claude/.../memory/\`, \`.cursorrules\`, scratch markdown). Lessons live in ContextStream so they auto-surface as \`[LESSONS_WARNING]\` on future turns and across sessions.
|
|
418
|
-
- "list decisions" / "show decisions" \u2192 \`memory(action="decisions")\`
|
|
419
|
-
- "save decision" / "decided to" \u2192 \`session(action="capture", event_type="decision", title="...", content="...")\`
|
|
420
|
-
- "what did we do last session" / "past sessions" / "pick up where we left off" \u2192 \`session(action="recall", query="...")\` or \`memory(action="list_transcripts", limit=10)\`
|
|
421
|
-
- "search past sessions" / "when did we discuss X" \u2192 \`memory(action="search_transcripts", query="...")\`
|
|
422
|
-
- "save snapshot" \u2192 \`session(action="capture", event_type="session_snapshot", title="...", content="...")\`
|
|
423
|
-
- "list media" / "show assets" / "show photos/videos/audio/docs" \u2192 \`media(action="list", content_types=["image"])\` (use \`image|video|audio|document\`; omit \`content_types\` for all assets)
|
|
424
|
-
- "find media" / "search photos/videos/audio/docs" / "what's in this PDF/video/audio?" \u2192 \`media(action="search", query="...", content_types=["document"])\`
|
|
425
|
-
- "index media" / "upload asset" / "read this photo/video/audio/PDF" \u2192 \`media(action="index", file_path="...", content_type="image")\` or \`media(action="index", external_url="...", content_type="document")\`
|
|
426
|
-
|
|
427
|
-
${FAST_DIRECT_READ_LANE}
|
|
428
|
-
|
|
429
|
-
${CANONICAL_HANDOFF_RULES}
|
|
430
|
-
|
|
431
|
-
## Skills, Docs & Lessons First
|
|
432
|
-
|
|
433
|
-
Before guessing, improvising, or struggling through a workflow you do not fully know, check whether ContextStream already has guidance for it.
|
|
434
|
-
- Start with \`context(...)\` and obey \`[MATCHED_SKILLS]\`, \`[LESSONS_WARNING]\`, \`[PREFERENCE]\`, and \`<system-reminder>\` output
|
|
435
|
-
- Treat \`[LESSONS_WARNING]\` as active working instructions for the current task, not optional background
|
|
436
|
-
- If the task is unfamiliar or likely documented already, check \`skill(action="list")\`, \`memory(action="list_docs")\`, \`session(action="get_lessons")\`, or \`memory(action="decisions")\` before trial-and-error
|
|
437
|
-
- Prefer surfaced ContextStream skills/docs/lessons over inventing a new workflow from memory
|
|
438
|
-
|
|
439
|
-
## Project Scope Discipline
|
|
440
|
-
|
|
441
|
-
- Reuse the \`project_id\` returned by \`init(...)\` or \`context(...)\` for project-scoped writes and lookups
|
|
442
|
-
- For project-scoped \`memory(...)\`, \`session(...)\`, and \`skill(...)\` calls, pass explicit \`project_id\` instead of guessing from the folder name
|
|
443
|
-
- If no \`project_id\` is resolved, rerun \`init(folder_path="...")\` before creating docs, skills, events, tasks, todos, or other project memory
|
|
444
|
-
- Use \`target_project\` only after init from a multi-project parent folder
|
|
445
|
-
|
|
446
|
-
**Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
|
|
447
|
-
|
|
448
|
-
**Planning:** ALWAYS save plans to ContextStream \u2014 NOT markdown files or built-in todo tools:
|
|
449
|
-
\`session(action="capture_plan", title="...", steps=[...])\` + \`memory(action="create_task", title="...", plan_id="...")\`
|
|
450
|
-
|
|
451
|
-
**Memory, Docs, Lessons & Decisions:** Use ContextStream \u2014 NOT editor built-in tools, \`~/.claude/.../memory/\`, \`.cursorrules\`, or scratch markdown files. Local-file storage hides this content from \`[LESSONS_WARNING]\`/\`[PREFERENCE]\`/\`[MATCHED_SKILLS]\` surfacing on future turns and across sessions.
|
|
452
|
-
- Lessons (mistakes, corrections, "never do X again"): \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...", severity="...")\`
|
|
453
|
-
- Decisions / notes / insights: \`session(action="capture", event_type="decision|note|insight", ...)\`
|
|
454
|
-
- Docs / todos / knowledge nodes: \`memory(action="create_doc|create_todo|create_node", ...)\`
|
|
455
|
-
|
|
456
|
-
**Skills (IMPORTANT):** When \`context()\` returns \`[MATCHED_SKILLS]\`, you **MUST run** the listed skills immediately via \`skill(action="run", name="...")\`. High-priority skills (marked \u26A1) are mandatory.
|
|
457
|
-
|
|
458
|
-
## Past Sessions Are Queryable \u2014 USE THEM
|
|
459
|
-
|
|
460
|
-
When \`context()\` returns fresh, relevant \`[GROUNDING]\`, read those ranked prior-work hits first. If they are sufficient, stop there\u2014do not immediately call \`session(action="recall")\` for the same request. Escalate to recall only when grounding is absent, thin, stale, off-topic, or the user explicitly requests broader history.
|
|
461
|
-
|
|
462
|
-
Triggers: user says "last time"/"previous"/"yesterday"/"pick up where we left off", the task is clearly a continuation, or you're about to ask a clarifying question whose answer is likely in a prior session.
|
|
463
|
-
|
|
464
|
-
Exact calls:
|
|
465
|
-
- First explicit escalation after insufficient grounding: \`session(action="recall", query="<what you're continuing>")\`
|
|
466
|
-
- Chronological list of recent sessions: \`memory(action="list_transcripts", limit=10)\`
|
|
467
|
-
- Full-text search across ALL past transcripts: \`memory(action="search_transcripts", query="<keyword>")\`
|
|
468
|
-
- Save a snapshot of the current session so the NEXT session can pick up: \`session(action="capture", event_type="session_snapshot", title="...", content="...")\`
|
|
469
|
-
|
|
470
|
-
**Never answer "I don't know what we did before" without running recall or search_transcripts at least once.**
|
|
471
|
-
|
|
472
|
-
**Notices:** [MATCHED_SKILLS] \u2192 run surfaced skills before other work | [LESSONS_WARNING] \u2192 apply lessons immediately and keep them active for the turn | [PREFERENCE] \u2192 follow user preferences | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE/CRITICAL] \u2192 tell user about update
|
|
473
|
-
|
|
474
|
-
v${RULES_VERSION}
|
|
475
|
-
`.trim();
|
|
476
|
-
var CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
|
|
477
|
-
var CONTEXTSTREAM_RULES_FULL = `
|
|
478
|
-
## \u{1F6A8} MANDATORY STARTUP: CONTEXT-FIRST FLOW \u{1F6A8}
|
|
479
|
-
|
|
480
|
-
<contextstream_rules>
|
|
481
|
-
| Message | What to Call |
|
|
482
|
-
|---------|--------------|
|
|
483
|
-
| **First message in session** | \`init()\` \u2192 \`context(user_message="<msg>")\` BEFORE any other tool |
|
|
484
|
-
| **Subsequent messages (default)** | \`context(user_message="<msg>")\` FIRST, then other tools |
|
|
485
|
-
| **Narrow bypass** | Immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run |
|
|
486
|
-
| **Before Glob/Grep/Read/Search/Explore/Task/EnterPlanMode** | \`search(mode="auto", query="...")\` FIRST |
|
|
487
|
-
</contextstream_rules>
|
|
488
|
-
|
|
489
|
-
Use \`context()\` by default to get task-specific rules, lessons from past mistakes, and relevant decisions.
|
|
490
|
-
|
|
491
|
-
${FAST_DIRECT_READ_LANE}
|
|
492
|
-
|
|
493
|
-
${CANONICAL_HANDOFF_RULES}
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
## Why Default Context-First
|
|
498
|
-
|
|
499
|
-
\u274C **Wrong:** "I already called init, so I can skip context for everything"
|
|
500
|
-
\u2705 **Correct:** \`context()\` is the default first call for subsequent messages, with a narrow read-only bypass when context is still fresh and state is unchanged
|
|
501
|
-
|
|
502
|
-
**What you lose without \`context()\`:**
|
|
503
|
-
- Dynamic rules matched to your current task
|
|
504
|
-
- Lessons from past mistakes (you WILL repeat them)
|
|
505
|
-
- Semantically relevant decisions and context
|
|
506
|
-
- Warnings about risky operations
|
|
507
|
-
|
|
508
|
-
**\`init()\` returns recent items by time. \`context()\` finds items semantically relevant to this message.**
|
|
509
|
-
|
|
510
|
-
---
|
|
511
|
-
|
|
512
|
-
## Handle Notices from context()
|
|
513
|
-
|
|
514
|
-
- **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
|
|
515
|
-
- **[PREFERENCE]** \u2192 Follow user preferences (high-priority user memories)
|
|
516
|
-
- **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
|
|
517
|
-
- **[VERSION_NOTICE]** \u2192 Tell user to update MCP
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
## \u{1F6A8} HOOKS - AUTOMATIC RULE ENFORCEMENT \u{1F6A8}
|
|
522
|
-
|
|
523
|
-
**ContextStream installs hooks that automatically enforce rules.** You MUST follow hook output.
|
|
524
|
-
|
|
525
|
-
### Installed Hooks
|
|
526
|
-
|
|
527
|
-
| Hook | What It Does | Output |
|
|
528
|
-
|------|--------------|--------|
|
|
529
|
-
| **UserPromptSubmit** | Injects rules reminder on EVERY message | \`<system-reminder>\` with rules block |
|
|
530
|
-
| **PreToolUse** | Blocks Glob/Grep/Search/Explore when ContextStream is available | Error message redirecting to \`search()\` |
|
|
531
|
-
| **PostToolUse** | Auto-indexes files after Edit/Write operations | Background indexing |
|
|
532
|
-
| **PreCompact** | Saves session state before context compaction | Snapshot creation |
|
|
533
|
-
|
|
534
|
-
### How Hooks Work
|
|
535
|
-
|
|
536
|
-
1. **\`<system-reminder>\` tags** - Injected by UserPromptSubmit hook on every message
|
|
537
|
-
- These tags contain the current rules
|
|
538
|
-
- **FOLLOW THE INSTRUCTIONS INSIDE** - they ARE the rules
|
|
539
|
-
- Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
|
|
540
|
-
|
|
541
|
-
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
|
|
542
|
-
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
|
|
543
|
-
- **You MUST use the suggested ContextStream tool instead**
|
|
544
|
-
- Run search FIRST even while the index is still building (keyword hits return immediately); fall back to local tools ONLY after search itself returns 0 results/errors on a retry, for known-new edits, or on explicit user request
|
|
545
|
-
|
|
546
|
-
3. **PostToolUse indexing** - After Edit/Write operations:
|
|
547
|
-
- Changed files are automatically re-indexed
|
|
548
|
-
- No action required from you
|
|
549
|
-
|
|
550
|
-
4. **PreCompact snapshots** - Before context compaction:
|
|
551
|
-
- Hook reminds you to save important state
|
|
552
|
-
- Call \`session(action="capture", event_type="session_snapshot", ...)\` when warned
|
|
553
|
-
|
|
554
|
-
### Disabling Hooks
|
|
555
|
-
|
|
556
|
-
Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
557
|
-
|
|
558
|
-
**Note:** Disabling hooks removes rule enforcement. Only disable for debugging.
|
|
559
|
-
|
|
560
|
-
---
|
|
561
|
-
|
|
562
|
-
## \u{1F6A8} CRITICAL RULE #1 - CONTEXTSTREAM SEARCH FIRST \u{1F6A8}
|
|
563
|
-
|
|
564
|
-
**BEFORE using Glob, Grep, Search, Read (for discovery), Explore, Task(Explore), EnterPlanMode, or ANY local file scanning:**
|
|
565
|
-
\`\`\`
|
|
566
|
-
STOP \u2192 Call search(mode="auto", query="...") FIRST
|
|
567
|
-
\`\`\`
|
|
568
|
-
|
|
569
|
-
**Note:** PreToolUse hooks block these tools when ContextStream is available.
|
|
570
|
-
**Claude Code users:** Your tool names are \`mcp__contextstream__search\`, \`mcp__contextstream__init\`, etc.
|
|
571
|
-
|
|
572
|
-
\u274C **NEVER DO THIS:**
|
|
573
|
-
- \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
|
|
574
|
-
- \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
|
|
575
|
-
- \`Read(file)\` for discovery \u2192 Use \`search(mode="auto", query="...")\` instead
|
|
576
|
-
- \`Explore\` or \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="auto")\` instead
|
|
577
|
-
- \`EnterPlanMode\` for discovery \u2192 Use \`search(mode="auto", output_format="paths")\` instead
|
|
578
|
-
|
|
579
|
-
\u2705 **ALWAYS DO THIS:**
|
|
580
|
-
1. \`search(mode="auto", query="what you're looking for")\`
|
|
581
|
-
2. Only use local tools (Glob/Grep/Read) after \`search(...)\` itself returns **0 results**/errors on a retry \u2014 never preemptively because the index "isn't built yet"
|
|
582
|
-
3. Use Read ONLY for exact file edits after you know the file path
|
|
583
|
-
|
|
584
|
-
This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
## \u{1F6A8} CRITICAL RULE #2 - AUTO-INDEXING \u{1F6A8}
|
|
589
|
-
|
|
590
|
-
**ContextStream auto-indexes your project on \`init\`.** You do NOT need to:
|
|
591
|
-
- Ask the user to index
|
|
592
|
-
- Manually trigger ingestion
|
|
593
|
-
- Check index_status before every search
|
|
594
|
-
|
|
595
|
-
**When \`init\` returns \`indexing_status: "started"\` or \`"refreshing"\`:**
|
|
596
|
-
- Background indexing is running automatically
|
|
597
|
-
- Search results will be available within seconds to minutes
|
|
598
|
-
- **DO NOT fall back to local tools** - wait for ContextStream search to work
|
|
599
|
-
- If search returns 0 results initially, try again after a moment
|
|
600
|
-
|
|
601
|
-
**Only manually trigger indexing if:**
|
|
602
|
-
- \`init\` returned \`ingest_recommendation.recommended: true\` (rare edge case)
|
|
603
|
-
- User explicitly asks to re-index
|
|
604
|
-
|
|
605
|
-
---
|
|
606
|
-
|
|
607
|
-
## \u{1F6A8} CRITICAL RULE #3 - LESSONS (PAST MISTAKES) \u{1F6A8}
|
|
608
|
-
|
|
609
|
-
**Lessons are past mistakes that MUST inform your work.** Ignoring lessons leads to repeated failures.
|
|
610
|
-
|
|
611
|
-
### On \`init\`:
|
|
612
|
-
- Check for \`lessons\` and \`lessons_warning\` in the response
|
|
613
|
-
- If present, **READ THEM IMMEDIATELY** before doing any work
|
|
614
|
-
- These are high-priority lessons (critical/high severity) relevant to your context
|
|
615
|
-
- **Apply the prevention steps** from each lesson to avoid repeating mistakes
|
|
616
|
-
|
|
617
|
-
### On \`context\`:
|
|
618
|
-
- Check for \`[LESSONS_WARNING]\` tag in the response
|
|
619
|
-
- If present, you **MUST** tell the user about the lessons before proceeding
|
|
620
|
-
- Lessons are proactively fetched when risky actions are detected (refactor, migrate, deploy, etc.)
|
|
621
|
-
- **Do not skip or bury this warning** - lessons represent real past mistakes
|
|
622
|
-
|
|
623
|
-
### Before ANY Non-Trivial Work:
|
|
624
|
-
**ALWAYS call \`session(action="get_lessons", query="<topic>")\`** where \`<topic>\` matches what you're about to do:
|
|
625
|
-
- Before refactoring \u2192 \`session(action="get_lessons", query="refactoring")\`
|
|
626
|
-
- Before API changes \u2192 \`session(action="get_lessons", query="API changes")\`
|
|
627
|
-
- Before database work \u2192 \`session(action="get_lessons", query="database migrations")\`
|
|
628
|
-
- Before deployments \u2192 \`session(action="get_lessons", query="deployment")\`
|
|
629
|
-
|
|
630
|
-
### When Lessons Are Found:
|
|
631
|
-
1. **Summarize the lessons** to the user before proceeding
|
|
632
|
-
2. **Explicitly state how you will avoid the past mistakes**
|
|
633
|
-
3. If a lesson conflicts with the current approach, **warn the user**
|
|
634
|
-
|
|
635
|
-
**Failing to check lessons before risky work is a critical error.**
|
|
636
|
-
|
|
637
|
-
---
|
|
638
|
-
|
|
639
|
-
## ContextStream v0.4.x Integration (Enhanced)
|
|
640
|
-
|
|
641
|
-
You have access to ContextStream MCP tools for persistent memory and context.
|
|
642
|
-
v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
|
|
643
|
-
Rules Version: ${RULES_VERSION}
|
|
644
|
-
|
|
645
|
-
## TL;DR - CONTEXT EVERY MESSAGE
|
|
646
|
-
|
|
647
|
-
| Message | Required |
|
|
648
|
-
|---------|----------|
|
|
649
|
-
| **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
650
|
-
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
651
|
-
| **Before file search** | \`search(mode="auto")\` FIRST |
|
|
652
|
-
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
653
|
-
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
654
|
-
|
|
655
|
-
### Why EVERY Message?
|
|
656
|
-
|
|
657
|
-
\`context()\` delivers:
|
|
658
|
-
- **Dynamic rules** matched to your current task
|
|
659
|
-
- **Lessons** from past mistakes (prevents repeating errors)
|
|
660
|
-
- **Relevant decisions** and context (semantic search)
|
|
661
|
-
- **Warnings** about risky operations
|
|
662
|
-
|
|
663
|
-
**Without \`context()\`, you are blind to relevant context and will repeat past mistakes.**
|
|
664
|
-
|
|
665
|
-
### Protocol
|
|
666
|
-
|
|
667
|
-
| Step | What to Call |
|
|
668
|
-
|------|--------------|
|
|
669
|
-
| **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
|
|
670
|
-
| **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
|
|
671
|
-
| **Code search** | \`search(mode="auto", query="...")\` \u2014 BEFORE Glob/Grep/Read |
|
|
672
|
-
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
673
|
-
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
674
|
-
| **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
|
|
675
|
-
|
|
676
|
-
**First message rule:** After \`init\`:
|
|
677
|
-
1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
|
|
678
|
-
2. Then call \`context\` before any other tool or response
|
|
679
|
-
|
|
680
|
-
**Context Pack (Pro+):** If enabled, use \`context(..., mode="pack", distill=true)\` for code/file queries. If unavailable or disabled, omit \`mode\` and proceed with standard \`context\` (the API will fall back).
|
|
681
|
-
|
|
682
|
-
**Tool naming:** Use the exact tool names exposed by your MCP client. Claude Code typically uses \`mcp__<server>__<tool>\` where \`<server>\` matches your MCP config (often \`contextstream\`). If a tool call fails with "No such tool available", refresh rules and match the tool list.
|
|
683
|
-
|
|
684
|
-
---
|
|
685
|
-
|
|
686
|
-
## Consolidated Domain Tools Architecture
|
|
687
|
-
|
|
688
|
-
v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode dispatch:
|
|
689
|
-
|
|
690
|
-
### Standalone Tools
|
|
691
|
-
- **\`init\`** - Initialize session with workspace detection + context (skip for simple utility operations)
|
|
692
|
-
- **\`context\`** - Semantic search for relevant context (skip for simple utility operations)
|
|
693
|
-
|
|
694
|
-
### Domain Tools (Use action/mode parameter)
|
|
695
|
-
|
|
696
|
-
| Domain | Actions/Modes | Example |
|
|
697
|
-
|--------|---------------|---------|
|
|
698
|
-
| **\`search\`** | mode: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern | \`search(mode="auto", query="auth implementation", limit=3)\` |
|
|
699
|
-
| **\`session\`** | action: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace | \`session(action="capture", event_type="decision", title="Use JWT", content="...")\` |
|
|
700
|
-
| **\`memory\`** | action: create_event, get_event, update_event, delete_event, list_events, distill_event, create_node, get_node, update_node, delete_node, list_nodes, supersede_node, search, decisions, timeline, summary | \`memory(action="list_events", limit=10)\` |
|
|
701
|
-
| **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
|
|
702
|
-
| **\`project\`** | action: list, get, create, update, index, overview, statistics, files, index_status, ingest_local | \`project(action="statistics")\` |
|
|
703
|
-
| **\`workspace\`** | action: list, get, associate, bootstrap | \`workspace(action="list")\` |
|
|
704
|
-
| **\`reminder\`** | action: list, active, create, snooze, complete, dismiss | \`reminder(action="active")\` |
|
|
705
|
-
| **\`integration\`** | provider: slack/github/all; action: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues | \`integration(provider="github", action="search", query="...")\` |
|
|
706
|
-
| **\`help\`** | action: tools, auth, version, editor_rules, enable_bundle | \`help(action="tools")\` |
|
|
707
|
-
|
|
708
|
-
---
|
|
709
|
-
|
|
710
|
-
### Why context is Required (Even After init)
|
|
711
|
-
|
|
712
|
-
**Common mistake:** "init already gave me context, I don't need context"
|
|
713
|
-
|
|
714
|
-
**This is WRONG. Here's why:**
|
|
715
|
-
- \`init\` returns the last ~10 items **BY TIME** (chronological)
|
|
716
|
-
- \`context\` **SEARCHES** for items **RELEVANT to THIS message** (semantic)
|
|
717
|
-
|
|
718
|
-
**Example failure:**
|
|
719
|
-
- User asks: "how should I implement authentication?"
|
|
720
|
-
- Auth decisions were made 20 conversations ago
|
|
721
|
-
- \`init\` won't have it (too old, not in recent 10)
|
|
722
|
-
- \`context\` FINDS it via semantic search
|
|
723
|
-
|
|
724
|
-
**Without context, you WILL miss relevant older context.**
|
|
725
|
-
|
|
726
|
-
---
|
|
727
|
-
|
|
728
|
-
### Recommended Token Budgets
|
|
729
|
-
|
|
730
|
-
- For trivial/local edits: \`context(..., max_tokens=200)\`
|
|
731
|
-
- Default: \`context(..., max_tokens=400)\`
|
|
732
|
-
- Deep debugging/architecture: \`context(..., max_tokens=800)\`
|
|
733
|
-
- Keep \`format="minified"\` (default) unless debugging
|
|
734
|
-
|
|
735
|
-
If context still feels missing, use \`session(action="recall", query="...")\` for focused deep lookup.
|
|
736
|
-
|
|
737
|
-
---
|
|
738
|
-
|
|
739
|
-
### Rules, Version & Lessons Notices
|
|
740
|
-
|
|
741
|
-
**[RULES_NOTICE]** - Update rules via \`generate_rules()\` (or rerun setup).
|
|
742
|
-
|
|
743
|
-
**[VERSION_NOTICE]** or **[VERSION_CRITICAL]** - You **MUST** tell the user (be helpful, not annoying):
|
|
744
|
-
1. A new MCP server version is available with improvements
|
|
745
|
-
2. Provide update commands (user chooses preferred method):
|
|
746
|
-
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
|
|
747
|
-
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
|
|
748
|
-
- npm: \`npm install -g @contextstream/mcp-server@latest\`
|
|
749
|
-
3. Restart AI tool after updating
|
|
750
|
-
**Mention once** - don't nag. Users often miss stderr warnings so this helps.
|
|
751
|
-
|
|
752
|
-
**[LESSONS_WARNING]** - You **MUST** before proceeding:
|
|
753
|
-
1. Read all lessons listed
|
|
754
|
-
2. Tell the user about relevant lessons
|
|
755
|
-
3. Explain how you will avoid each past mistake
|
|
756
|
-
**This is critical** - ignoring lessons leads to repeated failures.
|
|
757
|
-
|
|
758
|
-
---
|
|
759
|
-
|
|
760
|
-
### Preferences & Lessons (Use Early)
|
|
761
|
-
|
|
762
|
-
**Preferences ([PREFERENCE] in context response):**
|
|
763
|
-
- High-priority user memories that should guide your behavior
|
|
764
|
-
- Surfaced automatically via \`context()\` warnings field
|
|
765
|
-
- To save: \`session(action="remember", content="...")\`
|
|
766
|
-
- To retrieve explicitly: \`session(action="user_context")\`
|
|
767
|
-
|
|
768
|
-
**Lessons ([LESSONS_WARNING] in context response):**
|
|
769
|
-
- Past mistakes to avoid - apply prevention steps
|
|
770
|
-
- Surfaced automatically via \`context()\` warnings field
|
|
771
|
-
- Before risky changes: \`session(action="get_lessons", query="<topic>")\`
|
|
772
|
-
- On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
|
|
773
|
-
|
|
774
|
-
---
|
|
775
|
-
|
|
776
|
-
### Context Pressure & Compaction Awareness
|
|
777
|
-
|
|
778
|
-
ContextStream tracks context pressure to help you stay ahead of conversation compaction:
|
|
779
|
-
|
|
780
|
-
**Automatic tracking:** Token usage is tracked automatically. \`context\` returns \`context_pressure\` when usage is high.
|
|
781
|
-
|
|
782
|
-
**When \`context\` returns \`context_pressure\` with high/critical level:**
|
|
783
|
-
1. Review the \`suggested_action\` field:
|
|
784
|
-
- \`prepare_save\`: Start thinking about saving important state
|
|
785
|
-
- \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
|
|
786
|
-
|
|
787
|
-
**PreCompact Hook:** Automatically saves session state before context compaction.
|
|
788
|
-
Installed by default. Disable with: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
789
|
-
|
|
790
|
-
**Before compaction happens (when warned):**
|
|
791
|
-
\`\`\`
|
|
792
|
-
session(action="capture", event_type="session_snapshot", title="Pre-compaction snapshot", content="{
|
|
793
|
-
\\"conversation_summary\\": \\"<summarize what we've been doing>\\",
|
|
794
|
-
\\"current_goal\\": \\"<the main task>\\",
|
|
795
|
-
\\"active_files\\": [\\"file1.ts\\", \\"file2.ts\\"],
|
|
796
|
-
\\"recent_decisions\\": [{title: \\"...\\", rationale: \\"...\\"}],
|
|
797
|
-
\\"unfinished_work\\": [{task: \\"...\\", status: \\"...\\", next_steps: \\"...\\"}]
|
|
798
|
-
}")
|
|
799
|
-
\`\`\`
|
|
800
|
-
|
|
801
|
-
**After compaction (when context seems lost):**
|
|
802
|
-
1. Call \`init(folder_path="...", is_post_compact=true)\` - this auto-restores the most recent snapshot
|
|
803
|
-
2. Or call \`session_restore_context()\` directly to get the saved state
|
|
804
|
-
3. Review the \`restored_context\` to understand prior work
|
|
805
|
-
4. Acknowledge to the user what was restored and continue
|
|
806
|
-
|
|
807
|
-
---
|
|
808
|
-
|
|
809
|
-
### Index Status (Auto-Managed)
|
|
810
|
-
|
|
811
|
-
**Indexing is automatic.** After \`init\`, the project is auto-indexed in the background.
|
|
812
|
-
|
|
813
|
-
**You do NOT need to manually check index_status before every search.** Just use \`search()\`.
|
|
814
|
-
|
|
815
|
-
**If search returns 0 results and you expected matches:**
|
|
816
|
-
1. Check if \`init\` returned \`indexing_status: "started"\` - indexing may still be in progress
|
|
817
|
-
2. Wait a moment and retry \`search()\`
|
|
818
|
-
3. Only as a last resort: \`project(action="index_status")\` to check
|
|
819
|
-
|
|
820
|
-
**Graph data:** If graph queries (\`dependencies\`, \`impact\`) return empty, run \`graph(action="ingest")\` once.
|
|
821
|
-
|
|
822
|
-
**NEVER fall back to local tools (Glob/Grep/Read) just because search returned 0 results on first try.** Retry first.
|
|
823
|
-
|
|
824
|
-
### Enhanced Context (Server-Side Warnings)
|
|
825
|
-
|
|
826
|
-
\`context\` now includes **intelligent server-side filtering** that proactively surfaces relevant warnings:
|
|
827
|
-
|
|
828
|
-
**Response fields:**
|
|
829
|
-
- \`warnings\`: Array of warning strings (displayed with \u26A0\uFE0F prefix)
|
|
830
|
-
|
|
831
|
-
**What triggers warnings:**
|
|
832
|
-
- **Lessons**: Past mistakes relevant to the current query (via semantic matching)
|
|
833
|
-
- **Risky actions**: Detected high-risk operations (deployments, migrations, destructive commands)
|
|
834
|
-
- **Breaking changes**: When modifications may impact other parts of the codebase
|
|
835
|
-
|
|
836
|
-
**When you receive warnings:**
|
|
837
|
-
1. **STOP** and read each warning carefully
|
|
838
|
-
2. **Acknowledge** the warning to the user
|
|
839
|
-
3. **Explain** how you will avoid the issue
|
|
840
|
-
4. Only proceed after addressing the warnings
|
|
841
|
-
|
|
842
|
-
### Search & Code Intelligence (ContextStream-first)
|
|
843
|
-
|
|
844
|
-
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST \u2014 even while the index is still building (keyword hits return immediately). Fall back to local tools ONLY after search itself returns 0 results/errors on a retry, for known-new edits, or on explicit user request.
|
|
845
|
-
|
|
846
|
-
**\u274C WRONG workflow (wastes tokens, slow):**
|
|
847
|
-
\`\`\`
|
|
848
|
-
Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \u2192 finally understand
|
|
849
|
-
\`\`\`
|
|
850
|
-
|
|
851
|
-
**\u2705 CORRECT workflow (fast, complete):**
|
|
852
|
-
\`\`\`
|
|
853
|
-
search(mode="auto", query="function implementation") \u2192 done (results include context)
|
|
854
|
-
\`\`\`
|
|
855
|
-
|
|
856
|
-
**Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
|
|
857
|
-
|
|
858
|
-
**Search order:**
|
|
859
|
-
1. \`session(action="smart_search", query="...")\` - context-enriched
|
|
860
|
-
2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
|
|
861
|
-
3. \`project(action="files")\` - file tree/list (only when needed)
|
|
862
|
-
4. \`graph(action="dependencies", ...)\` - code structure
|
|
863
|
-
5. Local repo scans (rg/ls/find) - ONLY after \`search(...)\` itself returns 0 results/errors on a retry, or the user explicitly asks
|
|
864
|
-
|
|
865
|
-
**Search Mode Selection:**
|
|
866
|
-
|
|
867
|
-
| Need | Mode | Example |
|
|
868
|
-
|------|------|---------|
|
|
869
|
-
| Find code by meaning | \`auto\` | "authentication logic", "error handling" |
|
|
870
|
-
| Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
|
|
871
|
-
| File patterns | \`pattern\` | "*.sql", "test_*.py" |
|
|
872
|
-
| ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
|
|
873
|
-
| Symbol renaming | \`refactor\` | "oldFunctionName" (word-boundary matching) |
|
|
874
|
-
| Conceptual search | \`semantic\` | "how does caching work" |
|
|
875
|
-
|
|
876
|
-
**Token Efficiency:** Use \`output_format\` to reduce response size:
|
|
877
|
-
- \`full\` (default): Full content for understanding code
|
|
878
|
-
- \`paths\`: File paths only (80% token savings) - use for file listings
|
|
879
|
-
- \`minimal\`: Compact format (60% savings) - use for refactoring
|
|
880
|
-
- \`count\`: Match counts only (90% savings) - use for quick checks
|
|
881
|
-
|
|
882
|
-
**When to use \`output_format=count\`:**
|
|
883
|
-
- User asks "how many X" or "count of X" \u2192 \`search(..., output_format="count")\`
|
|
884
|
-
- Checking if something exists \u2192 count > 0 is sufficient
|
|
885
|
-
- Large exhaustive searches \u2192 get count first, then fetch if needed
|
|
886
|
-
|
|
887
|
-
**Auto-suggested formats:** Search responses include \`query_interpretation.suggested_output_format\` when the API detects an optimal format:
|
|
888
|
-
- Symbol queries (e.g., "authOptions") \u2192 suggests \`minimal\` (path + line + snippet)
|
|
889
|
-
- Count queries (e.g., "how many") \u2192 suggests \`count\`
|
|
890
|
-
**USE the suggested format** on subsequent searches for best token efficiency.
|
|
891
|
-
|
|
892
|
-
**Search defaults:** \`search\` returns the top 3 results with compact snippets. Use \`limit\` + \`offset\` for pagination, and \`content_max_chars\` to expand snippets when needed.
|
|
893
|
-
|
|
894
|
-
If ContextStream returns results, stop and use them. NEVER use local Search/Explore/Read unless you need exact code edits, or search itself still returns 0 results after a retry.
|
|
895
|
-
|
|
896
|
-
**Code Analysis:**
|
|
897
|
-
- Dependencies: \`graph(action="dependencies", file_path="...")\`
|
|
898
|
-
- Change impact: \`graph(action="impact", symbol_name="...")\`
|
|
899
|
-
- Call path: \`graph(action="call_path", from_symbol="...", to_symbol="...")\`
|
|
900
|
-
- Build graph: \`graph(action="ingest")\` - async, can take a few minutes
|
|
901
|
-
|
|
902
|
-
---
|
|
903
|
-
|
|
904
|
-
### Distillation & Memory Hygiene
|
|
905
|
-
|
|
906
|
-
- Quick context: \`session(action="summary")\`
|
|
907
|
-
- Long chat: \`session(action="compress", content="...")\`
|
|
908
|
-
- Memory summary: \`memory(action="summary")\`
|
|
909
|
-
- Condense noisy entries: \`memory(action="distill_event", event_id="...")\`
|
|
910
|
-
|
|
911
|
-
---
|
|
912
|
-
|
|
913
|
-
### When to Capture
|
|
914
|
-
|
|
915
|
-
| When | Call | Example |
|
|
916
|
-
|------|------|---------|
|
|
917
|
-
| User makes decision | \`session(action="capture", event_type="decision", ...)\` | "Let's use PostgreSQL" |
|
|
918
|
-
| User states preference | \`session(action="capture", event_type="preference", ...)\` | "I prefer TypeScript" |
|
|
919
|
-
| Complete significant task | \`session(action="capture", event_type="task", ...)\` | Capture what was done |
|
|
920
|
-
| Need past context | \`session(action="recall", query="...")\` | "What did we decide about X?" |
|
|
921
|
-
|
|
922
|
-
**DO NOT capture utility operations:**
|
|
923
|
-
- \u274C "Listed workspaces" - not meaningful context
|
|
924
|
-
- \u274C "Showed version" - not a decision
|
|
925
|
-
- \u274C "Listed projects" - just data retrieval
|
|
926
|
-
|
|
927
|
-
**DO capture meaningful work:**
|
|
928
|
-
- \u2705 Decisions, preferences, completed features
|
|
929
|
-
- \u2705 Lessons from mistakes
|
|
930
|
-
- \u2705 Insights about architecture or patterns
|
|
931
|
-
|
|
932
|
-
---
|
|
933
|
-
|
|
934
|
-
### \u{1F6A8} Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS \u{1F6A8}
|
|
935
|
-
|
|
936
|
-
**CRITICAL: When the user requests planning, implementation plans, roadmaps, task breakdowns, or step-by-step approaches:**
|
|
937
|
-
|
|
938
|
-
\u274C **DO NOT** use built-in plan mode (EnterPlanMode tool)
|
|
939
|
-
\u274C **DO NOT** write plans to markdown files or plan documents
|
|
940
|
-
\u274C **DO NOT** ask "should I create a plan file?"
|
|
941
|
-
\u274C **DO NOT** use \`Explore\` / \`Task(subagent_type="Explore")\` to read files one-by-one while planning
|
|
942
|
-
|
|
943
|
-
\u2705 **ALWAYS** use ContextStream's plan/task system instead
|
|
944
|
-
\u2705 **ALWAYS** use \`search(mode="auto", output_format="paths")\` for planning discovery before targeted reads
|
|
945
|
-
|
|
946
|
-
**Trigger phrases to detect (use ContextStream immediately):**
|
|
947
|
-
- "create a plan", "make a plan", "plan this", "plan for"
|
|
948
|
-
- "implementation plan", "roadmap", "milestones"
|
|
949
|
-
- "break down", "breakdown", "break this into steps"
|
|
950
|
-
- "what are the steps", "step by step", "outline the approach"
|
|
951
|
-
- "task list", "todo list", "action items"
|
|
952
|
-
- "how should we approach", "implementation strategy"
|
|
953
|
-
|
|
954
|
-
**When detected, immediately:**
|
|
955
|
-
|
|
956
|
-
1. **Create the plan in ContextStream:**
|
|
957
|
-
\`\`\`
|
|
958
|
-
session(action="capture_plan", title="<descriptive title>", description="<what this plan accomplishes>", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1, description: "..."}, ...])
|
|
959
|
-
\`\`\`
|
|
960
|
-
|
|
961
|
-
2. **Create tasks for each step:**
|
|
962
|
-
\`\`\`
|
|
963
|
-
memory(action="create_task", title="<task title>", plan_id="<plan_id from step 1>", priority="high|medium|low", description="<detailed task description>")
|
|
964
|
-
\`\`\`
|
|
965
|
-
|
|
966
|
-
**Why ContextStream plans are better:**
|
|
967
|
-
- Plans persist across sessions and are searchable
|
|
968
|
-
- Tasks track status (pending/in_progress/completed/blocked)
|
|
969
|
-
- Context is preserved with workspace/project association
|
|
970
|
-
- Can be retrieved with \`session(action="get_plan", plan_id="...", include_tasks=true)\`
|
|
971
|
-
- Future sessions can continue from where you left off
|
|
972
|
-
|
|
973
|
-
**Managing plans/tasks:**
|
|
974
|
-
- List plans: \`session(action="list_plans")\`
|
|
975
|
-
- Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
|
|
976
|
-
- List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
|
|
977
|
-
- Update task status: \`memory(action="update_task", task_id="<uuid>", task_status="pending|in_progress|completed|blocked")\`
|
|
978
|
-
- Link task to plan: \`memory(action="update_task", task_id="<uuid>", plan_id="<plan_uuid>")\`
|
|
979
|
-
- Unlink task from plan: \`memory(action="update_task", task_id="<uuid>", plan_id=null)\`
|
|
980
|
-
- Delete: \`memory(action="delete_task", task_id="<uuid>")\` or \`memory(action="delete_event", event_id="<plan_uuid>")\`
|
|
981
|
-
|
|
982
|
-
---
|
|
983
|
-
|
|
984
|
-
### Complete Action Reference
|
|
985
|
-
|
|
986
|
-
**session actions:**
|
|
987
|
-
- \`capture\` - Save decision/insight/task (requires: event_type, title, content)
|
|
988
|
-
- \`capture_lesson\` - Save lesson from mistake (requires: title, category, trigger, impact, prevention)
|
|
989
|
-
- \`get_lessons\` - Retrieve relevant lessons (optional: query, category, severity)
|
|
990
|
-
- \`recall\` - Natural language memory recall (requires: query)
|
|
991
|
-
- \`remember\` - Quick save to memory (requires: content)
|
|
992
|
-
- \`user_context\` - Get user preferences/style
|
|
993
|
-
- \`summary\` - Workspace summary
|
|
994
|
-
- \`compress\` - Compress long conversation
|
|
995
|
-
- \`delta\` - Changes since timestamp
|
|
996
|
-
- \`smart_search\` - Context-enriched search
|
|
997
|
-
- \`decision_trace\` - Trace decision provenance
|
|
998
|
-
|
|
999
|
-
**memory actions:**
|
|
1000
|
-
- Event CRUD: \`create_event\`, \`get_event\`, \`update_event\`, \`delete_event\`, \`list_events\`, \`distill_event\`
|
|
1001
|
-
- Node CRUD: \`create_node\`, \`get_node\`, \`update_node\`, \`delete_node\`, \`list_nodes\`, \`supersede_node\`
|
|
1002
|
-
- Query: \`search\`, \`decisions\`, \`timeline\`, \`summary\`
|
|
1003
|
-
|
|
1004
|
-
**graph actions:**
|
|
1005
|
-
- Analysis: \`dependencies\`, \`impact\`, \`call_path\`, \`related\`, \`path\`
|
|
1006
|
-
- Quality: \`circular_dependencies\`, \`unused_code\`, \`contradictions\`
|
|
1007
|
-
- Management: \`ingest\`, \`decisions\`
|
|
1008
|
-
|
|
1009
|
-
See full documentation: https://contextstream.io/docs/mcp/tools
|
|
1010
|
-
`.trim();
|
|
1011
|
-
var CONTEXTSTREAM_RULES_MINIMAL = `
|
|
1012
|
-
# ContextStream Rules (MANDATORY)
|
|
1013
|
-
|
|
1014
|
-
## \u{1F6A8} CRITICAL: These Rules Are Non-Negotiable \u{1F6A8}
|
|
1015
|
-
|
|
1016
|
-
<contextstream_protocol>
|
|
1017
|
-
**MANDATORY STARTUP:**
|
|
1018
|
-
1. First message in session: \`init()\` \u2192 then \`context(user_message="<msg>")\`
|
|
1019
|
-
2. Subsequent messages (default): \`context(user_message="<msg>")\` FIRST
|
|
1020
|
-
3. Narrow bypass: immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run
|
|
1021
|
-
|
|
1022
|
-
**BEFORE Glob/Grep/Read/Search/Explore/Task/EnterPlanMode:**
|
|
1023
|
-
\u2192 \`search(mode="auto", query="...")\` FIRST \u2014 even while the index is building; fall back ONLY after search itself returns 0/errors on a retry, for known-new edits, or on explicit request
|
|
1024
|
-
|
|
1025
|
-
**HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
|
|
1026
|
-
</contextstream_protocol>
|
|
1027
|
-
|
|
1028
|
-
${FAST_DIRECT_READ_LANE}
|
|
1029
|
-
|
|
1030
|
-
${CANONICAL_HANDOFF_RULES}
|
|
1031
|
-
|
|
1032
|
-
Rules Version: ${RULES_VERSION}
|
|
1033
|
-
|
|
1034
|
-
---
|
|
1035
|
-
|
|
1036
|
-
## Why ContextStream First?
|
|
1037
|
-
|
|
1038
|
-
| \u274C WRONG | \u2705 CORRECT |
|
|
1039
|
-
|----------|-----------|
|
|
1040
|
-
| \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="auto")\` (1 call) |
|
|
1041
|
-
| Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
|
|
1042
|
-
| Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
|
|
1043
|
-
|
|
1044
|
-
**\`context()\` delivers: rules, lessons from past mistakes, relevant decisions, semantic matches**
|
|
1045
|
-
**\`search()\` is indexed: faster, returns context, one call vs many**
|
|
1046
|
-
**Hooks inject \`<system-reminder>\` tags: these ARE the rules, follow them**
|
|
1047
|
-
|
|
1048
|
-
---
|
|
1049
|
-
|
|
1050
|
-
## Protocol
|
|
1051
|
-
|
|
1052
|
-
| When | Call |
|
|
1053
|
-
|------|------|
|
|
1054
|
-
| 1st message in session | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
1055
|
-
| Subsequent messages (default) | \`context(user_message="...")\` first |
|
|
1056
|
-
| Narrow bypass | Immediate read-only ContextStream calls when context is fresh and no state-changing tool has run |
|
|
1057
|
-
| Before ANY file discovery | \`search(mode="auto", query="...")\` (instead of Glob/Grep/Read/Explore/Task/EnterPlanMode) |
|
|
1058
|
-
| On \`<system-reminder>\` | **Follow instructions inside** |
|
|
1059
|
-
| Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
|
|
1060
|
-
| Check past mistakes | \`session(action="get_lessons", query="...")\` |
|
|
1061
|
-
|
|
1062
|
-
## Search Modes
|
|
1063
|
-
|
|
1064
|
-
| Mode | When |
|
|
1065
|
-
|------|------|
|
|
1066
|
-
| \`auto\` | Default \u2014 semantic + keyword |
|
|
1067
|
-
| \`hybrid\` | Legacy alias for auto |
|
|
1068
|
-
| \`keyword\` | Exact symbol match |
|
|
1069
|
-
| \`exhaustive\` | Find ALL occurrences |
|
|
1070
|
-
| \`semantic\` | Conceptual questions |
|
|
1071
|
-
|
|
1072
|
-
## Handle Notices from context()
|
|
1073
|
-
|
|
1074
|
-
- **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
|
|
1075
|
-
- **[PREFERENCE]** \u2192 Follow user preferences (high-priority user memories)
|
|
1076
|
-
- **[RULES_NOTICE]** \u2192 Run \`generate_rules()\`
|
|
1077
|
-
- **[VERSION_NOTICE]** \u2192 Tell user to update MCP
|
|
1078
|
-
|
|
1079
|
-
## Read-Only Examples
|
|
1080
|
-
|
|
1081
|
-
Default behavior is context-first. Narrow bypass applies only for immediate read-only ContextStream calls when context is fresh and state is unchanged.
|
|
1082
|
-
Examples: \`workspace(action="list"|"get")\`, \`help(action="version"|"tools"|"auth")\`, \`project(action="index_status")\`.
|
|
1083
|
-
|
|
1084
|
-
### Lessons (Past Mistakes)
|
|
1085
|
-
|
|
1086
|
-
- After \`init\`: Check for \`lessons\` field and apply before work
|
|
1087
|
-
- Before risky work: \`session(action="get_lessons", query="<topic>")\`
|
|
1088
|
-
- On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
|
|
1089
|
-
|
|
1090
|
-
### Context Pressure & Compaction
|
|
1091
|
-
|
|
1092
|
-
- If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
|
|
1093
|
-
- PreCompact hooks automatically save snapshots before compaction
|
|
1094
|
-
|
|
1095
|
-
### Enhanced Context (Warnings)
|
|
1096
|
-
|
|
1097
|
-
\`context\` returns server-side \`warnings\` for lessons, risky actions, and breaking changes.
|
|
1098
|
-
When warnings are present: **STOP**, acknowledge them, explain mitigation, then proceed.
|
|
1099
|
-
|
|
1100
|
-
### Automatic Context Restoration
|
|
1101
|
-
|
|
1102
|
-
**Context restoration is now enabled by default.** Every \`init\` call automatically:
|
|
1103
|
-
- Restores context from recent snapshots (if available)
|
|
1104
|
-
- Returns \`restored_context\` field with snapshot data
|
|
1105
|
-
- Sets \`is_post_compact=true\` in response when restoration occurs
|
|
1106
|
-
|
|
1107
|
-
**No special handling needed after compaction** - just call \`init\` normally.
|
|
1108
|
-
|
|
1109
|
-
To disable automatic restoration:
|
|
1110
|
-
- Pass \`is_post_compact=false\` in the API call
|
|
1111
|
-
- Or set \`CONTEXTSTREAM_RESTORE_CONTEXT=false\` environment variable
|
|
1112
|
-
|
|
1113
|
-
### Notices - MUST HANDLE IMMEDIATELY
|
|
1114
|
-
|
|
1115
|
-
- **[VERSION_NOTICE]** or **[VERSION_CRITICAL]**: Tell user about the update in a helpful, non-annoying way. Provide update commands:
|
|
1116
|
-
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
|
|
1117
|
-
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
|
|
1118
|
-
- npm: \`npm install -g @contextstream/mcp-server@latest\`
|
|
1119
|
-
- **[RULES_NOTICE]**: Run \`generate_rules(overwrite_existing=true)\` to update
|
|
1120
|
-
- **[LESSONS_WARNING]**: Read lessons, tell user about them, explain how you'll avoid past mistakes
|
|
1121
|
-
- **[PREFERENCE]**: Follow user preferences - these are high-priority user memories that should guide your behavior
|
|
1122
|
-
|
|
1123
|
-
### Plans & Tasks
|
|
1124
|
-
|
|
1125
|
-
When user asks for a plan, use ContextStream (not EnterPlanMode):
|
|
1126
|
-
1. \`session(action="capture_plan", title="...", steps=[...])\`
|
|
1127
|
-
2. \`memory(action="create_task", title="...", plan_id="<id>")\`
|
|
1128
|
-
|
|
1129
|
-
### Workspace-Only Mode (Multi-Project Folders)
|
|
1130
|
-
|
|
1131
|
-
If working in a parent folder containing multiple projects:
|
|
1132
|
-
\`\`\`
|
|
1133
|
-
init(folder_path="...", skip_project_creation=true)
|
|
1134
|
-
\`\`\`
|
|
1135
|
-
|
|
1136
|
-
This enables workspace-level memory and context without project-specific indexing.
|
|
1137
|
-
Use for monorepos or folders with multiple independent projects.
|
|
1138
|
-
|
|
1139
|
-
Full docs: https://contextstream.io/docs/mcp/tools
|
|
1140
|
-
`.trim();
|
|
1141
|
-
var NO_HOOKS_SUPPLEMENT = `
|
|
1142
|
-
|
|
1143
|
-
---
|
|
1144
|
-
## \u26A0\uFE0F IMPORTANT: No Hooks Available \u26A0\uFE0F
|
|
1145
|
-
|
|
1146
|
-
**This editor does NOT have hooks to enforce ContextStream behavior.**
|
|
1147
|
-
You MUST follow these rules manually - there is no automatic enforcement.
|
|
1148
|
-
|
|
1149
|
-
---
|
|
1150
|
-
|
|
1151
|
-
## \u{1F680} SESSION START PROTOCOL
|
|
1152
|
-
|
|
1153
|
-
**On EVERY new session, you MUST:**
|
|
1154
|
-
|
|
1155
|
-
1. **Call \`init(folder_path="<project_path>")\`** FIRST
|
|
1156
|
-
- This triggers project indexing
|
|
1157
|
-
- Check response for \`indexing_status\`
|
|
1158
|
-
- If \`"started"\` or \`"refreshing"\`: wait before searching
|
|
1159
|
-
|
|
1160
|
-
2. **Generate a unique session_id** (e.g., \`"session-" + timestamp\` or a UUID)
|
|
1161
|
-
- Use this SAME session_id for ALL context() calls in this conversation
|
|
1162
|
-
- This groups all turns together in the transcript
|
|
1163
|
-
|
|
1164
|
-
3. **Call \`context(user_message="<first_message>", save_exchange=true, session_id="<your-session-id>")\`**
|
|
1165
|
-
- Gets task-specific rules, lessons, and preferences
|
|
1166
|
-
- Check for [LESSONS_WARNING] - past mistakes to avoid
|
|
1167
|
-
- Check for [PREFERENCE] - user preferences to follow
|
|
1168
|
-
- Check for [RULES_NOTICE] - update rules if needed
|
|
1169
|
-
- **save_exchange=true** saves each conversation turn for later retrieval
|
|
1170
|
-
|
|
1171
|
-
4. **Default behavior:** call \`context(...)\` first on each message. Narrow bypass is allowed only for immediate read-only ContextStream calls when previous context is still fresh and no state-changing tool has run.
|
|
1172
|
-
|
|
1173
|
-
---
|
|
1174
|
-
|
|
1175
|
-
## \u{1F4BE} AUTOMATIC TRANSCRIPT SAVING (CRITICAL)
|
|
1176
|
-
|
|
1177
|
-
**This editor does NOT have hooks to auto-save transcripts.**
|
|
1178
|
-
You MUST save each conversation turn manually:
|
|
1179
|
-
|
|
1180
|
-
### On MOST messages (including the first):
|
|
1181
|
-
\`\`\`
|
|
1182
|
-
context(user_message="<user's message>", save_exchange=true, session_id="<session-id>")
|
|
1183
|
-
\`\`\`
|
|
1184
|
-
|
|
1185
|
-
### Why save_exchange matters:
|
|
1186
|
-
- Transcripts enable searching past conversations
|
|
1187
|
-
- Allows context restoration after compaction
|
|
1188
|
-
- Provides conversation history for debugging
|
|
1189
|
-
- Required for the Transcripts page in the dashboard
|
|
1190
|
-
|
|
1191
|
-
### Session ID Guidelines:
|
|
1192
|
-
- Generate ONCE at the start of the conversation
|
|
1193
|
-
- Use a unique identifier: \`"session-" + Date.now()\` or a UUID
|
|
1194
|
-
- Keep the SAME session_id for ALL context() calls in this session
|
|
1195
|
-
- Different sessions = different transcripts
|
|
1196
|
-
|
|
1197
|
-
---
|
|
1198
|
-
|
|
1199
|
-
## \u{1F4C1} FILE INDEXING (CRITICAL)
|
|
1200
|
-
|
|
1201
|
-
**There is NO automatic file indexing in this editor.**
|
|
1202
|
-
You MUST manage indexing manually:
|
|
1203
|
-
|
|
1204
|
-
### After Creating/Editing Files:
|
|
1205
|
-
\`\`\`
|
|
1206
|
-
project(action="index") # Re-index entire project
|
|
1207
|
-
\`\`\`
|
|
1208
|
-
|
|
1209
|
-
### For Single File Updates:
|
|
1210
|
-
\`\`\`
|
|
1211
|
-
project(action="ingest_local", path="<file_path>")
|
|
1212
|
-
\`\`\`
|
|
1213
|
-
|
|
1214
|
-
### Signs You Need to Re-index:
|
|
1215
|
-
- Search doesn't find code you just wrote
|
|
1216
|
-
- Search returns old versions of functions
|
|
1217
|
-
- New files don't appear in search results
|
|
1218
|
-
|
|
1219
|
-
### Best Practice:
|
|
1220
|
-
After completing a feature or making multiple file changes, ALWAYS run:
|
|
1221
|
-
\`\`\`
|
|
1222
|
-
project(action="index")
|
|
1223
|
-
\`\`\`
|
|
1224
|
-
|
|
1225
|
-
---
|
|
1226
|
-
|
|
1227
|
-
## \u{1F50D} SEARCH-FIRST (No PreToolUse Hook)
|
|
1228
|
-
|
|
1229
|
-
**There is NO hook to block local tools (Glob/Grep/Read/Explore/Task/EnterPlanMode).** You MUST self-enforce:
|
|
1230
|
-
|
|
1231
|
-
### Before ANY Search, Check Index Status:
|
|
1232
|
-
\`\`\`
|
|
1233
|
-
project(action="index_status")
|
|
1234
|
-
\`\`\`
|
|
1235
|
-
|
|
1236
|
-
This tells you:
|
|
1237
|
-
- \`indexed\`: true/false - is project indexed?
|
|
1238
|
-
- \`last_indexed_at\`: timestamp - when was it last indexed?
|
|
1239
|
-
- \`file_count\`: number - how many files indexed?
|
|
1240
|
-
|
|
1241
|
-
### Search Protocol:
|
|
1242
|
-
|
|
1243
|
-
**IF project is indexed and fresh:**
|
|
1244
|
-
\`\`\`
|
|
1245
|
-
search(mode="auto", query="what you're looking for")
|
|
1246
|
-
\`\`\`
|
|
1247
|
-
\u2192 Use this instead of Explore/Task/EnterPlanMode for file discovery.
|
|
1248
|
-
|
|
1249
|
-
**IF project is NOT indexed or very stale (>7 days):**
|
|
1250
|
-
\u2192 Run \`search(mode="auto", ...)\` anyway \u2014 keyword hits return immediately while indexing builds; retry once, and use local tools only if search itself returns 0/errors
|
|
1251
|
-
\u2192 OR run \`project(action="index")\` first, then search
|
|
1252
|
-
|
|
1253
|
-
**IF ContextStream search still returns 0 results or errors after retry/window:**
|
|
1254
|
-
\u2192 Use local tools (Glob/Grep/Read) as fallback
|
|
1255
|
-
|
|
1256
|
-
### Choose Search Mode Intelligently:
|
|
1257
|
-
- \`auto\` (recommended): query-aware mode selection
|
|
1258
|
-
- \`hybrid\`: mixed semantic + keyword retrieval for broad discovery
|
|
1259
|
-
- \`semantic\`: conceptual questions ("how does X work?")
|
|
1260
|
-
- \`keyword\`: exact text / quoted string
|
|
1261
|
-
- \`pattern\`: glob or regex (\`*.ts\`, \`foo\\s+bar\`)
|
|
1262
|
-
- \`refactor\`: symbol usage / rename-safe lookup
|
|
1263
|
-
- \`exhaustive\`: all occurrences / complete match coverage
|
|
1264
|
-
- \`team\`: cross-project team search
|
|
1265
|
-
|
|
1266
|
-
### Output Format Hints:
|
|
1267
|
-
- Use \`output_format="paths"\` for file listings and rename targets
|
|
1268
|
-
- Use \`output_format="count"\` for "how many" queries
|
|
1269
|
-
|
|
1270
|
-
### Two-Phase Search Pattern (for precision):
|
|
1271
|
-
- Pass 1 (discovery): \`search(mode="auto", query="<concept + module>", output_format="paths", limit=10)\`
|
|
1272
|
-
- Pass 2 (precision): use one of:
|
|
1273
|
-
- exact text/symbol: \`search(mode="keyword", query="\\"exact_text\\"", include_content=true)\`
|
|
1274
|
-
- symbol usage: \`search(mode="refactor", query="SymbolName", output_format="paths")\`
|
|
1275
|
-
- all occurrences: \`search(mode="exhaustive", query="symbol_or_text")\`
|
|
1276
|
-
- Then use local Read/Grep only on paths returned by ContextStream.
|
|
1277
|
-
|
|
1278
|
-
### When Local Tools Are OK:
|
|
1279
|
-
\u2705 The edit is known-new (just written this turn) and not yet indexed
|
|
1280
|
-
\u2705 ContextStream search still returns 0 results after retry
|
|
1281
|
-
\u2705 ContextStream returns errors
|
|
1282
|
-
\u2705 User explicitly requests local tools
|
|
1283
|
-
|
|
1284
|
-
### When to Use ContextStream Search:
|
|
1285
|
-
\u2705 Project is indexed and fresh
|
|
1286
|
-
\u2705 Looking for code by meaning/concept
|
|
1287
|
-
\u2705 Need semantic understanding
|
|
1288
|
-
|
|
1289
|
-
---
|
|
1290
|
-
|
|
1291
|
-
## \u{1F4BE} CONTEXT COMPACTION (No PreCompact Hook)
|
|
1292
|
-
|
|
1293
|
-
**There is NO automatic state saving before compaction.**
|
|
1294
|
-
You MUST save state manually when the conversation gets long:
|
|
1295
|
-
|
|
1296
|
-
### When to Save State:
|
|
1297
|
-
- After completing a major task
|
|
1298
|
-
- Before the conversation might be compacted
|
|
1299
|
-
- If \`context()\` returns \`context_pressure.level: "high"\`
|
|
1300
|
-
|
|
1301
|
-
### How to Save State:
|
|
1302
|
-
\`\`\`
|
|
1303
|
-
session(action="capture", event_type="session_snapshot",
|
|
1304
|
-
title="Session checkpoint",
|
|
1305
|
-
content="{ \\"summary\\": \\"what we did\\", \\"active_files\\": [...], \\"next_steps\\": [...] }")
|
|
1306
|
-
\`\`\`
|
|
1307
|
-
|
|
1308
|
-
### After Compaction (if context seems lost):
|
|
1309
|
-
\`\`\`
|
|
1310
|
-
init(folder_path="...", is_post_compact=true)
|
|
1311
|
-
\`\`\`
|
|
1312
|
-
This restores the most recent snapshot.
|
|
1313
|
-
|
|
1314
|
-
---
|
|
1315
|
-
|
|
1316
|
-
## \u{1F4CB} PLANS & TASKS (No EnterPlanMode)
|
|
1317
|
-
|
|
1318
|
-
**Always use ContextStream for planning:**
|
|
1319
|
-
|
|
1320
|
-
\`\`\`
|
|
1321
|
-
session(action="capture_plan", title="...", steps=[...])
|
|
1322
|
-
memory(action="create_task", title="...", plan_id="...")
|
|
1323
|
-
\`\`\`
|
|
1324
|
-
|
|
1325
|
-
\u274C DO NOT use built-in plan mode (\`EnterPlanMode\`) or \`Task(subagent_type="Explore")\` for file-by-file scans.
|
|
1326
|
-
\u2705 For planning discovery, use \`search(mode="auto", query="...", output_format="paths")\` then read only narrowed files.
|
|
1327
|
-
|
|
1328
|
-
---
|
|
1329
|
-
|
|
1330
|
-
## \u{1F504} VERSION UPDATES (Check Periodically)
|
|
1331
|
-
|
|
1332
|
-
**This editor does NOT have hooks to check for updates automatically.**
|
|
1333
|
-
You should check for updates using \`help(action="version")\` periodically (e.g., at session start).
|
|
1334
|
-
|
|
1335
|
-
### If the response includes [VERSION_NOTICE] or [VERSION_CRITICAL]:
|
|
1336
|
-
|
|
1337
|
-
**Tell the user** about the available update in a helpful, non-annoying way:
|
|
1338
|
-
- Frame it as "new features and improvements available"
|
|
1339
|
-
- Provide the update commands (user can choose their preferred method)
|
|
1340
|
-
- Don't nag repeatedly - mention once, then only if user asks
|
|
1341
|
-
|
|
1342
|
-
### Update Commands (provide all options):
|
|
1343
|
-
|
|
1344
|
-
**macOS/Linux:**
|
|
1345
|
-
\`\`\`bash
|
|
1346
|
-
curl -fsSL https://contextstream.io/scripts/setup.sh | bash
|
|
1347
|
-
\`\`\`
|
|
1348
|
-
|
|
1349
|
-
**Windows (PowerShell):**
|
|
1350
|
-
\`\`\`powershell
|
|
1351
|
-
irm https://contextstream.io/scripts/setup.ps1 | iex
|
|
1352
|
-
\`\`\`
|
|
1353
|
-
|
|
1354
|
-
**npm (requires Node.js 20+):**
|
|
1355
|
-
\`\`\`bash
|
|
1356
|
-
npm install -g @contextstream/mcp-server@latest
|
|
1357
|
-
\`\`\`
|
|
1358
|
-
|
|
1359
|
-
After updating, user should restart their AI tool.
|
|
1360
|
-
|
|
1361
|
-
---
|
|
1362
|
-
`;
|
|
1363
|
-
var ANTIGRAVITY_SUPPLEMENT = `
|
|
1364
|
-
---
|
|
1365
|
-
## Antigravity-Specific Reliability Notes
|
|
1366
|
-
|
|
1367
|
-
- Antigravity currently has no documented lifecycle hooks for ContextStream enforcement.
|
|
1368
|
-
- Treat ContextStream-first behavior as mandatory policy: run \`context(...)\` first, then \`search(mode="auto", ...)\` before local discovery.
|
|
1369
|
-
- Keep \`mcp_config.json\` valid and minimal: preserve non-ContextStream servers and only update the \`contextstream\` server block.
|
|
1370
|
-
- If ContextStream appears skipped, verify:
|
|
1371
|
-
1. MCP server status is healthy in Antigravity settings
|
|
1372
|
-
2. \`search(mode="auto", ...)\` ran FIRST (even while indexing) and was retried before any local fallback
|
|
1373
|
-
3. Instructions file contains the current ContextStream managed block
|
|
1374
|
-
`;
|
|
1375
|
-
var NO_HOOKS_EDITORS = ["copilot", "codex", "opencode", "aider", "antigravity", "kilo"];
|
|
1376
|
-
var TEMPLATES = {
|
|
1377
|
-
codex: {
|
|
1378
|
-
filename: "AGENTS.md",
|
|
1379
|
-
description: "Codex CLI agent instructions",
|
|
1380
|
-
build: (rules) => `# Codex CLI Instructions
|
|
1381
|
-
${rules}
|
|
1382
|
-
`
|
|
1383
|
-
},
|
|
1384
|
-
opencode: {
|
|
1385
|
-
filename: "AGENTS.md",
|
|
1386
|
-
description: "OpenCode CLI agent instructions",
|
|
1387
|
-
build: (rules) => `# OpenCode CLI Instructions
|
|
1388
|
-
${rules}
|
|
1389
|
-
`
|
|
1390
|
-
},
|
|
1391
|
-
cursor: {
|
|
1392
|
-
// Cursor Agent mode does not read .cursorrules at all; only
|
|
1393
|
-
// .cursor/rules/*.mdc loads in every mode (Chat/Composer/Agent).
|
|
1394
|
-
// Existing .cursorrules blocks are left in place for older Cursor
|
|
1395
|
-
// versions but are no longer created.
|
|
1396
|
-
filename: ".cursor/rules/contextstream.mdc",
|
|
1397
|
-
description: "Cursor AI rules (.cursor/rules MDC \u2014 loaded in all Cursor modes)",
|
|
1398
|
-
build: (rules) => `---
|
|
1399
|
-
alwaysApply: true
|
|
1400
|
-
---
|
|
1401
|
-
|
|
1402
|
-
# Cursor Rules
|
|
1403
|
-
${rules}
|
|
1404
|
-
`
|
|
1405
|
-
},
|
|
1406
|
-
windsurf: {
|
|
1407
|
-
filename: ".windsurf/rules/contextstream.md",
|
|
1408
|
-
description: "Windsurf AI rules",
|
|
1409
|
-
build: (rules) => `---
|
|
1410
|
-
trigger: always_on
|
|
1411
|
-
---
|
|
1412
|
-
|
|
1413
|
-
# Windsurf Rules
|
|
1414
|
-
${rules}
|
|
1415
|
-
`
|
|
1416
|
-
},
|
|
1417
|
-
cline: {
|
|
1418
|
-
filename: ".clinerules",
|
|
1419
|
-
description: "Cline AI rules",
|
|
1420
|
-
build: (rules) => `# Cline Rules
|
|
1421
|
-
${rules}
|
|
1422
|
-
`
|
|
1423
|
-
},
|
|
1424
|
-
kilo: {
|
|
1425
|
-
filename: ".kilo/rules/contextstream.md",
|
|
1426
|
-
description: "Kilo Code AI rules",
|
|
1427
|
-
build: (rules) => `# Kilo Code Rules
|
|
1428
|
-
${rules}
|
|
1429
|
-
`
|
|
1430
|
-
},
|
|
1431
|
-
roo: {
|
|
1432
|
-
filename: ".roo/rules/contextstream.md",
|
|
1433
|
-
description: "Roo Code AI rules",
|
|
1434
|
-
build: (rules) => `# Roo Code Rules
|
|
1435
|
-
${rules}
|
|
1436
|
-
`
|
|
1437
|
-
},
|
|
1438
|
-
claude: {
|
|
1439
|
-
filename: "CLAUDE.md",
|
|
1440
|
-
description: "Claude Code instructions",
|
|
1441
|
-
build: (rules) => `# Claude Code Instructions
|
|
1442
|
-
${rules}
|
|
1443
|
-
`
|
|
1444
|
-
},
|
|
1445
|
-
aider: {
|
|
1446
|
-
filename: ".aider.conf.yml",
|
|
1447
|
-
description: "Aider configuration with system prompt",
|
|
1448
|
-
build: (rules) => `# Aider Configuration
|
|
1449
|
-
# Note: Aider uses different config format - this adds to the system prompt
|
|
1450
|
-
|
|
1451
|
-
# Add ContextStream guidance to conventions
|
|
1452
|
-
conventions: |
|
|
1453
|
-
${rules.split("\n").map((line) => " " + line).join("\n")}
|
|
1454
|
-
`
|
|
1455
|
-
},
|
|
1456
|
-
antigravity: {
|
|
1457
|
-
filename: "GEMINI.md",
|
|
1458
|
-
description: "Google Antigravity AI rules",
|
|
1459
|
-
build: (rules) => `# Antigravity Agent Rules
|
|
1460
|
-
${rules}
|
|
1461
|
-
`
|
|
1462
|
-
},
|
|
1463
|
-
copilot: {
|
|
1464
|
-
filename: ".github/copilot-instructions.md",
|
|
1465
|
-
description: "VS Code GitHub Copilot instructions",
|
|
1466
|
-
build: (rules) => `## ContextStream MCP Integration
|
|
1467
|
-
|
|
1468
|
-
This project uses [ContextStream](https://contextstream.io) for persistent AI memory across sessions. Use the \`contextstream-workflow\` skill for detailed examples and reference material.
|
|
1469
|
-
|
|
1470
|
-
${rules}
|
|
1471
|
-
|
|
1472
|
-
### VS Code Copilot Notes
|
|
1473
|
-
|
|
1474
|
-
- Keep this file concise; put detailed workflows in \`.github/skills/contextstream-workflow/SKILL.md\`
|
|
1475
|
-
- Use ContextStream plans/tasks as the persistent record of work
|
|
1476
|
-
- Before code discovery, use \`search(mode="auto", query="...")\`
|
|
1477
|
-
|
|
1478
|
-
Full docs: https://contextstream.io/docs/mcp/tools
|
|
1479
|
-
`
|
|
1480
|
-
}
|
|
1481
|
-
};
|
|
1482
|
-
function getAvailableEditors() {
|
|
1483
|
-
return Object.keys(TEMPLATES);
|
|
1484
|
-
}
|
|
1485
|
-
function getTemplate(editor) {
|
|
1486
|
-
return TEMPLATES[editor.toLowerCase()] || null;
|
|
1487
|
-
}
|
|
1488
|
-
function generateRuleContent(editor, options) {
|
|
1489
|
-
const template = getTemplate(editor);
|
|
1490
|
-
if (!template) return null;
|
|
1491
|
-
const mode = options?.mode || "bootstrap";
|
|
1492
|
-
const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : mode === "bootstrap" ? CONTEXTSTREAM_RULES_BOOTSTRAP : CONTEXTSTREAM_RULES_DYNAMIC;
|
|
1493
|
-
let content = template.build(rules);
|
|
1494
|
-
if (editor.toLowerCase() !== "copilot" && (options?.workspaceName || options?.projectName)) {
|
|
1495
|
-
const header = `
|
|
1496
|
-
# Workspace: ${options.workspaceName || "Unknown"}
|
|
1497
|
-
${options.projectName ? `# Project: ${options.projectName}` : ""}
|
|
1498
|
-
${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
|
|
1499
|
-
|
|
1500
|
-
`;
|
|
1501
|
-
content = header + content;
|
|
1502
|
-
}
|
|
1503
|
-
if (NO_HOOKS_EDITORS.includes(editor.toLowerCase())) {
|
|
1504
|
-
content += NO_HOOKS_SUPPLEMENT;
|
|
1505
|
-
}
|
|
1506
|
-
if (editor.toLowerCase() === "antigravity") {
|
|
1507
|
-
content += ANTIGRAVITY_SUPPLEMENT;
|
|
1508
|
-
}
|
|
1509
|
-
if (options?.additionalRules) {
|
|
1510
|
-
content += "\n\n## Project-Specific Rules\n\n" + options.additionalRules;
|
|
1511
|
-
}
|
|
1512
|
-
if (editor.toLowerCase() === "claude") {
|
|
1513
|
-
content = applyMcpToolPrefix(content, `mcp__${DEFAULT_CLAUDE_MCP_SERVER_NAME}__`);
|
|
1514
|
-
}
|
|
1515
|
-
return {
|
|
1516
|
-
filename: template.filename,
|
|
1517
|
-
content: content.trim() + "\n"
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
// src/hooks/prompt-state.ts
|
|
1522
|
-
import * as fs from "node:fs";
|
|
1523
|
-
import * as path from "node:path";
|
|
1524
|
-
import { homedir as homedir2 } from "node:os";
|
|
1525
|
-
var STATE_PATH = path.join(homedir2(), ".contextstream", "prompt-state.json");
|
|
1526
|
-
function defaultState() {
|
|
1527
|
-
return { workspaces: {} };
|
|
1528
|
-
}
|
|
1529
|
-
function nowIso() {
|
|
1530
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1531
|
-
}
|
|
1532
|
-
function ensureStateDir() {
|
|
1533
|
-
try {
|
|
1534
|
-
fs.mkdirSync(path.dirname(STATE_PATH), { recursive: true });
|
|
1535
|
-
} catch {
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
function normalizePath(input) {
|
|
1539
|
-
try {
|
|
1540
|
-
return path.resolve(input);
|
|
1541
|
-
} catch {
|
|
1542
|
-
return input;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
function workspacePathsMatch(a, b) {
|
|
1546
|
-
const left = normalizePath(a);
|
|
1547
|
-
const right = normalizePath(b);
|
|
1548
|
-
return left === right || left.startsWith(`${right}${path.sep}`) || right.startsWith(`${left}${path.sep}`);
|
|
1549
|
-
}
|
|
1550
|
-
function readState() {
|
|
1551
|
-
try {
|
|
1552
|
-
const content = fs.readFileSync(STATE_PATH, "utf8");
|
|
1553
|
-
const parsed = JSON.parse(content);
|
|
1554
|
-
if (!parsed || typeof parsed !== "object" || !parsed.workspaces) {
|
|
1555
|
-
return defaultState();
|
|
1556
|
-
}
|
|
1557
|
-
return parsed;
|
|
1558
|
-
} catch {
|
|
1559
|
-
return defaultState();
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
function writeState(state) {
|
|
1563
|
-
try {
|
|
1564
|
-
ensureStateDir();
|
|
1565
|
-
fs.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
|
|
1566
|
-
} catch {
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
function getOrCreateEntry(state, cwd) {
|
|
1570
|
-
if (!cwd.trim()) return null;
|
|
1571
|
-
const exact = state.workspaces[cwd];
|
|
1572
|
-
if (exact) return { key: cwd, entry: exact };
|
|
1573
|
-
for (const [trackedCwd, trackedEntry] of Object.entries(state.workspaces)) {
|
|
1574
|
-
if (workspacePathsMatch(trackedCwd, cwd)) {
|
|
1575
|
-
return { key: trackedCwd, entry: trackedEntry };
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
const created = {
|
|
1579
|
-
require_context: false,
|
|
1580
|
-
require_init: false,
|
|
1581
|
-
last_context_at: void 0,
|
|
1582
|
-
last_state_change_at: void 0,
|
|
1583
|
-
index_wait_started_at: void 0,
|
|
1584
|
-
index_wait_until: void 0,
|
|
1585
|
-
updated_at: nowIso()
|
|
1586
|
-
};
|
|
1587
|
-
state.workspaces[cwd] = created;
|
|
1588
|
-
return { key: cwd, entry: created };
|
|
1589
|
-
}
|
|
1590
|
-
function cleanupStale(maxAgeSeconds) {
|
|
1591
|
-
const state = readState();
|
|
1592
|
-
const now = Date.now();
|
|
1593
|
-
let changed = false;
|
|
1594
|
-
for (const [cwd, entry] of Object.entries(state.workspaces)) {
|
|
1595
|
-
const updated = new Date(entry.updated_at);
|
|
1596
|
-
if (Number.isNaN(updated.getTime())) continue;
|
|
1597
|
-
const ageSeconds = (now - updated.getTime()) / 1e3;
|
|
1598
|
-
if (ageSeconds > maxAgeSeconds) {
|
|
1599
|
-
delete state.workspaces[cwd];
|
|
1600
|
-
changed = true;
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
if (changed) {
|
|
1604
|
-
writeState(state);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
function markInitRequired(cwd) {
|
|
1608
|
-
if (!cwd.trim()) return;
|
|
1609
|
-
const state = readState();
|
|
1610
|
-
const target = getOrCreateEntry(state, cwd);
|
|
1611
|
-
if (!target) return;
|
|
1612
|
-
target.entry.require_init = true;
|
|
1613
|
-
target.entry.updated_at = nowIso();
|
|
1614
|
-
writeState(state);
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
// src/hooks/session-init.ts
|
|
1618
|
-
var ENABLED = process.env.CONTEXTSTREAM_SESSION_INIT_ENABLED !== "false";
|
|
1619
|
-
var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
1620
|
-
var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
1621
|
-
var WORKSPACE_ID = null;
|
|
1622
|
-
var PROJECT_ID = null;
|
|
1623
|
-
function loadConfigFromMcpJson(cwd) {
|
|
1624
|
-
let searchDir = path2.resolve(cwd);
|
|
1625
|
-
for (let i = 0; i < 5; i++) {
|
|
1626
|
-
if (!API_KEY) {
|
|
1627
|
-
const mcpPath = path2.join(searchDir, ".mcp.json");
|
|
1628
|
-
if (fs2.existsSync(mcpPath)) {
|
|
1629
|
-
try {
|
|
1630
|
-
const content = fs2.readFileSync(mcpPath, "utf-8");
|
|
1631
|
-
const config = JSON.parse(content);
|
|
1632
|
-
const csEnv = config.mcpServers?.contextstream?.env;
|
|
1633
|
-
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
1634
|
-
API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
|
|
1635
|
-
}
|
|
1636
|
-
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
1637
|
-
API_URL = csEnv.CONTEXTSTREAM_API_URL;
|
|
1638
|
-
}
|
|
1639
|
-
if (csEnv?.CONTEXTSTREAM_WORKSPACE_ID) {
|
|
1640
|
-
WORKSPACE_ID = csEnv.CONTEXTSTREAM_WORKSPACE_ID;
|
|
1641
|
-
}
|
|
1642
|
-
} catch {
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
if (!WORKSPACE_ID || !PROJECT_ID) {
|
|
1647
|
-
const csConfigPath = path2.join(searchDir, ".contextstream", "config.json");
|
|
1648
|
-
if (fs2.existsSync(csConfigPath)) {
|
|
1649
|
-
try {
|
|
1650
|
-
const content = fs2.readFileSync(csConfigPath, "utf-8");
|
|
1651
|
-
const csConfig = JSON.parse(content);
|
|
1652
|
-
if (csConfig.workspace_id && !WORKSPACE_ID) {
|
|
1653
|
-
WORKSPACE_ID = csConfig.workspace_id;
|
|
1654
|
-
}
|
|
1655
|
-
if (csConfig.project_id && !PROJECT_ID) {
|
|
1656
|
-
PROJECT_ID = csConfig.project_id;
|
|
1657
|
-
}
|
|
1658
|
-
} catch {
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
const parentDir = path2.dirname(searchDir);
|
|
1663
|
-
if (parentDir === searchDir) break;
|
|
1664
|
-
searchDir = parentDir;
|
|
1665
|
-
}
|
|
1666
|
-
if (!API_KEY) {
|
|
1667
|
-
const homeMcpPath = path2.join(homedir3(), ".mcp.json");
|
|
1668
|
-
if (fs2.existsSync(homeMcpPath)) {
|
|
1669
|
-
try {
|
|
1670
|
-
const content = fs2.readFileSync(homeMcpPath, "utf-8");
|
|
1671
|
-
const config = JSON.parse(content);
|
|
1672
|
-
const csEnv = config.mcpServers?.contextstream?.env;
|
|
1673
|
-
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
1674
|
-
API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
|
|
1675
|
-
}
|
|
1676
|
-
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
1677
|
-
API_URL = csEnv.CONTEXTSTREAM_API_URL;
|
|
1678
|
-
}
|
|
1679
|
-
} catch {
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
async function fetchSessionContext() {
|
|
1685
|
-
if (!API_KEY) return null;
|
|
1686
|
-
try {
|
|
1687
|
-
const controller = new AbortController();
|
|
1688
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1689
|
-
const url = new URL(`${API_URL}/api/v1/context`);
|
|
1690
|
-
if (WORKSPACE_ID) url.searchParams.set("workspace_id", WORKSPACE_ID);
|
|
1691
|
-
if (PROJECT_ID) url.searchParams.set("project_id", PROJECT_ID);
|
|
1692
|
-
url.searchParams.set("include_rules", "true");
|
|
1693
|
-
url.searchParams.set("include_lessons", "true");
|
|
1694
|
-
url.searchParams.set("include_decisions", "true");
|
|
1695
|
-
url.searchParams.set("include_plans", "true");
|
|
1696
|
-
url.searchParams.set("limit", "5");
|
|
1697
|
-
const response = await fetch(url.toString(), {
|
|
1698
|
-
method: "GET",
|
|
1699
|
-
headers: {
|
|
1700
|
-
"X-API-Key": API_KEY
|
|
1701
|
-
},
|
|
1702
|
-
signal: controller.signal
|
|
1703
|
-
});
|
|
1704
|
-
clearTimeout(timeoutId);
|
|
1705
|
-
if (response.ok) {
|
|
1706
|
-
return await response.json();
|
|
1707
|
-
}
|
|
1708
|
-
return null;
|
|
1709
|
-
} catch {
|
|
1710
|
-
return null;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
function formatContext(ctx, options = {}) {
|
|
1714
|
-
const parts = ["\u2B21 ContextStream \u2014 Smart Context & Memory", ""];
|
|
1715
|
-
if (options.updateMarker) {
|
|
1716
|
-
parts.push(`
|
|
1717
|
-
## \u{1F389} UPDATE COMPLETE - RESTART CHAT SESSION`);
|
|
1718
|
-
parts.push(`ContextStream was auto-updated from v${options.updateMarker.previousVersion} to v${options.updateMarker.newVersion}.`);
|
|
1719
|
-
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."`);
|
|
1720
|
-
parts.push("");
|
|
1721
|
-
} else if (options.autoUpdateResult?.attempted) {
|
|
1722
|
-
if (options.autoUpdateResult.success) {
|
|
1723
|
-
parts.push(`
|
|
1724
|
-
## \u{1F504} AUTO-UPDATE INITIATED`);
|
|
1725
|
-
parts.push(`ContextStream is updating from v${options.autoUpdateResult.previousVersion} to v${options.autoUpdateResult.newVersion}.`);
|
|
1726
|
-
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."`);
|
|
1727
|
-
parts.push("");
|
|
1728
|
-
} else if (options.autoUpdateResult.error) {
|
|
1729
|
-
parts.push(`
|
|
1730
|
-
## \u26A0\uFE0F AUTO-UPDATE FAILED`);
|
|
1731
|
-
parts.push(`Automatic update failed: ${options.autoUpdateResult.error}`);
|
|
1732
|
-
const versionInfo = getVersionNoticeForHook(options.versionNotice || null);
|
|
1733
|
-
if (versionInfo) {
|
|
1734
|
-
parts.push(versionInfo);
|
|
1735
|
-
}
|
|
1736
|
-
parts.push("");
|
|
1737
|
-
}
|
|
1738
|
-
} else if (options.versionNotice?.behind && !isAutoUpdateEnabled()) {
|
|
1739
|
-
const versionInfo = getVersionNoticeForHook(options.versionNotice);
|
|
1740
|
-
if (versionInfo) {
|
|
1741
|
-
parts.push(`
|
|
1742
|
-
## \u{1F504} UPDATE AVAILABLE (auto-update disabled)`);
|
|
1743
|
-
parts.push(versionInfo);
|
|
1744
|
-
parts.push("");
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
if (!ctx) {
|
|
1748
|
-
parts.push(
|
|
1749
|
-
'\nNo saved context found yet. On the first message in this session call `mcp__contextstream__init(...)` then `mcp__contextstream__context(user_message="starting new session")`.'
|
|
1750
|
-
);
|
|
1751
|
-
return parts.join("\n");
|
|
1752
|
-
}
|
|
1753
|
-
if (ctx.lessons && ctx.lessons.length > 0) {
|
|
1754
|
-
parts.push("\n## \u26A0\uFE0F Lessons from Past Mistakes");
|
|
1755
|
-
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
1756
|
-
parts.push(`- **${lesson.title}**: ${lesson.prevention}`);
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
if (ctx.active_plans && ctx.active_plans.length > 0) {
|
|
1760
|
-
parts.push("\n## \u{1F4CB} Active Plans");
|
|
1761
|
-
for (const plan of ctx.active_plans.slice(0, 3)) {
|
|
1762
|
-
parts.push(`- ${plan.title} (${plan.status})`);
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
if (ctx.pending_tasks && ctx.pending_tasks.length > 0) {
|
|
1766
|
-
parts.push("\n## \u2705 Pending Tasks");
|
|
1767
|
-
for (const task of ctx.pending_tasks.slice(0, 5)) {
|
|
1768
|
-
parts.push(`- ${task.title}`);
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
if (ctx.recent_decisions && ctx.recent_decisions.length > 0) {
|
|
1772
|
-
parts.push("\n## \u{1F4DD} Recent Decisions");
|
|
1773
|
-
for (const decision of ctx.recent_decisions.slice(0, 3)) {
|
|
1774
|
-
parts.push(`- **${decision.title}**`);
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
parts.push("\n---");
|
|
1778
|
-
parts.push(
|
|
1779
|
-
'On the first message in a new session call `mcp__contextstream__init(...)` then `mcp__contextstream__context(user_message="...")`. After that, call `mcp__contextstream__context(user_message="...")` on every message.'
|
|
1780
|
-
);
|
|
1781
|
-
parts.push("\n## \u{1F4DC} Past Sessions Are Queryable \u2014 check before re-deriving context");
|
|
1782
|
-
parts.push(
|
|
1783
|
-
`Transcripts of every prior session are captured + indexed. If the user references past work ("last time", "previous", "yesterday", "pick up where we left off"), or you're continuing something, call ONE of these BEFORE asking clarifying questions:`
|
|
1784
|
-
);
|
|
1785
|
-
parts.push(
|
|
1786
|
-
'- `mcp__contextstream__session(action="recall", query="<what you\'re continuing>")` \u2014 ranked fusion of transcripts/snapshots/docs/decisions'
|
|
1787
|
-
);
|
|
1788
|
-
parts.push(
|
|
1789
|
-
'- `mcp__contextstream__memory(action="list_transcripts", limit=10)` \u2014 chronological list of recent sessions'
|
|
1790
|
-
);
|
|
1791
|
-
parts.push(
|
|
1792
|
-
'- `mcp__contextstream__memory(action="search_transcripts", query="<keyword>")` \u2014 full-text search across ALL past transcripts'
|
|
1793
|
-
);
|
|
1794
|
-
parts.push(
|
|
1795
|
-
'- `mcp__contextstream__memory(action="list_events", event_type="session_snapshot")` \u2014 manual bookmarks at turning points'
|
|
1796
|
-
);
|
|
1797
|
-
parts.push(
|
|
1798
|
-
'At the end of this session, save a snapshot so the next session can pick up: `mcp__contextstream__session(action="capture", event_type="session_snapshot", title="...", content="<what we did + next step>")`.'
|
|
1799
|
-
);
|
|
1800
|
-
return parts.join("\n");
|
|
1801
|
-
}
|
|
1802
|
-
var CONTEXTSTREAM_START_MARKER = "<!-- BEGIN ContextStream -->";
|
|
1803
|
-
var CONTEXTSTREAM_END_MARKER = "<!-- END ContextStream -->";
|
|
1804
|
-
function regenerateRuleFiles(folderPath) {
|
|
1805
|
-
let updated = 0;
|
|
1806
|
-
const editors = getAvailableEditors();
|
|
1807
|
-
for (const editor of editors) {
|
|
1808
|
-
const rule = generateRuleContent(editor, { mode: "bootstrap" });
|
|
1809
|
-
if (!rule) continue;
|
|
1810
|
-
const filePath = path2.join(folderPath, rule.filename);
|
|
1811
|
-
if (!fs2.existsSync(filePath)) continue;
|
|
1812
|
-
try {
|
|
1813
|
-
const existing = fs2.readFileSync(filePath, "utf8");
|
|
1814
|
-
const startIdx = existing.indexOf(CONTEXTSTREAM_START_MARKER);
|
|
1815
|
-
const endIdx = existing.indexOf(CONTEXTSTREAM_END_MARKER);
|
|
1816
|
-
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) continue;
|
|
1817
|
-
const before = existing.substring(0, startIdx).trimEnd();
|
|
1818
|
-
const after = existing.substring(endIdx + CONTEXTSTREAM_END_MARKER.length).trimStart();
|
|
1819
|
-
const newBlock = `${CONTEXTSTREAM_START_MARKER}
|
|
1820
|
-
${rule.content.trim()}
|
|
1821
|
-
${CONTEXTSTREAM_END_MARKER}`;
|
|
1822
|
-
const merged = [before, newBlock, after].filter((p) => p.length > 0).join("\n\n");
|
|
1823
|
-
fs2.writeFileSync(filePath, merged.trim() + "\n", "utf8");
|
|
1824
|
-
updated++;
|
|
1825
|
-
} catch {
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
return updated;
|
|
1829
|
-
}
|
|
1830
|
-
async function runSessionInitHook() {
|
|
1831
|
-
if (!ENABLED) {
|
|
1832
|
-
process.exit(0);
|
|
1833
|
-
}
|
|
1834
|
-
let inputData = "";
|
|
1835
|
-
for await (const chunk of process.stdin) {
|
|
1836
|
-
inputData += chunk;
|
|
1837
|
-
}
|
|
1838
|
-
if (!inputData.trim()) {
|
|
1839
|
-
process.exit(0);
|
|
1840
|
-
}
|
|
1841
|
-
let input;
|
|
1842
|
-
try {
|
|
1843
|
-
input = JSON.parse(inputData);
|
|
1844
|
-
} catch {
|
|
1845
|
-
process.exit(0);
|
|
1846
|
-
}
|
|
1847
|
-
const cwd = input.cwd || process.cwd();
|
|
1848
|
-
cleanupStale(360);
|
|
1849
|
-
markInitRequired(cwd);
|
|
1850
|
-
loadConfigFromMcpJson(cwd);
|
|
1851
|
-
const updateMarker = checkUpdateMarker();
|
|
1852
|
-
if (updateMarker) {
|
|
1853
|
-
regenerateRuleFiles(cwd);
|
|
1854
|
-
clearUpdateMarker();
|
|
1855
|
-
}
|
|
1856
|
-
const [context, autoUpdateResult, versionNotice] = await Promise.all([
|
|
1857
|
-
fetchSessionContext(),
|
|
1858
|
-
updateMarker ? Promise.resolve(null) : attemptAutoUpdate(),
|
|
1859
|
-
// Skip if already updated
|
|
1860
|
-
getUpdateNotice()
|
|
1861
|
-
]);
|
|
1862
|
-
const formattedContext = formatContext(context, {
|
|
1863
|
-
autoUpdateResult,
|
|
1864
|
-
versionNotice,
|
|
1865
|
-
updateMarker
|
|
1866
|
-
});
|
|
1867
|
-
console.log(
|
|
1868
|
-
JSON.stringify({
|
|
1869
|
-
additionalContext: formattedContext
|
|
1870
|
-
})
|
|
1871
|
-
);
|
|
1872
|
-
process.exit(0);
|
|
1873
|
-
}
|
|
1874
|
-
var isDirectRun = process.argv[1]?.includes("session-init") || process.argv[2] === "session-init";
|
|
1875
|
-
if (isDirectRun) {
|
|
1876
|
-
runSessionInitHook().catch(() => process.exit(0));
|
|
1877
|
-
}
|
|
1878
|
-
export {
|
|
1879
|
-
runSessionInitHook
|
|
1880
|
-
};
|