@contextstream/mcp-server 0.4.74 → 0.4.81

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.
@@ -151,44 +151,6 @@ function isContextFreshAndClean(cwd, maxAgeSeconds) {
151
151
  }
152
152
  return true;
153
153
  }
154
- function startIndexWaitWindow(cwd, waitSeconds) {
155
- if (!cwd.trim() || waitSeconds <= 0) return;
156
- const state = readState();
157
- const target = getOrCreateEntry(state, cwd);
158
- if (!target) return;
159
- const now = Date.now();
160
- const existingUntil = target.entry.index_wait_until ? new Date(target.entry.index_wait_until).getTime() : NaN;
161
- if (!Number.isNaN(existingUntil) && existingUntil > now) {
162
- target.entry.updated_at = nowIso();
163
- writeState(state);
164
- return;
165
- }
166
- target.entry.index_wait_started_at = new Date(now).toISOString();
167
- target.entry.index_wait_until = new Date(now + waitSeconds * 1e3).toISOString();
168
- target.entry.updated_at = nowIso();
169
- writeState(state);
170
- }
171
- function clearIndexWaitWindow(cwd) {
172
- if (!cwd.trim()) return;
173
- const state = readState();
174
- const target = getOrCreateEntry(state, cwd);
175
- if (!target) return;
176
- target.entry.index_wait_started_at = void 0;
177
- target.entry.index_wait_until = void 0;
178
- target.entry.updated_at = nowIso();
179
- writeState(state);
180
- }
181
- function indexWaitRemainingSeconds(cwd) {
182
- if (!cwd.trim()) return null;
183
- const state = readState();
184
- const target = getOrCreateEntry(state, cwd);
185
- if (!target?.entry.index_wait_until) return null;
186
- const until = new Date(target.entry.index_wait_until).getTime();
187
- if (Number.isNaN(until)) return null;
188
- const remainingMs = until - Date.now();
189
- if (remainingMs <= 0) return null;
190
- return Math.ceil(remainingMs / 1e3);
191
- }
192
154
 
193
155
  // src/hooks/pre-tool-use.ts
194
156
  var ENABLED = process.env.CONTEXTSTREAM_HOOK_ENABLED !== "false";
@@ -196,9 +158,6 @@ var INDEX_STATUS_FILE = path2.join(homedir2(), ".contextstream", "indexed-projec
196
158
  var DEBUG_FILE = "/tmp/pretooluse-hook-debug.log";
197
159
  var STALE_THRESHOLD_DAYS = 7;
198
160
  var CONTEXT_FRESHNESS_SECONDS = 120;
199
- var DEFAULT_INDEX_WAIT_SECONDS = 20;
200
- var MIN_INDEX_WAIT_SECONDS = 15;
201
- var MAX_INDEX_WAIT_SECONDS = 20;
202
161
  var DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"];
203
162
  function isDiscoveryGlob(pattern) {
204
163
  const patternLower = pattern.toLowerCase();
@@ -224,33 +183,6 @@ function isDiscoveryGrep(filePath) {
224
183
  }
225
184
  return false;
226
185
  }
227
- function configuredIndexWaitSeconds() {
228
- const parsed = Number.parseInt(process.env.CONTEXTSTREAM_INDEX_WAIT_SECONDS ?? "", 10);
229
- if (Number.isNaN(parsed)) return DEFAULT_INDEX_WAIT_SECONDS;
230
- return Math.min(MAX_INDEX_WAIT_SECONDS, Math.max(MIN_INDEX_WAIT_SECONDS, parsed));
231
- }
232
- function isLocalDiscoveryToolDuringIndexWait(tool, toolInput) {
233
- if (tool === "Glob" || tool === "Explore" || tool === "SemanticSearch" || tool === "codebase_search") {
234
- return true;
235
- }
236
- if (tool === "Task") {
237
- const subagentTypeRaw = toolInput?.subagent_type || toolInput?.subagentType || "";
238
- return subagentTypeRaw.toLowerCase().includes("explore");
239
- }
240
- if (tool === "Grep" || tool === "Search" || tool === "grep_search" || tool === "code_search") {
241
- const filePath = toolInput?.path || "";
242
- return isDiscoveryGrep(filePath);
243
- }
244
- if (tool === "Read" || tool === "ReadFile" || tool === "read_file") {
245
- const filePath = toolInput?.file_path || toolInput?.path || toolInput?.file || toolInput?.target_file || "";
246
- return isDiscoveryGrep(filePath);
247
- }
248
- if (tool === "list_files" || tool === "search_files" || tool === "search_files_content" || tool === "find_files" || tool === "find_by_name") {
249
- const pattern = toolInput?.path || toolInput?.regex || toolInput?.pattern || toolInput?.query || "";
250
- return !pattern || isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern);
251
- }
252
- return false;
253
- }
254
186
  function isProjectIndexed(cwd) {
255
187
  if (!fs2.existsSync(INDEX_STATUS_FILE)) {
256
188
  return { isIndexed: false, isStale: false };
@@ -420,11 +352,13 @@ function outputClineAllow() {
420
352
  process.exit(0);
421
353
  }
422
354
  function outputCursorBlock(reason) {
423
- console.log(JSON.stringify({ decision: "deny", reason }));
355
+ console.log(
356
+ JSON.stringify({ permission: "deny", agent_message: reason, user_message: reason })
357
+ );
424
358
  process.exit(0);
425
359
  }
426
360
  function outputCursorAllow() {
427
- console.log(JSON.stringify({ decision: "allow" }));
361
+ console.log(JSON.stringify({ permission: "allow" }));
428
362
  process.exit(0);
429
363
  }
430
364
  function blockWithMessage(editorFormat, message) {
@@ -447,6 +381,9 @@ function allowTool(editorFormat, cwd, recordStateChange) {
447
381
  process.exit(0);
448
382
  }
449
383
  function detectEditorFormat(input) {
384
+ if (process.argv.includes("--editor=cursor")) {
385
+ return "cursor";
386
+ }
450
387
  if (input.hookName !== void 0 || input.toolName !== void 0) {
451
388
  return "cline";
452
389
  }
@@ -515,19 +452,7 @@ async function runPreToolUseHook() {
515
452
  const { isIndexed, isStale } = isProjectIndexed(cwd);
516
453
  fs2.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}, isStale=${isStale}
517
454
  `);
518
- if (isIndexed && !isStale) {
519
- clearIndexWaitWindow(cwd);
520
- } else {
521
- const waitSeconds = configuredIndexWaitSeconds();
522
- if (isLocalDiscoveryToolDuringIndexWait(tool, toolInput)) {
523
- startIndexWaitWindow(cwd, waitSeconds);
524
- const remaining = indexWaitRemainingSeconds(cwd);
525
- if (remaining && remaining > 0) {
526
- const msg = `Index refresh grace window is active (${remaining}s remaining). Keep ContextStream search-first flow: mcp__contextstream__search(mode="auto", query="..."). Do not use local discovery tools yet for stale/not-indexed projects. Retry after refresh; local fallback is allowed only after ~${waitSeconds}s if index is still unavailable.`;
527
- blockWithMessage(editorFormat, msg);
528
- }
529
- allowTool(editorFormat, cwd, recordStateChange);
530
- }
455
+ if (!isIndexed || isStale) {
531
456
  allowTool(editorFormat, cwd, recordStateChange);
532
457
  }
533
458
  if (tool === "Glob") {
@@ -178,44 +178,6 @@ function isContextFreshAndClean(cwd, maxAgeSeconds) {
178
178
  }
179
179
  return true;
180
180
  }
181
- function startIndexWaitWindow(cwd, waitSeconds) {
182
- if (!cwd.trim() || waitSeconds <= 0) return;
183
- const state = readState();
184
- const target = getOrCreateEntry(state, cwd);
185
- if (!target) return;
186
- const now = Date.now();
187
- const existingUntil = target.entry.index_wait_until ? new Date(target.entry.index_wait_until).getTime() : NaN;
188
- if (!Number.isNaN(existingUntil) && existingUntil > now) {
189
- target.entry.updated_at = nowIso();
190
- writeState(state);
191
- return;
192
- }
193
- target.entry.index_wait_started_at = new Date(now).toISOString();
194
- target.entry.index_wait_until = new Date(now + waitSeconds * 1e3).toISOString();
195
- target.entry.updated_at = nowIso();
196
- writeState(state);
197
- }
198
- function clearIndexWaitWindow(cwd) {
199
- if (!cwd.trim()) return;
200
- const state = readState();
201
- const target = getOrCreateEntry(state, cwd);
202
- if (!target) return;
203
- target.entry.index_wait_started_at = void 0;
204
- target.entry.index_wait_until = void 0;
205
- target.entry.updated_at = nowIso();
206
- writeState(state);
207
- }
208
- function indexWaitRemainingSeconds(cwd) {
209
- if (!cwd.trim()) return null;
210
- const state = readState();
211
- const target = getOrCreateEntry(state, cwd);
212
- if (!target?.entry.index_wait_until) return null;
213
- const until = new Date(target.entry.index_wait_until).getTime();
214
- if (Number.isNaN(until)) return null;
215
- const remainingMs = until - Date.now();
216
- if (remainingMs <= 0) return null;
217
- return Math.ceil(remainingMs / 1e3);
218
- }
219
181
  var STATE_PATH;
220
182
  var init_prompt_state = __esm({
221
183
  "src/hooks/prompt-state.ts"() {
@@ -256,33 +218,6 @@ function isDiscoveryGrep(filePath) {
256
218
  }
257
219
  return false;
258
220
  }
259
- function configuredIndexWaitSeconds() {
260
- const parsed = Number.parseInt(process.env.CONTEXTSTREAM_INDEX_WAIT_SECONDS ?? "", 10);
261
- if (Number.isNaN(parsed)) return DEFAULT_INDEX_WAIT_SECONDS;
262
- return Math.min(MAX_INDEX_WAIT_SECONDS, Math.max(MIN_INDEX_WAIT_SECONDS, parsed));
263
- }
264
- function isLocalDiscoveryToolDuringIndexWait(tool, toolInput) {
265
- if (tool === "Glob" || tool === "Explore" || tool === "SemanticSearch" || tool === "codebase_search") {
266
- return true;
267
- }
268
- if (tool === "Task") {
269
- const subagentTypeRaw = toolInput?.subagent_type || toolInput?.subagentType || "";
270
- return subagentTypeRaw.toLowerCase().includes("explore");
271
- }
272
- if (tool === "Grep" || tool === "Search" || tool === "grep_search" || tool === "code_search") {
273
- const filePath = toolInput?.path || "";
274
- return isDiscoveryGrep(filePath);
275
- }
276
- if (tool === "Read" || tool === "ReadFile" || tool === "read_file") {
277
- const filePath = toolInput?.file_path || toolInput?.path || toolInput?.file || toolInput?.target_file || "";
278
- return isDiscoveryGrep(filePath);
279
- }
280
- if (tool === "list_files" || tool === "search_files" || tool === "search_files_content" || tool === "find_files" || tool === "find_by_name") {
281
- const pattern = toolInput?.path || toolInput?.regex || toolInput?.pattern || toolInput?.query || "";
282
- return !pattern || isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern);
283
- }
284
- return false;
285
- }
286
221
  function isProjectIndexed(cwd) {
287
222
  if (!fs2.existsSync(INDEX_STATUS_FILE)) {
288
223
  return { isIndexed: false, isStale: false };
@@ -452,11 +387,13 @@ function outputClineAllow() {
452
387
  process.exit(0);
453
388
  }
454
389
  function outputCursorBlock(reason) {
455
- console.log(JSON.stringify({ decision: "deny", reason }));
390
+ console.log(
391
+ JSON.stringify({ permission: "deny", agent_message: reason, user_message: reason })
392
+ );
456
393
  process.exit(0);
457
394
  }
458
395
  function outputCursorAllow() {
459
- console.log(JSON.stringify({ decision: "allow" }));
396
+ console.log(JSON.stringify({ permission: "allow" }));
460
397
  process.exit(0);
461
398
  }
462
399
  function blockWithMessage(editorFormat, message) {
@@ -479,6 +416,9 @@ function allowTool(editorFormat, cwd, recordStateChange) {
479
416
  process.exit(0);
480
417
  }
481
418
  function detectEditorFormat(input) {
419
+ if (process.argv.includes("--editor=cursor")) {
420
+ return "cursor";
421
+ }
482
422
  if (input.hookName !== void 0 || input.toolName !== void 0) {
483
423
  return "cline";
484
424
  }
@@ -547,19 +487,7 @@ async function runPreToolUseHook() {
547
487
  const { isIndexed, isStale } = isProjectIndexed(cwd);
548
488
  fs2.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}, isStale=${isStale}
549
489
  `);
550
- if (isIndexed && !isStale) {
551
- clearIndexWaitWindow(cwd);
552
- } else {
553
- const waitSeconds = configuredIndexWaitSeconds();
554
- if (isLocalDiscoveryToolDuringIndexWait(tool, toolInput)) {
555
- startIndexWaitWindow(cwd, waitSeconds);
556
- const remaining = indexWaitRemainingSeconds(cwd);
557
- if (remaining && remaining > 0) {
558
- const msg = `Index refresh grace window is active (${remaining}s remaining). Keep ContextStream search-first flow: mcp__contextstream__search(mode="auto", query="..."). Do not use local discovery tools yet for stale/not-indexed projects. Retry after refresh; local fallback is allowed only after ~${waitSeconds}s if index is still unavailable.`;
559
- blockWithMessage(editorFormat, msg);
560
- }
561
- allowTool(editorFormat, cwd, recordStateChange);
562
- }
490
+ if (!isIndexed || isStale) {
563
491
  allowTool(editorFormat, cwd, recordStateChange);
564
492
  }
565
493
  if (tool === "Glob") {
@@ -651,7 +579,7 @@ async function runPreToolUseHook() {
651
579
  }
652
580
  allowTool(editorFormat, cwd, recordStateChange);
653
581
  }
654
- var ENABLED, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, CONTEXT_FRESHNESS_SECONDS, DEFAULT_INDEX_WAIT_SECONDS, MIN_INDEX_WAIT_SECONDS, MAX_INDEX_WAIT_SECONDS, DISCOVERY_PATTERNS, isDirectRun;
582
+ var ENABLED, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, CONTEXT_FRESHNESS_SECONDS, DISCOVERY_PATTERNS, isDirectRun;
655
583
  var init_pre_tool_use = __esm({
656
584
  "src/hooks/pre-tool-use.ts"() {
657
585
  "use strict";
@@ -661,9 +589,6 @@ var init_pre_tool_use = __esm({
661
589
  DEBUG_FILE = "/tmp/pretooluse-hook-debug.log";
662
590
  STALE_THRESHOLD_DAYS = 7;
663
591
  CONTEXT_FRESHNESS_SECONDS = 120;
664
- DEFAULT_INDEX_WAIT_SECONDS = 20;
665
- MIN_INDEX_WAIT_SECONDS = 15;
666
- MAX_INDEX_WAIT_SECONDS = 20;
667
592
  DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"];
668
593
  isDirectRun = process.argv[1]?.includes("pre-tool-use") || process.argv[2] === "pre-tool-use";
669
594
  if (isDirectRun) {
@@ -1763,8 +1688,8 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
1763
1688
  \u2192 Use \`search(mode="auto", query="...")\`
1764
1689
 
1765
1690
  **IF indexed=false OR last_indexed_at is stale (>7 days):**
1766
- \u2192 Wait up to ~20s for background refresh, retry \`search(mode="auto", query="...")\`
1767
- \u2192 After grace window: local tools are allowed if search still misses
1691
+ \u2192 Run \`search(mode="auto", query="...")\` anyway \u2014 keyword hits return immediately while indexing builds
1692
+ \u2192 Local tools only if search itself returns 0 results/errors on a retry
1768
1693
 
1769
1694
  **IF search still returns 0 results or errors after retry/window:**
1770
1695
  \u2192 Fallback to local tools (Glob/Grep/Read)
@@ -1911,14 +1836,13 @@ COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via
1911
1836
  - On subsequent messages, default to context() first. Narrow bypass: immediate read-only ContextStream calls when context is fresh and no state-changing tool has run.
1912
1837
 
1913
1838
  2. FOR CODE SEARCH: Check index status, then search appropriately
1914
- \u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
1915
- \u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...") BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode
1916
- \u2705 IF NOT indexed OR stale: wait for background refresh up to ~20s, retry mcp__contextstream__search(mode="auto", query="..."), then allow local tools only after the grace window
1917
- \u2705 IF search still returns 0 results after retry/window: fallback to local tools (Glob/Grep/Read)
1839
+ \u2705 Use mcp__contextstream__search(mode="auto", query="...") BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode
1840
+ \u2705 Run search even while the index is still building \u2014 keyword hits return immediately, results fill in as you work
1841
+ \u2705 IF search itself returns 0 results/errors: retry once, THEN fall back to local tools (Glob/Grep/Read)
1918
1842
 
1919
1843
  3. WHEN LOCAL TOOLS (Glob/Grep/Read) ARE OK:
1920
- \u2705 Stale/not-indexed grace window has elapsed (~20s default, configurable)
1921
1844
  \u2705 ContextStream search still returns 0 results or errors after retry
1845
+ \u2705 The file was just written this turn (known-new, not yet indexed)
1922
1846
  \u2705 User explicitly requests local tools
1923
1847
 
1924
1848
  4. FOR PLANS & TASKS: Use ContextStream, not file-based plans
@@ -2519,21 +2443,33 @@ var init_rules_templates = __esm({
2519
2443
  // Renamed from context_smart - get relevant context every message
2520
2444
  "context_feedback",
2521
2445
  "generate_rules",
2446
+ "instruct",
2447
+ "flash",
2522
2448
  // Consolidated domain tools (v0.4.x default)
2523
2449
  "search",
2524
2450
  // Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
2525
2451
  "session",
2526
- // Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
2452
+ // 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
2527
2453
  "memory",
2528
2454
  // 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
2455
+ "entity",
2456
+ // Actions: list, get, create, update, delete for structured taxonomy entities
2457
+ "capsule",
2458
+ // Actions: open, create, share, bootstrap_prompt, graph, audit, delete
2459
+ "qa",
2460
+ // Actions: ask (grounded answer with citations), search (prior Q&A), save_kb/list_kb/get_kb/update_kb/delete_kb (knowledge base), feedback
2529
2461
  "graph",
2530
2462
  // Actions: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions
2531
2463
  "project",
2532
- // Actions: list, get, create, update, index, overview, statistics, files, index_status, ingest_local
2464
+ // Actions: list, get, create, update, index, purge, forget_local, remove_paths, merge, overview, statistics, files, index_status, ingest_local
2533
2465
  "workspace",
2534
2466
  // Actions: list, get, associate, bootstrap
2535
2467
  "reminder",
2536
2468
  // Actions: list, active, create, snooze, complete, dismiss
2469
+ "media",
2470
+ // Actions: index, status, search, get_clip, list, delete
2471
+ "skill",
2472
+ // Actions: list, run, create, update, supersede, import
2537
2473
  "integration",
2538
2474
  // Provider: slack, github, all; Actions: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues
2539
2475
  "help"
@@ -2550,9 +2486,60 @@ var init_rules_templates = __esm({
2550
2486
 
2551
2487
  **Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
2552
2488
 
2489
+ **Common queries \u2014 use these exact tool calls:**
2490
+ - "list lessons" / "show lessons" \u2192 \`session(action="get_lessons")\`
2491
+ - "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.
2492
+ - "list decisions" / "show decisions" \u2192 \`memory(action="decisions")\`
2493
+ - "save decision" / "decided to" \u2192 \`session(action="capture", event_type="decision", title="...", content="...")\`
2494
+ - "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)\`
2495
+ - "search past sessions" / "when did we discuss X" \u2192 \`memory(action="search_transcripts", query="...")\`
2496
+ - "save snapshot" \u2192 \`session(action="capture", event_type="session_snapshot", title="...", content="...")\`
2497
+ - "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)
2498
+ - "find media" / "search photos/videos/audio/docs" / "what's in this PDF/video/audio?" \u2192 \`media(action="search", query="...", content_types=["document"])\`
2499
+ - "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")\`
2500
+
2501
+ ## Skills, Docs & Lessons First
2502
+
2503
+ Before guessing, improvising, or struggling through a workflow you do not fully know, check whether ContextStream already has guidance for it.
2504
+ - Start with \`context(...)\` and obey \`[MATCHED_SKILLS]\`, \`[LESSONS_WARNING]\`, \`[PREFERENCE]\`, and \`<system-reminder>\` output
2505
+ - Treat \`[LESSONS_WARNING]\` as active working instructions for the current task, not optional background
2506
+ - 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
2507
+ - Prefer surfaced ContextStream skills/docs/lessons over inventing a new workflow from memory
2508
+
2509
+ ## Project Scope Discipline
2510
+
2511
+ - Reuse the \`project_id\` returned by \`init(...)\` or \`context(...)\` for project-scoped writes and lookups
2512
+ - For project-scoped \`memory(...)\`, \`session(...)\`, and \`skill(...)\` calls, pass explicit \`project_id\` instead of guessing from the folder name
2513
+ - If no \`project_id\` is resolved, rerun \`init(folder_path="...")\` before creating docs, skills, events, tasks, todos, or other project memory
2514
+ - Use \`target_project\` only after init from a multi-project parent folder
2515
+
2553
2516
  **Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
2554
2517
 
2555
- **Notices:** [LESSONS_WARNING] \u2192 apply lessons | [PREFERENCE] \u2192 follow user preferences | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE/CRITICAL] \u2192 tell user about update
2518
+ **Planning:** ALWAYS save plans to ContextStream \u2014 NOT markdown files or built-in todo tools:
2519
+ \`session(action="capture_plan", title="...", steps=[...])\` + \`memory(action="create_task", title="...", plan_id="...")\`
2520
+
2521
+ **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.
2522
+ - Lessons (mistakes, corrections, "never do X again"): \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...", severity="...")\`
2523
+ - Decisions / notes / insights: \`session(action="capture", event_type="decision|note|insight", ...)\`
2524
+ - Docs / todos / knowledge nodes: \`memory(action="create_doc|create_todo|create_node", ...)\`
2525
+
2526
+ **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.
2527
+
2528
+ ## Past Sessions Are Queryable \u2014 USE THEM
2529
+
2530
+ Transcripts of every prior session are captured + indexed. Before asking the user what you did last time, or re-deriving context you built together previously, check the transcript + snapshot layer.
2531
+
2532
+ 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.
2533
+
2534
+ Exact calls:
2535
+ - Ranked past-session context: \`session(action="recall", query="<what you're continuing>")\`
2536
+ - Chronological list of recent sessions: \`memory(action="list_transcripts", limit=10)\`
2537
+ - Full-text search across ALL past transcripts: \`memory(action="search_transcripts", query="<keyword>")\`
2538
+ - Save a snapshot of the current session so the NEXT session can pick up: \`session(action="capture", event_type="session_snapshot", title="...", content="...")\`
2539
+
2540
+ **Never answer "I don't know what we did before" without running recall or search_transcripts at least once.**
2541
+
2542
+ **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
2556
2543
 
2557
2544
  v${RULES_VERSION}
2558
2545
  `.trim();
@@ -2620,7 +2607,7 @@ Use \`context()\` by default to get task-specific rules, lessons from past mista
2620
2607
  2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
2621
2608
  - Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
2622
2609
  - **You MUST use the suggested ContextStream tool instead**
2623
- - For stale/not-indexed projects, wait for background refresh (up to ~20s), retry search, then allow local tools only after the grace window
2610
+ - 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
2624
2611
 
2625
2612
  3. **PostToolUse indexing** - After Edit/Write operations:
2626
2613
  - Changed files are automatically re-indexed
@@ -2657,7 +2644,7 @@ STOP \u2192 Call search(mode="auto", query="...") FIRST
2657
2644
 
2658
2645
  \u2705 **ALWAYS DO THIS:**
2659
2646
  1. \`search(mode="auto", query="what you're looking for")\`
2660
- 2. Only use local tools (Glob/Grep/Read) after stale/not-indexed refresh grace window elapses (~20s) or ContextStream still returns **0 results** after retry
2647
+ 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"
2661
2648
  3. Use Read ONLY for exact file edits after you know the file path
2662
2649
 
2663
2650
  This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
@@ -2920,7 +2907,7 @@ session(action="capture", event_type="session_snapshot", title="Pre-compaction s
2920
2907
 
2921
2908
  ### Search & Code Intelligence (ContextStream-first)
2922
2909
 
2923
- \u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST. For stale/not-indexed projects, wait ~20s for background refresh and retry search before local fallback.
2910
+ \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.
2924
2911
 
2925
2912
  **\u274C WRONG workflow (wastes tokens, slow):**
2926
2913
  \`\`\`
@@ -2939,7 +2926,7 @@ search(mode="auto", query="function implementation") \u2192 done (results includ
2939
2926
  2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
2940
2927
  3. \`project(action="files")\` - file tree/list (only when needed)
2941
2928
  4. \`graph(action="dependencies", ...)\` - code structure
2942
- 5. Local repo scans (rg/ls/find) - ONLY after refresh grace window/retry still yields no results/errors, or the user explicitly asks
2929
+ 5. Local repo scans (rg/ls/find) - ONLY after \`search(...)\` itself returns 0 results/errors on a retry, or the user explicitly asks
2943
2930
 
2944
2931
  **Search Mode Selection:**
2945
2932
 
@@ -2970,7 +2957,7 @@ search(mode="auto", query="function implementation") \u2192 done (results includ
2970
2957
 
2971
2958
  **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.
2972
2959
 
2973
- If ContextStream returns results, stop and use them. NEVER use local Search/Explore/Read unless you need exact code edits, or refresh grace window + retry still returns 0 results.
2960
+ 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.
2974
2961
 
2975
2962
  **Code Analysis:**
2976
2963
  - Dependencies: \`graph(action="dependencies", file_path="...")\`
@@ -3099,7 +3086,7 @@ See full documentation: https://contextstream.io/docs/mcp/tools
3099
3086
  3. Narrow bypass: immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run
3100
3087
 
3101
3088
  **BEFORE Glob/Grep/Read/Search/Explore/Task/EnterPlanMode:**
3102
- \u2192 \`search(mode="auto", query="...")\` FIRST \u2014 for stale/not-indexed, wait ~20s for refresh then retry before local fallback
3089
+ \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
3103
3090
 
3104
3091
  **HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
3105
3092
  </contextstream_protocol>
@@ -3322,7 +3309,7 @@ search(mode="auto", query="what you're looking for")
3322
3309
  \u2192 Use this instead of Explore/Task/EnterPlanMode for file discovery.
3323
3310
 
3324
3311
  **IF project is NOT indexed or very stale (>7 days):**
3325
- \u2192 Wait up to ~20s for background refresh, retry \`search(mode="auto", ...)\`, then allow local tools only after the grace window
3312
+ \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
3326
3313
  \u2192 OR run \`project(action="index")\` first, then search
3327
3314
 
3328
3315
  **IF ContextStream search still returns 0 results or errors after retry/window:**
@@ -3351,7 +3338,7 @@ search(mode="auto", query="what you're looking for")
3351
3338
  - Then use local Read/Grep only on paths returned by ContextStream.
3352
3339
 
3353
3340
  ### When Local Tools Are OK:
3354
- \u2705 Stale/not-indexed grace window has elapsed (~20s default, configurable)
3341
+ \u2705 The edit is known-new (just written this turn) and not yet indexed
3355
3342
  \u2705 ContextStream search still returns 0 results after retry
3356
3343
  \u2705 ContextStream returns errors
3357
3344
  \u2705 User explicitly requests local tools
@@ -3444,7 +3431,7 @@ After updating, user should restart their AI tool.
3444
3431
  - Keep \`mcp_config.json\` valid and minimal: preserve non-ContextStream servers and only update the \`contextstream\` server block.
3445
3432
  - If ContextStream appears skipped, verify:
3446
3433
  1. MCP server status is healthy in Antigravity settings
3447
- 2. Project is indexed and \`search(mode="auto", ...)\` is retried before local fallbacks
3434
+ 2. \`search(mode="auto", ...)\` ran FIRST (even while indexing) and was retried before any local fallback
3448
3435
  3. Instructions file contains the current ContextStream managed block
3449
3436
  `;
3450
3437
  NO_HOOKS_EDITORS = ["copilot", "codex", "opencode", "aider", "antigravity", "kilo"];
@@ -3464,9 +3451,17 @@ ${rules}
3464
3451
  `
3465
3452
  },
3466
3453
  cursor: {
3467
- filename: ".cursorrules",
3468
- description: "Cursor AI rules",
3469
- build: (rules) => `# Cursor Rules
3454
+ // Cursor Agent mode does not read .cursorrules at all; only
3455
+ // .cursor/rules/*.mdc loads in every mode (Chat/Composer/Agent).
3456
+ // Existing .cursorrules blocks are left in place for older Cursor
3457
+ // versions but are no longer created.
3458
+ filename: ".cursor/rules/contextstream.mdc",
3459
+ description: "Cursor AI rules (.cursor/rules MDC \u2014 loaded in all Cursor modes)",
3460
+ build: (rules) => `---
3461
+ alwaysApply: true
3462
+ ---
3463
+
3464
+ # Cursor Rules
3470
3465
  ${rules}
3471
3466
  `
3472
3467
  },
@@ -3715,6 +3710,25 @@ function formatContext(ctx, options = {}) {
3715
3710
  parts.push(
3716
3711
  '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.'
3717
3712
  );
3713
+ parts.push("\n## \u{1F4DC} Past Sessions Are Queryable \u2014 check before re-deriving context");
3714
+ parts.push(
3715
+ `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:`
3716
+ );
3717
+ parts.push(
3718
+ '- `mcp__contextstream__session(action="recall", query="<what you\'re continuing>")` \u2014 ranked fusion of transcripts/snapshots/docs/decisions'
3719
+ );
3720
+ parts.push(
3721
+ '- `mcp__contextstream__memory(action="list_transcripts", limit=10)` \u2014 chronological list of recent sessions'
3722
+ );
3723
+ parts.push(
3724
+ '- `mcp__contextstream__memory(action="search_transcripts", query="<keyword>")` \u2014 full-text search across ALL past transcripts'
3725
+ );
3726
+ parts.push(
3727
+ '- `mcp__contextstream__memory(action="list_events", event_type="session_snapshot")` \u2014 manual bookmarks at turning points'
3728
+ );
3729
+ parts.push(
3730
+ '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>")`.'
3731
+ );
3718
3732
  return parts.join("\n");
3719
3733
  }
3720
3734
  function regenerateRuleFiles(folderPath) {