@contextstream/mcp-server 0.4.48 → 0.4.49

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.
@@ -105,7 +105,7 @@ function buildHooksConfig(options) {
105
105
  ],
106
106
  UserPromptSubmit: userPromptHooks
107
107
  };
108
- if (options?.includePreCompact) {
108
+ if (options?.includePreCompact !== false) {
109
109
  config.PreCompact = [
110
110
  {
111
111
  // Match both manual (/compact) and automatic compaction
@@ -157,7 +157,7 @@ async function installHookScripts(options) {
157
157
  preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
158
158
  userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
159
159
  };
160
- if (options?.includePreCompact) {
160
+ if (options?.includePreCompact !== false) {
161
161
  result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
162
162
  }
163
163
  if (options?.includeMediaAware !== false) {
@@ -203,7 +203,7 @@ async function installClaudeCodeHooks(options) {
203
203
  "npx @contextstream/mcp-server hook pre-tool-use",
204
204
  "npx @contextstream/mcp-server hook user-prompt-submit"
205
205
  );
206
- if (options.includePreCompact) {
206
+ if (options.includePreCompact !== false) {
207
207
  result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
208
208
  }
209
209
  if (options.includeMediaAware !== false) {
@@ -270,12 +270,11 @@ When Media-Aware hook detects media patterns, it injects context about:
270
270
  - How to get clips for Remotion (with frame-based props)
271
271
  - How to index new media files
272
272
 
273
- ### PreCompact Hook (Optional)
273
+ ### PreCompact Hook
274
274
  - **Command:** \`npx @contextstream/mcp-server hook pre-compact\`
275
275
  - **Purpose:** Saves conversation state before context compaction
276
276
  - **Triggers:** Both manual (/compact) and automatic compaction
277
- - **Disable:** Set \`CONTEXTSTREAM_PRECOMPACT_ENABLED=false\` environment variable
278
- - **Note:** Enable with \`generate_rules(include_pre_compact=true)\` to activate
277
+ - **Installed:** By default (disable with \`CONTEXTSTREAM_HOOK_ENABLED=false\`)
279
278
 
280
279
  When PreCompact runs, it:
281
280
  1. Parses the transcript for active files and tool calls
@@ -2,11 +2,16 @@
2
2
 
3
3
  // src/hooks/user-prompt-submit.ts
4
4
  var ENABLED = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
5
- var REMINDER = `[CONTEXTSTREAM RULES]
6
- 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="hybrid") FIRST
7
- 2. Call context_smart at start of EVERY response
8
- 3. Local tools ONLY if ContextStream returns 0 results
9
- [END RULES]`;
5
+ var REMINDER = `[CONTEXTSTREAM - DO THIS FIRST]
6
+ mcp__contextstream__context(user_message="<user's message>") - CALL THIS BEFORE ANY OTHER TOOL
7
+
8
+ Why: Delivers task-specific rules, lessons from past mistakes, and relevant context.
9
+ Skip it = repeat past mistakes, miss relevant decisions, fly blind.
10
+
11
+ BEFORE Glob/Grep/Read/Search/Explore:
12
+ mcp__contextstream__search(mode="hybrid", query="...") FIRST
13
+ Local tools ONLY if search returns 0 results.
14
+ [END]`;
10
15
  function detectEditorFormat(input) {
11
16
  if (input.hookName !== void 0) {
12
17
  return "cline";
package/dist/index.js CHANGED
@@ -591,7 +591,7 @@ function buildHooksConfig(options) {
591
591
  ],
592
592
  UserPromptSubmit: userPromptHooks
593
593
  };
594
- if (options?.includePreCompact) {
594
+ if (options?.includePreCompact !== false) {
595
595
  config.PreCompact = [
596
596
  {
597
597
  // Match both manual (/compact) and automatic compaction
@@ -643,7 +643,7 @@ async function installHookScripts(options) {
643
643
  preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
644
644
  userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
645
645
  };
646
- if (options?.includePreCompact) {
646
+ if (options?.includePreCompact !== false) {
647
647
  result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
648
648
  }
649
649
  if (options?.includeMediaAware !== false) {
@@ -689,7 +689,7 @@ async function installClaudeCodeHooks(options) {
689
689
  "npx @contextstream/mcp-server hook pre-tool-use",
690
690
  "npx @contextstream/mcp-server hook user-prompt-submit"
691
691
  );
692
- if (options.includePreCompact) {
692
+ if (options.includePreCompact !== false) {
693
693
  result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
694
694
  }
695
695
  if (options.includeMediaAware !== false) {
@@ -756,12 +756,11 @@ When Media-Aware hook detects media patterns, it injects context about:
756
756
  - How to get clips for Remotion (with frame-based props)
757
757
  - How to index new media files
758
758
 
759
- ### PreCompact Hook (Optional)
759
+ ### PreCompact Hook
760
760
  - **Command:** \`npx @contextstream/mcp-server hook pre-compact\`
761
761
  - **Purpose:** Saves conversation state before context compaction
762
762
  - **Triggers:** Both manual (/compact) and automatic compaction
763
- - **Disable:** Set \`CONTEXTSTREAM_PRECOMPACT_ENABLED=false\` environment variable
764
- - **Note:** Enable with \`generate_rules(include_pre_compact=true)\` to activate
763
+ - **Installed:** By default (disable with \`CONTEXTSTREAM_HOOK_ENABLED=false\`)
765
764
 
766
765
  When PreCompact runs, it:
767
766
  1. Parses the transcript for active files and tool calls
@@ -2633,11 +2632,16 @@ var init_user_prompt_submit = __esm({
2633
2632
  "src/hooks/user-prompt-submit.ts"() {
2634
2633
  "use strict";
2635
2634
  ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
2636
- REMINDER = `[CONTEXTSTREAM RULES]
2637
- 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="hybrid") FIRST
2638
- 2. Call context_smart at start of EVERY response
2639
- 3. Local tools ONLY if ContextStream returns 0 results
2640
- [END RULES]`;
2635
+ REMINDER = `[CONTEXTSTREAM - DO THIS FIRST]
2636
+ mcp__contextstream__context(user_message="<user's message>") - CALL THIS BEFORE ANY OTHER TOOL
2637
+
2638
+ Why: Delivers task-specific rules, lessons from past mistakes, and relevant context.
2639
+ Skip it = repeat past mistakes, miss relevant decisions, fly blind.
2640
+
2641
+ BEFORE Glob/Grep/Read/Search/Explore:
2642
+ mcp__contextstream__search(mode="hybrid", query="...") FIRST
2643
+ Local tools ONLY if search returns 0 results.
2644
+ [END]`;
2641
2645
  isDirectRun3 = process.argv[1]?.includes("user-prompt-submit") || process.argv[2] === "user-prompt-submit";
2642
2646
  if (isDirectRun3) {
2643
2647
  runUserPromptSubmitHook().catch(() => process.exit(0));
@@ -12103,62 +12107,103 @@ function applyMcpToolPrefix(markdown, toolPrefix) {
12103
12107
  const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
12104
12108
  return markdown.replace(toolRegex, `${toolPrefix}$1`);
12105
12109
  }
12106
- var CONTEXTSTREAM_RULES_DYNAMIC = `
12107
- # ContextStream Rules (MANDATORY)
12110
+ var CONTEXTSTREAM_RULES_BOOTSTRAP = `
12111
+ <contextstream_rules>
12112
+ | Message | Required |
12113
+ |---------|----------|
12114
+ | **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
12115
+ | **Every message** | \`context(user_message="...")\` FIRST |
12116
+ | **Before file search** | \`search(mode="hybrid")\` BEFORE Glob/Grep/Read |
12117
+ </contextstream_rules>
12108
12118
 
12109
- ## \u{1F6A8} CRITICAL: Call These Tools or Fail \u{1F6A8}
12119
+ **Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
12110
12120
 
12111
- <contextstream_protocol>
12112
- **EVERY response MUST start with:**
12113
- 1. \`init()\` (1st message only) \u2192 then \`context(user_message="<msg>")\`
12114
- 2. \`context(user_message="<msg>")\` (EVERY subsequent message)
12121
+ **Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
12115
12122
 
12116
- **BEFORE Glob/Grep/Read/Search/Explore:**
12117
- \u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
12123
+ **Notices:** [LESSONS_WARNING] \u2192 tell user first | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE] \u2192 tell user to update
12118
12124
 
12119
- **HOOKS: Follow \`<system-reminder>\` instructions \u2014 they ARE the rules**
12120
- </contextstream_protocol>
12125
+ v${RULES_VERSION}
12126
+ `.trim();
12127
+ var CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
12128
+ var CONTEXTSTREAM_RULES_FULL = `
12129
+ ## \u{1F6A8} MANDATORY: CALL CONTEXT EVERY MESSAGE \u{1F6A8}
12121
12130
 
12122
- ### Why This Matters
12131
+ <contextstream_rules>
12132
+ **EVERY response MUST start with \`context(user_message="...")\`**
12123
12133
 
12124
- - **\`context()\`** = Your rules, lessons, and relevant memories delivered dynamically
12125
- - **\`search()\`** = Indexed search (1 call) vs Grep\u2192Read\u2192Read (4+ calls)
12126
- - **Hooks inject \`<system-reminder>\` tags** = Follow these instructions, they enforce ContextStream-first
12127
- - **Skipping these = missing context, repeated mistakes, slow searches**
12134
+ | Message | What to Call |
12135
+ |---------|--------------|
12136
+ | 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
12137
+ | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
12128
12138
 
12129
- ### Protocol
12139
+ **BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="hybrid")\` FIRST
12140
+ </contextstream_rules>
12130
12141
 
12131
- | When | Do This |
12132
- |------|---------|
12133
- | 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
12134
- | Every message after | \`context(user_message="...")\` |
12135
- | Before ANY file search | \`search(mode="hybrid")\` first |
12136
- | On \`<system-reminder>\` | **Follow the instructions inside** |
12137
- | Save decisions | \`session(action="capture", ...)\` |
12142
+ **This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
12143
+
12144
+ ---
12145
+
12146
+ ## Why \`context()\` is MANDATORY Every Message
12147
+
12148
+ \u274C **WRONG:** "I already called init, I don't need context"
12149
+ \u2705 **CORRECT:** \`context()\` is required EVERY message, not just the first
12138
12150
 
12139
- ### Handle Notices from context()
12151
+ **What you lose without \`context()\`:**
12152
+ - Dynamic rules matched to your current task
12153
+ - Lessons from past mistakes (you WILL repeat them)
12154
+ - Semantically relevant decisions and context
12155
+ - Warnings about risky operations
12156
+
12157
+ **\`init()\` gives recent items by TIME. \`context()\` finds items RELEVANT to THIS message.**
12158
+
12159
+ ---
12160
+
12161
+ ## Handle Notices from context()
12140
12162
 
12141
12163
  - **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
12142
12164
  - **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
12143
12165
  - **[VERSION_NOTICE]** \u2192 Tell user to update MCP
12144
12166
 
12145
- ### Fast Path (Skip context only for these)
12167
+ ---
12146
12168
 
12147
- Simple utility commands: "list workspaces", "show version", "list reminders"
12148
- \u2192 Just execute directly: \`workspace(action="list")\`, \`help(action="version")\`
12169
+ ## \u{1F6A8} HOOKS - AUTOMATIC RULE ENFORCEMENT \u{1F6A8}
12149
12170
 
12150
- Rules Version: ${RULES_VERSION}
12151
- `.trim();
12152
- var CONTEXTSTREAM_RULES_FULL = `
12153
- ## ContextStream Rules
12171
+ **ContextStream installs hooks that automatically enforce rules.** You MUST follow hook output.
12154
12172
 
12155
- <contextstream_rules>
12156
- 1. **SIMPLE UTILITY?** (list/show/version) \u2192 Just execute, skip context calls
12157
- 2. **CODING TASK?** \u2192 init \u2192 context \u2192 work \u2192 capture
12158
- 3. **SEARCH FIRST:** Before Glob/Grep/Read \u2192 \`search(mode="hybrid")\` FIRST
12159
- </contextstream_rules>
12173
+ ### Installed Hooks
12174
+
12175
+ | Hook | What It Does | Output |
12176
+ |------|--------------|--------|
12177
+ | **UserPromptSubmit** | Injects rules reminder on EVERY message | \`<system-reminder>\` with rules block |
12178
+ | **PreToolUse** | Blocks Glob/Grep/Search/Explore when ContextStream is available | Error message redirecting to \`search()\` |
12179
+ | **PostToolUse** | Auto-indexes files after Edit/Write operations | Background indexing |
12180
+ | **PreCompact** | Saves session state before context compaction | Snapshot creation |
12181
+
12182
+ ### How Hooks Work
12183
+
12184
+ 1. **\`<system-reminder>\` tags** - Injected by UserPromptSubmit hook on every message
12185
+ - These tags contain the current rules
12186
+ - **FOLLOW THE INSTRUCTIONS INSIDE** - they ARE the rules
12187
+ - Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
12160
12188
 
12161
- **Display this block at the start of responses to keep rules in context.**
12189
+ 2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
12190
+ - Hook returns error: \`STOP: Use mcp__contextstream__search(mode="hybrid") instead\`
12191
+ - **You MUST use the suggested ContextStream tool instead**
12192
+ - Local tools are only allowed if project is not indexed or ContextStream returns 0 results
12193
+
12194
+ 3. **PostToolUse indexing** - After Edit/Write operations:
12195
+ - Changed files are automatically re-indexed
12196
+ - No action required from you
12197
+
12198
+ 4. **PreCompact snapshots** - Before context compaction:
12199
+ - Hook reminds you to save important state
12200
+ - Call \`session(action="capture", event_type="session_snapshot", ...)\` when warned
12201
+
12202
+ ### Disabling Hooks
12203
+
12204
+ Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
12205
+
12206
+ **Note:** Disabling hooks removes rule enforcement. Only disable for debugging.
12162
12207
 
12163
12208
  ---
12164
12209
 
@@ -12244,27 +12289,27 @@ You have access to ContextStream MCP tools for persistent memory and context.
12244
12289
  v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
12245
12290
  Rules Version: ${RULES_VERSION}
12246
12291
 
12247
- ## TL;DR - WHEN TO USE CONTEXT
12292
+ ## TL;DR - CONTEXT EVERY MESSAGE
12248
12293
 
12249
- | Request Type | What to Do |
12250
- |--------------|------------|
12251
- | **\u{1F680} Simple utility** (list workspaces, show version) | **Just execute directly** - skip init, context, capture |
12252
- | **\u{1F4BB} Coding task** (edit, create, refactor) | Full context: init \u2192 context \u2192 work \u2192 capture |
12253
- | **\u{1F50D} Code search/discovery** | init \u2192 context \u2192 search() |
12254
- | **\u26A0\uFE0F Risky work** (deploy, migrate, refactor) | Check lessons first: \`session(action="get_lessons")\` |
12255
- | **User frustration/correction** | Capture lesson: \`session(action="capture_lesson", ...)\` |
12294
+ | Message | Required |
12295
+ |---------|----------|
12296
+ | **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
12297
+ | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
12298
+ | **Before file search** | \`search(mode="hybrid")\` FIRST |
12299
+ | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
12300
+ | **User correction** | \`session(action="capture_lesson", ...)\` |
12256
12301
 
12257
- ### Simple Utility Operations - FAST PATH
12302
+ ### Why EVERY Message?
12258
12303
 
12259
- **For simple queries, just execute and respond:**
12260
- - "list workspaces" \u2192 \`workspace(action="list")\` \u2192 done
12261
- - "list projects" \u2192 \`project(action="list")\` \u2192 done
12262
- - "show version" \u2192 \`help(action="version")\` \u2192 done
12263
- - "what reminders do I have" \u2192 \`reminder(action="list")\` \u2192 done
12304
+ \`context()\` delivers:
12305
+ - **Dynamic rules** matched to your current task
12306
+ - **Lessons** from past mistakes (prevents repeating errors)
12307
+ - **Relevant decisions** and context (semantic search)
12308
+ - **Warnings** about risky operations
12264
12309
 
12265
- **No init. No context. No capture.** These add noise, not value.
12310
+ **Without \`context()\`, you are blind to relevant context and will repeat past mistakes.**
12266
12311
 
12267
- ### Coding Tasks - FULL CONTEXT
12312
+ ### Protocol
12268
12313
 
12269
12314
  | Step | What to Call |
12270
12315
  |------|--------------|
@@ -12275,12 +12320,7 @@ Rules Version: ${RULES_VERSION}
12275
12320
  | **User correction** | \`session(action="capture_lesson", ...)\` |
12276
12321
  | **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
12277
12322
 
12278
- **How to detect simple utility operations:**
12279
- - Single-word commands: "list", "show", "version", "help"
12280
- - Data retrieval with no context dependency: "list my workspaces", "what projects do I have"
12281
- - Status checks: "am I authenticated?", "what's the server version?"
12282
-
12283
- **First message rule (for coding tasks):** After \`init\`:
12323
+ **First message rule:** After \`init\`:
12284
12324
  1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
12285
12325
  2. Then call \`context\` before any other tool or response
12286
12326
 
@@ -12380,8 +12420,8 @@ ContextStream tracks context pressure to help you stay ahead of conversation com
12380
12420
  - \`prepare_save\`: Start thinking about saving important state
12381
12421
  - \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
12382
12422
 
12383
- **PreCompact Hook (Optional):** If enabled, Claude Code will inject a reminder to save state before compaction.
12384
- Enable with: \`generate_rules(install_hooks=true, include_pre_compact=true)\`
12423
+ **PreCompact Hook:** Automatically saves session state before context compaction.
12424
+ Installed by default. Disable with: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
12385
12425
 
12386
12426
  **Before compaction happens (when warned):**
12387
12427
  \`\`\`
@@ -12678,7 +12718,7 @@ Everything else = full protocol (init \u2192 context \u2192 search \u2192 work)
12678
12718
  ### Context Pressure & Compaction
12679
12719
 
12680
12720
  - If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
12681
- - PreCompact hooks automatically save snapshots before compaction (if installed)
12721
+ - PreCompact hooks automatically save snapshots before compaction
12682
12722
 
12683
12723
  ### Enhanced Context (Warnings)
12684
12724
 
@@ -12793,8 +12833,8 @@ function getTemplate(editor) {
12793
12833
  function generateRuleContent(editor, options) {
12794
12834
  const template = getTemplate(editor);
12795
12835
  if (!template) return null;
12796
- const mode = options?.mode || "dynamic";
12797
- const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : CONTEXTSTREAM_RULES_DYNAMIC;
12836
+ const mode = options?.mode || "bootstrap";
12837
+ const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : mode === "bootstrap" ? CONTEXTSTREAM_RULES_BOOTSTRAP : CONTEXTSTREAM_RULES_DYNAMIC;
12798
12838
  let content = template.build(rules);
12799
12839
  if (options?.workspaceName || options?.projectName) {
12800
12840
  const header = `
@@ -13070,6 +13110,112 @@ function trackToolTokenSavings(client, tool, contextText, params, extraMetadata)
13070
13110
  }
13071
13111
  }
13072
13112
 
13113
+ // src/educational-microcopy.ts
13114
+ var SESSION_INIT_TIPS = [
13115
+ "AI work that doesn't disappear.",
13116
+ "Every conversation builds context. Every artifact persists.",
13117
+ "Your AI remembers. Now you can see what it knows.",
13118
+ "Context that survives sessions.",
13119
+ "The bridge between AI conversations and human artifacts."
13120
+ ];
13121
+ function getSessionInitTip(sessionId) {
13122
+ const index = sessionId ? Math.abs(hashString(sessionId)) % SESSION_INIT_TIPS.length : Math.floor(Math.random() * SESSION_INIT_TIPS.length);
13123
+ return SESSION_INIT_TIPS[index];
13124
+ }
13125
+ var CAPTURE_HINTS = {
13126
+ // Core event types
13127
+ decision: "Future you will thank present you.",
13128
+ preference: "Noted. This will inform future suggestions.",
13129
+ insight: "Captured for future reference.",
13130
+ note: "Saved. Won't disappear when the chat does.",
13131
+ implementation: "Implementation recorded.",
13132
+ task: "Task tracked.",
13133
+ bug: "Bug logged for tracking.",
13134
+ feature: "Feature request captured.",
13135
+ plan: "This plan will be here when you come back.",
13136
+ correction: "Correction noted. Learning from this.",
13137
+ lesson: "Learn once, remember forever.",
13138
+ warning: "Warning logged.",
13139
+ frustration: "Feedback captured. We're listening.",
13140
+ conversation: "Conversation preserved.",
13141
+ session_snapshot: "Session state saved. Ready to resume anytime."
13142
+ };
13143
+ function getCaptureHint(eventType) {
13144
+ return CAPTURE_HINTS[eventType] || "Captured. This will survive when the chat disappears.";
13145
+ }
13146
+ var EMPTY_STATE_HINTS = {
13147
+ // Plans
13148
+ list_plans: "No plans yet. Plans live here, not in chat transcripts.",
13149
+ get_plan: "Plan not found. Create one to track implementation across sessions.",
13150
+ // Memory & Search
13151
+ recall: "Nothing found yet. Context builds over time.",
13152
+ search: "No matches. Try a different query or let context accumulate.",
13153
+ list_events: "No events yet. Start capturing decisions and insights.",
13154
+ decisions: "No decisions captured yet. Record the 'why' behind your choices.",
13155
+ timeline: "Timeline empty. Events will appear as you work.",
13156
+ // Lessons
13157
+ get_lessons: "No lessons yet. Capture mistakes to learn from them.",
13158
+ // Tasks
13159
+ list_tasks: "No tasks yet. Break plans into trackable tasks.",
13160
+ // Todos
13161
+ list_todos: "No todos yet. Action items will appear here.",
13162
+ // Diagrams
13163
+ list_diagrams: "No diagrams yet. AI-generated diagrams persist here.",
13164
+ // Docs
13165
+ list_docs: "No docs yet. Turn AI explanations into shareable documentation.",
13166
+ // Reminders
13167
+ list_reminders: "No reminders set. Schedule follow-ups that won't be forgotten.",
13168
+ // Generic
13169
+ default: "Nothing yet. Start a conversation\u2014context builds over time."
13170
+ };
13171
+ function getEmptyStateHint(action) {
13172
+ return EMPTY_STATE_HINTS[action] || EMPTY_STATE_HINTS.default;
13173
+ }
13174
+ var POST_COMPACT_HINTS = {
13175
+ restored: "Picked up where you left off. Session ended, memory didn't.",
13176
+ restored_with_session: (sessionId) => `Restored from session ${sessionId}. Session ended, memory didn't.`,
13177
+ no_snapshot: "No prior session found. Fresh start\u2014context will build as you work.",
13178
+ failed: "Couldn't restore previous session. Use context to retrieve what you need."
13179
+ };
13180
+ var PLAN_HINTS = {
13181
+ created: "Plan saved. It will be here when you come back.",
13182
+ activated: "Plan is now active. Track progress across sessions.",
13183
+ completed: "Plan completed. The journey is preserved for future reference.",
13184
+ abandoned: "Plan archived. Abandoned plans still teach.",
13185
+ updated: "Plan updated. Changes are preserved."
13186
+ };
13187
+ function getPlanStatusHint(status) {
13188
+ const statusMap = {
13189
+ draft: PLAN_HINTS.created,
13190
+ active: PLAN_HINTS.activated,
13191
+ completed: PLAN_HINTS.completed,
13192
+ abandoned: PLAN_HINTS.abandoned,
13193
+ archived: PLAN_HINTS.abandoned
13194
+ };
13195
+ return statusMap[status] || PLAN_HINTS.updated;
13196
+ }
13197
+ var TASK_HINTS = {
13198
+ created: "Task tracked. It won't disappear when the chat does.",
13199
+ completed: "Task done. Progress is preserved.",
13200
+ blocked: "Task blocked. Capture what's blocking for future reference.",
13201
+ cancelled: "Task cancelled. The history remains."
13202
+ };
13203
+ var INTEGRATION_HINTS = {
13204
+ not_connected: "Connect integrations to sync context where your team already works.",
13205
+ slack: "Context from Slack conversations, connected.",
13206
+ github: "Issues and PRs linked to the decisions behind them.",
13207
+ notion: "Decisions synced to where your team documents."
13208
+ };
13209
+ function hashString(str) {
13210
+ let hash = 0;
13211
+ for (let i = 0; i < str.length; i++) {
13212
+ const char = str.charCodeAt(i);
13213
+ hash = (hash << 5) - hash + char;
13214
+ hash = hash & hash;
13215
+ }
13216
+ return hash;
13217
+ }
13218
+
13073
13219
  // src/tools.ts
13074
13220
  var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
13075
13221
  var LOG_QUIET = LOG_LEVEL === "quiet";
@@ -13135,6 +13281,7 @@ var lastAutoSavePressureLevel = "";
13135
13281
  var contextCallsSinceLastSave = 0;
13136
13282
  var SEARCH_RULES_REMINDER_ENABLED = process.env.CONTEXTSTREAM_SEARCH_REMINDER?.toLowerCase() !== "false";
13137
13283
  var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="hybrid") before Glob/Grep/Read. Local tools only if 0 results.`;
13284
+ var CONTEXT_CALL_REMINDER = `[CONTEXT] Call context(user_message="...") at start of EVERY response. This is MANDATORY.`;
13138
13285
  var LESSONS_REMINDER_PREFIX = `
13139
13286
  \u{1F6A8} [LESSONS_WARNING] Past Mistakes Found - READ BEFORE PROCEEDING!
13140
13287
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
@@ -16821,6 +16968,8 @@ This does semantic search on the first message. You only need context on subsequ
16821
16968
  ideRoots
16822
16969
  );
16823
16970
  result.tools_hint = getCoreToolsHint();
16971
+ const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
16972
+ result.educational_tip = getSessionInitTip(sessionId);
16824
16973
  const shouldRestoreContext = input.is_post_compact ?? RESTORE_CONTEXT_DEFAULT;
16825
16974
  if (shouldRestoreContext) {
16826
16975
  result.is_post_compact = true;
@@ -16890,15 +17039,15 @@ This does semantic search on the first message. You only need context on subsequ
16890
17039
  ...snapshotData
16891
17040
  };
16892
17041
  result.is_post_compact = true;
16893
- result.post_compact_hint = prevSessionId ? `Session restored from session ${prevSessionId}. Review 'restored_context' to continue where you left off.` : "Session restored from pre-compaction snapshot. Review the 'restored_context' to continue where you left off.";
17042
+ result.post_compact_hint = prevSessionId ? POST_COMPACT_HINTS.restored_with_session(prevSessionId) : POST_COMPACT_HINTS.restored;
16894
17043
  } else {
16895
17044
  result.is_post_compact = true;
16896
- result.post_compact_hint = "Post-compaction session started, but no snapshots found. Use context_smart to retrieve relevant context.";
17045
+ result.post_compact_hint = POST_COMPACT_HINTS.no_snapshot;
16897
17046
  }
16898
17047
  } catch (err) {
16899
17048
  logDebug(`Failed to restore post-compact context: ${err}`);
16900
17049
  result.is_post_compact = true;
16901
- result.post_compact_hint = "Post-compaction session started. Snapshot restoration failed, use context_smart for context.";
17050
+ result.post_compact_hint = POST_COMPACT_HINTS.failed;
16902
17051
  }
16903
17052
  }
16904
17053
  }
@@ -16934,7 +17083,7 @@ This does semantic search on the first message. You only need context on subsequ
16934
17083
  slack_connected: intStatus.slack,
16935
17084
  github_connected: intStatus.github,
16936
17085
  auto_hide_enabled: true,
16937
- hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : "Connect integrations at https://contextstream.io/settings/integrations to enable Slack/GitHub tools."
17086
+ hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : INTEGRATION_HINTS.not_connected + " https://contextstream.io/settings/integrations"
16938
17087
  };
16939
17088
  } catch (error) {
16940
17089
  logDebug(`Failed to check integration status: ${error}`);
@@ -17946,11 +18095,11 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
17946
18095
  workspace_id: external_exports.string().uuid().optional().describe("Workspace ID to include in rules"),
17947
18096
  project_name: external_exports.string().optional().describe("Project name to include in rules"),
17948
18097
  additional_rules: external_exports.string().optional().describe("Additional project-specific rules to append"),
17949
- mode: external_exports.enum(["minimal", "full"]).optional().describe("Rule verbosity mode (default: minimal)"),
17950
- overwrite_existing: external_exports.boolean().optional().describe("Allow overwriting existing rule files (ContextStream block only)"),
18098
+ mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional().default("bootstrap").describe("Rule verbosity: bootstrap (~15 lines, recommended), minimal (~80 lines), full (~600 lines)"),
18099
+ overwrite_existing: external_exports.boolean().optional().default(true).describe("Overwrite ContextStream block in existing rule files (default: true). User content outside the block is preserved."),
17951
18100
  apply_global: external_exports.boolean().optional().describe("Also write global rule files for supported editors"),
17952
18101
  install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks to enforce ContextStream-first search. Defaults to true for Claude users. Set to false to skip."),
17953
- include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to false."),
18102
+ include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to true."),
17954
18103
  dry_run: external_exports.boolean().optional().describe("If true, return content without writing files")
17955
18104
  })
17956
18105
  },
@@ -18041,7 +18190,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
18041
18190
  { editor, file: "~/.claude/hooks/contextstream-reminder.py", status: "dry run - would create" },
18042
18191
  { editor, file: "~/.claude/settings.json", status: "dry run - would update" }
18043
18192
  );
18044
- if (input.include_pre_compact) {
18193
+ if (input.include_pre_compact !== false) {
18045
18194
  hooksResults.push({ editor, file: "~/.claude/hooks/contextstream-precompact.py", status: "dry run - would create" });
18046
18195
  }
18047
18196
  } else if (editor === "cline") {
@@ -18072,7 +18221,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
18072
18221
  const allHookResults = await installAllEditorHooks({
18073
18222
  scope: "global",
18074
18223
  editors: hookSupportedEditors,
18075
- includePreCompact: input.include_pre_compact
18224
+ includePreCompact: input.include_pre_compact !== false
18076
18225
  });
18077
18226
  for (const result of allHookResults) {
18078
18227
  for (const file of result.installed) {
@@ -18659,6 +18808,9 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
18659
18808
  const instructionsLine = result.instructions ? `
18660
18809
 
18661
18810
  [INSTRUCTIONS] ${result.instructions}` : "";
18811
+ const contextRulesLine = `
18812
+
18813
+ ${CONTEXT_CALL_REMINDER}`;
18662
18814
  const allWarnings = [
18663
18815
  serverWarningsLine || lessonsWarningLine,
18664
18816
  // Server warnings OR client-side lesson detection
@@ -18671,6 +18823,8 @@ ${versionWarningLine}` : "",
18671
18823
  contextPressureWarning,
18672
18824
  semanticHints,
18673
18825
  instructionsLine,
18826
+ contextRulesLine,
18827
+ // Reinforce context() must be called every message
18674
18828
  searchRulesLine
18675
18829
  ].filter(Boolean).join("");
18676
18830
  const finalContext = postCompactContext + result.context;
@@ -19929,8 +20083,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19929
20083
  code_refs: input.code_refs,
19930
20084
  provenance: input.provenance
19931
20085
  });
20086
+ const captureHint = getCaptureHint(input.event_type);
20087
+ const resultWithHint = { ...result, hint: captureHint };
19932
20088
  return {
19933
- content: [{ type: "text", text: formatContent(result) }]
20089
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
19934
20090
  };
19935
20091
  }
19936
20092
  case "capture_lesson": {
@@ -19986,8 +20142,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19986
20142
  importance: input.severity === "critical" ? "critical" : input.severity === "high" ? "high" : "medium",
19987
20143
  tags: input.keywords || []
19988
20144
  });
20145
+ const lessonHint = getCaptureHint("lesson");
20146
+ const resultWithHint = { ...result, hint: lessonHint };
19989
20147
  return {
19990
- content: [{ type: "text", text: formatContent(result) }]
20148
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
19991
20149
  };
19992
20150
  }
19993
20151
  case "get_lessons": {
@@ -20000,8 +20158,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20000
20158
  context_hint: input.query,
20001
20159
  limit: input.limit
20002
20160
  });
20161
+ const lessons = result?.data?.lessons || result?.lessons || [];
20162
+ const resultWithHint = Array.isArray(lessons) && lessons.length === 0 ? { ...result, hint: getEmptyStateHint("get_lessons") } : result;
20003
20163
  return {
20004
- content: [{ type: "text", text: formatContent(result) }]
20164
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20005
20165
  };
20006
20166
  }
20007
20167
  case "recall": {
@@ -20015,7 +20175,9 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20015
20175
  include_related: input.include_related,
20016
20176
  include_decisions: input.include_decisions
20017
20177
  });
20018
- const outputText = formatContent(result);
20178
+ const recallResults = result?.data?.results || result?.results || [];
20179
+ const recallWithHint = Array.isArray(recallResults) && recallResults.length === 0 ? { ...result, hint: getEmptyStateHint("recall") } : result;
20180
+ const outputText = formatContent(recallWithHint);
20019
20181
  trackToolTokenSavings(client, "session_recall", outputText, {
20020
20182
  workspace_id: workspaceId,
20021
20183
  project_id: projectId
@@ -20035,8 +20197,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20035
20197
  content: input.content,
20036
20198
  importance
20037
20199
  });
20200
+ const rememberHint = getCaptureHint("preference");
20201
+ const resultWithHint = { ...result, hint: rememberHint };
20038
20202
  return {
20039
- content: [{ type: "text", text: formatContent(result) }]
20203
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20040
20204
  };
20041
20205
  }
20042
20206
  case "user_context": {
@@ -20149,8 +20313,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20149
20313
  source_tool: input.source_tool || "mcp",
20150
20314
  is_personal: input.is_personal
20151
20315
  });
20316
+ const planHint = getPlanStatusHint(input.status || "draft");
20317
+ const resultWithHint = { ...result, hint: planHint };
20152
20318
  return {
20153
- content: [{ type: "text", text: formatContent(result) }]
20319
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20154
20320
  };
20155
20321
  }
20156
20322
  case "get_plan": {
@@ -20180,8 +20346,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20180
20346
  tags: input.tags,
20181
20347
  due_at: input.due_at
20182
20348
  });
20349
+ const planUpdateHint = input.status ? getPlanStatusHint(input.status) : "Plan updated. Changes are preserved.";
20350
+ const resultWithHint = { ...result, hint: planUpdateHint };
20183
20351
  return {
20184
- content: [{ type: "text", text: formatContent(result) }]
20352
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20185
20353
  };
20186
20354
  }
20187
20355
  case "list_plans": {
@@ -20195,8 +20363,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20195
20363
  limit: input.limit,
20196
20364
  is_personal: input.is_personal
20197
20365
  });
20366
+ const plans = result?.data?.plans || result?.plans || result?.data?.items || result?.items || [];
20367
+ const resultWithHint = plans.length === 0 ? { ...result, hint: getEmptyStateHint("list_plans") } : result;
20198
20368
  return {
20199
- content: [{ type: "text", text: formatContent(result) }]
20369
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20200
20370
  };
20201
20371
  }
20202
20372
  case "restore_context": {
@@ -20690,8 +20860,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20690
20860
  project_id: projectId,
20691
20861
  limit: input.limit
20692
20862
  });
20863
+ const events = result?.data?.items || result?.items || result?.data || [];
20864
+ const resultWithHint = Array.isArray(events) && events.length === 0 ? { ...result, hint: getEmptyStateHint("list_events") } : result;
20693
20865
  return {
20694
- content: [{ type: "text", text: formatContent(result) }]
20866
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20695
20867
  };
20696
20868
  }
20697
20869
  case "import_batch": {
@@ -20884,8 +21056,10 @@ ${formatContent(result)}`
20884
21056
  tags: input.tags,
20885
21057
  is_personal: input.is_personal
20886
21058
  });
21059
+ const taskCreateHint = TASK_HINTS.created;
21060
+ const resultWithHint = { ...result, hint: taskCreateHint };
20887
21061
  return {
20888
- content: [{ type: "text", text: formatContent(result) }]
21062
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20889
21063
  };
20890
21064
  }
20891
21065
  case "get_task": {
@@ -20917,8 +21091,17 @@ ${formatContent(result)}`
20917
21091
  tags: input.tags,
20918
21092
  blocked_reason: input.blocked_reason
20919
21093
  });
21094
+ let taskUpdateHint = "Task updated.";
21095
+ if (input.task_status === "completed") {
21096
+ taskUpdateHint = TASK_HINTS.completed;
21097
+ } else if (input.task_status === "blocked") {
21098
+ taskUpdateHint = TASK_HINTS.blocked;
21099
+ } else if (input.task_status === "cancelled") {
21100
+ taskUpdateHint = TASK_HINTS.cancelled;
21101
+ }
21102
+ const resultWithHint = { ...result, hint: taskUpdateHint };
20920
21103
  return {
20921
- content: [{ type: "text", text: formatContent(result) }]
21104
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20922
21105
  };
20923
21106
  }
20924
21107
  case "delete_task": {
@@ -20945,8 +21128,10 @@ ${formatContent(result)}`
20945
21128
  limit: input.limit,
20946
21129
  is_personal: input.is_personal
20947
21130
  });
21131
+ const tasks = result?.data?.tasks || result?.tasks || result?.data?.items || result?.items || [];
21132
+ const resultWithHint = Array.isArray(tasks) && tasks.length === 0 ? { ...result, hint: getEmptyStateHint("list_tasks") } : result;
20948
21133
  return {
20949
- content: [{ type: "text", text: formatContent(result) }]
21134
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20950
21135
  };
20951
21136
  }
20952
21137
  case "reorder_tasks": {
@@ -20998,8 +21183,10 @@ ${formatContent(result)}`
20998
21183
  priority: input.todo_priority,
20999
21184
  is_personal: input.is_personal
21000
21185
  });
21186
+ const todos = todosResult?.data?.todos || todosResult?.todos || todosResult?.data?.items || todosResult?.items || [];
21187
+ const todosWithHint = Array.isArray(todos) && todos.length === 0 ? { ...todosResult, hint: getEmptyStateHint("list_todos") } : todosResult;
21001
21188
  return {
21002
- content: [{ type: "text", text: formatContent(todosResult) }]
21189
+ content: [{ type: "text", text: formatContent(todosWithHint) }]
21003
21190
  };
21004
21191
  }
21005
21192
  case "get_todo": {
@@ -21075,8 +21262,10 @@ ${formatContent(result)}`
21075
21262
  diagram_type: input.diagram_type,
21076
21263
  is_personal: input.is_personal
21077
21264
  });
21265
+ const diagrams = diagramsResult?.data?.diagrams || diagramsResult?.diagrams || diagramsResult?.data?.items || diagramsResult?.items || [];
21266
+ const diagramsWithHint = Array.isArray(diagrams) && diagrams.length === 0 ? { ...diagramsResult, hint: getEmptyStateHint("list_diagrams") } : diagramsResult;
21078
21267
  return {
21079
- content: [{ type: "text", text: formatContent(diagramsResult) }]
21268
+ content: [{ type: "text", text: formatContent(diagramsWithHint) }]
21080
21269
  };
21081
21270
  }
21082
21271
  case "get_diagram": {
@@ -21149,8 +21338,10 @@ ${formatContent(result)}`
21149
21338
  doc_type: input.doc_type,
21150
21339
  is_personal: input.is_personal
21151
21340
  });
21341
+ const docs = docsResult?.data?.docs || docsResult?.docs || docsResult?.data?.items || docsResult?.items || [];
21342
+ const docsWithHint = Array.isArray(docs) && docs.length === 0 ? { ...docsResult, hint: getEmptyStateHint("list_docs") } : docsResult;
21152
21343
  return {
21153
- content: [{ type: "text", text: formatContent(docsResult) }]
21344
+ content: [{ type: "text", text: formatContent(docsWithHint) }]
21154
21345
  };
21155
21346
  }
21156
21347
  case "get_doc": {
@@ -23048,14 +23239,14 @@ Content ID: ${input.content_id}`
23048
23239
  // For editor_rules
23049
23240
  folder_path: external_exports.string().optional(),
23050
23241
  editors: external_exports.array(external_exports.string()).optional(),
23051
- mode: external_exports.enum(["minimal", "full"]).optional(),
23242
+ mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional(),
23052
23243
  dry_run: external_exports.boolean().optional(),
23053
23244
  workspace_id: external_exports.string().uuid().optional(),
23054
23245
  workspace_name: external_exports.string().optional(),
23055
23246
  project_name: external_exports.string().optional(),
23056
23247
  additional_rules: external_exports.string().optional(),
23057
23248
  install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks (PreToolUse, UserPromptSubmit, PostToolUse). Default: true for Claude users."),
23058
- include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default: false."),
23249
+ include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default: true."),
23059
23250
  include_post_write: external_exports.boolean().optional().describe("Include PostToolUse hook for real-time file indexing after Edit/Write operations. Default: true."),
23060
23251
  // For enable_bundle
23061
23252
  bundle: external_exports.enum([
@@ -23112,7 +23303,7 @@ Each domain tool has an 'action' parameter for specific operations.` : "";
23112
23303
  scope: input.folder_path ? "both" : "user",
23113
23304
  projectPath: input.folder_path,
23114
23305
  dryRun: input.dry_run,
23115
- includePreCompact: input.include_pre_compact,
23306
+ includePreCompact: input.include_pre_compact !== false,
23116
23307
  includePostWrite: input.include_post_write
23117
23308
  });
23118
23309
  } catch (err) {
@@ -25845,7 +26036,7 @@ Code: ${device.user_code}`);
25845
26036
  }
25846
26037
  }
25847
26038
  }
25848
- const mode = "dynamic";
26039
+ const mode = "full";
25849
26040
  const detectedPlanName = await client.getPlanName();
25850
26041
  const detectedGraphTier = await client.getGraphTier();
25851
26042
  const graphTierLabel = detectedGraphTier === "full" ? "full graph" : detectedGraphTier === "lite" ? "graph-lite" : "none";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contextstream/mcp-server",
3
3
  "mcpName": "io.github.contextstreamio/mcp-server",
4
- "version": "0.4.48",
4
+ "version": "0.4.49",
5
5
  "description": "ContextStream MCP server - v0.4.x with consolidated domain tools (~11 tools, ~75% token reduction). Code context, memory, search, and AI tools.",
6
6
  "type": "module",
7
7
  "license": "MIT",