import { VERSION } from "./version.js";
/**
* Editor-specific rule templates for ContextStream integration.
* These instruct AI assistants to automatically use ContextStream for memory and context.
*/
export interface RuleTemplate {
filename: string;
description: string;
build: (rules: string) => string;
}
const DEFAULT_CLAUDE_MCP_SERVER_NAME = "contextstream";
export const RULES_VERSION = VERSION === "unknown" ? "0.0.0" : VERSION;
/**
* Complete list of all ContextStream MCP tools (v0.4.x consolidated architecture).
* This list is used for Claude Code prefixing and should match tools.ts exactly.
*
* v0.4.x uses consolidated domain tools (~11 tools) by default for ~75% token reduction.
*/
const CONTEXTSTREAM_TOOL_NAMES = [
// Standalone tools (always present)
"init", // Renamed from session_init - initialize conversation session
"context", // Renamed from context_smart - get relevant context every message
"context_feedback",
"generate_rules",
// Consolidated domain tools (v0.4.x default)
"search", // Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
"session", // Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
"memory", // 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
"graph", // Actions: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions
"project", // Actions: list, get, create, update, index, overview, statistics, files, index_status, ingest_local
"workspace", // Actions: list, get, associate, bootstrap
"reminder", // Actions: list, active, create, snooze, complete, dismiss
"integration", // Provider: slack, github, all; Actions: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues
"help", // Actions: tools, auth, version, editor_rules, enable_bundle
] as const;
function applyMcpToolPrefix(markdown: string, toolPrefix: string): string {
const toolPattern = CONTEXTSTREAM_TOOL_NAMES.join("|");
// Only prefix tool calls like `tool(...)` and avoid double-prefixing `mcp__<server>__tool(...)`.
const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
return markdown.replace(toolRegex, `${toolPrefix}$1`);
}
/**
* BOOTSTRAP rules - Minimal rules to ensure context() is called.
*
* Key insight: Static rules don't need to contain everything.
* They just need to be strong enough to ensure context() is called.
* Then context() delivers everything else dynamically.
*
* Target: ~40 lines. Anything more gets skimmed and ignored.
*/
const CONTEXTSTREAM_RULES_BOOTSTRAP = `
<contextstream_rules>
| Message | Required |
|---------|----------|
| **1st message** | \`init()\` ā \`context(user_message="...")\` |
| **Subsequent messages (default)** | \`context(user_message="...")\` FIRST (narrow read-only bypass when context is fresh and no state-changing tool has run) |
| **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode |
</contextstream_rules>
**Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
**Hooks:** \`<system-reminder>\` tags contain injected instructions ā follow them exactly.
**Notices:** [LESSONS_WARNING] ā apply lessons | [PREFERENCE] ā follow user preferences | [RULES_NOTICE] ā run \`generate_rules()\` | [VERSION_NOTICE/CRITICAL] ā tell user about update
v${RULES_VERSION}
`.trim();
/**
* Dynamic rules - relies on context() for dynamic rule delivery PLUS hooks for enforcement.
* Kept for backwards compatibility but BOOTSTRAP is now preferred.
*/
const CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
const CONTEXTSTREAM_RULES_FULL = `
## šØ MANDATORY STARTUP: CONTEXT-FIRST FLOW šØ
<contextstream_rules>
| Message | What to Call |
|---------|--------------|
| **First message in session** | \`init()\` ā \`context(user_message="<msg>")\` BEFORE any other tool |
| **Subsequent messages (default)** | \`context(user_message="<msg>")\` FIRST, then other tools |
| **Narrow bypass** | Immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run |
| **Before Glob/Grep/Read/Search/Explore/Task/EnterPlanMode** | \`search(mode="auto", query="...")\` FIRST |
</contextstream_rules>
Use \`context()\` by default to get task-specific rules, lessons from past mistakes, and relevant decisions.
---
## Why Default Context-First
ā **Wrong:** "I already called init, so I can skip context for everything"
ā
**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
**What you lose without \`context()\`:**
- Dynamic rules matched to your current task
- Lessons from past mistakes (you WILL repeat them)
- Semantically relevant decisions and context
- Warnings about risky operations
**\`init()\` returns recent items by time. \`context()\` finds items semantically relevant to this message.**
---
## Handle Notices from context()
- **[LESSONS_WARNING]** ā Tell user about past mistakes BEFORE proceeding
- **[PREFERENCE]** ā Follow user preferences (high-priority user memories)
- **[RULES_NOTICE]** ā Run \`generate_rules()\` to update
- **[VERSION_NOTICE]** ā Tell user to update MCP
---
## šØ HOOKS - AUTOMATIC RULE ENFORCEMENT šØ
**ContextStream installs hooks that automatically enforce rules.** You MUST follow hook output.
### Installed Hooks
| Hook | What It Does | Output |
|------|--------------|--------|
| **UserPromptSubmit** | Injects rules reminder on EVERY message | \`<system-reminder>\` with rules block |
| **PreToolUse** | Blocks Glob/Grep/Search/Explore when ContextStream is available | Error message redirecting to \`search()\` |
| **PostToolUse** | Auto-indexes files after Edit/Write operations | Background indexing |
| **PreCompact** | Saves session state before context compaction | Snapshot creation |
### How Hooks Work
1. **\`<system-reminder>\` tags** - Injected by UserPromptSubmit hook on every message
- These tags contain the current rules
- **FOLLOW THE INSTRUCTIONS INSIDE** - they ARE the rules
- Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
- **You MUST use the suggested ContextStream tool instead**
- Local tools are only allowed if project is not indexed or ContextStream returns 0 results
3. **PostToolUse indexing** - After Edit/Write operations:
- Changed files are automatically re-indexed
- No action required from you
4. **PreCompact snapshots** - Before context compaction:
- Hook reminds you to save important state
- Call \`session(action="capture", event_type="session_snapshot", ...)\` when warned
### Disabling Hooks
Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
**Note:** Disabling hooks removes rule enforcement. Only disable for debugging.
---
## šØ CRITICAL RULE #1 - CONTEXTSTREAM SEARCH FIRST šØ
**BEFORE using Glob, Grep, Search, Read (for discovery), Explore, Task(Explore), EnterPlanMode, or ANY local file scanning:**
\`\`\`
STOP ā Call search(mode="auto", query="...") FIRST
\`\`\`
**Note:** PreToolUse hooks block these tools when ContextStream is available.
**Claude Code users:** Your tool names are \`mcp__contextstream__search\`, \`mcp__contextstream__init\`, etc.
ā **NEVER DO THIS:**
- \`Glob("**/*.ts")\` ā Use \`search(mode="pattern", query="*.ts")\` instead
- \`Grep("functionName")\` ā Use \`search(mode="keyword", query="functionName")\` instead
- \`Read(file)\` for discovery ā Use \`search(mode="auto", query="...")\` instead
- \`Explore\` or \`Task(subagent_type="Explore")\` ā Use \`search(mode="auto")\` instead
- \`EnterPlanMode\` for discovery ā Use \`search(mode="auto", output_format="paths")\` instead
ā
**ALWAYS DO THIS:**
1. \`search(mode="auto", query="what you're looking for")\`
2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
3. Use Read ONLY for exact file edits after you know the file path
This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
---
## šØ CRITICAL RULE #2 - AUTO-INDEXING šØ
**ContextStream auto-indexes your project on \`init\`.** You do NOT need to:
- Ask the user to index
- Manually trigger ingestion
- Check index_status before every search
**When \`init\` returns \`indexing_status: "started"\` or \`"refreshing"\`:**
- Background indexing is running automatically
- Search results will be available within seconds to minutes
- **DO NOT fall back to local tools** - wait for ContextStream search to work
- If search returns 0 results initially, try again after a moment
**Only manually trigger indexing if:**
- \`init\` returned \`ingest_recommendation.recommended: true\` (rare edge case)
- User explicitly asks to re-index
---
## šØ CRITICAL RULE #3 - LESSONS (PAST MISTAKES) šØ
**Lessons are past mistakes that MUST inform your work.** Ignoring lessons leads to repeated failures.
### On \`init\`:
- Check for \`lessons\` and \`lessons_warning\` in the response
- If present, **READ THEM IMMEDIATELY** before doing any work
- These are high-priority lessons (critical/high severity) relevant to your context
- **Apply the prevention steps** from each lesson to avoid repeating mistakes
### On \`context\`:
- Check for \`[LESSONS_WARNING]\` tag in the response
- If present, you **MUST** tell the user about the lessons before proceeding
- Lessons are proactively fetched when risky actions are detected (refactor, migrate, deploy, etc.)
- **Do not skip or bury this warning** - lessons represent real past mistakes
### Before ANY Non-Trivial Work:
**ALWAYS call \`session(action="get_lessons", query="<topic>")\`** where \`<topic>\` matches what you're about to do:
- Before refactoring ā \`session(action="get_lessons", query="refactoring")\`
- Before API changes ā \`session(action="get_lessons", query="API changes")\`
- Before database work ā \`session(action="get_lessons", query="database migrations")\`
- Before deployments ā \`session(action="get_lessons", query="deployment")\`
### When Lessons Are Found:
1. **Summarize the lessons** to the user before proceeding
2. **Explicitly state how you will avoid the past mistakes**
3. If a lesson conflicts with the current approach, **warn the user**
**Failing to check lessons before risky work is a critical error.**
---
## ContextStream v0.4.x Integration (Enhanced)
You have access to ContextStream MCP tools for persistent memory and context.
v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
Rules Version: ${RULES_VERSION}
## TL;DR - CONTEXT EVERY MESSAGE
| Message | Required |
|---------|----------|
| **1st message** | \`init()\` ā \`context(user_message="<msg>")\` |
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
| **Before file search** | \`search(mode="auto")\` FIRST |
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
| **User correction** | \`session(action="capture_lesson", ...)\` |
### Why EVERY Message?
\`context()\` delivers:
- **Dynamic rules** matched to your current task
- **Lessons** from past mistakes (prevents repeating errors)
- **Relevant decisions** and context (semantic search)
- **Warnings** about risky operations
**Without \`context()\`, you are blind to relevant context and will repeat past mistakes.**
### Protocol
| Step | What to Call |
|------|--------------|
| **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
| **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
| **Code search** | \`search(mode="auto", query="...")\` ā BEFORE Glob/Grep/Read |
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
| **User correction** | \`session(action="capture_lesson", ...)\` |
| **ā ļø When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
**First message rule:** After \`init\`:
1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
2. Then call \`context\` before any other tool or response
**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).
**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.
---
## Consolidated Domain Tools Architecture
v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode dispatch:
### Standalone Tools
- **\`init\`** - Initialize session with workspace detection + context (skip for simple utility operations)
- **\`context\`** - Semantic search for relevant context (skip for simple utility operations)
### Domain Tools (Use action/mode parameter)
| Domain | Actions/Modes | Example |
|--------|---------------|---------|
| **\`search\`** | mode: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern | \`search(mode="auto", query="auth implementation", limit=3)\` |
| **\`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="...")\` |
| **\`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)\` |
| **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
| **\`project\`** | action: list, get, create, update, index, overview, statistics, files, index_status, ingest_local | \`project(action="statistics")\` |
| **\`workspace\`** | action: list, get, associate, bootstrap | \`workspace(action="list")\` |
| **\`reminder\`** | action: list, active, create, snooze, complete, dismiss | \`reminder(action="active")\` |
| **\`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="...")\` |
| **\`help\`** | action: tools, auth, version, editor_rules, enable_bundle | \`help(action="tools")\` |
---
### Why context is Required (Even After init)
**Common mistake:** "init already gave me context, I don't need context"
**This is WRONG. Here's why:**
- \`init\` returns the last ~10 items **BY TIME** (chronological)
- \`context\` **SEARCHES** for items **RELEVANT to THIS message** (semantic)
**Example failure:**
- User asks: "how should I implement authentication?"
- Auth decisions were made 20 conversations ago
- \`init\` won't have it (too old, not in recent 10)
- \`context\` FINDS it via semantic search
**Without context, you WILL miss relevant older context.**
---
### Recommended Token Budgets
- For trivial/local edits: \`context(..., max_tokens=200)\`
- Default: \`context(..., max_tokens=400)\`
- Deep debugging/architecture: \`context(..., max_tokens=800)\`
- Keep \`format="minified"\` (default) unless debugging
If context still feels missing, use \`session(action="recall", query="...")\` for focused deep lookup.
---
### Rules, Version & Lessons Notices
**[RULES_NOTICE]** - Update rules via \`generate_rules()\` (or rerun setup).
**[VERSION_NOTICE]** or **[VERSION_CRITICAL]** - You **MUST** tell the user (be helpful, not annoying):
1. A new MCP server version is available with improvements
2. Provide update commands (user chooses preferred method):
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
- npm: \`npm install -g @contextstream/mcp-server@latest\`
3. Restart AI tool after updating
**Mention once** - don't nag. Users often miss stderr warnings so this helps.
**[LESSONS_WARNING]** - You **MUST** before proceeding:
1. Read all lessons listed
2. Tell the user about relevant lessons
3. Explain how you will avoid each past mistake
**This is critical** - ignoring lessons leads to repeated failures.
---
### Preferences & Lessons (Use Early)
**Preferences ([PREFERENCE] in context response):**
- High-priority user memories that should guide your behavior
- Surfaced automatically via \`context()\` warnings field
- To save: \`session(action="remember", content="...")\`
- To retrieve explicitly: \`session(action="user_context")\`
**Lessons ([LESSONS_WARNING] in context response):**
- Past mistakes to avoid - apply prevention steps
- Surfaced automatically via \`context()\` warnings field
- Before risky changes: \`session(action="get_lessons", query="<topic>")\`
- On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
---
### Context Pressure & Compaction Awareness
ContextStream tracks context pressure to help you stay ahead of conversation compaction:
**Automatic tracking:** Token usage is tracked automatically. \`context\` returns \`context_pressure\` when usage is high.
**When \`context\` returns \`context_pressure\` with high/critical level:**
1. Review the \`suggested_action\` field:
- \`prepare_save\`: Start thinking about saving important state
- \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
**PreCompact Hook:** Automatically saves session state before context compaction.
Installed by default. Disable with: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
**Before compaction happens (when warned):**
\`\`\`
session(action="capture", event_type="session_snapshot", title="Pre-compaction snapshot", content="{
\\"conversation_summary\\": \\"<summarize what we've been doing>\\",
\\"current_goal\\": \\"<the main task>\\",
\\"active_files\\": [\\"file1.ts\\", \\"file2.ts\\"],
\\"recent_decisions\\": [{title: \\"...\\", rationale: \\"...\\"}],
\\"unfinished_work\\": [{task: \\"...\\", status: \\"...\\", next_steps: \\"...\\"}]
}")
\`\`\`
**After compaction (when context seems lost):**
1. Call \`init(folder_path="...", is_post_compact=true)\` - this auto-restores the most recent snapshot
2. Or call \`session_restore_context()\` directly to get the saved state
3. Review the \`restored_context\` to understand prior work
4. Acknowledge to the user what was restored and continue
---
### Index Status (Auto-Managed)
**Indexing is automatic.** After \`init\`, the project is auto-indexed in the background.
**You do NOT need to manually check index_status before every search.** Just use \`search()\`.
**If search returns 0 results and you expected matches:**
1. Check if \`init\` returned \`indexing_status: "started"\` - indexing may still be in progress
2. Wait a moment and retry \`search()\`
3. Only as a last resort: \`project(action="index_status")\` to check
**Graph data:** If graph queries (\`dependencies\`, \`impact\`) return empty, run \`graph(action="ingest")\` once.
**NEVER fall back to local tools (Glob/Grep/Read) just because search returned 0 results on first try.** Retry first.
### Enhanced Context (Server-Side Warnings)
\`context\` now includes **intelligent server-side filtering** that proactively surfaces relevant warnings:
**Response fields:**
- \`warnings\`: Array of warning strings (displayed with ā ļø prefix)
**What triggers warnings:**
- **Lessons**: Past mistakes relevant to the current query (via semantic matching)
- **Risky actions**: Detected high-risk operations (deployments, migrations, destructive commands)
- **Breaking changes**: When modifications may impact other parts of the codebase
**When you receive warnings:**
1. **STOP** and read each warning carefully
2. **Acknowledge** the warning to the user
3. **Explain** how you will avoid the issue
4. Only proceed after addressing the warnings
### Search & Code Intelligence (ContextStream-first)
ā ļø **STOP: Before using Search/Glob/Grep/Read/Explore** ā Call \`search(mode="auto")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
**ā WRONG workflow (wastes tokens, slow):**
\`\`\`
Grep "function" ā Read file1.ts ā Read file2.ts ā Read file3.ts ā finally understand
\`\`\`
**ā
CORRECT workflow (fast, complete):**
\`\`\`
search(mode="auto", query="function implementation") ā done (results include context)
\`\`\`
**Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
**Search order:**
1. \`session(action="smart_search", query="...")\` - context-enriched
2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
3. \`project(action="files")\` - file tree/list (only when needed)
4. \`graph(action="dependencies", ...)\` - code structure
5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
**Search Mode Selection:**
| Need | Mode | Example |
|------|------|---------|
| Find code by meaning | \`auto\` | "authentication logic", "error handling" |
| Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
| File patterns | \`pattern\` | "*.sql", "test_*.py" |
| ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
| Symbol renaming | \`refactor\` | "oldFunctionName" (word-boundary matching) |
| Conceptual search | \`semantic\` | "how does caching work" |
**Token Efficiency:** Use \`output_format\` to reduce response size:
- \`full\` (default): Full content for understanding code
- \`paths\`: File paths only (80% token savings) - use for file listings
- \`minimal\`: Compact format (60% savings) - use for refactoring
- \`count\`: Match counts only (90% savings) - use for quick checks
**When to use \`output_format=count\`:**
- User asks "how many X" or "count of X" ā \`search(..., output_format="count")\`
- Checking if something exists ā count > 0 is sufficient
- Large exhaustive searches ā get count first, then fetch if needed
**Auto-suggested formats:** Search responses include \`query_interpretation.suggested_output_format\` when the API detects an optimal format:
- Symbol queries (e.g., "authOptions") ā suggests \`minimal\` (path + line + snippet)
- Count queries (e.g., "how many") ā suggests \`count\`
**USE the suggested format** on subsequent searches for best token efficiency.
**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.
If ContextStream returns results, stop and use them. NEVER use local Search/Explore/Read unless you need exact code edits or ContextStream returned 0 results.
**Code Analysis:**
- Dependencies: \`graph(action="dependencies", file_path="...")\`
- Change impact: \`graph(action="impact", symbol_name="...")\`
- Call path: \`graph(action="call_path", from_symbol="...", to_symbol="...")\`
- Build graph: \`graph(action="ingest")\` - async, can take a few minutes
---
### Distillation & Memory Hygiene
- Quick context: \`session(action="summary")\`
- Long chat: \`session(action="compress", content="...")\`
- Memory summary: \`memory(action="summary")\`
- Condense noisy entries: \`memory(action="distill_event", event_id="...")\`
---
### When to Capture
| When | Call | Example |
|------|------|---------|
| User makes decision | \`session(action="capture", event_type="decision", ...)\` | "Let's use PostgreSQL" |
| User states preference | \`session(action="capture", event_type="preference", ...)\` | "I prefer TypeScript" |
| Complete significant task | \`session(action="capture", event_type="task", ...)\` | Capture what was done |
| Need past context | \`session(action="recall", query="...")\` | "What did we decide about X?" |
**DO NOT capture utility operations:**
- ā "Listed workspaces" - not meaningful context
- ā "Showed version" - not a decision
- ā "Listed projects" - just data retrieval
**DO capture meaningful work:**
- ā
Decisions, preferences, completed features
- ā
Lessons from mistakes
- ā
Insights about architecture or patterns
---
### šØ Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS šØ
**CRITICAL: When the user requests planning, implementation plans, roadmaps, task breakdowns, or step-by-step approaches:**
ā **DO NOT** use built-in plan mode (EnterPlanMode tool)
ā **DO NOT** write plans to markdown files or plan documents
ā **DO NOT** ask "should I create a plan file?"
ā **DO NOT** use \`Explore\` / \`Task(subagent_type="Explore")\` to read files one-by-one while planning
ā
**ALWAYS** use ContextStream's plan/task system instead
ā
**ALWAYS** use \`search(mode="auto", output_format="paths")\` for planning discovery before targeted reads
**Trigger phrases to detect (use ContextStream immediately):**
- "create a plan", "make a plan", "plan this", "plan for"
- "implementation plan", "roadmap", "milestones"
- "break down", "breakdown", "break this into steps"
- "what are the steps", "step by step", "outline the approach"
- "task list", "todo list", "action items"
- "how should we approach", "implementation strategy"
**When detected, immediately:**
1. **Create the plan in ContextStream:**
\`\`\`
session(action="capture_plan", title="<descriptive title>", description="<what this plan accomplishes>", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1, description: "..."}, ...])
\`\`\`
2. **Create tasks for each step:**
\`\`\`
memory(action="create_task", title="<task title>", plan_id="<plan_id from step 1>", priority="high|medium|low", description="<detailed task description>")
\`\`\`
**Why ContextStream plans are better:**
- Plans persist across sessions and are searchable
- Tasks track status (pending/in_progress/completed/blocked)
- Context is preserved with workspace/project association
- Can be retrieved with \`session(action="get_plan", plan_id="...", include_tasks=true)\`
- Future sessions can continue from where you left off
**Managing plans/tasks:**
- List plans: \`session(action="list_plans")\`
- Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
- List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
- Update task status: \`memory(action="update_task", task_id="<uuid>", task_status="pending|in_progress|completed|blocked")\`
- Link task to plan: \`memory(action="update_task", task_id="<uuid>", plan_id="<plan_uuid>")\`
- Unlink task from plan: \`memory(action="update_task", task_id="<uuid>", plan_id=null)\`
- Delete: \`memory(action="delete_task", task_id="<uuid>")\` or \`memory(action="delete_event", event_id="<plan_uuid>")\`
---
### Complete Action Reference
**session actions:**
- \`capture\` - Save decision/insight/task (requires: event_type, title, content)
- \`capture_lesson\` - Save lesson from mistake (requires: title, category, trigger, impact, prevention)
- \`get_lessons\` - Retrieve relevant lessons (optional: query, category, severity)
- \`recall\` - Natural language memory recall (requires: query)
- \`remember\` - Quick save to memory (requires: content)
- \`user_context\` - Get user preferences/style
- \`summary\` - Workspace summary
- \`compress\` - Compress long conversation
- \`delta\` - Changes since timestamp
- \`smart_search\` - Context-enriched search
- \`decision_trace\` - Trace decision provenance
**memory actions:**
- Event CRUD: \`create_event\`, \`get_event\`, \`update_event\`, \`delete_event\`, \`list_events\`, \`distill_event\`
- Node CRUD: \`create_node\`, \`get_node\`, \`update_node\`, \`delete_node\`, \`list_nodes\`, \`supersede_node\`
- Query: \`search\`, \`decisions\`, \`timeline\`, \`summary\`
**graph actions:**
- Analysis: \`dependencies\`, \`impact\`, \`call_path\`, \`related\`, \`path\`
- Quality: \`circular_dependencies\`, \`unused_code\`, \`contradictions\`
- Management: \`ingest\`, \`decisions\`
See full documentation: https://contextstream.io/docs/mcp/tools
`.trim();
const CONTEXTSTREAM_RULES_MINIMAL = `
# ContextStream Rules (MANDATORY)
## šØ CRITICAL: These Rules Are Non-Negotiable šØ
<contextstream_protocol>
**MANDATORY STARTUP:**
1. First message in session: \`init()\` ā then \`context(user_message="<msg>")\`
2. Subsequent messages (default): \`context(user_message="<msg>")\` FIRST
3. Narrow bypass: immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run
**BEFORE Glob/Grep/Read/Search/Explore/Task/EnterPlanMode:**
ā \`search(mode="auto", query="...")\` FIRST ā local tools ONLY if 0 results
**HOOKS: \`<system-reminder>\` tags contain instructions ā FOLLOW THEM**
</contextstream_protocol>
Rules Version: ${RULES_VERSION}
---
## Why ContextStream First?
| ā WRONG | ā
CORRECT |
|----------|-----------|
| \`Grep ā Read ā Read ā Read\` (4+ calls) | \`search(mode="auto")\` (1 call) |
| Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
| Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
**\`context()\` delivers: rules, lessons from past mistakes, relevant decisions, semantic matches**
**\`search()\` is indexed: faster, returns context, one call vs many**
**Hooks inject \`<system-reminder>\` tags: these ARE the rules, follow them**
---
## Protocol
| When | Call |
|------|------|
| 1st message in session | \`init()\` ā \`context(user_message="...")\` |
| Subsequent messages (default) | \`context(user_message="...")\` first |
| Narrow bypass | Immediate read-only ContextStream calls when context is fresh and no state-changing tool has run |
| Before ANY file discovery | \`search(mode="auto", query="...")\` (instead of Glob/Grep/Read/Explore/Task/EnterPlanMode) |
| On \`<system-reminder>\` | **Follow instructions inside** |
| Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
| Check past mistakes | \`session(action="get_lessons", query="...")\` |
## Search Modes
| Mode | When |
|------|------|
| \`auto\` | Default ā semantic + keyword |
| \`hybrid\` | Legacy alias for auto |
| \`keyword\` | Exact symbol match |
| \`exhaustive\` | Find ALL occurrences |
| \`semantic\` | Conceptual questions |
## Handle Notices from context()
- **[LESSONS_WARNING]** ā Tell user about past mistakes BEFORE proceeding
- **[PREFERENCE]** ā Follow user preferences (high-priority user memories)
- **[RULES_NOTICE]** ā Run \`generate_rules()\`
- **[VERSION_NOTICE]** ā Tell user to update MCP
## Read-Only Examples
Default behavior is context-first. Narrow bypass applies only for immediate read-only ContextStream calls when context is fresh and state is unchanged.
Examples: \`workspace(action="list"|"get")\`, \`help(action="version"|"tools"|"auth")\`, \`project(action="index_status")\`.
### Lessons (Past Mistakes)
- After \`init\`: Check for \`lessons\` field and apply before work
- Before risky work: \`session(action="get_lessons", query="<topic>")\`
- On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
### Context Pressure & Compaction
- If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
- PreCompact hooks automatically save snapshots before compaction
### Enhanced Context (Warnings)
\`context\` returns server-side \`warnings\` for lessons, risky actions, and breaking changes.
When warnings are present: **STOP**, acknowledge them, explain mitigation, then proceed.
### Automatic Context Restoration
**Context restoration is now enabled by default.** Every \`init\` call automatically:
- Restores context from recent snapshots (if available)
- Returns \`restored_context\` field with snapshot data
- Sets \`is_post_compact=true\` in response when restoration occurs
**No special handling needed after compaction** - just call \`init\` normally.
To disable automatic restoration:
- Pass \`is_post_compact=false\` in the API call
- Or set \`CONTEXTSTREAM_RESTORE_CONTEXT=false\` environment variable
### Notices - MUST HANDLE IMMEDIATELY
- **[VERSION_NOTICE]** or **[VERSION_CRITICAL]**: Tell user about the update in a helpful, non-annoying way. Provide update commands:
- macOS/Linux: \`curl -fsSL https://contextstream.io/scripts/setup.sh | bash\`
- Windows: \`irm https://contextstream.io/scripts/setup.ps1 | iex\`
- npm: \`npm install -g @contextstream/mcp-server@latest\`
- **[RULES_NOTICE]**: Run \`generate_rules(overwrite_existing=true)\` to update
- **[LESSONS_WARNING]**: Read lessons, tell user about them, explain how you'll avoid past mistakes
- **[PREFERENCE]**: Follow user preferences - these are high-priority user memories that should guide your behavior
### Plans & Tasks
When user asks for a plan, use ContextStream (not EnterPlanMode):
1. \`session(action="capture_plan", title="...", steps=[...])\`
2. \`memory(action="create_task", title="...", plan_id="<id>")\`
### Workspace-Only Mode (Multi-Project Folders)
If working in a parent folder containing multiple projects:
\`\`\`
init(folder_path="...", skip_project_creation=true)
\`\`\`
This enables workspace-level memory and context without project-specific indexing.
Use for monorepos or folders with multiple independent projects.
Full docs: https://contextstream.io/docs/mcp/tools
`.trim();
/**
* NO-HOOKS SUPPLEMENT - Additional guidance for editors without hooks support.
*
* Editors like Codex, Aider, and Antigravity don't have hooks to enforce behavior,
* so the rules file must contain explicit guidance for everything hooks would do:
* - Session initialization (SessionStart hook)
* - File indexing after changes (PostToolUse hook)
* - Context compaction awareness (PreCompact hook)
* - Search-first enforcement (PreToolUse hook)
*/
const NO_HOOKS_SUPPLEMENT = `
---
## ā ļø IMPORTANT: No Hooks Available ā ļø
**This editor does NOT have hooks to enforce ContextStream behavior.**
You MUST follow these rules manually - there is no automatic enforcement.
---
## š SESSION START PROTOCOL
**On EVERY new session, you MUST:**
1. **Call \`init(folder_path="<project_path>")\`** FIRST
- This triggers project indexing
- Check response for \`indexing_status\`
- If \`"started"\` or \`"refreshing"\`: wait before searching
2. **Generate a unique session_id** (e.g., \`"session-" + timestamp\` or a UUID)
- Use this SAME session_id for ALL context() calls in this conversation
- This groups all turns together in the transcript
3. **Call \`context(user_message="<first_message>", save_exchange=true, session_id="<your-session-id>")\`**
- Gets task-specific rules, lessons, and preferences
- Check for [LESSONS_WARNING] - past mistakes to avoid
- Check for [PREFERENCE] - user preferences to follow
- Check for [RULES_NOTICE] - update rules if needed
- **save_exchange=true** saves each conversation turn for later retrieval
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.
---
## š¾ AUTOMATIC TRANSCRIPT SAVING (CRITICAL)
**This editor does NOT have hooks to auto-save transcripts.**
You MUST save each conversation turn manually:
### On MOST messages (including the first):
\`\`\`
context(user_message="<user's message>", save_exchange=true, session_id="<session-id>")
\`\`\`
### Why save_exchange matters:
- Transcripts enable searching past conversations
- Allows context restoration after compaction
- Provides conversation history for debugging
- Required for the Transcripts page in the dashboard
### Session ID Guidelines:
- Generate ONCE at the start of the conversation
- Use a unique identifier: \`"session-" + Date.now()\` or a UUID
- Keep the SAME session_id for ALL context() calls in this session
- Different sessions = different transcripts
---
## š FILE INDEXING (CRITICAL)
**There is NO automatic file indexing in this editor.**
You MUST manage indexing manually:
### After Creating/Editing Files:
\`\`\`
project(action="index") # Re-index entire project
\`\`\`
### For Single File Updates:
\`\`\`
project(action="ingest_local", path="<file_path>")
\`\`\`
### Signs You Need to Re-index:
- Search doesn't find code you just wrote
- Search returns old versions of functions
- New files don't appear in search results
### Best Practice:
After completing a feature or making multiple file changes, ALWAYS run:
\`\`\`
project(action="index")
\`\`\`
---
## š SEARCH-FIRST (No PreToolUse Hook)
**There is NO hook to block local tools (Glob/Grep/Read/Explore/Task/EnterPlanMode).** You MUST self-enforce:
### Before ANY Search, Check Index Status:
\`\`\`
project(action="index_status")
\`\`\`
This tells you:
- \`indexed\`: true/false - is project indexed?
- \`last_indexed_at\`: timestamp - when was it last indexed?
- \`file_count\`: number - how many files indexed?
### Search Protocol:
**IF project is indexed and fresh:**
\`\`\`
search(mode="auto", query="what you're looking for")
\`\`\`
ā Use this instead of Explore/Task/EnterPlanMode for file discovery.
**IF project is NOT indexed or very stale (>7 days):**
ā Use local tools (Glob/Grep/Read) directly
ā OR run \`project(action="index")\` first, then search
**IF ContextStream search returns 0 results or errors:**
ā Use local tools (Glob/Grep/Read) as fallback
### Choose Search Mode Intelligently:
- \`auto\` (recommended): query-aware mode selection
- \`hybrid\`: mixed semantic + keyword retrieval for broad discovery
- \`semantic\`: conceptual questions ("how does X work?")
- \`keyword\`: exact text / quoted string
- \`pattern\`: glob or regex (\`*.ts\`, \`foo\\s+bar\`)
- \`refactor\`: symbol usage / rename-safe lookup
- \`exhaustive\`: all occurrences / complete match coverage
- \`team\`: cross-project team search
### Output Format Hints:
- Use \`output_format="paths"\` for file listings and rename targets
- Use \`output_format="count"\` for "how many" queries
### Two-Phase Search Pattern (for precision):
- Pass 1 (discovery): \`search(mode="auto", query="<concept + module>", output_format="paths", limit=10)\`
- Pass 2 (precision): use one of:
- exact text/symbol: \`search(mode="keyword", query="\\"exact_text\\"", include_content=true)\`
- symbol usage: \`search(mode="refactor", query="SymbolName", output_format="paths")\`
- all occurrences: \`search(mode="exhaustive", query="symbol_or_text")\`
- Then use local Read/Grep only on paths returned by ContextStream.
### When Local Tools Are OK:
ā
Project is not indexed
ā
Index is stale/outdated (>7 days old)
ā
ContextStream search returns 0 results
ā
ContextStream returns errors
ā
User explicitly requests local tools
### When to Use ContextStream Search:
ā
Project is indexed and fresh
ā
Looking for code by meaning/concept
ā
Need semantic understanding
---
## š¾ CONTEXT COMPACTION (No PreCompact Hook)
**There is NO automatic state saving before compaction.**
You MUST save state manually when the conversation gets long:
### When to Save State:
- After completing a major task
- Before the conversation might be compacted
- If \`context()\` returns \`context_pressure.level: "high"\`
### How to Save State:
\`\`\`
session(action="capture", event_type="session_snapshot",
title="Session checkpoint",
content="{ \\"summary\\": \\"what we did\\", \\"active_files\\": [...], \\"next_steps\\": [...] }")
\`\`\`
### After Compaction (if context seems lost):
\`\`\`
init(folder_path="...", is_post_compact=true)
\`\`\`
This restores the most recent snapshot.
---
## š PLANS & TASKS (No EnterPlanMode)
**Always use ContextStream for planning:**
\`\`\`
session(action="capture_plan", title="...", steps=[...])
memory(action="create_task", title="...", plan_id="...")
\`\`\`
ā DO NOT use built-in plan mode (\`EnterPlanMode\`) or \`Task(subagent_type="Explore")\` for file-by-file scans.
ā
For planning discovery, use \`search(mode="auto", query="...", output_format="paths")\` then read only narrowed files.
---
## š VERSION UPDATES (Check Periodically)
**This editor does NOT have hooks to check for updates automatically.**
You should check for updates using \`help(action="version")\` periodically (e.g., at session start).
### If the response includes [VERSION_NOTICE] or [VERSION_CRITICAL]:
**Tell the user** about the available update in a helpful, non-annoying way:
- Frame it as "new features and improvements available"
- Provide the update commands (user can choose their preferred method)
- Don't nag repeatedly - mention once, then only if user asks
### Update Commands (provide all options):
**macOS/Linux:**
\`\`\`bash
curl -fsSL https://contextstream.io/scripts/setup.sh | bash
\`\`\`
**Windows (PowerShell):**
\`\`\`powershell
irm https://contextstream.io/scripts/setup.ps1 | iex
\`\`\`
**npm (requires Node.js 18+):**
\`\`\`bash
npm install -g @contextstream/mcp-server@latest
\`\`\`
After updating, user should restart their AI tool.
---
`;
// Editors that don't have hooks support and need enhanced rules
const NO_HOOKS_EDITORS = ["codex", "aider", "antigravity"];
export const TEMPLATES: Record<string, RuleTemplate> = {
codex: {
filename: "AGENTS.md",
description: "Codex CLI agent instructions",
build: (rules) => `# Codex CLI Instructions
${rules}
`,
},
cursor: {
filename: ".cursorrules",
description: "Cursor AI rules",
build: (rules) => `# Cursor Rules
${rules}
`,
},
cline: {
filename: ".clinerules",
description: "Cline AI rules",
build: (rules) => `# Cline Rules
${rules}
`,
},
kilo: {
filename: ".kilocode/rules/contextstream.md",
description: "Kilo Code AI rules",
build: (rules) => `# Kilo Code Rules
${rules}
`,
},
roo: {
filename: ".roo/rules/contextstream.md",
description: "Roo Code AI rules",
build: (rules) => `# Roo Code Rules
${rules}
`,
},
claude: {
filename: "CLAUDE.md",
description: "Claude Code instructions",
build: (rules) => `# Claude Code Instructions
${rules}
`,
},
aider: {
filename: ".aider.conf.yml",
description: "Aider configuration with system prompt",
build: (rules) => `# Aider Configuration
# Note: Aider uses different config format - this adds to the system prompt
# Add ContextStream guidance to conventions
conventions: |
${rules
.split("\n")
.map((line) => " " + line)
.join("\n")}
`,
},
antigravity: {
filename: "GEMINI.md",
description: "Google Antigravity AI rules",
build: (rules) => `# Antigravity Agent Rules
${rules}
`,
},
};
/**
* Get all available editor types
*/
export function getAvailableEditors(): string[] {
return Object.keys(TEMPLATES);
}
/**
* Get template for a specific editor
*/
export function getTemplate(editor: string): RuleTemplate | null {
return TEMPLATES[editor.toLowerCase()] || null;
}
/**
* Generate rule content with workspace-specific customizations
*/
export function generateRuleContent(
editor: string,
options?: {
workspaceName?: string;
workspaceId?: string;
projectName?: string;
additionalRules?: string;
mode?: "dynamic" | "minimal" | "full" | "bootstrap";
}
): { filename: string; content: string } | null {
const template = getTemplate(editor);
if (!template) return null;
// Default to "bootstrap" mode - minimal but effective rules that ensure context() is called
// Full rules are delivered dynamically via context()
const mode = options?.mode || "bootstrap";
const rules = mode === "full"
? CONTEXTSTREAM_RULES_FULL
: mode === "minimal"
? CONTEXTSTREAM_RULES_MINIMAL
: mode === "bootstrap"
? CONTEXTSTREAM_RULES_BOOTSTRAP
: CONTEXTSTREAM_RULES_DYNAMIC;
let content = template.build(rules);
// Add workspace header if provided
if (options?.workspaceName || options?.projectName) {
const header = `
# Workspace: ${options.workspaceName || "Unknown"}
${options.projectName ? `# Project: ${options.projectName}` : ""}
${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
`;
content = header + content;
}
// Add NO_HOOKS_SUPPLEMENT for editors without hooks support
// These editors need explicit guidance since there's no enforcement mechanism
if (NO_HOOKS_EDITORS.includes(editor.toLowerCase())) {
content += NO_HOOKS_SUPPLEMENT;
}
// Append additional rules if provided
if (options?.additionalRules) {
content += "\n\n## Project-Specific Rules\n\n" + options.additionalRules;
}
// Claude Code requires `mcp__<server>__<tool>` naming convention for MCP tools.
// Other MCP clients typically use raw tool names.
if (editor.toLowerCase() === "claude") {
content = applyMcpToolPrefix(content, `mcp__${DEFAULT_CLAUDE_MCP_SERVER_NAME}__`);
}
return {
filename: template.filename,
content: content.trim() + "\n",
};
}
/**
* Generate all rule files for a project
*/
export function generateAllRuleFiles(options?: {
workspaceName?: string;
workspaceId?: string;
projectName?: string;
additionalRules?: string;
mode?: "dynamic" | "minimal" | "full" | "bootstrap";
}): Array<{ editor: string; filename: string; content: string }> {
return getAvailableEditors()
.map((editor) => {
const result = generateRuleContent(editor, options);
if (!result) return null;
return { editor, ...result };
})
.filter((r): r is { editor: string; filename: string; content: string } => r !== null);
}