@contextstream/mcp-server 0.4.58 → 0.4.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/auto-rules.js +7 -7
- package/dist/hooks/pre-tool-use.js +4 -4
- package/dist/hooks/runner.js +84 -77
- package/dist/hooks/session-init.js +21 -20
- package/dist/hooks/user-prompt-submit.js +72 -65
- package/dist/index.js +276 -228
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -958,7 +958,7 @@ var init_rules_templates = __esm({
|
|
|
958
958
|
"generate_rules",
|
|
959
959
|
// Consolidated domain tools (v0.4.x default)
|
|
960
960
|
"search",
|
|
961
|
-
// Modes: semantic, hybrid, keyword, pattern
|
|
961
|
+
// Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
|
|
962
962
|
"session",
|
|
963
963
|
// Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
|
|
964
964
|
"memory",
|
|
@@ -982,7 +982,7 @@ var init_rules_templates = __esm({
|
|
|
982
982
|
|---------|----------|
|
|
983
983
|
| **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
984
984
|
| **Every message** | \`context(user_message="...")\` FIRST |
|
|
985
|
-
| **Before file search** | \`search(mode="
|
|
985
|
+
| **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read |
|
|
986
986
|
</contextstream_rules>
|
|
987
987
|
|
|
988
988
|
**Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
|
|
@@ -1005,7 +1005,7 @@ v${RULES_VERSION}
|
|
|
1005
1005
|
| 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
1006
1006
|
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
1007
1007
|
|
|
1008
|
-
**BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="
|
|
1008
|
+
**BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="auto")\` FIRST
|
|
1009
1009
|
</contextstream_rules>
|
|
1010
1010
|
|
|
1011
1011
|
**This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
|
|
@@ -1057,7 +1057,7 @@ v${RULES_VERSION}
|
|
|
1057
1057
|
- Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
|
|
1058
1058
|
|
|
1059
1059
|
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
|
|
1060
|
-
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="
|
|
1060
|
+
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
|
|
1061
1061
|
- **You MUST use the suggested ContextStream tool instead**
|
|
1062
1062
|
- Local tools are only allowed if project is not indexed or ContextStream returns 0 results
|
|
1063
1063
|
|
|
@@ -1081,7 +1081,7 @@ Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
|
|
|
1081
1081
|
|
|
1082
1082
|
**BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
|
|
1083
1083
|
\`\`\`
|
|
1084
|
-
STOP \u2192 Call search(mode="
|
|
1084
|
+
STOP \u2192 Call search(mode="auto", query="...") FIRST
|
|
1085
1085
|
\`\`\`
|
|
1086
1086
|
|
|
1087
1087
|
**Note:** PreToolUse hooks block these tools when ContextStream is available.
|
|
@@ -1090,11 +1090,11 @@ STOP \u2192 Call search(mode="hybrid", query="...") FIRST
|
|
|
1090
1090
|
\u274C **NEVER DO THIS:**
|
|
1091
1091
|
- \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
|
|
1092
1092
|
- \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
|
|
1093
|
-
- \`Read(file)\` for discovery \u2192 Use \`search(mode="
|
|
1094
|
-
- \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="
|
|
1093
|
+
- \`Read(file)\` for discovery \u2192 Use \`search(mode="auto", query="...")\` instead
|
|
1094
|
+
- \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="auto")\` instead
|
|
1095
1095
|
|
|
1096
1096
|
\u2705 **ALWAYS DO THIS:**
|
|
1097
|
-
1. \`search(mode="
|
|
1097
|
+
1. \`search(mode="auto", query="what you're looking for")\`
|
|
1098
1098
|
2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
|
|
1099
1099
|
3. Use Read ONLY for exact file edits after you know the file path
|
|
1100
1100
|
|
|
@@ -1165,7 +1165,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
1165
1165
|
|---------|----------|
|
|
1166
1166
|
| **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
|
|
1167
1167
|
| **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
|
|
1168
|
-
| **Before file search** | \`search(mode="
|
|
1168
|
+
| **Before file search** | \`search(mode="auto")\` FIRST |
|
|
1169
1169
|
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
1170
1170
|
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
1171
1171
|
|
|
@@ -1185,7 +1185,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
1185
1185
|
|------|--------------|
|
|
1186
1186
|
| **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
|
|
1187
1187
|
| **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
|
|
1188
|
-
| **Code search** | \`search(mode="
|
|
1188
|
+
| **Code search** | \`search(mode="auto", query="...")\` \u2014 BEFORE Glob/Grep/Read |
|
|
1189
1189
|
| **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
|
|
1190
1190
|
| **User correction** | \`session(action="capture_lesson", ...)\` |
|
|
1191
1191
|
| **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
|
|
@@ -1212,7 +1212,7 @@ v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode
|
|
|
1212
1212
|
|
|
1213
1213
|
| Domain | Actions/Modes | Example |
|
|
1214
1214
|
|--------|---------------|---------|
|
|
1215
|
-
| **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="
|
|
1215
|
+
| **\`search\`** | mode: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern | \`search(mode="auto", query="auth implementation", limit=3)\` |
|
|
1216
1216
|
| **\`session\`** | action: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace | \`session(action="capture", event_type="decision", title="Use JWT", content="...")\` |
|
|
1217
1217
|
| **\`memory\`** | action: create_event, get_event, update_event, delete_event, list_events, distill_event, create_node, get_node, update_node, delete_node, list_nodes, supersede_node, search, decisions, timeline, summary | \`memory(action="list_events", limit=10)\` |
|
|
1218
1218
|
| **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
|
|
@@ -1358,7 +1358,7 @@ session(action="capture", event_type="session_snapshot", title="Pre-compaction s
|
|
|
1358
1358
|
|
|
1359
1359
|
### Search & Code Intelligence (ContextStream-first)
|
|
1360
1360
|
|
|
1361
|
-
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="
|
|
1361
|
+
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
|
|
1362
1362
|
|
|
1363
1363
|
**\u274C WRONG workflow (wastes tokens, slow):**
|
|
1364
1364
|
\`\`\`
|
|
@@ -1367,14 +1367,14 @@ Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \
|
|
|
1367
1367
|
|
|
1368
1368
|
**\u2705 CORRECT workflow (fast, complete):**
|
|
1369
1369
|
\`\`\`
|
|
1370
|
-
search(mode="
|
|
1370
|
+
search(mode="auto", query="function implementation") \u2192 done (results include context)
|
|
1371
1371
|
\`\`\`
|
|
1372
1372
|
|
|
1373
1373
|
**Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
|
|
1374
1374
|
|
|
1375
1375
|
**Search order:**
|
|
1376
1376
|
1. \`session(action="smart_search", query="...")\` - context-enriched
|
|
1377
|
-
2. \`search(mode="
|
|
1377
|
+
2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
|
|
1378
1378
|
3. \`project(action="files")\` - file tree/list (only when needed)
|
|
1379
1379
|
4. \`graph(action="dependencies", ...)\` - code structure
|
|
1380
1380
|
5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
|
|
@@ -1383,7 +1383,7 @@ search(mode="hybrid", query="function implementation") \u2192 done (results incl
|
|
|
1383
1383
|
|
|
1384
1384
|
| Need | Mode | Example |
|
|
1385
1385
|
|------|------|---------|
|
|
1386
|
-
| Find code by meaning | \`
|
|
1386
|
+
| Find code by meaning | \`auto\` | "authentication logic", "error handling" |
|
|
1387
1387
|
| Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
|
|
1388
1388
|
| File patterns | \`pattern\` | "*.sql", "test_*.py" |
|
|
1389
1389
|
| ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
|
|
@@ -1534,7 +1534,7 @@ See full documentation: https://contextstream.io/docs/mcp/tools
|
|
|
1534
1534
|
2. \`context(user_message="<msg>")\` (EVERY subsequent message)
|
|
1535
1535
|
|
|
1536
1536
|
**BEFORE Glob/Grep/Read/Search/Explore:**
|
|
1537
|
-
\u2192 \`search(mode="
|
|
1537
|
+
\u2192 \`search(mode="auto", query="...")\` FIRST \u2014 local tools ONLY if 0 results
|
|
1538
1538
|
|
|
1539
1539
|
**HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
|
|
1540
1540
|
</contextstream_protocol>
|
|
@@ -1547,7 +1547,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
1547
1547
|
|
|
1548
1548
|
| \u274C WRONG | \u2705 CORRECT |
|
|
1549
1549
|
|----------|-----------|
|
|
1550
|
-
| \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="
|
|
1550
|
+
| \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="auto")\` (1 call) |
|
|
1551
1551
|
| Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
|
|
1552
1552
|
| Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
|
|
1553
1553
|
|
|
@@ -1563,7 +1563,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
1563
1563
|
|------|------|
|
|
1564
1564
|
| 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
|
|
1565
1565
|
| Every message after | \`context(user_message="...")\` |
|
|
1566
|
-
| Before ANY file discovery | \`search(mode="
|
|
1566
|
+
| Before ANY file discovery | \`search(mode="auto", query="...")\` |
|
|
1567
1567
|
| On \`<system-reminder>\` | **Follow instructions inside** |
|
|
1568
1568
|
| Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
|
|
1569
1569
|
| Check past mistakes | \`session(action="get_lessons", query="...")\` |
|
|
@@ -1572,7 +1572,8 @@ Rules Version: ${RULES_VERSION}
|
|
|
1572
1572
|
|
|
1573
1573
|
| Mode | When |
|
|
1574
1574
|
|------|------|
|
|
1575
|
-
| \`
|
|
1575
|
+
| \`auto\` | Default \u2014 semantic + keyword |
|
|
1576
|
+
| \`hybrid\` | Legacy alias for auto |
|
|
1576
1577
|
| \`keyword\` | Exact symbol match |
|
|
1577
1578
|
| \`exhaustive\` | Find ALL occurrences |
|
|
1578
1579
|
| \`semantic\` | Conceptual questions |
|
|
@@ -1752,7 +1753,7 @@ This tells you:
|
|
|
1752
1753
|
|
|
1753
1754
|
**IF project is indexed and fresh:**
|
|
1754
1755
|
\`\`\`
|
|
1755
|
-
search(mode="
|
|
1756
|
+
search(mode="auto", query="what you're looking for")
|
|
1756
1757
|
\`\`\`
|
|
1757
1758
|
|
|
1758
1759
|
**IF project is NOT indexed or very stale (>7 days):**
|
|
@@ -2829,7 +2830,7 @@ def main():
|
|
|
2829
2830
|
if tool == "Glob":
|
|
2830
2831
|
pattern = inp.get("pattern", "")
|
|
2831
2832
|
if is_discovery_glob(pattern):
|
|
2832
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
2833
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
|
|
2833
2834
|
sys.exit(2)
|
|
2834
2835
|
|
|
2835
2836
|
elif tool == "Grep" or tool == "Search":
|
|
@@ -2841,12 +2842,12 @@ def main():
|
|
|
2841
2842
|
# Specific file - suggest Read instead
|
|
2842
2843
|
print(f"STOP: Use Read(\\"{path}\\") to view file content, or mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") for codebase search.", file=sys.stderr)
|
|
2843
2844
|
else:
|
|
2844
|
-
print(f"STOP: Use mcp__contextstream__search(mode=\\"
|
|
2845
|
+
print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
|
|
2845
2846
|
sys.exit(2)
|
|
2846
2847
|
|
|
2847
2848
|
elif tool == "Task":
|
|
2848
2849
|
if inp.get("subagent_type", "").lower() == "explore":
|
|
2849
|
-
print("STOP: Use mcp__contextstream__search(mode=\\"
|
|
2850
|
+
print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
|
|
2850
2851
|
sys.exit(2)
|
|
2851
2852
|
if inp.get("subagent_type", "").lower() == "plan":
|
|
2852
2853
|
print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
|
|
@@ -2873,7 +2874,7 @@ import os
|
|
|
2873
2874
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
2874
2875
|
|
|
2875
2876
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
2876
|
-
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="
|
|
2877
|
+
1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="auto") FIRST
|
|
2877
2878
|
2. Call context_smart at start of EVERY response
|
|
2878
2879
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
2879
2880
|
[END RULES]"""
|
|
@@ -3309,7 +3310,7 @@ def main():
|
|
|
3309
3310
|
pattern = params.get("path", "") or params.get("regex", "")
|
|
3310
3311
|
if is_discovery_glob(pattern) or is_discovery_grep(pattern):
|
|
3311
3312
|
output_block(
|
|
3312
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3313
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3313
3314
|
"ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
|
|
3314
3315
|
"[CONTEXTSTREAM] Use ContextStream search for code discovery."
|
|
3315
3316
|
)
|
|
@@ -3336,7 +3337,7 @@ import os
|
|
|
3336
3337
|
ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
|
|
3337
3338
|
|
|
3338
3339
|
REMINDER = """[CONTEXTSTREAM RULES]
|
|
3339
|
-
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="
|
|
3340
|
+
1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="auto") FIRST
|
|
3340
3341
|
2. Call context_smart at start of EVERY response
|
|
3341
3342
|
3. Local tools ONLY if ContextStream returns 0 results
|
|
3342
3343
|
[END RULES]"""
|
|
@@ -3487,7 +3488,7 @@ def main():
|
|
|
3487
3488
|
pattern = params.get("pattern", "") or params.get("path", "")
|
|
3488
3489
|
if is_discovery_glob(pattern):
|
|
3489
3490
|
output_deny(
|
|
3490
|
-
f"Use mcp__contextstream__search(mode=\\"
|
|
3491
|
+
f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
|
|
3491
3492
|
"ContextStream search is indexed and faster."
|
|
3492
3493
|
)
|
|
3493
3494
|
|
|
@@ -4059,7 +4060,7 @@ async function runPreToolUseHook() {
|
|
|
4059
4060
|
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
4060
4061
|
`);
|
|
4061
4062
|
if (isDiscoveryGlob(pattern)) {
|
|
4062
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
4063
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
|
|
4063
4064
|
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
4064
4065
|
`);
|
|
4065
4066
|
if (editorFormat === "cline") {
|
|
@@ -4082,7 +4083,7 @@ async function runPreToolUseHook() {
|
|
|
4082
4083
|
}
|
|
4083
4084
|
blockClaudeCode(msg);
|
|
4084
4085
|
} else {
|
|
4085
|
-
const msg = `STOP: Use mcp__contextstream__search(mode="
|
|
4086
|
+
const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
|
|
4086
4087
|
if (editorFormat === "cline") {
|
|
4087
4088
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
4088
4089
|
} else if (editorFormat === "cursor") {
|
|
@@ -4094,7 +4095,7 @@ async function runPreToolUseHook() {
|
|
|
4094
4095
|
} else if (tool === "Task") {
|
|
4095
4096
|
const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
|
|
4096
4097
|
if (subagentType === "explore") {
|
|
4097
|
-
const msg = 'STOP: Use mcp__contextstream__search(mode="
|
|
4098
|
+
const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
|
|
4098
4099
|
if (editorFormat === "cline") {
|
|
4099
4100
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
4100
4101
|
} else if (editorFormat === "cursor") {
|
|
@@ -4123,7 +4124,7 @@ async function runPreToolUseHook() {
|
|
|
4123
4124
|
if (tool === "list_files" || tool === "search_files") {
|
|
4124
4125
|
const pattern = toolInput?.path || toolInput?.regex || "";
|
|
4125
4126
|
if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
|
|
4126
|
-
const msg = `Use mcp__contextstream__search(mode="
|
|
4127
|
+
const msg = `Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
|
|
4127
4128
|
if (editorFormat === "cline") {
|
|
4128
4129
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
4129
4130
|
} else if (editorFormat === "cursor") {
|
|
@@ -4381,6 +4382,34 @@ async function saveLastExchange(exchange, cwd, clientName) {
|
|
|
4381
4382
|
} catch {
|
|
4382
4383
|
}
|
|
4383
4384
|
}
|
|
4385
|
+
async function fetchHookContext() {
|
|
4386
|
+
if (!API_KEY2) return null;
|
|
4387
|
+
try {
|
|
4388
|
+
const controller = new AbortController();
|
|
4389
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e3);
|
|
4390
|
+
const url = `${API_URL2}/api/v1/context/hook`;
|
|
4391
|
+
const body = {};
|
|
4392
|
+
if (WORKSPACE_ID) body.workspace_id = WORKSPACE_ID;
|
|
4393
|
+
if (PROJECT_ID) body.project_id = PROJECT_ID;
|
|
4394
|
+
const response = await fetch(url, {
|
|
4395
|
+
method: "POST",
|
|
4396
|
+
headers: {
|
|
4397
|
+
"X-API-Key": API_KEY2,
|
|
4398
|
+
"Content-Type": "application/json"
|
|
4399
|
+
},
|
|
4400
|
+
body: JSON.stringify(body),
|
|
4401
|
+
signal: controller.signal
|
|
4402
|
+
});
|
|
4403
|
+
clearTimeout(timeoutId);
|
|
4404
|
+
if (response.ok) {
|
|
4405
|
+
const data = await response.json();
|
|
4406
|
+
return data?.data?.context || null;
|
|
4407
|
+
}
|
|
4408
|
+
return null;
|
|
4409
|
+
} catch {
|
|
4410
|
+
return null;
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4384
4413
|
async function fetchSessionContext() {
|
|
4385
4414
|
if (!API_KEY2) return null;
|
|
4386
4415
|
try {
|
|
@@ -4459,26 +4488,6 @@ function transformSmartContextResponse(data) {
|
|
|
4459
4488
|
return null;
|
|
4460
4489
|
}
|
|
4461
4490
|
}
|
|
4462
|
-
function buildClaudeReminder(ctx, versionNotice) {
|
|
4463
|
-
const parts = [];
|
|
4464
|
-
if (versionNotice?.behind) {
|
|
4465
|
-
const versionInfo = getVersionNoticeForHook(versionNotice);
|
|
4466
|
-
if (versionInfo) {
|
|
4467
|
-
parts.push(versionInfo);
|
|
4468
|
-
parts.push("");
|
|
4469
|
-
}
|
|
4470
|
-
}
|
|
4471
|
-
const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
|
|
4472
|
-
if (highImportancePrefs.length > 0) {
|
|
4473
|
-
parts.push(`[USER PREFERENCES - Always respect these]`);
|
|
4474
|
-
for (const pref of highImportancePrefs.slice(0, 5)) {
|
|
4475
|
-
parts.push(`\u2022 ${pref.title}: ${pref.content}`);
|
|
4476
|
-
}
|
|
4477
|
-
parts.push("");
|
|
4478
|
-
}
|
|
4479
|
-
parts.push(REMINDER);
|
|
4480
|
-
return parts.join("\n");
|
|
4481
|
-
}
|
|
4482
4491
|
function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
4483
4492
|
const parts = [ENHANCED_REMINDER_HEADER];
|
|
4484
4493
|
if (versionNotice?.behind) {
|
|
@@ -4496,7 +4505,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
4496
4505
|
2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
|
|
4497
4506
|
3. Generate a unique session_id (e.g., "session-" + timestamp or UUID) - use this for ALL context() calls
|
|
4498
4507
|
4. Call \`context(user_message="...", save_exchange=true, session_id="<your-session-id>")\` for task-specific context
|
|
4499
|
-
5. Use \`search(mode="
|
|
4508
|
+
5. Use \`search(mode="auto")\` for code discovery (not Glob/Grep/Read)
|
|
4500
4509
|
|
|
4501
4510
|
`);
|
|
4502
4511
|
}
|
|
@@ -4537,7 +4546,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
|
|
|
4537
4546
|
parts.push("");
|
|
4538
4547
|
}
|
|
4539
4548
|
parts.push("---\n");
|
|
4540
|
-
parts.push(
|
|
4549
|
+
parts.push(FULL_REMINDER);
|
|
4541
4550
|
parts.push(`
|
|
4542
4551
|
|
|
4543
4552
|
---
|
|
@@ -4554,7 +4563,7 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
|
|
|
4554
4563
|
### \u{1F50D} Search Decision Tree:
|
|
4555
4564
|
|
|
4556
4565
|
**IF indexed=true AND last_indexed_at is recent:**
|
|
4557
|
-
\u2192 Use \`search(mode="
|
|
4566
|
+
\u2192 Use \`search(mode="auto", query="...")\`
|
|
4558
4567
|
|
|
4559
4568
|
**IF indexed=false OR last_indexed_at is stale (>7 days):**
|
|
4560
4569
|
\u2192 Use local tools (Glob/Grep/Read) directly
|
|
@@ -4625,61 +4634,58 @@ async function runUserPromptSubmitHook() {
|
|
|
4625
4634
|
}
|
|
4626
4635
|
const editorFormat = detectEditorFormat2(input);
|
|
4627
4636
|
const cwd = input.cwd || process.cwd();
|
|
4628
|
-
loadConfigFromMcpJson(cwd);
|
|
4629
|
-
const versionNoticePromise = getUpdateNotice();
|
|
4630
|
-
const lastExchange = extractLastExchange(input, editorFormat);
|
|
4631
|
-
const clientName = editorFormat === "claude" ? "claude-code" : editorFormat;
|
|
4632
|
-
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
4633
4637
|
if (editorFormat === "claude") {
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
+
loadConfigFromMcpJson(cwd);
|
|
4639
|
+
let context = REMINDER;
|
|
4640
|
+
if (API_KEY2) {
|
|
4641
|
+
try {
|
|
4642
|
+
const hookContext = await fetchHookContext();
|
|
4643
|
+
if (hookContext) {
|
|
4644
|
+
context = hookContext;
|
|
4645
|
+
}
|
|
4646
|
+
} catch {
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4638
4649
|
console.log(
|
|
4639
4650
|
JSON.stringify({
|
|
4640
4651
|
hookSpecificOutput: {
|
|
4641
4652
|
hookEventName: "UserPromptSubmit",
|
|
4642
|
-
additionalContext:
|
|
4653
|
+
additionalContext: context
|
|
4643
4654
|
}
|
|
4644
4655
|
})
|
|
4645
4656
|
);
|
|
4646
|
-
} else
|
|
4647
|
-
|
|
4648
|
-
const
|
|
4649
|
-
const
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
cancel: false,
|
|
4653
|
-
contextModification: enhancedReminder
|
|
4654
|
-
})
|
|
4655
|
-
);
|
|
4656
|
-
} else if (editorFormat === "cursor") {
|
|
4657
|
+
} else {
|
|
4658
|
+
loadConfigFromMcpJson(cwd);
|
|
4659
|
+
const versionNoticePromise = getUpdateNotice();
|
|
4660
|
+
const lastExchange = extractLastExchange(input, editorFormat);
|
|
4661
|
+
const clientName = editorFormat;
|
|
4662
|
+
const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
|
|
4657
4663
|
const newSession = isNewSession(input, editorFormat);
|
|
4658
4664
|
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4665
|
+
if (editorFormat === "cursor") {
|
|
4666
|
+
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").`;
|
|
4667
|
+
if (versionNotice?.behind) {
|
|
4668
|
+
cursorReminder += ` [UPDATE v${versionNotice.current}\u2192${versionNotice.latest}]`;
|
|
4669
|
+
}
|
|
4670
|
+
console.log(
|
|
4671
|
+
JSON.stringify({
|
|
4672
|
+
continue: true,
|
|
4673
|
+
user_message: cursorReminder
|
|
4674
|
+
})
|
|
4675
|
+
);
|
|
4676
|
+
} else {
|
|
4677
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
4678
|
+
console.log(
|
|
4679
|
+
JSON.stringify({
|
|
4680
|
+
cancel: false,
|
|
4681
|
+
contextModification: enhancedReminder
|
|
4682
|
+
})
|
|
4683
|
+
);
|
|
4662
4684
|
}
|
|
4663
|
-
console.log(
|
|
4664
|
-
JSON.stringify({
|
|
4665
|
-
continue: true,
|
|
4666
|
-
user_message: cursorReminder
|
|
4667
|
-
})
|
|
4668
|
-
);
|
|
4669
|
-
} else if (editorFormat === "antigravity") {
|
|
4670
|
-
const newSession = isNewSession(input, editorFormat);
|
|
4671
|
-
const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
|
|
4672
|
-
const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
|
|
4673
|
-
console.log(
|
|
4674
|
-
JSON.stringify({
|
|
4675
|
-
cancel: false,
|
|
4676
|
-
contextModification: enhancedReminder
|
|
4677
|
-
})
|
|
4678
|
-
);
|
|
4679
4685
|
}
|
|
4680
4686
|
process.exit(0);
|
|
4681
4687
|
}
|
|
4682
|
-
var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun3;
|
|
4688
|
+
var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, FULL_REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun3;
|
|
4683
4689
|
var init_user_prompt_submit = __esm({
|
|
4684
4690
|
"src/hooks/user-prompt-submit.ts"() {
|
|
4685
4691
|
"use strict";
|
|
@@ -4689,7 +4695,9 @@ var init_user_prompt_submit = __esm({
|
|
|
4689
4695
|
API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4690
4696
|
WORKSPACE_ID = null;
|
|
4691
4697
|
PROJECT_ID = null;
|
|
4692
|
-
REMINDER = `[CONTEXTSTREAM
|
|
4698
|
+
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.
|
|
4699
|
+
[END]`;
|
|
4700
|
+
FULL_REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
|
|
4693
4701
|
|
|
4694
4702
|
1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
|
|
4695
4703
|
- Returns: dynamic rules, lessons from past mistakes, relevant context
|
|
@@ -4699,7 +4707,7 @@ var init_user_prompt_submit = __esm({
|
|
|
4699
4707
|
|
|
4700
4708
|
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
4701
4709
|
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
4702
|
-
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="
|
|
4710
|
+
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...")
|
|
4703
4711
|
\u2705 IF NOT indexed OR stale: Use local tools (Glob/Grep/Read) directly
|
|
4704
4712
|
\u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
|
|
4705
4713
|
|
|
@@ -11311,7 +11319,16 @@ async function request(config, path22, options = {}) {
|
|
|
11311
11319
|
const authOverride = getAuthOverride();
|
|
11312
11320
|
const apiKey = authOverride?.apiKey ?? config.apiKey;
|
|
11313
11321
|
const jwt = authOverride?.jwt ?? config.jwt;
|
|
11314
|
-
const
|
|
11322
|
+
const rawPath = path22.startsWith("/") ? path22 : `/${path22}`;
|
|
11323
|
+
const apiPath = rawPath.startsWith("/api/") ? rawPath : `/api/v1${rawPath}`;
|
|
11324
|
+
const unauthenticatedEndpoints = ["/api/v1/auth/device/start", "/api/v1/auth/device/token"];
|
|
11325
|
+
const isUnauthenticatedEndpoint = unauthenticatedEndpoints.some(
|
|
11326
|
+
(endpoint) => apiPath === endpoint || apiPath.startsWith(`${endpoint}?`)
|
|
11327
|
+
);
|
|
11328
|
+
if (!isUnauthenticatedEndpoint && !apiKey && !jwt) {
|
|
11329
|
+
const message = config.allowHeaderAuth ? "Missing authentication: CONTEXTSTREAM_ALLOW_HEADER_AUTH is enabled, but this request did not include auth credentials." : "Missing authentication: set CONTEXTSTREAM_API_KEY or CONTEXTSTREAM_JWT.";
|
|
11330
|
+
throw new HttpError(401, message);
|
|
11331
|
+
}
|
|
11315
11332
|
const url = `${apiUrl.replace(/\/$/, "")}${apiPath}`;
|
|
11316
11333
|
const maxRetries = options.retries ?? MAX_RETRIES;
|
|
11317
11334
|
const baseDelay = options.retryDelay ?? BASE_DELAY;
|
|
@@ -12294,6 +12311,12 @@ var ContextStreamClient = class {
|
|
|
12294
12311
|
this.indexRefreshInProgress = false;
|
|
12295
12312
|
this.hasTriggeredAutoIndex = false;
|
|
12296
12313
|
}
|
|
12314
|
+
hasEffectiveAuth() {
|
|
12315
|
+
const authOverride = getAuthOverride();
|
|
12316
|
+
const apiKey = authOverride?.apiKey ?? this.config.apiKey;
|
|
12317
|
+
const jwt = authOverride?.jwt ?? this.config.jwt;
|
|
12318
|
+
return Boolean(apiKey || jwt);
|
|
12319
|
+
}
|
|
12297
12320
|
/**
|
|
12298
12321
|
* Update the client's default workspace/project IDs at runtime.
|
|
12299
12322
|
*
|
|
@@ -14731,7 +14754,7 @@ ${context2}`;
|
|
|
14731
14754
|
let candidateContext;
|
|
14732
14755
|
const CS_HEADER = "\u2B21 ContextStream \u2014 Smart Context & Memory";
|
|
14733
14756
|
const CS_HEADER_MINIFIED = "CS:active";
|
|
14734
|
-
const CS_FIRST_RULE = "\u26A0\uFE0F STOP: Before Glob/Grep/Read \u2192 search(mode=
|
|
14757
|
+
const CS_FIRST_RULE = "\u26A0\uFE0F STOP: Before Glob/Grep/Read \u2192 search(mode=auto) FIRST. Local tools ONLY if 0 results.";
|
|
14735
14758
|
const CS_FIRST_RULE_MINIFIED = "R:CS-first|Idx:project.index_status->ingest|NoLocalScanUnlessCSempty";
|
|
14736
14759
|
if (format === "minified") {
|
|
14737
14760
|
const parts = [CS_HEADER_MINIFIED, CS_FIRST_RULE_MINIFIED];
|
|
@@ -16537,10 +16560,22 @@ var TASK_HINTS = {
|
|
|
16537
16560
|
created: "Task created. Use update_task() to change status.",
|
|
16538
16561
|
completed: "Task completed. Well done!",
|
|
16539
16562
|
blocked: "Task blocked. Add blocked_reason for context.",
|
|
16563
|
+
cancelled: "Task cancelled.",
|
|
16540
16564
|
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
16541
16565
|
};
|
|
16542
16566
|
|
|
16543
16567
|
// src/tools.ts
|
|
16568
|
+
function parseBoolEnvDefault(raw, fallback) {
|
|
16569
|
+
if (raw === void 0) return fallback;
|
|
16570
|
+
const normalized = raw.trim().toLowerCase();
|
|
16571
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
|
|
16572
|
+
return true;
|
|
16573
|
+
}
|
|
16574
|
+
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
|
|
16575
|
+
return false;
|
|
16576
|
+
}
|
|
16577
|
+
return fallback;
|
|
16578
|
+
}
|
|
16544
16579
|
var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
|
|
16545
16580
|
var LOG_QUIET = LOG_LEVEL === "quiet";
|
|
16546
16581
|
var LOG_VERBOSE = LOG_LEVEL === "verbose";
|
|
@@ -16604,7 +16639,7 @@ var lastAutoSaveTime = 0;
|
|
|
16604
16639
|
var lastAutoSavePressureLevel = "";
|
|
16605
16640
|
var contextCallsSinceLastSave = 0;
|
|
16606
16641
|
var SEARCH_RULES_REMINDER_ENABLED = process.env.CONTEXTSTREAM_SEARCH_REMINDER?.toLowerCase() !== "false";
|
|
16607
|
-
var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="
|
|
16642
|
+
var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="auto") before Glob/Grep/Read. Local tools only if 0 results.`;
|
|
16608
16643
|
var CONTEXT_CALL_REMINDER = `[CONTEXT] Call context(user_message="...") at start of EVERY response. This is MANDATORY.`;
|
|
16609
16644
|
var LESSONS_REMINDER_PREFIX = `
|
|
16610
16645
|
\u{1F6A8} [LESSONS_WARNING] Past Mistakes Found - READ BEFORE PROCEEDING!
|
|
@@ -17834,7 +17869,7 @@ function getBundleInfo() {
|
|
|
17834
17869
|
core: "Essential session tools (always enabled)",
|
|
17835
17870
|
session: "Extended session management and utilities",
|
|
17836
17871
|
memory: "Full memory CRUD operations",
|
|
17837
|
-
search: "
|
|
17872
|
+
search: "Auto, semantic, hybrid (legacy alias), and keyword search",
|
|
17838
17873
|
graph: "Code graph analysis and dependencies",
|
|
17839
17874
|
workspace: "Workspace management",
|
|
17840
17875
|
project: "Project management and indexing",
|
|
@@ -17937,11 +17972,22 @@ function parsePositiveInt(raw, fallback) {
|
|
|
17937
17972
|
var OUTPUT_FORMAT = process.env.CONTEXTSTREAM_OUTPUT_FORMAT || "compact";
|
|
17938
17973
|
var COMPACT_OUTPUT = OUTPUT_FORMAT === "compact";
|
|
17939
17974
|
var SHOW_TIMING = process.env.CONTEXTSTREAM_SHOW_TIMING === "true" || process.env.CONTEXTSTREAM_SHOW_TIMING === "1";
|
|
17975
|
+
var INCLUDE_STRUCTURED_CONTENT = parseBoolEnvDefault(
|
|
17976
|
+
process.env.CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT,
|
|
17977
|
+
true
|
|
17978
|
+
);
|
|
17940
17979
|
var DEFAULT_SEARCH_LIMIT = parsePositiveInt(process.env.CONTEXTSTREAM_SEARCH_LIMIT, 3);
|
|
17941
17980
|
var DEFAULT_SEARCH_CONTENT_MAX_CHARS = parsePositiveInt(
|
|
17942
17981
|
process.env.CONTEXTSTREAM_SEARCH_MAX_CHARS,
|
|
17943
17982
|
400
|
|
17944
17983
|
);
|
|
17984
|
+
function maybeStripStructuredContent(result) {
|
|
17985
|
+
if (INCLUDE_STRUCTURED_CONTENT || !Object.prototype.hasOwnProperty.call(result, "structuredContent")) {
|
|
17986
|
+
return result;
|
|
17987
|
+
}
|
|
17988
|
+
const { structuredContent: _structuredContent, ...rest } = result;
|
|
17989
|
+
return rest;
|
|
17990
|
+
}
|
|
17945
17991
|
var CONSOLIDATED_MODE = process.env.CONTEXTSTREAM_CONSOLIDATED !== "false";
|
|
17946
17992
|
var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
|
|
17947
17993
|
"init",
|
|
@@ -17971,6 +18017,38 @@ var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
17971
18017
|
"help"
|
|
17972
18018
|
// Consolidates session_tools, auth_me, mcp_server_version, etc.
|
|
17973
18019
|
]);
|
|
18020
|
+
function mapToolToConsolidatedDomain(toolName) {
|
|
18021
|
+
if (CONSOLIDATED_TOOLS.has(toolName)) return toolName;
|
|
18022
|
+
if (toolName === "session_init") return "init";
|
|
18023
|
+
if (toolName === "context_smart") return "context";
|
|
18024
|
+
if (toolName === "generate_rules" || toolName === "generate_editor_rules") return "generate_rules";
|
|
18025
|
+
if (toolName.startsWith("search_")) return "search";
|
|
18026
|
+
if (toolName.startsWith("session_") || toolName === "context_feedback") return "session";
|
|
18027
|
+
if (toolName.startsWith("memory_") || toolName === "decision_trace") return "memory";
|
|
18028
|
+
if (toolName.startsWith("graph_")) return "graph";
|
|
18029
|
+
if (toolName.startsWith("projects_")) return "project";
|
|
18030
|
+
if (toolName.startsWith("workspaces_") || toolName.startsWith("workspace_")) return "workspace";
|
|
18031
|
+
if (toolName.startsWith("reminders_")) return "reminder";
|
|
18032
|
+
if (toolName.startsWith("slack_") || toolName.startsWith("github_") || toolName.startsWith("integrations_") || toolName.startsWith("notion_")) {
|
|
18033
|
+
return "integration";
|
|
18034
|
+
}
|
|
18035
|
+
if (toolName.startsWith("media_")) return "media";
|
|
18036
|
+
if (toolName === "session_tools" || toolName === "auth_me" || toolName === "mcp_server_version" || toolName === "tools_enable_bundle") {
|
|
18037
|
+
return "help";
|
|
18038
|
+
}
|
|
18039
|
+
return null;
|
|
18040
|
+
}
|
|
18041
|
+
function resolveConsolidatedAllowlist(allowlist) {
|
|
18042
|
+
if (!allowlist) return null;
|
|
18043
|
+
const mapped = /* @__PURE__ */ new Set();
|
|
18044
|
+
for (const toolName of allowlist) {
|
|
18045
|
+
const consolidated = mapToolToConsolidatedDomain(toolName);
|
|
18046
|
+
if (consolidated) {
|
|
18047
|
+
mapped.add(consolidated);
|
|
18048
|
+
}
|
|
18049
|
+
}
|
|
18050
|
+
return mapped;
|
|
18051
|
+
}
|
|
17974
18052
|
var TOOLSET_ALIASES = {
|
|
17975
18053
|
// Light mode - minimal, fastest
|
|
17976
18054
|
light: LIGHT_TOOLSET,
|
|
@@ -18141,10 +18219,15 @@ function setupClientDetection(server) {
|
|
|
18141
18219
|
function registerTools(server, client, sessionManager) {
|
|
18142
18220
|
const upgradeUrl2 = process.env.CONTEXTSTREAM_UPGRADE_URL || "https://contextstream.io/pricing";
|
|
18143
18221
|
const toolFilter = resolveToolFilter();
|
|
18144
|
-
const
|
|
18222
|
+
const rawToolAllowlist = toolFilter.allowlist;
|
|
18223
|
+
const toolAllowlist = CONSOLIDATED_MODE ? resolveConsolidatedAllowlist(rawToolAllowlist) : rawToolAllowlist;
|
|
18145
18224
|
if (toolAllowlist) {
|
|
18146
18225
|
const source = toolFilter.source;
|
|
18147
|
-
|
|
18226
|
+
if (CONSOLIDATED_MODE) {
|
|
18227
|
+
logDebug(`Toolset: ${source} (${toolAllowlist.size} consolidated domain tools)`);
|
|
18228
|
+
} else {
|
|
18229
|
+
logDebug(`Toolset: ${source} (${toolAllowlist.size} tools)`);
|
|
18230
|
+
}
|
|
18148
18231
|
} else {
|
|
18149
18232
|
logDebug("Toolset: complete (all tools)");
|
|
18150
18233
|
}
|
|
@@ -18541,7 +18624,8 @@ function registerTools(server, client, sessionManager) {
|
|
|
18541
18624
|
if (proGated) return proGated;
|
|
18542
18625
|
const integrationGated = await gateIfIntegrationTool(name);
|
|
18543
18626
|
if (integrationGated) return integrationGated;
|
|
18544
|
-
|
|
18627
|
+
const result = await handler(input, extra);
|
|
18628
|
+
return maybeStripStructuredContent(result);
|
|
18545
18629
|
} catch (error) {
|
|
18546
18630
|
const errorMessage = error?.message || String(error);
|
|
18547
18631
|
const errorDetails = error?.body || error?.details || null;
|
|
@@ -18561,11 +18645,11 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
18561
18645
|
}
|
|
18562
18646
|
};
|
|
18563
18647
|
const errorText = `[${errorCode}] ${errorMessage}${upgradeHint}${sessionHint}${errorDetails ? `: ${JSON.stringify(errorDetails)}` : ""}`;
|
|
18564
|
-
return {
|
|
18648
|
+
return maybeStripStructuredContent({
|
|
18565
18649
|
content: [{ type: "text", text: errorText }],
|
|
18566
18650
|
structuredContent: errorPayload,
|
|
18567
18651
|
isError: true
|
|
18568
|
-
};
|
|
18652
|
+
});
|
|
18569
18653
|
}
|
|
18570
18654
|
};
|
|
18571
18655
|
serverRef.registerTool(name, annotatedConfig, wrapWithAutoContext(name, safeHandler));
|
|
@@ -18622,7 +18706,7 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
18622
18706
|
});
|
|
18623
18707
|
return;
|
|
18624
18708
|
}
|
|
18625
|
-
if (
|
|
18709
|
+
if (toolAllowlist && !toolAllowlist.has(name)) {
|
|
18626
18710
|
if (ROUTER_MODE && !ROUTER_DIRECT_TOOLS.has(name)) {
|
|
18627
18711
|
operationsRegistry.set(name, {
|
|
18628
18712
|
name,
|
|
@@ -18771,7 +18855,7 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
18771
18855
|
Available bundles:
|
|
18772
18856
|
- session: Extended session management (~6 tools)
|
|
18773
18857
|
- memory: Full memory CRUD operations (~16 tools)
|
|
18774
|
-
- search:
|
|
18858
|
+
- search: Auto, semantic, hybrid (legacy alias), and keyword search (~3 tools)
|
|
18775
18859
|
- graph: Code graph analysis and dependencies (~9 tools)
|
|
18776
18860
|
- workspace: Workspace management (~4 tools)
|
|
18777
18861
|
- project: Project management and indexing (~10 tools)
|
|
@@ -19226,10 +19310,18 @@ Access: Free`,
|
|
|
19226
19310
|
output_format: input.output_format
|
|
19227
19311
|
};
|
|
19228
19312
|
}
|
|
19313
|
+
function getSearchAuthError() {
|
|
19314
|
+
if (client.hasEffectiveAuth()) return null;
|
|
19315
|
+
return errorResult(
|
|
19316
|
+
"Authentication required for search. Set CONTEXTSTREAM_API_KEY or CONTEXTSTREAM_JWT, or pass auth credentials from your MCP client when CONTEXTSTREAM_ALLOW_HEADER_AUTH=true."
|
|
19317
|
+
);
|
|
19318
|
+
}
|
|
19229
19319
|
registerTool(
|
|
19230
19320
|
"search_semantic",
|
|
19231
19321
|
{ title: "Semantic search", description: "Semantic vector search", inputSchema: searchSchema },
|
|
19232
19322
|
async (input) => {
|
|
19323
|
+
const authError = getSearchAuthError();
|
|
19324
|
+
if (authError) return authError;
|
|
19233
19325
|
const result = await client.searchSemantic(normalizeSearchParams(input));
|
|
19234
19326
|
return {
|
|
19235
19327
|
content: [{ type: "text", text: formatContent(result) }]
|
|
@@ -19244,6 +19336,8 @@ Access: Free`,
|
|
|
19244
19336
|
inputSchema: searchSchema
|
|
19245
19337
|
},
|
|
19246
19338
|
async (input) => {
|
|
19339
|
+
const authError = getSearchAuthError();
|
|
19340
|
+
if (authError) return authError;
|
|
19247
19341
|
const result = await client.searchHybrid(normalizeSearchParams(input));
|
|
19248
19342
|
return {
|
|
19249
19343
|
content: [{ type: "text", text: formatContent(result) }]
|
|
@@ -19254,6 +19348,8 @@ Access: Free`,
|
|
|
19254
19348
|
"search_keyword",
|
|
19255
19349
|
{ title: "Keyword search", description: "Keyword search", inputSchema: searchSchema },
|
|
19256
19350
|
async (input) => {
|
|
19351
|
+
const authError = getSearchAuthError();
|
|
19352
|
+
if (authError) return authError;
|
|
19257
19353
|
const result = await client.searchKeyword(normalizeSearchParams(input));
|
|
19258
19354
|
return {
|
|
19259
19355
|
content: [{ type: "text", text: formatContent(result) }]
|
|
@@ -19264,6 +19360,8 @@ Access: Free`,
|
|
|
19264
19360
|
"search_pattern",
|
|
19265
19361
|
{ title: "Pattern search", description: "Pattern/regex search", inputSchema: searchSchema },
|
|
19266
19362
|
async (input) => {
|
|
19363
|
+
const authError = getSearchAuthError();
|
|
19364
|
+
if (authError) return authError;
|
|
19267
19365
|
const result = await client.searchPattern(normalizeSearchParams(input));
|
|
19268
19366
|
return {
|
|
19269
19367
|
content: [{ type: "text", text: formatContent(result) }]
|
|
@@ -20291,7 +20389,7 @@ This does semantic search on the first message. You only need context on subsequ
|
|
|
20291
20389
|
);
|
|
20292
20390
|
result.tools_hint = getCoreToolsHint();
|
|
20293
20391
|
const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
|
|
20294
|
-
result.educational_tip = getSessionInitTip(sessionId);
|
|
20392
|
+
result.educational_tip = getSessionInitTip(sessionId ?? "");
|
|
20295
20393
|
const shouldRestoreContext = input.is_post_compact ?? RESTORE_CONTEXT_DEFAULT;
|
|
20296
20394
|
if (shouldRestoreContext) {
|
|
20297
20395
|
result.is_post_compact = true;
|
|
@@ -20557,7 +20655,7 @@ Format options:
|
|
|
20557
20655
|
|
|
20558
20656
|
Example output (grouped):
|
|
20559
20657
|
Session: init(start-conv) smart(each-msg) capture(save) recall(find) remember(quick)
|
|
20560
|
-
Search: semantic(meaning) hybrid(
|
|
20658
|
+
Search: auto(default) semantic(meaning) hybrid(alias) keyword(exact)
|
|
20561
20659
|
Memory: events(crud) nodes(knowledge) search(find) decisions(choices)`,
|
|
20562
20660
|
inputSchema: external_exports.object({
|
|
20563
20661
|
format: external_exports.enum(["grouped", "minimal", "full"]).optional().default("grouped").describe(
|
|
@@ -21004,7 +21102,7 @@ Use this in combination with session_init(is_post_compact=true) for seamless con
|
|
|
21004
21102
|
}
|
|
21005
21103
|
try {
|
|
21006
21104
|
if (input.snapshot_id) {
|
|
21007
|
-
const eventResult = await client.
|
|
21105
|
+
const eventResult = await client.getMemoryEvent(input.snapshot_id);
|
|
21008
21106
|
const event = eventResult?.data || eventResult;
|
|
21009
21107
|
if (!event || !event.content) {
|
|
21010
21108
|
return errorResult(
|
|
@@ -22095,7 +22193,7 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
|
|
|
22095
22193
|
contextCallsSinceLastSave++;
|
|
22096
22194
|
const now = Date.now();
|
|
22097
22195
|
const timeSinceLastSave = now - lastAutoSaveTime;
|
|
22098
|
-
const pressureLevelIncreased = lastAutoSavePressureLevel === ""
|
|
22196
|
+
const pressureLevelIncreased = lastAutoSavePressureLevel === "" || lastAutoSavePressureLevel === "medium" && (cp.level === "high" || cp.level === "critical") || lastAutoSavePressureLevel === "high" && cp.level === "critical";
|
|
22099
22197
|
const shouldSave = pressureLevelIncreased && timeSinceLastSave > AUTO_SAVE_MIN_INTERVAL_MS || contextCallsSinceLastSave >= AUTO_SAVE_CALL_INTERVAL || timeSinceLastSave > AUTO_SAVE_MIN_INTERVAL_MS * 2.5;
|
|
22100
22198
|
if (shouldSave) {
|
|
22101
22199
|
const trigger = pressureLevelIncreased ? "pressure_increase" : contextCallsSinceLastSave >= AUTO_SAVE_CALL_INTERVAL ? "call_count" : "time_interval";
|
|
@@ -23160,11 +23258,11 @@ Use this to remove a reminder that is no longer relevant.`,
|
|
|
23160
23258
|
"search",
|
|
23161
23259
|
{
|
|
23162
23260
|
title: "Search",
|
|
23163
|
-
description: `Search workspace memory and knowledge. Modes: semantic (meaning-based), hybrid (
|
|
23261
|
+
description: `Search workspace memory and knowledge. Modes: auto (recommended), semantic (meaning-based), hybrid (legacy alias for auto), keyword (exact match), pattern (regex), exhaustive (all matches like grep), refactor (word-boundary matching for symbol renaming), team (cross-project team search - team plans only).
|
|
23164
23262
|
|
|
23165
23263
|
Output formats: full (default, includes content), paths (file paths only - 80% token savings), minimal (compact - 60% savings), count (match counts only - 90% savings).`,
|
|
23166
23264
|
inputSchema: external_exports.object({
|
|
23167
|
-
mode: external_exports.enum(["semantic", "hybrid", "keyword", "pattern", "exhaustive", "refactor", "team"]).describe("Search mode"),
|
|
23265
|
+
mode: external_exports.enum(["auto", "semantic", "hybrid", "keyword", "pattern", "exhaustive", "refactor", "team"]).optional().default("auto").describe("Search mode (auto recommended; hybrid is a backward-compatible alias)"),
|
|
23168
23266
|
query: external_exports.string().describe("Search query"),
|
|
23169
23267
|
workspace_id: external_exports.string().uuid().optional(),
|
|
23170
23268
|
project_id: external_exports.string().uuid().optional(),
|
|
@@ -23179,21 +23277,25 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23179
23277
|
})
|
|
23180
23278
|
},
|
|
23181
23279
|
async (input) => {
|
|
23280
|
+
const authError = getSearchAuthError();
|
|
23281
|
+
if (authError) return authError;
|
|
23182
23282
|
client.checkAndIndexChangedFiles().catch(() => {
|
|
23183
23283
|
});
|
|
23184
23284
|
const params = normalizeSearchParams(input);
|
|
23185
23285
|
const startTime = Date.now();
|
|
23286
|
+
const requestedMode = input.mode || "auto";
|
|
23186
23287
|
let result;
|
|
23187
23288
|
let toolType;
|
|
23188
|
-
switch (
|
|
23189
|
-
case "
|
|
23190
|
-
result = await client.searchSemantic(params);
|
|
23191
|
-
toolType = "search_semantic";
|
|
23192
|
-
break;
|
|
23289
|
+
switch (requestedMode) {
|
|
23290
|
+
case "auto":
|
|
23193
23291
|
case "hybrid":
|
|
23194
23292
|
result = await client.searchHybrid(params);
|
|
23195
23293
|
toolType = "search_hybrid";
|
|
23196
23294
|
break;
|
|
23295
|
+
case "semantic":
|
|
23296
|
+
result = await client.searchSemantic(params);
|
|
23297
|
+
toolType = "search_semantic";
|
|
23298
|
+
break;
|
|
23197
23299
|
case "keyword":
|
|
23198
23300
|
result = await client.searchKeyword(params);
|
|
23199
23301
|
toolType = "search_keyword";
|
|
@@ -23257,6 +23359,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23257
23359
|
break;
|
|
23258
23360
|
}
|
|
23259
23361
|
default:
|
|
23362
|
+
result = await client.searchHybrid(params);
|
|
23260
23363
|
toolType = "search_hybrid";
|
|
23261
23364
|
}
|
|
23262
23365
|
const roundTripMs = Date.now() - startTime;
|
|
@@ -23728,7 +23831,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23728
23831
|
);
|
|
23729
23832
|
}
|
|
23730
23833
|
if (input.snapshot_id) {
|
|
23731
|
-
const eventResult = await client.
|
|
23834
|
+
const eventResult = await client.getMemoryEvent(input.snapshot_id);
|
|
23732
23835
|
const event = eventResult?.data || eventResult;
|
|
23733
23836
|
if (!event || !event.content) {
|
|
23734
23837
|
return errorResult(
|
|
@@ -24896,7 +24999,7 @@ ${formatContent(result)}`
|
|
|
24896
24999
|
workspace_id: ws.id,
|
|
24897
25000
|
status: input.todo_status,
|
|
24898
25001
|
priority: input.todo_priority,
|
|
24899
|
-
|
|
25002
|
+
per_page: input.limit ? Math.ceil(input.limit / workspacesForTodos.length) : 10
|
|
24900
25003
|
});
|
|
24901
25004
|
const items = todos?.data?.items || todos?.items || [];
|
|
24902
25005
|
allTodos.push(...items.map((t) => ({ ...t, workspace_name: ws.name })));
|
|
@@ -24946,7 +25049,7 @@ ${formatContent(result)}`
|
|
|
24946
25049
|
const diagrams = await client.diagramsList({
|
|
24947
25050
|
workspace_id: ws.id,
|
|
24948
25051
|
diagram_type: input.diagram_type,
|
|
24949
|
-
|
|
25052
|
+
per_page: input.limit ? Math.ceil(input.limit / workspacesForDiagrams.length) : 10
|
|
24950
25053
|
});
|
|
24951
25054
|
const items = diagrams?.data?.items || diagrams?.items || [];
|
|
24952
25055
|
allDiagrams.push(...items.map((d) => ({ ...d, workspace_name: ws.name })));
|
|
@@ -24988,7 +25091,7 @@ ${formatContent(result)}`
|
|
|
24988
25091
|
const docs = await client.docsList({
|
|
24989
25092
|
workspace_id: ws.id,
|
|
24990
25093
|
doc_type: input.doc_type,
|
|
24991
|
-
|
|
25094
|
+
per_page: input.limit ? Math.ceil(input.limit / workspacesForDocs.length) : 10
|
|
24992
25095
|
});
|
|
24993
25096
|
const items = docs?.data?.items || docs?.items || [];
|
|
24994
25097
|
allDocs.push(...items.map((d) => ({ ...d, workspace_name: ws.name })));
|
|
@@ -26257,7 +26360,6 @@ Last edited: ${updatedPage.last_edited_time}`
|
|
|
26257
26360
|
try {
|
|
26258
26361
|
const notionActivity = await client.notionActivity({
|
|
26259
26362
|
workspace_id: ws.id,
|
|
26260
|
-
days,
|
|
26261
26363
|
limit: perSourceLimit
|
|
26262
26364
|
});
|
|
26263
26365
|
const notionItems = notionActivity?.data || notionActivity || [];
|
|
@@ -26281,7 +26383,6 @@ Last edited: ${updatedPage.last_edited_time}`
|
|
|
26281
26383
|
try {
|
|
26282
26384
|
const slackActivity = await client.slackActivity({
|
|
26283
26385
|
workspace_id: ws.id,
|
|
26284
|
-
days,
|
|
26285
26386
|
limit: perSourceLimit
|
|
26286
26387
|
});
|
|
26287
26388
|
const slackItems = slackActivity?.data || slackActivity || [];
|
|
@@ -26305,7 +26406,6 @@ Last edited: ${updatedPage.last_edited_time}`
|
|
|
26305
26406
|
try {
|
|
26306
26407
|
const githubActivity = await client.githubActivity({
|
|
26307
26408
|
workspace_id: ws.id,
|
|
26308
|
-
days,
|
|
26309
26409
|
limit: perSourceLimit
|
|
26310
26410
|
});
|
|
26311
26411
|
const githubItems = githubActivity?.data || githubActivity || [];
|
|
@@ -29098,21 +29198,22 @@ async function isEditorInstalled(editor) {
|
|
|
29098
29198
|
}
|
|
29099
29199
|
}
|
|
29100
29200
|
var IS_WINDOWS = process.platform === "win32";
|
|
29101
|
-
function
|
|
29102
|
-
|
|
29201
|
+
function escapeTomlString(value) {
|
|
29202
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
29203
|
+
}
|
|
29204
|
+
function formatTomlEnvLines(env) {
|
|
29205
|
+
return Object.entries(env).map(([key, value]) => `${key} = "${escapeTomlString(value)}"`).join("\n");
|
|
29206
|
+
}
|
|
29207
|
+
function buildSetupEnv(params) {
|
|
29208
|
+
const contextPack = params.contextPackEnabled === false ? "false" : "true";
|
|
29209
|
+
return {
|
|
29103
29210
|
CONTEXTSTREAM_API_URL: params.apiUrl,
|
|
29104
|
-
CONTEXTSTREAM_API_KEY: params.apiKey
|
|
29211
|
+
CONTEXTSTREAM_API_KEY: params.apiKey,
|
|
29212
|
+
CONTEXTSTREAM_CONTEXT_PACK: contextPack
|
|
29105
29213
|
};
|
|
29106
|
-
|
|
29107
|
-
|
|
29108
|
-
|
|
29109
|
-
env.CONTEXTSTREAM_CONTEXT_PACK = params.contextPackEnabled === false ? "false" : "true";
|
|
29110
|
-
if (params.restoreContextEnabled === false) {
|
|
29111
|
-
env.CONTEXTSTREAM_RESTORE_CONTEXT = "false";
|
|
29112
|
-
}
|
|
29113
|
-
if (params.showTiming) {
|
|
29114
|
-
env.CONTEXTSTREAM_SHOW_TIMING = "true";
|
|
29115
|
-
}
|
|
29214
|
+
}
|
|
29215
|
+
function buildContextStreamMcpServer(params) {
|
|
29216
|
+
const env = buildSetupEnv(params);
|
|
29116
29217
|
if (IS_WINDOWS) {
|
|
29117
29218
|
return {
|
|
29118
29219
|
command: "cmd",
|
|
@@ -29127,20 +29228,7 @@ function buildContextStreamMcpServer(params) {
|
|
|
29127
29228
|
};
|
|
29128
29229
|
}
|
|
29129
29230
|
function buildContextStreamVsCodeServer(params) {
|
|
29130
|
-
const env =
|
|
29131
|
-
CONTEXTSTREAM_API_URL: params.apiUrl,
|
|
29132
|
-
CONTEXTSTREAM_API_KEY: params.apiKey
|
|
29133
|
-
};
|
|
29134
|
-
if (params.toolset === "router") {
|
|
29135
|
-
env.CONTEXTSTREAM_PROGRESSIVE_MODE = "true";
|
|
29136
|
-
}
|
|
29137
|
-
env.CONTEXTSTREAM_CONTEXT_PACK = params.contextPackEnabled === false ? "false" : "true";
|
|
29138
|
-
if (params.restoreContextEnabled === false) {
|
|
29139
|
-
env.CONTEXTSTREAM_RESTORE_CONTEXT = "false";
|
|
29140
|
-
}
|
|
29141
|
-
if (params.showTiming) {
|
|
29142
|
-
env.CONTEXTSTREAM_SHOW_TIMING = "true";
|
|
29143
|
-
}
|
|
29231
|
+
const env = buildSetupEnv(params);
|
|
29144
29232
|
if (IS_WINDOWS) {
|
|
29145
29233
|
return {
|
|
29146
29234
|
type: "stdio",
|
|
@@ -29238,16 +29326,10 @@ async function upsertCodexTomlConfig(filePath, params) {
|
|
|
29238
29326
|
await fs7.mkdir(path8.dirname(filePath), { recursive: true });
|
|
29239
29327
|
const exists = await fileExists(filePath);
|
|
29240
29328
|
const existing = exists ? await fs7.readFile(filePath, "utf8").catch(() => "") : "";
|
|
29329
|
+
const env = buildSetupEnv(params);
|
|
29330
|
+
const envLines = formatTomlEnvLines(env);
|
|
29241
29331
|
const marker = "[mcp_servers.contextstream]";
|
|
29242
29332
|
const envMarker = "[mcp_servers.contextstream.env]";
|
|
29243
|
-
const toolsetLine = params.toolset === "router" ? `CONTEXTSTREAM_PROGRESSIVE_MODE = "true"
|
|
29244
|
-
` : "";
|
|
29245
|
-
const contextPackLine = `CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"
|
|
29246
|
-
`;
|
|
29247
|
-
const restoreContextLine = params.restoreContextEnabled === false ? `CONTEXTSTREAM_RESTORE_CONTEXT = "false"
|
|
29248
|
-
` : "";
|
|
29249
|
-
const showTimingLine = params.showTiming ? `CONTEXTSTREAM_SHOW_TIMING = "true"
|
|
29250
|
-
` : "";
|
|
29251
29333
|
const commandLine = IS_WINDOWS ? `command = "cmd"
|
|
29252
29334
|
args = ["/c", "npx", "--prefer-online", "-y", "@contextstream/mcp-server@latest"]
|
|
29253
29335
|
` : `command = "npx"
|
|
@@ -29259,9 +29341,7 @@ args = ["--prefer-online", "-y", "@contextstream/mcp-server@latest"]
|
|
|
29259
29341
|
[mcp_servers.contextstream]
|
|
29260
29342
|
` + commandLine + `
|
|
29261
29343
|
[mcp_servers.contextstream.env]
|
|
29262
|
-
|
|
29263
|
-
CONTEXTSTREAM_API_KEY = "${params.apiKey}"
|
|
29264
|
-
` + toolsetLine + contextPackLine + restoreContextLine + showTimingLine;
|
|
29344
|
+
` + envLines + "\n";
|
|
29265
29345
|
if (!exists) {
|
|
29266
29346
|
await fs7.writeFile(filePath, block.trimStart(), "utf8");
|
|
29267
29347
|
return "created";
|
|
@@ -29273,10 +29353,7 @@ CONTEXTSTREAM_API_KEY = "${params.apiKey}"
|
|
|
29273
29353
|
if (!existing.includes(envMarker)) {
|
|
29274
29354
|
await fs7.writeFile(
|
|
29275
29355
|
filePath,
|
|
29276
|
-
existing.trimEnd() + "\n\n" + envMarker +
|
|
29277
|
-
CONTEXTSTREAM_API_URL = "${params.apiUrl}"
|
|
29278
|
-
CONTEXTSTREAM_API_KEY = "${params.apiKey}"
|
|
29279
|
-
` + toolsetLine + contextPackLine,
|
|
29356
|
+
existing.trimEnd() + "\n\n" + envMarker + "\n" + envLines + "\n",
|
|
29280
29357
|
"utf8"
|
|
29281
29358
|
);
|
|
29282
29359
|
return "updated";
|
|
@@ -29284,51 +29361,43 @@ CONTEXTSTREAM_API_KEY = "${params.apiKey}"
|
|
|
29284
29361
|
const lines = existing.split(/\r?\n/);
|
|
29285
29362
|
const out = [];
|
|
29286
29363
|
let inEnv = false;
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
let sawContextPack = false;
|
|
29364
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29365
|
+
const managedKeys = new Set(Object.keys(env));
|
|
29290
29366
|
for (const line of lines) {
|
|
29291
29367
|
const trimmed = line.trim();
|
|
29292
29368
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
29293
29369
|
if (inEnv && trimmed !== envMarker) {
|
|
29294
|
-
|
|
29295
|
-
|
|
29296
|
-
|
|
29297
|
-
|
|
29298
|
-
|
|
29299
|
-
);
|
|
29370
|
+
for (const [key, value] of Object.entries(env)) {
|
|
29371
|
+
if (!seen.has(key)) {
|
|
29372
|
+
out.push(`${key} = "${escapeTomlString(value)}"`);
|
|
29373
|
+
}
|
|
29374
|
+
}
|
|
29300
29375
|
inEnv = false;
|
|
29301
29376
|
}
|
|
29302
|
-
if (trimmed === envMarker)
|
|
29377
|
+
if (trimmed === envMarker) {
|
|
29378
|
+
inEnv = true;
|
|
29379
|
+
seen.clear();
|
|
29380
|
+
}
|
|
29303
29381
|
out.push(line);
|
|
29304
29382
|
continue;
|
|
29305
29383
|
}
|
|
29306
|
-
if (inEnv
|
|
29307
|
-
|
|
29308
|
-
|
|
29309
|
-
|
|
29310
|
-
|
|
29311
|
-
|
|
29312
|
-
|
|
29313
|
-
|
|
29314
|
-
continue;
|
|
29315
|
-
}
|
|
29316
|
-
if (inEnv && /^\s*CONTEXTSTREAM_CONTEXT_PACK\s*=/.test(line)) {
|
|
29317
|
-
out.push(
|
|
29318
|
-
`CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"`
|
|
29319
|
-
);
|
|
29320
|
-
sawContextPack = true;
|
|
29321
|
-
continue;
|
|
29384
|
+
if (inEnv) {
|
|
29385
|
+
const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=/);
|
|
29386
|
+
if (match && managedKeys.has(match[1])) {
|
|
29387
|
+
const key = match[1];
|
|
29388
|
+
out.push(`${key} = "${escapeTomlString(env[key])}"`);
|
|
29389
|
+
seen.add(key);
|
|
29390
|
+
continue;
|
|
29391
|
+
}
|
|
29322
29392
|
}
|
|
29323
29393
|
out.push(line);
|
|
29324
29394
|
}
|
|
29325
29395
|
if (inEnv) {
|
|
29326
|
-
|
|
29327
|
-
|
|
29328
|
-
|
|
29329
|
-
|
|
29330
|
-
|
|
29331
|
-
);
|
|
29396
|
+
for (const [key, value] of Object.entries(env)) {
|
|
29397
|
+
if (!seen.has(key)) {
|
|
29398
|
+
out.push(`${key} = "${escapeTomlString(value)}"`);
|
|
29399
|
+
}
|
|
29400
|
+
}
|
|
29332
29401
|
}
|
|
29333
29402
|
const updated = out.join("\n");
|
|
29334
29403
|
if (updated === existing) return "skipped";
|
|
@@ -29764,11 +29833,7 @@ Code: ${device.user_code}`);
|
|
|
29764
29833
|
const planLabel = detectedPlanName ?? "unknown";
|
|
29765
29834
|
console.log(`
|
|
29766
29835
|
Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
29767
|
-
const
|
|
29768
|
-
const isPro = detectedPlanName && ["pro", "team", "enterprise"].some((p) => detectedPlanName.toLowerCase().includes(p));
|
|
29769
|
-
const contextPackEnabled = isPro;
|
|
29770
|
-
const showTiming = false;
|
|
29771
|
-
const restoreContextEnabled = true;
|
|
29836
|
+
const contextPackEnabled = !!detectedPlanName && ["pro", "team", "enterprise"].some((p) => detectedPlanName.toLowerCase().includes(p));
|
|
29772
29837
|
console.log("\nAuto-Update:");
|
|
29773
29838
|
console.log(" When enabled, ContextStream will automatically update to the latest version");
|
|
29774
29839
|
console.log(" on new sessions (checks daily). You can disable this if you prefer manual updates.");
|
|
@@ -29856,23 +29921,9 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
29856
29921
|
)
|
|
29857
29922
|
) || mcpChoiceDefault;
|
|
29858
29923
|
const mcpScope = mcpChoice === "2" && hasCodex && !hasProjectMcpEditors ? "skip" : mcpChoice === "4" ? "skip" : mcpChoice === "1" ? "global" : mcpChoice === "2" ? "project" : "both";
|
|
29859
|
-
const mcpServer = buildContextStreamMcpServer({ apiUrl, apiKey,
|
|
29860
|
-
const mcpServerClaude = buildContextStreamMcpServer({
|
|
29861
|
-
|
|
29862
|
-
apiKey,
|
|
29863
|
-
toolset,
|
|
29864
|
-
contextPackEnabled,
|
|
29865
|
-
showTiming,
|
|
29866
|
-
restoreContextEnabled
|
|
29867
|
-
});
|
|
29868
|
-
const vsCodeServer = buildContextStreamVsCodeServer({
|
|
29869
|
-
apiUrl,
|
|
29870
|
-
apiKey,
|
|
29871
|
-
toolset,
|
|
29872
|
-
contextPackEnabled,
|
|
29873
|
-
showTiming,
|
|
29874
|
-
restoreContextEnabled
|
|
29875
|
-
});
|
|
29924
|
+
const mcpServer = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
29925
|
+
const mcpServerClaude = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
29926
|
+
const vsCodeServer = buildContextStreamVsCodeServer({ apiUrl, apiKey, contextPackEnabled });
|
|
29876
29927
|
const needsGlobalMcpConfig = mcpScope === "global" || mcpScope === "both" || mcpScope === "project" && hasCodex;
|
|
29877
29928
|
if (needsGlobalMcpConfig) {
|
|
29878
29929
|
console.log("\nInstalling global MCP config...");
|
|
@@ -29889,10 +29940,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
29889
29940
|
const status = await upsertCodexTomlConfig(filePath, {
|
|
29890
29941
|
apiUrl,
|
|
29891
29942
|
apiKey,
|
|
29892
|
-
|
|
29893
|
-
contextPackEnabled,
|
|
29894
|
-
showTiming,
|
|
29895
|
-
restoreContextEnabled
|
|
29943
|
+
contextPackEnabled
|
|
29896
29944
|
});
|
|
29897
29945
|
writeActions.push({ kind: "mcp-config", target: filePath, status });
|
|
29898
29946
|
console.log(`- ${EDITOR_LABELS[editor]}: ${status} ${filePath}`);
|
|
@@ -29918,10 +29966,9 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
29918
29966
|
console.log(
|
|
29919
29967
|
"- Claude Code: global MCP config is best done via `claude mcp add --transport stdio ...` (see docs)."
|
|
29920
29968
|
);
|
|
29921
|
-
const envHint = toolset === "router" ? " --env CONTEXTSTREAM_PROGRESSIVE_MODE=true" : "";
|
|
29922
29969
|
const packHint = contextPackEnabled === false ? " --env CONTEXTSTREAM_CONTEXT_PACK=false" : " --env CONTEXTSTREAM_CONTEXT_PACK=true";
|
|
29923
29970
|
console.log(
|
|
29924
|
-
` macOS/Linux: claude mcp add --transport stdio contextstream --scope user --env CONTEXTSTREAM_API_URL=... --env CONTEXTSTREAM_API_KEY=...${
|
|
29971
|
+
` macOS/Linux: claude mcp add --transport stdio contextstream --scope user --env CONTEXTSTREAM_API_URL=... --env CONTEXTSTREAM_API_KEY=...${packHint} -- npx --prefer-online -y @contextstream/mcp-server@latest`
|
|
29925
29972
|
);
|
|
29926
29973
|
console.log(
|
|
29927
29974
|
" Windows (native): use `cmd /c npx --prefer-online -y @contextstream/mcp-server@latest` after `--` if `npx` is not found."
|
|
@@ -30371,6 +30418,7 @@ Environment variables:
|
|
|
30371
30418
|
CONTEXTSTREAM_PROGRESSIVE_MODE Progressive disclosure: true|false (default: false, starts with ~13 core tools)
|
|
30372
30419
|
CONTEXTSTREAM_ROUTER_MODE Router pattern: true|false (default: false, exposes only 2 meta-tools)
|
|
30373
30420
|
CONTEXTSTREAM_OUTPUT_FORMAT Output verbosity: compact|pretty (default: compact, ~30% fewer tokens)
|
|
30421
|
+
CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT Include structured JSON payloads in tool results: true|false (default: true)
|
|
30374
30422
|
CONTEXTSTREAM_SEARCH_LIMIT Default MCP search limit (default: 3)
|
|
30375
30423
|
CONTEXTSTREAM_SEARCH_MAX_CHARS Max chars per search result content (default: 400)
|
|
30376
30424
|
CONTEXTSTREAM_CONSOLIDATED Consolidated domain tools: true|false (default: true in v0.4.x, ~75% token reduction)
|