@contextstream/mcp-server 0.4.58 → 0.4.59
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/hooks/auto-rules.js +7 -7
- package/dist/hooks/pre-tool-use.js +4 -4
- package/dist/hooks/runner.js +84 -77
- package/dist/hooks/session-init.js +21 -20
- package/dist/hooks/user-prompt-submit.js +72 -65
- package/dist/index.js +289 -190
- package/package.json +1 -1
package/dist/hooks/auto-rules.js
CHANGED
|
@@ -925,7 +925,7 @@ def main():
|
|
|
925
925
|
if tool == "Glob":
|
|
926
926
|
pattern = inp.get("pattern", "")
|
|
927
927
|
if is_discovery_glob(pattern):
|
|
928
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
928
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
|
|
929
929
|
sys.exit(2)
|
|
930
930
|
|
|
931
931
|
elif tool == "Grep" or tool == "Search":
|
|
@@ -937,12 +937,12 @@ def main():
|
|
|
937
937
|
# Specific file - suggest Read instead
|
|
938
938
|
print(f"STOP: Use Read(\\"{path}\\") to view file content, or mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") for codebase search.", file=sys.stderr)
|
|
939
939
|
else:
|
|
940
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
940
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
|
|
941
941
|
sys.exit(2)
|
|
942
942
|
|
|
943
943
|
elif tool == "Task":
|
|
944
944
|
if inp.get("subagent_type", "").lower() == "explore":
|
|
945
|
-
print("STOP: Use mcp__contextstream__search(mode=\\"
|
|
945
|
+
print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
|
|
946
946
|
sys.exit(2)
|
|
947
947
|
if inp.get("subagent_type", "").lower() == "plan":
|
|
948
948
|
print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
|
|
@@ -969,7 +969,7 @@ import os
|
|
|
969
969
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
970
970
|
|
|
971
971
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
972
|
-
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="
|
|
972
|
+
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="auto") FIRST
|
|
973
973
|
2. Call context_smart at start of EVERY response
|
|
974
974
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
975
975
|
[END RULES]"""
|
|
@@ -1405,7 +1405,7 @@ def main():
|
|
|
1405
1405
|
pattern = params.get("path", "") or params.get("regex", "")
|
|
1406
1406
|
if is_discovery_glob(pattern) or is_discovery_grep(pattern):
|
|
1407
1407
|
output_block(
|
|
1408
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
1408
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
1409
1409
|
"ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
|
|
1410
1410
|
"[CONTEXTSTREAM] Use ContextStream search for code discovery."
|
|
1411
1411
|
)
|
|
@@ -1432,7 +1432,7 @@ import os
|
|
|
1432
1432
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
1433
1433
|
|
|
1434
1434
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
1435
|
-
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="
|
|
1435
|
+
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="auto") FIRST
|
|
1436
1436
|
2. Call context_smart at start of EVERY response
|
|
1437
1437
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
1438
1438
|
[END RULES]"""
|
|
@@ -1583,7 +1583,7 @@ def main():
|
|
|
1583
1583
|
pattern = params.get("pattern", "") or params.get("path", "")
|
|
1584
1584
|
if is_discovery_glob(pattern):
|
|
1585
1585
|
output_deny(
|
|
1586
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
1586
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
1587
1587
|
"ContextStream search is indexed and faster."
|
|
1588
1588
|
)
|
|
1589
1589
|
|
|
@@ -173,7 +173,7 @@ async function runPreToolUseHook() {
|
|
|
173
173
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
174
174
|
`);
|
|
175
175
|
if (isDiscoveryGlob(pattern)) {
|
|
176
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
176
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
|
|
177
177
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
178
178
|
`);
|
|
179
179
|
if (editorFormat === "cline") {
|
|
@@ -196,7 +196,7 @@ async function runPreToolUseHook() {
|
|
|
196
196
|
}
|
|
197
197
|
blockClaudeCode(msg);
|
|
198
198
|
} else {
|
|
199
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
199
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
|
|
200
200
|
if (editorFormat === "cline") {
|
|
201
201
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
202
202
|
} else if (editorFormat === "cursor") {
|
|
@@ -208,7 +208,7 @@ async function runPreToolUseHook() {
|
|
|
208
208
|
} else if (tool === "Task") {
|
|
209
209
|
const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
|
|
210
210
|
if (subagentType === "explore") {
|
|
211
|
-
const msg = 'STOP: Use mcp__contextstream__search(mode="
|
|
211
|
+
const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
|
|
212
212
|
if (editorFormat === "cline") {
|
|
213
213
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
214
214
|
} else if (editorFormat === "cursor") {
|
|
@@ -237,7 +237,7 @@ async function runPreToolUseHook() {
|
|
|
237
237
|
if (tool === "list_files" || tool === "search_files") {
|
|
238
238
|
const pattern = toolInput?.path || toolInput?.regex || "";
|
|
239
239
|
if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
|
|
240
|
-
const msg = `Use mcp__contextstream__search(mode="
|
|
240
|
+
const msg = `Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
|
|
241
241
|
if (editorFormat === "cline") {
|
|
242
242
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
243
243
|
} else if (editorFormat === "cursor") {
|
package/dist/hooks/runner.js
CHANGED
|
@@ -181,7 +181,7 @@ async function runPreToolUseHook() {
|
|
|
181
181
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
182
182
|
`);
|
|
183
183
|
if (isDiscoveryGlob(pattern)) {
|
|
184
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
184
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
|
|
185
185
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
186
186
|
`);
|
|
187
187
|
if (editorFormat === "cline") {
|
|
@@ -204,7 +204,7 @@ async function runPreToolUseHook() {
|
|
|
204
204
|
}
|
|
205
205
|
blockClaudeCode(msg);
|
|
206
206
|
} else {
|
|
207
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
207
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
|
|
208
208
|
if (editorFormat === "cline") {
|
|
209
209
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
210
210
|
} else if (editorFormat === "cursor") {
|
|
@@ -216,7 +216,7 @@ async function runPreToolUseHook() {
|
|
|
216
216
|
} else if (tool === "Task") {
|
|
217
217
|
const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
|
|
218
218
|
if (subagentType === "explore") {
|
|
219
|
-
const msg = 'STOP: Use mcp__contextstream__search(mode="
|
|
219
|
+
const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
|
|
220
220
|
if (editorFormat === "cline") {
|
|
221
221
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
222
222
|
} else if (editorFormat === "cursor") {
|
|
@@ -245,7 +245,7 @@ async function runPreToolUseHook() {
|
|
|
245
245
|
if (tool === "list_files" || tool === "search_files") {
|
|
246
246
|
const pattern = toolInput?.path || toolInput?.regex || "";
|
|
247
247
|
if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
|
|
248
|
-
const msg = `Use mcp__contextstream__search(mode="
|
|
248
|
+
const msg = `Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
|
|
249
249
|
if (editorFormat === "cline") {
|
|
250
250
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
251
251
|
} else if (editorFormat === "cursor") {
|
|
@@ -677,6 +677,34 @@ async function saveLastExchange(exchange, cwd, clientName) {
|
|
|
677
677
|
} catch {
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
|
+
async function fetchHookContext() {
|
|
681
|
+
if (!API_KEY) return null;
|
|
682
|
+
try {
|
|
683
|
+
const controller = new AbortController();
|
|
684
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e3);
|
|
685
|
+
const url = `${API_URL}/api/v1/context/hook`;
|
|
686
|
+
const body = {};
|
|
687
|
+
if (WORKSPACE_ID) body.workspace_id = WORKSPACE_ID;
|
|
688
|
+
if (PROJECT_ID) body.project_id = PROJECT_ID;
|
|
689
|
+
const response = await fetch(url, {
|
|
690
|
+
method: "POST",
|
|
691
|
+
headers: {
|
|
692
|
+
"X-API-Key": API_KEY,
|
|
693
|
+
"Content-Type": "application/json"
|
|
694
|
+
},
|
|
695
|
+
body: JSON.stringify(body),
|
|
696
|
+
signal: controller.signal
|
|
697
|
+
});
|
|
698
|
+
clearTimeout(timeoutId);
|
|
699
|
+
if (response.ok) {
|
|
700
|
+
const data = await response.json();
|
|
701
|
+
return data?.data?.context || null;
|
|
702
|
+
}
|
|
703
|
+
return null;
|
|
704
|
+
} catch {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
680
708
|
async function fetchSessionContext() {
|
|
681
709
|
if (!API_KEY) return null;
|
|
682
710
|
try {
|
|
@@ -755,26 +783,6 @@ function transformSmartContextResponse(data) {
|
|
|
755
783
|
return null;
|
|
756
784
|
}
|
|
757
785
|
}
|
|
758
|
-
function buildClaudeReminder(ctx, versionNotice) {
|
|
759
|
-
const parts = [];
|
|
760
|
-
if (versionNotice?.behind) {
|
|
761
|
-
const versionInfo = getVersionNoticeForHook(versionNotice);
|
|
762
|
-
if (versionInfo) {
|
|
763
|
-
parts.push(versionInfo);
|
|
764
|
-
parts.push("");
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
|
|
768
|
-
if (highImportancePrefs.length > 0) {
|
|
769
|
-
parts.push(`[USER PREFERENCES - Always respect these]`);
|
|
770
|
-
for (const pref of highImportancePrefs.slice(0, 5)) {
|
|
771
|
-
parts.push(`\u2022 ${pref.title}: ${pref.content}`);
|
|
772
|
-
}
|
|
773
|
-
parts.push("");
|
|
774
|
-
}
|
|
775
|
-
parts.push(REMINDER);
|
|
776
|
-
return parts.join("\n");
|
|
777
|
-
}
|
|
778
786
|
function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
779
787
|
const parts = [ENHANCED_REMINDER_HEADER];
|
|
780
788
|
if (versionNotice?.behind) {
|
|
@@ -792,7 +800,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
792
800
|
2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
|
|
793
801
|
3. Generate a unique session_id (e.g., "session-" + timestamp or UUID) - use this for ALL context() calls
|
|
794
802
|
4. Call \`context(user_message="...", save_exchange=true, session_id="<your-session-id>")\` for task-specific context
|
|
795
|
-
5. Use \`search(mode="
|
|
803
|
+
5. Use \`search(mode="auto")\` for code discovery (not Glob/Grep/Read)
|
|
796
804
|
|
|
797
805
|
`);
|
|
798
806
|
}
|
|
@@ -833,7 +841,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
833
841
|
parts.push("");
|
|
834
842
|
}
|
|
835
843
|
parts.push("---\n");
|
|
836
|
-
parts.push(
|
|
844
|
+
parts.push(FULL_REMINDER);
|
|
837
845
|
parts.push(`
|
|
838
846
|
|
|
839
847
|
---
|
|
@@ -850,7 +858,7 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
|
|
|
850
858
|
### \u{1F50D} Search Decision Tree:
|
|
851
859
|
|
|
852
860
|
**IF indexed=true AND last_indexed_at is recent:**
|
|
853
|
-
\u2192 Use \`search(mode="
|
|
861
|
+
\u2192 Use \`search(mode="auto", query="...")\`
|
|
854
862
|
|
|
855
863
|
**IF indexed=false OR last_indexed_at is stale (>7 days):**
|
|
856
864
|
\u2192 Use local tools (Glob/Grep/Read) directly
|
|
@@ -921,61 +929,58 @@ async function runUserPromptSubmitHook() {
|
|
|
921
929
|
}
|
|
922
930
|
const editorFormat = detectEditorFormat2(input);
|
|
923
931
|
const cwd = input.cwd || process.cwd();
|
|
924
|
-
loadConfigFromMcpJson(cwd);
|
|
925
|
-
const versionNoticePromise = getUpdateNotice();
|
|
926
|
-
const lastExchange = extractLastExchange(input, editorFormat);
|
|
927
|
-
const clientName = editorFormat === "claude" ? "claude-code" : editorFormat;
|
|
928
|
-
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
929
932
|
if (editorFormat === "claude") {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
933
|
+
loadConfigFromMcpJson(cwd);
|
|
934
|
+
let context = REMINDER;
|
|
935
|
+
if (API_KEY) {
|
|
936
|
+
try {
|
|
937
|
+
const hookContext = await fetchHookContext();
|
|
938
|
+
if (hookContext) {
|
|
939
|
+
context = hookContext;
|
|
940
|
+
}
|
|
941
|
+
} catch {
|
|
942
|
+
}
|
|
943
|
+
}
|
|
934
944
|
console.log(
|
|
935
945
|
JSON.stringify({
|
|
936
946
|
hookSpecificOutput: {
|
|
937
947
|
hookEventName: "UserPromptSubmit",
|
|
938
|
-
additionalContext:
|
|
948
|
+
additionalContext: context
|
|
939
949
|
}
|
|
940
950
|
})
|
|
941
951
|
);
|
|
942
|
-
} else
|
|
943
|
-
|
|
944
|
-
const
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
cancel: false,
|
|
949
|
-
contextModification: enhancedReminder
|
|
950
|
-
})
|
|
951
|
-
);
|
|
952
|
-
} else if (editorFormat === "cursor") {
|
|
952
|
+
} else {
|
|
953
|
+
loadConfigFromMcpJson(cwd);
|
|
954
|
+
const versionNoticePromise = getUpdateNotice();
|
|
955
|
+
const lastExchange = extractLastExchange(input, editorFormat);
|
|
956
|
+
const clientName = editorFormat;
|
|
957
|
+
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
953
958
|
const newSession = isNewSession(input, editorFormat);
|
|
954
959
|
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
960
|
+
if (editorFormat === "cursor") {
|
|
961
|
+
let cursorReminder = ctx?.lessons?.length ? `[CONTEXTSTREAM] \u26A0\uFE0F ${ctx.lessons.length} lessons from past mistakes. Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="auto") before Glob/Grep. After file edits: project(action="index").` : `[CONTEXTSTREAM] Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="auto") before Glob/Grep/Read. After file edits: project(action="index").`;
|
|
962
|
+
if (versionNotice?.behind) {
|
|
963
|
+
cursorReminder += ` [UPDATE v${versionNotice.current}\u2192${versionNotice.latest}]`;
|
|
964
|
+
}
|
|
965
|
+
console.log(
|
|
966
|
+
JSON.stringify({
|
|
967
|
+
continue: true,
|
|
968
|
+
user_message: cursorReminder
|
|
969
|
+
})
|
|
970
|
+
);
|
|
971
|
+
} else {
|
|
972
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
973
|
+
console.log(
|
|
974
|
+
JSON.stringify({
|
|
975
|
+
cancel: false,
|
|
976
|
+
contextModification: enhancedReminder
|
|
977
|
+
})
|
|
978
|
+
);
|
|
958
979
|
}
|
|
959
|
-
console.log(
|
|
960
|
-
JSON.stringify({
|
|
961
|
-
continue: true,
|
|
962
|
-
user_message: cursorReminder
|
|
963
|
-
})
|
|
964
|
-
);
|
|
965
|
-
} else if (editorFormat === "antigravity") {
|
|
966
|
-
const newSession = isNewSession(input, editorFormat);
|
|
967
|
-
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
968
|
-
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
969
|
-
console.log(
|
|
970
|
-
JSON.stringify({
|
|
971
|
-
cancel: false,
|
|
972
|
-
contextModification: enhancedReminder
|
|
973
|
-
})
|
|
974
|
-
);
|
|
975
980
|
}
|
|
976
981
|
process.exit(0);
|
|
977
982
|
}
|
|
978
|
-
var ENABLED2, API_URL, API_KEY, WORKSPACE_ID, PROJECT_ID, REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun2;
|
|
983
|
+
var ENABLED2, API_URL, API_KEY, WORKSPACE_ID, PROJECT_ID, REMINDER, FULL_REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun2;
|
|
979
984
|
var init_user_prompt_submit = __esm({
|
|
980
985
|
"src/hooks/user-prompt-submit.ts"() {
|
|
981
986
|
"use strict";
|
|
@@ -985,7 +990,9 @@ var init_user_prompt_submit = __esm({
|
|
|
985
990
|
API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
986
991
|
WORKSPACE_ID = null;
|
|
987
992
|
PROJECT_ID = null;
|
|
988
|
-
REMINDER = `[CONTEXTSTREAM
|
|
993
|
+
REMINDER = `[CONTEXTSTREAM] Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. Response contains dynamic rules, lessons, preferences. For search: use search(mode="auto") if indexed, else local tools.
|
|
994
|
+
[END]`;
|
|
995
|
+
FULL_REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
|
|
989
996
|
|
|
990
997
|
1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
|
|
991
998
|
- Returns: dynamic rules, lessons from past mistakes, relevant context
|
|
@@ -995,7 +1002,7 @@ var init_user_prompt_submit = __esm({
|
|
|
995
1002
|
|
|
996
1003
|
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
997
1004
|
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
998
|
-
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="
|
|
1005
|
+
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...")
|
|
999
1006
|
\u2705 IF NOT indexed OR stale: Use local tools (Glob/Grep/Read) directly
|
|
1000
1007
|
\u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
|
|
1001
1008
|
|
|
@@ -2726,7 +2733,7 @@ def main():
|
|
|
2726
2733
|
if tool == "Glob":
|
|
2727
2734
|
pattern = inp.get("pattern", "")
|
|
2728
2735
|
if is_discovery_glob(pattern):
|
|
2729
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
2736
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
|
|
2730
2737
|
sys.exit(2)
|
|
2731
2738
|
|
|
2732
2739
|
elif tool == "Grep" or tool == "Search":
|
|
@@ -2738,12 +2745,12 @@ def main():
|
|
|
2738
2745
|
# Specific file - suggest Read instead
|
|
2739
2746
|
print(f"STOP: Use Read(\\"{path}\\") to view file content, or mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") for codebase search.", file=sys.stderr)
|
|
2740
2747
|
else:
|
|
2741
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
2748
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
|
|
2742
2749
|
sys.exit(2)
|
|
2743
2750
|
|
|
2744
2751
|
elif tool == "Task":
|
|
2745
2752
|
if inp.get("subagent_type", "").lower() == "explore":
|
|
2746
|
-
print("STOP: Use mcp__contextstream__search(mode=\\"
|
|
2753
|
+
print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
|
|
2747
2754
|
sys.exit(2)
|
|
2748
2755
|
if inp.get("subagent_type", "").lower() == "plan":
|
|
2749
2756
|
print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
|
|
@@ -2770,7 +2777,7 @@ import os
|
|
|
2770
2777
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
2771
2778
|
|
|
2772
2779
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
2773
|
-
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="
|
|
2780
|
+
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="auto") FIRST
|
|
2774
2781
|
2. Call context_smart at start of EVERY response
|
|
2775
2782
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
2776
2783
|
[END RULES]"""
|
|
@@ -3206,7 +3213,7 @@ def main():
|
|
|
3206
3213
|
pattern = params.get("path", "") or params.get("regex", "")
|
|
3207
3214
|
if is_discovery_glob(pattern) or is_discovery_grep(pattern):
|
|
3208
3215
|
output_block(
|
|
3209
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3216
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3210
3217
|
"ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
|
|
3211
3218
|
"[CONTEXTSTREAM] Use ContextStream search for code discovery."
|
|
3212
3219
|
)
|
|
@@ -3233,7 +3240,7 @@ import os
|
|
|
3233
3240
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
3234
3241
|
|
|
3235
3242
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
3236
|
-
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="
|
|
3243
|
+
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="auto") FIRST
|
|
3237
3244
|
2. Call context_smart at start of EVERY response
|
|
3238
3245
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
3239
3246
|
[END RULES]"""
|
|
@@ -3384,7 +3391,7 @@ def main():
|
|
|
3384
3391
|
pattern = params.get("pattern", "") or params.get("path", "")
|
|
3385
3392
|
if is_discovery_glob(pattern):
|
|
3386
3393
|
output_deny(
|
|
3387
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3394
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3388
3395
|
"ContextStream search is indexed and faster."
|
|
3389
3396
|
)
|
|
3390
3397
|
|
|
@@ -344,7 +344,7 @@ var CONTEXTSTREAM_TOOL_NAMES = [
|
|
|
344
344
|
"generate_rules",
|
|
345
345
|
// Consolidated domain tools (v0.4.x default)
|
|
346
346
|
"search",
|
|
347
|
-
// Modes: semantic, hybrid, keyword, pattern
|
|
347
|
+
// Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
|
|
348
348
|
"session",
|
|
349
349
|
// Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
|
|
350
350
|
"memory",
|
|
@@ -373,7 +373,7 @@ var CONTEXTSTREAM_RULES_BOOTSTRAP = `
|
|
|
373
373
|
|---------|----------|
|
|
374
374
|
| **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
375
375
|
| **Every message** | \`context(user_message="...")\` FIRST |
|
|
376
|
-
| **Before file search** | \`search(mode="
|
|
376
|
+
| **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read |
|
|
377
377
|
</contextstream_rules>
|
|
378
378
|
|
|
379
379
|
**Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
|
|
@@ -396,7 +396,7 @@ var CONTEXTSTREAM_RULES_FULL = `
|
|
|
396
396
|
| 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
397
397
|
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
398
398
|
|
|
399
|
-
**BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="
|
|
399
|
+
**BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="auto")\` FIRST
|
|
400
400
|
</contextstream_rules>
|
|
401
401
|
|
|
402
402
|
**This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
|
|
@@ -448,7 +448,7 @@ var CONTEXTSTREAM_RULES_FULL = `
|
|
|
448
448
|
- Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
|
|
449
449
|
|
|
450
450
|
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
|
|
451
|
-
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="
|
|
451
|
+
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
|
|
452
452
|
- **You MUST use the suggested ContextStream tool instead**
|
|
453
453
|
- Local tools are only allowed if project is not indexed or ContextStream returns 0 results
|
|
454
454
|
|
|
@@ -472,7 +472,7 @@ Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
|
472
472
|
|
|
473
473
|
**BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
|
|
474
474
|
\`\`\`
|
|
475
|
-
STOP \u2192 Call search(mode="
|
|
475
|
+
STOP \u2192 Call search(mode="auto", query="...") FIRST
|
|
476
476
|
\`\`\`
|
|
477
477
|
|
|
478
478
|
**Note:** PreToolUse hooks block these tools when ContextStream is available.
|
|
@@ -481,11 +481,11 @@ STOP \u2192 Call search(mode="hybrid", query="...") FIRST
|
|
|
481
481
|
\u274C **NEVER DO THIS:**
|
|
482
482
|
- \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
|
|
483
483
|
- \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
|
|
484
|
-
- \`Read(file)\` for discovery \u2192 Use \`search(mode="
|
|
485
|
-
- \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="
|
|
484
|
+
- \`Read(file)\` for discovery \u2192 Use \`search(mode="auto", query="...")\` instead
|
|
485
|
+
- \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="auto")\` instead
|
|
486
486
|
|
|
487
487
|
\u2705 **ALWAYS DO THIS:**
|
|
488
|
-
1. \`search(mode="
|
|
488
|
+
1. \`search(mode="auto", query="what you're looking for")\`
|
|
489
489
|
2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
|
|
490
490
|
3. Use Read ONLY for exact file edits after you know the file path
|
|
491
491
|
|
|
@@ -556,7 +556,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
556
556
|
|---------|----------|
|
|
557
557
|
| **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
558
558
|
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
559
|
-
| **Before file search** | \`search(mode="
|
|
559
|
+
| **Before file search** | \`search(mode="auto")\` FIRST |
|
|
560
560
|
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
561
561
|
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
562
562
|
|
|
@@ -576,7 +576,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
576
576
|
|------|--------------|
|
|
577
577
|
| **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
|
|
578
578
|
| **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
|
|
579
|
-
| **Code search** | \`search(mode="
|
|
579
|
+
| **Code search** | \`search(mode="auto", query="...")\` \u2014 BEFORE Glob/Grep/Read |
|
|
580
580
|
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
581
581
|
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
582
582
|
| **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
|
|
@@ -603,7 +603,7 @@ v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode
|
|
|
603
603
|
|
|
604
604
|
| Domain | Actions/Modes | Example |
|
|
605
605
|
|--------|---------------|---------|
|
|
606
|
-
| **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="
|
|
606
|
+
| **\`search\`** | mode: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern | \`search(mode="auto", query="auth implementation", limit=3)\` |
|
|
607
607
|
| **\`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="...")\` |
|
|
608
608
|
| **\`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)\` |
|
|
609
609
|
| **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
|
|
@@ -749,7 +749,7 @@ session(action="capture", event_type="session_snapshot", title="Pre-compaction s
|
|
|
749
749
|
|
|
750
750
|
### Search & Code Intelligence (ContextStream-first)
|
|
751
751
|
|
|
752
|
-
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="
|
|
752
|
+
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
|
|
753
753
|
|
|
754
754
|
**\u274C WRONG workflow (wastes tokens, slow):**
|
|
755
755
|
\`\`\`
|
|
@@ -758,14 +758,14 @@ Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \
|
|
|
758
758
|
|
|
759
759
|
**\u2705 CORRECT workflow (fast, complete):**
|
|
760
760
|
\`\`\`
|
|
761
|
-
search(mode="
|
|
761
|
+
search(mode="auto", query="function implementation") \u2192 done (results include context)
|
|
762
762
|
\`\`\`
|
|
763
763
|
|
|
764
764
|
**Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
|
|
765
765
|
|
|
766
766
|
**Search order:**
|
|
767
767
|
1. \`session(action="smart_search", query="...")\` - context-enriched
|
|
768
|
-
2. \`search(mode="
|
|
768
|
+
2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
|
|
769
769
|
3. \`project(action="files")\` - file tree/list (only when needed)
|
|
770
770
|
4. \`graph(action="dependencies", ...)\` - code structure
|
|
771
771
|
5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
|
|
@@ -774,7 +774,7 @@ search(mode="hybrid", query="function implementation") \u2192 done (results incl
|
|
|
774
774
|
|
|
775
775
|
| Need | Mode | Example |
|
|
776
776
|
|------|------|---------|
|
|
777
|
-
| Find code by meaning | \`
|
|
777
|
+
| Find code by meaning | \`auto\` | "authentication logic", "error handling" |
|
|
778
778
|
| Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
|
|
779
779
|
| File patterns | \`pattern\` | "*.sql", "test_*.py" |
|
|
780
780
|
| ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
|
|
@@ -925,7 +925,7 @@ var CONTEXTSTREAM_RULES_MINIMAL = `
|
|
|
925
925
|
2. \`context(user_message="<msg>")\` (EVERY subsequent message)
|
|
926
926
|
|
|
927
927
|
**BEFORE Glob/Grep/Read/Search/Explore:**
|
|
928
|
-
\u2192 \`search(mode="
|
|
928
|
+
\u2192 \`search(mode="auto", query="...")\` FIRST \u2014 local tools ONLY if 0 results
|
|
929
929
|
|
|
930
930
|
**HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
|
|
931
931
|
</contextstream_protocol>
|
|
@@ -938,7 +938,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
938
938
|
|
|
939
939
|
| \u274C WRONG | \u2705 CORRECT |
|
|
940
940
|
|----------|-----------|
|
|
941
|
-
| \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="
|
|
941
|
+
| \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="auto")\` (1 call) |
|
|
942
942
|
| Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
|
|
943
943
|
| Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
|
|
944
944
|
|
|
@@ -954,7 +954,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
954
954
|
|------|------|
|
|
955
955
|
| 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
956
956
|
| Every message after | \`context(user_message="...")\` |
|
|
957
|
-
| Before ANY file discovery | \`search(mode="
|
|
957
|
+
| Before ANY file discovery | \`search(mode="auto", query="...")\` |
|
|
958
958
|
| On \`<system-reminder>\` | **Follow instructions inside** |
|
|
959
959
|
| Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
|
|
960
960
|
| Check past mistakes | \`session(action="get_lessons", query="...")\` |
|
|
@@ -963,7 +963,8 @@ Rules Version: ${RULES_VERSION}
|
|
|
963
963
|
|
|
964
964
|
| Mode | When |
|
|
965
965
|
|------|------|
|
|
966
|
-
| \`
|
|
966
|
+
| \`auto\` | Default \u2014 semantic + keyword |
|
|
967
|
+
| \`hybrid\` | Legacy alias for auto |
|
|
967
968
|
| \`keyword\` | Exact symbol match |
|
|
968
969
|
| \`exhaustive\` | Find ALL occurrences |
|
|
969
970
|
| \`semantic\` | Conceptual questions |
|
|
@@ -1143,7 +1144,7 @@ This tells you:
|
|
|
1143
1144
|
|
|
1144
1145
|
**IF project is indexed and fresh:**
|
|
1145
1146
|
\`\`\`
|
|
1146
|
-
search(mode="
|
|
1147
|
+
search(mode="auto", query="what you're looking for")
|
|
1147
1148
|
\`\`\`
|
|
1148
1149
|
|
|
1149
1150
|
**IF project is NOT indexed or very stale (>7 days):**
|