@contextstream/mcp-server 0.4.48 → 0.4.50
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/README.md +27 -0
- package/dist/hooks/auto-rules.js +5 -6
- package/dist/hooks/post-write.js +0 -0
- package/dist/hooks/pre-compact.js +100 -11
- package/dist/hooks/user-prompt-submit.js +10 -5
- package/dist/index.js +675 -126
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -591,7 +591,7 @@ function buildHooksConfig(options) {
|
|
|
591
591
|
],
|
|
592
592
|
UserPromptSubmit: userPromptHooks
|
|
593
593
|
};
|
|
594
|
-
if (options?.includePreCompact) {
|
|
594
|
+
if (options?.includePreCompact !== false) {
|
|
595
595
|
config.PreCompact = [
|
|
596
596
|
{
|
|
597
597
|
// Match both manual (/compact) and automatic compaction
|
|
@@ -643,7 +643,7 @@ async function installHookScripts(options) {
|
|
|
643
643
|
preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
|
|
644
644
|
userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
|
|
645
645
|
};
|
|
646
|
-
if (options?.includePreCompact) {
|
|
646
|
+
if (options?.includePreCompact !== false) {
|
|
647
647
|
result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
|
|
648
648
|
}
|
|
649
649
|
if (options?.includeMediaAware !== false) {
|
|
@@ -689,7 +689,7 @@ async function installClaudeCodeHooks(options) {
|
|
|
689
689
|
"npx @contextstream/mcp-server hook pre-tool-use",
|
|
690
690
|
"npx @contextstream/mcp-server hook user-prompt-submit"
|
|
691
691
|
);
|
|
692
|
-
if (options.includePreCompact) {
|
|
692
|
+
if (options.includePreCompact !== false) {
|
|
693
693
|
result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
|
|
694
694
|
}
|
|
695
695
|
if (options.includeMediaAware !== false) {
|
|
@@ -756,12 +756,11 @@ When Media-Aware hook detects media patterns, it injects context about:
|
|
|
756
756
|
- How to get clips for Remotion (with frame-based props)
|
|
757
757
|
- How to index new media files
|
|
758
758
|
|
|
759
|
-
### PreCompact Hook
|
|
759
|
+
### PreCompact Hook
|
|
760
760
|
- **Command:** \`npx @contextstream/mcp-server hook pre-compact\`
|
|
761
761
|
- **Purpose:** Saves conversation state before context compaction
|
|
762
762
|
- **Triggers:** Both manual (/compact) and automatic compaction
|
|
763
|
-
- **
|
|
764
|
-
- **Note:** Enable with \`generate_rules(include_pre_compact=true)\` to activate
|
|
763
|
+
- **Installed:** By default (disable with \`CONTEXTSTREAM_HOOK_ENABLED=false\`)
|
|
765
764
|
|
|
766
765
|
When PreCompact runs, it:
|
|
767
766
|
1. Parses the transcript for active files and tool calls
|
|
@@ -2633,11 +2632,16 @@ var init_user_prompt_submit = __esm({
|
|
|
2633
2632
|
"src/hooks/user-prompt-submit.ts"() {
|
|
2634
2633
|
"use strict";
|
|
2635
2634
|
ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
|
|
2636
|
-
REMINDER = `[CONTEXTSTREAM
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2635
|
+
REMINDER = `[CONTEXTSTREAM - DO THIS FIRST]
|
|
2636
|
+
mcp__contextstream__context(user_message="<user's message>") - CALL THIS BEFORE ANY OTHER TOOL
|
|
2637
|
+
|
|
2638
|
+
Why: Delivers task-specific rules, lessons from past mistakes, and relevant context.
|
|
2639
|
+
Skip it = repeat past mistakes, miss relevant decisions, fly blind.
|
|
2640
|
+
|
|
2641
|
+
BEFORE Glob/Grep/Read/Search/Explore:
|
|
2642
|
+
mcp__contextstream__search(mode="hybrid", query="...") FIRST
|
|
2643
|
+
Local tools ONLY if search returns 0 results.
|
|
2644
|
+
[END]`;
|
|
2641
2645
|
isDirectRun3 = process.argv[1]?.includes("user-prompt-submit") || process.argv[2] === "user-prompt-submit";
|
|
2642
2646
|
if (isDirectRun3) {
|
|
2643
2647
|
runUserPromptSubmitHook().catch(() => process.exit(0));
|
|
@@ -2821,6 +2825,9 @@ function parseTranscript(transcriptPath) {
|
|
|
2821
2825
|
const activeFiles = /* @__PURE__ */ new Set();
|
|
2822
2826
|
const recentMessages = [];
|
|
2823
2827
|
const toolCalls = [];
|
|
2828
|
+
const messages = [];
|
|
2829
|
+
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2830
|
+
let firstTimestamp = true;
|
|
2824
2831
|
try {
|
|
2825
2832
|
const content = fs10.readFileSync(transcriptPath, "utf-8");
|
|
2826
2833
|
const lines = content.split("\n");
|
|
@@ -2829,6 +2836,11 @@ function parseTranscript(transcriptPath) {
|
|
|
2829
2836
|
try {
|
|
2830
2837
|
const entry = JSON.parse(line);
|
|
2831
2838
|
const msgType = entry.type || "";
|
|
2839
|
+
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
2840
|
+
if (firstTimestamp && entry.timestamp) {
|
|
2841
|
+
startedAt = entry.timestamp;
|
|
2842
|
+
firstTimestamp = false;
|
|
2843
|
+
}
|
|
2832
2844
|
if (msgType === "tool_use") {
|
|
2833
2845
|
const toolName = entry.name || "";
|
|
2834
2846
|
const toolInput = entry.input || {};
|
|
@@ -2844,11 +2856,40 @@ function parseTranscript(transcriptPath) {
|
|
|
2844
2856
|
activeFiles.add(`[glob:${pattern}]`);
|
|
2845
2857
|
}
|
|
2846
2858
|
}
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2859
|
+
messages.push({
|
|
2860
|
+
role: "assistant",
|
|
2861
|
+
content: `[Tool: ${toolName}]`,
|
|
2862
|
+
timestamp,
|
|
2863
|
+
tool_calls: { name: toolName, input: toolInput }
|
|
2864
|
+
});
|
|
2865
|
+
} else if (msgType === "tool_result") {
|
|
2866
|
+
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
2867
|
+
messages.push({
|
|
2868
|
+
role: "tool",
|
|
2869
|
+
content: resultContent,
|
|
2870
|
+
timestamp,
|
|
2871
|
+
tool_results: { name: entry.name }
|
|
2872
|
+
});
|
|
2873
|
+
} else if (msgType === "user" || entry.role === "user") {
|
|
2874
|
+
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
2875
|
+
if (userContent) {
|
|
2876
|
+
messages.push({
|
|
2877
|
+
role: "user",
|
|
2878
|
+
content: userContent,
|
|
2879
|
+
timestamp
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
2883
|
+
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
2884
|
+
if (assistantContent) {
|
|
2885
|
+
messages.push({
|
|
2886
|
+
role: "assistant",
|
|
2887
|
+
content: assistantContent,
|
|
2888
|
+
timestamp
|
|
2889
|
+
});
|
|
2890
|
+
if (assistantContent.length > 50) {
|
|
2891
|
+
recentMessages.push(assistantContent.slice(0, 500));
|
|
2892
|
+
}
|
|
2852
2893
|
}
|
|
2853
2894
|
}
|
|
2854
2895
|
} catch {
|
|
@@ -2861,10 +2902,56 @@ function parseTranscript(transcriptPath) {
|
|
|
2861
2902
|
activeFiles: Array.from(activeFiles).slice(-20),
|
|
2862
2903
|
// Last 20 files
|
|
2863
2904
|
toolCallCount: toolCalls.length,
|
|
2864
|
-
messageCount:
|
|
2865
|
-
lastTools: toolCalls.slice(-10).map((t) => t.name)
|
|
2905
|
+
messageCount: messages.length,
|
|
2906
|
+
lastTools: toolCalls.slice(-10).map((t) => t.name),
|
|
2866
2907
|
// Last 10 tool names
|
|
2908
|
+
messages,
|
|
2909
|
+
startedAt
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
2913
|
+
if (!API_KEY2) {
|
|
2914
|
+
return { success: false, message: "No API key configured" };
|
|
2915
|
+
}
|
|
2916
|
+
if (transcriptData.messages.length === 0) {
|
|
2917
|
+
return { success: false, message: "No messages to save" };
|
|
2918
|
+
}
|
|
2919
|
+
const payload = {
|
|
2920
|
+
session_id: sessionId,
|
|
2921
|
+
messages: transcriptData.messages,
|
|
2922
|
+
started_at: transcriptData.startedAt,
|
|
2923
|
+
source_type: "pre_compact",
|
|
2924
|
+
title: `Pre-compaction save (${trigger})`,
|
|
2925
|
+
metadata: {
|
|
2926
|
+
trigger,
|
|
2927
|
+
active_files: transcriptData.activeFiles,
|
|
2928
|
+
tool_call_count: transcriptData.toolCallCount
|
|
2929
|
+
},
|
|
2930
|
+
tags: ["pre_compaction", trigger]
|
|
2867
2931
|
};
|
|
2932
|
+
if (WORKSPACE_ID) {
|
|
2933
|
+
payload.workspace_id = WORKSPACE_ID;
|
|
2934
|
+
}
|
|
2935
|
+
try {
|
|
2936
|
+
const controller = new AbortController();
|
|
2937
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
2938
|
+
const response = await fetch(`${API_URL2}/api/v1/transcripts`, {
|
|
2939
|
+
method: "POST",
|
|
2940
|
+
headers: {
|
|
2941
|
+
"Content-Type": "application/json",
|
|
2942
|
+
"X-API-Key": API_KEY2
|
|
2943
|
+
},
|
|
2944
|
+
body: JSON.stringify(payload),
|
|
2945
|
+
signal: controller.signal
|
|
2946
|
+
});
|
|
2947
|
+
clearTimeout(timeoutId);
|
|
2948
|
+
if (response.ok) {
|
|
2949
|
+
return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
|
|
2950
|
+
}
|
|
2951
|
+
return { success: false, message: `API error: ${response.status}` };
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
return { success: false, message: String(error) };
|
|
2954
|
+
}
|
|
2868
2955
|
}
|
|
2869
2956
|
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
2870
2957
|
if (!API_KEY2) {
|
|
@@ -2946,13 +3033,19 @@ async function runPreCompactHook() {
|
|
|
2946
3033
|
}
|
|
2947
3034
|
let autoSaveStatus = "";
|
|
2948
3035
|
if (AUTO_SAVE && API_KEY2) {
|
|
2949
|
-
const
|
|
2950
|
-
if (success) {
|
|
3036
|
+
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
3037
|
+
if (transcriptResult.success) {
|
|
2951
3038
|
autoSaveStatus = `
|
|
2952
|
-
[ContextStream:
|
|
3039
|
+
[ContextStream: ${transcriptResult.message}]`;
|
|
2953
3040
|
} else {
|
|
2954
|
-
|
|
3041
|
+
const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
|
|
3042
|
+
if (success) {
|
|
3043
|
+
autoSaveStatus = `
|
|
3044
|
+
[ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
|
|
3045
|
+
} else {
|
|
3046
|
+
autoSaveStatus = `
|
|
2955
3047
|
[ContextStream: Auto-save failed - ${message}]`;
|
|
3048
|
+
}
|
|
2956
3049
|
}
|
|
2957
3050
|
}
|
|
2958
3051
|
const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
|
|
@@ -9931,6 +10024,35 @@ var ContextStreamClient = class {
|
|
|
9931
10024
|
}
|
|
9932
10025
|
});
|
|
9933
10026
|
}
|
|
10027
|
+
/**
|
|
10028
|
+
* Capture a memory event with a direct event_type.
|
|
10029
|
+
* Used for auto-save session snapshots and other system events.
|
|
10030
|
+
*/
|
|
10031
|
+
async captureMemoryEvent(params) {
|
|
10032
|
+
const withDefaults = this.withDefaults(params);
|
|
10033
|
+
const metadata = {
|
|
10034
|
+
...params.metadata || {}
|
|
10035
|
+
};
|
|
10036
|
+
if (params.tags && params.tags.length > 0) {
|
|
10037
|
+
metadata.tags = params.tags;
|
|
10038
|
+
}
|
|
10039
|
+
if (!metadata.captured_at) {
|
|
10040
|
+
metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10041
|
+
}
|
|
10042
|
+
if (!metadata.source) {
|
|
10043
|
+
metadata.source = "mcp_auto_capture";
|
|
10044
|
+
}
|
|
10045
|
+
return this.createMemoryEvent({
|
|
10046
|
+
workspace_id: withDefaults.workspace_id,
|
|
10047
|
+
project_id: withDefaults.project_id,
|
|
10048
|
+
event_type: params.event_type,
|
|
10049
|
+
title: params.title,
|
|
10050
|
+
content: params.content,
|
|
10051
|
+
provenance: params.provenance,
|
|
10052
|
+
code_refs: params.code_refs,
|
|
10053
|
+
metadata
|
|
10054
|
+
});
|
|
10055
|
+
}
|
|
9934
10056
|
submitContextFeedback(body) {
|
|
9935
10057
|
return request(this.config, "/context/smart/feedback", { body: this.withDefaults(body) });
|
|
9936
10058
|
}
|
|
@@ -10498,7 +10620,11 @@ var ContextStreamClient = class {
|
|
|
10498
10620
|
notice_inline: false,
|
|
10499
10621
|
// Session token tracking for context pressure
|
|
10500
10622
|
...params.session_tokens !== void 0 && { session_tokens: params.session_tokens },
|
|
10501
|
-
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold }
|
|
10623
|
+
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold },
|
|
10624
|
+
// Transcript save parameters
|
|
10625
|
+
...params.save_exchange !== void 0 && { save_exchange: params.save_exchange },
|
|
10626
|
+
...params.session_id !== void 0 && { session_id: params.session_id },
|
|
10627
|
+
...params.client_name !== void 0 && { client_name: params.client_name }
|
|
10502
10628
|
}
|
|
10503
10629
|
});
|
|
10504
10630
|
const data = unwrapApiResponse(apiResult);
|
|
@@ -12060,6 +12186,95 @@ ${context}`;
|
|
|
12060
12186
|
uuidSchema.parse(params.doc_id);
|
|
12061
12187
|
return request(this.config, `/docs/${params.doc_id}`, { method: "DELETE" });
|
|
12062
12188
|
}
|
|
12189
|
+
// -------------------------------------------------------------------------
|
|
12190
|
+
// Transcript methods (conversation session storage)
|
|
12191
|
+
// -------------------------------------------------------------------------
|
|
12192
|
+
/**
|
|
12193
|
+
* List transcripts for a workspace/project
|
|
12194
|
+
*/
|
|
12195
|
+
async listTranscripts(params) {
|
|
12196
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12197
|
+
const query = new URLSearchParams();
|
|
12198
|
+
if (withDefaults.workspace_id) query.set("workspace_id", withDefaults.workspace_id);
|
|
12199
|
+
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
12200
|
+
if (params?.session_id) query.set("session_id", params.session_id);
|
|
12201
|
+
if (params?.client_name) query.set("client_name", params.client_name);
|
|
12202
|
+
if (params?.started_after) query.set("started_after", params.started_after);
|
|
12203
|
+
if (params?.started_before) query.set("started_before", params.started_before);
|
|
12204
|
+
if (params?.limit) query.set("per_page", String(params.limit));
|
|
12205
|
+
if (params?.page) query.set("page", String(params.page));
|
|
12206
|
+
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
12207
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
12208
|
+
return request(this.config, `/transcripts${suffix}`, { method: "GET" });
|
|
12209
|
+
}
|
|
12210
|
+
/**
|
|
12211
|
+
* Get a specific transcript by ID
|
|
12212
|
+
*/
|
|
12213
|
+
async getTranscript(transcript_id) {
|
|
12214
|
+
uuidSchema.parse(transcript_id);
|
|
12215
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "GET" });
|
|
12216
|
+
}
|
|
12217
|
+
/**
|
|
12218
|
+
* Search transcripts by content
|
|
12219
|
+
*/
|
|
12220
|
+
async searchTranscripts(params) {
|
|
12221
|
+
const withDefaults = this.withDefaults(params);
|
|
12222
|
+
const queryParams = new URLSearchParams();
|
|
12223
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12224
|
+
queryParams.set("query", params.query);
|
|
12225
|
+
if (params.limit) queryParams.set("limit", String(params.limit));
|
|
12226
|
+
return request(this.config, `/transcripts/search?${queryParams.toString()}`, { method: "GET" });
|
|
12227
|
+
}
|
|
12228
|
+
/**
|
|
12229
|
+
* Delete a transcript
|
|
12230
|
+
*/
|
|
12231
|
+
async deleteTranscript(transcript_id) {
|
|
12232
|
+
uuidSchema.parse(transcript_id);
|
|
12233
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "DELETE" });
|
|
12234
|
+
}
|
|
12235
|
+
// -------------------------------------------------------------------------
|
|
12236
|
+
// Suggested Rules methods (ML-generated rule suggestions)
|
|
12237
|
+
// -------------------------------------------------------------------------
|
|
12238
|
+
/**
|
|
12239
|
+
* List suggested rules
|
|
12240
|
+
*/
|
|
12241
|
+
async listSuggestedRules(params) {
|
|
12242
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12243
|
+
const queryParams = new URLSearchParams();
|
|
12244
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12245
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
12246
|
+
if (params?.source_type) queryParams.set("source_type", params.source_type);
|
|
12247
|
+
if (params?.min_confidence) queryParams.set("min_confidence", String(params.min_confidence));
|
|
12248
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
12249
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
12250
|
+
return request(this.config, `/suggested-rules?${queryParams.toString()}`, { method: "GET" });
|
|
12251
|
+
}
|
|
12252
|
+
/**
|
|
12253
|
+
* Get pending suggested rules count
|
|
12254
|
+
*/
|
|
12255
|
+
async getSuggestedRulesPendingCount(params) {
|
|
12256
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12257
|
+
const queryParams = new URLSearchParams();
|
|
12258
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12259
|
+
return request(this.config, `/suggested-rules/pending-count?${queryParams.toString()}`, { method: "GET" });
|
|
12260
|
+
}
|
|
12261
|
+
/**
|
|
12262
|
+
* Get suggested rules feedback stats
|
|
12263
|
+
*/
|
|
12264
|
+
async getSuggestedRulesStats(params) {
|
|
12265
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12266
|
+
const queryParams = new URLSearchParams();
|
|
12267
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12268
|
+
return request(this.config, `/suggested-rules/stats?${queryParams.toString()}`, { method: "GET" });
|
|
12269
|
+
}
|
|
12270
|
+
/**
|
|
12271
|
+
* Perform action on suggested rule (accept/reject/modify)
|
|
12272
|
+
*/
|
|
12273
|
+
async suggestedRuleAction(params) {
|
|
12274
|
+
uuidSchema.parse(params.rule_id);
|
|
12275
|
+
const { rule_id, ...body } = params;
|
|
12276
|
+
return request(this.config, `/suggested-rules/${rule_id}/action`, { method: "POST", body });
|
|
12277
|
+
}
|
|
12063
12278
|
};
|
|
12064
12279
|
|
|
12065
12280
|
// src/tools.ts
|
|
@@ -12103,62 +12318,103 @@ function applyMcpToolPrefix(markdown, toolPrefix) {
|
|
|
12103
12318
|
const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
|
|
12104
12319
|
return markdown.replace(toolRegex, `${toolPrefix}$1`);
|
|
12105
12320
|
}
|
|
12106
|
-
var
|
|
12107
|
-
|
|
12321
|
+
var CONTEXTSTREAM_RULES_BOOTSTRAP = `
|
|
12322
|
+
<contextstream_rules>
|
|
12323
|
+
| Message | Required |
|
|
12324
|
+
|---------|----------|
|
|
12325
|
+
| **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
12326
|
+
| **Every message** | \`context(user_message="...")\` FIRST |
|
|
12327
|
+
| **Before file search** | \`search(mode="hybrid")\` BEFORE Glob/Grep/Read |
|
|
12328
|
+
</contextstream_rules>
|
|
12108
12329
|
|
|
12109
|
-
|
|
12330
|
+
**Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
|
|
12110
12331
|
|
|
12111
|
-
|
|
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)
|
|
12332
|
+
**Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
|
|
12115
12333
|
|
|
12116
|
-
**
|
|
12117
|
-
\u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
|
|
12334
|
+
**Notices:** [LESSONS_WARNING] \u2192 tell user first | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE] \u2192 tell user to update
|
|
12118
12335
|
|
|
12119
|
-
|
|
12120
|
-
|
|
12336
|
+
v${RULES_VERSION}
|
|
12337
|
+
`.trim();
|
|
12338
|
+
var CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
|
|
12339
|
+
var CONTEXTSTREAM_RULES_FULL = `
|
|
12340
|
+
## \u{1F6A8} MANDATORY: CALL CONTEXT EVERY MESSAGE \u{1F6A8}
|
|
12121
12341
|
|
|
12122
|
-
|
|
12342
|
+
<contextstream_rules>
|
|
12343
|
+
**EVERY response MUST start with \`context(user_message="...")\`**
|
|
12123
12344
|
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12345
|
+
| Message | What to Call |
|
|
12346
|
+
|---------|--------------|
|
|
12347
|
+
| 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
12348
|
+
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
12128
12349
|
|
|
12129
|
-
|
|
12350
|
+
**BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="hybrid")\` FIRST
|
|
12351
|
+
</contextstream_rules>
|
|
12130
12352
|
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12353
|
+
**This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
|
|
12354
|
+
|
|
12355
|
+
---
|
|
12356
|
+
|
|
12357
|
+
## Why \`context()\` is MANDATORY Every Message
|
|
12358
|
+
|
|
12359
|
+
\u274C **WRONG:** "I already called init, I don't need context"
|
|
12360
|
+
\u2705 **CORRECT:** \`context()\` is required EVERY message, not just the first
|
|
12361
|
+
|
|
12362
|
+
**What you lose without \`context()\`:**
|
|
12363
|
+
- Dynamic rules matched to your current task
|
|
12364
|
+
- Lessons from past mistakes (you WILL repeat them)
|
|
12365
|
+
- Semantically relevant decisions and context
|
|
12366
|
+
- Warnings about risky operations
|
|
12367
|
+
|
|
12368
|
+
**\`init()\` gives recent items by TIME. \`context()\` finds items RELEVANT to THIS message.**
|
|
12369
|
+
|
|
12370
|
+
---
|
|
12138
12371
|
|
|
12139
|
-
|
|
12372
|
+
## Handle Notices from context()
|
|
12140
12373
|
|
|
12141
12374
|
- **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
|
|
12142
12375
|
- **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
|
|
12143
12376
|
- **[VERSION_NOTICE]** \u2192 Tell user to update MCP
|
|
12144
12377
|
|
|
12145
|
-
|
|
12378
|
+
---
|
|
12146
12379
|
|
|
12147
|
-
|
|
12148
|
-
\u2192 Just execute directly: \`workspace(action="list")\`, \`help(action="version")\`
|
|
12380
|
+
## \u{1F6A8} HOOKS - AUTOMATIC RULE ENFORCEMENT \u{1F6A8}
|
|
12149
12381
|
|
|
12150
|
-
|
|
12151
|
-
`.trim();
|
|
12152
|
-
var CONTEXTSTREAM_RULES_FULL = `
|
|
12153
|
-
## ContextStream Rules
|
|
12382
|
+
**ContextStream installs hooks that automatically enforce rules.** You MUST follow hook output.
|
|
12154
12383
|
|
|
12155
|
-
|
|
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>
|
|
12384
|
+
### Installed Hooks
|
|
12160
12385
|
|
|
12161
|
-
|
|
12386
|
+
| Hook | What It Does | Output |
|
|
12387
|
+
|------|--------------|--------|
|
|
12388
|
+
| **UserPromptSubmit** | Injects rules reminder on EVERY message | \`<system-reminder>\` with rules block |
|
|
12389
|
+
| **PreToolUse** | Blocks Glob/Grep/Search/Explore when ContextStream is available | Error message redirecting to \`search()\` |
|
|
12390
|
+
| **PostToolUse** | Auto-indexes files after Edit/Write operations | Background indexing |
|
|
12391
|
+
| **PreCompact** | Saves session state before context compaction | Snapshot creation |
|
|
12392
|
+
|
|
12393
|
+
### How Hooks Work
|
|
12394
|
+
|
|
12395
|
+
1. **\`<system-reminder>\` tags** - Injected by UserPromptSubmit hook on every message
|
|
12396
|
+
- These tags contain the current rules
|
|
12397
|
+
- **FOLLOW THE INSTRUCTIONS INSIDE** - they ARE the rules
|
|
12398
|
+
- Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
|
|
12399
|
+
|
|
12400
|
+
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
|
|
12401
|
+
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="hybrid") instead\`
|
|
12402
|
+
- **You MUST use the suggested ContextStream tool instead**
|
|
12403
|
+
- Local tools are only allowed if project is not indexed or ContextStream returns 0 results
|
|
12404
|
+
|
|
12405
|
+
3. **PostToolUse indexing** - After Edit/Write operations:
|
|
12406
|
+
- Changed files are automatically re-indexed
|
|
12407
|
+
- No action required from you
|
|
12408
|
+
|
|
12409
|
+
4. **PreCompact snapshots** - Before context compaction:
|
|
12410
|
+
- Hook reminds you to save important state
|
|
12411
|
+
- Call \`session(action="capture", event_type="session_snapshot", ...)\` when warned
|
|
12412
|
+
|
|
12413
|
+
### Disabling Hooks
|
|
12414
|
+
|
|
12415
|
+
Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
12416
|
+
|
|
12417
|
+
**Note:** Disabling hooks removes rule enforcement. Only disable for debugging.
|
|
12162
12418
|
|
|
12163
12419
|
---
|
|
12164
12420
|
|
|
@@ -12244,27 +12500,27 @@ You have access to ContextStream MCP tools for persistent memory and context.
|
|
|
12244
12500
|
v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
|
|
12245
12501
|
Rules Version: ${RULES_VERSION}
|
|
12246
12502
|
|
|
12247
|
-
## TL;DR -
|
|
12503
|
+
## TL;DR - CONTEXT EVERY MESSAGE
|
|
12248
12504
|
|
|
12249
|
-
|
|
|
12250
|
-
|
|
12251
|
-
|
|
|
12252
|
-
|
|
|
12253
|
-
|
|
|
12254
|
-
|
|
|
12255
|
-
| **User
|
|
12505
|
+
| Message | Required |
|
|
12506
|
+
|---------|----------|
|
|
12507
|
+
| **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
12508
|
+
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
12509
|
+
| **Before file search** | \`search(mode="hybrid")\` FIRST |
|
|
12510
|
+
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
12511
|
+
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
12256
12512
|
|
|
12257
|
-
###
|
|
12513
|
+
### Why EVERY Message?
|
|
12258
12514
|
|
|
12259
|
-
|
|
12260
|
-
-
|
|
12261
|
-
-
|
|
12262
|
-
-
|
|
12263
|
-
-
|
|
12515
|
+
\`context()\` delivers:
|
|
12516
|
+
- **Dynamic rules** matched to your current task
|
|
12517
|
+
- **Lessons** from past mistakes (prevents repeating errors)
|
|
12518
|
+
- **Relevant decisions** and context (semantic search)
|
|
12519
|
+
- **Warnings** about risky operations
|
|
12264
12520
|
|
|
12265
|
-
**
|
|
12521
|
+
**Without \`context()\`, you are blind to relevant context and will repeat past mistakes.**
|
|
12266
12522
|
|
|
12267
|
-
###
|
|
12523
|
+
### Protocol
|
|
12268
12524
|
|
|
12269
12525
|
| Step | What to Call |
|
|
12270
12526
|
|------|--------------|
|
|
@@ -12275,12 +12531,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
12275
12531
|
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
12276
12532
|
| **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
|
|
12277
12533
|
|
|
12278
|
-
**
|
|
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\`:
|
|
12534
|
+
**First message rule:** After \`init\`:
|
|
12284
12535
|
1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
|
|
12285
12536
|
2. Then call \`context\` before any other tool or response
|
|
12286
12537
|
|
|
@@ -12380,8 +12631,8 @@ ContextStream tracks context pressure to help you stay ahead of conversation com
|
|
|
12380
12631
|
- \`prepare_save\`: Start thinking about saving important state
|
|
12381
12632
|
- \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
|
|
12382
12633
|
|
|
12383
|
-
**PreCompact Hook
|
|
12384
|
-
|
|
12634
|
+
**PreCompact Hook:** Automatically saves session state before context compaction.
|
|
12635
|
+
Installed by default. Disable with: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
12385
12636
|
|
|
12386
12637
|
**Before compaction happens (when warned):**
|
|
12387
12638
|
\`\`\`
|
|
@@ -12678,7 +12929,7 @@ Everything else = full protocol (init \u2192 context \u2192 search \u2192 work)
|
|
|
12678
12929
|
### Context Pressure & Compaction
|
|
12679
12930
|
|
|
12680
12931
|
- If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
|
|
12681
|
-
- PreCompact hooks automatically save snapshots before compaction
|
|
12932
|
+
- PreCompact hooks automatically save snapshots before compaction
|
|
12682
12933
|
|
|
12683
12934
|
### Enhanced Context (Warnings)
|
|
12684
12935
|
|
|
@@ -12793,8 +13044,8 @@ function getTemplate(editor) {
|
|
|
12793
13044
|
function generateRuleContent(editor, options) {
|
|
12794
13045
|
const template = getTemplate(editor);
|
|
12795
13046
|
if (!template) return null;
|
|
12796
|
-
const mode = options?.mode || "
|
|
12797
|
-
const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : CONTEXTSTREAM_RULES_DYNAMIC;
|
|
13047
|
+
const mode = options?.mode || "bootstrap";
|
|
13048
|
+
const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : mode === "bootstrap" ? CONTEXTSTREAM_RULES_BOOTSTRAP : CONTEXTSTREAM_RULES_DYNAMIC;
|
|
12798
13049
|
let content = template.build(rules);
|
|
12799
13050
|
if (options?.workspaceName || options?.projectName) {
|
|
12800
13051
|
const header = `
|
|
@@ -13070,6 +13321,85 @@ function trackToolTokenSavings(client, tool, contextText, params, extraMetadata)
|
|
|
13070
13321
|
}
|
|
13071
13322
|
}
|
|
13072
13323
|
|
|
13324
|
+
// src/microcopy.ts
|
|
13325
|
+
function getSessionInitTip(sessionId) {
|
|
13326
|
+
return `Session ${sessionId.slice(0, 8)}... initialized. Use context(user_message="...") to get relevant context.`;
|
|
13327
|
+
}
|
|
13328
|
+
function getCaptureHint(eventType) {
|
|
13329
|
+
switch (eventType) {
|
|
13330
|
+
case "decision":
|
|
13331
|
+
return "Decision captured. It will surface in future context() calls when relevant.";
|
|
13332
|
+
case "preference":
|
|
13333
|
+
return "Preference saved. Future sessions will respect this preference.";
|
|
13334
|
+
case "insight":
|
|
13335
|
+
return "Insight captured. Use recall() to retrieve it later.";
|
|
13336
|
+
case "task":
|
|
13337
|
+
return "Task captured. Use list_tasks() to view all tasks.";
|
|
13338
|
+
case "lesson":
|
|
13339
|
+
return "Lesson saved. It will warn you before similar mistakes.";
|
|
13340
|
+
case "session_snapshot":
|
|
13341
|
+
return "Session state saved. Use restore_context() after compaction.";
|
|
13342
|
+
default:
|
|
13343
|
+
return "Event captured. Use recall() to retrieve related events.";
|
|
13344
|
+
}
|
|
13345
|
+
}
|
|
13346
|
+
function getEmptyStateHint(operation) {
|
|
13347
|
+
switch (operation) {
|
|
13348
|
+
case "get_lessons":
|
|
13349
|
+
return "No lessons found. Lessons are captured when mistakes occur.";
|
|
13350
|
+
case "recall":
|
|
13351
|
+
return "No memories found. Use capture() or remember() to save context.";
|
|
13352
|
+
case "list_plans":
|
|
13353
|
+
return "No plans found. Use capture_plan() to create an implementation plan.";
|
|
13354
|
+
case "list_events":
|
|
13355
|
+
return "No events found. Events are captured as you work.";
|
|
13356
|
+
case "list_tasks":
|
|
13357
|
+
return "No tasks found. Use create_task() to add tasks.";
|
|
13358
|
+
case "list_todos":
|
|
13359
|
+
return "No todos found. Use create_todo() to add quick todos.";
|
|
13360
|
+
case "list_diagrams":
|
|
13361
|
+
return "No diagrams found. Use create_diagram() to save a Mermaid diagram.";
|
|
13362
|
+
case "list_docs":
|
|
13363
|
+
return "No docs found. Use create_doc() to save documentation.";
|
|
13364
|
+
default:
|
|
13365
|
+
return "No results found.";
|
|
13366
|
+
}
|
|
13367
|
+
}
|
|
13368
|
+
function getPlanStatusHint(status) {
|
|
13369
|
+
switch (status) {
|
|
13370
|
+
case "draft":
|
|
13371
|
+
return "Plan saved as draft. Update status to 'active' when ready.";
|
|
13372
|
+
case "active":
|
|
13373
|
+
return "Plan is now active. Create tasks to track implementation.";
|
|
13374
|
+
case "completed":
|
|
13375
|
+
return "Plan completed. Great work!";
|
|
13376
|
+
case "archived":
|
|
13377
|
+
return "Plan archived. It will still appear in searches.";
|
|
13378
|
+
case "abandoned":
|
|
13379
|
+
return "Plan abandoned. Consider capturing lessons learned.";
|
|
13380
|
+
default:
|
|
13381
|
+
return "Plan updated. Changes are preserved.";
|
|
13382
|
+
}
|
|
13383
|
+
}
|
|
13384
|
+
var POST_COMPACT_HINTS = {
|
|
13385
|
+
restored: "Context restored from pre-compaction snapshot.",
|
|
13386
|
+
restored_with_session: (sessionId) => `Context restored from session ${sessionId.slice(0, 8)}... snapshot.`,
|
|
13387
|
+
no_snapshot: "No snapshot found. Session state may be incomplete.",
|
|
13388
|
+
failed: "Failed to restore context. Try recall() to find relevant memories."
|
|
13389
|
+
};
|
|
13390
|
+
var INTEGRATION_HINTS = {
|
|
13391
|
+
connected: "Integration connected and syncing.",
|
|
13392
|
+
not_connected: "Integration not connected. Connect at:",
|
|
13393
|
+
sync_in_progress: "Sync in progress. Results may be incomplete.",
|
|
13394
|
+
sync_complete: "Sync complete. All data is current."
|
|
13395
|
+
};
|
|
13396
|
+
var TASK_HINTS = {
|
|
13397
|
+
created: "Task created. Use update_task() to change status.",
|
|
13398
|
+
completed: "Task completed. Well done!",
|
|
13399
|
+
blocked: "Task blocked. Add blocked_reason for context.",
|
|
13400
|
+
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
13401
|
+
};
|
|
13402
|
+
|
|
13073
13403
|
// src/tools.ts
|
|
13074
13404
|
var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
|
|
13075
13405
|
var LOG_QUIET = LOG_LEVEL === "quiet";
|
|
@@ -13135,6 +13465,7 @@ var lastAutoSavePressureLevel = "";
|
|
|
13135
13465
|
var contextCallsSinceLastSave = 0;
|
|
13136
13466
|
var SEARCH_RULES_REMINDER_ENABLED = process.env.CONTEXTSTREAM_SEARCH_REMINDER?.toLowerCase() !== "false";
|
|
13137
13467
|
var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="hybrid") before Glob/Grep/Read. Local tools only if 0 results.`;
|
|
13468
|
+
var CONTEXT_CALL_REMINDER = `[CONTEXT] Call context(user_message="...") at start of EVERY response. This is MANDATORY.`;
|
|
13138
13469
|
var LESSONS_REMINDER_PREFIX = `
|
|
13139
13470
|
\u{1F6A8} [LESSONS_WARNING] Past Mistakes Found - READ BEFORE PROCEEDING!
|
|
13140
13471
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
@@ -14733,7 +15064,10 @@ function registerTools(server, client, sessionManager) {
|
|
|
14733
15064
|
"notion_stats",
|
|
14734
15065
|
"notion_activity",
|
|
14735
15066
|
"notion_knowledge",
|
|
14736
|
-
"notion_summary"
|
|
15067
|
+
"notion_summary",
|
|
15068
|
+
// Media operations (credit-metered)
|
|
15069
|
+
"media_index",
|
|
15070
|
+
"media_search"
|
|
14737
15071
|
]);
|
|
14738
15072
|
const proTools = (() => {
|
|
14739
15073
|
const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
|
|
@@ -16821,6 +17155,8 @@ This does semantic search on the first message. You only need context on subsequ
|
|
|
16821
17155
|
ideRoots
|
|
16822
17156
|
);
|
|
16823
17157
|
result.tools_hint = getCoreToolsHint();
|
|
17158
|
+
const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
|
|
17159
|
+
result.educational_tip = getSessionInitTip(sessionId);
|
|
16824
17160
|
const shouldRestoreContext = input.is_post_compact ?? RESTORE_CONTEXT_DEFAULT;
|
|
16825
17161
|
if (shouldRestoreContext) {
|
|
16826
17162
|
result.is_post_compact = true;
|
|
@@ -16890,15 +17226,15 @@ This does semantic search on the first message. You only need context on subsequ
|
|
|
16890
17226
|
...snapshotData
|
|
16891
17227
|
};
|
|
16892
17228
|
result.is_post_compact = true;
|
|
16893
|
-
result.post_compact_hint = prevSessionId ?
|
|
17229
|
+
result.post_compact_hint = prevSessionId ? POST_COMPACT_HINTS.restored_with_session(prevSessionId) : POST_COMPACT_HINTS.restored;
|
|
16894
17230
|
} else {
|
|
16895
17231
|
result.is_post_compact = true;
|
|
16896
|
-
result.post_compact_hint =
|
|
17232
|
+
result.post_compact_hint = POST_COMPACT_HINTS.no_snapshot;
|
|
16897
17233
|
}
|
|
16898
17234
|
} catch (err) {
|
|
16899
17235
|
logDebug(`Failed to restore post-compact context: ${err}`);
|
|
16900
17236
|
result.is_post_compact = true;
|
|
16901
|
-
result.post_compact_hint =
|
|
17237
|
+
result.post_compact_hint = POST_COMPACT_HINTS.failed;
|
|
16902
17238
|
}
|
|
16903
17239
|
}
|
|
16904
17240
|
}
|
|
@@ -16934,7 +17270,7 @@ This does semantic search on the first message. You only need context on subsequ
|
|
|
16934
17270
|
slack_connected: intStatus.slack,
|
|
16935
17271
|
github_connected: intStatus.github,
|
|
16936
17272
|
auto_hide_enabled: true,
|
|
16937
|
-
hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." :
|
|
17273
|
+
hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : INTEGRATION_HINTS.not_connected + " https://contextstream.io/settings/integrations"
|
|
16938
17274
|
};
|
|
16939
17275
|
} catch (error) {
|
|
16940
17276
|
logDebug(`Failed to check integration status: ${error}`);
|
|
@@ -17946,11 +18282,11 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
|
|
|
17946
18282
|
workspace_id: external_exports.string().uuid().optional().describe("Workspace ID to include in rules"),
|
|
17947
18283
|
project_name: external_exports.string().optional().describe("Project name to include in rules"),
|
|
17948
18284
|
additional_rules: external_exports.string().optional().describe("Additional project-specific rules to append"),
|
|
17949
|
-
mode: external_exports.enum(["minimal", "full"]).optional().describe("Rule verbosity
|
|
17950
|
-
overwrite_existing: external_exports.boolean().optional().describe("
|
|
18285
|
+
mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional().default("bootstrap").describe("Rule verbosity: bootstrap (~15 lines, recommended), minimal (~80 lines), full (~600 lines)"),
|
|
18286
|
+
overwrite_existing: external_exports.boolean().optional().default(true).describe("Overwrite ContextStream block in existing rule files (default: true). User content outside the block is preserved."),
|
|
17951
18287
|
apply_global: external_exports.boolean().optional().describe("Also write global rule files for supported editors"),
|
|
17952
18288
|
install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks to enforce ContextStream-first search. Defaults to true for Claude users. Set to false to skip."),
|
|
17953
|
-
include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to
|
|
18289
|
+
include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to true."),
|
|
17954
18290
|
dry_run: external_exports.boolean().optional().describe("If true, return content without writing files")
|
|
17955
18291
|
})
|
|
17956
18292
|
},
|
|
@@ -18041,7 +18377,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
|
|
|
18041
18377
|
{ editor, file: "~/.claude/hooks/contextstream-reminder.py", status: "dry run - would create" },
|
|
18042
18378
|
{ editor, file: "~/.claude/settings.json", status: "dry run - would update" }
|
|
18043
18379
|
);
|
|
18044
|
-
if (input.include_pre_compact) {
|
|
18380
|
+
if (input.include_pre_compact !== false) {
|
|
18045
18381
|
hooksResults.push({ editor, file: "~/.claude/hooks/contextstream-precompact.py", status: "dry run - would create" });
|
|
18046
18382
|
}
|
|
18047
18383
|
} else if (editor === "cline") {
|
|
@@ -18072,7 +18408,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
|
|
|
18072
18408
|
const allHookResults = await installAllEditorHooks({
|
|
18073
18409
|
scope: "global",
|
|
18074
18410
|
editors: hookSupportedEditors,
|
|
18075
|
-
includePreCompact: input.include_pre_compact
|
|
18411
|
+
includePreCompact: input.include_pre_compact !== false
|
|
18076
18412
|
});
|
|
18077
18413
|
for (const result of allHookResults) {
|
|
18078
18414
|
for (const file of result.installed) {
|
|
@@ -18428,7 +18764,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18428
18764
|
mode: external_exports.enum(["standard", "pack"]).optional().describe("Context pack mode (default: pack when enabled)"),
|
|
18429
18765
|
distill: external_exports.boolean().optional().describe("Use distillation for context pack (default: true)"),
|
|
18430
18766
|
session_tokens: external_exports.number().optional().describe("Cumulative session token count for context pressure calculation"),
|
|
18431
|
-
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)")
|
|
18767
|
+
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)"),
|
|
18768
|
+
save_exchange: external_exports.boolean().optional().describe("Save this exchange to the transcript for later search (background task)"),
|
|
18769
|
+
session_id: external_exports.string().optional().describe("Session ID for transcript association (required if save_exchange is true)"),
|
|
18770
|
+
client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')")
|
|
18432
18771
|
})
|
|
18433
18772
|
},
|
|
18434
18773
|
async (input) => {
|
|
@@ -18504,6 +18843,14 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18504
18843
|
logDebug(`Failed to restore post-compact context: ${err}`);
|
|
18505
18844
|
}
|
|
18506
18845
|
}
|
|
18846
|
+
let sessionId = input.session_id;
|
|
18847
|
+
if (!sessionId && sessionManager && input.save_exchange) {
|
|
18848
|
+
sessionId = sessionManager.getSessionId();
|
|
18849
|
+
}
|
|
18850
|
+
let clientName = input.client_name;
|
|
18851
|
+
if (!clientName && detectedClientInfo) {
|
|
18852
|
+
clientName = detectedClientInfo.name;
|
|
18853
|
+
}
|
|
18507
18854
|
const result = await client.getSmartContext({
|
|
18508
18855
|
user_message: input.user_message,
|
|
18509
18856
|
workspace_id: workspaceId,
|
|
@@ -18513,7 +18860,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18513
18860
|
mode: input.mode,
|
|
18514
18861
|
distill: input.distill,
|
|
18515
18862
|
session_tokens: sessionTokens,
|
|
18516
|
-
context_threshold: contextThreshold
|
|
18863
|
+
context_threshold: contextThreshold,
|
|
18864
|
+
save_exchange: input.save_exchange,
|
|
18865
|
+
session_id: sessionId,
|
|
18866
|
+
client_name: clientName
|
|
18517
18867
|
});
|
|
18518
18868
|
if (sessionManager && result.token_estimate) {
|
|
18519
18869
|
sessionManager.addTokens(result.token_estimate);
|
|
@@ -18659,6 +19009,9 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
|
|
|
18659
19009
|
const instructionsLine = result.instructions ? `
|
|
18660
19010
|
|
|
18661
19011
|
[INSTRUCTIONS] ${result.instructions}` : "";
|
|
19012
|
+
const contextRulesLine = `
|
|
19013
|
+
|
|
19014
|
+
${CONTEXT_CALL_REMINDER}`;
|
|
18662
19015
|
const allWarnings = [
|
|
18663
19016
|
serverWarningsLine || lessonsWarningLine,
|
|
18664
19017
|
// Server warnings OR client-side lesson detection
|
|
@@ -18671,6 +19024,8 @@ ${versionWarningLine}` : "",
|
|
|
18671
19024
|
contextPressureWarning,
|
|
18672
19025
|
semanticHints,
|
|
18673
19026
|
instructionsLine,
|
|
19027
|
+
contextRulesLine,
|
|
19028
|
+
// Reinforce context() must be called every message
|
|
18674
19029
|
searchRulesLine
|
|
18675
19030
|
].filter(Boolean).join("");
|
|
18676
19031
|
const finalContext = postCompactContext + result.context;
|
|
@@ -19805,7 +20160,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19805
20160
|
"session",
|
|
19806
20161
|
{
|
|
19807
20162
|
title: "Session",
|
|
19808
|
-
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
20163
|
+
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Suggested rules actions: list_suggested_rules (view ML-generated rule suggestions), suggested_rule_action (accept/reject/modify a suggestion), suggested_rules_stats (view ML accuracy stats). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
19809
20164
|
inputSchema: external_exports.object({
|
|
19810
20165
|
action: external_exports.enum([
|
|
19811
20166
|
"capture",
|
|
@@ -19829,7 +20184,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19829
20184
|
// Team actions (team plans only)
|
|
19830
20185
|
"team_decisions",
|
|
19831
20186
|
"team_lessons",
|
|
19832
|
-
"team_plans"
|
|
20187
|
+
"team_plans",
|
|
20188
|
+
// Suggested rules actions (ML-generated)
|
|
20189
|
+
"list_suggested_rules",
|
|
20190
|
+
"suggested_rule_action",
|
|
20191
|
+
"suggested_rules_stats"
|
|
19833
20192
|
]).describe("Action to perform"),
|
|
19834
20193
|
workspace_id: external_exports.string().uuid().optional(),
|
|
19835
20194
|
project_id: external_exports.string().uuid().optional(),
|
|
@@ -19906,7 +20265,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19906
20265
|
is_personal: external_exports.boolean().optional().describe("Mark plan as personal (only visible to creator). For capture_plan/list_plans."),
|
|
19907
20266
|
// Restore context params
|
|
19908
20267
|
snapshot_id: external_exports.string().uuid().optional().describe("Specific snapshot ID to restore (defaults to most recent)"),
|
|
19909
|
-
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)")
|
|
20268
|
+
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)"),
|
|
20269
|
+
// Suggested rules params
|
|
20270
|
+
rule_id: external_exports.string().uuid().optional().describe("Suggested rule ID for actions"),
|
|
20271
|
+
rule_action: external_exports.enum(["accept", "reject", "modify"]).optional().describe("Action to perform on suggested rule"),
|
|
20272
|
+
modified_keywords: external_exports.array(external_exports.string()).optional().describe("Modified keywords when action is modify"),
|
|
20273
|
+
modified_instruction: external_exports.string().optional().describe("Modified instruction when action is modify"),
|
|
20274
|
+
min_confidence: external_exports.number().optional().describe("Minimum confidence threshold for listing rules")
|
|
19910
20275
|
})
|
|
19911
20276
|
},
|
|
19912
20277
|
async (input) => {
|
|
@@ -19929,8 +20294,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19929
20294
|
code_refs: input.code_refs,
|
|
19930
20295
|
provenance: input.provenance
|
|
19931
20296
|
});
|
|
20297
|
+
const captureHint = getCaptureHint(input.event_type);
|
|
20298
|
+
const resultWithHint = { ...result, hint: captureHint };
|
|
19932
20299
|
return {
|
|
19933
|
-
content: [{ type: "text", text: formatContent(
|
|
20300
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
19934
20301
|
};
|
|
19935
20302
|
}
|
|
19936
20303
|
case "capture_lesson": {
|
|
@@ -19986,8 +20353,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19986
20353
|
importance: input.severity === "critical" ? "critical" : input.severity === "high" ? "high" : "medium",
|
|
19987
20354
|
tags: input.keywords || []
|
|
19988
20355
|
});
|
|
20356
|
+
const lessonHint = getCaptureHint("lesson");
|
|
20357
|
+
const resultWithHint = { ...result, hint: lessonHint };
|
|
19989
20358
|
return {
|
|
19990
|
-
content: [{ type: "text", text: formatContent(
|
|
20359
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
19991
20360
|
};
|
|
19992
20361
|
}
|
|
19993
20362
|
case "get_lessons": {
|
|
@@ -20000,8 +20369,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20000
20369
|
context_hint: input.query,
|
|
20001
20370
|
limit: input.limit
|
|
20002
20371
|
});
|
|
20372
|
+
const lessons = result?.data?.lessons || result?.lessons || [];
|
|
20373
|
+
const resultWithHint = Array.isArray(lessons) && lessons.length === 0 ? { ...result, hint: getEmptyStateHint("get_lessons") } : result;
|
|
20003
20374
|
return {
|
|
20004
|
-
content: [{ type: "text", text: formatContent(
|
|
20375
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20005
20376
|
};
|
|
20006
20377
|
}
|
|
20007
20378
|
case "recall": {
|
|
@@ -20015,7 +20386,9 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20015
20386
|
include_related: input.include_related,
|
|
20016
20387
|
include_decisions: input.include_decisions
|
|
20017
20388
|
});
|
|
20018
|
-
const
|
|
20389
|
+
const recallResults = result?.data?.results || result?.results || [];
|
|
20390
|
+
const recallWithHint = Array.isArray(recallResults) && recallResults.length === 0 ? { ...result, hint: getEmptyStateHint("recall") } : result;
|
|
20391
|
+
const outputText = formatContent(recallWithHint);
|
|
20019
20392
|
trackToolTokenSavings(client, "session_recall", outputText, {
|
|
20020
20393
|
workspace_id: workspaceId,
|
|
20021
20394
|
project_id: projectId
|
|
@@ -20035,8 +20408,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20035
20408
|
content: input.content,
|
|
20036
20409
|
importance
|
|
20037
20410
|
});
|
|
20411
|
+
const rememberHint = getCaptureHint("preference");
|
|
20412
|
+
const resultWithHint = { ...result, hint: rememberHint };
|
|
20038
20413
|
return {
|
|
20039
|
-
content: [{ type: "text", text: formatContent(
|
|
20414
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20040
20415
|
};
|
|
20041
20416
|
}
|
|
20042
20417
|
case "user_context": {
|
|
@@ -20149,8 +20524,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20149
20524
|
source_tool: input.source_tool || "mcp",
|
|
20150
20525
|
is_personal: input.is_personal
|
|
20151
20526
|
});
|
|
20527
|
+
const planHint = getPlanStatusHint(input.status || "draft");
|
|
20528
|
+
const resultWithHint = { ...result, hint: planHint };
|
|
20152
20529
|
return {
|
|
20153
|
-
content: [{ type: "text", text: formatContent(
|
|
20530
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20154
20531
|
};
|
|
20155
20532
|
}
|
|
20156
20533
|
case "get_plan": {
|
|
@@ -20180,8 +20557,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20180
20557
|
tags: input.tags,
|
|
20181
20558
|
due_at: input.due_at
|
|
20182
20559
|
});
|
|
20560
|
+
const planUpdateHint = input.status ? getPlanStatusHint(input.status) : "Plan updated. Changes are preserved.";
|
|
20561
|
+
const resultWithHint = { ...result, hint: planUpdateHint };
|
|
20183
20562
|
return {
|
|
20184
|
-
content: [{ type: "text", text: formatContent(
|
|
20563
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20185
20564
|
};
|
|
20186
20565
|
}
|
|
20187
20566
|
case "list_plans": {
|
|
@@ -20195,8 +20574,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20195
20574
|
limit: input.limit,
|
|
20196
20575
|
is_personal: input.is_personal
|
|
20197
20576
|
});
|
|
20577
|
+
const plans = result?.data?.plans || result?.plans || result?.data?.items || result?.items || [];
|
|
20578
|
+
const resultWithHint = plans.length === 0 ? { ...result, hint: getEmptyStateHint("list_plans") } : result;
|
|
20198
20579
|
return {
|
|
20199
|
-
content: [{ type: "text", text: formatContent(
|
|
20580
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20200
20581
|
};
|
|
20201
20582
|
}
|
|
20202
20583
|
case "restore_context": {
|
|
@@ -20466,6 +20847,81 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20466
20847
|
]
|
|
20467
20848
|
};
|
|
20468
20849
|
}
|
|
20850
|
+
case "list_suggested_rules": {
|
|
20851
|
+
const result = await client.listSuggestedRules({
|
|
20852
|
+
workspace_id: workspaceId,
|
|
20853
|
+
status: input.status,
|
|
20854
|
+
min_confidence: input.min_confidence,
|
|
20855
|
+
limit: input.limit
|
|
20856
|
+
});
|
|
20857
|
+
const rules = result?.data?.items || result?.items || [];
|
|
20858
|
+
if (rules.length === 0) {
|
|
20859
|
+
return {
|
|
20860
|
+
content: [
|
|
20861
|
+
{
|
|
20862
|
+
type: "text",
|
|
20863
|
+
text: formatContent({
|
|
20864
|
+
suggested_rules: [],
|
|
20865
|
+
hint: "No pending rule suggestions. The ML system learns from your lessons and will suggest rules when patterns are detected."
|
|
20866
|
+
})
|
|
20867
|
+
}
|
|
20868
|
+
]
|
|
20869
|
+
};
|
|
20870
|
+
}
|
|
20871
|
+
return {
|
|
20872
|
+
content: [
|
|
20873
|
+
{
|
|
20874
|
+
type: "text",
|
|
20875
|
+
text: formatContent({
|
|
20876
|
+
suggested_rules: rules,
|
|
20877
|
+
total: result?.data?.total || rules.length,
|
|
20878
|
+
hint: "Use suggested_rule_action to accept, reject, or modify these suggestions."
|
|
20879
|
+
})
|
|
20880
|
+
}
|
|
20881
|
+
]
|
|
20882
|
+
};
|
|
20883
|
+
}
|
|
20884
|
+
case "suggested_rule_action": {
|
|
20885
|
+
if (!input.rule_id || !input.rule_action) {
|
|
20886
|
+
return errorResult("suggested_rule_action requires: rule_id, rule_action (accept/reject/modify)");
|
|
20887
|
+
}
|
|
20888
|
+
const result = await client.suggestedRuleAction({
|
|
20889
|
+
rule_id: input.rule_id,
|
|
20890
|
+
action: input.rule_action,
|
|
20891
|
+
modified_keywords: input.modified_keywords,
|
|
20892
|
+
modified_instruction: input.modified_instruction
|
|
20893
|
+
});
|
|
20894
|
+
const actionVerb = input.rule_action === "accept" ? "accepted" : input.rule_action === "reject" ? "rejected" : "modified";
|
|
20895
|
+
return {
|
|
20896
|
+
content: [
|
|
20897
|
+
{
|
|
20898
|
+
type: "text",
|
|
20899
|
+
text: formatContent({
|
|
20900
|
+
success: true,
|
|
20901
|
+
message: `Rule ${actionVerb} successfully`,
|
|
20902
|
+
rule: result?.data || result,
|
|
20903
|
+
hint: input.rule_action === "accept" ? "This rule will now be applied to future context() calls." : input.rule_action === "reject" ? "This pattern will have reduced confidence for future suggestions." : "The modified rule will be applied to future context() calls."
|
|
20904
|
+
})
|
|
20905
|
+
}
|
|
20906
|
+
]
|
|
20907
|
+
};
|
|
20908
|
+
}
|
|
20909
|
+
case "suggested_rules_stats": {
|
|
20910
|
+
const result = await client.getSuggestedRulesStats({
|
|
20911
|
+
workspace_id: workspaceId
|
|
20912
|
+
});
|
|
20913
|
+
return {
|
|
20914
|
+
content: [
|
|
20915
|
+
{
|
|
20916
|
+
type: "text",
|
|
20917
|
+
text: formatContent({
|
|
20918
|
+
stats: result?.data || result,
|
|
20919
|
+
hint: "These stats show ML vs Grok accuracy. The blend weight auto-adjusts based on these metrics."
|
|
20920
|
+
})
|
|
20921
|
+
}
|
|
20922
|
+
]
|
|
20923
|
+
};
|
|
20924
|
+
}
|
|
20469
20925
|
default:
|
|
20470
20926
|
return errorResult(`Unknown action: ${input.action}`);
|
|
20471
20927
|
}
|
|
@@ -20475,7 +20931,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20475
20931
|
"memory",
|
|
20476
20932
|
{
|
|
20477
20933
|
title: "Memory",
|
|
20478
|
-
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
20934
|
+
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Transcript actions: list_transcripts (list saved conversations), get_transcript (get full transcript by ID), search_transcripts (semantic search across conversations), delete_transcript. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
20479
20935
|
inputSchema: external_exports.object({
|
|
20480
20936
|
action: external_exports.enum([
|
|
20481
20937
|
"create_event",
|
|
@@ -20523,6 +20979,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20523
20979
|
"update_doc",
|
|
20524
20980
|
"delete_doc",
|
|
20525
20981
|
"create_roadmap",
|
|
20982
|
+
// Transcript actions
|
|
20983
|
+
"list_transcripts",
|
|
20984
|
+
"get_transcript",
|
|
20985
|
+
"search_transcripts",
|
|
20986
|
+
"delete_transcript",
|
|
20526
20987
|
// Team actions
|
|
20527
20988
|
"team_tasks",
|
|
20528
20989
|
"team_todos",
|
|
@@ -20628,7 +21089,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20628
21089
|
})
|
|
20629
21090
|
).optional().describe("Milestones for create_roadmap action"),
|
|
20630
21091
|
// Personal items param
|
|
20631
|
-
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs.")
|
|
21092
|
+
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs."),
|
|
21093
|
+
// Transcript params
|
|
21094
|
+
transcript_id: external_exports.string().uuid().optional().describe("Transcript ID for get_transcript/delete_transcript"),
|
|
21095
|
+
session_id: external_exports.string().optional().describe("Session ID filter for list_transcripts"),
|
|
21096
|
+
client_name: external_exports.string().optional().describe("Client name filter for list_transcripts (e.g., 'claude', 'cursor')"),
|
|
21097
|
+
started_after: external_exports.string().optional().describe("ISO timestamp - filter transcripts started after this time"),
|
|
21098
|
+
started_before: external_exports.string().optional().describe("ISO timestamp - filter transcripts started before this time")
|
|
20632
21099
|
})
|
|
20633
21100
|
},
|
|
20634
21101
|
async (input) => {
|
|
@@ -20690,8 +21157,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20690
21157
|
project_id: projectId,
|
|
20691
21158
|
limit: input.limit
|
|
20692
21159
|
});
|
|
21160
|
+
const events = result?.data?.items || result?.items || result?.data || [];
|
|
21161
|
+
const resultWithHint = Array.isArray(events) && events.length === 0 ? { ...result, hint: getEmptyStateHint("list_events") } : result;
|
|
20693
21162
|
return {
|
|
20694
|
-
content: [{ type: "text", text: formatContent(
|
|
21163
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20695
21164
|
};
|
|
20696
21165
|
}
|
|
20697
21166
|
case "import_batch": {
|
|
@@ -20884,8 +21353,10 @@ ${formatContent(result)}`
|
|
|
20884
21353
|
tags: input.tags,
|
|
20885
21354
|
is_personal: input.is_personal
|
|
20886
21355
|
});
|
|
21356
|
+
const taskCreateHint = TASK_HINTS.created;
|
|
21357
|
+
const resultWithHint = { ...result, hint: taskCreateHint };
|
|
20887
21358
|
return {
|
|
20888
|
-
content: [{ type: "text", text: formatContent(
|
|
21359
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20889
21360
|
};
|
|
20890
21361
|
}
|
|
20891
21362
|
case "get_task": {
|
|
@@ -20917,8 +21388,17 @@ ${formatContent(result)}`
|
|
|
20917
21388
|
tags: input.tags,
|
|
20918
21389
|
blocked_reason: input.blocked_reason
|
|
20919
21390
|
});
|
|
21391
|
+
let taskUpdateHint = "Task updated.";
|
|
21392
|
+
if (input.task_status === "completed") {
|
|
21393
|
+
taskUpdateHint = TASK_HINTS.completed;
|
|
21394
|
+
} else if (input.task_status === "blocked") {
|
|
21395
|
+
taskUpdateHint = TASK_HINTS.blocked;
|
|
21396
|
+
} else if (input.task_status === "cancelled") {
|
|
21397
|
+
taskUpdateHint = TASK_HINTS.cancelled;
|
|
21398
|
+
}
|
|
21399
|
+
const resultWithHint = { ...result, hint: taskUpdateHint };
|
|
20920
21400
|
return {
|
|
20921
|
-
content: [{ type: "text", text: formatContent(
|
|
21401
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20922
21402
|
};
|
|
20923
21403
|
}
|
|
20924
21404
|
case "delete_task": {
|
|
@@ -20945,8 +21425,10 @@ ${formatContent(result)}`
|
|
|
20945
21425
|
limit: input.limit,
|
|
20946
21426
|
is_personal: input.is_personal
|
|
20947
21427
|
});
|
|
21428
|
+
const tasks = result?.data?.tasks || result?.tasks || result?.data?.items || result?.items || [];
|
|
21429
|
+
const resultWithHint = Array.isArray(tasks) && tasks.length === 0 ? { ...result, hint: getEmptyStateHint("list_tasks") } : result;
|
|
20948
21430
|
return {
|
|
20949
|
-
content: [{ type: "text", text: formatContent(
|
|
21431
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
20950
21432
|
};
|
|
20951
21433
|
}
|
|
20952
21434
|
case "reorder_tasks": {
|
|
@@ -20998,8 +21480,10 @@ ${formatContent(result)}`
|
|
|
20998
21480
|
priority: input.todo_priority,
|
|
20999
21481
|
is_personal: input.is_personal
|
|
21000
21482
|
});
|
|
21483
|
+
const todos = todosResult?.data?.todos || todosResult?.todos || todosResult?.data?.items || todosResult?.items || [];
|
|
21484
|
+
const todosWithHint = Array.isArray(todos) && todos.length === 0 ? { ...todosResult, hint: getEmptyStateHint("list_todos") } : todosResult;
|
|
21001
21485
|
return {
|
|
21002
|
-
content: [{ type: "text", text: formatContent(
|
|
21486
|
+
content: [{ type: "text", text: formatContent(todosWithHint) }]
|
|
21003
21487
|
};
|
|
21004
21488
|
}
|
|
21005
21489
|
case "get_todo": {
|
|
@@ -21075,8 +21559,10 @@ ${formatContent(result)}`
|
|
|
21075
21559
|
diagram_type: input.diagram_type,
|
|
21076
21560
|
is_personal: input.is_personal
|
|
21077
21561
|
});
|
|
21562
|
+
const diagrams = diagramsResult?.data?.diagrams || diagramsResult?.diagrams || diagramsResult?.data?.items || diagramsResult?.items || [];
|
|
21563
|
+
const diagramsWithHint = Array.isArray(diagrams) && diagrams.length === 0 ? { ...diagramsResult, hint: getEmptyStateHint("list_diagrams") } : diagramsResult;
|
|
21078
21564
|
return {
|
|
21079
|
-
content: [{ type: "text", text: formatContent(
|
|
21565
|
+
content: [{ type: "text", text: formatContent(diagramsWithHint) }]
|
|
21080
21566
|
};
|
|
21081
21567
|
}
|
|
21082
21568
|
case "get_diagram": {
|
|
@@ -21149,8 +21635,10 @@ ${formatContent(result)}`
|
|
|
21149
21635
|
doc_type: input.doc_type,
|
|
21150
21636
|
is_personal: input.is_personal
|
|
21151
21637
|
});
|
|
21638
|
+
const docs = docsResult?.data?.docs || docsResult?.docs || docsResult?.data?.items || docsResult?.items || [];
|
|
21639
|
+
const docsWithHint = Array.isArray(docs) && docs.length === 0 ? { ...docsResult, hint: getEmptyStateHint("list_docs") } : docsResult;
|
|
21152
21640
|
return {
|
|
21153
|
-
content: [{ type: "text", text: formatContent(
|
|
21641
|
+
content: [{ type: "text", text: formatContent(docsWithHint) }]
|
|
21154
21642
|
};
|
|
21155
21643
|
}
|
|
21156
21644
|
case "get_doc": {
|
|
@@ -21381,6 +21869,54 @@ ${formatContent(result)}`
|
|
|
21381
21869
|
]
|
|
21382
21870
|
};
|
|
21383
21871
|
}
|
|
21872
|
+
// Transcript actions
|
|
21873
|
+
case "list_transcripts": {
|
|
21874
|
+
const result = await client.listTranscripts({
|
|
21875
|
+
workspace_id: workspaceId,
|
|
21876
|
+
project_id: projectId,
|
|
21877
|
+
session_id: input.session_id,
|
|
21878
|
+
client_name: input.client_name,
|
|
21879
|
+
started_after: input.started_after,
|
|
21880
|
+
started_before: input.started_before,
|
|
21881
|
+
limit: input.limit
|
|
21882
|
+
});
|
|
21883
|
+
const transcripts = result?.data?.items || result?.items || result?.data || [];
|
|
21884
|
+
const resultWithHint = Array.isArray(transcripts) && transcripts.length === 0 ? { ...result, hint: "No transcripts found. Enable save_exchange in context() calls to save conversations." } : result;
|
|
21885
|
+
return {
|
|
21886
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
21887
|
+
};
|
|
21888
|
+
}
|
|
21889
|
+
case "get_transcript": {
|
|
21890
|
+
if (!input.transcript_id) {
|
|
21891
|
+
return errorResult("get_transcript requires: transcript_id");
|
|
21892
|
+
}
|
|
21893
|
+
const result = await client.getTranscript(input.transcript_id);
|
|
21894
|
+
return {
|
|
21895
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21896
|
+
};
|
|
21897
|
+
}
|
|
21898
|
+
case "search_transcripts": {
|
|
21899
|
+
if (!input.query) {
|
|
21900
|
+
return errorResult("search_transcripts requires: query");
|
|
21901
|
+
}
|
|
21902
|
+
const result = await client.searchTranscripts({
|
|
21903
|
+
workspace_id: workspaceId,
|
|
21904
|
+
query: input.query,
|
|
21905
|
+
limit: input.limit
|
|
21906
|
+
});
|
|
21907
|
+
return {
|
|
21908
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21909
|
+
};
|
|
21910
|
+
}
|
|
21911
|
+
case "delete_transcript": {
|
|
21912
|
+
if (!input.transcript_id) {
|
|
21913
|
+
return errorResult("delete_transcript requires: transcript_id");
|
|
21914
|
+
}
|
|
21915
|
+
const result = await client.deleteTranscript(input.transcript_id);
|
|
21916
|
+
return {
|
|
21917
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21918
|
+
};
|
|
21919
|
+
}
|
|
21384
21920
|
default:
|
|
21385
21921
|
return errorResult(`Unknown action: ${input.action}`);
|
|
21386
21922
|
}
|
|
@@ -22669,6 +23205,8 @@ Example workflow:
|
|
|
22669
23205
|
const projectId = resolveProjectId(input.project_id);
|
|
22670
23206
|
switch (input.action) {
|
|
22671
23207
|
case "index": {
|
|
23208
|
+
const indexGate = await gateIfProTool("media_index");
|
|
23209
|
+
if (indexGate) return indexGate;
|
|
22672
23210
|
if (!input.file_path && !input.external_url) {
|
|
22673
23211
|
return errorResult("index requires: file_path or external_url");
|
|
22674
23212
|
}
|
|
@@ -22833,6 +23371,8 @@ Created: ${content.created_at}`
|
|
|
22833
23371
|
}
|
|
22834
23372
|
}
|
|
22835
23373
|
case "search": {
|
|
23374
|
+
const searchGate = await gateIfProTool("media_search");
|
|
23375
|
+
if (searchGate) return searchGate;
|
|
22836
23376
|
if (!input.query) {
|
|
22837
23377
|
return errorResult("search requires: query");
|
|
22838
23378
|
}
|
|
@@ -23048,14 +23588,14 @@ Content ID: ${input.content_id}`
|
|
|
23048
23588
|
// For editor_rules
|
|
23049
23589
|
folder_path: external_exports.string().optional(),
|
|
23050
23590
|
editors: external_exports.array(external_exports.string()).optional(),
|
|
23051
|
-
mode: external_exports.enum(["minimal", "full"]).optional(),
|
|
23591
|
+
mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional(),
|
|
23052
23592
|
dry_run: external_exports.boolean().optional(),
|
|
23053
23593
|
workspace_id: external_exports.string().uuid().optional(),
|
|
23054
23594
|
workspace_name: external_exports.string().optional(),
|
|
23055
23595
|
project_name: external_exports.string().optional(),
|
|
23056
23596
|
additional_rules: external_exports.string().optional(),
|
|
23057
23597
|
install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks (PreToolUse, UserPromptSubmit, PostToolUse). Default: true for Claude users."),
|
|
23058
|
-
include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default:
|
|
23598
|
+
include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default: true."),
|
|
23059
23599
|
include_post_write: external_exports.boolean().optional().describe("Include PostToolUse hook for real-time file indexing after Edit/Write operations. Default: true."),
|
|
23060
23600
|
// For enable_bundle
|
|
23061
23601
|
bundle: external_exports.enum([
|
|
@@ -23112,7 +23652,7 @@ Each domain tool has an 'action' parameter for specific operations.` : "";
|
|
|
23112
23652
|
scope: input.folder_path ? "both" : "user",
|
|
23113
23653
|
projectPath: input.folder_path,
|
|
23114
23654
|
dryRun: input.dry_run,
|
|
23115
|
-
includePreCompact: input.include_pre_compact,
|
|
23655
|
+
includePreCompact: input.include_pre_compact !== false,
|
|
23116
23656
|
includePostWrite: input.include_post_write
|
|
23117
23657
|
});
|
|
23118
23658
|
} catch (err) {
|
|
@@ -24053,6 +24593,7 @@ function registerPrompts(server) {
|
|
|
24053
24593
|
}
|
|
24054
24594
|
|
|
24055
24595
|
// src/session-manager.ts
|
|
24596
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
24056
24597
|
var SessionManager = class _SessionManager {
|
|
24057
24598
|
constructor(server, client) {
|
|
24058
24599
|
this.server = server;
|
|
@@ -24084,12 +24625,20 @@ var SessionManager = class _SessionManager {
|
|
|
24084
24625
|
this.lastHighPressureAt = null;
|
|
24085
24626
|
this.lastHighPressureTokens = 0;
|
|
24086
24627
|
this.postCompactRestoreCompleted = false;
|
|
24628
|
+
this.sessionId = `mcp-${randomUUID2()}`;
|
|
24087
24629
|
}
|
|
24088
24630
|
static {
|
|
24089
24631
|
// Each conversation turn typically includes: user message (~500), AI response (~1500),
|
|
24090
24632
|
// system prompt overhead (~500), and reasoning (~1500). Conservative estimate: 3000/turn
|
|
24091
24633
|
this.TOKENS_PER_TURN_ESTIMATE = 3e3;
|
|
24092
24634
|
}
|
|
24635
|
+
/**
|
|
24636
|
+
* Get the unique session ID for this MCP connection.
|
|
24637
|
+
* Used for transcript saving and session association.
|
|
24638
|
+
*/
|
|
24639
|
+
getSessionId() {
|
|
24640
|
+
return this.sessionId;
|
|
24641
|
+
}
|
|
24093
24642
|
/**
|
|
24094
24643
|
* Check if session has been auto-initialized
|
|
24095
24644
|
*/
|
|
@@ -24624,7 +25173,7 @@ var SessionManager = class _SessionManager {
|
|
|
24624
25173
|
|
|
24625
25174
|
// src/http-gateway.ts
|
|
24626
25175
|
import { createServer } from "node:http";
|
|
24627
|
-
import { randomUUID as
|
|
25176
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24628
25177
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24629
25178
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
24630
25179
|
var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
|
|
@@ -24801,7 +25350,7 @@ async function createSession() {
|
|
|
24801
25350
|
registerPrompts(server);
|
|
24802
25351
|
}
|
|
24803
25352
|
const transport = new StreamableHTTPServerTransport({
|
|
24804
|
-
sessionIdGenerator: () =>
|
|
25353
|
+
sessionIdGenerator: () => randomUUID3(),
|
|
24805
25354
|
enableJsonResponse: ENABLE_JSON_RESPONSE,
|
|
24806
25355
|
onsessionclosed: (sessionId) => {
|
|
24807
25356
|
sessions.delete(sessionId);
|
|
@@ -25845,7 +26394,7 @@ Code: ${device.user_code}`);
|
|
|
25845
26394
|
}
|
|
25846
26395
|
}
|
|
25847
26396
|
}
|
|
25848
|
-
const mode = "
|
|
26397
|
+
const mode = "full";
|
|
25849
26398
|
const detectedPlanName = await client.getPlanName();
|
|
25850
26399
|
const detectedGraphTier = await client.getGraphTier();
|
|
25851
26400
|
const graphTierLabel = detectedGraphTier === "full" ? "full graph" : detectedGraphTier === "lite" ? "graph-lite" : "none";
|