@contextstream/mcp-server 0.4.46 → 0.4.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,181 +38,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  mod
39
39
  ));
40
40
 
41
- // src/version.ts
42
- import { createRequire } from "module";
43
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
44
- import { homedir } from "os";
45
- import { join } from "path";
46
- function getVersion() {
47
- try {
48
- const require2 = createRequire(import.meta.url);
49
- const pkg = require2("../package.json");
50
- const version = pkg?.version;
51
- if (typeof version === "string" && version.trim()) return version.trim();
52
- } catch {
53
- }
54
- return "unknown";
55
- }
56
- function compareVersions(v1, v2) {
57
- const parts1 = v1.split(".").map(Number);
58
- const parts2 = v2.split(".").map(Number);
59
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
60
- const p1 = parts1[i] ?? 0;
61
- const p2 = parts2[i] ?? 0;
62
- if (p1 < p2) return -1;
63
- if (p1 > p2) return 1;
64
- }
65
- return 0;
66
- }
67
- function getCacheFilePath() {
68
- return join(homedir(), ".contextstream", "version-cache.json");
69
- }
70
- function readCache() {
71
- try {
72
- const cacheFile = getCacheFilePath();
73
- if (!existsSync(cacheFile)) return null;
74
- const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
75
- if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
76
- return data;
77
- } catch {
78
- return null;
79
- }
80
- }
81
- function writeCache(latestVersion) {
82
- try {
83
- const configDir = join(homedir(), ".contextstream");
84
- if (!existsSync(configDir)) {
85
- mkdirSync(configDir, { recursive: true });
86
- }
87
- const cacheFile = getCacheFilePath();
88
- writeFileSync(
89
- cacheFile,
90
- JSON.stringify({
91
- latestVersion,
92
- checkedAt: Date.now()
93
- })
94
- );
95
- } catch {
96
- }
97
- }
98
- async function fetchLatestVersion() {
99
- try {
100
- const controller = new AbortController();
101
- const timeout = setTimeout(() => controller.abort(), 5e3);
102
- const response = await fetch(NPM_LATEST_URL, {
103
- signal: controller.signal,
104
- headers: { Accept: "application/json" }
105
- });
106
- clearTimeout(timeout);
107
- if (!response.ok) return null;
108
- const data = await response.json();
109
- return typeof data.version === "string" ? data.version : null;
110
- } catch {
111
- return null;
112
- }
113
- }
114
- async function resolveLatestVersion() {
115
- const cached = readCache();
116
- if (cached) return cached.latestVersion;
117
- if (!latestVersionPromise) {
118
- latestVersionPromise = fetchLatestVersion().finally(() => {
119
- latestVersionPromise = null;
120
- });
121
- }
122
- const latestVersion = await latestVersionPromise;
123
- if (latestVersion) {
124
- writeCache(latestVersion);
125
- }
126
- return latestVersion;
127
- }
128
- async function checkForUpdates() {
129
- const notice = await getUpdateNotice();
130
- if (notice?.behind) {
131
- showUpdateWarning(notice.current, notice.latest);
132
- }
133
- }
134
- function showUpdateWarning(currentVersion, latestVersion) {
135
- console.error("");
136
- console.error("\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");
137
- console.error(`\u26A0\uFE0F Update available: v${currentVersion} \u2192 v${latestVersion}`);
138
- console.error("");
139
- console.error(` Run: ${UPGRADE_COMMAND}`);
140
- console.error("");
141
- console.error(" Then restart your AI tool to use the new version.");
142
- console.error("\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");
143
- console.error("");
144
- }
145
- async function getUpdateNotice() {
146
- const currentVersion = VERSION;
147
- if (currentVersion === "unknown") return null;
148
- try {
149
- const latestVersion = await resolveLatestVersion();
150
- if (!latestVersion) return null;
151
- if (compareVersions(currentVersion, latestVersion) < 0) {
152
- return {
153
- current: currentVersion,
154
- latest: latestVersion,
155
- behind: true,
156
- upgrade_command: UPGRADE_COMMAND
157
- };
158
- }
159
- } catch {
160
- }
161
- return null;
162
- }
163
- function getVersionsBehind(current, latest) {
164
- try {
165
- const currentParts = current.split(".").map(Number);
166
- const latestParts = latest.split(".").map(Number);
167
- if ((latestParts[0] ?? 0) > (currentParts[0] ?? 0)) {
168
- return 10 + ((latestParts[1] ?? 0) - (currentParts[1] ?? 0));
169
- }
170
- const minorDiff = (latestParts[1] ?? 0) - (currentParts[1] ?? 0);
171
- const patchDiff = (latestParts[2] ?? 0) - (currentParts[2] ?? 0);
172
- if (minorDiff > 0) {
173
- return minorDiff;
174
- } else if (minorDiff === 0 && patchDiff > 0) {
175
- return 1;
176
- }
177
- return 0;
178
- } catch {
179
- return 0;
180
- }
181
- }
182
- function getVersionWarning(notice) {
183
- if (!notice?.behind) return null;
184
- const versionsBehind = getVersionsBehind(notice.current, notice.latest);
185
- if (versionsBehind >= 3) {
186
- return `[VERSION_CRITICAL] MCP server is ${versionsBehind} versions behind (${notice.current} \u2192 ${notice.latest}). Update immediately: ${notice.upgrade_command}`;
187
- } else if (versionsBehind >= 1) {
188
- return `[VERSION_NOTICE] MCP server update available (${notice.current} \u2192 ${notice.latest}). Run: ${notice.upgrade_command}`;
189
- }
190
- return null;
191
- }
192
- function getVersionInstructions(notice) {
193
- if (!notice?.behind) return null;
194
- const versionsBehind = getVersionsBehind(notice.current, notice.latest);
195
- if (versionsBehind >= 5) {
196
- return `\u26A0\uFE0F CRITICAL: User's MCP is ${versionsBehind} versions behind. Mention update in EVERY response until updated.`;
197
- } else if (versionsBehind >= 3) {
198
- return `\u26A0\uFE0F User's MCP is ${versionsBehind} versions behind. Remind about update every 2-3 messages.`;
199
- } else if (versionsBehind >= 1) {
200
- return `Note: MCP update available (${notice.current}\u2192${notice.latest}). Mention once at session start.`;
201
- }
202
- return null;
203
- }
204
- var UPGRADE_COMMAND, NPM_LATEST_URL, VERSION, CACHE_TTL_MS, latestVersionPromise;
205
- var init_version = __esm({
206
- "src/version.ts"() {
207
- "use strict";
208
- UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
209
- NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
210
- VERSION = getVersion();
211
- CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
212
- latestVersionPromise = null;
213
- }
214
- });
215
-
216
41
  // node_modules/ignore/index.js
217
42
  var require_ignore = __commonJS({
218
43
  "node_modules/ignore/index.js"(exports, module) {
@@ -670,776 +495,1489 @@ var require_ignore = __commonJS({
670
495
  }
671
496
  });
672
497
 
673
- // src/rules-templates.ts
674
- var rules_templates_exports = {};
675
- __export(rules_templates_exports, {
676
- RULES_VERSION: () => RULES_VERSION,
677
- TEMPLATES: () => TEMPLATES,
678
- generateAllRuleFiles: () => generateAllRuleFiles,
679
- generateRuleContent: () => generateRuleContent,
680
- getAvailableEditors: () => getAvailableEditors,
681
- getTemplate: () => getTemplate
498
+ // src/hooks-config.ts
499
+ var hooks_config_exports = {};
500
+ __export(hooks_config_exports, {
501
+ CLINE_POSTTOOLUSE_HOOK_SCRIPT: () => CLINE_POSTTOOLUSE_HOOK_SCRIPT,
502
+ CLINE_PRETOOLUSE_HOOK_SCRIPT: () => CLINE_PRETOOLUSE_HOOK_SCRIPT,
503
+ CLINE_USER_PROMPT_HOOK_SCRIPT: () => CLINE_USER_PROMPT_HOOK_SCRIPT,
504
+ CURSOR_BEFORE_SUBMIT_HOOK_SCRIPT: () => CURSOR_BEFORE_SUBMIT_HOOK_SCRIPT,
505
+ CURSOR_PRETOOLUSE_HOOK_SCRIPT: () => CURSOR_PRETOOLUSE_HOOK_SCRIPT,
506
+ MEDIA_AWARE_HOOK_SCRIPT: () => MEDIA_AWARE_HOOK_SCRIPT,
507
+ PRECOMPACT_HOOK_SCRIPT: () => PRECOMPACT_HOOK_SCRIPT,
508
+ PRETOOLUSE_HOOK_SCRIPT: () => PRETOOLUSE_HOOK_SCRIPT,
509
+ USER_PROMPT_HOOK_SCRIPT: () => USER_PROMPT_HOOK_SCRIPT,
510
+ buildHooksConfig: () => buildHooksConfig,
511
+ generateAllHooksDocumentation: () => generateAllHooksDocumentation,
512
+ generateHooksDocumentation: () => generateHooksDocumentation,
513
+ getClaudeSettingsPath: () => getClaudeSettingsPath,
514
+ getClineHooksDir: () => getClineHooksDir,
515
+ getCursorHooksConfigPath: () => getCursorHooksConfigPath,
516
+ getCursorHooksDir: () => getCursorHooksDir,
517
+ getHooksDir: () => getHooksDir,
518
+ getIndexStatusPath: () => getIndexStatusPath,
519
+ getKiloCodeHooksDir: () => getKiloCodeHooksDir,
520
+ getRooCodeHooksDir: () => getRooCodeHooksDir,
521
+ installAllEditorHooks: () => installAllEditorHooks,
522
+ installClaudeCodeHooks: () => installClaudeCodeHooks,
523
+ installClineHookScripts: () => installClineHookScripts,
524
+ installCursorHookScripts: () => installCursorHookScripts,
525
+ installEditorHooks: () => installEditorHooks,
526
+ installHookScripts: () => installHookScripts,
527
+ installKiloCodeHookScripts: () => installKiloCodeHookScripts,
528
+ installRooCodeHookScripts: () => installRooCodeHookScripts,
529
+ markProjectIndexed: () => markProjectIndexed,
530
+ mergeHooksIntoSettings: () => mergeHooksIntoSettings,
531
+ readClaudeSettings: () => readClaudeSettings,
532
+ readCursorHooksConfig: () => readCursorHooksConfig,
533
+ readIndexStatus: () => readIndexStatus,
534
+ unmarkProjectIndexed: () => unmarkProjectIndexed,
535
+ writeClaudeSettings: () => writeClaudeSettings,
536
+ writeCursorHooksConfig: () => writeCursorHooksConfig,
537
+ writeIndexStatus: () => writeIndexStatus
682
538
  });
683
- function applyMcpToolPrefix(markdown, toolPrefix) {
684
- const toolPattern = CONTEXTSTREAM_TOOL_NAMES.join("|");
685
- const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
686
- return markdown.replace(toolRegex, `${toolPrefix}$1`);
687
- }
688
- function getAvailableEditors() {
689
- return Object.keys(TEMPLATES);
690
- }
691
- function getTemplate(editor) {
692
- return TEMPLATES[editor.toLowerCase()] || null;
693
- }
694
- function generateRuleContent(editor, options) {
695
- const template = getTemplate(editor);
696
- if (!template) return null;
697
- const mode = options?.mode || "dynamic";
698
- const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : CONTEXTSTREAM_RULES_DYNAMIC;
699
- let content = template.build(rules);
700
- if (options?.workspaceName || options?.projectName) {
701
- const header = `
702
- # Workspace: ${options.workspaceName || "Unknown"}
703
- ${options.projectName ? `# Project: ${options.projectName}` : ""}
704
- ${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
705
-
706
- `;
707
- content = header + content;
708
- }
709
- if (options?.additionalRules) {
710
- content += "\n\n## Project-Specific Rules\n\n" + options.additionalRules;
539
+ import * as fs4 from "node:fs/promises";
540
+ import * as path5 from "node:path";
541
+ import { homedir as homedir2 } from "node:os";
542
+ function getClaudeSettingsPath(scope, projectPath) {
543
+ if (scope === "user") {
544
+ return path5.join(homedir2(), ".claude", "settings.json");
711
545
  }
712
- if (editor.toLowerCase() === "claude") {
713
- content = applyMcpToolPrefix(content, `mcp__${DEFAULT_CLAUDE_MCP_SERVER_NAME}__`);
546
+ if (!projectPath) {
547
+ throw new Error("projectPath required for project scope");
714
548
  }
715
- return {
716
- filename: template.filename,
717
- content: content.trim() + "\n"
718
- };
549
+ return path5.join(projectPath, ".claude", "settings.json");
719
550
  }
720
- function generateAllRuleFiles(options) {
721
- return getAvailableEditors().map((editor) => {
722
- const result = generateRuleContent(editor, options);
723
- if (!result) return null;
724
- return { editor, ...result };
725
- }).filter((r) => r !== null);
551
+ function getHooksDir() {
552
+ return path5.join(homedir2(), ".claude", "hooks");
553
+ }
554
+ function buildHooksConfig(options) {
555
+ const userPromptHooks = [
556
+ {
557
+ matcher: "*",
558
+ hooks: [
559
+ {
560
+ type: "command",
561
+ command: "npx @contextstream/mcp-server hook user-prompt-submit",
562
+ timeout: 5
563
+ }
564
+ ]
565
+ }
566
+ ];
567
+ if (options?.includeMediaAware !== false) {
568
+ userPromptHooks.push({
569
+ matcher: "*",
570
+ hooks: [
571
+ {
572
+ type: "command",
573
+ command: "npx @contextstream/mcp-server hook media-aware",
574
+ timeout: 5
575
+ }
576
+ ]
577
+ });
578
+ }
579
+ const config = {
580
+ PreToolUse: [
581
+ {
582
+ matcher: "Glob|Grep|Search|Task|EnterPlanMode",
583
+ hooks: [
584
+ {
585
+ type: "command",
586
+ command: "npx @contextstream/mcp-server hook pre-tool-use",
587
+ timeout: 5
588
+ }
589
+ ]
590
+ }
591
+ ],
592
+ UserPromptSubmit: userPromptHooks
593
+ };
594
+ if (options?.includePreCompact) {
595
+ config.PreCompact = [
596
+ {
597
+ // Match both manual (/compact) and automatic compaction
598
+ matcher: "*",
599
+ hooks: [
600
+ {
601
+ type: "command",
602
+ command: "npx @contextstream/mcp-server hook pre-compact",
603
+ timeout: 10
604
+ }
605
+ ]
606
+ }
607
+ ];
608
+ }
609
+ const postToolUseHooks = [];
610
+ if (options?.includePostWrite !== false) {
611
+ postToolUseHooks.push({
612
+ matcher: "Edit|Write|NotebookEdit",
613
+ hooks: [
614
+ {
615
+ type: "command",
616
+ command: "npx @contextstream/mcp-server hook post-write",
617
+ timeout: 10
618
+ }
619
+ ]
620
+ });
621
+ }
622
+ if (options?.includeAutoRules !== false) {
623
+ postToolUseHooks.push({
624
+ matcher: "mcp__contextstream__init|mcp__contextstream__context",
625
+ hooks: [
626
+ {
627
+ type: "command",
628
+ command: "npx @contextstream/mcp-server hook auto-rules",
629
+ timeout: 15
630
+ }
631
+ ]
632
+ });
633
+ }
634
+ if (postToolUseHooks.length > 0) {
635
+ config.PostToolUse = postToolUseHooks;
636
+ }
637
+ return config;
638
+ }
639
+ async function installHookScripts(options) {
640
+ const hooksDir = getHooksDir();
641
+ await fs4.mkdir(hooksDir, { recursive: true });
642
+ const result = {
643
+ preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
644
+ userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
645
+ };
646
+ if (options?.includePreCompact) {
647
+ result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
648
+ }
649
+ if (options?.includeMediaAware !== false) {
650
+ result.mediaAware = "npx @contextstream/mcp-server hook media-aware";
651
+ }
652
+ if (options?.includeAutoRules !== false) {
653
+ result.autoRules = "npx @contextstream/mcp-server hook auto-rules";
654
+ }
655
+ return result;
656
+ }
657
+ async function readClaudeSettings(scope, projectPath) {
658
+ const settingsPath = getClaudeSettingsPath(scope, projectPath);
659
+ try {
660
+ const content = await fs4.readFile(settingsPath, "utf-8");
661
+ return JSON.parse(content);
662
+ } catch {
663
+ return {};
664
+ }
665
+ }
666
+ async function writeClaudeSettings(settings, scope, projectPath) {
667
+ const settingsPath = getClaudeSettingsPath(scope, projectPath);
668
+ const dir = path5.dirname(settingsPath);
669
+ await fs4.mkdir(dir, { recursive: true });
670
+ await fs4.writeFile(settingsPath, JSON.stringify(settings, null, 2));
671
+ }
672
+ function mergeHooksIntoSettings(existingSettings, newHooks) {
673
+ const settings = { ...existingSettings };
674
+ const existingHooks = settings.hooks || {};
675
+ for (const [hookType, matchers] of Object.entries(newHooks || {})) {
676
+ if (!matchers) continue;
677
+ const existing = existingHooks?.[hookType] || [];
678
+ const filtered = existing.filter((m) => {
679
+ return !m.hooks?.some((h) => h.command?.includes("contextstream"));
680
+ });
681
+ existingHooks[hookType] = [...filtered, ...matchers];
682
+ }
683
+ settings.hooks = existingHooks;
684
+ return settings;
685
+ }
686
+ async function installClaudeCodeHooks(options) {
687
+ const result = { scripts: [], settings: [] };
688
+ result.scripts.push(
689
+ "npx @contextstream/mcp-server hook pre-tool-use",
690
+ "npx @contextstream/mcp-server hook user-prompt-submit"
691
+ );
692
+ if (options.includePreCompact) {
693
+ result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
694
+ }
695
+ if (options.includeMediaAware !== false) {
696
+ result.scripts.push("npx @contextstream/mcp-server hook media-aware");
697
+ }
698
+ if (options.includePostWrite !== false) {
699
+ result.scripts.push("npx @contextstream/mcp-server hook post-write");
700
+ }
701
+ if (options.includeAutoRules !== false) {
702
+ result.scripts.push("npx @contextstream/mcp-server hook auto-rules");
703
+ }
704
+ const hooksConfig = buildHooksConfig({
705
+ includePreCompact: options.includePreCompact,
706
+ includeMediaAware: options.includeMediaAware,
707
+ includePostWrite: options.includePostWrite,
708
+ includeAutoRules: options.includeAutoRules
709
+ });
710
+ if (options.scope === "user" || options.scope === "both") {
711
+ const settingsPath = getClaudeSettingsPath("user");
712
+ if (!options.dryRun) {
713
+ const existing = await readClaudeSettings("user");
714
+ const merged = mergeHooksIntoSettings(existing, hooksConfig);
715
+ await writeClaudeSettings(merged, "user");
716
+ }
717
+ result.settings.push(settingsPath);
718
+ }
719
+ if ((options.scope === "project" || options.scope === "both") && options.projectPath) {
720
+ const settingsPath = getClaudeSettingsPath("project", options.projectPath);
721
+ if (!options.dryRun) {
722
+ const existing = await readClaudeSettings("project", options.projectPath);
723
+ const merged = mergeHooksIntoSettings(existing, hooksConfig);
724
+ await writeClaudeSettings(merged, "project", options.projectPath);
725
+ }
726
+ result.settings.push(settingsPath);
727
+ }
728
+ return result;
729
+ }
730
+ function generateHooksDocumentation() {
731
+ return `
732
+ ## Claude Code Hooks (ContextStream)
733
+
734
+ ContextStream installs hooks to enforce ContextStream-first behavior.
735
+ All hooks run via Node.js - no Python dependency required.
736
+
737
+ ### PreToolUse Hook
738
+ - **Command:** \`npx @contextstream/mcp-server hook pre-tool-use\`
739
+ - **Purpose:** Blocks Glob/Grep/Search/EnterPlanMode and redirects to ContextStream
740
+ - **Blocked tools:** Glob, Grep, Search, Task(Explore), Task(Plan), EnterPlanMode
741
+ - **Disable:** Set \`CONTEXTSTREAM_HOOK_ENABLED=false\` environment variable
742
+
743
+ ### UserPromptSubmit Hook
744
+ - **Command:** \`npx @contextstream/mcp-server hook user-prompt-submit\`
745
+ - **Purpose:** Injects a reminder about ContextStream rules on every message
746
+ - **Disable:** Set \`CONTEXTSTREAM_REMINDER_ENABLED=false\` environment variable
747
+
748
+ ### Media-Aware Hook
749
+ - **Command:** \`npx @contextstream/mcp-server hook media-aware\`
750
+ - **Purpose:** Detects media-related prompts and injects media tool guidance
751
+ - **Triggers:** Patterns like video, clips, Remotion, image, audio, creative assets
752
+ - **Disable:** Set \`CONTEXTSTREAM_MEDIA_HOOK_ENABLED=false\` environment variable
753
+
754
+ When Media-Aware hook detects media patterns, it injects context about:
755
+ - How to search indexed media assets
756
+ - How to get clips for Remotion (with frame-based props)
757
+ - How to index new media files
758
+
759
+ ### PreCompact Hook (Optional)
760
+ - **Command:** \`npx @contextstream/mcp-server hook pre-compact\`
761
+ - **Purpose:** Saves conversation state before context compaction
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
765
+
766
+ When PreCompact runs, it:
767
+ 1. Parses the transcript for active files and tool calls
768
+ 2. Saves a session_snapshot to ContextStream API
769
+ 3. Injects context about using \`session_init(is_post_compact=true)\` after compaction
770
+
771
+ ### PostToolUse Hook (Real-time Indexing)
772
+ - **Command:** \`npx @contextstream/mcp-server hook post-write\`
773
+ - **Purpose:** Indexes files immediately after Edit/Write/NotebookEdit operations
774
+ - **Matcher:** Edit|Write|NotebookEdit
775
+ - **Disable:** Set \`CONTEXTSTREAM_POSTWRITE_ENABLED=false\` environment variable
776
+
777
+ ### Why Hooks?
778
+ Claude Code has strong built-in behaviors to use its default tools (Grep, Glob, Read)
779
+ and its built-in plan mode. CLAUDE.md instructions decay over long conversations.
780
+ Hooks provide:
781
+ 1. **Physical enforcement** - Blocked tools can't be used
782
+ 2. **Continuous reminders** - Rules stay in recent context
783
+ 3. **Better UX** - Faster searches via indexed ContextStream
784
+ 4. **Persistent plans** - ContextStream plans survive across sessions
785
+ 5. **Compaction awareness** - Save state before context is compacted
786
+ 6. **Real-time indexing** - Files indexed immediately after writes
787
+
788
+ ### Manual Configuration
789
+ If you prefer to configure manually, add to \`~/.claude/settings.json\`:
790
+ \`\`\`json
791
+ {
792
+ "hooks": {
793
+ "PreToolUse": [{
794
+ "matcher": "Glob|Grep|Search|Task|EnterPlanMode",
795
+ "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook pre-tool-use"}]
796
+ }],
797
+ "UserPromptSubmit": [
798
+ {
799
+ "matcher": "*",
800
+ "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook user-prompt-submit"}]
801
+ },
802
+ {
803
+ "matcher": "*",
804
+ "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook media-aware"}]
805
+ }
806
+ ],
807
+ "PreCompact": [{
808
+ "matcher": "*",
809
+ "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook pre-compact", "timeout": 10}]
810
+ }],
811
+ "PostToolUse": [{
812
+ "matcher": "Edit|Write|NotebookEdit",
813
+ "hooks": [{"type": "command", "command": "npx @contextstream/mcp-server hook post-write", "timeout": 10}]
814
+ }]
815
+ }
726
816
  }
727
- var DEFAULT_CLAUDE_MCP_SERVER_NAME, RULES_VERSION, CONTEXTSTREAM_TOOL_NAMES, CONTEXTSTREAM_RULES_DYNAMIC, CONTEXTSTREAM_RULES_FULL, CONTEXTSTREAM_RULES_MINIMAL, TEMPLATES;
728
- var init_rules_templates = __esm({
729
- "src/rules-templates.ts"() {
730
- "use strict";
731
- init_version();
732
- DEFAULT_CLAUDE_MCP_SERVER_NAME = "contextstream";
733
- RULES_VERSION = VERSION === "unknown" ? "0.0.0" : VERSION;
734
- CONTEXTSTREAM_TOOL_NAMES = [
735
- // Standalone tools (always present)
736
- "init",
737
- // Renamed from session_init - initialize conversation session
738
- "context",
739
- // Renamed from context_smart - get relevant context every message
740
- "context_feedback",
741
- "generate_rules",
742
- // Consolidated domain tools (v0.4.x default)
743
- "search",
744
- // Modes: semantic, hybrid, keyword, pattern
745
- "session",
746
- // Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
747
- "memory",
748
- // 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
749
- "graph",
750
- // Actions: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions
751
- "project",
752
- // Actions: list, get, create, update, index, overview, statistics, files, index_status, ingest_local
753
- "workspace",
754
- // Actions: list, get, associate, bootstrap
755
- "reminder",
756
- // Actions: list, active, create, snooze, complete, dismiss
757
- "integration",
758
- // Provider: slack, github, all; Actions: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues
759
- "help"
760
- // Actions: tools, auth, version, editor_rules, enable_bundle
761
- ];
762
- CONTEXTSTREAM_RULES_DYNAMIC = `
763
- ## ContextStream Dynamic Rules (Powered by our SmartRouter)
764
-
765
- All rules are served dynamically via \`context\`. This file is minimal by design.
766
-
767
- ### Every Message Protocol
768
-
769
- | Message | What to Call |
770
- |---------|--------------|
771
- | **1st message** | \`init()\` then \`context(user_message="<msg>")\` |
772
- | **2nd+ messages** | \`context(user_message="<msg>")\` |
773
-
774
- Note: \`init()\` auto-detects folder from IDE. Only pass \`folder_path\` if auto-detection fails.
775
-
776
- ### Follow the Instructions Field
777
-
778
- The \`context\` response includes an \`instructions\` field with context-aware guidance.
779
- **Follow these instructions.** They are dynamically matched to your query and include:
780
- - Search guidance (when/how to search)
781
- - Git workflow rules (commit, PR, safety)
782
- - Planning rules (use ContextStream plans, not file-based)
783
- - Media/code analysis guidance
784
- - Lessons from past mistakes
785
- - And more...
786
-
787
- ### Notices
788
-
789
- Handle notices from \`context\` response:
790
- - **[VERSION_NOTICE]**: Tell user to update MCP
791
- - **[RULES_NOTICE]**: Run \`generate_rules()\`
792
- - **[LESSONS_WARNING]**: Apply lessons immediately
793
-
794
- Rules Version: ${RULES_VERSION}
795
- `.trim();
796
- CONTEXTSTREAM_RULES_FULL = `
797
- ## ContextStream Rules
798
-
799
- <contextstream_rules>
800
- 1. **SIMPLE UTILITY?** (list/show/version) \u2192 Just execute, skip context calls
801
- 2. **CODING TASK?** \u2192 init \u2192 context \u2192 work \u2192 capture
802
- 3. **SEARCH FIRST:** Before Glob/Grep/Read \u2192 \`search(mode="hybrid")\` FIRST
803
- </contextstream_rules>
804
-
805
- **Display this block at the start of responses to keep rules in context.**
806
-
807
- ---
808
-
809
- ## \u{1F6A8} CRITICAL RULE #1 - CONTEXTSTREAM SEARCH FIRST \u{1F6A8}
810
-
811
- **BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
812
- \`\`\`
813
- STOP \u2192 Call search(mode="hybrid", query="...") FIRST
814
817
  \`\`\`
818
+ `.trim();
819
+ }
820
+ function getIndexStatusPath() {
821
+ return path5.join(homedir2(), ".contextstream", "indexed-projects.json");
822
+ }
823
+ async function readIndexStatus() {
824
+ const statusPath = getIndexStatusPath();
825
+ try {
826
+ const content = await fs4.readFile(statusPath, "utf-8");
827
+ return JSON.parse(content);
828
+ } catch {
829
+ return { version: 1, projects: {} };
830
+ }
831
+ }
832
+ async function writeIndexStatus(status) {
833
+ const statusPath = getIndexStatusPath();
834
+ const dir = path5.dirname(statusPath);
835
+ await fs4.mkdir(dir, { recursive: true });
836
+ await fs4.writeFile(statusPath, JSON.stringify(status, null, 2));
837
+ }
838
+ async function markProjectIndexed(projectPath, options) {
839
+ const status = await readIndexStatus();
840
+ const resolvedPath = path5.resolve(projectPath);
841
+ status.projects[resolvedPath] = {
842
+ indexed_at: (/* @__PURE__ */ new Date()).toISOString(),
843
+ project_id: options?.project_id,
844
+ project_name: options?.project_name
845
+ };
846
+ await writeIndexStatus(status);
847
+ }
848
+ async function unmarkProjectIndexed(projectPath) {
849
+ const status = await readIndexStatus();
850
+ const resolvedPath = path5.resolve(projectPath);
851
+ delete status.projects[resolvedPath];
852
+ await writeIndexStatus(status);
853
+ }
854
+ function getClineHooksDir(scope, projectPath) {
855
+ if (scope === "global") {
856
+ return path5.join(homedir2(), "Documents", "Cline", "Rules", "Hooks");
857
+ }
858
+ if (!projectPath) {
859
+ throw new Error("projectPath required for project scope");
860
+ }
861
+ return path5.join(projectPath, ".clinerules", "hooks");
862
+ }
863
+ async function installClineHookScripts(options) {
864
+ const hooksDir = getClineHooksDir(options.scope, options.projectPath);
865
+ await fs4.mkdir(hooksDir, { recursive: true });
866
+ const preToolUsePath = path5.join(hooksDir, "PreToolUse");
867
+ const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
868
+ const postToolUsePath = path5.join(hooksDir, "PostToolUse");
869
+ await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
870
+ await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
871
+ const result = {
872
+ preToolUse: preToolUsePath,
873
+ userPromptSubmit: userPromptPath
874
+ };
875
+ if (options.includePostWrite !== false) {
876
+ await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
877
+ result.postToolUse = postToolUsePath;
878
+ }
879
+ return result;
880
+ }
881
+ function getRooCodeHooksDir(scope, projectPath) {
882
+ if (scope === "global") {
883
+ return path5.join(homedir2(), ".roo", "hooks");
884
+ }
885
+ if (!projectPath) {
886
+ throw new Error("projectPath required for project scope");
887
+ }
888
+ return path5.join(projectPath, ".roo", "hooks");
889
+ }
890
+ async function installRooCodeHookScripts(options) {
891
+ const hooksDir = getRooCodeHooksDir(options.scope, options.projectPath);
892
+ await fs4.mkdir(hooksDir, { recursive: true });
893
+ const preToolUsePath = path5.join(hooksDir, "PreToolUse");
894
+ const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
895
+ const postToolUsePath = path5.join(hooksDir, "PostToolUse");
896
+ await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
897
+ await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
898
+ const result = {
899
+ preToolUse: preToolUsePath,
900
+ userPromptSubmit: userPromptPath
901
+ };
902
+ if (options.includePostWrite !== false) {
903
+ await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
904
+ result.postToolUse = postToolUsePath;
905
+ }
906
+ return result;
907
+ }
908
+ function getKiloCodeHooksDir(scope, projectPath) {
909
+ if (scope === "global") {
910
+ return path5.join(homedir2(), ".kilocode", "hooks");
911
+ }
912
+ if (!projectPath) {
913
+ throw new Error("projectPath required for project scope");
914
+ }
915
+ return path5.join(projectPath, ".kilocode", "hooks");
916
+ }
917
+ async function installKiloCodeHookScripts(options) {
918
+ const hooksDir = getKiloCodeHooksDir(options.scope, options.projectPath);
919
+ await fs4.mkdir(hooksDir, { recursive: true });
920
+ const preToolUsePath = path5.join(hooksDir, "PreToolUse");
921
+ const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
922
+ const postToolUsePath = path5.join(hooksDir, "PostToolUse");
923
+ await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
924
+ await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
925
+ const result = {
926
+ preToolUse: preToolUsePath,
927
+ userPromptSubmit: userPromptPath
928
+ };
929
+ if (options.includePostWrite !== false) {
930
+ await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
931
+ result.postToolUse = postToolUsePath;
932
+ }
933
+ return result;
934
+ }
935
+ function getCursorHooksConfigPath(scope, projectPath) {
936
+ if (scope === "global") {
937
+ return path5.join(homedir2(), ".cursor", "hooks.json");
938
+ }
939
+ if (!projectPath) {
940
+ throw new Error("projectPath required for project scope");
941
+ }
942
+ return path5.join(projectPath, ".cursor", "hooks.json");
943
+ }
944
+ function getCursorHooksDir(scope, projectPath) {
945
+ if (scope === "global") {
946
+ return path5.join(homedir2(), ".cursor", "hooks");
947
+ }
948
+ if (!projectPath) {
949
+ throw new Error("projectPath required for project scope");
950
+ }
951
+ return path5.join(projectPath, ".cursor", "hooks");
952
+ }
953
+ async function readCursorHooksConfig(scope, projectPath) {
954
+ const configPath = getCursorHooksConfigPath(scope, projectPath);
955
+ try {
956
+ const content = await fs4.readFile(configPath, "utf-8");
957
+ return JSON.parse(content);
958
+ } catch {
959
+ return { version: 1, hooks: {} };
960
+ }
961
+ }
962
+ async function writeCursorHooksConfig(config, scope, projectPath) {
963
+ const configPath = getCursorHooksConfigPath(scope, projectPath);
964
+ const dir = path5.dirname(configPath);
965
+ await fs4.mkdir(dir, { recursive: true });
966
+ await fs4.writeFile(configPath, JSON.stringify(config, null, 2));
967
+ }
968
+ async function installCursorHookScripts(options) {
969
+ const hooksDir = getCursorHooksDir(options.scope, options.projectPath);
970
+ await fs4.mkdir(hooksDir, { recursive: true });
971
+ const existingConfig = await readCursorHooksConfig(options.scope, options.projectPath);
972
+ const filterContextStreamHooks = (hooks) => {
973
+ if (!hooks) return [];
974
+ return hooks.filter((h) => {
975
+ const hook = h;
976
+ return !hook.command?.includes("contextstream");
977
+ });
978
+ };
979
+ const filteredPreToolUse = filterContextStreamHooks(existingConfig.hooks.preToolUse);
980
+ const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
981
+ const config = {
982
+ version: 1,
983
+ hooks: {
984
+ ...existingConfig.hooks,
985
+ preToolUse: [
986
+ ...filteredPreToolUse,
987
+ {
988
+ command: "npx @contextstream/mcp-server hook pre-tool-use",
989
+ type: "command",
990
+ timeout: 5,
991
+ matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
992
+ }
993
+ ],
994
+ beforeSubmitPrompt: [
995
+ ...filteredBeforeSubmit,
996
+ {
997
+ command: "npx @contextstream/mcp-server hook user-prompt-submit",
998
+ type: "command",
999
+ timeout: 5
1000
+ }
1001
+ ]
1002
+ }
1003
+ };
1004
+ await writeCursorHooksConfig(config, options.scope, options.projectPath);
1005
+ const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
1006
+ return {
1007
+ preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
1008
+ beforeSubmitPrompt: "npx @contextstream/mcp-server hook user-prompt-submit",
1009
+ config: configPath
1010
+ };
1011
+ }
1012
+ async function installEditorHooks(options) {
1013
+ const { editor, scope, projectPath, includePreCompact, includePostWrite } = options;
1014
+ switch (editor) {
1015
+ case "claude": {
1016
+ if (scope === "project" && !projectPath) {
1017
+ throw new Error("projectPath required for project scope");
1018
+ }
1019
+ const scripts = await installHookScripts({ includePreCompact });
1020
+ const hooksConfig = buildHooksConfig({ includePreCompact, includePostWrite });
1021
+ const settingsScope = scope === "global" ? "user" : "project";
1022
+ const existing = await readClaudeSettings(settingsScope, projectPath);
1023
+ const merged = mergeHooksIntoSettings(existing, hooksConfig);
1024
+ await writeClaudeSettings(merged, settingsScope, projectPath);
1025
+ const installed = [scripts.preToolUse, scripts.userPrompt];
1026
+ if (scripts.preCompact) installed.push(scripts.preCompact);
1027
+ return {
1028
+ editor: "claude",
1029
+ installed,
1030
+ hooksDir: getHooksDir()
1031
+ };
1032
+ }
1033
+ case "cline": {
1034
+ const scripts = await installClineHookScripts({ scope, projectPath, includePostWrite });
1035
+ const installed = [scripts.preToolUse, scripts.userPromptSubmit];
1036
+ if (scripts.postToolUse) installed.push(scripts.postToolUse);
1037
+ return {
1038
+ editor: "cline",
1039
+ installed,
1040
+ hooksDir: getClineHooksDir(scope, projectPath)
1041
+ };
1042
+ }
1043
+ case "roo": {
1044
+ const scripts = await installRooCodeHookScripts({ scope, projectPath, includePostWrite });
1045
+ const installed = [scripts.preToolUse, scripts.userPromptSubmit];
1046
+ if (scripts.postToolUse) installed.push(scripts.postToolUse);
1047
+ return {
1048
+ editor: "roo",
1049
+ installed,
1050
+ hooksDir: getRooCodeHooksDir(scope, projectPath)
1051
+ };
1052
+ }
1053
+ case "kilo": {
1054
+ const scripts = await installKiloCodeHookScripts({ scope, projectPath, includePostWrite });
1055
+ const installed = [scripts.preToolUse, scripts.userPromptSubmit];
1056
+ if (scripts.postToolUse) installed.push(scripts.postToolUse);
1057
+ return {
1058
+ editor: "kilo",
1059
+ installed,
1060
+ hooksDir: getKiloCodeHooksDir(scope, projectPath)
1061
+ };
1062
+ }
1063
+ case "cursor": {
1064
+ const scripts = await installCursorHookScripts({ scope, projectPath });
1065
+ return {
1066
+ editor: "cursor",
1067
+ installed: [scripts.preToolUse, scripts.beforeSubmitPrompt],
1068
+ hooksDir: getCursorHooksDir(scope, projectPath)
1069
+ };
1070
+ }
1071
+ default:
1072
+ throw new Error(`Unsupported editor: ${editor}`);
1073
+ }
1074
+ }
1075
+ async function installAllEditorHooks(options) {
1076
+ const editors = options.editors || ["claude", "cline", "roo", "kilo", "cursor"];
1077
+ const results = [];
1078
+ for (const editor of editors) {
1079
+ try {
1080
+ const result = await installEditorHooks({
1081
+ editor,
1082
+ scope: options.scope,
1083
+ projectPath: options.projectPath,
1084
+ includePreCompact: options.includePreCompact,
1085
+ includePostWrite: options.includePostWrite
1086
+ });
1087
+ results.push(result);
1088
+ } catch (error) {
1089
+ console.error(`Failed to install hooks for ${editor}:`, error);
1090
+ }
1091
+ }
1092
+ return results;
1093
+ }
1094
+ function generateAllHooksDocumentation() {
1095
+ return `
1096
+ ## Editor Hooks Support (ContextStream)
815
1097
 
816
- **Note:** PreToolUse hooks block these tools when ContextStream is available.
817
- **Claude Code users:** Your tool names are \`mcp__contextstream__search\`, \`mcp__contextstream__init\`, etc.
818
-
819
- \u274C **NEVER DO THIS:**
820
- - \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
821
- - \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
822
- - \`Read(file)\` for discovery \u2192 Use \`search(mode="hybrid", query="...")\` instead
823
- - \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="hybrid")\` instead
824
-
825
- \u2705 **ALWAYS DO THIS:**
826
- 1. \`search(mode="hybrid", query="what you're looking for")\`
827
- 2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
828
- 3. Use Read ONLY for exact file edits after you know the file path
829
-
830
- This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
831
-
832
- ---
833
-
834
- ## \u{1F6A8} CRITICAL RULE #2 - AUTO-INDEXING \u{1F6A8}
835
-
836
- **ContextStream auto-indexes your project on \`init\`.** You do NOT need to:
837
- - Ask the user to index
838
- - Manually trigger ingestion
839
- - Check index_status before every search
840
-
841
- **When \`init\` returns \`indexing_status: "started"\` or \`"refreshing"\`:**
842
- - Background indexing is running automatically
843
- - Search results will be available within seconds to minutes
844
- - **DO NOT fall back to local tools** - wait for ContextStream search to work
845
- - If search returns 0 results initially, try again after a moment
846
-
847
- **Only manually trigger indexing if:**
848
- - \`init\` returned \`ingest_recommendation.recommended: true\` (rare edge case)
849
- - User explicitly asks to re-index
850
-
851
- ---
852
-
853
- ## \u{1F6A8} CRITICAL RULE #3 - LESSONS (PAST MISTAKES) \u{1F6A8}
854
-
855
- **Lessons are past mistakes that MUST inform your work.** Ignoring lessons leads to repeated failures.
856
-
857
- ### On \`init\`:
858
- - Check for \`lessons\` and \`lessons_warning\` in the response
859
- - If present, **READ THEM IMMEDIATELY** before doing any work
860
- - These are high-priority lessons (critical/high severity) relevant to your context
861
- - **Apply the prevention steps** from each lesson to avoid repeating mistakes
862
-
863
- ### On \`context\`:
864
- - Check for \`[LESSONS_WARNING]\` tag in the response
865
- - If present, you **MUST** tell the user about the lessons before proceeding
866
- - Lessons are proactively fetched when risky actions are detected (refactor, migrate, deploy, etc.)
867
- - **Do not skip or bury this warning** - lessons represent real past mistakes
868
-
869
- ### Before ANY Non-Trivial Work:
870
- **ALWAYS call \`session(action="get_lessons", query="<topic>")\`** where \`<topic>\` matches what you're about to do:
871
- - Before refactoring \u2192 \`session(action="get_lessons", query="refactoring")\`
872
- - Before API changes \u2192 \`session(action="get_lessons", query="API changes")\`
873
- - Before database work \u2192 \`session(action="get_lessons", query="database migrations")\`
874
- - Before deployments \u2192 \`session(action="get_lessons", query="deployment")\`
875
-
876
- ### When Lessons Are Found:
877
- 1. **Summarize the lessons** to the user before proceeding
878
- 2. **Explicitly state how you will avoid the past mistakes**
879
- 3. If a lesson conflicts with the current approach, **warn the user**
880
-
881
- **Failing to check lessons before risky work is a critical error.**
882
-
883
- ---
884
-
885
- ## ContextStream v0.4.x Integration (Enhanced)
886
-
887
- You have access to ContextStream MCP tools for persistent memory and context.
888
- v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
889
- Rules Version: ${RULES_VERSION}
890
-
891
- ## TL;DR - WHEN TO USE CONTEXT
892
-
893
- | Request Type | What to Do |
894
- |--------------|------------|
895
- | **\u{1F680} Simple utility** (list workspaces, show version) | **Just execute directly** - skip init, context, capture |
896
- | **\u{1F4BB} Coding task** (edit, create, refactor) | Full context: init \u2192 context \u2192 work \u2192 capture |
897
- | **\u{1F50D} Code search/discovery** | init \u2192 context \u2192 search() |
898
- | **\u26A0\uFE0F Risky work** (deploy, migrate, refactor) | Check lessons first: \`session(action="get_lessons")\` |
899
- | **User frustration/correction** | Capture lesson: \`session(action="capture_lesson", ...)\` |
900
-
901
- ### Simple Utility Operations - FAST PATH
902
-
903
- **For simple queries, just execute and respond:**
904
- - "list workspaces" \u2192 \`workspace(action="list")\` \u2192 done
905
- - "list projects" \u2192 \`project(action="list")\` \u2192 done
906
- - "show version" \u2192 \`help(action="version")\` \u2192 done
907
- - "what reminders do I have" \u2192 \`reminder(action="list")\` \u2192 done
908
-
909
- **No init. No context. No capture.** These add noise, not value.
910
-
911
- ### Coding Tasks - FULL CONTEXT
912
-
913
- | Step | What to Call |
914
- |------|--------------|
915
- | **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
916
- | **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
917
- | **Code search** | \`search(mode="hybrid", query="...")\` \u2014 BEFORE Glob/Grep/Read |
918
- | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
919
- | **User correction** | \`session(action="capture_lesson", ...)\` |
920
- | **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
921
-
922
- **How to detect simple utility operations:**
923
- - Single-word commands: "list", "show", "version", "help"
924
- - Data retrieval with no context dependency: "list my workspaces", "what projects do I have"
925
- - Status checks: "am I authenticated?", "what's the server version?"
926
-
927
- **First message rule (for coding tasks):** After \`init\`:
928
- 1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
929
- 2. Then call \`context\` before any other tool or response
930
-
931
- **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).
932
-
933
- **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.
934
-
935
- ---
936
-
937
- ## Consolidated Domain Tools Architecture
938
-
939
- v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode dispatch:
940
-
941
- ### Standalone Tools
942
- - **\`init\`** - Initialize session with workspace detection + context (skip for simple utility operations)
943
- - **\`context\`** - Semantic search for relevant context (skip for simple utility operations)
944
-
945
- ### Domain Tools (Use action/mode parameter)
946
-
947
- | Domain | Actions/Modes | Example |
948
- |--------|---------------|---------|
949
- | **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="hybrid", query="auth implementation", limit=3)\` |
950
- | **\`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="...")\` |
951
- | **\`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)\` |
952
- | **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
953
- | **\`project\`** | action: list, get, create, update, index, overview, statistics, files, index_status, ingest_local | \`project(action="statistics")\` |
954
- | **\`workspace\`** | action: list, get, associate, bootstrap | \`workspace(action="list")\` |
955
- | **\`reminder\`** | action: list, active, create, snooze, complete, dismiss | \`reminder(action="active")\` |
956
- | **\`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="...")\` |
957
- | **\`help\`** | action: tools, auth, version, editor_rules, enable_bundle | \`help(action="tools")\` |
958
-
959
- ---
960
-
961
- ### Why context is Required (Even After init)
962
-
963
- **Common mistake:** "init already gave me context, I don't need context"
964
-
965
- **This is WRONG. Here's why:**
966
- - \`init\` returns the last ~10 items **BY TIME** (chronological)
967
- - \`context\` **SEARCHES** for items **RELEVANT to THIS message** (semantic)
968
-
969
- **Example failure:**
970
- - User asks: "how should I implement authentication?"
971
- - Auth decisions were made 20 conversations ago
972
- - \`init\` won't have it (too old, not in recent 10)
973
- - \`context\` FINDS it via semantic search
974
-
975
- **Without context, you WILL miss relevant older context.**
976
-
977
- ---
978
-
979
- ### Recommended Token Budgets
980
-
981
- - For trivial/local edits: \`context(..., max_tokens=200)\`
982
- - Default: \`context(..., max_tokens=400)\`
983
- - Deep debugging/architecture: \`context(..., max_tokens=800)\`
984
- - Keep \`format="minified"\` (default) unless debugging
985
-
986
- If context still feels missing, use \`session(action="recall", query="...")\` for focused deep lookup.
987
-
988
- ---
989
-
990
- ### Rules, Version & Lessons Notices
991
-
992
- **[RULES_NOTICE]** - Update rules via \`generate_rules()\` (or rerun setup).
993
-
994
- **[VERSION_NOTICE]** - You **MUST IMMEDIATELY** tell the user:
995
- 1. A new MCP server version is available
996
- 2. The exact update command to run
997
- 3. That they need to restart their AI tool after updating
998
- **Do not skip this** - users often miss stderr warnings.
999
-
1000
- **[LESSONS_WARNING]** - You **MUST** before proceeding:
1001
- 1. Read all lessons listed
1002
- 2. Tell the user about relevant lessons
1003
- 3. Explain how you will avoid each past mistake
1004
- **This is critical** - ignoring lessons leads to repeated failures.
1005
-
1006
- ---
1007
-
1008
- ### Preferences & Lessons (Use Early)
1009
-
1010
- - If preferences/style matter: \`session(action="user_context")\`
1011
- - Before risky changes: \`session(action="get_lessons", query="<topic>")\`
1012
- - On frustration/corrections: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
1013
-
1014
- ---
1015
-
1016
- ### Context Pressure & Compaction Awareness
1017
-
1018
- ContextStream tracks context pressure to help you stay ahead of conversation compaction:
1019
-
1020
- **Automatic tracking:** Token usage is tracked automatically. \`context\` returns \`context_pressure\` when usage is high.
1021
-
1022
- **When \`context\` returns \`context_pressure\` with high/critical level:**
1023
- 1. Review the \`suggested_action\` field:
1024
- - \`prepare_save\`: Start thinking about saving important state
1025
- - \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
1026
-
1027
- **PreCompact Hook (Optional):** If enabled, Claude Code will inject a reminder to save state before compaction.
1028
- Enable with: \`generate_rules(install_hooks=true, include_pre_compact=true)\`
1029
-
1030
- **Before compaction happens (when warned):**
1031
- \`\`\`
1032
- session(action="capture", event_type="session_snapshot", title="Pre-compaction snapshot", content="{
1033
- \\"conversation_summary\\": \\"<summarize what we've been doing>\\",
1034
- \\"current_goal\\": \\"<the main task>\\",
1035
- \\"active_files\\": [\\"file1.ts\\", \\"file2.ts\\"],
1036
- \\"recent_decisions\\": [{title: \\"...\\", rationale: \\"...\\"}],
1037
- \\"unfinished_work\\": [{task: \\"...\\", status: \\"...\\", next_steps: \\"...\\"}]
1038
- }")
1039
- \`\`\`
1040
-
1041
- **After compaction (when context seems lost):**
1042
- 1. Call \`init(folder_path="...", is_post_compact=true)\` - this auto-restores the most recent snapshot
1043
- 2. Or call \`session_restore_context()\` directly to get the saved state
1044
- 3. Review the \`restored_context\` to understand prior work
1045
- 4. Acknowledge to the user what was restored and continue
1046
-
1047
- ---
1048
-
1049
- ### Index Status (Auto-Managed)
1050
-
1051
- **Indexing is automatic.** After \`init\`, the project is auto-indexed in the background.
1052
-
1053
- **You do NOT need to manually check index_status before every search.** Just use \`search()\`.
1054
-
1055
- **If search returns 0 results and you expected matches:**
1056
- 1. Check if \`init\` returned \`indexing_status: "started"\` - indexing may still be in progress
1057
- 2. Wait a moment and retry \`search()\`
1058
- 3. Only as a last resort: \`project(action="index_status")\` to check
1059
-
1060
- **Graph data:** If graph queries (\`dependencies\`, \`impact\`) return empty, run \`graph(action="ingest")\` once.
1061
-
1062
- **NEVER fall back to local tools (Glob/Grep/Read) just because search returned 0 results on first try.** Retry first.
1063
-
1064
- ### Enhanced Context (Server-Side Warnings)
1065
-
1066
- \`context\` now includes **intelligent server-side filtering** that proactively surfaces relevant warnings:
1067
-
1068
- **Response fields:**
1069
- - \`warnings\`: Array of warning strings (displayed with \u26A0\uFE0F prefix)
1070
-
1071
- **What triggers warnings:**
1072
- - **Lessons**: Past mistakes relevant to the current query (via semantic matching)
1073
- - **Risky actions**: Detected high-risk operations (deployments, migrations, destructive commands)
1074
- - **Breaking changes**: When modifications may impact other parts of the codebase
1075
-
1076
- **When you receive warnings:**
1077
- 1. **STOP** and read each warning carefully
1078
- 2. **Acknowledge** the warning to the user
1079
- 3. **Explain** how you will avoid the issue
1080
- 4. Only proceed after addressing the warnings
1081
-
1082
- ### Search & Code Intelligence (ContextStream-first)
1083
-
1084
- \u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="hybrid")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
1085
-
1086
- **\u274C WRONG workflow (wastes tokens, slow):**
1087
- \`\`\`
1088
- Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \u2192 finally understand
1089
- \`\`\`
1090
-
1091
- **\u2705 CORRECT workflow (fast, complete):**
1092
- \`\`\`
1093
- search(mode="hybrid", query="function implementation") \u2192 done (results include context)
1094
- \`\`\`
1095
-
1096
- **Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
1097
-
1098
- **Search order:**
1099
- 1. \`session(action="smart_search", query="...")\` - context-enriched
1100
- 2. \`search(mode="hybrid", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
1101
- 3. \`project(action="files")\` - file tree/list (only when needed)
1102
- 4. \`graph(action="dependencies", ...)\` - code structure
1103
- 5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
1104
-
1105
- **Search Mode Selection:**
1106
-
1107
- | Need | Mode | Example |
1108
- |------|------|---------|
1109
- | Find code by meaning | \`hybrid\` | "authentication logic", "error handling" |
1110
- | Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
1111
- | File patterns | \`pattern\` | "*.sql", "test_*.py" |
1112
- | ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
1113
- | Symbol renaming | \`refactor\` | "oldFunctionName" (word-boundary matching) |
1114
- | Conceptual search | \`semantic\` | "how does caching work" |
1115
-
1116
- **Token Efficiency:** Use \`output_format\` to reduce response size:
1117
- - \`full\` (default): Full content for understanding code
1118
- - \`paths\`: File paths only (80% token savings) - use for file listings
1119
- - \`minimal\`: Compact format (60% savings) - use for refactoring
1120
- - \`count\`: Match counts only (90% savings) - use for quick checks
1121
-
1122
- **When to use \`output_format=count\`:**
1123
- - User asks "how many X" or "count of X" \u2192 \`search(..., output_format="count")\`
1124
- - Checking if something exists \u2192 count > 0 is sufficient
1125
- - Large exhaustive searches \u2192 get count first, then fetch if needed
1126
-
1127
- **Auto-suggested formats:** Search responses include \`query_interpretation.suggested_output_format\` when the API detects an optimal format:
1128
- - Symbol queries (e.g., "authOptions") \u2192 suggests \`minimal\` (path + line + snippet)
1129
- - Count queries (e.g., "how many") \u2192 suggests \`count\`
1130
- **USE the suggested format** on subsequent searches for best token efficiency.
1131
-
1132
- **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.
1133
-
1134
- 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.
1135
-
1136
- **Code Analysis:**
1137
- - Dependencies: \`graph(action="dependencies", file_path="...")\`
1138
- - Change impact: \`graph(action="impact", symbol_name="...")\`
1139
- - Call path: \`graph(action="call_path", from_symbol="...", to_symbol="...")\`
1140
- - Build graph: \`graph(action="ingest")\` - async, can take a few minutes
1141
-
1142
- ---
1143
-
1144
- ### Distillation & Memory Hygiene
1145
-
1146
- - Quick context: \`session(action="summary")\`
1147
- - Long chat: \`session(action="compress", content="...")\`
1148
- - Memory summary: \`memory(action="summary")\`
1149
- - Condense noisy entries: \`memory(action="distill_event", event_id="...")\`
1150
-
1151
- ---
1152
-
1153
- ### When to Capture
1154
-
1155
- | When | Call | Example |
1156
- |------|------|---------|
1157
- | User makes decision | \`session(action="capture", event_type="decision", ...)\` | "Let's use PostgreSQL" |
1158
- | User states preference | \`session(action="capture", event_type="preference", ...)\` | "I prefer TypeScript" |
1159
- | Complete significant task | \`session(action="capture", event_type="task", ...)\` | Capture what was done |
1160
- | Need past context | \`session(action="recall", query="...")\` | "What did we decide about X?" |
1161
-
1162
- **DO NOT capture utility operations:**
1163
- - \u274C "Listed workspaces" - not meaningful context
1164
- - \u274C "Showed version" - not a decision
1165
- - \u274C "Listed projects" - just data retrieval
1166
-
1167
- **DO capture meaningful work:**
1168
- - \u2705 Decisions, preferences, completed features
1169
- - \u2705 Lessons from mistakes
1170
- - \u2705 Insights about architecture or patterns
1171
-
1172
- ---
1173
-
1174
- ### \u{1F6A8} Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS \u{1F6A8}
1175
-
1176
- **CRITICAL: When the user requests planning, implementation plans, roadmaps, task breakdowns, or step-by-step approaches:**
1177
-
1178
- \u274C **DO NOT** use built-in plan mode (EnterPlanMode tool)
1179
- \u274C **DO NOT** write plans to markdown files or plan documents
1180
- \u274C **DO NOT** ask "should I create a plan file?"
1181
-
1182
- \u2705 **ALWAYS** use ContextStream's plan/task system instead
1183
-
1184
- **Trigger phrases to detect (use ContextStream immediately):**
1185
- - "create a plan", "make a plan", "plan this", "plan for"
1186
- - "implementation plan", "roadmap", "milestones"
1187
- - "break down", "breakdown", "break this into steps"
1188
- - "what are the steps", "step by step", "outline the approach"
1189
- - "task list", "todo list", "action items"
1190
- - "how should we approach", "implementation strategy"
1191
-
1192
- **When detected, immediately:**
1193
-
1194
- 1. **Create the plan in ContextStream:**
1195
- \`\`\`
1196
- session(action="capture_plan", title="<descriptive title>", description="<what this plan accomplishes>", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1, description: "..."}, ...])
1197
- \`\`\`
1198
-
1199
- 2. **Create tasks for each step:**
1200
- \`\`\`
1201
- memory(action="create_task", title="<task title>", plan_id="<plan_id from step 1>", priority="high|medium|low", description="<detailed task description>")
1202
- \`\`\`
1203
-
1204
- **Why ContextStream plans are better:**
1205
- - Plans persist across sessions and are searchable
1206
- - Tasks track status (pending/in_progress/completed/blocked)
1207
- - Context is preserved with workspace/project association
1208
- - Can be retrieved with \`session(action="get_plan", plan_id="...", include_tasks=true)\`
1209
- - Future sessions can continue from where you left off
1210
-
1211
- **Managing plans/tasks:**
1212
- - List plans: \`session(action="list_plans")\`
1213
- - Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
1214
- - List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
1215
- - Update task status: \`memory(action="update_task", task_id="<uuid>", task_status="pending|in_progress|completed|blocked")\`
1216
- - Link task to plan: \`memory(action="update_task", task_id="<uuid>", plan_id="<plan_uuid>")\`
1217
- - Unlink task from plan: \`memory(action="update_task", task_id="<uuid>", plan_id=null)\`
1218
- - Delete: \`memory(action="delete_task", task_id="<uuid>")\` or \`memory(action="delete_event", event_id="<plan_uuid>")\`
1219
-
1220
- ---
1221
-
1222
- ### Complete Action Reference
1223
-
1224
- **session actions:**
1225
- - \`capture\` - Save decision/insight/task (requires: event_type, title, content)
1226
- - \`capture_lesson\` - Save lesson from mistake (requires: title, category, trigger, impact, prevention)
1227
- - \`get_lessons\` - Retrieve relevant lessons (optional: query, category, severity)
1228
- - \`recall\` - Natural language memory recall (requires: query)
1229
- - \`remember\` - Quick save to memory (requires: content)
1230
- - \`user_context\` - Get user preferences/style
1231
- - \`summary\` - Workspace summary
1232
- - \`compress\` - Compress long conversation
1233
- - \`delta\` - Changes since timestamp
1234
- - \`smart_search\` - Context-enriched search
1235
- - \`decision_trace\` - Trace decision provenance
1236
-
1237
- **memory actions:**
1238
- - Event CRUD: \`create_event\`, \`get_event\`, \`update_event\`, \`delete_event\`, \`list_events\`, \`distill_event\`
1239
- - Node CRUD: \`create_node\`, \`get_node\`, \`update_node\`, \`delete_node\`, \`list_nodes\`, \`supersede_node\`
1240
- - Query: \`search\`, \`decisions\`, \`timeline\`, \`summary\`
1241
-
1242
- **graph actions:**
1243
- - Analysis: \`dependencies\`, \`impact\`, \`call_path\`, \`related\`, \`path\`
1244
- - Quality: \`circular_dependencies\`, \`unused_code\`, \`contradictions\`
1245
- - Management: \`ingest\`, \`decisions\`
1098
+ ContextStream can install hooks for multiple AI code editors to enforce ContextStream-first behavior.
1246
1099
 
1247
- See full documentation: https://contextstream.io/docs/mcp/tools
1248
- `.trim();
1249
- CONTEXTSTREAM_RULES_MINIMAL = `
1250
- ## ContextStream Rules
1100
+ ### Supported Editors
1251
1101
 
1252
- <contextstream_rules>
1253
- 1. **SIMPLE UTILITY?** (list/show/version) \u2192 Just execute, skip context calls
1254
- 2. **CODING TASK?** \u2192 init \u2192 context \u2192 work \u2192 capture
1255
- 3. **SEARCH FIRST:** Before Glob/Grep/Read \u2192 \`search(mode="hybrid")\` FIRST
1256
- </contextstream_rules>
1102
+ | Editor | Hooks Location | Hook Types |
1103
+ |--------|---------------|------------|
1104
+ | **Claude Code** | \`~/.claude/hooks/\` | PreToolUse, UserPromptSubmit, PreCompact |
1105
+ | **Cursor** | \`~/.cursor/hooks/\` | preToolUse, beforeSubmit |
1106
+ | **Cline** | \`~/Documents/Cline/Rules/Hooks/\` | PreToolUse, UserPromptSubmit |
1107
+ | **Roo Code** | \`~/.roo/hooks/\` | PreToolUse, UserPromptSubmit |
1108
+ | **Kilo Code** | \`~/.kilocode/hooks/\` | PreToolUse, UserPromptSubmit |
1257
1109
 
1258
- **Display this block at the start of responses to keep rules in context.**
1110
+ ### Claude Code Hooks
1259
1111
 
1260
- ---
1112
+ ${generateHooksDocumentation()}
1261
1113
 
1262
- ## ContextStream v0.4.x (Hooks Enforced)
1114
+ ### Cursor Hooks
1263
1115
 
1264
- Rules Version: ${RULES_VERSION}
1265
- **Note:** PreToolUse hooks block Glob/Grep/Search when ContextStream is available.
1116
+ Cursor uses a \`hooks.json\` configuration file:
1117
+ - **preToolUse**: Blocks discovery tools before execution
1118
+ - **beforeSubmitPrompt**: Injects ContextStream rules reminder
1266
1119
 
1267
- ### For Coding Tasks
1120
+ #### Output Format
1121
+ \`\`\`json
1122
+ { "decision": "allow" }
1123
+ \`\`\`
1124
+ or
1125
+ \`\`\`json
1126
+ { "decision": "deny", "reason": "Use ContextStream search instead" }
1127
+ \`\`\`
1268
1128
 
1269
- | Action | Tool Call |
1270
- |--------|-----------|
1271
- | **1st message** | \`init(folder_path="<cwd>", context_hint="<msg>")\` then \`context(...)\` |
1272
- | **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
1273
- | **Code search** | \`search(mode="hybrid", query="...")\` \u2014 BEFORE any local tools |
1274
- | **Save decisions** | \`session(action="capture", event_type="decision", ...)\` |
1129
+ ### Cline/Roo/Kilo Code Hooks
1275
1130
 
1276
- ### Search Modes
1131
+ These editors use the same hook format (JSON output):
1132
+ - **PreToolUse**: Blocks discovery tools, redirects to ContextStream search
1133
+ - **UserPromptSubmit**: Injects ContextStream rules reminder
1277
1134
 
1278
- | Mode | Use Case |
1279
- |------|----------|
1280
- | \`hybrid\` | General code search (default) |
1281
- | \`keyword\` | Exact symbol/string match |
1282
- | \`exhaustive\` | Find ALL matches (grep-like) |
1283
- | \`semantic\` | Conceptual questions |
1135
+ Hooks are executable scripts named after the hook type (no extension).
1284
1136
 
1285
- ### Why ContextStream First?
1137
+ #### Output Format
1138
+ \`\`\`json
1139
+ {
1140
+ "cancel": true,
1141
+ "errorMessage": "Use ContextStream search instead",
1142
+ "contextModification": "[CONTEXTSTREAM] Use search tool first"
1143
+ }
1144
+ \`\`\`
1286
1145
 
1287
- \u274C **WRONG:** \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ tool calls, slow)
1288
- \u2705 **CORRECT:** \`search(mode="hybrid")\` (1 call, returns context)
1146
+ ### Installation
1289
1147
 
1290
- ContextStream search is **indexed** and returns semantic matches + context in ONE call.
1148
+ Use \`generate_rules(install_hooks=true, editors=["claude", "cursor", "cline", "roo", "kilo"])\` to install hooks for specific editors, or omit \`editors\` to install for all.
1291
1149
 
1292
- ### Quick Reference
1150
+ ### Disabling Hooks
1293
1151
 
1294
- | Tool | Example |
1295
- |------|---------|
1296
- | \`search\` | \`search(mode="hybrid", query="auth", limit=3)\` |
1297
- | \`session\` | \`session(action="capture", event_type="decision", title="...", content="...")\` |
1298
- | \`memory\` | \`memory(action="list_events", limit=10)\` |
1299
- | \`graph\` | \`graph(action="dependencies", file_path="...")\` |
1300
-
1301
- ### \u{1F680} FAST PATH: Simple Utility Operations
1302
-
1303
- **For simple utility commands, SKIP the ceremony and just execute directly:**
1304
-
1305
- | Command Type | Just Call | Skip |
1306
- |--------------|-----------|------|
1307
- | List workspaces | \`workspace(action="list")\` | init, context, capture |
1308
- | List projects | \`project(action="list")\` | init, context, capture |
1309
- | Show version | \`help(action="version")\` | init, context, capture |
1310
- | List reminders | \`reminder(action="list")\` | init, context, capture |
1311
- | Check auth | \`help(action="auth")\` | init, context, capture |
1312
-
1313
- **Detect simple operations by these patterns:**
1314
- - "list ...", "show ...", "what are my ...", "get ..."
1315
- - Single-action queries with no context dependency
1316
- - User just wants data, not analysis or coding help
1317
-
1318
- **DO NOT add overhead for utility operations:**
1319
- - \u274C Don't call init just to list workspaces
1320
- - \u274C Don't call context for simple queries
1321
- - \u274C Don't capture "listed workspaces" as an event (that's noise)
1322
-
1323
- **Use full context ceremony ONLY for:**
1324
- - Coding tasks (edit, create, refactor, debug)
1325
- - Search/discovery (finding code, understanding architecture)
1326
- - Tasks where past decisions or lessons matter
1152
+ Set environment variables:
1153
+ - \`CONTEXTSTREAM_HOOK_ENABLED=false\` - Disable PreToolUse blocking
1154
+ - \`CONTEXTSTREAM_REMINDER_ENABLED=false\` - Disable UserPromptSubmit reminders
1155
+ `.trim();
1156
+ }
1157
+ var PRETOOLUSE_HOOK_SCRIPT, USER_PROMPT_HOOK_SCRIPT, MEDIA_AWARE_HOOK_SCRIPT, PRECOMPACT_HOOK_SCRIPT, CLINE_PRETOOLUSE_HOOK_SCRIPT, CLINE_USER_PROMPT_HOOK_SCRIPT, CLINE_POSTTOOLUSE_HOOK_SCRIPT, CLINE_HOOK_WRAPPER, CURSOR_PRETOOLUSE_HOOK_SCRIPT, CURSOR_BEFORE_SUBMIT_HOOK_SCRIPT;
1158
+ var init_hooks_config = __esm({
1159
+ "src/hooks-config.ts"() {
1160
+ "use strict";
1161
+ PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
1162
+ """
1163
+ ContextStream PreToolUse Hook for Claude Code
1164
+ Blocks Grep/Glob/Search/Task(Explore)/EnterPlanMode and redirects to ContextStream.
1165
+
1166
+ Only blocks if the current project is indexed in ContextStream.
1167
+ If not indexed, allows local tools through with a suggestion to index.
1168
+ """
1169
+
1170
+ import json
1171
+ import sys
1172
+ import os
1173
+ from pathlib import Path
1174
+ from datetime import datetime, timedelta
1175
+
1176
+ ENABLED = os.environ.get("CONTEXTSTREAM_HOOK_ENABLED", "true").lower() == "true"
1177
+ INDEX_STATUS_FILE = Path.home() / ".contextstream" / "indexed-projects.json"
1178
+ # Consider index stale after 7 days
1179
+ STALE_THRESHOLD_DAYS = 7
1180
+
1181
+ DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"]
1182
+
1183
+ def is_discovery_glob(pattern):
1184
+ pattern_lower = pattern.lower()
1185
+ for p in DISCOVERY_PATTERNS:
1186
+ if p in pattern_lower:
1187
+ return True
1188
+ if pattern_lower.startswith("**/*.") or pattern_lower.startswith("**/"):
1189
+ return True
1190
+ if "**" in pattern or "*/" in pattern:
1191
+ return True
1192
+ return False
1193
+
1194
+ def is_discovery_grep(file_path):
1195
+ if not file_path or file_path in [".", "./", "*", "**"]:
1196
+ return True
1197
+ if "*" in file_path or "**" in file_path:
1198
+ return True
1199
+ return False
1200
+
1201
+ def is_project_indexed(cwd: str) -> tuple[bool, bool]:
1202
+ """
1203
+ Check if the current directory is in an indexed project.
1204
+ Returns (is_indexed, is_stale).
1205
+ """
1206
+ if not INDEX_STATUS_FILE.exists():
1207
+ return False, False
1208
+
1209
+ try:
1210
+ with open(INDEX_STATUS_FILE, "r") as f:
1211
+ data = json.load(f)
1212
+ except:
1213
+ return False, False
1214
+
1215
+ projects = data.get("projects", {})
1216
+ cwd_path = Path(cwd).resolve()
1217
+
1218
+ # Check if cwd is within any indexed project
1219
+ for project_path, info in projects.items():
1220
+ try:
1221
+ indexed_path = Path(project_path).resolve()
1222
+ # Check if cwd is the project or a subdirectory
1223
+ if cwd_path == indexed_path or indexed_path in cwd_path.parents:
1224
+ # Check if stale
1225
+ indexed_at = info.get("indexed_at")
1226
+ if indexed_at:
1227
+ try:
1228
+ indexed_time = datetime.fromisoformat(indexed_at.replace("Z", "+00:00"))
1229
+ if datetime.now(indexed_time.tzinfo) - indexed_time > timedelta(days=STALE_THRESHOLD_DAYS):
1230
+ return True, True # Indexed but stale
1231
+ except:
1232
+ pass
1233
+ return True, False # Indexed and fresh
1234
+ except:
1235
+ continue
1236
+
1237
+ return False, False
1238
+
1239
+ def main():
1240
+ if not ENABLED:
1241
+ sys.exit(0)
1242
+
1243
+ try:
1244
+ data = json.load(sys.stdin)
1245
+ except:
1246
+ sys.exit(0)
1247
+
1248
+ tool = data.get("tool_name", "")
1249
+ inp = data.get("tool_input", {})
1250
+ cwd = data.get("cwd", os.getcwd())
1251
+
1252
+ # Check if project is indexed
1253
+ is_indexed, is_stale = is_project_indexed(cwd)
1254
+
1255
+ if not is_indexed:
1256
+ # Project not indexed - allow local tools but suggest indexing
1257
+ # Don't block, just exit successfully
1258
+ sys.exit(0)
1259
+
1260
+ if is_stale:
1261
+ # Index is stale - allow with warning (printed but not blocking)
1262
+ # Still allow the tool but remind about re-indexing
1263
+ pass # Continue to blocking logic but could add warning
1264
+
1265
+ if tool == "Glob":
1266
+ pattern = inp.get("pattern", "")
1267
+ if is_discovery_glob(pattern):
1268
+ print(f"STOP: Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
1269
+ sys.exit(2)
1270
+
1271
+ elif tool == "Grep" or tool == "Search":
1272
+ # Block ALL Grep/Search operations - use ContextStream search or Read for specific files
1273
+ pattern = inp.get("pattern", "")
1274
+ path = inp.get("path", "")
1275
+ if pattern:
1276
+ if path and not is_discovery_grep(path):
1277
+ # Specific file - suggest Read instead
1278
+ print(f"STOP: Use Read(\\"{path}\\") to view file content, or mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") for codebase search.", file=sys.stderr)
1279
+ else:
1280
+ print(f"STOP: Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
1281
+ sys.exit(2)
1282
+
1283
+ elif tool == "Task":
1284
+ if inp.get("subagent_type", "").lower() == "explore":
1285
+ print("STOP: Use mcp__contextstream__search(mode=\\"hybrid\\") instead of Task(Explore).", file=sys.stderr)
1286
+ sys.exit(2)
1287
+ if inp.get("subagent_type", "").lower() == "plan":
1288
+ print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
1289
+ sys.exit(2)
1290
+
1291
+ elif tool == "EnterPlanMode":
1292
+ print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\", title=\\"...\\", steps=[...]) instead of EnterPlanMode. ContextStream plans persist across sessions and are searchable.", file=sys.stderr)
1293
+ sys.exit(2)
1294
+
1295
+ sys.exit(0)
1296
+
1297
+ if __name__ == "__main__":
1298
+ main()
1299
+ `;
1300
+ USER_PROMPT_HOOK_SCRIPT = `#!/usr/bin/env python3
1301
+ """
1302
+ ContextStream UserPromptSubmit Hook - Injects reminder on every message.
1303
+ """
1327
1304
 
1328
- ### Lessons (Past Mistakes)
1305
+ import json
1306
+ import sys
1307
+ import os
1329
1308
 
1330
- - After \`init\`: Check for \`lessons\` field and apply before work
1331
- - Before risky work: \`session(action="get_lessons", query="<topic>")\`
1332
- - On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
1309
+ ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
1333
1310
 
1334
- ### Context Pressure & Compaction
1311
+ REMINDER = """[CONTEXTSTREAM RULES]
1312
+ 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="hybrid") FIRST
1313
+ 2. Call context_smart at start of EVERY response
1314
+ 3. Local tools ONLY if ContextStream returns 0 results
1315
+ [END RULES]"""
1335
1316
 
1336
- - If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
1337
- - PreCompact hooks automatically save snapshots before compaction (if installed)
1317
+ def main():
1318
+ if not ENABLED:
1319
+ sys.exit(0)
1338
1320
 
1339
- ### Enhanced Context (Warnings)
1321
+ try:
1322
+ json.load(sys.stdin)
1323
+ except:
1324
+ sys.exit(0)
1340
1325
 
1341
- \`context\` returns server-side \`warnings\` for lessons, risky actions, and breaking changes.
1342
- When warnings are present: **STOP**, acknowledge them, explain mitigation, then proceed.
1326
+ print(json.dumps({"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": REMINDER}}))
1327
+ sys.exit(0)
1343
1328
 
1344
- ### Automatic Context Restoration
1329
+ if __name__ == "__main__":
1330
+ main()
1331
+ `;
1332
+ MEDIA_AWARE_HOOK_SCRIPT = `#!/usr/bin/env python3
1333
+ """
1334
+ ContextStream Media-Aware Hook for Claude Code
1345
1335
 
1346
- **Context restoration is now enabled by default.** Every \`init\` call automatically:
1347
- - Restores context from recent snapshots (if available)
1348
- - Returns \`restored_context\` field with snapshot data
1349
- - Sets \`is_post_compact=true\` in response when restoration occurs
1336
+ Detects media-related prompts and injects context about the media tool.
1337
+ """
1350
1338
 
1351
- **No special handling needed after compaction** - just call \`init\` normally.
1339
+ import json
1340
+ import sys
1341
+ import os
1342
+ import re
1352
1343
 
1353
- To disable automatic restoration:
1354
- - Pass \`is_post_compact=false\` in the API call
1355
- - Or set \`CONTEXTSTREAM_RESTORE_CONTEXT=false\` environment variable
1344
+ ENABLED = os.environ.get("CONTEXTSTREAM_MEDIA_HOOK_ENABLED", "true").lower() == "true"
1356
1345
 
1357
- ### Notices - MUST HANDLE IMMEDIATELY
1346
+ # Media patterns (case-insensitive)
1347
+ PATTERNS = [
1348
+ r"\\b(video|videos|clip|clips|footage|keyframe)s?\\b",
1349
+ r"\\b(remotion|timeline|video\\s*edit)\\b",
1350
+ r"\\b(image|images|photo|photos|picture|thumbnail)s?\\b",
1351
+ r"\\b(audio|podcast|transcript|transcription|voice)\\b",
1352
+ r"\\b(media|asset|assets|creative|b-roll)\\b",
1353
+ r"\\b(find|search|show).*(clip|video|image|audio|footage|media)\\b",
1354
+ ]
1358
1355
 
1359
- - **[VERSION_NOTICE]**: Tell the user about the update and command to run
1360
- - **[RULES_NOTICE]**: Run \`generate_rules(overwrite_existing=true)\` to update
1361
- - **[LESSONS_WARNING]**: Read lessons, tell user about them, explain how you'll avoid past mistakes
1356
+ COMPILED = [re.compile(p, re.IGNORECASE) for p in PATTERNS]
1362
1357
 
1363
- ### Plans & Tasks
1358
+ MEDIA_CONTEXT = """[MEDIA TOOLS AVAILABLE]
1359
+ Your workspace may have indexed media. Use ContextStream media tools:
1364
1360
 
1365
- When user asks for a plan, use ContextStream (not EnterPlanMode):
1366
- 1. \`session(action="capture_plan", title="...", steps=[...])\`
1367
- 2. \`memory(action="create_task", title="...", plan_id="<id>")\`
1361
+ - **Search**: \`mcp__contextstream__media(action="search", query="description")\`
1362
+ - **Get clip**: \`mcp__contextstream__media(action="get_clip", content_id="...", start="1:34", end="2:15", output_format="remotion|ffmpeg|raw")\`
1363
+ - **List assets**: \`mcp__contextstream__media(action="list")\`
1364
+ - **Index**: \`mcp__contextstream__media(action="index", file_path="...", content_type="video|audio|image|document")\`
1368
1365
 
1369
- ### Workspace-Only Mode (Multi-Project Folders)
1366
+ For Remotion: use \`output_format="remotion"\` to get frame-based props.
1367
+ [END MEDIA TOOLS]"""
1368
+
1369
+ def matches(text):
1370
+ return any(p.search(text) for p in COMPILED)
1371
+
1372
+ def main():
1373
+ if not ENABLED:
1374
+ sys.exit(0)
1375
+
1376
+ try:
1377
+ data = json.load(sys.stdin)
1378
+ except:
1379
+ sys.exit(0)
1380
+
1381
+ prompt = data.get("prompt", "")
1382
+ if not prompt:
1383
+ session = data.get("session", {})
1384
+ for msg in reversed(session.get("messages", [])):
1385
+ if msg.get("role") == "user":
1386
+ content = msg.get("content", "")
1387
+ prompt = content if isinstance(content, str) else ""
1388
+ if isinstance(content, list):
1389
+ for b in content:
1390
+ if isinstance(b, dict) and b.get("type") == "text":
1391
+ prompt = b.get("text", "")
1392
+ break
1393
+ break
1394
+
1395
+ if not prompt or not matches(prompt):
1396
+ sys.exit(0)
1397
+
1398
+ print(json.dumps({"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": MEDIA_CONTEXT}}))
1399
+ sys.exit(0)
1400
+
1401
+ if __name__ == "__main__":
1402
+ main()
1403
+ `;
1404
+ PRECOMPACT_HOOK_SCRIPT = `#!/usr/bin/env python3
1405
+ """
1406
+ ContextStream PreCompact Hook for Claude Code
1407
+
1408
+ Runs BEFORE conversation context is compacted (manual via /compact or automatic).
1409
+ Automatically saves conversation state to ContextStream by parsing the transcript.
1410
+
1411
+ Input (via stdin):
1412
+ {
1413
+ "session_id": "...",
1414
+ "transcript_path": "/path/to/transcript.jsonl",
1415
+ "permission_mode": "default",
1416
+ "hook_event_name": "PreCompact",
1417
+ "trigger": "manual" | "auto",
1418
+ "custom_instructions": "..."
1419
+ }
1420
+
1421
+ Output (to stdout):
1422
+ {
1423
+ "hookSpecificOutput": {
1424
+ "hookEventName": "PreCompact",
1425
+ "additionalContext": "... status message ..."
1426
+ }
1427
+ }
1428
+ """
1429
+
1430
+ import json
1431
+ import sys
1432
+ import os
1433
+ import re
1434
+ import urllib.request
1435
+ import urllib.error
1436
+
1437
+ ENABLED = os.environ.get("CONTEXTSTREAM_PRECOMPACT_ENABLED", "true").lower() == "true"
1438
+ AUTO_SAVE = os.environ.get("CONTEXTSTREAM_PRECOMPACT_AUTO_SAVE", "true").lower() == "true"
1439
+ API_URL = os.environ.get("CONTEXTSTREAM_API_URL", "https://api.contextstream.io")
1440
+ API_KEY = os.environ.get("CONTEXTSTREAM_API_KEY", "")
1441
+
1442
+ WORKSPACE_ID = None
1443
+
1444
+ def load_config_from_mcp_json(cwd):
1445
+ """Load API config from .mcp.json if env vars not set."""
1446
+ global API_URL, API_KEY, WORKSPACE_ID
1447
+
1448
+ # Try to find .mcp.json and .contextstream/config.json in cwd or parent directories
1449
+ search_dir = cwd
1450
+ for _ in range(5): # Search up to 5 levels
1451
+ # Load API config from .mcp.json
1452
+ if not API_KEY:
1453
+ mcp_path = os.path.join(search_dir, ".mcp.json")
1454
+ if os.path.exists(mcp_path):
1455
+ try:
1456
+ with open(mcp_path, 'r') as f:
1457
+ config = json.load(f)
1458
+ servers = config.get("mcpServers", {})
1459
+ cs_config = servers.get("contextstream", {})
1460
+ env = cs_config.get("env", {})
1461
+ if env.get("CONTEXTSTREAM_API_KEY"):
1462
+ API_KEY = env["CONTEXTSTREAM_API_KEY"]
1463
+ if env.get("CONTEXTSTREAM_API_URL"):
1464
+ API_URL = env["CONTEXTSTREAM_API_URL"]
1465
+ except:
1466
+ pass
1467
+
1468
+ # Load workspace_id from .contextstream/config.json
1469
+ if not WORKSPACE_ID:
1470
+ cs_config_path = os.path.join(search_dir, ".contextstream", "config.json")
1471
+ if os.path.exists(cs_config_path):
1472
+ try:
1473
+ with open(cs_config_path, 'r') as f:
1474
+ cs_config = json.load(f)
1475
+ if cs_config.get("workspace_id"):
1476
+ WORKSPACE_ID = cs_config["workspace_id"]
1477
+ except:
1478
+ pass
1479
+
1480
+ parent = os.path.dirname(search_dir)
1481
+ if parent == search_dir:
1482
+ break
1483
+ search_dir = parent
1484
+
1485
+ def parse_transcript(transcript_path):
1486
+ """Parse transcript to extract active files, decisions, and context."""
1487
+ active_files = set()
1488
+ recent_messages = []
1489
+ tool_calls = []
1490
+
1491
+ try:
1492
+ with open(transcript_path, 'r') as f:
1493
+ for line in f:
1494
+ try:
1495
+ entry = json.loads(line.strip())
1496
+ msg_type = entry.get("type", "")
1497
+
1498
+ # Extract files from tool calls
1499
+ if msg_type == "tool_use":
1500
+ tool_name = entry.get("name", "")
1501
+ tool_input = entry.get("input", {})
1502
+ tool_calls.append({"name": tool_name, "input": tool_input})
1503
+
1504
+ # Extract file paths from common tools
1505
+ if tool_name in ["Read", "Write", "Edit", "NotebookEdit"]:
1506
+ file_path = tool_input.get("file_path") or tool_input.get("notebook_path")
1507
+ if file_path:
1508
+ active_files.add(file_path)
1509
+ elif tool_name == "Glob":
1510
+ pattern = tool_input.get("pattern", "")
1511
+ if pattern:
1512
+ active_files.add(f"[glob:{pattern}]")
1513
+
1514
+ # Collect recent assistant messages for summary
1515
+ if msg_type == "assistant" and entry.get("content"):
1516
+ content = entry.get("content", "")
1517
+ if isinstance(content, str) and len(content) > 50:
1518
+ recent_messages.append(content[:500])
1519
+
1520
+ except json.JSONDecodeError:
1521
+ continue
1522
+ except Exception as e:
1523
+ pass
1370
1524
 
1371
- If working in a parent folder containing multiple projects:
1372
- \`\`\`
1373
- init(folder_path="...", skip_project_creation=true)
1374
- \`\`\`
1525
+ return {
1526
+ "active_files": list(active_files)[-20:], # Last 20 files
1527
+ "tool_call_count": len(tool_calls),
1528
+ "message_count": len(recent_messages),
1529
+ "last_tools": [t["name"] for t in tool_calls[-10:]], # Last 10 tool names
1530
+ }
1531
+
1532
+ def save_snapshot(session_id, transcript_data, trigger):
1533
+ """Save snapshot to ContextStream API."""
1534
+ if not API_KEY:
1535
+ return False, "No API key configured"
1536
+
1537
+ snapshot_content = {
1538
+ "session_id": session_id,
1539
+ "trigger": trigger,
1540
+ "captured_at": None, # API will set timestamp
1541
+ "active_files": transcript_data.get("active_files", []),
1542
+ "tool_call_count": transcript_data.get("tool_call_count", 0),
1543
+ "last_tools": transcript_data.get("last_tools", []),
1544
+ "auto_captured": True,
1545
+ }
1546
+
1547
+ payload = {
1548
+ "event_type": "session_snapshot",
1549
+ "title": f"Auto Pre-compaction Snapshot ({trigger})",
1550
+ "content": json.dumps(snapshot_content),
1551
+ "importance": "high",
1552
+ "tags": ["session_snapshot", "pre_compaction", "auto_captured"],
1553
+ "source_type": "hook",
1554
+ }
1555
+
1556
+ # Add workspace_id if available
1557
+ if WORKSPACE_ID:
1558
+ payload["workspace_id"] = WORKSPACE_ID
1559
+
1560
+ try:
1561
+ req = urllib.request.Request(
1562
+ f"{API_URL}/api/v1/memory/events",
1563
+ data=json.dumps(payload).encode('utf-8'),
1564
+ headers={
1565
+ "Content-Type": "application/json",
1566
+ "X-API-Key": API_KEY,
1567
+ },
1568
+ method="POST"
1569
+ )
1570
+ with urllib.request.urlopen(req, timeout=5) as resp:
1571
+ return True, "Snapshot saved"
1572
+ except urllib.error.URLError as e:
1573
+ return False, str(e)
1574
+ except Exception as e:
1575
+ return False, str(e)
1576
+
1577
+ def main():
1578
+ if not ENABLED:
1579
+ sys.exit(0)
1580
+
1581
+ try:
1582
+ data = json.load(sys.stdin)
1583
+ except:
1584
+ sys.exit(0)
1585
+
1586
+ # Load config from .mcp.json if env vars not set
1587
+ cwd = data.get("cwd", os.getcwd())
1588
+ load_config_from_mcp_json(cwd)
1589
+
1590
+ session_id = data.get("session_id", "unknown")
1591
+ transcript_path = data.get("transcript_path", "")
1592
+ trigger = data.get("trigger", "unknown")
1593
+ custom_instructions = data.get("custom_instructions", "")
1594
+
1595
+ # Parse transcript for context
1596
+ transcript_data = {}
1597
+ if transcript_path and os.path.exists(transcript_path):
1598
+ transcript_data = parse_transcript(transcript_path)
1599
+
1600
+ # Auto-save snapshot if enabled
1601
+ auto_save_status = ""
1602
+ if AUTO_SAVE and API_KEY:
1603
+ success, msg = save_snapshot(session_id, transcript_data, trigger)
1604
+ if success:
1605
+ auto_save_status = f"\\n[ContextStream: Auto-saved snapshot with {len(transcript_data.get('active_files', []))} active files]"
1606
+ else:
1607
+ auto_save_status = f"\\n[ContextStream: Auto-save failed - {msg}]"
1608
+
1609
+ # Build context injection for the AI (backup in case auto-save fails)
1610
+ files_list = ", ".join(transcript_data.get("active_files", [])[:5]) or "none detected"
1611
+ context = f"""[CONTEXT COMPACTION - {trigger.upper()}]{auto_save_status}
1612
+
1613
+ Active files detected: {files_list}
1614
+ Tool calls in session: {transcript_data.get('tool_call_count', 0)}
1615
+
1616
+ After compaction, call session_init(is_post_compact=true) to restore context.
1617
+ {f"User instructions: {custom_instructions}" if custom_instructions else ""}"""
1618
+
1619
+ output = {
1620
+ "hookSpecificOutput": {
1621
+ "hookEventName": "PreCompact",
1622
+ "additionalContext": context
1623
+ }
1624
+ }
1625
+
1626
+ print(json.dumps(output))
1627
+ sys.exit(0)
1628
+
1629
+ if __name__ == "__main__":
1630
+ main()
1631
+ `;
1632
+ CLINE_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
1633
+ """
1634
+ ContextStream PreToolUse Hook for Cline
1635
+ Blocks discovery tools and redirects to ContextStream search.
1636
+
1637
+ Cline hooks use JSON output format:
1638
+ {
1639
+ "cancel": true/false,
1640
+ "errorMessage": "optional error description",
1641
+ "contextModification": "optional text to inject"
1642
+ }
1643
+ """
1644
+
1645
+ import json
1646
+ import sys
1647
+ import os
1648
+ from pathlib import Path
1649
+ from datetime import datetime, timedelta
1650
+
1651
+ ENABLED = os.environ.get("CONTEXTSTREAM_HOOK_ENABLED", "true").lower() == "true"
1652
+ INDEX_STATUS_FILE = Path.home() / ".contextstream" / "indexed-projects.json"
1653
+ STALE_THRESHOLD_DAYS = 7
1654
+
1655
+ DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"]
1656
+
1657
+ def is_discovery_glob(pattern):
1658
+ pattern_lower = pattern.lower()
1659
+ for p in DISCOVERY_PATTERNS:
1660
+ if p in pattern_lower:
1661
+ return True
1662
+ if pattern_lower.startswith("**/*.") or pattern_lower.startswith("**/"):
1663
+ return True
1664
+ if "**" in pattern or "*/" in pattern:
1665
+ return True
1666
+ return False
1667
+
1668
+ def is_discovery_grep(file_path):
1669
+ if not file_path or file_path in [".", "./", "*", "**"]:
1670
+ return True
1671
+ if "*" in file_path or "**" in file_path:
1672
+ return True
1673
+ return False
1674
+
1675
+ def is_project_indexed(workspace_roots):
1676
+ """Check if any workspace root is in an indexed project."""
1677
+ if not INDEX_STATUS_FILE.exists():
1678
+ return False, False
1679
+
1680
+ try:
1681
+ with open(INDEX_STATUS_FILE, "r") as f:
1682
+ data = json.load(f)
1683
+ except:
1684
+ return False, False
1685
+
1686
+ projects = data.get("projects", {})
1687
+
1688
+ for workspace in workspace_roots:
1689
+ cwd_path = Path(workspace).resolve()
1690
+ for project_path, info in projects.items():
1691
+ try:
1692
+ indexed_path = Path(project_path).resolve()
1693
+ if cwd_path == indexed_path or indexed_path in cwd_path.parents:
1694
+ indexed_at = info.get("indexed_at")
1695
+ if indexed_at:
1696
+ try:
1697
+ indexed_time = datetime.fromisoformat(indexed_at.replace("Z", "+00:00"))
1698
+ if datetime.now(indexed_time.tzinfo) - indexed_time > timedelta(days=STALE_THRESHOLD_DAYS):
1699
+ return True, True
1700
+ except:
1701
+ pass
1702
+ return True, False
1703
+ except:
1704
+ continue
1705
+ return False, False
1706
+
1707
+ def output_allow(context_mod=None):
1708
+ result = {"cancel": False}
1709
+ if context_mod:
1710
+ result["contextModification"] = context_mod
1711
+ print(json.dumps(result))
1712
+ sys.exit(0)
1713
+
1714
+ def output_block(error_msg, context_mod=None):
1715
+ result = {"cancel": True, "errorMessage": error_msg}
1716
+ if context_mod:
1717
+ result["contextModification"] = context_mod
1718
+ print(json.dumps(result))
1719
+ sys.exit(0)
1720
+
1721
+ def main():
1722
+ if not ENABLED:
1723
+ output_allow()
1724
+
1725
+ try:
1726
+ data = json.load(sys.stdin)
1727
+ except:
1728
+ output_allow()
1729
+
1730
+ hook_name = data.get("hookName", "")
1731
+ if hook_name != "PreToolUse":
1732
+ output_allow()
1733
+
1734
+ tool = data.get("toolName", "")
1735
+ params = data.get("toolParameters", {})
1736
+ workspace_roots = data.get("workspaceRoots", [])
1737
+
1738
+ # Check if project is indexed
1739
+ is_indexed, is_stale = is_project_indexed(workspace_roots)
1740
+ if not is_indexed:
1741
+ output_allow()
1742
+
1743
+ # Check for discovery patterns
1744
+ if tool == "list_files" or tool == "search_files":
1745
+ pattern = params.get("path", "") or params.get("regex", "")
1746
+ if is_discovery_glob(pattern) or is_discovery_grep(pattern):
1747
+ output_block(
1748
+ f"Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}. "
1749
+ "ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
1750
+ "[CONTEXTSTREAM] Use ContextStream search for code discovery."
1751
+ )
1752
+
1753
+ elif tool == "read_file":
1754
+ # Allow read_file by default - blocking discovery at search level is enough
1755
+ pass
1756
+
1757
+ output_allow()
1758
+
1759
+ if __name__ == "__main__":
1760
+ main()
1761
+ `;
1762
+ CLINE_USER_PROMPT_HOOK_SCRIPT = `#!/usr/bin/env python3
1763
+ """
1764
+ ContextStream UserPromptSubmit Hook for Cline
1765
+ Injects reminder about ContextStream rules on every message.
1766
+ """
1375
1767
 
1376
- This enables workspace-level memory and context without project-specific indexing.
1377
- Use for monorepos or folders with multiple independent projects.
1768
+ import json
1769
+ import sys
1770
+ import os
1378
1771
 
1379
- Full docs: https://contextstream.io/docs/mcp/tools
1380
- `.trim();
1381
- TEMPLATES = {
1382
- codex: {
1383
- filename: "AGENTS.md",
1384
- description: "Codex CLI agent instructions",
1385
- build: (rules) => `# Codex CLI Instructions
1386
- ${rules}
1387
- `
1388
- },
1389
- cursor: {
1390
- filename: ".cursorrules",
1391
- description: "Cursor AI rules",
1392
- build: (rules) => `# Cursor Rules
1393
- ${rules}
1394
- `
1395
- },
1396
- cline: {
1397
- filename: ".clinerules",
1398
- description: "Cline AI rules",
1399
- build: (rules) => `# Cline Rules
1400
- ${rules}
1401
- `
1402
- },
1403
- kilo: {
1404
- filename: ".kilocode/rules/contextstream.md",
1405
- description: "Kilo Code AI rules",
1406
- build: (rules) => `# Kilo Code Rules
1407
- ${rules}
1408
- `
1409
- },
1410
- roo: {
1411
- filename: ".roo/rules/contextstream.md",
1412
- description: "Roo Code AI rules",
1413
- build: (rules) => `# Roo Code Rules
1414
- ${rules}
1415
- `
1416
- },
1417
- claude: {
1418
- filename: "CLAUDE.md",
1419
- description: "Claude Code instructions",
1420
- build: (rules) => `# Claude Code Instructions
1421
- ${rules}
1422
- `
1423
- },
1424
- aider: {
1425
- filename: ".aider.conf.yml",
1426
- description: "Aider configuration with system prompt",
1427
- build: (rules) => `# Aider Configuration
1428
- # Note: Aider uses different config format - this adds to the system prompt
1772
+ ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
1429
1773
 
1430
- # Add ContextStream guidance to conventions
1431
- conventions: |
1432
- ${rules.split("\n").map((line) => " " + line).join("\n")}
1433
- `
1434
- },
1435
- antigravity: {
1436
- filename: "GEMINI.md",
1437
- description: "Google Antigravity AI rules",
1438
- build: (rules) => `# Antigravity Agent Rules
1439
- ${rules}
1440
- `
1441
- }
1442
- };
1774
+ REMINDER = """[CONTEXTSTREAM RULES]
1775
+ 1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="hybrid") FIRST
1776
+ 2. Call context_smart at start of EVERY response
1777
+ 3. Local tools ONLY if ContextStream returns 0 results
1778
+ [END RULES]"""
1779
+
1780
+ def main():
1781
+ if not ENABLED:
1782
+ print(json.dumps({"cancel": False}))
1783
+ sys.exit(0)
1784
+
1785
+ try:
1786
+ json.load(sys.stdin)
1787
+ except:
1788
+ print(json.dumps({"cancel": False}))
1789
+ sys.exit(0)
1790
+
1791
+ print(json.dumps({
1792
+ "cancel": False,
1793
+ "contextModification": REMINDER
1794
+ }))
1795
+ sys.exit(0)
1796
+
1797
+ if __name__ == "__main__":
1798
+ main()
1799
+ `;
1800
+ CLINE_POSTTOOLUSE_HOOK_SCRIPT = `#!/bin/bash
1801
+ # ContextStream PostToolUse Hook for Cline/Roo/Kilo Code
1802
+ # Indexes files after Edit/Write/NotebookEdit operations for real-time search updates.
1803
+ #
1804
+ # The hook receives JSON on stdin with tool_name and toolParameters.
1805
+ # Only runs for write operations (write_to_file, edit_file).
1806
+
1807
+ TOOL_NAME=$(cat | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('toolName', d.get('tool_name', '')))" 2>/dev/null)
1808
+
1809
+ case "$TOOL_NAME" in
1810
+ write_to_file|edit_file|Write|Edit|NotebookEdit)
1811
+ npx @contextstream/mcp-server hook post-write
1812
+ ;;
1813
+ esac
1814
+
1815
+ exit 0
1816
+ `;
1817
+ CLINE_HOOK_WRAPPER = (hookName) => `#!/bin/bash
1818
+ # ContextStream ${hookName} Hook Wrapper for Cline/Roo/Kilo Code
1819
+ # Calls the Node.js hook via npx
1820
+ exec npx @contextstream/mcp-server hook ${hookName}
1821
+ `;
1822
+ CURSOR_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
1823
+ """
1824
+ ContextStream PreToolUse Hook for Cursor
1825
+ Blocks discovery tools and redirects to ContextStream search.
1826
+
1827
+ Cursor hooks use JSON output format:
1828
+ {
1829
+ "decision": "allow" | "deny",
1830
+ "reason": "optional error description"
1831
+ }
1832
+ """
1833
+
1834
+ import json
1835
+ import sys
1836
+ import os
1837
+ from pathlib import Path
1838
+ from datetime import datetime, timedelta
1839
+
1840
+ ENABLED = os.environ.get("CONTEXTSTREAM_HOOK_ENABLED", "true").lower() == "true"
1841
+ INDEX_STATUS_FILE = Path.home() / ".contextstream" / "indexed-projects.json"
1842
+ STALE_THRESHOLD_DAYS = 7
1843
+
1844
+ DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"]
1845
+
1846
+ def is_discovery_glob(pattern):
1847
+ pattern_lower = pattern.lower()
1848
+ for p in DISCOVERY_PATTERNS:
1849
+ if p in pattern_lower:
1850
+ return True
1851
+ if pattern_lower.startswith("**/*.") or pattern_lower.startswith("**/"):
1852
+ return True
1853
+ if "**" in pattern or "*/" in pattern:
1854
+ return True
1855
+ return False
1856
+
1857
+ def is_discovery_grep(file_path):
1858
+ if not file_path or file_path in [".", "./", "*", "**"]:
1859
+ return True
1860
+ if "*" in file_path or "**" in file_path:
1861
+ return True
1862
+ return False
1863
+
1864
+ def is_project_indexed(workspace_roots):
1865
+ """Check if any workspace root is in an indexed project."""
1866
+ if not INDEX_STATUS_FILE.exists():
1867
+ return False, False
1868
+
1869
+ try:
1870
+ with open(INDEX_STATUS_FILE, "r") as f:
1871
+ data = json.load(f)
1872
+ except:
1873
+ return False, False
1874
+
1875
+ projects = data.get("projects", {})
1876
+
1877
+ for workspace in workspace_roots:
1878
+ cwd_path = Path(workspace).resolve()
1879
+ for project_path, info in projects.items():
1880
+ try:
1881
+ indexed_path = Path(project_path).resolve()
1882
+ if cwd_path == indexed_path or indexed_path in cwd_path.parents:
1883
+ indexed_at = info.get("indexed_at")
1884
+ if indexed_at:
1885
+ try:
1886
+ indexed_time = datetime.fromisoformat(indexed_at.replace("Z", "+00:00"))
1887
+ if datetime.now(indexed_time.tzinfo) - indexed_time > timedelta(days=STALE_THRESHOLD_DAYS):
1888
+ return True, True
1889
+ except:
1890
+ pass
1891
+ return True, False
1892
+ except:
1893
+ continue
1894
+ return False, False
1895
+
1896
+ def output_allow():
1897
+ print(json.dumps({"decision": "allow"}))
1898
+ sys.exit(0)
1899
+
1900
+ def output_deny(reason):
1901
+ print(json.dumps({"decision": "deny", "reason": reason}))
1902
+ sys.exit(0)
1903
+
1904
+ def main():
1905
+ if not ENABLED:
1906
+ output_allow()
1907
+
1908
+ try:
1909
+ data = json.load(sys.stdin)
1910
+ except:
1911
+ output_allow()
1912
+
1913
+ hook_name = data.get("hook_event_name", "")
1914
+ if hook_name != "preToolUse":
1915
+ output_allow()
1916
+
1917
+ tool = data.get("tool_name", "")
1918
+ params = data.get("tool_input", {}) or data.get("parameters", {})
1919
+ workspace_roots = data.get("workspace_roots", [])
1920
+
1921
+ # Check if project is indexed
1922
+ is_indexed, _ = is_project_indexed(workspace_roots)
1923
+ if not is_indexed:
1924
+ output_allow()
1925
+
1926
+ # Check for Cursor tools
1927
+ if tool in ["Glob", "glob", "list_files"]:
1928
+ pattern = params.get("pattern", "") or params.get("path", "")
1929
+ if is_discovery_glob(pattern):
1930
+ output_deny(
1931
+ f"Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}. "
1932
+ "ContextStream search is indexed and faster."
1933
+ )
1934
+
1935
+ elif tool in ["Grep", "grep", "search_files", "ripgrep"]:
1936
+ pattern = params.get("pattern", "") or params.get("regex", "")
1937
+ file_path = params.get("path", "")
1938
+ if is_discovery_grep(file_path):
1939
+ output_deny(
1940
+ f"Use mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") instead of {tool}. "
1941
+ "ContextStream search is indexed and faster."
1942
+ )
1943
+
1944
+ output_allow()
1945
+
1946
+ if __name__ == "__main__":
1947
+ main()
1948
+ `;
1949
+ CURSOR_BEFORE_SUBMIT_HOOK_SCRIPT = `#!/usr/bin/env python3
1950
+ """
1951
+ ContextStream BeforeSubmitPrompt Hook for Cursor
1952
+ Injects reminder about ContextStream rules.
1953
+ """
1954
+
1955
+ import json
1956
+ import sys
1957
+ import os
1958
+
1959
+ ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
1960
+
1961
+ def main():
1962
+ if not ENABLED:
1963
+ print(json.dumps({"continue": True}))
1964
+ sys.exit(0)
1965
+
1966
+ try:
1967
+ json.load(sys.stdin)
1968
+ except:
1969
+ print(json.dumps({"continue": True}))
1970
+ sys.exit(0)
1971
+
1972
+ print(json.dumps({
1973
+ "continue": True,
1974
+ "user_message": "[CONTEXTSTREAM] Search with mcp__contextstream__search before using Glob/Grep/Read"
1975
+ }))
1976
+ sys.exit(0)
1977
+
1978
+ if __name__ == "__main__":
1979
+ main()
1980
+ `;
1443
1981
  }
1444
1982
  });
1445
1983
 
@@ -2502,14 +3040,49 @@ function extractCwd3(input) {
2502
3040
  if (input.cwd) return input.cwd;
2503
3041
  return process.cwd();
2504
3042
  }
2505
- async function generateRulesForFolder(folderPath) {
2506
- const { generateAllRuleFiles: generateAllRuleFiles2 } = await Promise.resolve().then(() => (init_rules_templates(), rules_templates_exports));
2507
- await generateAllRuleFiles2({
2508
- folderPath,
2509
- editors: ["cursor", "cline", "kilo", "roo", "claude", "aider", "codex"],
2510
- overwriteExisting: true,
2511
- mode: "minimal"
2512
- // Use minimal mode for auto-updates
3043
+ function hasPythonHooks(settingsPath) {
3044
+ try {
3045
+ if (!fs11.existsSync(settingsPath)) return false;
3046
+ const content = fs11.readFileSync(settingsPath, "utf-8");
3047
+ const settings = JSON.parse(content);
3048
+ const hooks = settings.hooks;
3049
+ if (!hooks) return false;
3050
+ for (const hookType of Object.keys(hooks)) {
3051
+ const matchers = hooks[hookType];
3052
+ if (!Array.isArray(matchers)) continue;
3053
+ for (const matcher of matchers) {
3054
+ const hookList = matcher.hooks;
3055
+ if (!Array.isArray(hookList)) continue;
3056
+ for (const hook of hookList) {
3057
+ const cmd = hook.command || "";
3058
+ if (cmd.includes("python3") && cmd.includes("contextstream")) {
3059
+ return true;
3060
+ }
3061
+ }
3062
+ }
3063
+ }
3064
+ return false;
3065
+ } catch {
3066
+ return false;
3067
+ }
3068
+ }
3069
+ function detectPythonHooks(cwd) {
3070
+ const globalSettingsPath = path12.join(homedir9(), ".claude", "settings.json");
3071
+ const projectSettingsPath = path12.join(cwd, ".claude", "settings.json");
3072
+ return {
3073
+ global: hasPythonHooks(globalSettingsPath),
3074
+ project: hasPythonHooks(projectSettingsPath)
3075
+ };
3076
+ }
3077
+ async function upgradeHooksForFolder(folderPath) {
3078
+ const { installClaudeCodeHooks: installClaudeCodeHooks3 } = await Promise.resolve().then(() => (init_hooks_config(), hooks_config_exports));
3079
+ await installClaudeCodeHooks3({
3080
+ scope: "both",
3081
+ projectPath: folderPath,
3082
+ includePreCompact: true,
3083
+ includeMediaAware: true,
3084
+ includePostWrite: true,
3085
+ includeAutoRules: true
2513
3086
  });
2514
3087
  }
2515
3088
  async function runAutoRulesHook() {
@@ -2537,14 +3110,17 @@ async function runAutoRulesHook() {
2537
3110
  if (!isContextTool) {
2538
3111
  process.exit(0);
2539
3112
  }
3113
+ const cwd = extractCwd3(input);
3114
+ const pythonHooks = detectPythonHooks(cwd);
3115
+ const hasPythonHooksToUpgrade = pythonHooks.global || pythonHooks.project;
2540
3116
  const rulesNotice = extractRulesNotice(input);
2541
- if (!rulesNotice || rulesNotice.status === "current") {
3117
+ const rulesNeedUpdate = rulesNotice && rulesNotice.status !== "current";
3118
+ if (!hasPythonHooksToUpgrade && !rulesNeedUpdate) {
2542
3119
  process.exit(0);
2543
3120
  }
2544
- const cwd = extractCwd3(input);
2545
- const folderPath = rulesNotice.update_args?.folder_path || cwd;
3121
+ const folderPath = rulesNotice?.update_args?.folder_path || cwd;
2546
3122
  try {
2547
- await generateRulesForFolder(folderPath);
3123
+ await upgradeHooksForFolder(folderPath);
2548
3124
  markAsRan();
2549
3125
  } catch {
2550
3126
  }
@@ -6611,8 +7187,176 @@ var coerce = {
6611
7187
  };
6612
7188
  var NEVER = INVALID;
6613
7189
 
7190
+ // src/version.ts
7191
+ import { createRequire } from "module";
7192
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
7193
+ import { homedir } from "os";
7194
+ import { join } from "path";
7195
+ var UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
7196
+ var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
7197
+ function getVersion() {
7198
+ try {
7199
+ const require2 = createRequire(import.meta.url);
7200
+ const pkg = require2("../package.json");
7201
+ const version = pkg?.version;
7202
+ if (typeof version === "string" && version.trim()) return version.trim();
7203
+ } catch {
7204
+ }
7205
+ return "unknown";
7206
+ }
7207
+ var VERSION = getVersion();
7208
+ function compareVersions(v1, v2) {
7209
+ const parts1 = v1.split(".").map(Number);
7210
+ const parts2 = v2.split(".").map(Number);
7211
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
7212
+ const p1 = parts1[i] ?? 0;
7213
+ const p2 = parts2[i] ?? 0;
7214
+ if (p1 < p2) return -1;
7215
+ if (p1 > p2) return 1;
7216
+ }
7217
+ return 0;
7218
+ }
7219
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
7220
+ var latestVersionPromise = null;
7221
+ function getCacheFilePath() {
7222
+ return join(homedir(), ".contextstream", "version-cache.json");
7223
+ }
7224
+ function readCache() {
7225
+ try {
7226
+ const cacheFile = getCacheFilePath();
7227
+ if (!existsSync(cacheFile)) return null;
7228
+ const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
7229
+ if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
7230
+ return data;
7231
+ } catch {
7232
+ return null;
7233
+ }
7234
+ }
7235
+ function writeCache(latestVersion) {
7236
+ try {
7237
+ const configDir = join(homedir(), ".contextstream");
7238
+ if (!existsSync(configDir)) {
7239
+ mkdirSync(configDir, { recursive: true });
7240
+ }
7241
+ const cacheFile = getCacheFilePath();
7242
+ writeFileSync(
7243
+ cacheFile,
7244
+ JSON.stringify({
7245
+ latestVersion,
7246
+ checkedAt: Date.now()
7247
+ })
7248
+ );
7249
+ } catch {
7250
+ }
7251
+ }
7252
+ async function fetchLatestVersion() {
7253
+ try {
7254
+ const controller = new AbortController();
7255
+ const timeout = setTimeout(() => controller.abort(), 5e3);
7256
+ const response = await fetch(NPM_LATEST_URL, {
7257
+ signal: controller.signal,
7258
+ headers: { Accept: "application/json" }
7259
+ });
7260
+ clearTimeout(timeout);
7261
+ if (!response.ok) return null;
7262
+ const data = await response.json();
7263
+ return typeof data.version === "string" ? data.version : null;
7264
+ } catch {
7265
+ return null;
7266
+ }
7267
+ }
7268
+ async function resolveLatestVersion() {
7269
+ const cached = readCache();
7270
+ if (cached) return cached.latestVersion;
7271
+ if (!latestVersionPromise) {
7272
+ latestVersionPromise = fetchLatestVersion().finally(() => {
7273
+ latestVersionPromise = null;
7274
+ });
7275
+ }
7276
+ const latestVersion = await latestVersionPromise;
7277
+ if (latestVersion) {
7278
+ writeCache(latestVersion);
7279
+ }
7280
+ return latestVersion;
7281
+ }
7282
+ async function checkForUpdates() {
7283
+ const notice = await getUpdateNotice();
7284
+ if (notice?.behind) {
7285
+ showUpdateWarning(notice.current, notice.latest);
7286
+ }
7287
+ }
7288
+ function showUpdateWarning(currentVersion, latestVersion) {
7289
+ console.error("");
7290
+ console.error("\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");
7291
+ console.error(`\u26A0\uFE0F Update available: v${currentVersion} \u2192 v${latestVersion}`);
7292
+ console.error("");
7293
+ console.error(` Run: ${UPGRADE_COMMAND}`);
7294
+ console.error("");
7295
+ console.error(" Then restart your AI tool to use the new version.");
7296
+ console.error("\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");
7297
+ console.error("");
7298
+ }
7299
+ async function getUpdateNotice() {
7300
+ const currentVersion = VERSION;
7301
+ if (currentVersion === "unknown") return null;
7302
+ try {
7303
+ const latestVersion = await resolveLatestVersion();
7304
+ if (!latestVersion) return null;
7305
+ if (compareVersions(currentVersion, latestVersion) < 0) {
7306
+ return {
7307
+ current: currentVersion,
7308
+ latest: latestVersion,
7309
+ behind: true,
7310
+ upgrade_command: UPGRADE_COMMAND
7311
+ };
7312
+ }
7313
+ } catch {
7314
+ }
7315
+ return null;
7316
+ }
7317
+ function getVersionsBehind(current, latest) {
7318
+ try {
7319
+ const currentParts = current.split(".").map(Number);
7320
+ const latestParts = latest.split(".").map(Number);
7321
+ if ((latestParts[0] ?? 0) > (currentParts[0] ?? 0)) {
7322
+ return 10 + ((latestParts[1] ?? 0) - (currentParts[1] ?? 0));
7323
+ }
7324
+ const minorDiff = (latestParts[1] ?? 0) - (currentParts[1] ?? 0);
7325
+ const patchDiff = (latestParts[2] ?? 0) - (currentParts[2] ?? 0);
7326
+ if (minorDiff > 0) {
7327
+ return minorDiff;
7328
+ } else if (minorDiff === 0 && patchDiff > 0) {
7329
+ return 1;
7330
+ }
7331
+ return 0;
7332
+ } catch {
7333
+ return 0;
7334
+ }
7335
+ }
7336
+ function getVersionWarning(notice) {
7337
+ if (!notice?.behind) return null;
7338
+ const versionsBehind = getVersionsBehind(notice.current, notice.latest);
7339
+ if (versionsBehind >= 3) {
7340
+ return `[VERSION_CRITICAL] MCP server is ${versionsBehind} versions behind (${notice.current} \u2192 ${notice.latest}). Update immediately: ${notice.upgrade_command}`;
7341
+ } else if (versionsBehind >= 1) {
7342
+ return `[VERSION_NOTICE] MCP server update available (${notice.current} \u2192 ${notice.latest}). Run: ${notice.upgrade_command}`;
7343
+ }
7344
+ return null;
7345
+ }
7346
+ function getVersionInstructions(notice) {
7347
+ if (!notice?.behind) return null;
7348
+ const versionsBehind = getVersionsBehind(notice.current, notice.latest);
7349
+ if (versionsBehind >= 5) {
7350
+ return `\u26A0\uFE0F CRITICAL: User's MCP is ${versionsBehind} versions behind. Mention update in EVERY response until updated.`;
7351
+ } else if (versionsBehind >= 3) {
7352
+ return `\u26A0\uFE0F User's MCP is ${versionsBehind} versions behind. Remind about update every 2-3 messages.`;
7353
+ } else if (versionsBehind >= 1) {
7354
+ return `Note: MCP update available (${notice.current}\u2192${notice.latest}). Mention once at session start.`;
7355
+ }
7356
+ return null;
7357
+ }
7358
+
6614
7359
  // src/config.ts
6615
- init_version();
6616
7360
  var DEFAULT_API_URL = "https://api.contextstream.io";
6617
7361
  function parseBooleanEnv(value) {
6618
7362
  if (value === void 0) return void 0;
@@ -7471,7 +8215,6 @@ var CacheKeys = {
7471
8215
  var globalCache = new MemoryCache();
7472
8216
 
7473
8217
  // src/client.ts
7474
- init_version();
7475
8218
  var uuidSchema = external_exports.string().uuid();
7476
8219
  function unwrapApiResponse(result) {
7477
8220
  if (!result || typeof result !== "object") return result;
@@ -8915,6 +9658,19 @@ var ContextStreamClient = class {
8915
9658
  }
8916
9659
  } catch {
8917
9660
  }
9661
+ try {
9662
+ const rememberItems = await this.getHighPriorityRememberItems({
9663
+ workspace_id: workspaceId,
9664
+ project_id: projectId,
9665
+ context_hint: params.context_hint,
9666
+ limit: 5
9667
+ });
9668
+ if (rememberItems.length > 0) {
9669
+ context.remember_items = rememberItems;
9670
+ context.remember_warning = `\u{1F4CC} ${rememberItems.length} user preference(s) to remember. ALWAYS check these before making changes.`;
9671
+ }
9672
+ } catch {
9673
+ }
8918
9674
  } catch (e) {
8919
9675
  console.error(
8920
9676
  "[ContextStream] Batched endpoint failed, falling back to individual calls:",
@@ -9892,6 +10648,26 @@ ${context2}`;
9892
10648
  } catch (e) {
9893
10649
  errors.push(`lessons: ${e?.message || "fetch failed"}`);
9894
10650
  }
10651
+ try {
10652
+ const rememberItems = await this.getHighPriorityRememberItems({
10653
+ workspace_id: withDefaults.workspace_id,
10654
+ project_id: withDefaults.project_id,
10655
+ context_hint: params.user_message,
10656
+ limit: 5
10657
+ });
10658
+ for (const item of rememberItems) {
10659
+ const prefix = item.importance === "critical" ? "\u{1F6A8} " : "\u{1F4CC} ";
10660
+ items.push({
10661
+ type: "R",
10662
+ key: "remember",
10663
+ value: `${prefix}${item.content.slice(0, 150)}`,
10664
+ relevance: 1
10665
+ // Remember items are ALWAYS highest priority
10666
+ });
10667
+ }
10668
+ } catch (e) {
10669
+ errors.push(`remember: ${e?.message || "fetch failed"}`);
10670
+ }
9895
10671
  if (errors.length > 0) {
9896
10672
  console.error("[ContextStream] context_smart errors:", errors.join(", "));
9897
10673
  }
@@ -10044,6 +10820,38 @@ ${context}`;
10044
10820
  return [];
10045
10821
  }
10046
10822
  }
10823
+ /**
10824
+ * Get high-priority remember items that should be surfaced proactively.
10825
+ * These are user-flagged important items that should always be checked.
10826
+ */
10827
+ async getHighPriorityRememberItems(params) {
10828
+ const limit = params.limit || 5;
10829
+ try {
10830
+ const searchQuery = params.context_hint ? `${params.context_hint} user preference remember important` : "user preference remember important always_surface";
10831
+ const searchResult = await this.memorySearch({
10832
+ query: searchQuery,
10833
+ workspace_id: params.workspace_id,
10834
+ project_id: params.project_id,
10835
+ limit: limit * 2
10836
+ // Fetch more to filter
10837
+ });
10838
+ if (!searchResult?.results) return [];
10839
+ const rememberItems = searchResult.results.filter((item) => {
10840
+ const tags = item.metadata?.tags || [];
10841
+ return tags.includes("user_remember") || tags.includes("always_surface");
10842
+ }).slice(0, limit).map((item) => {
10843
+ const importance = item.metadata?.importance || "high";
10844
+ return {
10845
+ content: item.content || item.title || "",
10846
+ importance,
10847
+ created_at: item.occurred_at
10848
+ };
10849
+ });
10850
+ return rememberItems;
10851
+ } catch {
10852
+ return [];
10853
+ }
10854
+ }
10047
10855
  /**
10048
10856
  * Extract keywords from a message for relevance matching
10049
10857
  */
@@ -11254,659 +12062,951 @@ ${context}`;
11254
12062
  }
11255
12063
  };
11256
12064
 
11257
- // src/tools.ts
11258
- import * as fs5 from "node:fs";
11259
- import * as path6 from "node:path";
11260
- import { homedir as homedir3 } from "node:os";
11261
- init_rules_templates();
11262
- init_version();
12065
+ // src/tools.ts
12066
+ import * as fs5 from "node:fs";
12067
+ import * as path6 from "node:path";
12068
+ import { homedir as homedir3 } from "node:os";
12069
+
12070
+ // src/rules-templates.ts
12071
+ var DEFAULT_CLAUDE_MCP_SERVER_NAME = "contextstream";
12072
+ var RULES_VERSION = VERSION === "unknown" ? "0.0.0" : VERSION;
12073
+ var CONTEXTSTREAM_TOOL_NAMES = [
12074
+ // Standalone tools (always present)
12075
+ "init",
12076
+ // Renamed from session_init - initialize conversation session
12077
+ "context",
12078
+ // Renamed from context_smart - get relevant context every message
12079
+ "context_feedback",
12080
+ "generate_rules",
12081
+ // Consolidated domain tools (v0.4.x default)
12082
+ "search",
12083
+ // Modes: semantic, hybrid, keyword, pattern
12084
+ "session",
12085
+ // Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
12086
+ "memory",
12087
+ // 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
12088
+ "graph",
12089
+ // Actions: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions
12090
+ "project",
12091
+ // Actions: list, get, create, update, index, overview, statistics, files, index_status, ingest_local
12092
+ "workspace",
12093
+ // Actions: list, get, associate, bootstrap
12094
+ "reminder",
12095
+ // Actions: list, active, create, snooze, complete, dismiss
12096
+ "integration",
12097
+ // Provider: slack, github, all; Actions: status, search, stats, activity, contributors, knowledge, summary, channels, discussions, sync_users, repos, issues
12098
+ "help"
12099
+ // Actions: tools, auth, version, editor_rules, enable_bundle
12100
+ ];
12101
+ function applyMcpToolPrefix(markdown, toolPrefix) {
12102
+ const toolPattern = CONTEXTSTREAM_TOOL_NAMES.join("|");
12103
+ const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
12104
+ return markdown.replace(toolRegex, `${toolPrefix}$1`);
12105
+ }
12106
+ var CONTEXTSTREAM_RULES_DYNAMIC = `
12107
+ # ContextStream Rules (MANDATORY)
12108
+
12109
+ ## \u{1F6A8} CRITICAL: Call These Tools or Fail \u{1F6A8}
12110
+
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)
12115
+
12116
+ **BEFORE Glob/Grep/Read/Search/Explore:**
12117
+ \u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
12118
+
12119
+ **HOOKS: Follow \`<system-reminder>\` instructions \u2014 they ARE the rules**
12120
+ </contextstream_protocol>
12121
+
12122
+ ### Why This Matters
12123
+
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**
12128
+
12129
+ ### Protocol
12130
+
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", ...)\` |
12138
+
12139
+ ### Handle Notices from context()
12140
+
12141
+ - **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
12142
+ - **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
12143
+ - **[VERSION_NOTICE]** \u2192 Tell user to update MCP
12144
+
12145
+ ### Fast Path (Skip context only for these)
12146
+
12147
+ Simple utility commands: "list workspaces", "show version", "list reminders"
12148
+ \u2192 Just execute directly: \`workspace(action="list")\`, \`help(action="version")\`
12149
+
12150
+ Rules Version: ${RULES_VERSION}
12151
+ `.trim();
12152
+ var CONTEXTSTREAM_RULES_FULL = `
12153
+ ## ContextStream Rules
12154
+
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>
12160
+
12161
+ **Display this block at the start of responses to keep rules in context.**
12162
+
12163
+ ---
12164
+
12165
+ ## \u{1F6A8} CRITICAL RULE #1 - CONTEXTSTREAM SEARCH FIRST \u{1F6A8}
12166
+
12167
+ **BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
12168
+ \`\`\`
12169
+ STOP \u2192 Call search(mode="hybrid", query="...") FIRST
12170
+ \`\`\`
12171
+
12172
+ **Note:** PreToolUse hooks block these tools when ContextStream is available.
12173
+ **Claude Code users:** Your tool names are \`mcp__contextstream__search\`, \`mcp__contextstream__init\`, etc.
12174
+
12175
+ \u274C **NEVER DO THIS:**
12176
+ - \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
12177
+ - \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
12178
+ - \`Read(file)\` for discovery \u2192 Use \`search(mode="hybrid", query="...")\` instead
12179
+ - \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="hybrid")\` instead
12180
+
12181
+ \u2705 **ALWAYS DO THIS:**
12182
+ 1. \`search(mode="hybrid", query="what you're looking for")\`
12183
+ 2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
12184
+ 3. Use Read ONLY for exact file edits after you know the file path
12185
+
12186
+ This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
12187
+
12188
+ ---
12189
+
12190
+ ## \u{1F6A8} CRITICAL RULE #2 - AUTO-INDEXING \u{1F6A8}
12191
+
12192
+ **ContextStream auto-indexes your project on \`init\`.** You do NOT need to:
12193
+ - Ask the user to index
12194
+ - Manually trigger ingestion
12195
+ - Check index_status before every search
12196
+
12197
+ **When \`init\` returns \`indexing_status: "started"\` or \`"refreshing"\`:**
12198
+ - Background indexing is running automatically
12199
+ - Search results will be available within seconds to minutes
12200
+ - **DO NOT fall back to local tools** - wait for ContextStream search to work
12201
+ - If search returns 0 results initially, try again after a moment
12202
+
12203
+ **Only manually trigger indexing if:**
12204
+ - \`init\` returned \`ingest_recommendation.recommended: true\` (rare edge case)
12205
+ - User explicitly asks to re-index
12206
+
12207
+ ---
12208
+
12209
+ ## \u{1F6A8} CRITICAL RULE #3 - LESSONS (PAST MISTAKES) \u{1F6A8}
12210
+
12211
+ **Lessons are past mistakes that MUST inform your work.** Ignoring lessons leads to repeated failures.
12212
+
12213
+ ### On \`init\`:
12214
+ - Check for \`lessons\` and \`lessons_warning\` in the response
12215
+ - If present, **READ THEM IMMEDIATELY** before doing any work
12216
+ - These are high-priority lessons (critical/high severity) relevant to your context
12217
+ - **Apply the prevention steps** from each lesson to avoid repeating mistakes
12218
+
12219
+ ### On \`context\`:
12220
+ - Check for \`[LESSONS_WARNING]\` tag in the response
12221
+ - If present, you **MUST** tell the user about the lessons before proceeding
12222
+ - Lessons are proactively fetched when risky actions are detected (refactor, migrate, deploy, etc.)
12223
+ - **Do not skip or bury this warning** - lessons represent real past mistakes
12224
+
12225
+ ### Before ANY Non-Trivial Work:
12226
+ **ALWAYS call \`session(action="get_lessons", query="<topic>")\`** where \`<topic>\` matches what you're about to do:
12227
+ - Before refactoring \u2192 \`session(action="get_lessons", query="refactoring")\`
12228
+ - Before API changes \u2192 \`session(action="get_lessons", query="API changes")\`
12229
+ - Before database work \u2192 \`session(action="get_lessons", query="database migrations")\`
12230
+ - Before deployments \u2192 \`session(action="get_lessons", query="deployment")\`
12231
+
12232
+ ### When Lessons Are Found:
12233
+ 1. **Summarize the lessons** to the user before proceeding
12234
+ 2. **Explicitly state how you will avoid the past mistakes**
12235
+ 3. If a lesson conflicts with the current approach, **warn the user**
12236
+
12237
+ **Failing to check lessons before risky work is a critical error.**
12238
+
12239
+ ---
12240
+
12241
+ ## ContextStream v0.4.x Integration (Enhanced)
12242
+
12243
+ You have access to ContextStream MCP tools for persistent memory and context.
12244
+ v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
12245
+ Rules Version: ${RULES_VERSION}
12246
+
12247
+ ## TL;DR - WHEN TO USE CONTEXT
12248
+
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", ...)\` |
12256
+
12257
+ ### Simple Utility Operations - FAST PATH
12258
+
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
12264
+
12265
+ **No init. No context. No capture.** These add noise, not value.
12266
+
12267
+ ### Coding Tasks - FULL CONTEXT
12268
+
12269
+ | Step | What to Call |
12270
+ |------|--------------|
12271
+ | **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
12272
+ | **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
12273
+ | **Code search** | \`search(mode="hybrid", query="...")\` \u2014 BEFORE Glob/Grep/Read |
12274
+ | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
12275
+ | **User correction** | \`session(action="capture_lesson", ...)\` |
12276
+ | **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
12277
+
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\`:
12284
+ 1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
12285
+ 2. Then call \`context\` before any other tool or response
12286
+
12287
+ **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).
12288
+
12289
+ **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.
12290
+
12291
+ ---
12292
+
12293
+ ## Consolidated Domain Tools Architecture
12294
+
12295
+ v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode dispatch:
12296
+
12297
+ ### Standalone Tools
12298
+ - **\`init\`** - Initialize session with workspace detection + context (skip for simple utility operations)
12299
+ - **\`context\`** - Semantic search for relevant context (skip for simple utility operations)
12300
+
12301
+ ### Domain Tools (Use action/mode parameter)
12302
+
12303
+ | Domain | Actions/Modes | Example |
12304
+ |--------|---------------|---------|
12305
+ | **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="hybrid", query="auth implementation", limit=3)\` |
12306
+ | **\`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="...")\` |
12307
+ | **\`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)\` |
12308
+ | **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
12309
+ | **\`project\`** | action: list, get, create, update, index, overview, statistics, files, index_status, ingest_local | \`project(action="statistics")\` |
12310
+ | **\`workspace\`** | action: list, get, associate, bootstrap | \`workspace(action="list")\` |
12311
+ | **\`reminder\`** | action: list, active, create, snooze, complete, dismiss | \`reminder(action="active")\` |
12312
+ | **\`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="...")\` |
12313
+ | **\`help\`** | action: tools, auth, version, editor_rules, enable_bundle | \`help(action="tools")\` |
12314
+
12315
+ ---
12316
+
12317
+ ### Why context is Required (Even After init)
12318
+
12319
+ **Common mistake:** "init already gave me context, I don't need context"
12320
+
12321
+ **This is WRONG. Here's why:**
12322
+ - \`init\` returns the last ~10 items **BY TIME** (chronological)
12323
+ - \`context\` **SEARCHES** for items **RELEVANT to THIS message** (semantic)
12324
+
12325
+ **Example failure:**
12326
+ - User asks: "how should I implement authentication?"
12327
+ - Auth decisions were made 20 conversations ago
12328
+ - \`init\` won't have it (too old, not in recent 10)
12329
+ - \`context\` FINDS it via semantic search
12330
+
12331
+ **Without context, you WILL miss relevant older context.**
12332
+
12333
+ ---
12334
+
12335
+ ### Recommended Token Budgets
12336
+
12337
+ - For trivial/local edits: \`context(..., max_tokens=200)\`
12338
+ - Default: \`context(..., max_tokens=400)\`
12339
+ - Deep debugging/architecture: \`context(..., max_tokens=800)\`
12340
+ - Keep \`format="minified"\` (default) unless debugging
12341
+
12342
+ If context still feels missing, use \`session(action="recall", query="...")\` for focused deep lookup.
12343
+
12344
+ ---
12345
+
12346
+ ### Rules, Version & Lessons Notices
12347
+
12348
+ **[RULES_NOTICE]** - Update rules via \`generate_rules()\` (or rerun setup).
12349
+
12350
+ **[VERSION_NOTICE]** - You **MUST IMMEDIATELY** tell the user:
12351
+ 1. A new MCP server version is available
12352
+ 2. The exact update command to run
12353
+ 3. That they need to restart their AI tool after updating
12354
+ **Do not skip this** - users often miss stderr warnings.
12355
+
12356
+ **[LESSONS_WARNING]** - You **MUST** before proceeding:
12357
+ 1. Read all lessons listed
12358
+ 2. Tell the user about relevant lessons
12359
+ 3. Explain how you will avoid each past mistake
12360
+ **This is critical** - ignoring lessons leads to repeated failures.
12361
+
12362
+ ---
12363
+
12364
+ ### Preferences & Lessons (Use Early)
12365
+
12366
+ - If preferences/style matter: \`session(action="user_context")\`
12367
+ - Before risky changes: \`session(action="get_lessons", query="<topic>")\`
12368
+ - On frustration/corrections: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
12369
+
12370
+ ---
12371
+
12372
+ ### Context Pressure & Compaction Awareness
12373
+
12374
+ ContextStream tracks context pressure to help you stay ahead of conversation compaction:
12375
+
12376
+ **Automatic tracking:** Token usage is tracked automatically. \`context\` returns \`context_pressure\` when usage is high.
12377
+
12378
+ **When \`context\` returns \`context_pressure\` with high/critical level:**
12379
+ 1. Review the \`suggested_action\` field:
12380
+ - \`prepare_save\`: Start thinking about saving important state
12381
+ - \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
12382
+
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)\`
12385
+
12386
+ **Before compaction happens (when warned):**
12387
+ \`\`\`
12388
+ session(action="capture", event_type="session_snapshot", title="Pre-compaction snapshot", content="{
12389
+ \\"conversation_summary\\": \\"<summarize what we've been doing>\\",
12390
+ \\"current_goal\\": \\"<the main task>\\",
12391
+ \\"active_files\\": [\\"file1.ts\\", \\"file2.ts\\"],
12392
+ \\"recent_decisions\\": [{title: \\"...\\", rationale: \\"...\\"}],
12393
+ \\"unfinished_work\\": [{task: \\"...\\", status: \\"...\\", next_steps: \\"...\\"}]
12394
+ }")
12395
+ \`\`\`
12396
+
12397
+ **After compaction (when context seems lost):**
12398
+ 1. Call \`init(folder_path="...", is_post_compact=true)\` - this auto-restores the most recent snapshot
12399
+ 2. Or call \`session_restore_context()\` directly to get the saved state
12400
+ 3. Review the \`restored_context\` to understand prior work
12401
+ 4. Acknowledge to the user what was restored and continue
12402
+
12403
+ ---
12404
+
12405
+ ### Index Status (Auto-Managed)
12406
+
12407
+ **Indexing is automatic.** After \`init\`, the project is auto-indexed in the background.
12408
+
12409
+ **You do NOT need to manually check index_status before every search.** Just use \`search()\`.
12410
+
12411
+ **If search returns 0 results and you expected matches:**
12412
+ 1. Check if \`init\` returned \`indexing_status: "started"\` - indexing may still be in progress
12413
+ 2. Wait a moment and retry \`search()\`
12414
+ 3. Only as a last resort: \`project(action="index_status")\` to check
12415
+
12416
+ **Graph data:** If graph queries (\`dependencies\`, \`impact\`) return empty, run \`graph(action="ingest")\` once.
12417
+
12418
+ **NEVER fall back to local tools (Glob/Grep/Read) just because search returned 0 results on first try.** Retry first.
12419
+
12420
+ ### Enhanced Context (Server-Side Warnings)
12421
+
12422
+ \`context\` now includes **intelligent server-side filtering** that proactively surfaces relevant warnings:
12423
+
12424
+ **Response fields:**
12425
+ - \`warnings\`: Array of warning strings (displayed with \u26A0\uFE0F prefix)
12426
+
12427
+ **What triggers warnings:**
12428
+ - **Lessons**: Past mistakes relevant to the current query (via semantic matching)
12429
+ - **Risky actions**: Detected high-risk operations (deployments, migrations, destructive commands)
12430
+ - **Breaking changes**: When modifications may impact other parts of the codebase
12431
+
12432
+ **When you receive warnings:**
12433
+ 1. **STOP** and read each warning carefully
12434
+ 2. **Acknowledge** the warning to the user
12435
+ 3. **Explain** how you will avoid the issue
12436
+ 4. Only proceed after addressing the warnings
12437
+
12438
+ ### Search & Code Intelligence (ContextStream-first)
12439
+
12440
+ \u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="hybrid")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
12441
+
12442
+ **\u274C WRONG workflow (wastes tokens, slow):**
12443
+ \`\`\`
12444
+ Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \u2192 finally understand
12445
+ \`\`\`
12446
+
12447
+ **\u2705 CORRECT workflow (fast, complete):**
12448
+ \`\`\`
12449
+ search(mode="hybrid", query="function implementation") \u2192 done (results include context)
12450
+ \`\`\`
12451
+
12452
+ **Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
12453
+
12454
+ **Search order:**
12455
+ 1. \`session(action="smart_search", query="...")\` - context-enriched
12456
+ 2. \`search(mode="hybrid", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
12457
+ 3. \`project(action="files")\` - file tree/list (only when needed)
12458
+ 4. \`graph(action="dependencies", ...)\` - code structure
12459
+ 5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
12460
+
12461
+ **Search Mode Selection:**
12462
+
12463
+ | Need | Mode | Example |
12464
+ |------|------|---------|
12465
+ | Find code by meaning | \`hybrid\` | "authentication logic", "error handling" |
12466
+ | Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
12467
+ | File patterns | \`pattern\` | "*.sql", "test_*.py" |
12468
+ | ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
12469
+ | Symbol renaming | \`refactor\` | "oldFunctionName" (word-boundary matching) |
12470
+ | Conceptual search | \`semantic\` | "how does caching work" |
12471
+
12472
+ **Token Efficiency:** Use \`output_format\` to reduce response size:
12473
+ - \`full\` (default): Full content for understanding code
12474
+ - \`paths\`: File paths only (80% token savings) - use for file listings
12475
+ - \`minimal\`: Compact format (60% savings) - use for refactoring
12476
+ - \`count\`: Match counts only (90% savings) - use for quick checks
12477
+
12478
+ **When to use \`output_format=count\`:**
12479
+ - User asks "how many X" or "count of X" \u2192 \`search(..., output_format="count")\`
12480
+ - Checking if something exists \u2192 count > 0 is sufficient
12481
+ - Large exhaustive searches \u2192 get count first, then fetch if needed
12482
+
12483
+ **Auto-suggested formats:** Search responses include \`query_interpretation.suggested_output_format\` when the API detects an optimal format:
12484
+ - Symbol queries (e.g., "authOptions") \u2192 suggests \`minimal\` (path + line + snippet)
12485
+ - Count queries (e.g., "how many") \u2192 suggests \`count\`
12486
+ **USE the suggested format** on subsequent searches for best token efficiency.
12487
+
12488
+ **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.
12489
+
12490
+ 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.
12491
+
12492
+ **Code Analysis:**
12493
+ - Dependencies: \`graph(action="dependencies", file_path="...")\`
12494
+ - Change impact: \`graph(action="impact", symbol_name="...")\`
12495
+ - Call path: \`graph(action="call_path", from_symbol="...", to_symbol="...")\`
12496
+ - Build graph: \`graph(action="ingest")\` - async, can take a few minutes
12497
+
12498
+ ---
12499
+
12500
+ ### Distillation & Memory Hygiene
12501
+
12502
+ - Quick context: \`session(action="summary")\`
12503
+ - Long chat: \`session(action="compress", content="...")\`
12504
+ - Memory summary: \`memory(action="summary")\`
12505
+ - Condense noisy entries: \`memory(action="distill_event", event_id="...")\`
12506
+
12507
+ ---
12508
+
12509
+ ### When to Capture
12510
+
12511
+ | When | Call | Example |
12512
+ |------|------|---------|
12513
+ | User makes decision | \`session(action="capture", event_type="decision", ...)\` | "Let's use PostgreSQL" |
12514
+ | User states preference | \`session(action="capture", event_type="preference", ...)\` | "I prefer TypeScript" |
12515
+ | Complete significant task | \`session(action="capture", event_type="task", ...)\` | Capture what was done |
12516
+ | Need past context | \`session(action="recall", query="...")\` | "What did we decide about X?" |
12517
+
12518
+ **DO NOT capture utility operations:**
12519
+ - \u274C "Listed workspaces" - not meaningful context
12520
+ - \u274C "Showed version" - not a decision
12521
+ - \u274C "Listed projects" - just data retrieval
12522
+
12523
+ **DO capture meaningful work:**
12524
+ - \u2705 Decisions, preferences, completed features
12525
+ - \u2705 Lessons from mistakes
12526
+ - \u2705 Insights about architecture or patterns
12527
+
12528
+ ---
11263
12529
 
11264
- // src/tool-catalog.ts
11265
- var TOOL_CATALOG = [
11266
- {
11267
- name: "Session",
11268
- tools: [
11269
- { name: "init", hint: "start-conv" },
11270
- { name: "smart", hint: "each-msg" },
11271
- { name: "capture", hint: "save" },
11272
- { name: "recall", hint: "find" },
11273
- { name: "remember", hint: "quick" },
11274
- { name: "compress", hint: "end" },
11275
- { name: "summary", hint: "brief" },
11276
- { name: "delta", hint: "changes" },
11277
- { name: "get_lessons", hint: "learn" },
11278
- { name: "capture_lesson", hint: "mistake" },
11279
- { name: "get_user_context", hint: "prefs" },
11280
- { name: "smart_search", hint: "deep-find" },
11281
- // Plan actions
11282
- { name: "capture_plan", hint: "save-plan" },
11283
- { name: "get_plan", hint: "get-plan" },
11284
- { name: "update_plan", hint: "edit-plan" },
11285
- { name: "list_plans", hint: "list-plans" }
11286
- ]
11287
- },
11288
- {
11289
- name: "Search",
11290
- tools: [
11291
- { name: "semantic", hint: "meaning" },
11292
- { name: "hybrid", hint: "combo" },
11293
- { name: "keyword", hint: "exact" },
11294
- { name: "pattern", hint: "code" }
11295
- ]
11296
- },
11297
- {
11298
- name: "Memory",
11299
- tools: [
11300
- { name: "create_event", hint: "new" },
11301
- { name: "list_events", hint: "list" },
11302
- { name: "get_event", hint: "get" },
11303
- { name: "update_event", hint: "edit" },
11304
- { name: "delete_event", hint: "rm" },
11305
- { name: "search", hint: "find" },
11306
- { name: "decisions", hint: "choices" },
11307
- { name: "timeline", hint: "history" },
11308
- { name: "distill_event", hint: "extract" },
11309
- // Task actions
11310
- { name: "create_task", hint: "new-task" },
11311
- { name: "get_task", hint: "get-task" },
11312
- { name: "update_task", hint: "edit-task" },
11313
- { name: "delete_task", hint: "rm-task" },
11314
- { name: "list_tasks", hint: "list-tasks" },
11315
- { name: "reorder_tasks", hint: "sort-tasks" }
11316
- ]
11317
- },
11318
- {
11319
- name: "Knowledge",
11320
- tools: [
11321
- { name: "create_node", hint: "new" },
11322
- { name: "list_nodes", hint: "list" },
11323
- { name: "get_node", hint: "get" },
11324
- { name: "update_node", hint: "edit" },
11325
- { name: "delete_node", hint: "rm" },
11326
- { name: "supersede_node", hint: "replace" }
11327
- ]
11328
- },
11329
- {
11330
- name: "Graph",
11331
- tools: [
11332
- { name: "related", hint: "links" },
11333
- { name: "path", hint: "trace" },
11334
- { name: "decisions", hint: "choices" },
11335
- { name: "dependencies", hint: "deps" },
11336
- { name: "ingest", hint: "build" },
11337
- { name: "impact", hint: "changes" },
11338
- { name: "contradictions", hint: "conflicts" }
11339
- ]
11340
- },
11341
- {
11342
- name: "Media",
11343
- tools: [
11344
- { name: "index", hint: "add-media" },
11345
- { name: "status", hint: "progress" },
11346
- { name: "search", hint: "find-clip" },
11347
- { name: "get_clip", hint: "get-segment" },
11348
- { name: "list", hint: "browse" },
11349
- { name: "delete", hint: "remove" }
11350
- ]
11351
- },
11352
- {
11353
- name: "Workspace",
11354
- tools: [
11355
- { name: "list", hint: "" },
11356
- { name: "get", hint: "" },
11357
- { name: "create", hint: "" },
11358
- { name: "associate", hint: "link-folder" },
11359
- { name: "bootstrap", hint: "new-ws" }
11360
- ]
11361
- },
11362
- {
11363
- name: "Project",
11364
- tools: [
11365
- { name: "list", hint: "" },
11366
- { name: "get", hint: "" },
11367
- { name: "create", hint: "" },
11368
- { name: "index", hint: "scan-code" },
11369
- { name: "files", hint: "list-files" },
11370
- { name: "overview", hint: "summary" }
11371
- ]
11372
- },
11373
- {
11374
- name: "AI",
11375
- tools: [
11376
- { name: "context", hint: "smart-ctx" },
11377
- { name: "plan", hint: "generate" },
11378
- { name: "tasks", hint: "breakdown" },
11379
- { name: "embeddings", hint: "vectors" }
11380
- ]
11381
- },
11382
- {
11383
- name: "Notion",
11384
- tools: [
11385
- { name: "create_page", hint: "new-page" },
11386
- { name: "search_pages", hint: "find" },
11387
- { name: "list_databases", hint: "list-dbs" },
11388
- { name: "get_page", hint: "get" },
11389
- { name: "query_database", hint: "query-db" },
11390
- { name: "update_page", hint: "edit" },
11391
- { name: "stats", hint: "overview" },
11392
- { name: "activity", hint: "recent" },
11393
- { name: "knowledge", hint: "insights" },
11394
- { name: "summary", hint: "brief" }
11395
- ]
11396
- }
11397
- ];
11398
- function generateToolCatalog(format = "grouped", category) {
11399
- let categories = TOOL_CATALOG;
11400
- if (category) {
11401
- const filtered = TOOL_CATALOG.filter((c) => c.name.toLowerCase() === category.toLowerCase());
11402
- if (filtered.length > 0) {
11403
- categories = filtered;
11404
- }
11405
- }
11406
- switch (format) {
11407
- case "minimal":
11408
- return generateMinimal(categories);
11409
- case "full":
11410
- return generateFull(categories);
11411
- case "grouped":
11412
- default:
11413
- return generateGrouped(categories);
11414
- }
11415
- }
11416
- function generateGrouped(categories) {
11417
- return categories.map((cat) => {
11418
- const tools = cat.tools.map((t) => t.hint ? `${t.name}(${t.hint})` : t.name).join(" ");
11419
- return `${cat.name}: ${tools}`;
11420
- }).join("\n");
11421
- }
11422
- function generateMinimal(categories) {
11423
- return categories.map((cat) => {
11424
- const tools = cat.tools.map((t) => t.name).join("|");
11425
- return `${cat.name}:${tools}`;
11426
- }).join("\n");
11427
- }
11428
- function generateFull(categories) {
11429
- const lines = [];
11430
- for (const cat of categories) {
11431
- lines.push(`## ${cat.name}`);
11432
- for (const tool of cat.tools) {
11433
- const prefix = cat.name.toLowerCase().replace(/\s+/g, "_");
11434
- const fullName = `${prefix}_${tool.name}`;
11435
- lines.push(`- ${fullName}: ${tool.hint || "standard CRUD"}`);
11436
- }
11437
- }
11438
- return lines.join("\n");
11439
- }
11440
- function getCoreToolsHint() {
11441
- return `Session: init(start) smart(each-msg) capture(save) recall(find) remember(quick)`;
11442
- }
12530
+ ### \u{1F6A8} Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS \u{1F6A8}
12531
+
12532
+ **CRITICAL: When the user requests planning, implementation plans, roadmaps, task breakdowns, or step-by-step approaches:**
12533
+
12534
+ \u274C **DO NOT** use built-in plan mode (EnterPlanMode tool)
12535
+ \u274C **DO NOT** write plans to markdown files or plan documents
12536
+ \u274C **DO NOT** ask "should I create a plan file?"
12537
+
12538
+ \u2705 **ALWAYS** use ContextStream's plan/task system instead
12539
+
12540
+ **Trigger phrases to detect (use ContextStream immediately):**
12541
+ - "create a plan", "make a plan", "plan this", "plan for"
12542
+ - "implementation plan", "roadmap", "milestones"
12543
+ - "break down", "breakdown", "break this into steps"
12544
+ - "what are the steps", "step by step", "outline the approach"
12545
+ - "task list", "todo list", "action items"
12546
+ - "how should we approach", "implementation strategy"
12547
+
12548
+ **When detected, immediately:**
12549
+
12550
+ 1. **Create the plan in ContextStream:**
12551
+ \`\`\`
12552
+ session(action="capture_plan", title="<descriptive title>", description="<what this plan accomplishes>", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1, description: "..."}, ...])
12553
+ \`\`\`
12554
+
12555
+ 2. **Create tasks for each step:**
12556
+ \`\`\`
12557
+ memory(action="create_task", title="<task title>", plan_id="<plan_id from step 1>", priority="high|medium|low", description="<detailed task description>")
12558
+ \`\`\`
12559
+
12560
+ **Why ContextStream plans are better:**
12561
+ - Plans persist across sessions and are searchable
12562
+ - Tasks track status (pending/in_progress/completed/blocked)
12563
+ - Context is preserved with workspace/project association
12564
+ - Can be retrieved with \`session(action="get_plan", plan_id="...", include_tasks=true)\`
12565
+ - Future sessions can continue from where you left off
12566
+
12567
+ **Managing plans/tasks:**
12568
+ - List plans: \`session(action="list_plans")\`
12569
+ - Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
12570
+ - List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
12571
+ - Update task status: \`memory(action="update_task", task_id="<uuid>", task_status="pending|in_progress|completed|blocked")\`
12572
+ - Link task to plan: \`memory(action="update_task", task_id="<uuid>", plan_id="<plan_uuid>")\`
12573
+ - Unlink task from plan: \`memory(action="update_task", task_id="<uuid>", plan_id=null)\`
12574
+ - Delete: \`memory(action="delete_task", task_id="<uuid>")\` or \`memory(action="delete_event", event_id="<plan_uuid>")\`
12575
+
12576
+ ---
12577
+
12578
+ ### Complete Action Reference
12579
+
12580
+ **session actions:**
12581
+ - \`capture\` - Save decision/insight/task (requires: event_type, title, content)
12582
+ - \`capture_lesson\` - Save lesson from mistake (requires: title, category, trigger, impact, prevention)
12583
+ - \`get_lessons\` - Retrieve relevant lessons (optional: query, category, severity)
12584
+ - \`recall\` - Natural language memory recall (requires: query)
12585
+ - \`remember\` - Quick save to memory (requires: content)
12586
+ - \`user_context\` - Get user preferences/style
12587
+ - \`summary\` - Workspace summary
12588
+ - \`compress\` - Compress long conversation
12589
+ - \`delta\` - Changes since timestamp
12590
+ - \`smart_search\` - Context-enriched search
12591
+ - \`decision_trace\` - Trace decision provenance
12592
+
12593
+ **memory actions:**
12594
+ - Event CRUD: \`create_event\`, \`get_event\`, \`update_event\`, \`delete_event\`, \`list_events\`, \`distill_event\`
12595
+ - Node CRUD: \`create_node\`, \`get_node\`, \`update_node\`, \`delete_node\`, \`list_nodes\`, \`supersede_node\`
12596
+ - Query: \`search\`, \`decisions\`, \`timeline\`, \`summary\`
12597
+
12598
+ **graph actions:**
12599
+ - Analysis: \`dependencies\`, \`impact\`, \`call_path\`, \`related\`, \`path\`
12600
+ - Quality: \`circular_dependencies\`, \`unused_code\`, \`contradictions\`
12601
+ - Management: \`ingest\`, \`decisions\`
11443
12602
 
11444
- // src/hooks-config.ts
11445
- import * as fs4 from "node:fs/promises";
11446
- import * as path5 from "node:path";
11447
- import { homedir as homedir2 } from "node:os";
11448
- function getClaudeSettingsPath(scope, projectPath) {
11449
- if (scope === "user") {
11450
- return path5.join(homedir2(), ".claude", "settings.json");
11451
- }
11452
- if (!projectPath) {
11453
- throw new Error("projectPath required for project scope");
11454
- }
11455
- return path5.join(projectPath, ".claude", "settings.json");
11456
- }
11457
- function getHooksDir() {
11458
- return path5.join(homedir2(), ".claude", "hooks");
11459
- }
11460
- function buildHooksConfig(options) {
11461
- const userPromptHooks = [
11462
- {
11463
- matcher: "*",
11464
- hooks: [
11465
- {
11466
- type: "command",
11467
- command: "npx @contextstream/mcp-server hook user-prompt-submit",
11468
- timeout: 5
11469
- }
11470
- ]
11471
- }
11472
- ];
11473
- if (options?.includeMediaAware !== false) {
11474
- userPromptHooks.push({
11475
- matcher: "*",
11476
- hooks: [
11477
- {
11478
- type: "command",
11479
- command: "npx @contextstream/mcp-server hook media-aware",
11480
- timeout: 5
11481
- }
11482
- ]
11483
- });
11484
- }
11485
- const config = {
11486
- PreToolUse: [
11487
- {
11488
- matcher: "Glob|Grep|Search|Task|EnterPlanMode",
11489
- hooks: [
11490
- {
11491
- type: "command",
11492
- command: "npx @contextstream/mcp-server hook pre-tool-use",
11493
- timeout: 5
11494
- }
11495
- ]
11496
- }
11497
- ],
11498
- UserPromptSubmit: userPromptHooks
11499
- };
11500
- if (options?.includePreCompact) {
11501
- config.PreCompact = [
11502
- {
11503
- // Match both manual (/compact) and automatic compaction
11504
- matcher: "*",
11505
- hooks: [
11506
- {
11507
- type: "command",
11508
- command: "npx @contextstream/mcp-server hook pre-compact",
11509
- timeout: 10
11510
- }
11511
- ]
11512
- }
11513
- ];
11514
- }
11515
- const postToolUseHooks = [];
11516
- if (options?.includePostWrite !== false) {
11517
- postToolUseHooks.push({
11518
- matcher: "Edit|Write|NotebookEdit",
11519
- hooks: [
11520
- {
11521
- type: "command",
11522
- command: "npx @contextstream/mcp-server hook post-write",
11523
- timeout: 10
11524
- }
11525
- ]
11526
- });
11527
- }
11528
- if (options?.includeAutoRules !== false) {
11529
- postToolUseHooks.push({
11530
- matcher: "mcp__contextstream__init|mcp__contextstream__context",
11531
- hooks: [
11532
- {
11533
- type: "command",
11534
- command: "npx @contextstream/mcp-server hook auto-rules",
11535
- timeout: 15
11536
- }
11537
- ]
11538
- });
11539
- }
11540
- if (postToolUseHooks.length > 0) {
11541
- config.PostToolUse = postToolUseHooks;
11542
- }
11543
- return config;
11544
- }
11545
- async function installHookScripts(options) {
11546
- const hooksDir = getHooksDir();
11547
- await fs4.mkdir(hooksDir, { recursive: true });
11548
- const result = {
11549
- preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
11550
- userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
11551
- };
11552
- if (options?.includePreCompact) {
11553
- result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
11554
- }
11555
- if (options?.includeMediaAware !== false) {
11556
- result.mediaAware = "npx @contextstream/mcp-server hook media-aware";
11557
- }
11558
- if (options?.includeAutoRules !== false) {
11559
- result.autoRules = "npx @contextstream/mcp-server hook auto-rules";
11560
- }
11561
- return result;
11562
- }
11563
- async function readClaudeSettings(scope, projectPath) {
11564
- const settingsPath = getClaudeSettingsPath(scope, projectPath);
11565
- try {
11566
- const content = await fs4.readFile(settingsPath, "utf-8");
11567
- return JSON.parse(content);
11568
- } catch {
11569
- return {};
11570
- }
11571
- }
11572
- async function writeClaudeSettings(settings, scope, projectPath) {
11573
- const settingsPath = getClaudeSettingsPath(scope, projectPath);
11574
- const dir = path5.dirname(settingsPath);
11575
- await fs4.mkdir(dir, { recursive: true });
11576
- await fs4.writeFile(settingsPath, JSON.stringify(settings, null, 2));
11577
- }
11578
- function mergeHooksIntoSettings(existingSettings, newHooks) {
11579
- const settings = { ...existingSettings };
11580
- const existingHooks = settings.hooks || {};
11581
- for (const [hookType, matchers] of Object.entries(newHooks || {})) {
11582
- if (!matchers) continue;
11583
- const existing = existingHooks?.[hookType] || [];
11584
- const filtered = existing.filter((m) => {
11585
- return !m.hooks?.some((h) => h.command?.includes("contextstream"));
11586
- });
11587
- existingHooks[hookType] = [...filtered, ...matchers];
11588
- }
11589
- settings.hooks = existingHooks;
11590
- return settings;
11591
- }
11592
- async function installClaudeCodeHooks(options) {
11593
- const result = { scripts: [], settings: [] };
11594
- result.scripts.push(
11595
- "npx @contextstream/mcp-server hook pre-tool-use",
11596
- "npx @contextstream/mcp-server hook user-prompt-submit"
11597
- );
11598
- if (options.includePreCompact) {
11599
- result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
11600
- }
11601
- if (options.includeMediaAware !== false) {
11602
- result.scripts.push("npx @contextstream/mcp-server hook media-aware");
11603
- }
11604
- if (options.includePostWrite !== false) {
11605
- result.scripts.push("npx @contextstream/mcp-server hook post-write");
11606
- }
11607
- if (options.includeAutoRules !== false) {
11608
- result.scripts.push("npx @contextstream/mcp-server hook auto-rules");
11609
- }
11610
- const hooksConfig = buildHooksConfig({
11611
- includePreCompact: options.includePreCompact,
11612
- includeMediaAware: options.includeMediaAware,
11613
- includePostWrite: options.includePostWrite,
11614
- includeAutoRules: options.includeAutoRules
11615
- });
11616
- if (options.scope === "user" || options.scope === "both") {
11617
- const settingsPath = getClaudeSettingsPath("user");
11618
- if (!options.dryRun) {
11619
- const existing = await readClaudeSettings("user");
11620
- const merged = mergeHooksIntoSettings(existing, hooksConfig);
11621
- await writeClaudeSettings(merged, "user");
11622
- }
11623
- result.settings.push(settingsPath);
11624
- }
11625
- if ((options.scope === "project" || options.scope === "both") && options.projectPath) {
11626
- const settingsPath = getClaudeSettingsPath("project", options.projectPath);
11627
- if (!options.dryRun) {
11628
- const existing = await readClaudeSettings("project", options.projectPath);
11629
- const merged = mergeHooksIntoSettings(existing, hooksConfig);
11630
- await writeClaudeSettings(merged, "project", options.projectPath);
11631
- }
11632
- result.settings.push(settingsPath);
11633
- }
11634
- return result;
11635
- }
11636
- function getIndexStatusPath() {
11637
- return path5.join(homedir2(), ".contextstream", "indexed-projects.json");
11638
- }
11639
- async function readIndexStatus() {
11640
- const statusPath = getIndexStatusPath();
11641
- try {
11642
- const content = await fs4.readFile(statusPath, "utf-8");
11643
- return JSON.parse(content);
11644
- } catch {
11645
- return { version: 1, projects: {} };
12603
+ See full documentation: https://contextstream.io/docs/mcp/tools
12604
+ `.trim();
12605
+ var CONTEXTSTREAM_RULES_MINIMAL = `
12606
+ # ContextStream Rules (MANDATORY)
12607
+
12608
+ ## \u{1F6A8} CRITICAL: These Rules Are Non-Negotiable \u{1F6A8}
12609
+
12610
+ <contextstream_protocol>
12611
+ **EVERY coding response MUST start with:**
12612
+ 1. \`init()\` (1st message only) \u2192 then \`context(user_message="<msg>")\`
12613
+ 2. \`context(user_message="<msg>")\` (EVERY subsequent message)
12614
+
12615
+ **BEFORE Glob/Grep/Read/Search/Explore:**
12616
+ \u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
12617
+
12618
+ **HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
12619
+ </contextstream_protocol>
12620
+
12621
+ Rules Version: ${RULES_VERSION}
12622
+
12623
+ ---
12624
+
12625
+ ## Why ContextStream First?
12626
+
12627
+ | \u274C WRONG | \u2705 CORRECT |
12628
+ |----------|-----------|
12629
+ | \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="hybrid")\` (1 call) |
12630
+ | Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
12631
+ | Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
12632
+
12633
+ **\`context()\` delivers: rules, lessons from past mistakes, relevant decisions, semantic matches**
12634
+ **\`search()\` is indexed: faster, returns context, one call vs many**
12635
+ **Hooks inject \`<system-reminder>\` tags: these ARE the rules, follow them**
12636
+
12637
+ ---
12638
+
12639
+ ## Protocol
12640
+
12641
+ | When | Call |
12642
+ |------|------|
12643
+ | 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
12644
+ | Every message after | \`context(user_message="...")\` |
12645
+ | Before ANY file discovery | \`search(mode="hybrid", query="...")\` |
12646
+ | On \`<system-reminder>\` | **Follow instructions inside** |
12647
+ | Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
12648
+ | Check past mistakes | \`session(action="get_lessons", query="...")\` |
12649
+
12650
+ ## Search Modes
12651
+
12652
+ | Mode | When |
12653
+ |------|------|
12654
+ | \`hybrid\` | Default \u2014 semantic + keyword |
12655
+ | \`keyword\` | Exact symbol match |
12656
+ | \`exhaustive\` | Find ALL occurrences |
12657
+ | \`semantic\` | Conceptual questions |
12658
+
12659
+ ## Handle Notices from context()
12660
+
12661
+ - **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
12662
+ - **[RULES_NOTICE]** \u2192 Run \`generate_rules()\`
12663
+ - **[VERSION_NOTICE]** \u2192 Tell user to update MCP
12664
+
12665
+ ## Fast Path (Simple Utilities Only)
12666
+
12667
+ Skip init/context ONLY for: "list workspaces", "show version", "list reminders"
12668
+ \u2192 Just call: \`workspace(action="list")\`, \`help(action="version")\`, etc.
12669
+
12670
+ Everything else = full protocol (init \u2192 context \u2192 search \u2192 work)
12671
+
12672
+ ### Lessons (Past Mistakes)
12673
+
12674
+ - After \`init\`: Check for \`lessons\` field and apply before work
12675
+ - Before risky work: \`session(action="get_lessons", query="<topic>")\`
12676
+ - On mistakes: \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\`
12677
+
12678
+ ### Context Pressure & Compaction
12679
+
12680
+ - If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
12681
+ - PreCompact hooks automatically save snapshots before compaction (if installed)
12682
+
12683
+ ### Enhanced Context (Warnings)
12684
+
12685
+ \`context\` returns server-side \`warnings\` for lessons, risky actions, and breaking changes.
12686
+ When warnings are present: **STOP**, acknowledge them, explain mitigation, then proceed.
12687
+
12688
+ ### Automatic Context Restoration
12689
+
12690
+ **Context restoration is now enabled by default.** Every \`init\` call automatically:
12691
+ - Restores context from recent snapshots (if available)
12692
+ - Returns \`restored_context\` field with snapshot data
12693
+ - Sets \`is_post_compact=true\` in response when restoration occurs
12694
+
12695
+ **No special handling needed after compaction** - just call \`init\` normally.
12696
+
12697
+ To disable automatic restoration:
12698
+ - Pass \`is_post_compact=false\` in the API call
12699
+ - Or set \`CONTEXTSTREAM_RESTORE_CONTEXT=false\` environment variable
12700
+
12701
+ ### Notices - MUST HANDLE IMMEDIATELY
12702
+
12703
+ - **[VERSION_NOTICE]**: Tell the user about the update and command to run
12704
+ - **[RULES_NOTICE]**: Run \`generate_rules(overwrite_existing=true)\` to update
12705
+ - **[LESSONS_WARNING]**: Read lessons, tell user about them, explain how you'll avoid past mistakes
12706
+
12707
+ ### Plans & Tasks
12708
+
12709
+ When user asks for a plan, use ContextStream (not EnterPlanMode):
12710
+ 1. \`session(action="capture_plan", title="...", steps=[...])\`
12711
+ 2. \`memory(action="create_task", title="...", plan_id="<id>")\`
12712
+
12713
+ ### Workspace-Only Mode (Multi-Project Folders)
12714
+
12715
+ If working in a parent folder containing multiple projects:
12716
+ \`\`\`
12717
+ init(folder_path="...", skip_project_creation=true)
12718
+ \`\`\`
12719
+
12720
+ This enables workspace-level memory and context without project-specific indexing.
12721
+ Use for monorepos or folders with multiple independent projects.
12722
+
12723
+ Full docs: https://contextstream.io/docs/mcp/tools
12724
+ `.trim();
12725
+ var TEMPLATES = {
12726
+ codex: {
12727
+ filename: "AGENTS.md",
12728
+ description: "Codex CLI agent instructions",
12729
+ build: (rules) => `# Codex CLI Instructions
12730
+ ${rules}
12731
+ `
12732
+ },
12733
+ cursor: {
12734
+ filename: ".cursorrules",
12735
+ description: "Cursor AI rules",
12736
+ build: (rules) => `# Cursor Rules
12737
+ ${rules}
12738
+ `
12739
+ },
12740
+ cline: {
12741
+ filename: ".clinerules",
12742
+ description: "Cline AI rules",
12743
+ build: (rules) => `# Cline Rules
12744
+ ${rules}
12745
+ `
12746
+ },
12747
+ kilo: {
12748
+ filename: ".kilocode/rules/contextstream.md",
12749
+ description: "Kilo Code AI rules",
12750
+ build: (rules) => `# Kilo Code Rules
12751
+ ${rules}
12752
+ `
12753
+ },
12754
+ roo: {
12755
+ filename: ".roo/rules/contextstream.md",
12756
+ description: "Roo Code AI rules",
12757
+ build: (rules) => `# Roo Code Rules
12758
+ ${rules}
12759
+ `
12760
+ },
12761
+ claude: {
12762
+ filename: "CLAUDE.md",
12763
+ description: "Claude Code instructions",
12764
+ build: (rules) => `# Claude Code Instructions
12765
+ ${rules}
12766
+ `
12767
+ },
12768
+ aider: {
12769
+ filename: ".aider.conf.yml",
12770
+ description: "Aider configuration with system prompt",
12771
+ build: (rules) => `# Aider Configuration
12772
+ # Note: Aider uses different config format - this adds to the system prompt
12773
+
12774
+ # Add ContextStream guidance to conventions
12775
+ conventions: |
12776
+ ${rules.split("\n").map((line) => " " + line).join("\n")}
12777
+ `
12778
+ },
12779
+ antigravity: {
12780
+ filename: "GEMINI.md",
12781
+ description: "Google Antigravity AI rules",
12782
+ build: (rules) => `# Antigravity Agent Rules
12783
+ ${rules}
12784
+ `
11646
12785
  }
12786
+ };
12787
+ function getAvailableEditors() {
12788
+ return Object.keys(TEMPLATES);
11647
12789
  }
11648
- async function writeIndexStatus(status) {
11649
- const statusPath = getIndexStatusPath();
11650
- const dir = path5.dirname(statusPath);
11651
- await fs4.mkdir(dir, { recursive: true });
11652
- await fs4.writeFile(statusPath, JSON.stringify(status, null, 2));
11653
- }
11654
- async function markProjectIndexed(projectPath, options) {
11655
- const status = await readIndexStatus();
11656
- const resolvedPath = path5.resolve(projectPath);
11657
- status.projects[resolvedPath] = {
11658
- indexed_at: (/* @__PURE__ */ new Date()).toISOString(),
11659
- project_id: options?.project_id,
11660
- project_name: options?.project_name
11661
- };
11662
- await writeIndexStatus(status);
11663
- }
11664
- function getClineHooksDir(scope, projectPath) {
11665
- if (scope === "global") {
11666
- return path5.join(homedir2(), "Documents", "Cline", "Rules", "Hooks");
11667
- }
11668
- if (!projectPath) {
11669
- throw new Error("projectPath required for project scope");
11670
- }
11671
- return path5.join(projectPath, ".clinerules", "hooks");
12790
+ function getTemplate(editor) {
12791
+ return TEMPLATES[editor.toLowerCase()] || null;
11672
12792
  }
11673
- var CLINE_HOOK_WRAPPER = (hookName) => `#!/bin/bash
11674
- # ContextStream ${hookName} Hook Wrapper for Cline/Roo/Kilo Code
11675
- # Calls the Node.js hook via npx
11676
- exec npx @contextstream/mcp-server hook ${hookName}
12793
+ function generateRuleContent(editor, options) {
12794
+ const template = getTemplate(editor);
12795
+ 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;
12798
+ let content = template.build(rules);
12799
+ if (options?.workspaceName || options?.projectName) {
12800
+ const header = `
12801
+ # Workspace: ${options.workspaceName || "Unknown"}
12802
+ ${options.projectName ? `# Project: ${options.projectName}` : ""}
12803
+ ${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
12804
+
11677
12805
  `;
11678
- async function installClineHookScripts(options) {
11679
- const hooksDir = getClineHooksDir(options.scope, options.projectPath);
11680
- await fs4.mkdir(hooksDir, { recursive: true });
11681
- const preToolUsePath = path5.join(hooksDir, "PreToolUse");
11682
- const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
11683
- const postToolUsePath = path5.join(hooksDir, "PostToolUse");
11684
- await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
11685
- await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
11686
- const result = {
11687
- preToolUse: preToolUsePath,
11688
- userPromptSubmit: userPromptPath
11689
- };
11690
- if (options.includePostWrite !== false) {
11691
- await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
11692
- result.postToolUse = postToolUsePath;
11693
- }
11694
- return result;
11695
- }
11696
- function getRooCodeHooksDir(scope, projectPath) {
11697
- if (scope === "global") {
11698
- return path5.join(homedir2(), ".roo", "hooks");
11699
- }
11700
- if (!projectPath) {
11701
- throw new Error("projectPath required for project scope");
11702
- }
11703
- return path5.join(projectPath, ".roo", "hooks");
11704
- }
11705
- async function installRooCodeHookScripts(options) {
11706
- const hooksDir = getRooCodeHooksDir(options.scope, options.projectPath);
11707
- await fs4.mkdir(hooksDir, { recursive: true });
11708
- const preToolUsePath = path5.join(hooksDir, "PreToolUse");
11709
- const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
11710
- const postToolUsePath = path5.join(hooksDir, "PostToolUse");
11711
- await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
11712
- await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
11713
- const result = {
11714
- preToolUse: preToolUsePath,
11715
- userPromptSubmit: userPromptPath
11716
- };
11717
- if (options.includePostWrite !== false) {
11718
- await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
11719
- result.postToolUse = postToolUsePath;
12806
+ content = header + content;
11720
12807
  }
11721
- return result;
11722
- }
11723
- function getKiloCodeHooksDir(scope, projectPath) {
11724
- if (scope === "global") {
11725
- return path5.join(homedir2(), ".kilocode", "hooks");
12808
+ if (options?.additionalRules) {
12809
+ content += "\n\n## Project-Specific Rules\n\n" + options.additionalRules;
11726
12810
  }
11727
- if (!projectPath) {
11728
- throw new Error("projectPath required for project scope");
12811
+ if (editor.toLowerCase() === "claude") {
12812
+ content = applyMcpToolPrefix(content, `mcp__${DEFAULT_CLAUDE_MCP_SERVER_NAME}__`);
11729
12813
  }
11730
- return path5.join(projectPath, ".kilocode", "hooks");
11731
- }
11732
- async function installKiloCodeHookScripts(options) {
11733
- const hooksDir = getKiloCodeHooksDir(options.scope, options.projectPath);
11734
- await fs4.mkdir(hooksDir, { recursive: true });
11735
- const preToolUsePath = path5.join(hooksDir, "PreToolUse");
11736
- const userPromptPath = path5.join(hooksDir, "UserPromptSubmit");
11737
- const postToolUsePath = path5.join(hooksDir, "PostToolUse");
11738
- await fs4.writeFile(preToolUsePath, CLINE_HOOK_WRAPPER("pre-tool-use"), { mode: 493 });
11739
- await fs4.writeFile(userPromptPath, CLINE_HOOK_WRAPPER("user-prompt-submit"), { mode: 493 });
11740
- const result = {
11741
- preToolUse: preToolUsePath,
11742
- userPromptSubmit: userPromptPath
12814
+ return {
12815
+ filename: template.filename,
12816
+ content: content.trim() + "\n"
11743
12817
  };
11744
- if (options.includePostWrite !== false) {
11745
- await fs4.writeFile(postToolUsePath, CLINE_HOOK_WRAPPER("post-write"), { mode: 493 });
11746
- result.postToolUse = postToolUsePath;
11747
- }
11748
- return result;
11749
12818
  }
11750
- function getCursorHooksConfigPath(scope, projectPath) {
11751
- if (scope === "global") {
11752
- return path5.join(homedir2(), ".cursor", "hooks.json");
11753
- }
11754
- if (!projectPath) {
11755
- throw new Error("projectPath required for project scope");
11756
- }
11757
- return path5.join(projectPath, ".cursor", "hooks.json");
12819
+ function generateAllRuleFiles(options) {
12820
+ return getAvailableEditors().map((editor) => {
12821
+ const result = generateRuleContent(editor, options);
12822
+ if (!result) return null;
12823
+ return { editor, ...result };
12824
+ }).filter((r) => r !== null);
11758
12825
  }
11759
- function getCursorHooksDir(scope, projectPath) {
11760
- if (scope === "global") {
11761
- return path5.join(homedir2(), ".cursor", "hooks");
12826
+
12827
+ // src/tool-catalog.ts
12828
+ var TOOL_CATALOG = [
12829
+ {
12830
+ name: "Session",
12831
+ tools: [
12832
+ { name: "init", hint: "start-conv" },
12833
+ { name: "smart", hint: "each-msg" },
12834
+ { name: "capture", hint: "save" },
12835
+ { name: "recall", hint: "find" },
12836
+ { name: "remember", hint: "quick" },
12837
+ { name: "compress", hint: "end" },
12838
+ { name: "summary", hint: "brief" },
12839
+ { name: "delta", hint: "changes" },
12840
+ { name: "get_lessons", hint: "learn" },
12841
+ { name: "capture_lesson", hint: "mistake" },
12842
+ { name: "get_user_context", hint: "prefs" },
12843
+ { name: "smart_search", hint: "deep-find" },
12844
+ // Plan actions
12845
+ { name: "capture_plan", hint: "save-plan" },
12846
+ { name: "get_plan", hint: "get-plan" },
12847
+ { name: "update_plan", hint: "edit-plan" },
12848
+ { name: "list_plans", hint: "list-plans" }
12849
+ ]
12850
+ },
12851
+ {
12852
+ name: "Search",
12853
+ tools: [
12854
+ { name: "semantic", hint: "meaning" },
12855
+ { name: "hybrid", hint: "combo" },
12856
+ { name: "keyword", hint: "exact" },
12857
+ { name: "pattern", hint: "code" }
12858
+ ]
12859
+ },
12860
+ {
12861
+ name: "Memory",
12862
+ tools: [
12863
+ { name: "create_event", hint: "new" },
12864
+ { name: "list_events", hint: "list" },
12865
+ { name: "get_event", hint: "get" },
12866
+ { name: "update_event", hint: "edit" },
12867
+ { name: "delete_event", hint: "rm" },
12868
+ { name: "search", hint: "find" },
12869
+ { name: "decisions", hint: "choices" },
12870
+ { name: "timeline", hint: "history" },
12871
+ { name: "distill_event", hint: "extract" },
12872
+ // Task actions
12873
+ { name: "create_task", hint: "new-task" },
12874
+ { name: "get_task", hint: "get-task" },
12875
+ { name: "update_task", hint: "edit-task" },
12876
+ { name: "delete_task", hint: "rm-task" },
12877
+ { name: "list_tasks", hint: "list-tasks" },
12878
+ { name: "reorder_tasks", hint: "sort-tasks" }
12879
+ ]
12880
+ },
12881
+ {
12882
+ name: "Knowledge",
12883
+ tools: [
12884
+ { name: "create_node", hint: "new" },
12885
+ { name: "list_nodes", hint: "list" },
12886
+ { name: "get_node", hint: "get" },
12887
+ { name: "update_node", hint: "edit" },
12888
+ { name: "delete_node", hint: "rm" },
12889
+ { name: "supersede_node", hint: "replace" }
12890
+ ]
12891
+ },
12892
+ {
12893
+ name: "Graph",
12894
+ tools: [
12895
+ { name: "related", hint: "links" },
12896
+ { name: "path", hint: "trace" },
12897
+ { name: "decisions", hint: "choices" },
12898
+ { name: "dependencies", hint: "deps" },
12899
+ { name: "ingest", hint: "build" },
12900
+ { name: "impact", hint: "changes" },
12901
+ { name: "contradictions", hint: "conflicts" }
12902
+ ]
12903
+ },
12904
+ {
12905
+ name: "Media",
12906
+ tools: [
12907
+ { name: "index", hint: "add-media" },
12908
+ { name: "status", hint: "progress" },
12909
+ { name: "search", hint: "find-clip" },
12910
+ { name: "get_clip", hint: "get-segment" },
12911
+ { name: "list", hint: "browse" },
12912
+ { name: "delete", hint: "remove" }
12913
+ ]
12914
+ },
12915
+ {
12916
+ name: "Workspace",
12917
+ tools: [
12918
+ { name: "list", hint: "" },
12919
+ { name: "get", hint: "" },
12920
+ { name: "create", hint: "" },
12921
+ { name: "associate", hint: "link-folder" },
12922
+ { name: "bootstrap", hint: "new-ws" }
12923
+ ]
12924
+ },
12925
+ {
12926
+ name: "Project",
12927
+ tools: [
12928
+ { name: "list", hint: "" },
12929
+ { name: "get", hint: "" },
12930
+ { name: "create", hint: "" },
12931
+ { name: "index", hint: "scan-code" },
12932
+ { name: "files", hint: "list-files" },
12933
+ { name: "overview", hint: "summary" }
12934
+ ]
12935
+ },
12936
+ {
12937
+ name: "AI",
12938
+ tools: [
12939
+ { name: "context", hint: "smart-ctx" },
12940
+ { name: "plan", hint: "generate" },
12941
+ { name: "tasks", hint: "breakdown" },
12942
+ { name: "embeddings", hint: "vectors" }
12943
+ ]
12944
+ },
12945
+ {
12946
+ name: "Notion",
12947
+ tools: [
12948
+ { name: "create_page", hint: "new-page" },
12949
+ { name: "search_pages", hint: "find" },
12950
+ { name: "list_databases", hint: "list-dbs" },
12951
+ { name: "get_page", hint: "get" },
12952
+ { name: "query_database", hint: "query-db" },
12953
+ { name: "update_page", hint: "edit" },
12954
+ { name: "stats", hint: "overview" },
12955
+ { name: "activity", hint: "recent" },
12956
+ { name: "knowledge", hint: "insights" },
12957
+ { name: "summary", hint: "brief" }
12958
+ ]
11762
12959
  }
11763
- if (!projectPath) {
11764
- throw new Error("projectPath required for project scope");
12960
+ ];
12961
+ function generateToolCatalog(format = "grouped", category) {
12962
+ let categories = TOOL_CATALOG;
12963
+ if (category) {
12964
+ const filtered = TOOL_CATALOG.filter((c) => c.name.toLowerCase() === category.toLowerCase());
12965
+ if (filtered.length > 0) {
12966
+ categories = filtered;
12967
+ }
11765
12968
  }
11766
- return path5.join(projectPath, ".cursor", "hooks");
11767
- }
11768
- async function readCursorHooksConfig(scope, projectPath) {
11769
- const configPath = getCursorHooksConfigPath(scope, projectPath);
11770
- try {
11771
- const content = await fs4.readFile(configPath, "utf-8");
11772
- return JSON.parse(content);
11773
- } catch {
11774
- return { version: 1, hooks: {} };
12969
+ switch (format) {
12970
+ case "minimal":
12971
+ return generateMinimal(categories);
12972
+ case "full":
12973
+ return generateFull(categories);
12974
+ case "grouped":
12975
+ default:
12976
+ return generateGrouped(categories);
11775
12977
  }
11776
12978
  }
11777
- async function writeCursorHooksConfig(config, scope, projectPath) {
11778
- const configPath = getCursorHooksConfigPath(scope, projectPath);
11779
- const dir = path5.dirname(configPath);
11780
- await fs4.mkdir(dir, { recursive: true });
11781
- await fs4.writeFile(configPath, JSON.stringify(config, null, 2));
12979
+ function generateGrouped(categories) {
12980
+ return categories.map((cat) => {
12981
+ const tools = cat.tools.map((t) => t.hint ? `${t.name}(${t.hint})` : t.name).join(" ");
12982
+ return `${cat.name}: ${tools}`;
12983
+ }).join("\n");
11782
12984
  }
11783
- async function installCursorHookScripts(options) {
11784
- const hooksDir = getCursorHooksDir(options.scope, options.projectPath);
11785
- await fs4.mkdir(hooksDir, { recursive: true });
11786
- const existingConfig = await readCursorHooksConfig(options.scope, options.projectPath);
11787
- const filterContextStreamHooks = (hooks) => {
11788
- if (!hooks) return [];
11789
- return hooks.filter((h) => {
11790
- const hook = h;
11791
- return !hook.command?.includes("contextstream");
11792
- });
11793
- };
11794
- const filteredPreToolUse = filterContextStreamHooks(existingConfig.hooks.preToolUse);
11795
- const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
11796
- const config = {
11797
- version: 1,
11798
- hooks: {
11799
- ...existingConfig.hooks,
11800
- preToolUse: [
11801
- ...filteredPreToolUse,
11802
- {
11803
- command: "npx @contextstream/mcp-server hook pre-tool-use",
11804
- type: "command",
11805
- timeout: 5,
11806
- matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
11807
- }
11808
- ],
11809
- beforeSubmitPrompt: [
11810
- ...filteredBeforeSubmit,
11811
- {
11812
- command: "npx @contextstream/mcp-server hook user-prompt-submit",
11813
- type: "command",
11814
- timeout: 5
11815
- }
11816
- ]
11817
- }
11818
- };
11819
- await writeCursorHooksConfig(config, options.scope, options.projectPath);
11820
- const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
11821
- return {
11822
- preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
11823
- beforeSubmitPrompt: "npx @contextstream/mcp-server hook user-prompt-submit",
11824
- config: configPath
11825
- };
12985
+ function generateMinimal(categories) {
12986
+ return categories.map((cat) => {
12987
+ const tools = cat.tools.map((t) => t.name).join("|");
12988
+ return `${cat.name}:${tools}`;
12989
+ }).join("\n");
11826
12990
  }
11827
- async function installEditorHooks(options) {
11828
- const { editor, scope, projectPath, includePreCompact, includePostWrite } = options;
11829
- switch (editor) {
11830
- case "claude": {
11831
- if (scope === "project" && !projectPath) {
11832
- throw new Error("projectPath required for project scope");
11833
- }
11834
- const scripts = await installHookScripts({ includePreCompact });
11835
- const hooksConfig = buildHooksConfig({ includePreCompact, includePostWrite });
11836
- const settingsScope = scope === "global" ? "user" : "project";
11837
- const existing = await readClaudeSettings(settingsScope, projectPath);
11838
- const merged = mergeHooksIntoSettings(existing, hooksConfig);
11839
- await writeClaudeSettings(merged, settingsScope, projectPath);
11840
- const installed = [scripts.preToolUse, scripts.userPrompt];
11841
- if (scripts.preCompact) installed.push(scripts.preCompact);
11842
- return {
11843
- editor: "claude",
11844
- installed,
11845
- hooksDir: getHooksDir()
11846
- };
11847
- }
11848
- case "cline": {
11849
- const scripts = await installClineHookScripts({ scope, projectPath, includePostWrite });
11850
- const installed = [scripts.preToolUse, scripts.userPromptSubmit];
11851
- if (scripts.postToolUse) installed.push(scripts.postToolUse);
11852
- return {
11853
- editor: "cline",
11854
- installed,
11855
- hooksDir: getClineHooksDir(scope, projectPath)
11856
- };
11857
- }
11858
- case "roo": {
11859
- const scripts = await installRooCodeHookScripts({ scope, projectPath, includePostWrite });
11860
- const installed = [scripts.preToolUse, scripts.userPromptSubmit];
11861
- if (scripts.postToolUse) installed.push(scripts.postToolUse);
11862
- return {
11863
- editor: "roo",
11864
- installed,
11865
- hooksDir: getRooCodeHooksDir(scope, projectPath)
11866
- };
11867
- }
11868
- case "kilo": {
11869
- const scripts = await installKiloCodeHookScripts({ scope, projectPath, includePostWrite });
11870
- const installed = [scripts.preToolUse, scripts.userPromptSubmit];
11871
- if (scripts.postToolUse) installed.push(scripts.postToolUse);
11872
- return {
11873
- editor: "kilo",
11874
- installed,
11875
- hooksDir: getKiloCodeHooksDir(scope, projectPath)
11876
- };
11877
- }
11878
- case "cursor": {
11879
- const scripts = await installCursorHookScripts({ scope, projectPath });
11880
- return {
11881
- editor: "cursor",
11882
- installed: [scripts.preToolUse, scripts.beforeSubmitPrompt],
11883
- hooksDir: getCursorHooksDir(scope, projectPath)
11884
- };
12991
+ function generateFull(categories) {
12992
+ const lines = [];
12993
+ for (const cat of categories) {
12994
+ lines.push(`## ${cat.name}`);
12995
+ for (const tool of cat.tools) {
12996
+ const prefix = cat.name.toLowerCase().replace(/\s+/g, "_");
12997
+ const fullName = `${prefix}_${tool.name}`;
12998
+ lines.push(`- ${fullName}: ${tool.hint || "standard CRUD"}`);
11885
12999
  }
11886
- default:
11887
- throw new Error(`Unsupported editor: ${editor}`);
11888
13000
  }
13001
+ return lines.join("\n");
11889
13002
  }
11890
- async function installAllEditorHooks(options) {
11891
- const editors = options.editors || ["claude", "cline", "roo", "kilo", "cursor"];
11892
- const results = [];
11893
- for (const editor of editors) {
11894
- try {
11895
- const result = await installEditorHooks({
11896
- editor,
11897
- scope: options.scope,
11898
- projectPath: options.projectPath,
11899
- includePreCompact: options.includePreCompact,
11900
- includePostWrite: options.includePostWrite
11901
- });
11902
- results.push(result);
11903
- } catch (error) {
11904
- console.error(`Failed to install hooks for ${editor}:`, error);
11905
- }
11906
- }
11907
- return results;
13003
+ function getCoreToolsHint() {
13004
+ return `Session: init(start) smart(each-msg) capture(save) recall(find) remember(quick)`;
11908
13005
  }
11909
13006
 
13007
+ // src/tools.ts
13008
+ init_hooks_config();
13009
+
11910
13010
  // src/token-savings.ts
11911
13011
  var TOKEN_SAVINGS_FORMULA_VERSION = 1;
11912
13012
  var MAX_CHARS_PER_EVENT = 2e7;
@@ -12122,6 +13222,28 @@ ${LESSONS_REMINDER_PREFIX}
12122
13222
  ${lessonLines.join("\n")}
12123
13223
  \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`;
12124
13224
  }
13225
+ var REMEMBER_REMINDER_PREFIX = `
13226
+ \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
13227
+ \u{1F4CC} USER PREFERENCES - MUST FOLLOW
13228
+ These are user-specified preferences that MUST be checked and followed.
13229
+ \u26A0\uFE0F IMPORTANT: Always verify your actions align with these preferences.
13230
+ `;
13231
+ function generateRememberReminder(result) {
13232
+ const rememberItems = result.remember_items;
13233
+ if (!rememberItems || rememberItems.length === 0) {
13234
+ return "";
13235
+ }
13236
+ const itemLines = rememberItems.slice(0, 5).map((item, i) => {
13237
+ const importance = item.importance === "critical" ? "\u{1F6A8}" : "\u{1F4CC}";
13238
+ const content = item.content || "";
13239
+ return `${i + 1}. ${importance} ${content.slice(0, 150)}`;
13240
+ });
13241
+ return `
13242
+
13243
+ ${REMEMBER_REMINDER_PREFIX}
13244
+ ${itemLines.join("\n")}
13245
+ \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`;
13246
+ }
12125
13247
  function generateRulesUpdateWarning(rulesNotice) {
12126
13248
  if (!rulesNotice || rulesNotice.status !== "behind" && rulesNotice.status !== "missing") {
12127
13249
  return "";
@@ -15925,6 +17047,10 @@ ${noticeLines.filter(Boolean).join("\n")}`;
15925
17047
  if (lessonsReminder) {
15926
17048
  text = `${text}${lessonsReminder}`;
15927
17049
  }
17050
+ const rememberReminder = generateRememberReminder(result);
17051
+ if (rememberReminder) {
17052
+ text = `${text}${rememberReminder}`;
17053
+ }
15928
17054
  if (SEARCH_RULES_REMINDER_ENABLED) {
15929
17055
  text = `${text}
15930
17056
 
@@ -19994,13 +21120,19 @@ ${formatContent(result)}`
19994
21120
  if (!workspaceId) {
19995
21121
  return errorResult("create_doc requires workspace_id. Call session_init first.");
19996
21122
  }
21123
+ const detectedEditor = getDetectedClientName();
21124
+ const aiMetadata = detectedEditor ? {
21125
+ created_by_ai: true,
21126
+ ai_editor: detectedEditor,
21127
+ ...input.metadata || {}
21128
+ } : input.metadata;
19997
21129
  const docResult = await client.docsCreate({
19998
21130
  workspace_id: workspaceId,
19999
21131
  project_id: projectId,
20000
21132
  title: input.title,
20001
21133
  content: input.content,
20002
21134
  doc_type: input.doc_type,
20003
- metadata: input.metadata,
21135
+ metadata: aiMetadata,
20004
21136
  is_personal: input.is_personal
20005
21137
  });
20006
21138
  return {
@@ -22081,7 +23213,7 @@ function registerLimitedTools(server) {
22081
23213
  text: `ContextStream: API key not configured.
22082
23214
 
22083
23215
  To set up (creates key + configures your editor):
22084
- npx -y @contextstream/mcp-server setup
23216
+ npx --prefer-online -y @contextstream/mcp-server@latest setup
22085
23217
 
22086
23218
  This will:
22087
23219
  - Start a 5-day Pro trial
@@ -22089,7 +23221,7 @@ This will:
22089
23221
  - Write rules files for better AI assistance
22090
23222
 
22091
23223
  Preview first:
22092
- npx -y @contextstream/mcp-server setup --dry-run
23224
+ npx --prefer-online -y @contextstream/mcp-server@latest setup --dry-run
22093
23225
 
22094
23226
  After setup, restart your editor to enable all ContextStream tools.`
22095
23227
  }
@@ -23495,7 +24627,6 @@ import { createServer } from "node:http";
23495
24627
  import { randomUUID as randomUUID2 } from "node:crypto";
23496
24628
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23497
24629
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23498
- init_version();
23499
24630
  var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
23500
24631
  var PORT = Number.parseInt(process.env.MCP_HTTP_PORT || "8787", 10);
23501
24632
  var MCP_PATH = process.env.MCP_HTTP_PATH || "/mcp";
@@ -23774,7 +24905,6 @@ async function runHttpGateway() {
23774
24905
  }
23775
24906
 
23776
24907
  // src/index.ts
23777
- init_version();
23778
24908
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
23779
24909
  import { homedir as homedir10 } from "os";
23780
24910
  import { join as join14 } from "path";
@@ -23785,8 +24915,6 @@ import * as path8 from "node:path";
23785
24915
  import { homedir as homedir5 } from "node:os";
23786
24916
  import { stdin, stdout } from "node:process";
23787
24917
  import { createInterface } from "node:readline/promises";
23788
- init_rules_templates();
23789
- init_version();
23790
24918
 
23791
24919
  // src/credentials.ts
23792
24920
  import * as fs6 from "node:fs/promises";
@@ -23851,6 +24979,7 @@ async function writeSavedCredentials(input) {
23851
24979
  }
23852
24980
 
23853
24981
  // src/setup.ts
24982
+ init_hooks_config();
23854
24983
  var EDITOR_LABELS = {
23855
24984
  codex: "Codex CLI",
23856
24985
  claude: "Claude Code",
@@ -24231,13 +25360,13 @@ function buildContextStreamMcpServer(params) {
24231
25360
  if (IS_WINDOWS) {
24232
25361
  return {
24233
25362
  command: "cmd",
24234
- args: ["/c", "npx", "-y", "@contextstream/mcp-server"],
25363
+ args: ["/c", "npx", "--prefer-online", "-y", "@contextstream/mcp-server@latest"],
24235
25364
  env
24236
25365
  };
24237
25366
  }
24238
25367
  return {
24239
25368
  command: "npx",
24240
- args: ["-y", "@contextstream/mcp-server"],
25369
+ args: ["--prefer-online", "-y", "@contextstream/mcp-server@latest"],
24241
25370
  env
24242
25371
  };
24243
25372
  }
@@ -24260,14 +25389,14 @@ function buildContextStreamVsCodeServer(params) {
24260
25389
  return {
24261
25390
  type: "stdio",
24262
25391
  command: "cmd",
24263
- args: ["/c", "npx", "-y", "@contextstream/mcp-server"],
25392
+ args: ["/c", "npx", "--prefer-online", "-y", "@contextstream/mcp-server@latest"],
24264
25393
  env
24265
25394
  };
24266
25395
  }
24267
25396
  return {
24268
25397
  type: "stdio",
24269
25398
  command: "npx",
24270
- args: ["-y", "@contextstream/mcp-server"],
25399
+ args: ["--prefer-online", "-y", "@contextstream/mcp-server@latest"],
24271
25400
  env
24272
25401
  };
24273
25402
  }
@@ -24361,9 +25490,9 @@ async function upsertCodexTomlConfig(filePath, params) {
24361
25490
  const showTimingLine = params.showTiming ? `CONTEXTSTREAM_SHOW_TIMING = "true"
24362
25491
  ` : "";
24363
25492
  const commandLine = IS_WINDOWS ? `command = "cmd"
24364
- args = ["/c", "npx", "-y", "@contextstream/mcp-server"]
25493
+ args = ["/c", "npx", "--prefer-online", "-y", "@contextstream/mcp-server@latest"]
24365
25494
  ` : `command = "npx"
24366
- args = ["-y", "@contextstream/mcp-server"]
25495
+ args = ["--prefer-online", "-y", "@contextstream/mcp-server@latest"]
24367
25496
  `;
24368
25497
  const block = `
24369
25498
 
@@ -24511,7 +25640,7 @@ async function runSetupWizard(args) {
24511
25640
  console.log(` Latest version is v${versionNotice.latest}`);
24512
25641
  console.log("");
24513
25642
  console.log(" To use the latest version, exit and run:");
24514
- console.log(" npx -y @contextstream/mcp-server@latest setup");
25643
+ console.log(" npx --prefer-online -y @contextstream/mcp-server@latest setup");
24515
25644
  console.log("\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");
24516
25645
  console.log("");
24517
25646
  const continueAnyway = normalizeInput(
@@ -24866,10 +25995,10 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
24866
25995
  const envHint = toolset === "router" ? " --env CONTEXTSTREAM_PROGRESSIVE_MODE=true" : "";
24867
25996
  const packHint = contextPackEnabled === false ? " --env CONTEXTSTREAM_CONTEXT_PACK=false" : " --env CONTEXTSTREAM_CONTEXT_PACK=true";
24868
25997
  console.log(
24869
- ` macOS/Linux: claude mcp add --transport stdio contextstream --scope user --env CONTEXTSTREAM_API_URL=... --env CONTEXTSTREAM_API_KEY=...${envHint}${packHint} -- npx -y @contextstream/mcp-server`
25998
+ ` macOS/Linux: claude mcp add --transport stdio contextstream --scope user --env CONTEXTSTREAM_API_URL=... --env CONTEXTSTREAM_API_KEY=...${envHint}${packHint} -- npx --prefer-online -y @contextstream/mcp-server@latest`
24870
25999
  );
24871
26000
  console.log(
24872
- " Windows (native): use `cmd /c npx -y @contextstream/mcp-server` after `--` if `npx` is not found."
26001
+ " Windows (native): use `cmd /c npx --prefer-online -y @contextstream/mcp-server@latest` after `--` if `npx` is not found."
24873
26002
  );
24874
26003
  continue;
24875
26004
  }
@@ -25204,7 +26333,7 @@ function printHelp() {
25204
26333
  console.log(`ContextStream MCP Server (contextstream-mcp) v${VERSION}
25205
26334
 
25206
26335
  Usage:
25207
- npx -y @contextstream/mcp-server
26336
+ npx --prefer-online -y @contextstream/mcp-server@latest
25208
26337
  contextstream-mcp
25209
26338
  contextstream-mcp setup
25210
26339
  contextstream-mcp http
@@ -25251,10 +26380,10 @@ Environment variables:
25251
26380
  Examples:
25252
26381
  CONTEXTSTREAM_API_URL="https://api.contextstream.io" \\
25253
26382
  CONTEXTSTREAM_API_KEY="your_api_key" \\
25254
- npx -y @contextstream/mcp-server
26383
+ npx --prefer-online -y @contextstream/mcp-server@latest
25255
26384
 
25256
26385
  Setup wizard:
25257
- npx -y @contextstream/mcp-server setup
26386
+ npx --prefer-online -y @contextstream/mcp-server@latest setup
25258
26387
 
25259
26388
  Notes:
25260
26389
  - When used from an MCP client (e.g. Codex, Cursor, VS Code),
@@ -25268,7 +26397,7 @@ async function runLimitedModeServer() {
25268
26397
  });
25269
26398
  registerLimitedTools(server);
25270
26399
  console.error(`ContextStream MCP server v${VERSION} (limited mode)`);
25271
- console.error('Run "npx -y @contextstream/mcp-server setup" to enable all tools.');
26400
+ console.error('Run "npx --prefer-online -y @contextstream/mcp-server@latest setup" to enable all tools.');
25272
26401
  const transport = new StdioServerTransport();
25273
26402
  await server.connect(transport);
25274
26403
  console.error("ContextStream MCP server connected (limited mode - setup required)");