@contextstream/mcp-server 0.4.64 → 0.4.65
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 +43 -1
- package/dist/hooks/auto-rules.js +1 -1
- package/dist/hooks/notification.js +5 -2
- package/dist/hooks/permission-request.js +5 -2
- package/dist/hooks/post-tool-use-failure.js +5 -2
- package/dist/hooks/pre-compact.js +1 -4
- package/dist/hooks/runner.js +8 -11
- package/dist/hooks/session-init.js +1 -4
- package/dist/hooks/stop.js +5 -2
- package/dist/hooks/subagent-start.js +5 -2
- package/dist/hooks/subagent-stop.js +5 -2
- package/dist/hooks/task-completed.js +5 -2
- package/dist/hooks/teammate-idle.js +5 -2
- package/dist/index.js +613 -104
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1720,7 +1720,7 @@ function buildHooksConfig(options) {
|
|
|
1720
1720
|
{
|
|
1721
1721
|
type: "command",
|
|
1722
1722
|
command: getHookCommand("session-start"),
|
|
1723
|
-
timeout:
|
|
1723
|
+
timeout: 15
|
|
1724
1724
|
}
|
|
1725
1725
|
]
|
|
1726
1726
|
}
|
|
@@ -4879,9 +4879,11 @@ function readHookInput() {
|
|
|
4879
4879
|
}
|
|
4880
4880
|
}
|
|
4881
4881
|
function writeHookOutput(output) {
|
|
4882
|
+
const eventName = output?.hookEventName || (typeof process.env.HOOK_EVENT_NAME === "string" ? process.env.HOOK_EVENT_NAME.trim() : "");
|
|
4883
|
+
const canUseHookSpecificOutput = HOOK_SPECIFIC_OUTPUT_EVENTS.has(eventName);
|
|
4882
4884
|
const payload = output && (output.additionalContext || output.blocked || output.reason) ? {
|
|
4883
|
-
hookSpecificOutput: output.additionalContext ? {
|
|
4884
|
-
hookEventName:
|
|
4885
|
+
hookSpecificOutput: output.additionalContext && canUseHookSpecificOutput ? {
|
|
4886
|
+
hookEventName: eventName,
|
|
4885
4887
|
additionalContext: output.additionalContext
|
|
4886
4888
|
} : void 0,
|
|
4887
4889
|
additionalContext: output.additionalContext,
|
|
@@ -5082,11 +5084,12 @@ async function fetchFastContext(config, body) {
|
|
|
5082
5084
|
return null;
|
|
5083
5085
|
}
|
|
5084
5086
|
}
|
|
5085
|
-
var DEFAULT_API_URL2;
|
|
5087
|
+
var DEFAULT_API_URL2, HOOK_SPECIFIC_OUTPUT_EVENTS;
|
|
5086
5088
|
var init_common = __esm({
|
|
5087
5089
|
"src/hooks/common.ts"() {
|
|
5088
5090
|
"use strict";
|
|
5089
5091
|
DEFAULT_API_URL2 = "https://api.contextstream.io";
|
|
5092
|
+
HOOK_SPECIFIC_OUTPUT_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "UserPromptSubmit", "PostToolUse"]);
|
|
5090
5093
|
}
|
|
5091
5094
|
});
|
|
5092
5095
|
|
|
@@ -7107,10 +7110,7 @@ After compaction, call session_init(is_post_compact=true) to restore context.${c
|
|
|
7107
7110
|
User instructions: ${customInstructions}` : ""}`;
|
|
7108
7111
|
console.log(
|
|
7109
7112
|
JSON.stringify({
|
|
7110
|
-
|
|
7111
|
-
hookEventName: "PreCompact",
|
|
7112
|
-
additionalContext: context
|
|
7113
|
-
}
|
|
7113
|
+
additionalContext: context
|
|
7114
7114
|
})
|
|
7115
7115
|
);
|
|
7116
7116
|
process.exit(0);
|
|
@@ -7542,10 +7542,7 @@ async function runSessionInitHook() {
|
|
|
7542
7542
|
});
|
|
7543
7543
|
console.log(
|
|
7544
7544
|
JSON.stringify({
|
|
7545
|
-
|
|
7546
|
-
hookEventName: "SessionStart",
|
|
7547
|
-
additionalContext: formattedContext
|
|
7548
|
-
}
|
|
7545
|
+
additionalContext: formattedContext
|
|
7549
7546
|
})
|
|
7550
7547
|
);
|
|
7551
7548
|
process.exit(0);
|
|
@@ -12913,6 +12910,15 @@ function normalizeNodeType(input) {
|
|
|
12913
12910
|
);
|
|
12914
12911
|
}
|
|
12915
12912
|
}
|
|
12913
|
+
function normalizeTags(tags) {
|
|
12914
|
+
if (!tags || tags.length === 0) return void 0;
|
|
12915
|
+
const normalized = Array.from(
|
|
12916
|
+
new Set(
|
|
12917
|
+
tags.map((tag) => String(tag ?? "").trim()).filter(Boolean)
|
|
12918
|
+
)
|
|
12919
|
+
);
|
|
12920
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
12921
|
+
}
|
|
12916
12922
|
function pickString(value) {
|
|
12917
12923
|
if (typeof value !== "string") return null;
|
|
12918
12924
|
const trimmed = value.trim();
|
|
@@ -13448,7 +13454,12 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13448
13454
|
if (!body.content || body.content.trim().length === 0) {
|
|
13449
13455
|
throw new Error("content is required and cannot be empty");
|
|
13450
13456
|
}
|
|
13451
|
-
|
|
13457
|
+
const normalizedTags = normalizeTags(body.tags);
|
|
13458
|
+
const apiBody = {
|
|
13459
|
+
...withDefaults,
|
|
13460
|
+
...normalizedTags ? { tags: normalizedTags } : {}
|
|
13461
|
+
};
|
|
13462
|
+
return request(this.config, "/memory/events", { body: apiBody });
|
|
13452
13463
|
}
|
|
13453
13464
|
bulkIngestEvents(body) {
|
|
13454
13465
|
return request(this.config, "/memory/events/ingest", { body: this.withDefaults(body) });
|
|
@@ -14863,7 +14874,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14863
14874
|
async captureContext(params) {
|
|
14864
14875
|
const withDefaults = this.withDefaults(params);
|
|
14865
14876
|
let apiEventType = "manual_note";
|
|
14866
|
-
const tags = params.tags || [];
|
|
14877
|
+
const tags = [...params.tags || []];
|
|
14867
14878
|
switch (params.event_type) {
|
|
14868
14879
|
case "conversation":
|
|
14869
14880
|
apiEventType = "chat";
|
|
@@ -14906,18 +14917,20 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14906
14917
|
apiEventType = "manual_note";
|
|
14907
14918
|
tags.push(params.event_type);
|
|
14908
14919
|
}
|
|
14920
|
+
const normalizedTags = normalizeTags(tags);
|
|
14909
14921
|
return this.createMemoryEvent({
|
|
14910
14922
|
workspace_id: withDefaults.workspace_id,
|
|
14911
14923
|
project_id: withDefaults.project_id,
|
|
14912
14924
|
event_type: apiEventType,
|
|
14913
14925
|
title: params.title,
|
|
14914
14926
|
content: params.content,
|
|
14927
|
+
tags: normalizedTags,
|
|
14915
14928
|
provenance: params.provenance,
|
|
14916
14929
|
code_refs: params.code_refs,
|
|
14917
14930
|
metadata: {
|
|
14918
14931
|
original_type: params.event_type,
|
|
14919
14932
|
session_id: params.session_id,
|
|
14920
|
-
tags,
|
|
14933
|
+
...normalizedTags ? { tags: normalizedTags } : {},
|
|
14921
14934
|
importance: params.importance || "medium",
|
|
14922
14935
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14923
14936
|
source: "mcp_auto_capture"
|
|
@@ -14933,8 +14946,9 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14933
14946
|
const metadata = {
|
|
14934
14947
|
...params.metadata || {}
|
|
14935
14948
|
};
|
|
14936
|
-
|
|
14937
|
-
|
|
14949
|
+
const normalizedTags = normalizeTags(params.tags);
|
|
14950
|
+
if (normalizedTags) {
|
|
14951
|
+
metadata.tags = normalizedTags;
|
|
14938
14952
|
}
|
|
14939
14953
|
if (!metadata.captured_at) {
|
|
14940
14954
|
metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -14948,6 +14962,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14948
14962
|
event_type: params.event_type,
|
|
14949
14963
|
title: params.title,
|
|
14950
14964
|
content: params.content,
|
|
14965
|
+
tags: normalizedTags,
|
|
14951
14966
|
provenance: params.provenance,
|
|
14952
14967
|
code_refs: params.code_refs,
|
|
14953
14968
|
metadata
|
|
@@ -14967,6 +14982,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14967
14982
|
*/
|
|
14968
14983
|
async sessionRemember(params) {
|
|
14969
14984
|
const withDefaults = this.withDefaults(params);
|
|
14985
|
+
const normalizedTags = normalizeTags(params.tags);
|
|
14970
14986
|
if (!withDefaults.workspace_id) {
|
|
14971
14987
|
throw new Error(
|
|
14972
14988
|
"workspace_id is required for session_remember. Set defaultWorkspaceId in config or provide workspace_id."
|
|
@@ -14978,7 +14994,8 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14978
14994
|
workspace_id: withDefaults.workspace_id,
|
|
14979
14995
|
project_id: withDefaults.project_id,
|
|
14980
14996
|
importance: params.importance,
|
|
14981
|
-
await_indexing: params.await_indexing
|
|
14997
|
+
await_indexing: params.await_indexing,
|
|
14998
|
+
...normalizedTags ? { tags: normalizedTags } : {}
|
|
14982
14999
|
}
|
|
14983
15000
|
});
|
|
14984
15001
|
}
|
|
@@ -17045,6 +17062,7 @@ ${context}`;
|
|
|
17045
17062
|
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
17046
17063
|
if (params?.doc_type) query.set("doc_type", params.doc_type);
|
|
17047
17064
|
if (params?.is_personal !== void 0) query.set("is_personal", String(params.is_personal));
|
|
17065
|
+
if (params?.query) query.set("query", params.query);
|
|
17048
17066
|
if (params?.page) query.set("page", String(params.page));
|
|
17049
17067
|
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
17050
17068
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
@@ -17694,6 +17712,56 @@ function classifyIndexConfidence(indexed, apiIndexed, locallyIndexed, freshness)
|
|
|
17694
17712
|
reason: "Index state is inferred but lacks corroborating API/local metadata."
|
|
17695
17713
|
};
|
|
17696
17714
|
}
|
|
17715
|
+
function classifyGraphIngestIndexState(input) {
|
|
17716
|
+
const { statusResult, locallyIndexed } = input;
|
|
17717
|
+
const candidates = candidateObjects(statusResult);
|
|
17718
|
+
const projectIndexState = readString(candidates, "project_index_state")?.toLowerCase();
|
|
17719
|
+
const indexInProgress = apiResultIsIndexing(statusResult);
|
|
17720
|
+
const indexed = apiResultReportsIndexed(statusResult) || locallyIndexed;
|
|
17721
|
+
const indexedAt = extractIndexTimestamp(statusResult);
|
|
17722
|
+
const ageHours = indexedAt !== void 0 ? Math.floor((Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60)) : void 0;
|
|
17723
|
+
const freshness = classifyIndexFreshness(indexed, ageHours);
|
|
17724
|
+
if (indexInProgress) {
|
|
17725
|
+
return {
|
|
17726
|
+
state: "indexing",
|
|
17727
|
+
freshness,
|
|
17728
|
+
indexInProgress,
|
|
17729
|
+
indexed,
|
|
17730
|
+
projectIndexState,
|
|
17731
|
+
ageHours
|
|
17732
|
+
};
|
|
17733
|
+
}
|
|
17734
|
+
const explicitlyMissing = projectIndexState === "missing" || projectIndexState === "not_indexed" || projectIndexState === "unindexed";
|
|
17735
|
+
if (!indexed || explicitlyMissing) {
|
|
17736
|
+
return {
|
|
17737
|
+
state: "missing",
|
|
17738
|
+
freshness,
|
|
17739
|
+
indexInProgress,
|
|
17740
|
+
indexed,
|
|
17741
|
+
projectIndexState,
|
|
17742
|
+
ageHours
|
|
17743
|
+
};
|
|
17744
|
+
}
|
|
17745
|
+
const explicitlyStale = projectIndexState === "stale";
|
|
17746
|
+
if (freshness === "stale" || explicitlyStale) {
|
|
17747
|
+
return {
|
|
17748
|
+
state: "stale",
|
|
17749
|
+
freshness,
|
|
17750
|
+
indexInProgress,
|
|
17751
|
+
indexed,
|
|
17752
|
+
projectIndexState,
|
|
17753
|
+
ageHours
|
|
17754
|
+
};
|
|
17755
|
+
}
|
|
17756
|
+
return {
|
|
17757
|
+
state: "ready",
|
|
17758
|
+
freshness,
|
|
17759
|
+
indexInProgress,
|
|
17760
|
+
indexed,
|
|
17761
|
+
projectIndexState,
|
|
17762
|
+
ageHours
|
|
17763
|
+
};
|
|
17764
|
+
}
|
|
17697
17765
|
|
|
17698
17766
|
// src/todo-utils.ts
|
|
17699
17767
|
function normalizeTodoStatus(status) {
|
|
@@ -17942,6 +18010,32 @@ After updating, restart the AI tool to use the new version.
|
|
|
17942
18010
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
17943
18011
|
`.trim();
|
|
17944
18012
|
}
|
|
18013
|
+
function generateSuggestedRulesNotice(result) {
|
|
18014
|
+
const suggestedRules = result.suggested_rules;
|
|
18015
|
+
if (!suggestedRules || suggestedRules.length === 0) {
|
|
18016
|
+
return "";
|
|
18017
|
+
}
|
|
18018
|
+
const ruleLines = suggestedRules.slice(0, 3).map((rule, i) => {
|
|
18019
|
+
const cat = rule.category || "general";
|
|
18020
|
+
const confidence = rule.confidence ? `${Math.round(rule.confidence * 100)}%` : "?";
|
|
18021
|
+
const count = rule.occurrence_count || 0;
|
|
18022
|
+
const keywords = (rule.keywords || []).join(", ");
|
|
18023
|
+
return `${i + 1}. [${cat}] ${rule.instruction || ""} (confidence: ${confidence}, seen ${count}x)
|
|
18024
|
+
Keywords: ${keywords}
|
|
18025
|
+
Rule ID: ${rule.id}`;
|
|
18026
|
+
});
|
|
18027
|
+
return `
|
|
18028
|
+
|
|
18029
|
+
\u{1F4A1} [SUGGESTED_RULES] ContextStream detected recurring patterns and generated rule suggestions.
|
|
18030
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
18031
|
+
Present these to the user. They can accept or reject each one.
|
|
18032
|
+
|
|
18033
|
+
${ruleLines.join("\n")}
|
|
18034
|
+
|
|
18035
|
+
To accept: session(action="suggested_rule_action", rule_id="<id>", rule_action="accept")
|
|
18036
|
+
To reject: session(action="suggested_rule_action", rule_id="<id>", rule_action="reject")
|
|
18037
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`;
|
|
18038
|
+
}
|
|
17945
18039
|
var DEFAULT_PARAM_DESCRIPTIONS = {
|
|
17946
18040
|
api_key: "ContextStream API key.",
|
|
17947
18041
|
apiKey: "ContextStream API key.",
|
|
@@ -20231,6 +20325,9 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20231
20325
|
if ("data" in value) return extractCollectionArray(value.data);
|
|
20232
20326
|
return void 0;
|
|
20233
20327
|
}
|
|
20328
|
+
function normalizeDocLookupText(value) {
|
|
20329
|
+
return value.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
20330
|
+
}
|
|
20234
20331
|
function tokenizeForDocMatch(query) {
|
|
20235
20332
|
const stopWords = /* @__PURE__ */ new Set([
|
|
20236
20333
|
"the",
|
|
@@ -20248,23 +20345,99 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20248
20345
|
"find",
|
|
20249
20346
|
"plan",
|
|
20250
20347
|
"phase",
|
|
20251
|
-
"phases"
|
|
20348
|
+
"phases",
|
|
20349
|
+
"get",
|
|
20350
|
+
"open",
|
|
20351
|
+
"show",
|
|
20352
|
+
"read"
|
|
20252
20353
|
]);
|
|
20253
|
-
return query.split(
|
|
20354
|
+
return normalizeDocLookupText(query).split(/\s+/).map((term) => term.trim()).filter((term) => term.length >= 3 && !stopWords.has(term));
|
|
20254
20355
|
}
|
|
20255
|
-
function scoreDocMatch(doc, terms) {
|
|
20256
|
-
const
|
|
20257
|
-
const
|
|
20258
|
-
const
|
|
20259
|
-
|
|
20356
|
+
function scoreDocMatch(doc, query, terms) {
|
|
20357
|
+
const rawQuery = query.trim();
|
|
20358
|
+
const normalizedQuery = normalizeDocLookupText(rawQuery);
|
|
20359
|
+
const titleRaw = String(doc?.title ?? doc?.name ?? doc?.summary ?? "").trim();
|
|
20360
|
+
const title = normalizeDocLookupText(titleRaw);
|
|
20361
|
+
const docId = String(doc?.id ?? "").trim();
|
|
20362
|
+
if (docId && rawQuery && docId.toLowerCase() === rawQuery.toLowerCase()) {
|
|
20363
|
+
return { doc, score: 100, exact: true, source: "doc_id" };
|
|
20364
|
+
}
|
|
20365
|
+
if (title && normalizedQuery && title === normalizedQuery) {
|
|
20366
|
+
return { doc, score: 95, exact: true, source: "exact_title" };
|
|
20367
|
+
}
|
|
20368
|
+
if (title && normalizedQuery && title.includes(normalizedQuery) && normalizedQuery.length >= 8) {
|
|
20369
|
+
return { doc, score: 80, exact: false, source: "title_contains_query" };
|
|
20370
|
+
}
|
|
20371
|
+
if (terms.length === 0) {
|
|
20372
|
+
return { doc, score: 0, exact: false, source: "term_overlap" };
|
|
20373
|
+
}
|
|
20374
|
+
const matchedTerms = terms.filter((term) => title.includes(term)).length;
|
|
20375
|
+
if (matchedTerms === 0) {
|
|
20376
|
+
return { doc, score: 0, exact: false, source: "term_overlap" };
|
|
20377
|
+
}
|
|
20378
|
+
if (matchedTerms === terms.length) {
|
|
20379
|
+
return {
|
|
20380
|
+
doc,
|
|
20381
|
+
score: 60 + matchedTerms * 5,
|
|
20382
|
+
exact: false,
|
|
20383
|
+
source: "all_terms"
|
|
20384
|
+
};
|
|
20385
|
+
}
|
|
20386
|
+
return {
|
|
20387
|
+
doc,
|
|
20388
|
+
score: matchedTerms * 10,
|
|
20389
|
+
exact: false,
|
|
20390
|
+
source: "term_overlap"
|
|
20391
|
+
};
|
|
20260
20392
|
}
|
|
20261
|
-
function
|
|
20393
|
+
function rankDocsForQueryMatches(docs, query, limit) {
|
|
20262
20394
|
const terms = tokenizeForDocMatch(query);
|
|
20263
|
-
|
|
20264
|
-
const scored = docs.map((doc, idx) => ({ idx, score: scoreDocMatch(doc, terms) }));
|
|
20395
|
+
const scored = docs.map((doc, idx) => ({ idx, ...scoreDocMatch(doc, query, terms) }));
|
|
20265
20396
|
scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
20266
|
-
|
|
20267
|
-
|
|
20397
|
+
return scored.filter((entry) => entry.score > 0).slice(0, limit);
|
|
20398
|
+
}
|
|
20399
|
+
function rankDocsForQuery(docs, query, limit) {
|
|
20400
|
+
const rankedMatches = rankDocsForQueryMatches(docs, query, limit);
|
|
20401
|
+
return rankedMatches.length > 0 ? rankedMatches.map((entry) => entry.doc) : docs.slice(0, limit);
|
|
20402
|
+
}
|
|
20403
|
+
function selectResolvedDocMatch(matches) {
|
|
20404
|
+
if (matches.length === 0) return void 0;
|
|
20405
|
+
if (matches[0].exact) return matches[0];
|
|
20406
|
+
const top = matches[0];
|
|
20407
|
+
const second = matches[1];
|
|
20408
|
+
if (top.score >= 80 && (!second || second.score <= 40)) {
|
|
20409
|
+
return top;
|
|
20410
|
+
}
|
|
20411
|
+
return void 0;
|
|
20412
|
+
}
|
|
20413
|
+
function extractMemorySearchResults(response) {
|
|
20414
|
+
const result = response?.data ?? response;
|
|
20415
|
+
if (Array.isArray(result?.results)) return result.results;
|
|
20416
|
+
if (Array.isArray(result?.items)) return result.items;
|
|
20417
|
+
return [];
|
|
20418
|
+
}
|
|
20419
|
+
function buildHybridMemoryDocResults(memoryResults, docMatches, limit) {
|
|
20420
|
+
const docs = docMatches.map((match) => ({
|
|
20421
|
+
entity_type: "doc",
|
|
20422
|
+
id: match.doc?.id ?? "unknown",
|
|
20423
|
+
title: match.doc?.title ?? match.doc?.name ?? "Untitled",
|
|
20424
|
+
preview: String(match.doc?.content ?? "").slice(0, 150),
|
|
20425
|
+
score: match.score,
|
|
20426
|
+
match_source: match.source,
|
|
20427
|
+
doc_type: match.doc?.doc_type ?? "general"
|
|
20428
|
+
}));
|
|
20429
|
+
const memory = memoryResults.map((item) => ({
|
|
20430
|
+
entity_type: "memory",
|
|
20431
|
+
id: item?.id ?? item?.node_id ?? "unknown",
|
|
20432
|
+
title: item?.title ?? item?.summary ?? item?.name ?? "Untitled",
|
|
20433
|
+
preview: String(item?.content ?? item?.details ?? "").slice(0, 150),
|
|
20434
|
+
score: Math.round(Number(item?.score ?? 0) * 100),
|
|
20435
|
+
match_source: "memory_search",
|
|
20436
|
+
node_type: item?.node_type ?? item?.event_type ?? item?.type ?? "unknown"
|
|
20437
|
+
}));
|
|
20438
|
+
const combined = [...docs, ...memory];
|
|
20439
|
+
combined.sort((a, b) => Number(b.score || 0) - Number(a.score || 0));
|
|
20440
|
+
return combined.slice(0, limit);
|
|
20268
20441
|
}
|
|
20269
20442
|
async function findDocsFallback(workspaceId, candidateProjectIds, query, limit) {
|
|
20270
20443
|
const uniqueCandidates = [];
|
|
@@ -20398,6 +20571,137 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20398
20571
|
}
|
|
20399
20572
|
})();
|
|
20400
20573
|
}
|
|
20574
|
+
async function runGraphIngestWithPreflight(projectId, wait) {
|
|
20575
|
+
const folderPath = resolveFolderPath(void 0, sessionManager);
|
|
20576
|
+
const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
|
|
20577
|
+
const locallyIndexed = localIndexProjectId === projectId;
|
|
20578
|
+
const suggestedPath = folderPath || "<your_project_path>";
|
|
20579
|
+
let statusResult = {};
|
|
20580
|
+
try {
|
|
20581
|
+
statusResult = await client.projectIndexStatus(projectId);
|
|
20582
|
+
} catch (error) {
|
|
20583
|
+
if (!isNotFoundError(error)) throw error;
|
|
20584
|
+
}
|
|
20585
|
+
const preflight = classifyGraphIngestIndexState({
|
|
20586
|
+
statusResult,
|
|
20587
|
+
locallyIndexed
|
|
20588
|
+
});
|
|
20589
|
+
const preflightData = {
|
|
20590
|
+
index_state: preflight.state,
|
|
20591
|
+
index_freshness: preflight.freshness,
|
|
20592
|
+
index_age_hours: preflight.ageHours ?? null,
|
|
20593
|
+
index_in_progress: preflight.indexInProgress,
|
|
20594
|
+
project_index_state: preflight.projectIndexState ?? null,
|
|
20595
|
+
locally_indexed: locallyIndexed
|
|
20596
|
+
};
|
|
20597
|
+
if (preflight.state === "indexing") {
|
|
20598
|
+
const deferred = {
|
|
20599
|
+
status: "deferred",
|
|
20600
|
+
wait,
|
|
20601
|
+
reason: "Project index refresh is in progress. Graph ingest is deferred until index refresh completes.",
|
|
20602
|
+
preflight: {
|
|
20603
|
+
...preflightData,
|
|
20604
|
+
auto_refresh_started: false,
|
|
20605
|
+
graph_ingest_executed: false
|
|
20606
|
+
},
|
|
20607
|
+
next_step: `Run project(action="index_status") and retry graph(action="ingest") when freshness is not stale.`
|
|
20608
|
+
};
|
|
20609
|
+
return {
|
|
20610
|
+
content: [
|
|
20611
|
+
{
|
|
20612
|
+
type: "text",
|
|
20613
|
+
text: `Project indexing is currently in progress; graph ingest was deferred to avoid rebuilding edges from stale embeddings.
|
|
20614
|
+
|
|
20615
|
+
${formatContent(deferred)}`
|
|
20616
|
+
}
|
|
20617
|
+
],
|
|
20618
|
+
structuredContent: deferred
|
|
20619
|
+
};
|
|
20620
|
+
}
|
|
20621
|
+
if (preflight.state === "stale" || preflight.state === "missing") {
|
|
20622
|
+
const validPath = folderPath ? await validateReadableDirectory(folderPath) : void 0;
|
|
20623
|
+
if (!validPath?.ok) {
|
|
20624
|
+
return errorResult(
|
|
20625
|
+
`Graph ingest is blocked because the project index is ${preflight.state} and no readable project folder is available for auto-refresh.
|
|
20626
|
+
Run project(action="ingest_local", path="${suggestedPath}") first, then retry graph(action="ingest").`
|
|
20627
|
+
);
|
|
20628
|
+
}
|
|
20629
|
+
if (!preflight.indexInProgress) {
|
|
20630
|
+
startBackgroundIngest(projectId, validPath.resolvedPath, { force: true }, { preflight: true });
|
|
20631
|
+
}
|
|
20632
|
+
const deferred = {
|
|
20633
|
+
status: "deferred",
|
|
20634
|
+
wait,
|
|
20635
|
+
reason: `Project index is ${preflight.state}. Started index refresh before graph ingest to avoid destructive edge rebuilds from stale embeddings.`,
|
|
20636
|
+
preflight: {
|
|
20637
|
+
...preflightData,
|
|
20638
|
+
auto_refresh_started: !preflight.indexInProgress,
|
|
20639
|
+
auto_refresh_path: validPath.resolvedPath,
|
|
20640
|
+
auto_refresh_force: true,
|
|
20641
|
+
graph_ingest_executed: false
|
|
20642
|
+
},
|
|
20643
|
+
next_step: `Monitor with project(action="index_status"), then rerun graph(action="ingest").`
|
|
20644
|
+
};
|
|
20645
|
+
return {
|
|
20646
|
+
content: [
|
|
20647
|
+
{
|
|
20648
|
+
type: "text",
|
|
20649
|
+
text: `Project index is ${preflight.state}; started index refresh and deferred graph ingest to prevent stale-edge rebuild.
|
|
20650
|
+
|
|
20651
|
+
${formatContent(deferred)}`
|
|
20652
|
+
}
|
|
20653
|
+
],
|
|
20654
|
+
structuredContent: deferred
|
|
20655
|
+
};
|
|
20656
|
+
}
|
|
20657
|
+
let estimate = null;
|
|
20658
|
+
try {
|
|
20659
|
+
const stats = await client.projectStatistics(projectId);
|
|
20660
|
+
estimate = estimateGraphIngestMinutes(stats);
|
|
20661
|
+
} catch (error) {
|
|
20662
|
+
logDebug(`Failed to fetch project statistics for graph estimate: ${error}`);
|
|
20663
|
+
}
|
|
20664
|
+
const result = await client.graphIngest({ project_id: projectId, wait });
|
|
20665
|
+
const estimateText = estimate ? `Estimated time: ${estimate.min}-${estimate.max} min${estimate.basis ? ` (based on ${estimate.basis})` : ""}.` : "Estimated time varies with repo size.";
|
|
20666
|
+
const note = `Graph ingestion is running ${wait ? "synchronously" : "asynchronously"} and can take a few minutes. ${estimateText}`;
|
|
20667
|
+
const structured = toStructured(result);
|
|
20668
|
+
const structuredContent = structured && typeof structured === "object" ? {
|
|
20669
|
+
...structured,
|
|
20670
|
+
wait,
|
|
20671
|
+
note,
|
|
20672
|
+
preflight: {
|
|
20673
|
+
...preflightData,
|
|
20674
|
+
auto_refresh_started: false,
|
|
20675
|
+
graph_ingest_executed: true
|
|
20676
|
+
},
|
|
20677
|
+
...estimate ? {
|
|
20678
|
+
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
20679
|
+
estimate_basis: estimate.basis
|
|
20680
|
+
} : {}
|
|
20681
|
+
} : {
|
|
20682
|
+
wait,
|
|
20683
|
+
note,
|
|
20684
|
+
preflight: {
|
|
20685
|
+
...preflightData,
|
|
20686
|
+
auto_refresh_started: false,
|
|
20687
|
+
graph_ingest_executed: true
|
|
20688
|
+
},
|
|
20689
|
+
...estimate ? {
|
|
20690
|
+
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
20691
|
+
estimate_basis: estimate.basis
|
|
20692
|
+
} : {}
|
|
20693
|
+
};
|
|
20694
|
+
return {
|
|
20695
|
+
content: [
|
|
20696
|
+
{
|
|
20697
|
+
type: "text",
|
|
20698
|
+
text: `${note}
|
|
20699
|
+
${formatContent(result)}`
|
|
20700
|
+
}
|
|
20701
|
+
],
|
|
20702
|
+
structuredContent
|
|
20703
|
+
};
|
|
20704
|
+
}
|
|
20401
20705
|
function summarizeVideoTextExtraction(metadata) {
|
|
20402
20706
|
if (!metadata || typeof metadata !== "object") return void 0;
|
|
20403
20707
|
const extraction = metadata.video_text_extraction;
|
|
@@ -21753,43 +22057,7 @@ Access: Free`,
|
|
|
21753
22057
|
);
|
|
21754
22058
|
}
|
|
21755
22059
|
const wait = input.wait ?? false;
|
|
21756
|
-
|
|
21757
|
-
try {
|
|
21758
|
-
const stats = await client.projectStatistics(projectId);
|
|
21759
|
-
estimate = estimateGraphIngestMinutes(stats);
|
|
21760
|
-
} catch (error) {
|
|
21761
|
-
logDebug(`Failed to fetch project statistics for graph estimate: ${error}`);
|
|
21762
|
-
}
|
|
21763
|
-
const result = await client.graphIngest({ project_id: projectId, wait });
|
|
21764
|
-
const estimateText = estimate ? `Estimated time: ${estimate.min}-${estimate.max} min${estimate.basis ? ` (based on ${estimate.basis})` : ""}.` : "Estimated time varies with repo size.";
|
|
21765
|
-
const note = `Graph ingestion is running ${wait ? "synchronously" : "asynchronously"} and can take a few minutes. ${estimateText}`;
|
|
21766
|
-
const structured = toStructured(result);
|
|
21767
|
-
const structuredContent = structured && typeof structured === "object" ? {
|
|
21768
|
-
...structured,
|
|
21769
|
-
wait,
|
|
21770
|
-
note,
|
|
21771
|
-
...estimate ? {
|
|
21772
|
-
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
21773
|
-
estimate_basis: estimate.basis
|
|
21774
|
-
} : {}
|
|
21775
|
-
} : {
|
|
21776
|
-
wait,
|
|
21777
|
-
note,
|
|
21778
|
-
...estimate ? {
|
|
21779
|
-
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
21780
|
-
estimate_basis: estimate.basis
|
|
21781
|
-
} : {}
|
|
21782
|
-
};
|
|
21783
|
-
return {
|
|
21784
|
-
content: [
|
|
21785
|
-
{
|
|
21786
|
-
type: "text",
|
|
21787
|
-
text: `${note}
|
|
21788
|
-
${formatContent(result)}`
|
|
21789
|
-
}
|
|
21790
|
-
],
|
|
21791
|
-
structuredContent
|
|
21792
|
-
};
|
|
22060
|
+
return runGraphIngestWithPreflight(projectId, wait);
|
|
21793
22061
|
}
|
|
21794
22062
|
);
|
|
21795
22063
|
registerTool(
|
|
@@ -24406,6 +24674,7 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
|
|
|
24406
24674
|
const instructionsLine = result.instructions ? `
|
|
24407
24675
|
|
|
24408
24676
|
[INSTRUCTIONS] ${result.instructions}` : "";
|
|
24677
|
+
const suggestedRulesLine = generateSuggestedRulesNotice(result);
|
|
24409
24678
|
const contextRulesLine = `
|
|
24410
24679
|
|
|
24411
24680
|
${CONTEXT_CALL_REMINDER}`;
|
|
@@ -24418,6 +24687,7 @@ ${rulesWarningLine}` : "",
|
|
|
24418
24687
|
versionWarningLine ? `
|
|
24419
24688
|
|
|
24420
24689
|
${versionWarningLine}` : "",
|
|
24690
|
+
suggestedRulesLine,
|
|
24421
24691
|
contextPressureWarning,
|
|
24422
24692
|
semanticHints,
|
|
24423
24693
|
instructionsLine,
|
|
@@ -26150,7 +26420,8 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26150
26420
|
workspace_id: workspaceId,
|
|
26151
26421
|
project_id: projectId,
|
|
26152
26422
|
content: input.content,
|
|
26153
|
-
importance
|
|
26423
|
+
importance,
|
|
26424
|
+
tags: input.tags
|
|
26154
26425
|
});
|
|
26155
26426
|
const rememberHint = getCaptureHint("preference");
|
|
26156
26427
|
const resultWithHint = { ...result, hint: rememberHint };
|
|
@@ -26787,7 +27058,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26787
27058
|
order: external_exports.number().optional().describe("Task order within plan"),
|
|
26788
27059
|
task_ids: external_exports.array(external_exports.string().uuid()).optional().describe("Task IDs for reorder_tasks"),
|
|
26789
27060
|
blocked_reason: external_exports.string().optional().describe("Reason when task is blocked"),
|
|
26790
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Tags for task"),
|
|
27061
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Tags for event or task categorization"),
|
|
26791
27062
|
// Batch import params
|
|
26792
27063
|
events: external_exports.array(
|
|
26793
27064
|
external_exports.object({
|
|
@@ -26824,7 +27095,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26824
27095
|
diagram_id: external_exports.string().uuid().optional().describe("Diagram ID for get_diagram/update_diagram/delete_diagram"),
|
|
26825
27096
|
diagram_type: external_exports.enum(["flowchart", "sequence", "class", "er", "gantt", "mindmap", "pie", "other"]).optional().describe("Mermaid diagram type"),
|
|
26826
27097
|
// Doc params
|
|
26827
|
-
doc_id: external_exports.string().
|
|
27098
|
+
doc_id: external_exports.string().optional().describe("Doc ID for get_doc/update_doc/delete_doc. For get_doc, accepts UUID or title/query text."),
|
|
26828
27099
|
doc_type: external_exports.enum(["roadmap", "spec", "general"]).optional().describe("Document type"),
|
|
26829
27100
|
milestones: external_exports.array(
|
|
26830
27101
|
external_exports.object({
|
|
@@ -26859,6 +27130,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26859
27130
|
event_type: input.event_type,
|
|
26860
27131
|
title: input.title,
|
|
26861
27132
|
content: input.content,
|
|
27133
|
+
tags: input.tags,
|
|
26862
27134
|
metadata: input.metadata,
|
|
26863
27135
|
provenance: input.provenance,
|
|
26864
27136
|
code_refs: input.code_refs
|
|
@@ -27020,13 +27292,42 @@ ${formatContent(result)}`
|
|
|
27020
27292
|
if (!input.query) {
|
|
27021
27293
|
return errorResult("search requires: query");
|
|
27022
27294
|
}
|
|
27023
|
-
const
|
|
27024
|
-
|
|
27025
|
-
|
|
27295
|
+
const [memoryResult, docsResult] = await Promise.all([
|
|
27296
|
+
client.memorySearch({
|
|
27297
|
+
workspace_id: workspaceId,
|
|
27298
|
+
project_id: projectId,
|
|
27299
|
+
query: input.query,
|
|
27300
|
+
limit: input.limit
|
|
27301
|
+
}),
|
|
27302
|
+
workspaceId ? client.docsList({
|
|
27303
|
+
workspace_id: workspaceId,
|
|
27304
|
+
project_id: projectId,
|
|
27305
|
+
per_page: Math.max(10, Math.min(input.limit ?? 10, 25))
|
|
27306
|
+
}) : Promise.resolve(void 0)
|
|
27307
|
+
]);
|
|
27308
|
+
const memoryItems = extractMemorySearchResults(memoryResult);
|
|
27309
|
+
const docsItems = extractCollectionArray(docsResult ?? {}) ?? [];
|
|
27310
|
+
const docMatches = rankDocsForQueryMatches(
|
|
27311
|
+
docsItems,
|
|
27312
|
+
input.query,
|
|
27313
|
+
Math.max(1, Math.min(input.limit ?? 10, 25))
|
|
27314
|
+
);
|
|
27315
|
+
const hybridResults = buildHybridMemoryDocResults(
|
|
27316
|
+
memoryItems,
|
|
27317
|
+
docMatches,
|
|
27318
|
+
Math.max(1, Math.min(input.limit ?? 10, 25))
|
|
27319
|
+
);
|
|
27320
|
+
const outputText = formatContent({
|
|
27026
27321
|
query: input.query,
|
|
27027
|
-
|
|
27322
|
+
results: hybridResults,
|
|
27323
|
+
memory_results: memoryItems,
|
|
27324
|
+
doc_matches: docMatches.map((entry) => ({
|
|
27325
|
+
...entry.doc,
|
|
27326
|
+
match_score: entry.score,
|
|
27327
|
+
exact_match: entry.exact,
|
|
27328
|
+
match_source: entry.source
|
|
27329
|
+
}))
|
|
27028
27330
|
});
|
|
27029
|
-
const outputText = formatContent(result);
|
|
27030
27331
|
trackToolTokenSavings(client, "memory_search", outputText, {
|
|
27031
27332
|
workspace_id: workspaceId,
|
|
27032
27333
|
project_id: projectId
|
|
@@ -27402,17 +27703,80 @@ ${formatContent(result)}`
|
|
|
27402
27703
|
if (!input.doc_id) {
|
|
27403
27704
|
return errorResult("get_doc requires: doc_id");
|
|
27404
27705
|
}
|
|
27405
|
-
const
|
|
27706
|
+
const directDocId = normalizeUuid(input.doc_id);
|
|
27707
|
+
if (directDocId) {
|
|
27708
|
+
const getDocResult = await client.docsGet({ doc_id: directDocId });
|
|
27709
|
+
return {
|
|
27710
|
+
content: [{ type: "text", text: formatContent(getDocResult) }]
|
|
27711
|
+
};
|
|
27712
|
+
}
|
|
27713
|
+
if (!workspaceId) {
|
|
27714
|
+
return errorResult(
|
|
27715
|
+
"get_doc title/query lookups require workspace_id. Call session_init first or pass a doc UUID."
|
|
27716
|
+
);
|
|
27717
|
+
}
|
|
27718
|
+
const candidateProjectIds = [projectId, explicitProjectId, void 0];
|
|
27719
|
+
const fallback = await findDocsFallback(
|
|
27720
|
+
workspaceId,
|
|
27721
|
+
candidateProjectIds,
|
|
27722
|
+
input.doc_id,
|
|
27723
|
+
Math.max(10, Math.min(input.limit ?? 20, 30))
|
|
27724
|
+
);
|
|
27725
|
+
const rankedMatches = rankDocsForQueryMatches(
|
|
27726
|
+
fallback?.docs ?? [],
|
|
27727
|
+
input.doc_id,
|
|
27728
|
+
Math.max(1, Math.min(input.limit ?? 10, 10))
|
|
27729
|
+
);
|
|
27730
|
+
const resolved = selectResolvedDocMatch(rankedMatches);
|
|
27731
|
+
if (resolved) {
|
|
27732
|
+
const resolvedDocId = String(resolved.doc?.id ?? "");
|
|
27733
|
+
if (normalizeUuid(resolvedDocId)) {
|
|
27734
|
+
const getDocResult = await client.docsGet({ doc_id: resolvedDocId });
|
|
27735
|
+
return {
|
|
27736
|
+
content: [
|
|
27737
|
+
{
|
|
27738
|
+
type: "text",
|
|
27739
|
+
text: `Resolved doc query "${input.doc_id}" to doc ID ${resolvedDocId}.
|
|
27740
|
+
|
|
27741
|
+
${formatContent(
|
|
27742
|
+
getDocResult
|
|
27743
|
+
)}`
|
|
27744
|
+
}
|
|
27745
|
+
]
|
|
27746
|
+
};
|
|
27747
|
+
}
|
|
27748
|
+
}
|
|
27749
|
+
const topMatches = rankedMatches.slice(0, 5).map((entry) => ({
|
|
27750
|
+
...entry.doc ?? {},
|
|
27751
|
+
match_score: entry.score,
|
|
27752
|
+
exact_match: entry.exact,
|
|
27753
|
+
match_source: entry.source
|
|
27754
|
+
}));
|
|
27755
|
+
const noMatchMessage = topMatches.length > 0 ? `Could not resolve "${input.doc_id}" to a single doc confidently.` : `No docs found matching "${input.doc_id}".`;
|
|
27406
27756
|
return {
|
|
27407
|
-
content: [
|
|
27757
|
+
content: [
|
|
27758
|
+
{
|
|
27759
|
+
type: "text",
|
|
27760
|
+
text: `${noMatchMessage}
|
|
27761
|
+
|
|
27762
|
+
${formatContent({
|
|
27763
|
+
query: input.doc_id,
|
|
27764
|
+
doc_matches: topMatches
|
|
27765
|
+
})}`
|
|
27766
|
+
}
|
|
27767
|
+
]
|
|
27408
27768
|
};
|
|
27409
27769
|
}
|
|
27410
27770
|
case "update_doc": {
|
|
27411
27771
|
if (!input.doc_id) {
|
|
27412
27772
|
return errorResult("update_doc requires: doc_id");
|
|
27413
27773
|
}
|
|
27774
|
+
const updateDocId = normalizeUuid(input.doc_id);
|
|
27775
|
+
if (!updateDocId) {
|
|
27776
|
+
return errorResult("update_doc requires a valid UUID doc_id.");
|
|
27777
|
+
}
|
|
27414
27778
|
const updateDocResult = await client.docsUpdate({
|
|
27415
|
-
doc_id:
|
|
27779
|
+
doc_id: updateDocId,
|
|
27416
27780
|
title: input.title,
|
|
27417
27781
|
content: input.content,
|
|
27418
27782
|
doc_type: input.doc_type,
|
|
@@ -27426,7 +27790,11 @@ ${formatContent(result)}`
|
|
|
27426
27790
|
if (!input.doc_id) {
|
|
27427
27791
|
return errorResult("delete_doc requires: doc_id");
|
|
27428
27792
|
}
|
|
27429
|
-
const
|
|
27793
|
+
const deleteDocId = normalizeUuid(input.doc_id);
|
|
27794
|
+
if (!deleteDocId) {
|
|
27795
|
+
return errorResult("delete_doc requires a valid UUID doc_id.");
|
|
27796
|
+
}
|
|
27797
|
+
const deleteDocResult = await client.docsDelete({ doc_id: deleteDocId });
|
|
27430
27798
|
return {
|
|
27431
27799
|
content: [{ type: "text", text: formatContent(deleteDocResult) }]
|
|
27432
27800
|
};
|
|
@@ -27841,13 +28209,7 @@ ${formatContent(result)}`
|
|
|
27841
28209
|
if (!projectId) {
|
|
27842
28210
|
return errorResult("ingest requires: project_id");
|
|
27843
28211
|
}
|
|
27844
|
-
|
|
27845
|
-
project_id: projectId,
|
|
27846
|
-
wait: input.wait
|
|
27847
|
-
});
|
|
27848
|
-
return {
|
|
27849
|
-
content: [{ type: "text", text: formatContent(result) }]
|
|
27850
|
-
};
|
|
28212
|
+
return runGraphIngestWithPreflight(projectId, input.wait ?? false);
|
|
27851
28213
|
}
|
|
27852
28214
|
case "circular_dependencies": {
|
|
27853
28215
|
if (!projectId) {
|
|
@@ -31705,6 +32067,7 @@ init_hooks_config();
|
|
|
31705
32067
|
init_files();
|
|
31706
32068
|
var EDITOR_LABELS = {
|
|
31707
32069
|
codex: "Codex CLI",
|
|
32070
|
+
opencode: "OpenCode",
|
|
31708
32071
|
claude: "Claude Code",
|
|
31709
32072
|
cursor: "Cursor / VS Code",
|
|
31710
32073
|
cline: "Cline",
|
|
@@ -31714,7 +32077,7 @@ var EDITOR_LABELS = {
|
|
|
31714
32077
|
antigravity: "Antigravity (Google)"
|
|
31715
32078
|
};
|
|
31716
32079
|
function supportsProjectMcpConfig(editor) {
|
|
31717
|
-
return editor === "cursor" || editor === "claude" || editor === "kilo" || editor === "roo" || editor === "antigravity";
|
|
32080
|
+
return editor === "opencode" || editor === "cursor" || editor === "claude" || editor === "kilo" || editor === "roo" || editor === "antigravity";
|
|
31718
32081
|
}
|
|
31719
32082
|
function normalizeInput(value) {
|
|
31720
32083
|
return value.trim();
|
|
@@ -31960,6 +32323,28 @@ async function isCodexInstalled() {
|
|
|
31960
32323
|
].filter((candidate) => Boolean(candidate));
|
|
31961
32324
|
return anyPathExists(candidates);
|
|
31962
32325
|
}
|
|
32326
|
+
function openCodeConfigPath() {
|
|
32327
|
+
const home = homedir6();
|
|
32328
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
32329
|
+
if (process.platform === "win32") {
|
|
32330
|
+
const appData = process.env.APPDATA || path8.join(home, "AppData", "Roaming");
|
|
32331
|
+
return path8.join(appData, "opencode", "opencode.json");
|
|
32332
|
+
}
|
|
32333
|
+
const configRoot = xdgConfigHome || path8.join(home, ".config");
|
|
32334
|
+
return path8.join(configRoot, "opencode", "opencode.json");
|
|
32335
|
+
}
|
|
32336
|
+
async function isOpenCodeInstalled() {
|
|
32337
|
+
const configPath = openCodeConfigPath();
|
|
32338
|
+
const configDir = path8.dirname(configPath);
|
|
32339
|
+
const home = homedir6();
|
|
32340
|
+
const candidates = [
|
|
32341
|
+
configDir,
|
|
32342
|
+
configPath,
|
|
32343
|
+
path8.join(home, ".bun", "bin", "opencode"),
|
|
32344
|
+
path8.join(home, ".local", "bin", "opencode")
|
|
32345
|
+
];
|
|
32346
|
+
return anyPathExists(candidates);
|
|
32347
|
+
}
|
|
31963
32348
|
async function isClaudeInstalled() {
|
|
31964
32349
|
const home = homedir6();
|
|
31965
32350
|
const candidates = [path8.join(home, ".claude"), path8.join(home, ".config", "claude")];
|
|
@@ -32031,10 +32416,12 @@ async function isAntigravityInstalled() {
|
|
|
32031
32416
|
const localApp = process.env.LOCALAPPDATA;
|
|
32032
32417
|
const programFiles = process.env.ProgramFiles;
|
|
32033
32418
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
32034
|
-
if (localApp)
|
|
32419
|
+
if (localApp)
|
|
32420
|
+
candidates.push(path8.join(localApp, "Programs", "Antigravity", "Antigravity.exe"));
|
|
32035
32421
|
if (localApp) candidates.push(path8.join(localApp, "Antigravity", "Antigravity.exe"));
|
|
32036
32422
|
if (programFiles) candidates.push(path8.join(programFiles, "Antigravity", "Antigravity.exe"));
|
|
32037
|
-
if (programFilesX86)
|
|
32423
|
+
if (programFilesX86)
|
|
32424
|
+
candidates.push(path8.join(programFilesX86, "Antigravity", "Antigravity.exe"));
|
|
32038
32425
|
} else {
|
|
32039
32426
|
candidates.push("/usr/bin/antigravity");
|
|
32040
32427
|
candidates.push("/usr/local/bin/antigravity");
|
|
@@ -32047,6 +32434,8 @@ async function isEditorInstalled(editor) {
|
|
|
32047
32434
|
switch (editor) {
|
|
32048
32435
|
case "codex":
|
|
32049
32436
|
return isCodexInstalled();
|
|
32437
|
+
case "opencode":
|
|
32438
|
+
return isOpenCodeInstalled();
|
|
32050
32439
|
case "claude":
|
|
32051
32440
|
return isClaudeInstalled();
|
|
32052
32441
|
case "cursor":
|
|
@@ -32066,6 +32455,8 @@ async function isEditorInstalled(editor) {
|
|
|
32066
32455
|
}
|
|
32067
32456
|
}
|
|
32068
32457
|
var IS_WINDOWS = process.platform === "win32";
|
|
32458
|
+
var DEFAULT_CONTEXTSTREAM_API_URL = "https://api.contextstream.io";
|
|
32459
|
+
var OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
32069
32460
|
function escapeTomlString(value) {
|
|
32070
32461
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
32071
32462
|
}
|
|
@@ -32112,6 +32503,26 @@ function buildContextStreamVsCodeServer(params) {
|
|
|
32112
32503
|
env
|
|
32113
32504
|
};
|
|
32114
32505
|
}
|
|
32506
|
+
function buildContextStreamOpenCodeEnvironment(params) {
|
|
32507
|
+
const environment = {
|
|
32508
|
+
CONTEXTSTREAM_API_KEY: "{env:CONTEXTSTREAM_API_KEY}"
|
|
32509
|
+
};
|
|
32510
|
+
if (normalizeApiUrl(params.apiUrl) !== DEFAULT_CONTEXTSTREAM_API_URL) {
|
|
32511
|
+
environment.CONTEXTSTREAM_API_URL = params.apiUrl;
|
|
32512
|
+
}
|
|
32513
|
+
if (params.contextPackEnabled === false) {
|
|
32514
|
+
environment.CONTEXTSTREAM_CONTEXT_PACK = "false";
|
|
32515
|
+
}
|
|
32516
|
+
return environment;
|
|
32517
|
+
}
|
|
32518
|
+
function buildContextStreamOpenCodeLocalServer(params) {
|
|
32519
|
+
return {
|
|
32520
|
+
type: "local",
|
|
32521
|
+
command: ["npx", "-y", "contextstream-mcp"],
|
|
32522
|
+
environment: buildContextStreamOpenCodeEnvironment(params),
|
|
32523
|
+
enabled: true
|
|
32524
|
+
};
|
|
32525
|
+
}
|
|
32115
32526
|
function stripJsonComments(input) {
|
|
32116
32527
|
return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
32117
32528
|
}
|
|
@@ -32170,6 +32581,32 @@ async function upsertJsonVsCodeMcpConfig(filePath, server) {
|
|
|
32170
32581
|
if (!exists) return "created";
|
|
32171
32582
|
return before === after ? "skipped" : "updated";
|
|
32172
32583
|
}
|
|
32584
|
+
async function upsertOpenCodeMcpConfig(filePath, server) {
|
|
32585
|
+
await fs7.mkdir(path8.dirname(filePath), { recursive: true });
|
|
32586
|
+
const exists = await fileExists(filePath);
|
|
32587
|
+
let root = {};
|
|
32588
|
+
if (exists) {
|
|
32589
|
+
const raw = await fs7.readFile(filePath, "utf8").catch(() => "");
|
|
32590
|
+
const parsed = tryParseJsonLike(raw);
|
|
32591
|
+
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
32592
|
+
root = parsed.value;
|
|
32593
|
+
}
|
|
32594
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) root = {};
|
|
32595
|
+
if (!root.mcp || typeof root.mcp !== "object" || Array.isArray(root.mcp)) root.mcp = {};
|
|
32596
|
+
const before = JSON.stringify({
|
|
32597
|
+
schema: root.$schema ?? null,
|
|
32598
|
+
contextstream: root.mcp.contextstream ?? null
|
|
32599
|
+
});
|
|
32600
|
+
root.$schema = OPENCODE_CONFIG_SCHEMA_URL;
|
|
32601
|
+
root.mcp.contextstream = server;
|
|
32602
|
+
const after = JSON.stringify({
|
|
32603
|
+
schema: root.$schema ?? null,
|
|
32604
|
+
contextstream: root.mcp.contextstream ?? null
|
|
32605
|
+
});
|
|
32606
|
+
await fs7.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
32607
|
+
if (!exists) return "created";
|
|
32608
|
+
return before === after ? "skipped" : "updated";
|
|
32609
|
+
}
|
|
32173
32610
|
function claudeDesktopConfigPath() {
|
|
32174
32611
|
const home = homedir6();
|
|
32175
32612
|
if (process.platform === "darwin") {
|
|
@@ -32374,9 +32811,7 @@ async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun
|
|
|
32374
32811
|
}
|
|
32375
32812
|
console.log("\nProject selection (current directory):");
|
|
32376
32813
|
options.forEach((opt, i) => console.log(` ${i + 1}) ${opt.label}`));
|
|
32377
|
-
const choiceRaw = normalizeInput(
|
|
32378
|
-
await rl.question(`Choose [1-${options.length}] (default 1): `)
|
|
32379
|
-
);
|
|
32814
|
+
const choiceRaw = normalizeInput(await rl.question(`Choose [1-${options.length}] (default 1): `));
|
|
32380
32815
|
const choiceNum = Number.parseInt(choiceRaw || "1", 10);
|
|
32381
32816
|
const selected = Number.isFinite(choiceNum) ? options[choiceNum - 1] : options[0];
|
|
32382
32817
|
if (!selected || selected.kind === "skip") {
|
|
@@ -32442,7 +32877,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32442
32877
|
}
|
|
32443
32878
|
}
|
|
32444
32879
|
if (!projectId) {
|
|
32445
|
-
console.log(
|
|
32880
|
+
console.log(
|
|
32881
|
+
`${colors.yellow}! Could not resolve project ID for ${projectName}${colors.reset}`
|
|
32882
|
+
);
|
|
32446
32883
|
return;
|
|
32447
32884
|
}
|
|
32448
32885
|
} catch (err) {
|
|
@@ -32460,7 +32897,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32460
32897
|
console.log(`${colors.dim}No indexable files found${colors.reset}`);
|
|
32461
32898
|
return;
|
|
32462
32899
|
} else {
|
|
32463
|
-
console.log(
|
|
32900
|
+
console.log(
|
|
32901
|
+
`${colors.dim}Found ${totalFiles.toLocaleString()} files for indexing${colors.reset}`
|
|
32902
|
+
);
|
|
32464
32903
|
}
|
|
32465
32904
|
} catch {
|
|
32466
32905
|
console.log(`${colors.dim}Scanning files...${colors.reset}`);
|
|
@@ -32480,7 +32919,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32480
32919
|
const percentage = (progress * 100).toFixed(1);
|
|
32481
32920
|
const speed = filesPerSec.toFixed(1);
|
|
32482
32921
|
const size = formatBytes(bytesIndexed);
|
|
32483
|
-
process.stdout.write(
|
|
32922
|
+
process.stdout.write(
|
|
32923
|
+
`\r${colors.cyan}${spinner}${colors.reset} ${progressBar} ${colors.bright}${percentage}%${colors.reset} | ${colors.green}${filesIndexed.toLocaleString()}${colors.reset}/${totalFiles.toLocaleString()} files | ${colors.magenta}${size}${colors.reset} | ${colors.blue}${speed} files/s${colors.reset} `
|
|
32924
|
+
);
|
|
32484
32925
|
};
|
|
32485
32926
|
const progressInterval = setInterval(updateProgress, 80);
|
|
32486
32927
|
const ingestWithRetry = async (batch, maxRetries = 3) => {
|
|
@@ -32798,7 +33239,7 @@ Code: ${device.user_code}`);
|
|
|
32798
33239
|
);
|
|
32799
33240
|
}
|
|
32800
33241
|
}
|
|
32801
|
-
const NO_HOOKS_EDITORS2 = ["codex", "aider", "antigravity"];
|
|
33242
|
+
const NO_HOOKS_EDITORS2 = ["codex", "opencode", "aider", "antigravity"];
|
|
32802
33243
|
const getModeForEditor = (editor) => NO_HOOKS_EDITORS2.includes(editor) ? "full" : "bootstrap";
|
|
32803
33244
|
const detectedPlanName = await client.getPlanName();
|
|
32804
33245
|
const detectedGraphTier = await client.getGraphTier();
|
|
@@ -32809,7 +33250,9 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32809
33250
|
const contextPackEnabled = !!detectedPlanName && ["pro", "team", "enterprise"].some((p) => detectedPlanName.toLowerCase().includes(p));
|
|
32810
33251
|
console.log("\nAuto-Update:");
|
|
32811
33252
|
console.log(" When enabled, ContextStream will automatically update to the latest version");
|
|
32812
|
-
console.log(
|
|
33253
|
+
console.log(
|
|
33254
|
+
" on new sessions (checks daily). You can disable this if you prefer manual updates."
|
|
33255
|
+
);
|
|
32813
33256
|
const currentAutoUpdate = isAutoUpdateEnabled();
|
|
32814
33257
|
const autoUpdateChoice = normalizeInput(
|
|
32815
33258
|
await rl.question(`Enable auto-update? [${currentAutoUpdate ? "Y/n" : "y/N"}]: `)
|
|
@@ -32823,6 +33266,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32823
33266
|
}
|
|
32824
33267
|
const editors = [
|
|
32825
33268
|
"codex",
|
|
33269
|
+
"opencode",
|
|
32826
33270
|
"claude",
|
|
32827
33271
|
"cursor",
|
|
32828
33272
|
"cline",
|
|
@@ -32896,7 +33340,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32896
33340
|
const mcpScope = mcpChoice === "2" && hasCodex && !hasProjectMcpEditors ? "skip" : mcpChoice === "4" ? "skip" : mcpChoice === "1" ? "global" : mcpChoice === "2" ? "project" : "both";
|
|
32897
33341
|
const mcpServer = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
32898
33342
|
const mcpServerClaude = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
33343
|
+
const mcpServerOpenCode = buildContextStreamOpenCodeLocalServer({
|
|
33344
|
+
apiUrl,
|
|
33345
|
+
apiKey,
|
|
33346
|
+
contextPackEnabled
|
|
33347
|
+
});
|
|
32899
33348
|
const vsCodeServer = buildContextStreamVsCodeServer({ apiUrl, apiKey, contextPackEnabled });
|
|
33349
|
+
let hasPrintedOpenCodeEnvNote = false;
|
|
33350
|
+
const printOpenCodeEnvNote = () => {
|
|
33351
|
+
if (hasPrintedOpenCodeEnvNote) return;
|
|
33352
|
+
hasPrintedOpenCodeEnvNote = true;
|
|
33353
|
+
console.log(
|
|
33354
|
+
" OpenCode reads CONTEXTSTREAM_API_KEY from your environment. Export it before launching OpenCode."
|
|
33355
|
+
);
|
|
33356
|
+
};
|
|
32900
33357
|
const needsGlobalMcpConfig = mcpScope === "global" || mcpScope === "both" || mcpScope === "project" && hasCodex;
|
|
32901
33358
|
if (needsGlobalMcpConfig) {
|
|
32902
33359
|
console.log("\nInstalling global MCP config...");
|
|
@@ -32948,6 +33405,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32948
33405
|
);
|
|
32949
33406
|
continue;
|
|
32950
33407
|
}
|
|
33408
|
+
if (editor === "opencode") {
|
|
33409
|
+
const filePath = openCodeConfigPath();
|
|
33410
|
+
if (dryRun) {
|
|
33411
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
33412
|
+
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
33413
|
+
printOpenCodeEnvNote();
|
|
33414
|
+
continue;
|
|
33415
|
+
}
|
|
33416
|
+
const status = await upsertOpenCodeMcpConfig(filePath, mcpServerOpenCode);
|
|
33417
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status });
|
|
33418
|
+
console.log(`- ${EDITOR_LABELS[editor]}: ${status} ${filePath}`);
|
|
33419
|
+
printOpenCodeEnvNote();
|
|
33420
|
+
continue;
|
|
33421
|
+
}
|
|
32951
33422
|
if (editor === "cursor") {
|
|
32952
33423
|
const filePath = path8.join(homedir6(), ".cursor", "mcp.json");
|
|
32953
33424
|
if (dryRun) {
|
|
@@ -32990,6 +33461,8 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32990
33461
|
kilo: "kilo",
|
|
32991
33462
|
codex: null,
|
|
32992
33463
|
// No hooks API
|
|
33464
|
+
opencode: null,
|
|
33465
|
+
// No hooks API
|
|
32993
33466
|
aider: null,
|
|
32994
33467
|
// No hooks API
|
|
32995
33468
|
antigravity: null
|
|
@@ -33030,6 +33503,12 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
33030
33503
|
if (scope === "global" || scope === "both") {
|
|
33031
33504
|
console.log("\nInstalling global rules...");
|
|
33032
33505
|
for (const editor of configuredEditors) {
|
|
33506
|
+
if (editor === "opencode") {
|
|
33507
|
+
console.log(
|
|
33508
|
+
`- ${EDITOR_LABELS[editor]}: rules are not auto-generated yet (MCP config only).`
|
|
33509
|
+
);
|
|
33510
|
+
continue;
|
|
33511
|
+
}
|
|
33033
33512
|
const filePath = globalRulesPathForEditor(editor);
|
|
33034
33513
|
if (!filePath) {
|
|
33035
33514
|
console.log(
|
|
@@ -33170,6 +33649,21 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33170
33649
|
}
|
|
33171
33650
|
continue;
|
|
33172
33651
|
}
|
|
33652
|
+
if (editor === "opencode") {
|
|
33653
|
+
const openCodePath = path8.join(projectPath, "opencode.json");
|
|
33654
|
+
if (dryRun) {
|
|
33655
|
+
writeActions.push({
|
|
33656
|
+
kind: "mcp-config",
|
|
33657
|
+
target: openCodePath,
|
|
33658
|
+
status: "dry-run"
|
|
33659
|
+
});
|
|
33660
|
+
} else {
|
|
33661
|
+
const status = await upsertOpenCodeMcpConfig(openCodePath, mcpServerOpenCode);
|
|
33662
|
+
writeActions.push({ kind: "mcp-config", target: openCodePath, status });
|
|
33663
|
+
}
|
|
33664
|
+
printOpenCodeEnvNote();
|
|
33665
|
+
continue;
|
|
33666
|
+
}
|
|
33173
33667
|
if (editor === "kilo") {
|
|
33174
33668
|
const kiloPath = path8.join(projectPath, ".kilocode", "mcp.json");
|
|
33175
33669
|
if (dryRun) {
|
|
@@ -33201,6 +33695,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33201
33695
|
for (const editor of selectedEditors) {
|
|
33202
33696
|
if (scope !== "project" && scope !== "both") continue;
|
|
33203
33697
|
if (!configuredEditors.includes(editor)) continue;
|
|
33698
|
+
if (editor === "opencode") continue;
|
|
33204
33699
|
const rule = generateRuleContent(editor, {
|
|
33205
33700
|
workspaceName,
|
|
33206
33701
|
workspaceId: workspaceId && workspaceId !== "dry-run" ? workspaceId : void 0,
|
|
@@ -33240,11 +33735,15 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33240
33735
|
console.log("PROJECT INDEXING");
|
|
33241
33736
|
console.log("\u2500".repeat(60));
|
|
33242
33737
|
if (filesIndexed === 0) {
|
|
33243
|
-
console.log(
|
|
33738
|
+
console.log(
|
|
33739
|
+
"Indexing enables semantic code search and AI-powered graph knowledge for rich AI context."
|
|
33740
|
+
);
|
|
33244
33741
|
} else {
|
|
33245
33742
|
console.log("Your project index is stale and could use a refresh.");
|
|
33246
33743
|
}
|
|
33247
|
-
console.log(
|
|
33744
|
+
console.log(
|
|
33745
|
+
"Powered by our blazing-fast Rust engine, indexing typically takes under a minute,"
|
|
33746
|
+
);
|
|
33248
33747
|
console.log("though larger projects may take a bit longer.\n");
|
|
33249
33748
|
console.log("Your code is private and securely stored.\n");
|
|
33250
33749
|
const indexChoice = normalizeInput(
|
|
@@ -33259,7 +33758,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33259
33758
|
if (indexingEnabled) {
|
|
33260
33759
|
await indexProjectWithProgress(client, process.cwd(), cwdConfig.workspace_id);
|
|
33261
33760
|
} else {
|
|
33262
|
-
console.log(
|
|
33761
|
+
console.log(
|
|
33762
|
+
"\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>"
|
|
33763
|
+
);
|
|
33263
33764
|
}
|
|
33264
33765
|
}
|
|
33265
33766
|
} catch {
|
|
@@ -33269,8 +33770,12 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33269
33770
|
console.log("\n" + "\u2500".repeat(60));
|
|
33270
33771
|
console.log("PROJECT INDEXING");
|
|
33271
33772
|
console.log("\u2500".repeat(60));
|
|
33272
|
-
console.log(
|
|
33273
|
-
|
|
33773
|
+
console.log(
|
|
33774
|
+
"Indexing enables semantic code search and AI-powered graph knowledge for rich AI context."
|
|
33775
|
+
);
|
|
33776
|
+
console.log(
|
|
33777
|
+
"Powered by our blazing-fast Rust engine, indexing typically takes under a minute,"
|
|
33778
|
+
);
|
|
33274
33779
|
console.log("though larger projects may take a bit longer.\n");
|
|
33275
33780
|
console.log("Your code is private and securely stored.\n");
|
|
33276
33781
|
const indexChoice = normalizeInput(
|
|
@@ -33292,7 +33797,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33292
33797
|
await indexProjectWithProgress(client, projectPath, workspaceId);
|
|
33293
33798
|
}
|
|
33294
33799
|
} else {
|
|
33295
|
-
console.log(
|
|
33800
|
+
console.log(
|
|
33801
|
+
"\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>"
|
|
33802
|
+
);
|
|
33296
33803
|
}
|
|
33297
33804
|
}
|
|
33298
33805
|
console.log("\nDone.");
|
|
@@ -33313,7 +33820,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33313
33820
|
"- For UI-based MCP setup (Cline/Kilo/Roo global), see https://contextstream.io/docs/mcp"
|
|
33314
33821
|
);
|
|
33315
33822
|
console.log("");
|
|
33316
|
-
console.log(
|
|
33823
|
+
console.log(
|
|
33824
|
+
"You're all set! ContextStream gives your AI persistent memory, semantic code search, and cross-session context."
|
|
33825
|
+
);
|
|
33317
33826
|
console.log("More at: https://contextstream.io/docs/mcp");
|
|
33318
33827
|
} finally {
|
|
33319
33828
|
rl.close();
|