@contextstream/mcp-server 0.4.64 → 0.4.66
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 +950 -121
- 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,46 @@ 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
|
+
}
|
|
12922
|
+
function extractEventTags(item) {
|
|
12923
|
+
const tags = [];
|
|
12924
|
+
if (Array.isArray(item.tags)) {
|
|
12925
|
+
tags.push(...item.tags.filter((t) => typeof t === "string"));
|
|
12926
|
+
}
|
|
12927
|
+
const metaTags = item.metadata?.tags;
|
|
12928
|
+
if (Array.isArray(metaTags)) {
|
|
12929
|
+
tags.push(...metaTags.filter((t) => typeof t === "string"));
|
|
12930
|
+
}
|
|
12931
|
+
return tags;
|
|
12932
|
+
}
|
|
12933
|
+
function extractEffectiveEventType(item) {
|
|
12934
|
+
for (const field of ["event_type", "node_type", "type"]) {
|
|
12935
|
+
const val = item[field];
|
|
12936
|
+
if (typeof val === "string" && val.trim()) return val.trim();
|
|
12937
|
+
}
|
|
12938
|
+
for (const field of ["original_type", "node_type", "event_type", "type"]) {
|
|
12939
|
+
const val = item.metadata?.[field];
|
|
12940
|
+
if (typeof val === "string" && val.trim()) return val.trim();
|
|
12941
|
+
}
|
|
12942
|
+
return "unknown";
|
|
12943
|
+
}
|
|
12944
|
+
function isLessonResult(item) {
|
|
12945
|
+
const effectiveType = extractEffectiveEventType(item);
|
|
12946
|
+
if (effectiveType === "lesson") return true;
|
|
12947
|
+
const tags = extractEventTags(item);
|
|
12948
|
+
if (tags.some((t) => t === "lesson" || t === "lesson_system")) return true;
|
|
12949
|
+
const content = typeof item.content === "string" ? item.content : "";
|
|
12950
|
+
if (content.includes("### Prevention") && content.includes("### Trigger")) return true;
|
|
12951
|
+
return false;
|
|
12952
|
+
}
|
|
12916
12953
|
function pickString(value) {
|
|
12917
12954
|
if (typeof value !== "string") return null;
|
|
12918
12955
|
const trimmed = value.trim();
|
|
@@ -13448,7 +13485,12 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13448
13485
|
if (!body.content || body.content.trim().length === 0) {
|
|
13449
13486
|
throw new Error("content is required and cannot be empty");
|
|
13450
13487
|
}
|
|
13451
|
-
|
|
13488
|
+
const normalizedTags = normalizeTags(body.tags);
|
|
13489
|
+
const apiBody = {
|
|
13490
|
+
...withDefaults,
|
|
13491
|
+
...normalizedTags ? { tags: normalizedTags } : {}
|
|
13492
|
+
};
|
|
13493
|
+
return request(this.config, "/memory/events", { body: apiBody });
|
|
13452
13494
|
}
|
|
13453
13495
|
bulkIngestEvents(body) {
|
|
13454
13496
|
return request(this.config, "/memory/events/ingest", { body: this.withDefaults(body) });
|
|
@@ -13461,6 +13503,8 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13461
13503
|
const query = new URLSearchParams();
|
|
13462
13504
|
if (params?.limit) query.set("limit", String(params.limit));
|
|
13463
13505
|
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
13506
|
+
if (params?.event_type) query.set("event_type", params.event_type);
|
|
13507
|
+
if (params?.tags && params.tags.length > 0) query.set("tags", params.tags.join(","));
|
|
13464
13508
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
13465
13509
|
return request(this.config, `/memory/events/workspace/${withDefaults.workspace_id}${suffix}`, {
|
|
13466
13510
|
method: "GET"
|
|
@@ -14863,7 +14907,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14863
14907
|
async captureContext(params) {
|
|
14864
14908
|
const withDefaults = this.withDefaults(params);
|
|
14865
14909
|
let apiEventType = "manual_note";
|
|
14866
|
-
const tags = params.tags || [];
|
|
14910
|
+
const tags = [...params.tags || []];
|
|
14867
14911
|
switch (params.event_type) {
|
|
14868
14912
|
case "conversation":
|
|
14869
14913
|
apiEventType = "chat";
|
|
@@ -14906,18 +14950,20 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14906
14950
|
apiEventType = "manual_note";
|
|
14907
14951
|
tags.push(params.event_type);
|
|
14908
14952
|
}
|
|
14953
|
+
const normalizedTags = normalizeTags(tags);
|
|
14909
14954
|
return this.createMemoryEvent({
|
|
14910
14955
|
workspace_id: withDefaults.workspace_id,
|
|
14911
14956
|
project_id: withDefaults.project_id,
|
|
14912
14957
|
event_type: apiEventType,
|
|
14913
14958
|
title: params.title,
|
|
14914
14959
|
content: params.content,
|
|
14960
|
+
tags: normalizedTags,
|
|
14915
14961
|
provenance: params.provenance,
|
|
14916
14962
|
code_refs: params.code_refs,
|
|
14917
14963
|
metadata: {
|
|
14918
14964
|
original_type: params.event_type,
|
|
14919
14965
|
session_id: params.session_id,
|
|
14920
|
-
tags,
|
|
14966
|
+
...normalizedTags ? { tags: normalizedTags } : {},
|
|
14921
14967
|
importance: params.importance || "medium",
|
|
14922
14968
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14923
14969
|
source: "mcp_auto_capture"
|
|
@@ -14933,8 +14979,9 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14933
14979
|
const metadata = {
|
|
14934
14980
|
...params.metadata || {}
|
|
14935
14981
|
};
|
|
14936
|
-
|
|
14937
|
-
|
|
14982
|
+
const normalizedTags = normalizeTags(params.tags);
|
|
14983
|
+
if (normalizedTags) {
|
|
14984
|
+
metadata.tags = normalizedTags;
|
|
14938
14985
|
}
|
|
14939
14986
|
if (!metadata.captured_at) {
|
|
14940
14987
|
metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -14948,6 +14995,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14948
14995
|
event_type: params.event_type,
|
|
14949
14996
|
title: params.title,
|
|
14950
14997
|
content: params.content,
|
|
14998
|
+
tags: normalizedTags,
|
|
14951
14999
|
provenance: params.provenance,
|
|
14952
15000
|
code_refs: params.code_refs,
|
|
14953
15001
|
metadata
|
|
@@ -14967,6 +15015,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14967
15015
|
*/
|
|
14968
15016
|
async sessionRemember(params) {
|
|
14969
15017
|
const withDefaults = this.withDefaults(params);
|
|
15018
|
+
const normalizedTags = normalizeTags(params.tags);
|
|
14970
15019
|
if (!withDefaults.workspace_id) {
|
|
14971
15020
|
throw new Error(
|
|
14972
15021
|
"workspace_id is required for session_remember. Set defaultWorkspaceId in config or provide workspace_id."
|
|
@@ -14978,7 +15027,8 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14978
15027
|
workspace_id: withDefaults.workspace_id,
|
|
14979
15028
|
project_id: withDefaults.project_id,
|
|
14980
15029
|
importance: params.importance,
|
|
14981
|
-
await_indexing: params.await_indexing
|
|
15030
|
+
await_indexing: params.await_indexing,
|
|
15031
|
+
...normalizedTags ? { tags: normalizedTags } : {}
|
|
14982
15032
|
}
|
|
14983
15033
|
});
|
|
14984
15034
|
}
|
|
@@ -15818,14 +15868,13 @@ ${context}`;
|
|
|
15818
15868
|
});
|
|
15819
15869
|
if (!searchResult?.results) return [];
|
|
15820
15870
|
const lessons = searchResult.results.filter((item) => {
|
|
15821
|
-
|
|
15822
|
-
const
|
|
15823
|
-
if (!isLesson) return false;
|
|
15871
|
+
if (!isLessonResult(item)) return false;
|
|
15872
|
+
const tags = extractEventTags(item);
|
|
15824
15873
|
const severityTag = tags.find((t) => t.startsWith("severity:"));
|
|
15825
15874
|
const severity = severityTag?.split(":")[1] || item.metadata?.importance || "medium";
|
|
15826
15875
|
return severity === "critical" || severity === "high";
|
|
15827
15876
|
}).slice(0, limit).map((item) => {
|
|
15828
|
-
const tags = item
|
|
15877
|
+
const tags = extractEventTags(item);
|
|
15829
15878
|
const severityTag = tags.find((t) => t.startsWith("severity:"));
|
|
15830
15879
|
const severity = severityTag?.split(":")[1] || item.metadata?.importance || "medium";
|
|
15831
15880
|
const category = tags.find(
|
|
@@ -15839,7 +15888,7 @@ ${context}`;
|
|
|
15839
15888
|
) || "unknown";
|
|
15840
15889
|
const content = item.content || "";
|
|
15841
15890
|
const preventionMatch = content.match(/### Prevention\n([\s\S]*?)(?:\n\n|\n\*\*|$)/);
|
|
15842
|
-
const prevention = preventionMatch?.[1]?.trim() || content.slice(0,
|
|
15891
|
+
const prevention = preventionMatch?.[1]?.trim() || content.slice(0, 1e3);
|
|
15843
15892
|
return {
|
|
15844
15893
|
title: item.title || "Lesson",
|
|
15845
15894
|
severity,
|
|
@@ -17045,6 +17094,7 @@ ${context}`;
|
|
|
17045
17094
|
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
17046
17095
|
if (params?.doc_type) query.set("doc_type", params.doc_type);
|
|
17047
17096
|
if (params?.is_personal !== void 0) query.set("is_personal", String(params.is_personal));
|
|
17097
|
+
if (params?.query) query.set("query", params.query);
|
|
17048
17098
|
if (params?.page) query.set("page", String(params.page));
|
|
17049
17099
|
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
17050
17100
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
@@ -17093,6 +17143,55 @@ ${context}`;
|
|
|
17093
17143
|
return request(this.config, `/docs/${params.doc_id}`, { method: "DELETE" });
|
|
17094
17144
|
}
|
|
17095
17145
|
// -------------------------------------------------------------------------
|
|
17146
|
+
// Skill methods (portable instruction + action bundles)
|
|
17147
|
+
// -------------------------------------------------------------------------
|
|
17148
|
+
async listSkills(params) {
|
|
17149
|
+
const withDefaults = this.withDefaults(params || {});
|
|
17150
|
+
const query = new URLSearchParams();
|
|
17151
|
+
if (withDefaults.workspace_id) query.set("workspace_id", withDefaults.workspace_id);
|
|
17152
|
+
if (params?.project_id) query.set("project_id", params.project_id);
|
|
17153
|
+
if (params?.scope) query.set("scope", params.scope);
|
|
17154
|
+
if (params?.status) query.set("status", params.status);
|
|
17155
|
+
if (params?.category) query.set("category", params.category);
|
|
17156
|
+
if (params?.query) query.set("query", params.query);
|
|
17157
|
+
if (params?.is_personal !== void 0) query.set("is_personal", String(params.is_personal));
|
|
17158
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
17159
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
17160
|
+
return request(this.config, `/skills${suffix}`, { method: "GET" });
|
|
17161
|
+
}
|
|
17162
|
+
async getSkill(skillId) {
|
|
17163
|
+
return request(this.config, `/skills/${skillId}`, { method: "GET" });
|
|
17164
|
+
}
|
|
17165
|
+
async createSkill(params) {
|
|
17166
|
+
return request(this.config, "/skills", { body: this.withDefaults(params) });
|
|
17167
|
+
}
|
|
17168
|
+
async updateSkill(skillId, params) {
|
|
17169
|
+
return request(this.config, `/skills/${skillId}`, {
|
|
17170
|
+
method: "PATCH",
|
|
17171
|
+
body: params
|
|
17172
|
+
});
|
|
17173
|
+
}
|
|
17174
|
+
async runSkill(skillId, params) {
|
|
17175
|
+
return request(this.config, `/skills/${skillId}/run`, {
|
|
17176
|
+
body: params || {}
|
|
17177
|
+
});
|
|
17178
|
+
}
|
|
17179
|
+
async deleteSkill(skillId) {
|
|
17180
|
+
return request(this.config, `/skills/${skillId}`, { method: "DELETE" });
|
|
17181
|
+
}
|
|
17182
|
+
async importSkills(params) {
|
|
17183
|
+
return request(this.config, "/skills/import", { body: this.withDefaults(params) });
|
|
17184
|
+
}
|
|
17185
|
+
async exportSkills(params) {
|
|
17186
|
+
const withDefaults = this.withDefaults(params || {});
|
|
17187
|
+
return request(this.config, "/skills/export", { body: withDefaults });
|
|
17188
|
+
}
|
|
17189
|
+
async shareSkill(skillId, scope) {
|
|
17190
|
+
return request(this.config, `/skills/${skillId}/share`, {
|
|
17191
|
+
body: { scope }
|
|
17192
|
+
});
|
|
17193
|
+
}
|
|
17194
|
+
// -------------------------------------------------------------------------
|
|
17096
17195
|
// Transcript methods (conversation session storage)
|
|
17097
17196
|
// -------------------------------------------------------------------------
|
|
17098
17197
|
/**
|
|
@@ -17184,14 +17283,14 @@ ${context}`;
|
|
|
17184
17283
|
};
|
|
17185
17284
|
|
|
17186
17285
|
// src/tools.ts
|
|
17187
|
-
init_files();
|
|
17188
|
-
init_rules_templates();
|
|
17189
|
-
init_version();
|
|
17190
17286
|
import * as fs5 from "node:fs";
|
|
17191
17287
|
import * as path6 from "node:path";
|
|
17192
17288
|
import { execFile } from "node:child_process";
|
|
17193
17289
|
import { homedir as homedir4 } from "node:os";
|
|
17194
17290
|
import { promisify as promisify2 } from "node:util";
|
|
17291
|
+
init_files();
|
|
17292
|
+
init_rules_templates();
|
|
17293
|
+
init_version();
|
|
17195
17294
|
|
|
17196
17295
|
// src/tool-catalog.ts
|
|
17197
17296
|
var TOOL_CATALOG = [
|
|
@@ -17694,6 +17793,56 @@ function classifyIndexConfidence(indexed, apiIndexed, locallyIndexed, freshness)
|
|
|
17694
17793
|
reason: "Index state is inferred but lacks corroborating API/local metadata."
|
|
17695
17794
|
};
|
|
17696
17795
|
}
|
|
17796
|
+
function classifyGraphIngestIndexState(input) {
|
|
17797
|
+
const { statusResult, locallyIndexed } = input;
|
|
17798
|
+
const candidates = candidateObjects(statusResult);
|
|
17799
|
+
const projectIndexState = readString(candidates, "project_index_state")?.toLowerCase();
|
|
17800
|
+
const indexInProgress = apiResultIsIndexing(statusResult);
|
|
17801
|
+
const indexed = apiResultReportsIndexed(statusResult) || locallyIndexed;
|
|
17802
|
+
const indexedAt = extractIndexTimestamp(statusResult);
|
|
17803
|
+
const ageHours = indexedAt !== void 0 ? Math.floor((Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60)) : void 0;
|
|
17804
|
+
const freshness = classifyIndexFreshness(indexed, ageHours);
|
|
17805
|
+
if (indexInProgress) {
|
|
17806
|
+
return {
|
|
17807
|
+
state: "indexing",
|
|
17808
|
+
freshness,
|
|
17809
|
+
indexInProgress,
|
|
17810
|
+
indexed,
|
|
17811
|
+
projectIndexState,
|
|
17812
|
+
ageHours
|
|
17813
|
+
};
|
|
17814
|
+
}
|
|
17815
|
+
const explicitlyMissing = projectIndexState === "missing" || projectIndexState === "not_indexed" || projectIndexState === "unindexed";
|
|
17816
|
+
if (!indexed || explicitlyMissing) {
|
|
17817
|
+
return {
|
|
17818
|
+
state: "missing",
|
|
17819
|
+
freshness,
|
|
17820
|
+
indexInProgress,
|
|
17821
|
+
indexed,
|
|
17822
|
+
projectIndexState,
|
|
17823
|
+
ageHours
|
|
17824
|
+
};
|
|
17825
|
+
}
|
|
17826
|
+
const explicitlyStale = projectIndexState === "stale";
|
|
17827
|
+
if (freshness === "stale" || explicitlyStale) {
|
|
17828
|
+
return {
|
|
17829
|
+
state: "stale",
|
|
17830
|
+
freshness,
|
|
17831
|
+
indexInProgress,
|
|
17832
|
+
indexed,
|
|
17833
|
+
projectIndexState,
|
|
17834
|
+
ageHours
|
|
17835
|
+
};
|
|
17836
|
+
}
|
|
17837
|
+
return {
|
|
17838
|
+
state: "ready",
|
|
17839
|
+
freshness,
|
|
17840
|
+
indexInProgress,
|
|
17841
|
+
indexed,
|
|
17842
|
+
projectIndexState,
|
|
17843
|
+
ageHours
|
|
17844
|
+
};
|
|
17845
|
+
}
|
|
17697
17846
|
|
|
17698
17847
|
// src/todo-utils.ts
|
|
17699
17848
|
function normalizeTodoStatus(status) {
|
|
@@ -17942,6 +18091,32 @@ After updating, restart the AI tool to use the new version.
|
|
|
17942
18091
|
\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
18092
|
`.trim();
|
|
17944
18093
|
}
|
|
18094
|
+
function generateSuggestedRulesNotice(result) {
|
|
18095
|
+
const suggestedRules = result.suggested_rules;
|
|
18096
|
+
if (!suggestedRules || suggestedRules.length === 0) {
|
|
18097
|
+
return "";
|
|
18098
|
+
}
|
|
18099
|
+
const ruleLines = suggestedRules.slice(0, 3).map((rule, i) => {
|
|
18100
|
+
const cat = rule.category || "general";
|
|
18101
|
+
const confidence = rule.confidence ? `${Math.round(rule.confidence * 100)}%` : "?";
|
|
18102
|
+
const count = rule.occurrence_count || 0;
|
|
18103
|
+
const keywords = (rule.keywords || []).join(", ");
|
|
18104
|
+
return `${i + 1}. [${cat}] ${rule.instruction || ""} (confidence: ${confidence}, seen ${count}x)
|
|
18105
|
+
Keywords: ${keywords}
|
|
18106
|
+
Rule ID: ${rule.id}`;
|
|
18107
|
+
});
|
|
18108
|
+
return `
|
|
18109
|
+
|
|
18110
|
+
\u{1F4A1} [SUGGESTED_RULES] ContextStream detected recurring patterns and generated rule suggestions.
|
|
18111
|
+
\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
|
|
18112
|
+
Present these to the user. They can accept or reject each one.
|
|
18113
|
+
|
|
18114
|
+
${ruleLines.join("\n")}
|
|
18115
|
+
|
|
18116
|
+
To accept: session(action="suggested_rule_action", rule_id="<id>", rule_action="accept")
|
|
18117
|
+
To reject: session(action="suggested_rule_action", rule_id="<id>", rule_action="reject")
|
|
18118
|
+
\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`;
|
|
18119
|
+
}
|
|
17945
18120
|
var DEFAULT_PARAM_DESCRIPTIONS = {
|
|
17946
18121
|
api_key: "ContextStream API key.",
|
|
17947
18122
|
apiKey: "ContextStream API key.",
|
|
@@ -19219,6 +19394,8 @@ var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
19219
19394
|
// Consolidates slack_*, github_*, notion_*, integrations_*
|
|
19220
19395
|
"media",
|
|
19221
19396
|
// Consolidates media indexing, search, and clip retrieval for Remotion/FFmpeg
|
|
19397
|
+
"skill",
|
|
19398
|
+
// Skill management: list, get, create, update, run, delete, import, export, share
|
|
19222
19399
|
"help"
|
|
19223
19400
|
// Consolidates session_tools, auth_me, mcp_server_version, etc.
|
|
19224
19401
|
]);
|
|
@@ -19238,6 +19415,7 @@ function mapToolToConsolidatedDomain(toolName) {
|
|
|
19238
19415
|
return "integration";
|
|
19239
19416
|
}
|
|
19240
19417
|
if (toolName.startsWith("media_")) return "media";
|
|
19418
|
+
if (toolName.startsWith("skill_")) return "skill";
|
|
19241
19419
|
if (toolName === "session_tools" || toolName === "auth_me" || toolName === "mcp_server_version" || toolName === "tools_enable_bundle") {
|
|
19242
19420
|
return "help";
|
|
19243
19421
|
}
|
|
@@ -19333,6 +19511,31 @@ function toStructured(data) {
|
|
|
19333
19511
|
}
|
|
19334
19512
|
return void 0;
|
|
19335
19513
|
}
|
|
19514
|
+
function formatSkillDetail(skill) {
|
|
19515
|
+
const lines = [
|
|
19516
|
+
`**${skill.title || skill.name || "?"}** (${skill.name || "?"})`,
|
|
19517
|
+
`- ID: ${skill.id || "?"}`,
|
|
19518
|
+
`- Scope: ${skill.scope || "personal"} | Status: ${skill.status || "active"}`
|
|
19519
|
+
];
|
|
19520
|
+
if (skill.description) lines.push(`- Description: ${skill.description}`);
|
|
19521
|
+
if (skill.trigger_patterns?.length) lines.push(`- Triggers: ${skill.trigger_patterns.join(", ")}`);
|
|
19522
|
+
if (skill.categories?.length) lines.push(`- Categories: ${skill.categories.join(", ")}`);
|
|
19523
|
+
if (skill.priority != null) lines.push(`- Priority: ${skill.priority}`);
|
|
19524
|
+
if (skill.version) lines.push(`- Version: ${skill.version}`);
|
|
19525
|
+
if (skill.instruction_body) {
|
|
19526
|
+
const body = String(skill.instruction_body);
|
|
19527
|
+
lines.push(`
|
|
19528
|
+
### Instruction
|
|
19529
|
+
${body.length > 2e3 ? body.slice(0, 2e3) + "\u2026" : body}`);
|
|
19530
|
+
}
|
|
19531
|
+
return lines.join("\n");
|
|
19532
|
+
}
|
|
19533
|
+
function formatRunResult(result) {
|
|
19534
|
+
if (result.instruction) return result.instruction;
|
|
19535
|
+
if (result.output) return `Skill output:
|
|
19536
|
+
${result.output}`;
|
|
19537
|
+
return JSON.stringify(result, null, 2);
|
|
19538
|
+
}
|
|
19336
19539
|
function readStatNumber(payload, key) {
|
|
19337
19540
|
if (!payload || typeof payload !== "object") return void 0;
|
|
19338
19541
|
const direct = payload[key];
|
|
@@ -20231,6 +20434,9 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20231
20434
|
if ("data" in value) return extractCollectionArray(value.data);
|
|
20232
20435
|
return void 0;
|
|
20233
20436
|
}
|
|
20437
|
+
function normalizeDocLookupText(value) {
|
|
20438
|
+
return value.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
20439
|
+
}
|
|
20234
20440
|
function tokenizeForDocMatch(query) {
|
|
20235
20441
|
const stopWords = /* @__PURE__ */ new Set([
|
|
20236
20442
|
"the",
|
|
@@ -20248,23 +20454,99 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20248
20454
|
"find",
|
|
20249
20455
|
"plan",
|
|
20250
20456
|
"phase",
|
|
20251
|
-
"phases"
|
|
20457
|
+
"phases",
|
|
20458
|
+
"get",
|
|
20459
|
+
"open",
|
|
20460
|
+
"show",
|
|
20461
|
+
"read"
|
|
20252
20462
|
]);
|
|
20253
|
-
return query.split(
|
|
20463
|
+
return normalizeDocLookupText(query).split(/\s+/).map((term) => term.trim()).filter((term) => term.length >= 3 && !stopWords.has(term));
|
|
20254
20464
|
}
|
|
20255
|
-
function scoreDocMatch(doc, terms) {
|
|
20256
|
-
const
|
|
20257
|
-
const
|
|
20258
|
-
const
|
|
20259
|
-
|
|
20465
|
+
function scoreDocMatch(doc, query, terms) {
|
|
20466
|
+
const rawQuery = query.trim();
|
|
20467
|
+
const normalizedQuery = normalizeDocLookupText(rawQuery);
|
|
20468
|
+
const titleRaw = String(doc?.title ?? doc?.name ?? doc?.summary ?? "").trim();
|
|
20469
|
+
const title = normalizeDocLookupText(titleRaw);
|
|
20470
|
+
const docId = String(doc?.id ?? "").trim();
|
|
20471
|
+
if (docId && rawQuery && docId.toLowerCase() === rawQuery.toLowerCase()) {
|
|
20472
|
+
return { doc, score: 100, exact: true, source: "doc_id" };
|
|
20473
|
+
}
|
|
20474
|
+
if (title && normalizedQuery && title === normalizedQuery) {
|
|
20475
|
+
return { doc, score: 95, exact: true, source: "exact_title" };
|
|
20476
|
+
}
|
|
20477
|
+
if (title && normalizedQuery && title.includes(normalizedQuery) && normalizedQuery.length >= 8) {
|
|
20478
|
+
return { doc, score: 80, exact: false, source: "title_contains_query" };
|
|
20479
|
+
}
|
|
20480
|
+
if (terms.length === 0) {
|
|
20481
|
+
return { doc, score: 0, exact: false, source: "term_overlap" };
|
|
20482
|
+
}
|
|
20483
|
+
const matchedTerms = terms.filter((term) => title.includes(term)).length;
|
|
20484
|
+
if (matchedTerms === 0) {
|
|
20485
|
+
return { doc, score: 0, exact: false, source: "term_overlap" };
|
|
20486
|
+
}
|
|
20487
|
+
if (matchedTerms === terms.length) {
|
|
20488
|
+
return {
|
|
20489
|
+
doc,
|
|
20490
|
+
score: 60 + matchedTerms * 5,
|
|
20491
|
+
exact: false,
|
|
20492
|
+
source: "all_terms"
|
|
20493
|
+
};
|
|
20494
|
+
}
|
|
20495
|
+
return {
|
|
20496
|
+
doc,
|
|
20497
|
+
score: matchedTerms * 10,
|
|
20498
|
+
exact: false,
|
|
20499
|
+
source: "term_overlap"
|
|
20500
|
+
};
|
|
20260
20501
|
}
|
|
20261
|
-
function
|
|
20502
|
+
function rankDocsForQueryMatches(docs, query, limit) {
|
|
20262
20503
|
const terms = tokenizeForDocMatch(query);
|
|
20263
|
-
|
|
20264
|
-
const scored = docs.map((doc, idx) => ({ idx, score: scoreDocMatch(doc, terms) }));
|
|
20504
|
+
const scored = docs.map((doc, idx) => ({ idx, ...scoreDocMatch(doc, query, terms) }));
|
|
20265
20505
|
scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
20266
|
-
|
|
20267
|
-
|
|
20506
|
+
return scored.filter((entry) => entry.score > 0).slice(0, limit);
|
|
20507
|
+
}
|
|
20508
|
+
function rankDocsForQuery(docs, query, limit) {
|
|
20509
|
+
const rankedMatches = rankDocsForQueryMatches(docs, query, limit);
|
|
20510
|
+
return rankedMatches.length > 0 ? rankedMatches.map((entry) => entry.doc) : docs.slice(0, limit);
|
|
20511
|
+
}
|
|
20512
|
+
function selectResolvedDocMatch(matches) {
|
|
20513
|
+
if (matches.length === 0) return void 0;
|
|
20514
|
+
if (matches[0].exact) return matches[0];
|
|
20515
|
+
const top = matches[0];
|
|
20516
|
+
const second = matches[1];
|
|
20517
|
+
if (top.score >= 80 && (!second || second.score <= 40)) {
|
|
20518
|
+
return top;
|
|
20519
|
+
}
|
|
20520
|
+
return void 0;
|
|
20521
|
+
}
|
|
20522
|
+
function extractMemorySearchResults(response) {
|
|
20523
|
+
const result = response?.data ?? response;
|
|
20524
|
+
if (Array.isArray(result?.results)) return result.results;
|
|
20525
|
+
if (Array.isArray(result?.items)) return result.items;
|
|
20526
|
+
return [];
|
|
20527
|
+
}
|
|
20528
|
+
function buildHybridMemoryDocResults(memoryResults, docMatches, limit) {
|
|
20529
|
+
const docs = docMatches.map((match) => ({
|
|
20530
|
+
entity_type: "doc",
|
|
20531
|
+
id: match.doc?.id ?? "unknown",
|
|
20532
|
+
title: match.doc?.title ?? match.doc?.name ?? "Untitled",
|
|
20533
|
+
preview: String(match.doc?.content ?? "").slice(0, 150),
|
|
20534
|
+
score: match.score,
|
|
20535
|
+
match_source: match.source,
|
|
20536
|
+
doc_type: match.doc?.doc_type ?? "general"
|
|
20537
|
+
}));
|
|
20538
|
+
const memory = memoryResults.map((item) => ({
|
|
20539
|
+
entity_type: "memory",
|
|
20540
|
+
id: item?.id ?? item?.node_id ?? "unknown",
|
|
20541
|
+
title: item?.title ?? item?.summary ?? item?.name ?? "Untitled",
|
|
20542
|
+
preview: String(item?.content ?? item?.details ?? "").slice(0, 150),
|
|
20543
|
+
score: Math.round(Number(item?.score ?? 0) * 100),
|
|
20544
|
+
match_source: "memory_search",
|
|
20545
|
+
node_type: item?.node_type ?? item?.event_type ?? item?.type ?? "unknown"
|
|
20546
|
+
}));
|
|
20547
|
+
const combined = [...docs, ...memory];
|
|
20548
|
+
combined.sort((a, b) => Number(b.score || 0) - Number(a.score || 0));
|
|
20549
|
+
return combined.slice(0, limit);
|
|
20268
20550
|
}
|
|
20269
20551
|
async function findDocsFallback(workspaceId, candidateProjectIds, query, limit) {
|
|
20270
20552
|
const uniqueCandidates = [];
|
|
@@ -20398,6 +20680,137 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20398
20680
|
}
|
|
20399
20681
|
})();
|
|
20400
20682
|
}
|
|
20683
|
+
async function runGraphIngestWithPreflight(projectId, wait) {
|
|
20684
|
+
const folderPath = resolveFolderPath(void 0, sessionManager);
|
|
20685
|
+
const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
|
|
20686
|
+
const locallyIndexed = localIndexProjectId === projectId;
|
|
20687
|
+
const suggestedPath = folderPath || "<your_project_path>";
|
|
20688
|
+
let statusResult = {};
|
|
20689
|
+
try {
|
|
20690
|
+
statusResult = await client.projectIndexStatus(projectId);
|
|
20691
|
+
} catch (error) {
|
|
20692
|
+
if (!isNotFoundError(error)) throw error;
|
|
20693
|
+
}
|
|
20694
|
+
const preflight = classifyGraphIngestIndexState({
|
|
20695
|
+
statusResult,
|
|
20696
|
+
locallyIndexed
|
|
20697
|
+
});
|
|
20698
|
+
const preflightData = {
|
|
20699
|
+
index_state: preflight.state,
|
|
20700
|
+
index_freshness: preflight.freshness,
|
|
20701
|
+
index_age_hours: preflight.ageHours ?? null,
|
|
20702
|
+
index_in_progress: preflight.indexInProgress,
|
|
20703
|
+
project_index_state: preflight.projectIndexState ?? null,
|
|
20704
|
+
locally_indexed: locallyIndexed
|
|
20705
|
+
};
|
|
20706
|
+
if (preflight.state === "indexing") {
|
|
20707
|
+
const deferred = {
|
|
20708
|
+
status: "deferred",
|
|
20709
|
+
wait,
|
|
20710
|
+
reason: "Project index refresh is in progress. Graph ingest is deferred until index refresh completes.",
|
|
20711
|
+
preflight: {
|
|
20712
|
+
...preflightData,
|
|
20713
|
+
auto_refresh_started: false,
|
|
20714
|
+
graph_ingest_executed: false
|
|
20715
|
+
},
|
|
20716
|
+
next_step: `Run project(action="index_status") and retry graph(action="ingest") when freshness is not stale.`
|
|
20717
|
+
};
|
|
20718
|
+
return {
|
|
20719
|
+
content: [
|
|
20720
|
+
{
|
|
20721
|
+
type: "text",
|
|
20722
|
+
text: `Project indexing is currently in progress; graph ingest was deferred to avoid rebuilding edges from stale embeddings.
|
|
20723
|
+
|
|
20724
|
+
${formatContent(deferred)}`
|
|
20725
|
+
}
|
|
20726
|
+
],
|
|
20727
|
+
structuredContent: deferred
|
|
20728
|
+
};
|
|
20729
|
+
}
|
|
20730
|
+
if (preflight.state === "stale" || preflight.state === "missing") {
|
|
20731
|
+
const validPath = folderPath ? await validateReadableDirectory(folderPath) : void 0;
|
|
20732
|
+
if (!validPath?.ok) {
|
|
20733
|
+
return errorResult(
|
|
20734
|
+
`Graph ingest is blocked because the project index is ${preflight.state} and no readable project folder is available for auto-refresh.
|
|
20735
|
+
Run project(action="ingest_local", path="${suggestedPath}") first, then retry graph(action="ingest").`
|
|
20736
|
+
);
|
|
20737
|
+
}
|
|
20738
|
+
if (!preflight.indexInProgress) {
|
|
20739
|
+
startBackgroundIngest(projectId, validPath.resolvedPath, { force: true }, { preflight: true });
|
|
20740
|
+
}
|
|
20741
|
+
const deferred = {
|
|
20742
|
+
status: "deferred",
|
|
20743
|
+
wait,
|
|
20744
|
+
reason: `Project index is ${preflight.state}. Started index refresh before graph ingest to avoid destructive edge rebuilds from stale embeddings.`,
|
|
20745
|
+
preflight: {
|
|
20746
|
+
...preflightData,
|
|
20747
|
+
auto_refresh_started: !preflight.indexInProgress,
|
|
20748
|
+
auto_refresh_path: validPath.resolvedPath,
|
|
20749
|
+
auto_refresh_force: true,
|
|
20750
|
+
graph_ingest_executed: false
|
|
20751
|
+
},
|
|
20752
|
+
next_step: `Monitor with project(action="index_status"), then rerun graph(action="ingest").`
|
|
20753
|
+
};
|
|
20754
|
+
return {
|
|
20755
|
+
content: [
|
|
20756
|
+
{
|
|
20757
|
+
type: "text",
|
|
20758
|
+
text: `Project index is ${preflight.state}; started index refresh and deferred graph ingest to prevent stale-edge rebuild.
|
|
20759
|
+
|
|
20760
|
+
${formatContent(deferred)}`
|
|
20761
|
+
}
|
|
20762
|
+
],
|
|
20763
|
+
structuredContent: deferred
|
|
20764
|
+
};
|
|
20765
|
+
}
|
|
20766
|
+
let estimate = null;
|
|
20767
|
+
try {
|
|
20768
|
+
const stats = await client.projectStatistics(projectId);
|
|
20769
|
+
estimate = estimateGraphIngestMinutes(stats);
|
|
20770
|
+
} catch (error) {
|
|
20771
|
+
logDebug(`Failed to fetch project statistics for graph estimate: ${error}`);
|
|
20772
|
+
}
|
|
20773
|
+
const result = await client.graphIngest({ project_id: projectId, wait });
|
|
20774
|
+
const estimateText = estimate ? `Estimated time: ${estimate.min}-${estimate.max} min${estimate.basis ? ` (based on ${estimate.basis})` : ""}.` : "Estimated time varies with repo size.";
|
|
20775
|
+
const note = `Graph ingestion is running ${wait ? "synchronously" : "asynchronously"} and can take a few minutes. ${estimateText}`;
|
|
20776
|
+
const structured = toStructured(result);
|
|
20777
|
+
const structuredContent = structured && typeof structured === "object" ? {
|
|
20778
|
+
...structured,
|
|
20779
|
+
wait,
|
|
20780
|
+
note,
|
|
20781
|
+
preflight: {
|
|
20782
|
+
...preflightData,
|
|
20783
|
+
auto_refresh_started: false,
|
|
20784
|
+
graph_ingest_executed: true
|
|
20785
|
+
},
|
|
20786
|
+
...estimate ? {
|
|
20787
|
+
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
20788
|
+
estimate_basis: estimate.basis
|
|
20789
|
+
} : {}
|
|
20790
|
+
} : {
|
|
20791
|
+
wait,
|
|
20792
|
+
note,
|
|
20793
|
+
preflight: {
|
|
20794
|
+
...preflightData,
|
|
20795
|
+
auto_refresh_started: false,
|
|
20796
|
+
graph_ingest_executed: true
|
|
20797
|
+
},
|
|
20798
|
+
...estimate ? {
|
|
20799
|
+
estimate_minutes: { min: estimate.min, max: estimate.max },
|
|
20800
|
+
estimate_basis: estimate.basis
|
|
20801
|
+
} : {}
|
|
20802
|
+
};
|
|
20803
|
+
return {
|
|
20804
|
+
content: [
|
|
20805
|
+
{
|
|
20806
|
+
type: "text",
|
|
20807
|
+
text: `${note}
|
|
20808
|
+
${formatContent(result)}`
|
|
20809
|
+
}
|
|
20810
|
+
],
|
|
20811
|
+
structuredContent
|
|
20812
|
+
};
|
|
20813
|
+
}
|
|
20401
20814
|
function summarizeVideoTextExtraction(metadata) {
|
|
20402
20815
|
if (!metadata || typeof metadata !== "object") return void 0;
|
|
20403
20816
|
const extraction = metadata.video_text_extraction;
|
|
@@ -21446,6 +21859,201 @@ Access: Free`,
|
|
|
21446
21859
|
};
|
|
21447
21860
|
}
|
|
21448
21861
|
);
|
|
21862
|
+
registerTool(
|
|
21863
|
+
"skill",
|
|
21864
|
+
{
|
|
21865
|
+
title: "Manage reusable skills",
|
|
21866
|
+
description: `Manage and execute reusable skills (instruction + action bundles). Skills are portable across projects, sessions, and tools.
|
|
21867
|
+
|
|
21868
|
+
Actions:
|
|
21869
|
+
- list: Browse skills (filter by scope, status, category)
|
|
21870
|
+
- get: Get skill details by ID or name
|
|
21871
|
+
- create: Define a new skill with name, instruction, and triggers
|
|
21872
|
+
- update: Modify an existing skill
|
|
21873
|
+
- run: Execute a skill (by ID or name)
|
|
21874
|
+
- delete: Remove a skill
|
|
21875
|
+
- import: Import skills from file or content (supports markdown, JSON, cursorrules, claude_md)
|
|
21876
|
+
- export: Export skills in various formats
|
|
21877
|
+
- share: Change skill visibility scope`,
|
|
21878
|
+
inputSchema: external_exports.object({
|
|
21879
|
+
action: external_exports.enum(["list", "get", "create", "update", "run", "delete", "import", "export", "share"]).describe("The action to perform"),
|
|
21880
|
+
skill_id: external_exports.string().optional().describe("Skill ID (UUID)"),
|
|
21881
|
+
name: external_exports.string().optional().describe("Skill name (slug, e.g. 'deploy-checker')"),
|
|
21882
|
+
title: external_exports.string().optional().describe("Skill display title"),
|
|
21883
|
+
description: external_exports.string().optional().describe("Skill description"),
|
|
21884
|
+
instruction_body: external_exports.string().optional().describe("Markdown instruction text (the prompt)"),
|
|
21885
|
+
trigger_patterns: external_exports.array(external_exports.string()).optional().describe("Keywords/phrases for auto-activation"),
|
|
21886
|
+
trigger_regex: external_exports.string().optional().describe("Optional regex for advanced trigger matching"),
|
|
21887
|
+
categories: external_exports.array(external_exports.string()).optional().describe("Tags for discovery/filtering"),
|
|
21888
|
+
actions: external_exports.any().optional().describe("Action steps array [{type, tool, params, ...}]"),
|
|
21889
|
+
params: external_exports.any().optional().describe("Parameters passed to skill execution"),
|
|
21890
|
+
dry_run: external_exports.boolean().optional().describe("Preview execution without running"),
|
|
21891
|
+
scope: external_exports.enum(["personal", "team", "public", "all"]).optional().describe("Visibility scope"),
|
|
21892
|
+
status: external_exports.enum(["active", "draft", "archived"]).optional().describe("Skill status"),
|
|
21893
|
+
is_personal: external_exports.boolean().optional().describe("Whether skill is personal"),
|
|
21894
|
+
priority: external_exports.number().optional().describe("Skill priority 0-100 (higher = matched first)"),
|
|
21895
|
+
content: external_exports.string().optional().describe("Content string for import"),
|
|
21896
|
+
file_path: external_exports.string().optional().describe("Local file path for import"),
|
|
21897
|
+
format: external_exports.enum(["auto", "json", "markdown", "skills_md", "cursorrules", "claude_md", "aider", "zip"]).optional().describe("Import/export format"),
|
|
21898
|
+
source_tool: external_exports.string().optional().describe("Source tool name (for import provenance)"),
|
|
21899
|
+
source_file: external_exports.string().optional().describe("Source filename (for import provenance)"),
|
|
21900
|
+
skill_ids: external_exports.array(external_exports.string()).optional().describe("Skill IDs for export"),
|
|
21901
|
+
change_summary: external_exports.string().optional().describe("Summary of changes (for version history)"),
|
|
21902
|
+
workspace_id: external_exports.string().optional().describe("Workspace ID (UUID)"),
|
|
21903
|
+
project_id: external_exports.string().optional().describe("Project ID (UUID)"),
|
|
21904
|
+
query: external_exports.string().optional().describe("Search query"),
|
|
21905
|
+
category: external_exports.string().optional().describe("Filter by category tag"),
|
|
21906
|
+
limit: external_exports.number().optional().describe("Max results to return")
|
|
21907
|
+
})
|
|
21908
|
+
},
|
|
21909
|
+
async (input) => {
|
|
21910
|
+
const action = input.action;
|
|
21911
|
+
switch (action) {
|
|
21912
|
+
case "list": {
|
|
21913
|
+
const result = await client.listSkills({
|
|
21914
|
+
workspace_id: input.workspace_id,
|
|
21915
|
+
project_id: input.project_id,
|
|
21916
|
+
scope: input.scope,
|
|
21917
|
+
status: input.status,
|
|
21918
|
+
category: input.category,
|
|
21919
|
+
query: input.query,
|
|
21920
|
+
is_personal: input.is_personal,
|
|
21921
|
+
limit: input.limit
|
|
21922
|
+
});
|
|
21923
|
+
const items = result.items || [];
|
|
21924
|
+
let text = `Found ${items.length} skill(s).
|
|
21925
|
+
`;
|
|
21926
|
+
for (const item of items) {
|
|
21927
|
+
const name = item.name || "?";
|
|
21928
|
+
const title = item.title || "?";
|
|
21929
|
+
const scope = item.scope || "?";
|
|
21930
|
+
const status = item.status || "?";
|
|
21931
|
+
const id = item.id || "?";
|
|
21932
|
+
text += `- ${title} (${name}) [${scope}|${status}] id=${id}
|
|
21933
|
+
`;
|
|
21934
|
+
}
|
|
21935
|
+
return { content: [{ type: "text", text }] };
|
|
21936
|
+
}
|
|
21937
|
+
case "get": {
|
|
21938
|
+
let skillData;
|
|
21939
|
+
if (input.skill_id) {
|
|
21940
|
+
skillData = await client.getSkill(input.skill_id);
|
|
21941
|
+
} else if (input.name) {
|
|
21942
|
+
const result = await client.listSkills({
|
|
21943
|
+
workspace_id: input.workspace_id,
|
|
21944
|
+
query: input.name,
|
|
21945
|
+
limit: 1
|
|
21946
|
+
});
|
|
21947
|
+
skillData = result.items?.find((s) => s.name === input.name) || result.items?.[0];
|
|
21948
|
+
if (!skillData) throw new Error(`Skill '${input.name}' not found`);
|
|
21949
|
+
} else {
|
|
21950
|
+
throw new Error("Either skill_id or name is required for 'get'");
|
|
21951
|
+
}
|
|
21952
|
+
const detail = formatSkillDetail(skillData);
|
|
21953
|
+
return { content: [{ type: "text", text: detail }] };
|
|
21954
|
+
}
|
|
21955
|
+
case "create": {
|
|
21956
|
+
if (!input.name) throw new Error("'name' is required for create");
|
|
21957
|
+
if (!input.instruction_body) throw new Error("'instruction_body' is required for create");
|
|
21958
|
+
const result = await client.createSkill({
|
|
21959
|
+
name: input.name,
|
|
21960
|
+
title: input.title || input.name,
|
|
21961
|
+
instruction_body: input.instruction_body,
|
|
21962
|
+
description: input.description,
|
|
21963
|
+
trigger_patterns: input.trigger_patterns,
|
|
21964
|
+
trigger_regex: input.trigger_regex,
|
|
21965
|
+
categories: input.categories,
|
|
21966
|
+
actions: input.actions,
|
|
21967
|
+
scope: input.scope,
|
|
21968
|
+
is_personal: input.is_personal,
|
|
21969
|
+
priority: input.priority,
|
|
21970
|
+
workspace_id: input.scope === "team" ? input.workspace_id : void 0,
|
|
21971
|
+
project_id: void 0,
|
|
21972
|
+
// Skills are account-level by default
|
|
21973
|
+
source_tool: input.source_tool,
|
|
21974
|
+
source_file: input.source_file
|
|
21975
|
+
});
|
|
21976
|
+
return { content: [{ type: "text", text: `Skill '${input.name}' created (id=${result.id || "?"}).` }] };
|
|
21977
|
+
}
|
|
21978
|
+
case "update": {
|
|
21979
|
+
if (!input.skill_id) throw new Error("'skill_id' is required for update");
|
|
21980
|
+
const result = await client.updateSkill(input.skill_id, {
|
|
21981
|
+
title: input.title,
|
|
21982
|
+
description: input.description,
|
|
21983
|
+
instruction_body: input.instruction_body,
|
|
21984
|
+
trigger_patterns: input.trigger_patterns,
|
|
21985
|
+
trigger_regex: input.trigger_regex,
|
|
21986
|
+
categories: input.categories,
|
|
21987
|
+
actions: input.actions,
|
|
21988
|
+
scope: input.scope,
|
|
21989
|
+
status: input.status,
|
|
21990
|
+
is_personal: input.is_personal,
|
|
21991
|
+
priority: input.priority,
|
|
21992
|
+
change_summary: input.change_summary
|
|
21993
|
+
});
|
|
21994
|
+
return { content: [{ type: "text", text: `Skill ${input.skill_id} updated (version=${result.version || 0}).` }] };
|
|
21995
|
+
}
|
|
21996
|
+
case "run": {
|
|
21997
|
+
let resolvedId = input.skill_id;
|
|
21998
|
+
if (!resolvedId && input.name) {
|
|
21999
|
+
const result2 = await client.listSkills({
|
|
22000
|
+
workspace_id: input.workspace_id,
|
|
22001
|
+
query: input.name,
|
|
22002
|
+
limit: 1
|
|
22003
|
+
});
|
|
22004
|
+
const found = result2.items?.find((s) => s.name === input.name) || result2.items?.[0];
|
|
22005
|
+
if (!found?.id) throw new Error(`Skill '${input.name}' not found`);
|
|
22006
|
+
resolvedId = found.id;
|
|
22007
|
+
}
|
|
22008
|
+
if (!resolvedId) throw new Error("Either skill_id or name is required for 'run'");
|
|
22009
|
+
const result = await client.runSkill(resolvedId, {
|
|
22010
|
+
params: input.params,
|
|
22011
|
+
dry_run: input.dry_run
|
|
22012
|
+
});
|
|
22013
|
+
return { content: [{ type: "text", text: formatRunResult(result) }] };
|
|
22014
|
+
}
|
|
22015
|
+
case "delete": {
|
|
22016
|
+
if (!input.skill_id) throw new Error("'skill_id' is required for delete");
|
|
22017
|
+
await client.deleteSkill(input.skill_id);
|
|
22018
|
+
return { content: [{ type: "text", text: `Skill ${input.skill_id} deleted.` }] };
|
|
22019
|
+
}
|
|
22020
|
+
case "import": {
|
|
22021
|
+
let importContent = input.content;
|
|
22022
|
+
if (!importContent && input.file_path) {
|
|
22023
|
+
const { readFile: readFile4 } = await import("fs/promises");
|
|
22024
|
+
importContent = await readFile4(input.file_path, "utf-8");
|
|
22025
|
+
}
|
|
22026
|
+
if (!importContent) throw new Error("Either 'content' or 'file_path' is required for import");
|
|
22027
|
+
const result = await client.importSkills({
|
|
22028
|
+
content: importContent,
|
|
22029
|
+
format: input.format,
|
|
22030
|
+
source_tool: input.source_tool,
|
|
22031
|
+
source_file: input.source_file || input.file_path,
|
|
22032
|
+
scope: input.scope,
|
|
22033
|
+
workspace_id: input.workspace_id
|
|
22034
|
+
});
|
|
22035
|
+
return { content: [{ type: "text", text: `Import complete: ${result.imported || 0} imported, ${result.skipped || 0} skipped (duplicates).` }] };
|
|
22036
|
+
}
|
|
22037
|
+
case "export": {
|
|
22038
|
+
const result = await client.exportSkills({
|
|
22039
|
+
skill_ids: input.skill_ids,
|
|
22040
|
+
format: input.format,
|
|
22041
|
+
scope: input.scope,
|
|
22042
|
+
workspace_id: input.workspace_id
|
|
22043
|
+
});
|
|
22044
|
+
return { content: [{ type: "text", text: result.content || JSON.stringify(result, null, 2) }] };
|
|
22045
|
+
}
|
|
22046
|
+
case "share": {
|
|
22047
|
+
if (!input.skill_id) throw new Error("'skill_id' is required for share");
|
|
22048
|
+
if (!input.scope) throw new Error("'scope' is required for share");
|
|
22049
|
+
const result = await client.shareSkill(input.skill_id, input.scope);
|
|
22050
|
+
return { content: [{ type: "text", text: `Skill ${input.skill_id} shared with scope=${result.scope || input.scope}.` }] };
|
|
22051
|
+
}
|
|
22052
|
+
default:
|
|
22053
|
+
throw new Error(`Invalid skill action: '${action}'. Valid: list, get, create, update, run, delete, import, export, share`);
|
|
22054
|
+
}
|
|
22055
|
+
}
|
|
22056
|
+
);
|
|
21449
22057
|
registerTool(
|
|
21450
22058
|
"memory_bulk_ingest",
|
|
21451
22059
|
{
|
|
@@ -21468,15 +22076,24 @@ Access: Free`,
|
|
|
21468
22076
|
"memory_list_events",
|
|
21469
22077
|
{
|
|
21470
22078
|
title: "List memory events",
|
|
21471
|
-
description: "List memory events (optionally scoped)",
|
|
22079
|
+
description: "List memory events (optionally scoped). Supports tag-based and event_type filtering for precise provenance tracking.",
|
|
21472
22080
|
inputSchema: external_exports.object({
|
|
21473
22081
|
workspace_id: external_exports.string().uuid().optional(),
|
|
21474
22082
|
project_id: external_exports.string().uuid().optional(),
|
|
21475
|
-
limit: external_exports.number().optional()
|
|
22083
|
+
limit: external_exports.number().optional(),
|
|
22084
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Filter events that contain ALL of these tags"),
|
|
22085
|
+
event_type: external_exports.string().optional().describe("Filter by event type (e.g. decision, lesson, manual_note)")
|
|
21476
22086
|
})
|
|
21477
22087
|
},
|
|
21478
22088
|
async (input) => {
|
|
21479
22089
|
const result = await client.listMemoryEvents(input);
|
|
22090
|
+
if (input.tags && input.tags.length > 0 && result.items) {
|
|
22091
|
+
const requiredTags = input.tags;
|
|
22092
|
+
result.items = result.items.filter((item) => {
|
|
22093
|
+
const itemTags = extractEventTags(item);
|
|
22094
|
+
return requiredTags.every((tag) => itemTags.includes(tag));
|
|
22095
|
+
});
|
|
22096
|
+
}
|
|
21480
22097
|
return {
|
|
21481
22098
|
content: [{ type: "text", text: formatContent(result) }]
|
|
21482
22099
|
};
|
|
@@ -21530,16 +22147,24 @@ Access: Free`,
|
|
|
21530
22147
|
"memory_search",
|
|
21531
22148
|
{
|
|
21532
22149
|
title: "Memory-aware search",
|
|
21533
|
-
description: "Search memory events/notes",
|
|
22150
|
+
description: "Search memory events/notes. Supports optional tag-based pre-filtering.",
|
|
21534
22151
|
inputSchema: external_exports.object({
|
|
21535
22152
|
query: external_exports.string(),
|
|
21536
22153
|
workspace_id: external_exports.string().uuid().optional(),
|
|
21537
22154
|
project_id: external_exports.string().uuid().optional(),
|
|
21538
|
-
limit: external_exports.number().optional()
|
|
22155
|
+
limit: external_exports.number().optional(),
|
|
22156
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Filter results that contain ALL of these tags")
|
|
21539
22157
|
})
|
|
21540
22158
|
},
|
|
21541
22159
|
async (input) => {
|
|
21542
22160
|
const result = await client.memorySearch(input);
|
|
22161
|
+
if (input.tags && input.tags.length > 0 && result.results) {
|
|
22162
|
+
const requiredTags = input.tags;
|
|
22163
|
+
result.results = result.results.filter((item) => {
|
|
22164
|
+
const itemTags = extractEventTags(item);
|
|
22165
|
+
return requiredTags.every((tag) => itemTags.includes(tag));
|
|
22166
|
+
});
|
|
22167
|
+
}
|
|
21543
22168
|
return {
|
|
21544
22169
|
content: [{ type: "text", text: formatContent(result) }]
|
|
21545
22170
|
};
|
|
@@ -21753,43 +22378,7 @@ Access: Free`,
|
|
|
21753
22378
|
);
|
|
21754
22379
|
}
|
|
21755
22380
|
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
|
-
};
|
|
22381
|
+
return runGraphIngestWithPreflight(projectId, wait);
|
|
21793
22382
|
}
|
|
21794
22383
|
);
|
|
21795
22384
|
registerTool(
|
|
@@ -23363,9 +23952,8 @@ Returns lessons filtered by:
|
|
|
23363
23952
|
// Fetch more to filter
|
|
23364
23953
|
});
|
|
23365
23954
|
const lessons = (searchResult.results || []).filter((item) => {
|
|
23366
|
-
|
|
23367
|
-
const
|
|
23368
|
-
if (!isLesson) return false;
|
|
23955
|
+
if (!isLessonResult(item)) return false;
|
|
23956
|
+
const tags = extractEventTags(item);
|
|
23369
23957
|
if (input.category && !tags.includes(input.category)) {
|
|
23370
23958
|
return false;
|
|
23371
23959
|
}
|
|
@@ -23384,7 +23972,7 @@ Returns lessons filtered by:
|
|
|
23384
23972
|
};
|
|
23385
23973
|
}
|
|
23386
23974
|
const formattedLessons = lessons.map((lesson, i) => {
|
|
23387
|
-
const tags = lesson
|
|
23975
|
+
const tags = extractEventTags(lesson);
|
|
23388
23976
|
const severity = tags.find((t) => t.startsWith("severity:"))?.split(":")[1] || "medium";
|
|
23389
23977
|
const category = tags.find(
|
|
23390
23978
|
(t) => [
|
|
@@ -23403,7 +23991,7 @@ Returns lessons filtered by:
|
|
|
23403
23991
|
}[severity] || "\u26AA";
|
|
23404
23992
|
return `${i + 1}. ${severityEmoji} **${lesson.title}**
|
|
23405
23993
|
Category: ${category} | Severity: ${severity}
|
|
23406
|
-
${lesson.content?.slice(0,
|
|
23994
|
+
${lesson.content?.slice(0, 500)}...`;
|
|
23407
23995
|
}).join("\n\n");
|
|
23408
23996
|
return {
|
|
23409
23997
|
content: [
|
|
@@ -24406,6 +24994,7 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
|
|
|
24406
24994
|
const instructionsLine = result.instructions ? `
|
|
24407
24995
|
|
|
24408
24996
|
[INSTRUCTIONS] ${result.instructions}` : "";
|
|
24997
|
+
const suggestedRulesLine = generateSuggestedRulesNotice(result);
|
|
24409
24998
|
const contextRulesLine = `
|
|
24410
24999
|
|
|
24411
25000
|
${CONTEXT_CALL_REMINDER}`;
|
|
@@ -24418,6 +25007,7 @@ ${rulesWarningLine}` : "",
|
|
|
24418
25007
|
versionWarningLine ? `
|
|
24419
25008
|
|
|
24420
25009
|
${versionWarningLine}` : "",
|
|
25010
|
+
suggestedRulesLine,
|
|
24421
25011
|
contextPressureWarning,
|
|
24422
25012
|
semanticHints,
|
|
24423
25013
|
instructionsLine,
|
|
@@ -26150,7 +26740,8 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26150
26740
|
workspace_id: workspaceId,
|
|
26151
26741
|
project_id: projectId,
|
|
26152
26742
|
content: input.content,
|
|
26153
|
-
importance
|
|
26743
|
+
importance,
|
|
26744
|
+
tags: input.tags
|
|
26154
26745
|
});
|
|
26155
26746
|
const rememberHint = getCaptureHint("preference");
|
|
26156
26747
|
const resultWithHint = { ...result, hint: rememberHint };
|
|
@@ -26787,7 +27378,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26787
27378
|
order: external_exports.number().optional().describe("Task order within plan"),
|
|
26788
27379
|
task_ids: external_exports.array(external_exports.string().uuid()).optional().describe("Task IDs for reorder_tasks"),
|
|
26789
27380
|
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"),
|
|
27381
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Tags for event or task categorization"),
|
|
26791
27382
|
// Batch import params
|
|
26792
27383
|
events: external_exports.array(
|
|
26793
27384
|
external_exports.object({
|
|
@@ -26824,7 +27415,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26824
27415
|
diagram_id: external_exports.string().uuid().optional().describe("Diagram ID for get_diagram/update_diagram/delete_diagram"),
|
|
26825
27416
|
diagram_type: external_exports.enum(["flowchart", "sequence", "class", "er", "gantt", "mindmap", "pie", "other"]).optional().describe("Mermaid diagram type"),
|
|
26826
27417
|
// Doc params
|
|
26827
|
-
doc_id: external_exports.string().
|
|
27418
|
+
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
27419
|
doc_type: external_exports.enum(["roadmap", "spec", "general"]).optional().describe("Document type"),
|
|
26829
27420
|
milestones: external_exports.array(
|
|
26830
27421
|
external_exports.object({
|
|
@@ -26859,6 +27450,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26859
27450
|
event_type: input.event_type,
|
|
26860
27451
|
title: input.title,
|
|
26861
27452
|
content: input.content,
|
|
27453
|
+
tags: input.tags,
|
|
26862
27454
|
metadata: input.metadata,
|
|
26863
27455
|
provenance: input.provenance,
|
|
26864
27456
|
code_refs: input.code_refs
|
|
@@ -27020,13 +27612,42 @@ ${formatContent(result)}`
|
|
|
27020
27612
|
if (!input.query) {
|
|
27021
27613
|
return errorResult("search requires: query");
|
|
27022
27614
|
}
|
|
27023
|
-
const
|
|
27024
|
-
|
|
27025
|
-
|
|
27615
|
+
const [memoryResult, docsResult] = await Promise.all([
|
|
27616
|
+
client.memorySearch({
|
|
27617
|
+
workspace_id: workspaceId,
|
|
27618
|
+
project_id: projectId,
|
|
27619
|
+
query: input.query,
|
|
27620
|
+
limit: input.limit
|
|
27621
|
+
}),
|
|
27622
|
+
workspaceId ? client.docsList({
|
|
27623
|
+
workspace_id: workspaceId,
|
|
27624
|
+
project_id: projectId,
|
|
27625
|
+
per_page: Math.max(10, Math.min(input.limit ?? 10, 25))
|
|
27626
|
+
}) : Promise.resolve(void 0)
|
|
27627
|
+
]);
|
|
27628
|
+
const memoryItems = extractMemorySearchResults(memoryResult);
|
|
27629
|
+
const docsItems = extractCollectionArray(docsResult ?? {}) ?? [];
|
|
27630
|
+
const docMatches = rankDocsForQueryMatches(
|
|
27631
|
+
docsItems,
|
|
27632
|
+
input.query,
|
|
27633
|
+
Math.max(1, Math.min(input.limit ?? 10, 25))
|
|
27634
|
+
);
|
|
27635
|
+
const hybridResults = buildHybridMemoryDocResults(
|
|
27636
|
+
memoryItems,
|
|
27637
|
+
docMatches,
|
|
27638
|
+
Math.max(1, Math.min(input.limit ?? 10, 25))
|
|
27639
|
+
);
|
|
27640
|
+
const outputText = formatContent({
|
|
27026
27641
|
query: input.query,
|
|
27027
|
-
|
|
27642
|
+
results: hybridResults,
|
|
27643
|
+
memory_results: memoryItems,
|
|
27644
|
+
doc_matches: docMatches.map((entry) => ({
|
|
27645
|
+
...entry.doc,
|
|
27646
|
+
match_score: entry.score,
|
|
27647
|
+
exact_match: entry.exact,
|
|
27648
|
+
match_source: entry.source
|
|
27649
|
+
}))
|
|
27028
27650
|
});
|
|
27029
|
-
const outputText = formatContent(result);
|
|
27030
27651
|
trackToolTokenSavings(client, "memory_search", outputText, {
|
|
27031
27652
|
workspace_id: workspaceId,
|
|
27032
27653
|
project_id: projectId
|
|
@@ -27402,17 +28023,80 @@ ${formatContent(result)}`
|
|
|
27402
28023
|
if (!input.doc_id) {
|
|
27403
28024
|
return errorResult("get_doc requires: doc_id");
|
|
27404
28025
|
}
|
|
27405
|
-
const
|
|
28026
|
+
const directDocId = normalizeUuid(input.doc_id);
|
|
28027
|
+
if (directDocId) {
|
|
28028
|
+
const getDocResult = await client.docsGet({ doc_id: directDocId });
|
|
28029
|
+
return {
|
|
28030
|
+
content: [{ type: "text", text: formatContent(getDocResult) }]
|
|
28031
|
+
};
|
|
28032
|
+
}
|
|
28033
|
+
if (!workspaceId) {
|
|
28034
|
+
return errorResult(
|
|
28035
|
+
"get_doc title/query lookups require workspace_id. Call session_init first or pass a doc UUID."
|
|
28036
|
+
);
|
|
28037
|
+
}
|
|
28038
|
+
const candidateProjectIds = [projectId, explicitProjectId, void 0];
|
|
28039
|
+
const fallback = await findDocsFallback(
|
|
28040
|
+
workspaceId,
|
|
28041
|
+
candidateProjectIds,
|
|
28042
|
+
input.doc_id,
|
|
28043
|
+
Math.max(10, Math.min(input.limit ?? 20, 30))
|
|
28044
|
+
);
|
|
28045
|
+
const rankedMatches = rankDocsForQueryMatches(
|
|
28046
|
+
fallback?.docs ?? [],
|
|
28047
|
+
input.doc_id,
|
|
28048
|
+
Math.max(1, Math.min(input.limit ?? 10, 10))
|
|
28049
|
+
);
|
|
28050
|
+
const resolved = selectResolvedDocMatch(rankedMatches);
|
|
28051
|
+
if (resolved) {
|
|
28052
|
+
const resolvedDocId = String(resolved.doc?.id ?? "");
|
|
28053
|
+
if (normalizeUuid(resolvedDocId)) {
|
|
28054
|
+
const getDocResult = await client.docsGet({ doc_id: resolvedDocId });
|
|
28055
|
+
return {
|
|
28056
|
+
content: [
|
|
28057
|
+
{
|
|
28058
|
+
type: "text",
|
|
28059
|
+
text: `Resolved doc query "${input.doc_id}" to doc ID ${resolvedDocId}.
|
|
28060
|
+
|
|
28061
|
+
${formatContent(
|
|
28062
|
+
getDocResult
|
|
28063
|
+
)}`
|
|
28064
|
+
}
|
|
28065
|
+
]
|
|
28066
|
+
};
|
|
28067
|
+
}
|
|
28068
|
+
}
|
|
28069
|
+
const topMatches = rankedMatches.slice(0, 5).map((entry) => ({
|
|
28070
|
+
...entry.doc ?? {},
|
|
28071
|
+
match_score: entry.score,
|
|
28072
|
+
exact_match: entry.exact,
|
|
28073
|
+
match_source: entry.source
|
|
28074
|
+
}));
|
|
28075
|
+
const noMatchMessage = topMatches.length > 0 ? `Could not resolve "${input.doc_id}" to a single doc confidently.` : `No docs found matching "${input.doc_id}".`;
|
|
27406
28076
|
return {
|
|
27407
|
-
content: [
|
|
28077
|
+
content: [
|
|
28078
|
+
{
|
|
28079
|
+
type: "text",
|
|
28080
|
+
text: `${noMatchMessage}
|
|
28081
|
+
|
|
28082
|
+
${formatContent({
|
|
28083
|
+
query: input.doc_id,
|
|
28084
|
+
doc_matches: topMatches
|
|
28085
|
+
})}`
|
|
28086
|
+
}
|
|
28087
|
+
]
|
|
27408
28088
|
};
|
|
27409
28089
|
}
|
|
27410
28090
|
case "update_doc": {
|
|
27411
28091
|
if (!input.doc_id) {
|
|
27412
28092
|
return errorResult("update_doc requires: doc_id");
|
|
27413
28093
|
}
|
|
28094
|
+
const updateDocId = normalizeUuid(input.doc_id);
|
|
28095
|
+
if (!updateDocId) {
|
|
28096
|
+
return errorResult("update_doc requires a valid UUID doc_id.");
|
|
28097
|
+
}
|
|
27414
28098
|
const updateDocResult = await client.docsUpdate({
|
|
27415
|
-
doc_id:
|
|
28099
|
+
doc_id: updateDocId,
|
|
27416
28100
|
title: input.title,
|
|
27417
28101
|
content: input.content,
|
|
27418
28102
|
doc_type: input.doc_type,
|
|
@@ -27426,7 +28110,11 @@ ${formatContent(result)}`
|
|
|
27426
28110
|
if (!input.doc_id) {
|
|
27427
28111
|
return errorResult("delete_doc requires: doc_id");
|
|
27428
28112
|
}
|
|
27429
|
-
const
|
|
28113
|
+
const deleteDocId = normalizeUuid(input.doc_id);
|
|
28114
|
+
if (!deleteDocId) {
|
|
28115
|
+
return errorResult("delete_doc requires a valid UUID doc_id.");
|
|
28116
|
+
}
|
|
28117
|
+
const deleteDocResult = await client.docsDelete({ doc_id: deleteDocId });
|
|
27430
28118
|
return {
|
|
27431
28119
|
content: [{ type: "text", text: formatContent(deleteDocResult) }]
|
|
27432
28120
|
};
|
|
@@ -27841,13 +28529,7 @@ ${formatContent(result)}`
|
|
|
27841
28529
|
if (!projectId) {
|
|
27842
28530
|
return errorResult("ingest requires: project_id");
|
|
27843
28531
|
}
|
|
27844
|
-
|
|
27845
|
-
project_id: projectId,
|
|
27846
|
-
wait: input.wait
|
|
27847
|
-
});
|
|
27848
|
-
return {
|
|
27849
|
-
content: [{ type: "text", text: formatContent(result) }]
|
|
27850
|
-
};
|
|
28532
|
+
return runGraphIngestWithPreflight(projectId, input.wait ?? false);
|
|
27851
28533
|
}
|
|
27852
28534
|
case "circular_dependencies": {
|
|
27853
28535
|
if (!projectId) {
|
|
@@ -31705,6 +32387,7 @@ init_hooks_config();
|
|
|
31705
32387
|
init_files();
|
|
31706
32388
|
var EDITOR_LABELS = {
|
|
31707
32389
|
codex: "Codex CLI",
|
|
32390
|
+
opencode: "OpenCode",
|
|
31708
32391
|
claude: "Claude Code",
|
|
31709
32392
|
cursor: "Cursor / VS Code",
|
|
31710
32393
|
cline: "Cline",
|
|
@@ -31714,7 +32397,7 @@ var EDITOR_LABELS = {
|
|
|
31714
32397
|
antigravity: "Antigravity (Google)"
|
|
31715
32398
|
};
|
|
31716
32399
|
function supportsProjectMcpConfig(editor) {
|
|
31717
|
-
return editor === "cursor" || editor === "claude" || editor === "kilo" || editor === "roo" || editor === "antigravity";
|
|
32400
|
+
return editor === "opencode" || editor === "cursor" || editor === "claude" || editor === "kilo" || editor === "roo" || editor === "antigravity";
|
|
31718
32401
|
}
|
|
31719
32402
|
function normalizeInput(value) {
|
|
31720
32403
|
return value.trim();
|
|
@@ -31960,6 +32643,28 @@ async function isCodexInstalled() {
|
|
|
31960
32643
|
].filter((candidate) => Boolean(candidate));
|
|
31961
32644
|
return anyPathExists(candidates);
|
|
31962
32645
|
}
|
|
32646
|
+
function openCodeConfigPath() {
|
|
32647
|
+
const home = homedir6();
|
|
32648
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
32649
|
+
if (process.platform === "win32") {
|
|
32650
|
+
const appData = process.env.APPDATA || path8.join(home, "AppData", "Roaming");
|
|
32651
|
+
return path8.join(appData, "opencode", "opencode.json");
|
|
32652
|
+
}
|
|
32653
|
+
const configRoot = xdgConfigHome || path8.join(home, ".config");
|
|
32654
|
+
return path8.join(configRoot, "opencode", "opencode.json");
|
|
32655
|
+
}
|
|
32656
|
+
async function isOpenCodeInstalled() {
|
|
32657
|
+
const configPath = openCodeConfigPath();
|
|
32658
|
+
const configDir = path8.dirname(configPath);
|
|
32659
|
+
const home = homedir6();
|
|
32660
|
+
const candidates = [
|
|
32661
|
+
configDir,
|
|
32662
|
+
configPath,
|
|
32663
|
+
path8.join(home, ".bun", "bin", "opencode"),
|
|
32664
|
+
path8.join(home, ".local", "bin", "opencode")
|
|
32665
|
+
];
|
|
32666
|
+
return anyPathExists(candidates);
|
|
32667
|
+
}
|
|
31963
32668
|
async function isClaudeInstalled() {
|
|
31964
32669
|
const home = homedir6();
|
|
31965
32670
|
const candidates = [path8.join(home, ".claude"), path8.join(home, ".config", "claude")];
|
|
@@ -32031,10 +32736,12 @@ async function isAntigravityInstalled() {
|
|
|
32031
32736
|
const localApp = process.env.LOCALAPPDATA;
|
|
32032
32737
|
const programFiles = process.env.ProgramFiles;
|
|
32033
32738
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
32034
|
-
if (localApp)
|
|
32739
|
+
if (localApp)
|
|
32740
|
+
candidates.push(path8.join(localApp, "Programs", "Antigravity", "Antigravity.exe"));
|
|
32035
32741
|
if (localApp) candidates.push(path8.join(localApp, "Antigravity", "Antigravity.exe"));
|
|
32036
32742
|
if (programFiles) candidates.push(path8.join(programFiles, "Antigravity", "Antigravity.exe"));
|
|
32037
|
-
if (programFilesX86)
|
|
32743
|
+
if (programFilesX86)
|
|
32744
|
+
candidates.push(path8.join(programFilesX86, "Antigravity", "Antigravity.exe"));
|
|
32038
32745
|
} else {
|
|
32039
32746
|
candidates.push("/usr/bin/antigravity");
|
|
32040
32747
|
candidates.push("/usr/local/bin/antigravity");
|
|
@@ -32047,6 +32754,8 @@ async function isEditorInstalled(editor) {
|
|
|
32047
32754
|
switch (editor) {
|
|
32048
32755
|
case "codex":
|
|
32049
32756
|
return isCodexInstalled();
|
|
32757
|
+
case "opencode":
|
|
32758
|
+
return isOpenCodeInstalled();
|
|
32050
32759
|
case "claude":
|
|
32051
32760
|
return isClaudeInstalled();
|
|
32052
32761
|
case "cursor":
|
|
@@ -32066,6 +32775,8 @@ async function isEditorInstalled(editor) {
|
|
|
32066
32775
|
}
|
|
32067
32776
|
}
|
|
32068
32777
|
var IS_WINDOWS = process.platform === "win32";
|
|
32778
|
+
var DEFAULT_CONTEXTSTREAM_API_URL = "https://api.contextstream.io";
|
|
32779
|
+
var OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
32069
32780
|
function escapeTomlString(value) {
|
|
32070
32781
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
32071
32782
|
}
|
|
@@ -32112,6 +32823,26 @@ function buildContextStreamVsCodeServer(params) {
|
|
|
32112
32823
|
env
|
|
32113
32824
|
};
|
|
32114
32825
|
}
|
|
32826
|
+
function buildContextStreamOpenCodeEnvironment(params) {
|
|
32827
|
+
const environment = {
|
|
32828
|
+
CONTEXTSTREAM_API_KEY: "{env:CONTEXTSTREAM_API_KEY}"
|
|
32829
|
+
};
|
|
32830
|
+
if (normalizeApiUrl(params.apiUrl) !== DEFAULT_CONTEXTSTREAM_API_URL) {
|
|
32831
|
+
environment.CONTEXTSTREAM_API_URL = params.apiUrl;
|
|
32832
|
+
}
|
|
32833
|
+
if (params.contextPackEnabled === false) {
|
|
32834
|
+
environment.CONTEXTSTREAM_CONTEXT_PACK = "false";
|
|
32835
|
+
}
|
|
32836
|
+
return environment;
|
|
32837
|
+
}
|
|
32838
|
+
function buildContextStreamOpenCodeLocalServer(params) {
|
|
32839
|
+
return {
|
|
32840
|
+
type: "local",
|
|
32841
|
+
command: ["npx", "-y", "contextstream-mcp"],
|
|
32842
|
+
environment: buildContextStreamOpenCodeEnvironment(params),
|
|
32843
|
+
enabled: true
|
|
32844
|
+
};
|
|
32845
|
+
}
|
|
32115
32846
|
function stripJsonComments(input) {
|
|
32116
32847
|
return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
32117
32848
|
}
|
|
@@ -32170,6 +32901,32 @@ async function upsertJsonVsCodeMcpConfig(filePath, server) {
|
|
|
32170
32901
|
if (!exists) return "created";
|
|
32171
32902
|
return before === after ? "skipped" : "updated";
|
|
32172
32903
|
}
|
|
32904
|
+
async function upsertOpenCodeMcpConfig(filePath, server) {
|
|
32905
|
+
await fs7.mkdir(path8.dirname(filePath), { recursive: true });
|
|
32906
|
+
const exists = await fileExists(filePath);
|
|
32907
|
+
let root = {};
|
|
32908
|
+
if (exists) {
|
|
32909
|
+
const raw = await fs7.readFile(filePath, "utf8").catch(() => "");
|
|
32910
|
+
const parsed = tryParseJsonLike(raw);
|
|
32911
|
+
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
32912
|
+
root = parsed.value;
|
|
32913
|
+
}
|
|
32914
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) root = {};
|
|
32915
|
+
if (!root.mcp || typeof root.mcp !== "object" || Array.isArray(root.mcp)) root.mcp = {};
|
|
32916
|
+
const before = JSON.stringify({
|
|
32917
|
+
schema: root.$schema ?? null,
|
|
32918
|
+
contextstream: root.mcp.contextstream ?? null
|
|
32919
|
+
});
|
|
32920
|
+
root.$schema = OPENCODE_CONFIG_SCHEMA_URL;
|
|
32921
|
+
root.mcp.contextstream = server;
|
|
32922
|
+
const after = JSON.stringify({
|
|
32923
|
+
schema: root.$schema ?? null,
|
|
32924
|
+
contextstream: root.mcp.contextstream ?? null
|
|
32925
|
+
});
|
|
32926
|
+
await fs7.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
32927
|
+
if (!exists) return "created";
|
|
32928
|
+
return before === after ? "skipped" : "updated";
|
|
32929
|
+
}
|
|
32173
32930
|
function claudeDesktopConfigPath() {
|
|
32174
32931
|
const home = homedir6();
|
|
32175
32932
|
if (process.platform === "darwin") {
|
|
@@ -32374,9 +33131,7 @@ async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun
|
|
|
32374
33131
|
}
|
|
32375
33132
|
console.log("\nProject selection (current directory):");
|
|
32376
33133
|
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
|
-
);
|
|
33134
|
+
const choiceRaw = normalizeInput(await rl.question(`Choose [1-${options.length}] (default 1): `));
|
|
32380
33135
|
const choiceNum = Number.parseInt(choiceRaw || "1", 10);
|
|
32381
33136
|
const selected = Number.isFinite(choiceNum) ? options[choiceNum - 1] : options[0];
|
|
32382
33137
|
if (!selected || selected.kind === "skip") {
|
|
@@ -32442,7 +33197,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32442
33197
|
}
|
|
32443
33198
|
}
|
|
32444
33199
|
if (!projectId) {
|
|
32445
|
-
console.log(
|
|
33200
|
+
console.log(
|
|
33201
|
+
`${colors.yellow}! Could not resolve project ID for ${projectName}${colors.reset}`
|
|
33202
|
+
);
|
|
32446
33203
|
return;
|
|
32447
33204
|
}
|
|
32448
33205
|
} catch (err) {
|
|
@@ -32460,7 +33217,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32460
33217
|
console.log(`${colors.dim}No indexable files found${colors.reset}`);
|
|
32461
33218
|
return;
|
|
32462
33219
|
} else {
|
|
32463
|
-
console.log(
|
|
33220
|
+
console.log(
|
|
33221
|
+
`${colors.dim}Found ${totalFiles.toLocaleString()} files for indexing${colors.reset}`
|
|
33222
|
+
);
|
|
32464
33223
|
}
|
|
32465
33224
|
} catch {
|
|
32466
33225
|
console.log(`${colors.dim}Scanning files...${colors.reset}`);
|
|
@@ -32480,7 +33239,9 @@ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
|
32480
33239
|
const percentage = (progress * 100).toFixed(1);
|
|
32481
33240
|
const speed = filesPerSec.toFixed(1);
|
|
32482
33241
|
const size = formatBytes(bytesIndexed);
|
|
32483
|
-
process.stdout.write(
|
|
33242
|
+
process.stdout.write(
|
|
33243
|
+
`\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} `
|
|
33244
|
+
);
|
|
32484
33245
|
};
|
|
32485
33246
|
const progressInterval = setInterval(updateProgress, 80);
|
|
32486
33247
|
const ingestWithRetry = async (batch, maxRetries = 3) => {
|
|
@@ -32798,7 +33559,7 @@ Code: ${device.user_code}`);
|
|
|
32798
33559
|
);
|
|
32799
33560
|
}
|
|
32800
33561
|
}
|
|
32801
|
-
const NO_HOOKS_EDITORS2 = ["codex", "aider", "antigravity"];
|
|
33562
|
+
const NO_HOOKS_EDITORS2 = ["codex", "opencode", "aider", "antigravity"];
|
|
32802
33563
|
const getModeForEditor = (editor) => NO_HOOKS_EDITORS2.includes(editor) ? "full" : "bootstrap";
|
|
32803
33564
|
const detectedPlanName = await client.getPlanName();
|
|
32804
33565
|
const detectedGraphTier = await client.getGraphTier();
|
|
@@ -32809,7 +33570,9 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32809
33570
|
const contextPackEnabled = !!detectedPlanName && ["pro", "team", "enterprise"].some((p) => detectedPlanName.toLowerCase().includes(p));
|
|
32810
33571
|
console.log("\nAuto-Update:");
|
|
32811
33572
|
console.log(" When enabled, ContextStream will automatically update to the latest version");
|
|
32812
|
-
console.log(
|
|
33573
|
+
console.log(
|
|
33574
|
+
" on new sessions (checks daily). You can disable this if you prefer manual updates."
|
|
33575
|
+
);
|
|
32813
33576
|
const currentAutoUpdate = isAutoUpdateEnabled();
|
|
32814
33577
|
const autoUpdateChoice = normalizeInput(
|
|
32815
33578
|
await rl.question(`Enable auto-update? [${currentAutoUpdate ? "Y/n" : "y/N"}]: `)
|
|
@@ -32823,6 +33586,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32823
33586
|
}
|
|
32824
33587
|
const editors = [
|
|
32825
33588
|
"codex",
|
|
33589
|
+
"opencode",
|
|
32826
33590
|
"claude",
|
|
32827
33591
|
"cursor",
|
|
32828
33592
|
"cline",
|
|
@@ -32896,7 +33660,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32896
33660
|
const mcpScope = mcpChoice === "2" && hasCodex && !hasProjectMcpEditors ? "skip" : mcpChoice === "4" ? "skip" : mcpChoice === "1" ? "global" : mcpChoice === "2" ? "project" : "both";
|
|
32897
33661
|
const mcpServer = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
32898
33662
|
const mcpServerClaude = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
33663
|
+
const mcpServerOpenCode = buildContextStreamOpenCodeLocalServer({
|
|
33664
|
+
apiUrl,
|
|
33665
|
+
apiKey,
|
|
33666
|
+
contextPackEnabled
|
|
33667
|
+
});
|
|
32899
33668
|
const vsCodeServer = buildContextStreamVsCodeServer({ apiUrl, apiKey, contextPackEnabled });
|
|
33669
|
+
let hasPrintedOpenCodeEnvNote = false;
|
|
33670
|
+
const printOpenCodeEnvNote = () => {
|
|
33671
|
+
if (hasPrintedOpenCodeEnvNote) return;
|
|
33672
|
+
hasPrintedOpenCodeEnvNote = true;
|
|
33673
|
+
console.log(
|
|
33674
|
+
" OpenCode reads CONTEXTSTREAM_API_KEY from your environment. Export it before launching OpenCode."
|
|
33675
|
+
);
|
|
33676
|
+
};
|
|
32900
33677
|
const needsGlobalMcpConfig = mcpScope === "global" || mcpScope === "both" || mcpScope === "project" && hasCodex;
|
|
32901
33678
|
if (needsGlobalMcpConfig) {
|
|
32902
33679
|
console.log("\nInstalling global MCP config...");
|
|
@@ -32948,6 +33725,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32948
33725
|
);
|
|
32949
33726
|
continue;
|
|
32950
33727
|
}
|
|
33728
|
+
if (editor === "opencode") {
|
|
33729
|
+
const filePath = openCodeConfigPath();
|
|
33730
|
+
if (dryRun) {
|
|
33731
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
33732
|
+
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
33733
|
+
printOpenCodeEnvNote();
|
|
33734
|
+
continue;
|
|
33735
|
+
}
|
|
33736
|
+
const status = await upsertOpenCodeMcpConfig(filePath, mcpServerOpenCode);
|
|
33737
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status });
|
|
33738
|
+
console.log(`- ${EDITOR_LABELS[editor]}: ${status} ${filePath}`);
|
|
33739
|
+
printOpenCodeEnvNote();
|
|
33740
|
+
continue;
|
|
33741
|
+
}
|
|
32951
33742
|
if (editor === "cursor") {
|
|
32952
33743
|
const filePath = path8.join(homedir6(), ".cursor", "mcp.json");
|
|
32953
33744
|
if (dryRun) {
|
|
@@ -32990,6 +33781,8 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
32990
33781
|
kilo: "kilo",
|
|
32991
33782
|
codex: null,
|
|
32992
33783
|
// No hooks API
|
|
33784
|
+
opencode: null,
|
|
33785
|
+
// No hooks API
|
|
32993
33786
|
aider: null,
|
|
32994
33787
|
// No hooks API
|
|
32995
33788
|
antigravity: null
|
|
@@ -33030,6 +33823,12 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
33030
33823
|
if (scope === "global" || scope === "both") {
|
|
33031
33824
|
console.log("\nInstalling global rules...");
|
|
33032
33825
|
for (const editor of configuredEditors) {
|
|
33826
|
+
if (editor === "opencode") {
|
|
33827
|
+
console.log(
|
|
33828
|
+
`- ${EDITOR_LABELS[editor]}: rules are not auto-generated yet (MCP config only).`
|
|
33829
|
+
);
|
|
33830
|
+
continue;
|
|
33831
|
+
}
|
|
33033
33832
|
const filePath = globalRulesPathForEditor(editor);
|
|
33034
33833
|
if (!filePath) {
|
|
33035
33834
|
console.log(
|
|
@@ -33170,6 +33969,21 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33170
33969
|
}
|
|
33171
33970
|
continue;
|
|
33172
33971
|
}
|
|
33972
|
+
if (editor === "opencode") {
|
|
33973
|
+
const openCodePath = path8.join(projectPath, "opencode.json");
|
|
33974
|
+
if (dryRun) {
|
|
33975
|
+
writeActions.push({
|
|
33976
|
+
kind: "mcp-config",
|
|
33977
|
+
target: openCodePath,
|
|
33978
|
+
status: "dry-run"
|
|
33979
|
+
});
|
|
33980
|
+
} else {
|
|
33981
|
+
const status = await upsertOpenCodeMcpConfig(openCodePath, mcpServerOpenCode);
|
|
33982
|
+
writeActions.push({ kind: "mcp-config", target: openCodePath, status });
|
|
33983
|
+
}
|
|
33984
|
+
printOpenCodeEnvNote();
|
|
33985
|
+
continue;
|
|
33986
|
+
}
|
|
33173
33987
|
if (editor === "kilo") {
|
|
33174
33988
|
const kiloPath = path8.join(projectPath, ".kilocode", "mcp.json");
|
|
33175
33989
|
if (dryRun) {
|
|
@@ -33201,6 +34015,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33201
34015
|
for (const editor of selectedEditors) {
|
|
33202
34016
|
if (scope !== "project" && scope !== "both") continue;
|
|
33203
34017
|
if (!configuredEditors.includes(editor)) continue;
|
|
34018
|
+
if (editor === "opencode") continue;
|
|
33204
34019
|
const rule = generateRuleContent(editor, {
|
|
33205
34020
|
workspaceName,
|
|
33206
34021
|
workspaceId: workspaceId && workspaceId !== "dry-run" ? workspaceId : void 0,
|
|
@@ -33240,11 +34055,15 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33240
34055
|
console.log("PROJECT INDEXING");
|
|
33241
34056
|
console.log("\u2500".repeat(60));
|
|
33242
34057
|
if (filesIndexed === 0) {
|
|
33243
|
-
console.log(
|
|
34058
|
+
console.log(
|
|
34059
|
+
"Indexing enables semantic code search and AI-powered graph knowledge for rich AI context."
|
|
34060
|
+
);
|
|
33244
34061
|
} else {
|
|
33245
34062
|
console.log("Your project index is stale and could use a refresh.");
|
|
33246
34063
|
}
|
|
33247
|
-
console.log(
|
|
34064
|
+
console.log(
|
|
34065
|
+
"Powered by our blazing-fast Rust engine, indexing typically takes under a minute,"
|
|
34066
|
+
);
|
|
33248
34067
|
console.log("though larger projects may take a bit longer.\n");
|
|
33249
34068
|
console.log("Your code is private and securely stored.\n");
|
|
33250
34069
|
const indexChoice = normalizeInput(
|
|
@@ -33259,7 +34078,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33259
34078
|
if (indexingEnabled) {
|
|
33260
34079
|
await indexProjectWithProgress(client, process.cwd(), cwdConfig.workspace_id);
|
|
33261
34080
|
} else {
|
|
33262
|
-
console.log(
|
|
34081
|
+
console.log(
|
|
34082
|
+
"\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>"
|
|
34083
|
+
);
|
|
33263
34084
|
}
|
|
33264
34085
|
}
|
|
33265
34086
|
} catch {
|
|
@@ -33269,8 +34090,12 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33269
34090
|
console.log("\n" + "\u2500".repeat(60));
|
|
33270
34091
|
console.log("PROJECT INDEXING");
|
|
33271
34092
|
console.log("\u2500".repeat(60));
|
|
33272
|
-
console.log(
|
|
33273
|
-
|
|
34093
|
+
console.log(
|
|
34094
|
+
"Indexing enables semantic code search and AI-powered graph knowledge for rich AI context."
|
|
34095
|
+
);
|
|
34096
|
+
console.log(
|
|
34097
|
+
"Powered by our blazing-fast Rust engine, indexing typically takes under a minute,"
|
|
34098
|
+
);
|
|
33274
34099
|
console.log("though larger projects may take a bit longer.\n");
|
|
33275
34100
|
console.log("Your code is private and securely stored.\n");
|
|
33276
34101
|
const indexChoice = normalizeInput(
|
|
@@ -33292,7 +34117,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33292
34117
|
await indexProjectWithProgress(client, projectPath, workspaceId);
|
|
33293
34118
|
}
|
|
33294
34119
|
} else {
|
|
33295
|
-
console.log(
|
|
34120
|
+
console.log(
|
|
34121
|
+
"\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>"
|
|
34122
|
+
);
|
|
33296
34123
|
}
|
|
33297
34124
|
}
|
|
33298
34125
|
console.log("\nDone.");
|
|
@@ -33313,7 +34140,9 @@ Applying to ${projects.length} project(s)...`);
|
|
|
33313
34140
|
"- For UI-based MCP setup (Cline/Kilo/Roo global), see https://contextstream.io/docs/mcp"
|
|
33314
34141
|
);
|
|
33315
34142
|
console.log("");
|
|
33316
|
-
console.log(
|
|
34143
|
+
console.log(
|
|
34144
|
+
"You're all set! ContextStream gives your AI persistent memory, semantic code search, and cross-session context."
|
|
34145
|
+
);
|
|
33317
34146
|
console.log("More at: https://contextstream.io/docs/mcp");
|
|
33318
34147
|
} finally {
|
|
33319
34148
|
rl.close();
|