@contextstream/mcp-server 0.4.57 → 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 +72 -45
- package/dist/hooks/pre-tool-use.js +4 -4
- package/dist/hooks/runner.js +150 -114
- package/dist/hooks/session-init.js +1037 -1
- package/dist/hooks/user-prompt-submit.js +73 -64
- package/dist/index.js +15320 -14913
- package/dist/test-server.js +1 -1
- package/package.json +1 -1
package/dist/hooks/runner.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -187,7 +181,7 @@ async function runPreToolUseHook() {
|
|
|
187
181
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
188
182
|
`);
|
|
189
183
|
if (isDiscoveryGlob(pattern)) {
|
|
190
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
184
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
|
|
191
185
|
fs.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
192
186
|
`);
|
|
193
187
|
if (editorFormat === "cline") {
|
|
@@ -210,7 +204,7 @@ async function runPreToolUseHook() {
|
|
|
210
204
|
}
|
|
211
205
|
blockClaudeCode(msg);
|
|
212
206
|
} else {
|
|
213
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
207
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
|
|
214
208
|
if (editorFormat === "cline") {
|
|
215
209
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
216
210
|
} else if (editorFormat === "cursor") {
|
|
@@ -222,7 +216,7 @@ async function runPreToolUseHook() {
|
|
|
222
216
|
} else if (tool === "Task") {
|
|
223
217
|
const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
|
|
224
218
|
if (subagentType === "explore") {
|
|
225
|
-
const msg = 'STOP: Use mcp__contextstream__search(mode="
|
|
219
|
+
const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
|
|
226
220
|
if (editorFormat === "cline") {
|
|
227
221
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
228
222
|
} else if (editorFormat === "cursor") {
|
|
@@ -251,7 +245,7 @@ async function runPreToolUseHook() {
|
|
|
251
245
|
if (tool === "list_files" || tool === "search_files") {
|
|
252
246
|
const pattern = toolInput?.path || toolInput?.regex || "";
|
|
253
247
|
if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
|
|
254
|
-
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.`;
|
|
255
249
|
if (editorFormat === "cline") {
|
|
256
250
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
257
251
|
} else if (editorFormat === "cursor") {
|
|
@@ -451,7 +445,7 @@ var init_version = __esm({
|
|
|
451
445
|
};
|
|
452
446
|
UPGRADE_COMMAND = UPDATE_COMMANDS.npm;
|
|
453
447
|
VERSION = getVersion();
|
|
454
|
-
CACHE_TTL_MS =
|
|
448
|
+
CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
455
449
|
latestVersionPromise = null;
|
|
456
450
|
}
|
|
457
451
|
});
|
|
@@ -683,6 +677,34 @@ async function saveLastExchange(exchange, cwd, clientName) {
|
|
|
683
677
|
} catch {
|
|
684
678
|
}
|
|
685
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
|
+
}
|
|
686
708
|
async function fetchSessionContext() {
|
|
687
709
|
if (!API_KEY) return null;
|
|
688
710
|
try {
|
|
@@ -761,26 +783,6 @@ function transformSmartContextResponse(data) {
|
|
|
761
783
|
return null;
|
|
762
784
|
}
|
|
763
785
|
}
|
|
764
|
-
function buildClaudeReminder(ctx, versionNotice) {
|
|
765
|
-
const parts = [];
|
|
766
|
-
if (versionNotice?.behind) {
|
|
767
|
-
const versionInfo = getVersionNoticeForHook(versionNotice);
|
|
768
|
-
if (versionInfo) {
|
|
769
|
-
parts.push(versionInfo);
|
|
770
|
-
parts.push("");
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
|
|
774
|
-
if (highImportancePrefs.length > 0) {
|
|
775
|
-
parts.push(`[USER PREFERENCES - Always respect these]`);
|
|
776
|
-
for (const pref of highImportancePrefs.slice(0, 5)) {
|
|
777
|
-
parts.push(`\u2022 ${pref.title}: ${pref.content}`);
|
|
778
|
-
}
|
|
779
|
-
parts.push("");
|
|
780
|
-
}
|
|
781
|
-
parts.push(REMINDER);
|
|
782
|
-
return parts.join("\n");
|
|
783
|
-
}
|
|
784
786
|
function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
785
787
|
const parts = [ENHANCED_REMINDER_HEADER];
|
|
786
788
|
if (versionNotice?.behind) {
|
|
@@ -798,7 +800,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
798
800
|
2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
|
|
799
801
|
3. Generate a unique session_id (e.g., "session-" + timestamp or UUID) - use this for ALL context() calls
|
|
800
802
|
4. Call \`context(user_message="...", save_exchange=true, session_id="<your-session-id>")\` for task-specific context
|
|
801
|
-
5. Use \`search(mode="
|
|
803
|
+
5. Use \`search(mode="auto")\` for code discovery (not Glob/Grep/Read)
|
|
802
804
|
|
|
803
805
|
`);
|
|
804
806
|
}
|
|
@@ -839,7 +841,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
839
841
|
parts.push("");
|
|
840
842
|
}
|
|
841
843
|
parts.push("---\n");
|
|
842
|
-
parts.push(
|
|
844
|
+
parts.push(FULL_REMINDER);
|
|
843
845
|
parts.push(`
|
|
844
846
|
|
|
845
847
|
---
|
|
@@ -856,7 +858,7 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
|
|
|
856
858
|
### \u{1F50D} Search Decision Tree:
|
|
857
859
|
|
|
858
860
|
**IF indexed=true AND last_indexed_at is recent:**
|
|
859
|
-
\u2192 Use \`search(mode="
|
|
861
|
+
\u2192 Use \`search(mode="auto", query="...")\`
|
|
860
862
|
|
|
861
863
|
**IF indexed=false OR last_indexed_at is stale (>7 days):**
|
|
862
864
|
\u2192 Use local tools (Glob/Grep/Read) directly
|
|
@@ -927,59 +929,58 @@ async function runUserPromptSubmitHook() {
|
|
|
927
929
|
}
|
|
928
930
|
const editorFormat = detectEditorFormat2(input);
|
|
929
931
|
const cwd = input.cwd || process.cwd();
|
|
930
|
-
loadConfigFromMcpJson(cwd);
|
|
931
|
-
const versionNoticePromise = getUpdateNotice();
|
|
932
|
-
const lastExchange = extractLastExchange(input, editorFormat);
|
|
933
|
-
const clientName = editorFormat === "claude" ? "claude-code" : editorFormat;
|
|
934
|
-
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
935
932
|
if (editorFormat === "claude") {
|
|
936
|
-
|
|
937
|
-
|
|
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
|
+
}
|
|
938
944
|
console.log(
|
|
939
945
|
JSON.stringify({
|
|
940
946
|
hookSpecificOutput: {
|
|
941
947
|
hookEventName: "UserPromptSubmit",
|
|
942
|
-
additionalContext:
|
|
948
|
+
additionalContext: context
|
|
943
949
|
}
|
|
944
950
|
})
|
|
945
951
|
);
|
|
946
|
-
} else
|
|
947
|
-
|
|
948
|
-
const
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
cancel: false,
|
|
953
|
-
contextModification: enhancedReminder
|
|
954
|
-
})
|
|
955
|
-
);
|
|
956
|
-
} 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();
|
|
957
958
|
const newSession = isNewSession(input, editorFormat);
|
|
958
959
|
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
+
);
|
|
962
979
|
}
|
|
963
|
-
console.log(
|
|
964
|
-
JSON.stringify({
|
|
965
|
-
continue: true,
|
|
966
|
-
user_message: cursorReminder
|
|
967
|
-
})
|
|
968
|
-
);
|
|
969
|
-
} else if (editorFormat === "antigravity") {
|
|
970
|
-
const newSession = isNewSession(input, editorFormat);
|
|
971
|
-
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
972
|
-
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
973
|
-
console.log(
|
|
974
|
-
JSON.stringify({
|
|
975
|
-
cancel: false,
|
|
976
|
-
contextModification: enhancedReminder
|
|
977
|
-
})
|
|
978
|
-
);
|
|
979
980
|
}
|
|
980
981
|
process.exit(0);
|
|
981
982
|
}
|
|
982
|
-
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;
|
|
983
984
|
var init_user_prompt_submit = __esm({
|
|
984
985
|
"src/hooks/user-prompt-submit.ts"() {
|
|
985
986
|
"use strict";
|
|
@@ -989,7 +990,9 @@ var init_user_prompt_submit = __esm({
|
|
|
989
990
|
API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
990
991
|
WORKSPACE_ID = null;
|
|
991
992
|
PROJECT_ID = null;
|
|
992
|
-
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]
|
|
993
996
|
|
|
994
997
|
1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
|
|
995
998
|
- Returns: dynamic rules, lessons from past mistakes, relevant context
|
|
@@ -999,7 +1002,7 @@ var init_user_prompt_submit = __esm({
|
|
|
999
1002
|
|
|
1000
1003
|
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
1001
1004
|
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
1002
|
-
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="
|
|
1005
|
+
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...")
|
|
1003
1006
|
\u2705 IF NOT indexed OR stale: Use local tools (Glob/Grep/Read) directly
|
|
1004
1007
|
\u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
|
|
1005
1008
|
|
|
@@ -1857,20 +1860,31 @@ __export(hooks_config_exports, {
|
|
|
1857
1860
|
writeIndexStatus: () => writeIndexStatus
|
|
1858
1861
|
});
|
|
1859
1862
|
import * as fs5 from "node:fs/promises";
|
|
1863
|
+
import * as fsSync from "node:fs";
|
|
1860
1864
|
import * as path5 from "node:path";
|
|
1861
1865
|
import { homedir as homedir6 } from "node:os";
|
|
1862
1866
|
import { fileURLToPath } from "node:url";
|
|
1863
1867
|
function getHookCommand(hookName2) {
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
+
const isWindows = process.platform === "win32";
|
|
1869
|
+
if (isWindows) {
|
|
1870
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
1871
|
+
if (localAppData) {
|
|
1872
|
+
const windowsBinaryPath = path5.join(localAppData, "ContextStream", "contextstream-mcp.exe");
|
|
1873
|
+
if (fsSync.existsSync(windowsBinaryPath)) {
|
|
1874
|
+
return `"${windowsBinaryPath}" hook ${hookName2}`;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
} else {
|
|
1878
|
+
const unixBinaryPath = "/usr/local/bin/contextstream-mcp";
|
|
1879
|
+
if (fsSync.existsSync(unixBinaryPath)) {
|
|
1880
|
+
return `${unixBinaryPath} hook ${hookName2}`;
|
|
1881
|
+
}
|
|
1868
1882
|
}
|
|
1869
1883
|
try {
|
|
1870
1884
|
const __dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
1871
1885
|
const indexPath = path5.join(__dirname, "index.js");
|
|
1872
|
-
if (
|
|
1873
|
-
return `node ${indexPath} hook ${hookName2}`;
|
|
1886
|
+
if (fsSync.existsSync(indexPath)) {
|
|
1887
|
+
return `node "${indexPath}" hook ${hookName2}`;
|
|
1874
1888
|
}
|
|
1875
1889
|
} catch {
|
|
1876
1890
|
}
|
|
@@ -2283,20 +2297,43 @@ function getClineHooksDir(scope, projectPath) {
|
|
|
2283
2297
|
}
|
|
2284
2298
|
return path5.join(projectPath, ".clinerules", "hooks");
|
|
2285
2299
|
}
|
|
2300
|
+
function getHookWrapperScript(hookName2) {
|
|
2301
|
+
const isWindows = process.platform === "win32";
|
|
2302
|
+
const command = getHookCommand(hookName2);
|
|
2303
|
+
if (isWindows) {
|
|
2304
|
+
return {
|
|
2305
|
+
content: `@echo off\r
|
|
2306
|
+
${command}\r
|
|
2307
|
+
`,
|
|
2308
|
+
extension: ".cmd"
|
|
2309
|
+
};
|
|
2310
|
+
} else {
|
|
2311
|
+
return {
|
|
2312
|
+
content: `#!/bin/bash
|
|
2313
|
+
# ContextStream ${hookName2} Hook Wrapper for Cline/Roo/Kilo Code
|
|
2314
|
+
exec ${command}
|
|
2315
|
+
`,
|
|
2316
|
+
extension: ""
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2286
2320
|
async function installClineHookScripts(options) {
|
|
2287
2321
|
const hooksDir = getClineHooksDir(options.scope, options.projectPath);
|
|
2288
2322
|
await fs5.mkdir(hooksDir, { recursive: true });
|
|
2289
|
-
const
|
|
2290
|
-
const
|
|
2291
|
-
const
|
|
2292
|
-
|
|
2293
|
-
|
|
2323
|
+
const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
|
|
2324
|
+
const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
|
|
2325
|
+
const postWriteWrapper = getHookWrapperScript("post-write");
|
|
2326
|
+
const preToolUsePath = path5.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
|
|
2327
|
+
const userPromptPath = path5.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
|
|
2328
|
+
const postToolUsePath = path5.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
|
|
2329
|
+
await fs5.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
|
|
2330
|
+
await fs5.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
|
|
2294
2331
|
const result = {
|
|
2295
2332
|
preToolUse: preToolUsePath,
|
|
2296
2333
|
userPromptSubmit: userPromptPath
|
|
2297
2334
|
};
|
|
2298
2335
|
if (options.includePostWrite !== false) {
|
|
2299
|
-
await fs5.writeFile(postToolUsePath,
|
|
2336
|
+
await fs5.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
|
|
2300
2337
|
result.postToolUse = postToolUsePath;
|
|
2301
2338
|
}
|
|
2302
2339
|
return result;
|
|
@@ -2313,17 +2350,20 @@ function getRooCodeHooksDir(scope, projectPath) {
|
|
|
2313
2350
|
async function installRooCodeHookScripts(options) {
|
|
2314
2351
|
const hooksDir = getRooCodeHooksDir(options.scope, options.projectPath);
|
|
2315
2352
|
await fs5.mkdir(hooksDir, { recursive: true });
|
|
2316
|
-
const
|
|
2317
|
-
const
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
|
|
2353
|
+
const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
|
|
2354
|
+
const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
|
|
2355
|
+
const postWriteWrapper = getHookWrapperScript("post-write");
|
|
2356
|
+
const preToolUsePath = path5.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
|
|
2357
|
+
const userPromptPath = path5.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
|
|
2358
|
+
const postToolUsePath = path5.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
|
|
2359
|
+
await fs5.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
|
|
2360
|
+
await fs5.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
|
|
2321
2361
|
const result = {
|
|
2322
2362
|
preToolUse: preToolUsePath,
|
|
2323
2363
|
userPromptSubmit: userPromptPath
|
|
2324
2364
|
};
|
|
2325
2365
|
if (options.includePostWrite !== false) {
|
|
2326
|
-
await fs5.writeFile(postToolUsePath,
|
|
2366
|
+
await fs5.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
|
|
2327
2367
|
result.postToolUse = postToolUsePath;
|
|
2328
2368
|
}
|
|
2329
2369
|
return result;
|
|
@@ -2340,17 +2380,20 @@ function getKiloCodeHooksDir(scope, projectPath) {
|
|
|
2340
2380
|
async function installKiloCodeHookScripts(options) {
|
|
2341
2381
|
const hooksDir = getKiloCodeHooksDir(options.scope, options.projectPath);
|
|
2342
2382
|
await fs5.mkdir(hooksDir, { recursive: true });
|
|
2343
|
-
const
|
|
2344
|
-
const
|
|
2345
|
-
const
|
|
2346
|
-
|
|
2347
|
-
|
|
2383
|
+
const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
|
|
2384
|
+
const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
|
|
2385
|
+
const postWriteWrapper = getHookWrapperScript("post-write");
|
|
2386
|
+
const preToolUsePath = path5.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
|
|
2387
|
+
const userPromptPath = path5.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
|
|
2388
|
+
const postToolUsePath = path5.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
|
|
2389
|
+
await fs5.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
|
|
2390
|
+
await fs5.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
|
|
2348
2391
|
const result = {
|
|
2349
2392
|
preToolUse: preToolUsePath,
|
|
2350
2393
|
userPromptSubmit: userPromptPath
|
|
2351
2394
|
};
|
|
2352
2395
|
if (options.includePostWrite !== false) {
|
|
2353
|
-
await fs5.writeFile(postToolUsePath,
|
|
2396
|
+
await fs5.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
|
|
2354
2397
|
result.postToolUse = postToolUsePath;
|
|
2355
2398
|
}
|
|
2356
2399
|
return result;
|
|
@@ -2579,7 +2622,7 @@ Set environment variables:
|
|
|
2579
2622
|
- \`CONTEXTSTREAM_REMINDER_ENABLED=false\` - Disable UserPromptSubmit reminders
|
|
2580
2623
|
`.trim();
|
|
2581
2624
|
}
|
|
2582
|
-
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,
|
|
2625
|
+
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, CURSOR_PRETOOLUSE_HOOK_SCRIPT, CURSOR_BEFORE_SUBMIT_HOOK_SCRIPT;
|
|
2583
2626
|
var init_hooks_config = __esm({
|
|
2584
2627
|
"src/hooks-config.ts"() {
|
|
2585
2628
|
"use strict";
|
|
@@ -2690,7 +2733,7 @@ def main():
|
|
|
2690
2733
|
if tool == "Glob":
|
|
2691
2734
|
pattern = inp.get("pattern", "")
|
|
2692
2735
|
if is_discovery_glob(pattern):
|
|
2693
|
-
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)
|
|
2694
2737
|
sys.exit(2)
|
|
2695
2738
|
|
|
2696
2739
|
elif tool == "Grep" or tool == "Search":
|
|
@@ -2702,12 +2745,12 @@ def main():
|
|
|
2702
2745
|
# Specific file - suggest Read instead
|
|
2703
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)
|
|
2704
2747
|
else:
|
|
2705
|
-
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)
|
|
2706
2749
|
sys.exit(2)
|
|
2707
2750
|
|
|
2708
2751
|
elif tool == "Task":
|
|
2709
2752
|
if inp.get("subagent_type", "").lower() == "explore":
|
|
2710
|
-
print("STOP: Use mcp__contextstream__search(mode=\\"
|
|
2753
|
+
print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
|
|
2711
2754
|
sys.exit(2)
|
|
2712
2755
|
if inp.get("subagent_type", "").lower() == "plan":
|
|
2713
2756
|
print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
|
|
@@ -2734,7 +2777,7 @@ import os
|
|
|
2734
2777
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
2735
2778
|
|
|
2736
2779
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
2737
|
-
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="
|
|
2780
|
+
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="auto") FIRST
|
|
2738
2781
|
2. Call context_smart at start of EVERY response
|
|
2739
2782
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
2740
2783
|
[END RULES]"""
|
|
@@ -3170,7 +3213,7 @@ def main():
|
|
|
3170
3213
|
pattern = params.get("path", "") or params.get("regex", "")
|
|
3171
3214
|
if is_discovery_glob(pattern) or is_discovery_grep(pattern):
|
|
3172
3215
|
output_block(
|
|
3173
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3216
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3174
3217
|
"ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
|
|
3175
3218
|
"[CONTEXTSTREAM] Use ContextStream search for code discovery."
|
|
3176
3219
|
)
|
|
@@ -3197,7 +3240,7 @@ import os
|
|
|
3197
3240
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
3198
3241
|
|
|
3199
3242
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
3200
|
-
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
|
|
3201
3244
|
2. Call context_smart at start of EVERY response
|
|
3202
3245
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
3203
3246
|
[END RULES]"""
|
|
@@ -3239,13 +3282,6 @@ esac
|
|
|
3239
3282
|
|
|
3240
3283
|
exit 0
|
|
3241
3284
|
`;
|
|
3242
|
-
CLINE_HOOK_WRAPPER = (hookName2) => {
|
|
3243
|
-
const command = getHookCommand(hookName2);
|
|
3244
|
-
return `#!/bin/bash
|
|
3245
|
-
# ContextStream ${hookName2} Hook Wrapper for Cline/Roo/Kilo Code
|
|
3246
|
-
exec ${command}
|
|
3247
|
-
`;
|
|
3248
|
-
};
|
|
3249
3285
|
CURSOR_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
|
|
3250
3286
|
"""
|
|
3251
3287
|
ContextStream PreToolUse Hook for Cursor
|
|
@@ -3355,7 +3391,7 @@ def main():
|
|
|
3355
3391
|
pattern = params.get("pattern", "") or params.get("path", "")
|
|
3356
3392
|
if is_discovery_glob(pattern):
|
|
3357
3393
|
output_deny(
|
|
3358
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3394
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3359
3395
|
"ContextStream search is indexed and faster."
|
|
3360
3396
|
)
|
|
3361
3397
|
|