@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.
@@ -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
- };