@contextstream/mcp-server 0.4.67 → 0.4.71
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 +156 -139
- package/dist/hooks/on-read.js +169 -11
- package/dist/hooks/pre-tool-use.js +87 -5
- package/dist/hooks/runner.js +110 -31
- package/dist/hooks/session-init.js +14 -13
- package/dist/hooks/user-prompt-submit.js +12 -12
- package/dist/index.js +1550 -714
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -733,7 +733,7 @@ var require_ignore = __commonJS({
|
|
|
733
733
|
// path matching.
|
|
734
734
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
735
735
|
// @returns {TestResult} true if a file is ignored
|
|
736
|
-
test(
|
|
736
|
+
test(path21, checkUnignored, mode) {
|
|
737
737
|
let ignored = false;
|
|
738
738
|
let unignored = false;
|
|
739
739
|
let matchedRule;
|
|
@@ -742,7 +742,7 @@ var require_ignore = __commonJS({
|
|
|
742
742
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
743
743
|
return;
|
|
744
744
|
}
|
|
745
|
-
const matched = rule[mode].test(
|
|
745
|
+
const matched = rule[mode].test(path21);
|
|
746
746
|
if (!matched) {
|
|
747
747
|
return;
|
|
748
748
|
}
|
|
@@ -763,17 +763,17 @@ var require_ignore = __commonJS({
|
|
|
763
763
|
var throwError = (message, Ctor) => {
|
|
764
764
|
throw new Ctor(message);
|
|
765
765
|
};
|
|
766
|
-
var checkPath = (
|
|
767
|
-
if (!isString(
|
|
766
|
+
var checkPath = (path21, originalPath, doThrow) => {
|
|
767
|
+
if (!isString(path21)) {
|
|
768
768
|
return doThrow(
|
|
769
769
|
`path must be a string, but got \`${originalPath}\``,
|
|
770
770
|
TypeError
|
|
771
771
|
);
|
|
772
772
|
}
|
|
773
|
-
if (!
|
|
773
|
+
if (!path21) {
|
|
774
774
|
return doThrow(`path must not be empty`, TypeError);
|
|
775
775
|
}
|
|
776
|
-
if (checkPath.isNotRelative(
|
|
776
|
+
if (checkPath.isNotRelative(path21)) {
|
|
777
777
|
const r = "`path.relative()`d";
|
|
778
778
|
return doThrow(
|
|
779
779
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -782,7 +782,7 @@ var require_ignore = __commonJS({
|
|
|
782
782
|
}
|
|
783
783
|
return true;
|
|
784
784
|
};
|
|
785
|
-
var isNotRelative = (
|
|
785
|
+
var isNotRelative = (path21) => REGEX_TEST_INVALID_PATH.test(path21);
|
|
786
786
|
checkPath.isNotRelative = isNotRelative;
|
|
787
787
|
checkPath.convert = (p) => p;
|
|
788
788
|
var Ignore2 = class {
|
|
@@ -812,19 +812,19 @@ var require_ignore = __commonJS({
|
|
|
812
812
|
}
|
|
813
813
|
// @returns {TestResult}
|
|
814
814
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
815
|
-
const
|
|
815
|
+
const path21 = originalPath && checkPath.convert(originalPath);
|
|
816
816
|
checkPath(
|
|
817
|
-
|
|
817
|
+
path21,
|
|
818
818
|
originalPath,
|
|
819
819
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
820
820
|
);
|
|
821
|
-
return this._t(
|
|
821
|
+
return this._t(path21, cache, checkUnignored, slices);
|
|
822
822
|
}
|
|
823
|
-
checkIgnore(
|
|
824
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
825
|
-
return this.test(
|
|
823
|
+
checkIgnore(path21) {
|
|
824
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
|
|
825
|
+
return this.test(path21);
|
|
826
826
|
}
|
|
827
|
-
const slices =
|
|
827
|
+
const slices = path21.split(SLASH).filter(Boolean);
|
|
828
828
|
slices.pop();
|
|
829
829
|
if (slices.length) {
|
|
830
830
|
const parent = this._t(
|
|
@@ -837,18 +837,18 @@ var require_ignore = __commonJS({
|
|
|
837
837
|
return parent;
|
|
838
838
|
}
|
|
839
839
|
}
|
|
840
|
-
return this._rules.test(
|
|
840
|
+
return this._rules.test(path21, false, MODE_CHECK_IGNORE);
|
|
841
841
|
}
|
|
842
|
-
_t(
|
|
843
|
-
if (
|
|
844
|
-
return cache[
|
|
842
|
+
_t(path21, cache, checkUnignored, slices) {
|
|
843
|
+
if (path21 in cache) {
|
|
844
|
+
return cache[path21];
|
|
845
845
|
}
|
|
846
846
|
if (!slices) {
|
|
847
|
-
slices =
|
|
847
|
+
slices = path21.split(SLASH).filter(Boolean);
|
|
848
848
|
}
|
|
849
849
|
slices.pop();
|
|
850
850
|
if (!slices.length) {
|
|
851
|
-
return cache[
|
|
851
|
+
return cache[path21] = this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
852
852
|
}
|
|
853
853
|
const parent = this._t(
|
|
854
854
|
slices.join(SLASH) + SLASH,
|
|
@@ -856,29 +856,29 @@ var require_ignore = __commonJS({
|
|
|
856
856
|
checkUnignored,
|
|
857
857
|
slices
|
|
858
858
|
);
|
|
859
|
-
return cache[
|
|
859
|
+
return cache[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
860
860
|
}
|
|
861
|
-
ignores(
|
|
862
|
-
return this._test(
|
|
861
|
+
ignores(path21) {
|
|
862
|
+
return this._test(path21, this._ignoreCache, false).ignored;
|
|
863
863
|
}
|
|
864
864
|
createFilter() {
|
|
865
|
-
return (
|
|
865
|
+
return (path21) => !this.ignores(path21);
|
|
866
866
|
}
|
|
867
867
|
filter(paths) {
|
|
868
868
|
return makeArray(paths).filter(this.createFilter());
|
|
869
869
|
}
|
|
870
870
|
// @returns {TestResult}
|
|
871
|
-
test(
|
|
872
|
-
return this._test(
|
|
871
|
+
test(path21) {
|
|
872
|
+
return this._test(path21, this._testCache, true);
|
|
873
873
|
}
|
|
874
874
|
};
|
|
875
875
|
var factory = (options) => new Ignore2(options);
|
|
876
|
-
var isPathValid = (
|
|
876
|
+
var isPathValid = (path21) => checkPath(path21 && checkPath.convert(path21), path21, RETURN_FALSE);
|
|
877
877
|
var setupWindows = () => {
|
|
878
878
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
879
879
|
checkPath.convert = makePosix;
|
|
880
880
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
881
|
-
checkPath.isNotRelative = (
|
|
881
|
+
checkPath.isNotRelative = (path21) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path21) || isNotRelative(path21);
|
|
882
882
|
};
|
|
883
883
|
if (
|
|
884
884
|
// Detect `process` so that it can run in browsers.
|
|
@@ -3778,7 +3778,7 @@ memory(
|
|
|
3778
3778
|
|
|
3779
3779
|
- Do not store trivial file reads or command output as memory
|
|
3780
3780
|
- Do not skip \`context(...)\` on later turns
|
|
3781
|
-
- Do not use local file scanning before \`search(...)
|
|
3781
|
+
- Do not use local file scanning before \`search(...)\`; for stale/not-indexed projects, wait ~20s for refresh and retry first, then local fallback is allowed
|
|
3782
3782
|
- Do not use editor-only task lists as the persistent record; mirror important work in ContextStream
|
|
3783
3783
|
`;
|
|
3784
3784
|
}
|
|
@@ -3960,7 +3960,7 @@ Use \`context()\` by default to get task-specific rules, lessons from past mista
|
|
|
3960
3960
|
2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
|
|
3961
3961
|
- Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
|
|
3962
3962
|
- **You MUST use the suggested ContextStream tool instead**
|
|
3963
|
-
|
|
3963
|
+
- For stale/not-indexed projects, wait for background refresh (up to ~20s), retry search, then allow local tools only after the grace window
|
|
3964
3964
|
|
|
3965
3965
|
3. **PostToolUse indexing** - After Edit/Write operations:
|
|
3966
3966
|
- Changed files are automatically re-indexed
|
|
@@ -3997,7 +3997,7 @@ STOP \u2192 Call search(mode="auto", query="...") FIRST
|
|
|
3997
3997
|
|
|
3998
3998
|
\u2705 **ALWAYS DO THIS:**
|
|
3999
3999
|
1. \`search(mode="auto", query="what you're looking for")\`
|
|
4000
|
-
2. Only use local tools (Glob/Grep/Read)
|
|
4000
|
+
2. Only use local tools (Glob/Grep/Read) after stale/not-indexed refresh grace window elapses (~20s) or ContextStream still returns **0 results** after retry
|
|
4001
4001
|
3. Use Read ONLY for exact file edits after you know the file path
|
|
4002
4002
|
|
|
4003
4003
|
This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
|
|
@@ -4260,7 +4260,7 @@ session(action="capture", event_type="session_snapshot", title="Pre-compaction s
|
|
|
4260
4260
|
|
|
4261
4261
|
### Search & Code Intelligence (ContextStream-first)
|
|
4262
4262
|
|
|
4263
|
-
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST.
|
|
4263
|
+
\u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST. For stale/not-indexed projects, wait ~20s for background refresh and retry search before local fallback.
|
|
4264
4264
|
|
|
4265
4265
|
**\u274C WRONG workflow (wastes tokens, slow):**
|
|
4266
4266
|
\`\`\`
|
|
@@ -4279,7 +4279,7 @@ search(mode="auto", query="function implementation") \u2192 done (results includ
|
|
|
4279
4279
|
2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
|
|
4280
4280
|
3. \`project(action="files")\` - file tree/list (only when needed)
|
|
4281
4281
|
4. \`graph(action="dependencies", ...)\` - code structure
|
|
4282
|
-
5. Local repo scans (rg/ls/find) - ONLY
|
|
4282
|
+
5. Local repo scans (rg/ls/find) - ONLY after refresh grace window/retry still yields no results/errors, or the user explicitly asks
|
|
4283
4283
|
|
|
4284
4284
|
**Search Mode Selection:**
|
|
4285
4285
|
|
|
@@ -4310,7 +4310,7 @@ search(mode="auto", query="function implementation") \u2192 done (results includ
|
|
|
4310
4310
|
|
|
4311
4311
|
**Search defaults:** \`search\` returns the top 3 results with compact snippets. Use \`limit\` + \`offset\` for pagination, and \`content_max_chars\` to expand snippets when needed.
|
|
4312
4312
|
|
|
4313
|
-
If ContextStream returns results, stop and use them. NEVER use local Search/Explore/Read unless you need exact code edits or
|
|
4313
|
+
If ContextStream returns results, stop and use them. NEVER use local Search/Explore/Read unless you need exact code edits, or refresh grace window + retry still returns 0 results.
|
|
4314
4314
|
|
|
4315
4315
|
**Code Analysis:**
|
|
4316
4316
|
- Dependencies: \`graph(action="dependencies", file_path="...")\`
|
|
@@ -4439,7 +4439,7 @@ See full documentation: https://contextstream.io/docs/mcp/tools
|
|
|
4439
4439
|
3. Narrow bypass: immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run
|
|
4440
4440
|
|
|
4441
4441
|
**BEFORE Glob/Grep/Read/Search/Explore/Task/EnterPlanMode:**
|
|
4442
|
-
\u2192 \`search(mode="auto", query="...")\` FIRST \u2014
|
|
4442
|
+
\u2192 \`search(mode="auto", query="...")\` FIRST \u2014 for stale/not-indexed, wait ~20s for refresh then retry before local fallback
|
|
4443
4443
|
|
|
4444
4444
|
**HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
|
|
4445
4445
|
</contextstream_protocol>
|
|
@@ -4662,10 +4662,10 @@ search(mode="auto", query="what you're looking for")
|
|
|
4662
4662
|
\u2192 Use this instead of Explore/Task/EnterPlanMode for file discovery.
|
|
4663
4663
|
|
|
4664
4664
|
**IF project is NOT indexed or very stale (>7 days):**
|
|
4665
|
-
\u2192
|
|
4665
|
+
\u2192 Wait up to ~20s for background refresh, retry \`search(mode="auto", ...)\`, then allow local tools only after the grace window
|
|
4666
4666
|
\u2192 OR run \`project(action="index")\` first, then search
|
|
4667
4667
|
|
|
4668
|
-
**IF ContextStream search returns 0 results or errors:**
|
|
4668
|
+
**IF ContextStream search still returns 0 results or errors after retry/window:**
|
|
4669
4669
|
\u2192 Use local tools (Glob/Grep/Read) as fallback
|
|
4670
4670
|
|
|
4671
4671
|
### Choose Search Mode Intelligently:
|
|
@@ -4691,9 +4691,8 @@ search(mode="auto", query="what you're looking for")
|
|
|
4691
4691
|
- Then use local Read/Grep only on paths returned by ContextStream.
|
|
4692
4692
|
|
|
4693
4693
|
### When Local Tools Are OK:
|
|
4694
|
-
\u2705
|
|
4695
|
-
\u2705
|
|
4696
|
-
\u2705 ContextStream search returns 0 results
|
|
4694
|
+
\u2705 Stale/not-indexed grace window has elapsed (~20s default, configurable)
|
|
4695
|
+
\u2705 ContextStream search still returns 0 results after retry
|
|
4697
4696
|
\u2705 ContextStream returns errors
|
|
4698
4697
|
\u2705 User explicitly requests local tools
|
|
4699
4698
|
|
|
@@ -4788,7 +4787,7 @@ After updating, user should restart their AI tool.
|
|
|
4788
4787
|
2. Project is indexed and \`search(mode="auto", ...)\` is retried before local fallbacks
|
|
4789
4788
|
3. Instructions file contains the current ContextStream managed block
|
|
4790
4789
|
`;
|
|
4791
|
-
NO_HOOKS_EDITORS = ["copilot", "codex", "opencode", "aider", "antigravity"];
|
|
4790
|
+
NO_HOOKS_EDITORS = ["copilot", "codex", "opencode", "aider", "antigravity", "kilo"];
|
|
4792
4791
|
TEMPLATES = {
|
|
4793
4792
|
codex: {
|
|
4794
4793
|
filename: "AGENTS.md",
|
|
@@ -4830,7 +4829,7 @@ ${rules}
|
|
|
4830
4829
|
`
|
|
4831
4830
|
},
|
|
4832
4831
|
kilo: {
|
|
4833
|
-
filename: ".
|
|
4832
|
+
filename: ".kilo/rules/contextstream.md",
|
|
4834
4833
|
description: "Kilo Code AI rules",
|
|
4835
4834
|
build: (rules) => `# Kilo Code Rules
|
|
4836
4835
|
${rules}
|
|
@@ -4895,9 +4894,9 @@ var post_write_exports = {};
|
|
|
4895
4894
|
__export(post_write_exports, {
|
|
4896
4895
|
runPostWriteHook: () => runPostWriteHook
|
|
4897
4896
|
});
|
|
4898
|
-
import * as
|
|
4899
|
-
import * as
|
|
4900
|
-
import { homedir as
|
|
4897
|
+
import * as fs9 from "node:fs";
|
|
4898
|
+
import * as path10 from "node:path";
|
|
4899
|
+
import { homedir as homedir8 } from "node:os";
|
|
4901
4900
|
function extractFilePath(input) {
|
|
4902
4901
|
if (input.tool_input) {
|
|
4903
4902
|
const filePath = input.tool_input.file_path || input.tool_input.notebook_path || input.tool_input.path;
|
|
@@ -4922,17 +4921,17 @@ function extractCwd(input) {
|
|
|
4922
4921
|
return process.cwd();
|
|
4923
4922
|
}
|
|
4924
4923
|
function findLocalConfig(startDir) {
|
|
4925
|
-
let currentDir =
|
|
4924
|
+
let currentDir = path10.resolve(startDir);
|
|
4926
4925
|
for (let i = 0; i < 10; i++) {
|
|
4927
|
-
const configPath =
|
|
4928
|
-
if (
|
|
4926
|
+
const configPath = path10.join(currentDir, ".contextstream", "config.json");
|
|
4927
|
+
if (fs9.existsSync(configPath)) {
|
|
4929
4928
|
try {
|
|
4930
|
-
const content =
|
|
4929
|
+
const content = fs9.readFileSync(configPath, "utf-8");
|
|
4931
4930
|
return JSON.parse(content);
|
|
4932
4931
|
} catch {
|
|
4933
4932
|
}
|
|
4934
4933
|
}
|
|
4935
|
-
const parentDir =
|
|
4934
|
+
const parentDir = path10.dirname(currentDir);
|
|
4936
4935
|
if (parentDir === currentDir) break;
|
|
4937
4936
|
currentDir = parentDir;
|
|
4938
4937
|
}
|
|
@@ -4944,12 +4943,12 @@ function loadApiConfig(startDir) {
|
|
|
4944
4943
|
if (apiKey) {
|
|
4945
4944
|
return { apiUrl, apiKey };
|
|
4946
4945
|
}
|
|
4947
|
-
let currentDir =
|
|
4946
|
+
let currentDir = path10.resolve(startDir);
|
|
4948
4947
|
for (let i = 0; i < 10; i++) {
|
|
4949
|
-
const mcpPath =
|
|
4950
|
-
if (
|
|
4948
|
+
const mcpPath = path10.join(currentDir, ".mcp.json");
|
|
4949
|
+
if (fs9.existsSync(mcpPath)) {
|
|
4951
4950
|
try {
|
|
4952
|
-
const content =
|
|
4951
|
+
const content = fs9.readFileSync(mcpPath, "utf-8");
|
|
4953
4952
|
const config = JSON.parse(content);
|
|
4954
4953
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4955
4954
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -4962,15 +4961,15 @@ function loadApiConfig(startDir) {
|
|
|
4962
4961
|
} catch {
|
|
4963
4962
|
}
|
|
4964
4963
|
}
|
|
4965
|
-
const parentDir =
|
|
4964
|
+
const parentDir = path10.dirname(currentDir);
|
|
4966
4965
|
if (parentDir === currentDir) break;
|
|
4967
4966
|
currentDir = parentDir;
|
|
4968
4967
|
}
|
|
4969
4968
|
if (!apiKey) {
|
|
4970
|
-
const homeMcpPath =
|
|
4971
|
-
if (
|
|
4969
|
+
const homeMcpPath = path10.join(homedir8(), ".mcp.json");
|
|
4970
|
+
if (fs9.existsSync(homeMcpPath)) {
|
|
4972
4971
|
try {
|
|
4973
|
-
const content =
|
|
4972
|
+
const content = fs9.readFileSync(homeMcpPath, "utf-8");
|
|
4974
4973
|
const config = JSON.parse(content);
|
|
4975
4974
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4976
4975
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -4986,15 +4985,15 @@ function loadApiConfig(startDir) {
|
|
|
4986
4985
|
return { apiUrl, apiKey };
|
|
4987
4986
|
}
|
|
4988
4987
|
function shouldIndexFile(filePath) {
|
|
4989
|
-
const ext =
|
|
4988
|
+
const ext = path10.extname(filePath).toLowerCase();
|
|
4990
4989
|
if (!INDEXABLE_EXTENSIONS.has(ext)) {
|
|
4991
|
-
const basename5 =
|
|
4990
|
+
const basename5 = path10.basename(filePath).toLowerCase();
|
|
4992
4991
|
if (!["dockerfile", "makefile", "rakefile", "gemfile", "procfile"].includes(basename5)) {
|
|
4993
4992
|
return false;
|
|
4994
4993
|
}
|
|
4995
4994
|
}
|
|
4996
4995
|
try {
|
|
4997
|
-
const stats =
|
|
4996
|
+
const stats = fs9.statSync(filePath);
|
|
4998
4997
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
4999
4998
|
return false;
|
|
5000
4999
|
}
|
|
@@ -5004,7 +5003,7 @@ function shouldIndexFile(filePath) {
|
|
|
5004
5003
|
return true;
|
|
5005
5004
|
}
|
|
5006
5005
|
function detectLanguage2(filePath) {
|
|
5007
|
-
const ext =
|
|
5006
|
+
const ext = path10.extname(filePath).toLowerCase();
|
|
5008
5007
|
const langMap = {
|
|
5009
5008
|
".ts": "typescript",
|
|
5010
5009
|
".tsx": "typescript",
|
|
@@ -5073,8 +5072,8 @@ function detectLanguage2(filePath) {
|
|
|
5073
5072
|
return langMap[ext] || "text";
|
|
5074
5073
|
}
|
|
5075
5074
|
async function indexFile(filePath, projectId, apiUrl, apiKey, projectRoot) {
|
|
5076
|
-
const content =
|
|
5077
|
-
const relativePath =
|
|
5075
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
5076
|
+
const relativePath = path10.relative(projectRoot, filePath);
|
|
5078
5077
|
const payload = {
|
|
5079
5078
|
files: [
|
|
5080
5079
|
{
|
|
@@ -5106,13 +5105,13 @@ async function indexFile(filePath, projectId, apiUrl, apiKey, projectRoot) {
|
|
|
5106
5105
|
}
|
|
5107
5106
|
}
|
|
5108
5107
|
function findProjectRoot(filePath) {
|
|
5109
|
-
let currentDir =
|
|
5108
|
+
let currentDir = path10.dirname(path10.resolve(filePath));
|
|
5110
5109
|
for (let i = 0; i < 10; i++) {
|
|
5111
|
-
const configPath =
|
|
5112
|
-
if (
|
|
5110
|
+
const configPath = path10.join(currentDir, ".contextstream", "config.json");
|
|
5111
|
+
if (fs9.existsSync(configPath)) {
|
|
5113
5112
|
return currentDir;
|
|
5114
5113
|
}
|
|
5115
|
-
const parentDir =
|
|
5114
|
+
const parentDir = path10.dirname(currentDir);
|
|
5116
5115
|
if (parentDir === currentDir) break;
|
|
5117
5116
|
currentDir = parentDir;
|
|
5118
5117
|
}
|
|
@@ -5140,8 +5139,8 @@ async function runPostWriteHook() {
|
|
|
5140
5139
|
process.exit(0);
|
|
5141
5140
|
}
|
|
5142
5141
|
const cwd = extractCwd(input);
|
|
5143
|
-
const absolutePath =
|
|
5144
|
-
if (!
|
|
5142
|
+
const absolutePath = path10.isAbsolute(filePath) ? filePath : path10.resolve(cwd, filePath);
|
|
5143
|
+
if (!fs9.existsSync(absolutePath) || !shouldIndexFile(absolutePath)) {
|
|
5145
5144
|
process.exit(0);
|
|
5146
5145
|
}
|
|
5147
5146
|
const projectRoot = findProjectRoot(absolutePath);
|
|
@@ -5245,12 +5244,12 @@ var init_post_write = __esm({
|
|
|
5245
5244
|
});
|
|
5246
5245
|
|
|
5247
5246
|
// src/hooks/common.ts
|
|
5248
|
-
import * as
|
|
5249
|
-
import * as
|
|
5250
|
-
import { homedir as
|
|
5247
|
+
import * as fs10 from "node:fs";
|
|
5248
|
+
import * as path11 from "node:path";
|
|
5249
|
+
import { homedir as homedir9 } from "node:os";
|
|
5251
5250
|
function readHookInput() {
|
|
5252
5251
|
try {
|
|
5253
|
-
return JSON.parse(
|
|
5252
|
+
return JSON.parse(fs10.readFileSync(0, "utf8"));
|
|
5254
5253
|
} catch {
|
|
5255
5254
|
return {};
|
|
5256
5255
|
}
|
|
@@ -5279,13 +5278,13 @@ function loadHookConfig(cwd) {
|
|
|
5279
5278
|
let jwt = process.env.CONTEXTSTREAM_JWT || "";
|
|
5280
5279
|
let workspaceId = process.env.CONTEXTSTREAM_WORKSPACE_ID || null;
|
|
5281
5280
|
let projectId = process.env.CONTEXTSTREAM_PROJECT_ID || null;
|
|
5282
|
-
let searchDir =
|
|
5281
|
+
let searchDir = path11.resolve(cwd);
|
|
5283
5282
|
for (let i = 0; i < 6; i++) {
|
|
5284
5283
|
if (!apiKey && !jwt) {
|
|
5285
|
-
const mcpPath =
|
|
5286
|
-
if (
|
|
5284
|
+
const mcpPath = path11.join(searchDir, ".mcp.json");
|
|
5285
|
+
if (fs10.existsSync(mcpPath)) {
|
|
5287
5286
|
try {
|
|
5288
|
-
const config = JSON.parse(
|
|
5287
|
+
const config = JSON.parse(fs10.readFileSync(mcpPath, "utf8"));
|
|
5289
5288
|
const env = config.mcpServers?.contextstream?.env;
|
|
5290
5289
|
if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
|
|
5291
5290
|
if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
|
|
@@ -5297,25 +5296,25 @@ function loadHookConfig(cwd) {
|
|
|
5297
5296
|
}
|
|
5298
5297
|
}
|
|
5299
5298
|
if (!workspaceId || !projectId) {
|
|
5300
|
-
const localConfigPath =
|
|
5301
|
-
if (
|
|
5299
|
+
const localConfigPath = path11.join(searchDir, ".contextstream", "config.json");
|
|
5300
|
+
if (fs10.existsSync(localConfigPath)) {
|
|
5302
5301
|
try {
|
|
5303
|
-
const localConfig = JSON.parse(
|
|
5302
|
+
const localConfig = JSON.parse(fs10.readFileSync(localConfigPath, "utf8"));
|
|
5304
5303
|
if (localConfig.workspace_id && !workspaceId) workspaceId = localConfig.workspace_id;
|
|
5305
5304
|
if (localConfig.project_id && !projectId) projectId = localConfig.project_id;
|
|
5306
5305
|
} catch {
|
|
5307
5306
|
}
|
|
5308
5307
|
}
|
|
5309
5308
|
}
|
|
5310
|
-
const parentDir =
|
|
5309
|
+
const parentDir = path11.dirname(searchDir);
|
|
5311
5310
|
if (parentDir === searchDir) break;
|
|
5312
5311
|
searchDir = parentDir;
|
|
5313
5312
|
}
|
|
5314
5313
|
if (!apiKey && !jwt) {
|
|
5315
|
-
const homeMcpPath =
|
|
5316
|
-
if (
|
|
5314
|
+
const homeMcpPath = path11.join(homedir9(), ".mcp.json");
|
|
5315
|
+
if (fs10.existsSync(homeMcpPath)) {
|
|
5317
5316
|
try {
|
|
5318
|
-
const config = JSON.parse(
|
|
5317
|
+
const config = JSON.parse(fs10.readFileSync(homeMcpPath, "utf8"));
|
|
5319
5318
|
const env = config.mcpServers?.contextstream?.env;
|
|
5320
5319
|
if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
|
|
5321
5320
|
if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
|
|
@@ -5475,9 +5474,9 @@ var post_tool_use_failure_exports = {};
|
|
|
5475
5474
|
__export(post_tool_use_failure_exports, {
|
|
5476
5475
|
runPostToolUseFailureHook: () => runPostToolUseFailureHook
|
|
5477
5476
|
});
|
|
5478
|
-
import * as
|
|
5479
|
-
import * as
|
|
5480
|
-
import { homedir as
|
|
5477
|
+
import * as fs11 from "node:fs";
|
|
5478
|
+
import * as path12 from "node:path";
|
|
5479
|
+
import { homedir as homedir10 } from "node:os";
|
|
5481
5480
|
function extractErrorText(input) {
|
|
5482
5481
|
return typeof input.error === "string" && input.error || typeof input.tool_error === "string" && input.tool_error || typeof input.stderr === "string" && input.stderr || "Tool execution failed";
|
|
5483
5482
|
}
|
|
@@ -5488,13 +5487,13 @@ function failureFingerprint(toolName, errorText) {
|
|
|
5488
5487
|
function incrementFailureCounter(fingerprint) {
|
|
5489
5488
|
let counters = {};
|
|
5490
5489
|
try {
|
|
5491
|
-
counters = JSON.parse(
|
|
5490
|
+
counters = JSON.parse(fs11.readFileSync(FAILURE_COUNTERS_FILE, "utf8"));
|
|
5492
5491
|
} catch {
|
|
5493
5492
|
counters = {};
|
|
5494
5493
|
}
|
|
5495
5494
|
counters[fingerprint] = (counters[fingerprint] || 0) + 1;
|
|
5496
|
-
|
|
5497
|
-
|
|
5495
|
+
fs11.mkdirSync(path12.dirname(FAILURE_COUNTERS_FILE), { recursive: true });
|
|
5496
|
+
fs11.writeFileSync(FAILURE_COUNTERS_FILE, JSON.stringify(counters, null, 2), "utf8");
|
|
5498
5497
|
return counters[fingerprint];
|
|
5499
5498
|
}
|
|
5500
5499
|
async function runPostToolUseFailureHook() {
|
|
@@ -5547,7 +5546,7 @@ var init_post_tool_use_failure = __esm({
|
|
|
5547
5546
|
"src/hooks/post-tool-use-failure.ts"() {
|
|
5548
5547
|
"use strict";
|
|
5549
5548
|
init_common();
|
|
5550
|
-
FAILURE_COUNTERS_FILE =
|
|
5549
|
+
FAILURE_COUNTERS_FILE = path12.join(homedir10(), ".contextstream", "hook-failure-counts.json");
|
|
5551
5550
|
isDirectRun2 = process.argv[1]?.includes("post-tool-use-failure") || process.argv[2] === "post-tool-use-failure";
|
|
5552
5551
|
if (isDirectRun2) {
|
|
5553
5552
|
runPostToolUseFailureHook().catch(() => process.exit(0));
|
|
@@ -5713,10 +5712,10 @@ var subagent_stop_exports = {};
|
|
|
5713
5712
|
__export(subagent_stop_exports, {
|
|
5714
5713
|
runSubagentStopHook: () => runSubagentStopHook
|
|
5715
5714
|
});
|
|
5716
|
-
import * as
|
|
5715
|
+
import * as fs12 from "node:fs";
|
|
5717
5716
|
function parseTranscript(transcriptPath) {
|
|
5718
5717
|
try {
|
|
5719
|
-
const content =
|
|
5718
|
+
const content = fs12.readFileSync(transcriptPath, "utf8");
|
|
5720
5719
|
const assistantMessages = [];
|
|
5721
5720
|
let toolCallCount = 0;
|
|
5722
5721
|
for (const rawLine of content.split("\n")) {
|
|
@@ -5778,7 +5777,7 @@ async function runSubagentStopHook() {
|
|
|
5778
5777
|
const agentType = typeof input.agent_type === "string" && input.agent_type || typeof input.subagent_type === "string" && input.subagent_type || "unknown";
|
|
5779
5778
|
const agentId = typeof input.agent_id === "string" && input.agent_id || "unknown";
|
|
5780
5779
|
const transcriptPath = typeof input.agent_transcript_path === "string" && input.agent_transcript_path || typeof input.transcript_path === "string" && input.transcript_path || "";
|
|
5781
|
-
const transcript = transcriptPath &&
|
|
5780
|
+
const transcript = transcriptPath && fs12.existsSync(transcriptPath) ? parseTranscript(transcriptPath) : { assistantMessages: [], toolCallCount: 0 };
|
|
5782
5781
|
const summaryText = transcript.assistantMessages.join("\n\n") || typeof input.summary === "string" && input.summary || "(No assistant output found in subagent transcript.)";
|
|
5783
5782
|
if (agentType.toLowerCase() === "plan") {
|
|
5784
5783
|
const suppliedPlanId = typeof input.plan_id === "string" && input.plan_id || typeof input.planId === "string" && input.planId || null;
|
|
@@ -6002,9 +6001,9 @@ var init_teammate_idle = __esm({
|
|
|
6002
6001
|
});
|
|
6003
6002
|
|
|
6004
6003
|
// src/hooks/prompt-state.ts
|
|
6005
|
-
import * as
|
|
6006
|
-
import * as
|
|
6007
|
-
import { homedir as
|
|
6004
|
+
import * as fs13 from "node:fs";
|
|
6005
|
+
import * as path13 from "node:path";
|
|
6006
|
+
import { homedir as homedir11 } from "node:os";
|
|
6008
6007
|
function defaultState() {
|
|
6009
6008
|
return { workspaces: {} };
|
|
6010
6009
|
}
|
|
@@ -6013,13 +6012,13 @@ function nowIso() {
|
|
|
6013
6012
|
}
|
|
6014
6013
|
function ensureStateDir() {
|
|
6015
6014
|
try {
|
|
6016
|
-
|
|
6015
|
+
fs13.mkdirSync(path13.dirname(STATE_PATH), { recursive: true });
|
|
6017
6016
|
} catch {
|
|
6018
6017
|
}
|
|
6019
6018
|
}
|
|
6020
6019
|
function normalizePath(input) {
|
|
6021
6020
|
try {
|
|
6022
|
-
return
|
|
6021
|
+
return path13.resolve(input);
|
|
6023
6022
|
} catch {
|
|
6024
6023
|
return input;
|
|
6025
6024
|
}
|
|
@@ -6027,11 +6026,11 @@ function normalizePath(input) {
|
|
|
6027
6026
|
function workspacePathsMatch(a, b) {
|
|
6028
6027
|
const left = normalizePath(a);
|
|
6029
6028
|
const right = normalizePath(b);
|
|
6030
|
-
return left === right || left.startsWith(`${right}${
|
|
6029
|
+
return left === right || left.startsWith(`${right}${path13.sep}`) || right.startsWith(`${left}${path13.sep}`);
|
|
6031
6030
|
}
|
|
6032
6031
|
function readState() {
|
|
6033
6032
|
try {
|
|
6034
|
-
const content =
|
|
6033
|
+
const content = fs13.readFileSync(STATE_PATH, "utf8");
|
|
6035
6034
|
const parsed = JSON.parse(content);
|
|
6036
6035
|
if (!parsed || typeof parsed !== "object" || !parsed.workspaces) {
|
|
6037
6036
|
return defaultState();
|
|
@@ -6044,7 +6043,7 @@ function readState() {
|
|
|
6044
6043
|
function writeState(state) {
|
|
6045
6044
|
try {
|
|
6046
6045
|
ensureStateDir();
|
|
6047
|
-
|
|
6046
|
+
fs13.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
|
|
6048
6047
|
} catch {
|
|
6049
6048
|
}
|
|
6050
6049
|
}
|
|
@@ -6062,6 +6061,8 @@ function getOrCreateEntry(state, cwd) {
|
|
|
6062
6061
|
require_init: false,
|
|
6063
6062
|
last_context_at: void 0,
|
|
6064
6063
|
last_state_change_at: void 0,
|
|
6064
|
+
index_wait_started_at: void 0,
|
|
6065
|
+
index_wait_until: void 0,
|
|
6065
6066
|
updated_at: nowIso()
|
|
6066
6067
|
};
|
|
6067
6068
|
state.workspaces[cwd] = created;
|
|
@@ -6100,6 +6101,8 @@ function clearContextRequired(cwd) {
|
|
|
6100
6101
|
if (!target) return;
|
|
6101
6102
|
target.entry.require_context = false;
|
|
6102
6103
|
target.entry.last_context_at = nowIso();
|
|
6104
|
+
target.entry.index_wait_started_at = void 0;
|
|
6105
|
+
target.entry.index_wait_until = void 0;
|
|
6103
6106
|
target.entry.updated_at = nowIso();
|
|
6104
6107
|
writeState(state);
|
|
6105
6108
|
}
|
|
@@ -6160,11 +6163,49 @@ function isContextFreshAndClean(cwd, maxAgeSeconds) {
|
|
|
6160
6163
|
}
|
|
6161
6164
|
return true;
|
|
6162
6165
|
}
|
|
6166
|
+
function startIndexWaitWindow(cwd, waitSeconds) {
|
|
6167
|
+
if (!cwd.trim() || waitSeconds <= 0) return;
|
|
6168
|
+
const state = readState();
|
|
6169
|
+
const target = getOrCreateEntry(state, cwd);
|
|
6170
|
+
if (!target) return;
|
|
6171
|
+
const now = Date.now();
|
|
6172
|
+
const existingUntil = target.entry.index_wait_until ? new Date(target.entry.index_wait_until).getTime() : NaN;
|
|
6173
|
+
if (!Number.isNaN(existingUntil) && existingUntil > now) {
|
|
6174
|
+
target.entry.updated_at = nowIso();
|
|
6175
|
+
writeState(state);
|
|
6176
|
+
return;
|
|
6177
|
+
}
|
|
6178
|
+
target.entry.index_wait_started_at = new Date(now).toISOString();
|
|
6179
|
+
target.entry.index_wait_until = new Date(now + waitSeconds * 1e3).toISOString();
|
|
6180
|
+
target.entry.updated_at = nowIso();
|
|
6181
|
+
writeState(state);
|
|
6182
|
+
}
|
|
6183
|
+
function clearIndexWaitWindow(cwd) {
|
|
6184
|
+
if (!cwd.trim()) return;
|
|
6185
|
+
const state = readState();
|
|
6186
|
+
const target = getOrCreateEntry(state, cwd);
|
|
6187
|
+
if (!target) return;
|
|
6188
|
+
target.entry.index_wait_started_at = void 0;
|
|
6189
|
+
target.entry.index_wait_until = void 0;
|
|
6190
|
+
target.entry.updated_at = nowIso();
|
|
6191
|
+
writeState(state);
|
|
6192
|
+
}
|
|
6193
|
+
function indexWaitRemainingSeconds(cwd) {
|
|
6194
|
+
if (!cwd.trim()) return null;
|
|
6195
|
+
const state = readState();
|
|
6196
|
+
const target = getOrCreateEntry(state, cwd);
|
|
6197
|
+
if (!target?.entry.index_wait_until) return null;
|
|
6198
|
+
const until = new Date(target.entry.index_wait_until).getTime();
|
|
6199
|
+
if (Number.isNaN(until)) return null;
|
|
6200
|
+
const remainingMs = until - Date.now();
|
|
6201
|
+
if (remainingMs <= 0) return null;
|
|
6202
|
+
return Math.ceil(remainingMs / 1e3);
|
|
6203
|
+
}
|
|
6163
6204
|
var STATE_PATH;
|
|
6164
6205
|
var init_prompt_state = __esm({
|
|
6165
6206
|
"src/hooks/prompt-state.ts"() {
|
|
6166
6207
|
"use strict";
|
|
6167
|
-
STATE_PATH =
|
|
6208
|
+
STATE_PATH = path13.join(homedir11(), ".contextstream", "prompt-state.json");
|
|
6168
6209
|
}
|
|
6169
6210
|
});
|
|
6170
6211
|
|
|
@@ -6173,9 +6214,9 @@ var pre_tool_use_exports = {};
|
|
|
6173
6214
|
__export(pre_tool_use_exports, {
|
|
6174
6215
|
runPreToolUseHook: () => runPreToolUseHook
|
|
6175
6216
|
});
|
|
6176
|
-
import * as
|
|
6177
|
-
import * as
|
|
6178
|
-
import { homedir as
|
|
6217
|
+
import * as fs14 from "node:fs";
|
|
6218
|
+
import * as path14 from "node:path";
|
|
6219
|
+
import { homedir as homedir12 } from "node:os";
|
|
6179
6220
|
function isDiscoveryGlob(pattern) {
|
|
6180
6221
|
const patternLower = pattern.toLowerCase();
|
|
6181
6222
|
for (const p of DISCOVERY_PATTERNS) {
|
|
@@ -6200,23 +6241,50 @@ function isDiscoveryGrep(filePath) {
|
|
|
6200
6241
|
}
|
|
6201
6242
|
return false;
|
|
6202
6243
|
}
|
|
6244
|
+
function configuredIndexWaitSeconds() {
|
|
6245
|
+
const parsed = Number.parseInt(process.env.CONTEXTSTREAM_INDEX_WAIT_SECONDS ?? "", 10);
|
|
6246
|
+
if (Number.isNaN(parsed)) return DEFAULT_INDEX_WAIT_SECONDS;
|
|
6247
|
+
return Math.min(MAX_INDEX_WAIT_SECONDS, Math.max(MIN_INDEX_WAIT_SECONDS, parsed));
|
|
6248
|
+
}
|
|
6249
|
+
function isLocalDiscoveryToolDuringIndexWait(tool, toolInput) {
|
|
6250
|
+
if (tool === "Glob" || tool === "Explore" || tool === "SemanticSearch" || tool === "codebase_search") {
|
|
6251
|
+
return true;
|
|
6252
|
+
}
|
|
6253
|
+
if (tool === "Task") {
|
|
6254
|
+
const subagentTypeRaw = toolInput?.subagent_type || toolInput?.subagentType || "";
|
|
6255
|
+
return subagentTypeRaw.toLowerCase().includes("explore");
|
|
6256
|
+
}
|
|
6257
|
+
if (tool === "Grep" || tool === "Search" || tool === "grep_search" || tool === "code_search") {
|
|
6258
|
+
const filePath = toolInput?.path || "";
|
|
6259
|
+
return isDiscoveryGrep(filePath);
|
|
6260
|
+
}
|
|
6261
|
+
if (tool === "Read" || tool === "ReadFile" || tool === "read_file") {
|
|
6262
|
+
const filePath = toolInput?.file_path || toolInput?.path || toolInput?.file || toolInput?.target_file || "";
|
|
6263
|
+
return isDiscoveryGrep(filePath);
|
|
6264
|
+
}
|
|
6265
|
+
if (tool === "list_files" || tool === "search_files" || tool === "search_files_content" || tool === "find_files" || tool === "find_by_name") {
|
|
6266
|
+
const pattern = toolInput?.path || toolInput?.regex || toolInput?.pattern || toolInput?.query || "";
|
|
6267
|
+
return !pattern || isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern);
|
|
6268
|
+
}
|
|
6269
|
+
return false;
|
|
6270
|
+
}
|
|
6203
6271
|
function isProjectIndexed(cwd) {
|
|
6204
|
-
if (!
|
|
6272
|
+
if (!fs14.existsSync(INDEX_STATUS_FILE)) {
|
|
6205
6273
|
return { isIndexed: false, isStale: false };
|
|
6206
6274
|
}
|
|
6207
6275
|
let data;
|
|
6208
6276
|
try {
|
|
6209
|
-
const content =
|
|
6277
|
+
const content = fs14.readFileSync(INDEX_STATUS_FILE, "utf-8");
|
|
6210
6278
|
data = JSON.parse(content);
|
|
6211
6279
|
} catch {
|
|
6212
6280
|
return { isIndexed: false, isStale: false };
|
|
6213
6281
|
}
|
|
6214
6282
|
const projects = data.projects || {};
|
|
6215
|
-
const cwdPath =
|
|
6283
|
+
const cwdPath = path14.resolve(cwd);
|
|
6216
6284
|
for (const [projectPath, info] of Object.entries(projects)) {
|
|
6217
6285
|
try {
|
|
6218
|
-
const indexedPath =
|
|
6219
|
-
if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath +
|
|
6286
|
+
const indexedPath = path14.resolve(projectPath);
|
|
6287
|
+
if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath + path14.sep)) {
|
|
6220
6288
|
const indexedAt = info.indexed_at;
|
|
6221
6289
|
if (indexedAt) {
|
|
6222
6290
|
try {
|
|
@@ -6348,7 +6416,7 @@ function blockClaudeCode(message) {
|
|
|
6348
6416
|
additionalContext: `[CONTEXTSTREAM] ${message}`
|
|
6349
6417
|
}
|
|
6350
6418
|
};
|
|
6351
|
-
|
|
6419
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] REDIRECT (additionalContext): ${JSON.stringify(response)}
|
|
6352
6420
|
`);
|
|
6353
6421
|
console.log(JSON.stringify(response));
|
|
6354
6422
|
process.exit(0);
|
|
@@ -6405,11 +6473,11 @@ function detectEditorFormat(input) {
|
|
|
6405
6473
|
return "claude";
|
|
6406
6474
|
}
|
|
6407
6475
|
async function runPreToolUseHook() {
|
|
6408
|
-
|
|
6476
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] Hook invoked at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
6409
6477
|
`);
|
|
6410
6478
|
console.error("[PreToolUse] Hook invoked at", (/* @__PURE__ */ new Date()).toISOString());
|
|
6411
6479
|
if (!ENABLED2) {
|
|
6412
|
-
|
|
6480
|
+
fs14.appendFileSync(DEBUG_FILE, "[PreToolUse] Hook disabled, exiting\n");
|
|
6413
6481
|
console.error("[PreToolUse] Hook disabled, exiting");
|
|
6414
6482
|
process.exit(0);
|
|
6415
6483
|
}
|
|
@@ -6439,7 +6507,7 @@ async function runPreToolUseHook() {
|
|
|
6439
6507
|
isContextstreamCall,
|
|
6440
6508
|
normalizedContextstreamTool
|
|
6441
6509
|
);
|
|
6442
|
-
|
|
6510
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] tool=${tool}, cwd=${cwd}, editorFormat=${editorFormat}
|
|
6443
6511
|
`);
|
|
6444
6512
|
cleanupStale(180);
|
|
6445
6513
|
if (isInitRequired(cwd)) {
|
|
@@ -6461,21 +6529,31 @@ async function runPreToolUseHook() {
|
|
|
6461
6529
|
blockWithMessage(editorFormat, msg);
|
|
6462
6530
|
}
|
|
6463
6531
|
}
|
|
6464
|
-
const { isIndexed } = isProjectIndexed(cwd);
|
|
6465
|
-
|
|
6466
|
-
`);
|
|
6467
|
-
if (!isIndexed) {
|
|
6468
|
-
fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] Project not indexed, allowing
|
|
6532
|
+
const { isIndexed, isStale } = isProjectIndexed(cwd);
|
|
6533
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}, isStale=${isStale}
|
|
6469
6534
|
`);
|
|
6535
|
+
if (isIndexed && !isStale) {
|
|
6536
|
+
clearIndexWaitWindow(cwd);
|
|
6537
|
+
} else {
|
|
6538
|
+
const waitSeconds = configuredIndexWaitSeconds();
|
|
6539
|
+
if (isLocalDiscoveryToolDuringIndexWait(tool, toolInput)) {
|
|
6540
|
+
startIndexWaitWindow(cwd, waitSeconds);
|
|
6541
|
+
const remaining = indexWaitRemainingSeconds(cwd);
|
|
6542
|
+
if (remaining && remaining > 0) {
|
|
6543
|
+
const msg = `Index refresh grace window is active (${remaining}s remaining). Keep ContextStream search-first flow: mcp__contextstream__search(mode="auto", query="..."). Do not use local discovery tools yet for stale/not-indexed projects. Retry after refresh; local fallback is allowed only after ~${waitSeconds}s if index is still unavailable.`;
|
|
6544
|
+
blockWithMessage(editorFormat, msg);
|
|
6545
|
+
}
|
|
6546
|
+
allowTool(editorFormat, cwd, recordStateChange);
|
|
6547
|
+
}
|
|
6470
6548
|
allowTool(editorFormat, cwd, recordStateChange);
|
|
6471
6549
|
}
|
|
6472
6550
|
if (tool === "Glob") {
|
|
6473
6551
|
const pattern = toolInput?.pattern || "";
|
|
6474
|
-
|
|
6552
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
6475
6553
|
`);
|
|
6476
6554
|
if (isDiscoveryGlob(pattern)) {
|
|
6477
6555
|
const msg = `This project index is current. Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob for faster, richer code results.`;
|
|
6478
|
-
|
|
6556
|
+
fs14.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
6479
6557
|
`);
|
|
6480
6558
|
if (editorFormat === "cline") {
|
|
6481
6559
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
@@ -6558,16 +6636,19 @@ async function runPreToolUseHook() {
|
|
|
6558
6636
|
}
|
|
6559
6637
|
allowTool(editorFormat, cwd, recordStateChange);
|
|
6560
6638
|
}
|
|
6561
|
-
var ENABLED2, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, CONTEXT_FRESHNESS_SECONDS, DISCOVERY_PATTERNS, isDirectRun9;
|
|
6639
|
+
var ENABLED2, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, CONTEXT_FRESHNESS_SECONDS, DEFAULT_INDEX_WAIT_SECONDS, MIN_INDEX_WAIT_SECONDS, MAX_INDEX_WAIT_SECONDS, DISCOVERY_PATTERNS, isDirectRun9;
|
|
6562
6640
|
var init_pre_tool_use = __esm({
|
|
6563
6641
|
"src/hooks/pre-tool-use.ts"() {
|
|
6564
6642
|
"use strict";
|
|
6565
6643
|
init_prompt_state();
|
|
6566
6644
|
ENABLED2 = process.env.CONTEXTSTREAM_HOOK_ENABLED !== "false";
|
|
6567
|
-
INDEX_STATUS_FILE =
|
|
6645
|
+
INDEX_STATUS_FILE = path14.join(homedir12(), ".contextstream", "indexed-projects.json");
|
|
6568
6646
|
DEBUG_FILE = "/tmp/pretooluse-hook-debug.log";
|
|
6569
6647
|
STALE_THRESHOLD_DAYS = 7;
|
|
6570
6648
|
CONTEXT_FRESHNESS_SECONDS = 120;
|
|
6649
|
+
DEFAULT_INDEX_WAIT_SECONDS = 20;
|
|
6650
|
+
MIN_INDEX_WAIT_SECONDS = 15;
|
|
6651
|
+
MAX_INDEX_WAIT_SECONDS = 20;
|
|
6571
6652
|
DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"];
|
|
6572
6653
|
isDirectRun9 = process.argv[1]?.includes("pre-tool-use") || process.argv[2] === "pre-tool-use";
|
|
6573
6654
|
if (isDirectRun9) {
|
|
@@ -6581,17 +6662,17 @@ var user_prompt_submit_exports = {};
|
|
|
6581
6662
|
__export(user_prompt_submit_exports, {
|
|
6582
6663
|
runUserPromptSubmitHook: () => runUserPromptSubmitHook
|
|
6583
6664
|
});
|
|
6584
|
-
import * as
|
|
6585
|
-
import * as
|
|
6586
|
-
import { homedir as
|
|
6665
|
+
import * as fs15 from "node:fs";
|
|
6666
|
+
import * as path15 from "node:path";
|
|
6667
|
+
import { homedir as homedir13 } from "node:os";
|
|
6587
6668
|
function loadConfigFromMcpJson(cwd) {
|
|
6588
|
-
let searchDir =
|
|
6669
|
+
let searchDir = path15.resolve(cwd);
|
|
6589
6670
|
for (let i = 0; i < 5; i++) {
|
|
6590
6671
|
if (!API_KEY2) {
|
|
6591
|
-
const mcpPath =
|
|
6592
|
-
if (
|
|
6672
|
+
const mcpPath = path15.join(searchDir, ".mcp.json");
|
|
6673
|
+
if (fs15.existsSync(mcpPath)) {
|
|
6593
6674
|
try {
|
|
6594
|
-
const content =
|
|
6675
|
+
const content = fs15.readFileSync(mcpPath, "utf-8");
|
|
6595
6676
|
const config = JSON.parse(content);
|
|
6596
6677
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
6597
6678
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -6608,10 +6689,10 @@ function loadConfigFromMcpJson(cwd) {
|
|
|
6608
6689
|
}
|
|
6609
6690
|
}
|
|
6610
6691
|
if (!WORKSPACE_ID || !PROJECT_ID) {
|
|
6611
|
-
const csConfigPath =
|
|
6612
|
-
if (
|
|
6692
|
+
const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
|
|
6693
|
+
if (fs15.existsSync(csConfigPath)) {
|
|
6613
6694
|
try {
|
|
6614
|
-
const content =
|
|
6695
|
+
const content = fs15.readFileSync(csConfigPath, "utf-8");
|
|
6615
6696
|
const csConfig = JSON.parse(content);
|
|
6616
6697
|
if (csConfig.workspace_id && !WORKSPACE_ID) {
|
|
6617
6698
|
WORKSPACE_ID = csConfig.workspace_id;
|
|
@@ -6623,15 +6704,15 @@ function loadConfigFromMcpJson(cwd) {
|
|
|
6623
6704
|
}
|
|
6624
6705
|
}
|
|
6625
6706
|
}
|
|
6626
|
-
const parentDir =
|
|
6707
|
+
const parentDir = path15.dirname(searchDir);
|
|
6627
6708
|
if (parentDir === searchDir) break;
|
|
6628
6709
|
searchDir = parentDir;
|
|
6629
6710
|
}
|
|
6630
6711
|
if (!API_KEY2) {
|
|
6631
|
-
const homeMcpPath =
|
|
6632
|
-
if (
|
|
6712
|
+
const homeMcpPath = path15.join(homedir13(), ".mcp.json");
|
|
6713
|
+
if (fs15.existsSync(homeMcpPath)) {
|
|
6633
6714
|
try {
|
|
6634
|
-
const content =
|
|
6715
|
+
const content = fs15.readFileSync(homeMcpPath, "utf-8");
|
|
6635
6716
|
const config = JSON.parse(content);
|
|
6636
6717
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
6637
6718
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -6647,7 +6728,7 @@ function loadConfigFromMcpJson(cwd) {
|
|
|
6647
6728
|
}
|
|
6648
6729
|
function readTranscriptFile(transcriptPath) {
|
|
6649
6730
|
try {
|
|
6650
|
-
const content =
|
|
6731
|
+
const content = fs15.readFileSync(transcriptPath, "utf-8");
|
|
6651
6732
|
const lines = content.trim().split("\n");
|
|
6652
6733
|
const messages = [];
|
|
6653
6734
|
for (const line of lines) {
|
|
@@ -6987,23 +7068,22 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
|
|
|
6987
7068
|
\u2192 Use \`search(mode="auto", query="...")\`
|
|
6988
7069
|
|
|
6989
7070
|
**IF indexed=false OR last_indexed_at is stale (>7 days):**
|
|
6990
|
-
\u2192
|
|
6991
|
-
\u2192
|
|
7071
|
+
\u2192 Wait up to ~20s for background refresh, retry \`search(mode="auto", query="...")\`
|
|
7072
|
+
\u2192 After grace window: local tools are allowed if search still misses
|
|
6992
7073
|
|
|
6993
|
-
**IF search returns 0 results or errors:**
|
|
7074
|
+
**IF search still returns 0 results or errors after retry/window:**
|
|
6994
7075
|
\u2192 Fallback to local tools (Glob/Grep/Read)
|
|
6995
7076
|
|
|
6996
7077
|
### \u2705 When Local Tools (Glob/Grep/Read) Are OK:
|
|
6997
|
-
-
|
|
6998
|
-
-
|
|
6999
|
-
- ContextStream search returns 0 results
|
|
7078
|
+
- Stale/not-indexed grace window has elapsed (~20s default, configurable)
|
|
7079
|
+
- ContextStream search still returns 0 results after retry
|
|
7000
7080
|
- ContextStream returns errors
|
|
7001
7081
|
- User explicitly requests local tools
|
|
7002
7082
|
|
|
7003
7083
|
### On Session Start:
|
|
7004
7084
|
1. Call \`init(folder_path="...")\` - triggers initial indexing
|
|
7005
7085
|
2. Check \`project(action="index_status")\` before searching
|
|
7006
|
-
3. If not indexed: use local tools
|
|
7086
|
+
3. If not indexed: wait for background refresh (~20s), retry search, then use local tools only after the grace window
|
|
7007
7087
|
|
|
7008
7088
|
### After File Changes (Edit/Write/Create):
|
|
7009
7089
|
Files are NOT auto-indexed. You MUST:
|
|
@@ -7138,13 +7218,12 @@ COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via
|
|
|
7138
7218
|
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
7139
7219
|
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
7140
7220
|
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...") BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode
|
|
7141
|
-
\u2705 IF NOT indexed OR stale:
|
|
7142
|
-
\u2705 IF search returns 0 results:
|
|
7221
|
+
\u2705 IF NOT indexed OR stale: wait for background refresh up to ~20s, retry mcp__contextstream__search(mode="auto", query="..."), then allow local tools only after the grace window
|
|
7222
|
+
\u2705 IF search still returns 0 results after retry/window: fallback to local tools (Glob/Grep/Read)
|
|
7143
7223
|
|
|
7144
7224
|
3. WHEN LOCAL TOOLS (Glob/Grep/Read) ARE OK:
|
|
7145
|
-
\u2705
|
|
7146
|
-
\u2705
|
|
7147
|
-
\u2705 ContextStream search returns 0 results or errors
|
|
7225
|
+
\u2705 Stale/not-indexed grace window has elapsed (~20s default, configurable)
|
|
7226
|
+
\u2705 ContextStream search still returns 0 results or errors after retry
|
|
7148
7227
|
\u2705 User explicitly requests local tools
|
|
7149
7228
|
|
|
7150
7229
|
4. FOR PLANS & TASKS: Use ContextStream, not file-based plans
|
|
@@ -7190,17 +7269,17 @@ var pre_compact_exports = {};
|
|
|
7190
7269
|
__export(pre_compact_exports, {
|
|
7191
7270
|
runPreCompactHook: () => runPreCompactHook
|
|
7192
7271
|
});
|
|
7193
|
-
import * as
|
|
7194
|
-
import * as
|
|
7195
|
-
import { homedir as
|
|
7272
|
+
import * as fs16 from "node:fs";
|
|
7273
|
+
import * as path16 from "node:path";
|
|
7274
|
+
import { homedir as homedir14 } from "node:os";
|
|
7196
7275
|
function loadConfigFromMcpJson2(cwd) {
|
|
7197
|
-
let searchDir =
|
|
7276
|
+
let searchDir = path16.resolve(cwd);
|
|
7198
7277
|
for (let i = 0; i < 5; i++) {
|
|
7199
7278
|
if (!API_KEY3) {
|
|
7200
|
-
const mcpPath =
|
|
7201
|
-
if (
|
|
7279
|
+
const mcpPath = path16.join(searchDir, ".mcp.json");
|
|
7280
|
+
if (fs16.existsSync(mcpPath)) {
|
|
7202
7281
|
try {
|
|
7203
|
-
const content =
|
|
7282
|
+
const content = fs16.readFileSync(mcpPath, "utf-8");
|
|
7204
7283
|
const config = JSON.parse(content);
|
|
7205
7284
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7206
7285
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7214,10 +7293,10 @@ function loadConfigFromMcpJson2(cwd) {
|
|
|
7214
7293
|
}
|
|
7215
7294
|
}
|
|
7216
7295
|
if (!WORKSPACE_ID2) {
|
|
7217
|
-
const csConfigPath =
|
|
7218
|
-
if (
|
|
7296
|
+
const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
|
|
7297
|
+
if (fs16.existsSync(csConfigPath)) {
|
|
7219
7298
|
try {
|
|
7220
|
-
const content =
|
|
7299
|
+
const content = fs16.readFileSync(csConfigPath, "utf-8");
|
|
7221
7300
|
const csConfig = JSON.parse(content);
|
|
7222
7301
|
if (csConfig.workspace_id) {
|
|
7223
7302
|
WORKSPACE_ID2 = csConfig.workspace_id;
|
|
@@ -7226,15 +7305,15 @@ function loadConfigFromMcpJson2(cwd) {
|
|
|
7226
7305
|
}
|
|
7227
7306
|
}
|
|
7228
7307
|
}
|
|
7229
|
-
const parentDir =
|
|
7308
|
+
const parentDir = path16.dirname(searchDir);
|
|
7230
7309
|
if (parentDir === searchDir) break;
|
|
7231
7310
|
searchDir = parentDir;
|
|
7232
7311
|
}
|
|
7233
7312
|
if (!API_KEY3) {
|
|
7234
|
-
const homeMcpPath =
|
|
7235
|
-
if (
|
|
7313
|
+
const homeMcpPath = path16.join(homedir14(), ".mcp.json");
|
|
7314
|
+
if (fs16.existsSync(homeMcpPath)) {
|
|
7236
7315
|
try {
|
|
7237
|
-
const content =
|
|
7316
|
+
const content = fs16.readFileSync(homeMcpPath, "utf-8");
|
|
7238
7317
|
const config = JSON.parse(content);
|
|
7239
7318
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7240
7319
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7256,7 +7335,7 @@ function parseTranscript2(transcriptPath) {
|
|
|
7256
7335
|
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7257
7336
|
let firstTimestamp = true;
|
|
7258
7337
|
try {
|
|
7259
|
-
const content =
|
|
7338
|
+
const content = fs16.readFileSync(transcriptPath, "utf-8");
|
|
7260
7339
|
const lines = content.split("\n");
|
|
7261
7340
|
for (const line of lines) {
|
|
7262
7341
|
if (!line.trim()) continue;
|
|
@@ -7457,7 +7536,7 @@ async function runPreCompactHook() {
|
|
|
7457
7536
|
messages: [],
|
|
7458
7537
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7459
7538
|
};
|
|
7460
|
-
if (transcriptPath &&
|
|
7539
|
+
if (transcriptPath && fs16.existsSync(transcriptPath)) {
|
|
7461
7540
|
transcriptData = parseTranscript2(transcriptPath);
|
|
7462
7541
|
}
|
|
7463
7542
|
let autoSaveStatus = "";
|
|
@@ -7513,17 +7592,17 @@ var post_compact_exports = {};
|
|
|
7513
7592
|
__export(post_compact_exports, {
|
|
7514
7593
|
runPostCompactHook: () => runPostCompactHook
|
|
7515
7594
|
});
|
|
7516
|
-
import * as
|
|
7517
|
-
import * as
|
|
7518
|
-
import { homedir as
|
|
7595
|
+
import * as fs17 from "node:fs";
|
|
7596
|
+
import * as path17 from "node:path";
|
|
7597
|
+
import { homedir as homedir15 } from "node:os";
|
|
7519
7598
|
function loadConfigFromMcpJson3(cwd) {
|
|
7520
|
-
let searchDir =
|
|
7599
|
+
let searchDir = path17.resolve(cwd);
|
|
7521
7600
|
for (let i = 0; i < 5; i++) {
|
|
7522
7601
|
if (!API_KEY4) {
|
|
7523
|
-
const mcpPath =
|
|
7524
|
-
if (
|
|
7602
|
+
const mcpPath = path17.join(searchDir, ".mcp.json");
|
|
7603
|
+
if (fs17.existsSync(mcpPath)) {
|
|
7525
7604
|
try {
|
|
7526
|
-
const content =
|
|
7605
|
+
const content = fs17.readFileSync(mcpPath, "utf-8");
|
|
7527
7606
|
const config = JSON.parse(content);
|
|
7528
7607
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7529
7608
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7537,10 +7616,10 @@ function loadConfigFromMcpJson3(cwd) {
|
|
|
7537
7616
|
}
|
|
7538
7617
|
}
|
|
7539
7618
|
if (!WORKSPACE_ID3) {
|
|
7540
|
-
const csConfigPath =
|
|
7541
|
-
if (
|
|
7619
|
+
const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
|
|
7620
|
+
if (fs17.existsSync(csConfigPath)) {
|
|
7542
7621
|
try {
|
|
7543
|
-
const content =
|
|
7622
|
+
const content = fs17.readFileSync(csConfigPath, "utf-8");
|
|
7544
7623
|
const csConfig = JSON.parse(content);
|
|
7545
7624
|
if (csConfig.workspace_id) {
|
|
7546
7625
|
WORKSPACE_ID3 = csConfig.workspace_id;
|
|
@@ -7549,15 +7628,15 @@ function loadConfigFromMcpJson3(cwd) {
|
|
|
7549
7628
|
}
|
|
7550
7629
|
}
|
|
7551
7630
|
}
|
|
7552
|
-
const parentDir =
|
|
7631
|
+
const parentDir = path17.dirname(searchDir);
|
|
7553
7632
|
if (parentDir === searchDir) break;
|
|
7554
7633
|
searchDir = parentDir;
|
|
7555
7634
|
}
|
|
7556
7635
|
if (!API_KEY4) {
|
|
7557
|
-
const homeMcpPath =
|
|
7558
|
-
if (
|
|
7636
|
+
const homeMcpPath = path17.join(homedir15(), ".mcp.json");
|
|
7637
|
+
if (fs17.existsSync(homeMcpPath)) {
|
|
7559
7638
|
try {
|
|
7560
|
-
const content =
|
|
7639
|
+
const content = fs17.readFileSync(homeMcpPath, "utf-8");
|
|
7561
7640
|
const config = JSON.parse(content);
|
|
7562
7641
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7563
7642
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7691,17 +7770,17 @@ var session_init_exports = {};
|
|
|
7691
7770
|
__export(session_init_exports, {
|
|
7692
7771
|
runSessionInitHook: () => runSessionInitHook
|
|
7693
7772
|
});
|
|
7694
|
-
import * as
|
|
7695
|
-
import * as
|
|
7696
|
-
import { homedir as
|
|
7773
|
+
import * as fs18 from "node:fs";
|
|
7774
|
+
import * as path18 from "node:path";
|
|
7775
|
+
import { homedir as homedir16 } from "node:os";
|
|
7697
7776
|
function loadConfigFromMcpJson4(cwd) {
|
|
7698
|
-
let searchDir =
|
|
7777
|
+
let searchDir = path18.resolve(cwd);
|
|
7699
7778
|
for (let i = 0; i < 5; i++) {
|
|
7700
7779
|
if (!API_KEY5) {
|
|
7701
|
-
const mcpPath =
|
|
7702
|
-
if (
|
|
7780
|
+
const mcpPath = path18.join(searchDir, ".mcp.json");
|
|
7781
|
+
if (fs18.existsSync(mcpPath)) {
|
|
7703
7782
|
try {
|
|
7704
|
-
const content =
|
|
7783
|
+
const content = fs18.readFileSync(mcpPath, "utf-8");
|
|
7705
7784
|
const config = JSON.parse(content);
|
|
7706
7785
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7707
7786
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7718,10 +7797,10 @@ function loadConfigFromMcpJson4(cwd) {
|
|
|
7718
7797
|
}
|
|
7719
7798
|
}
|
|
7720
7799
|
if (!WORKSPACE_ID4 || !PROJECT_ID2) {
|
|
7721
|
-
const csConfigPath =
|
|
7722
|
-
if (
|
|
7800
|
+
const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
|
|
7801
|
+
if (fs18.existsSync(csConfigPath)) {
|
|
7723
7802
|
try {
|
|
7724
|
-
const content =
|
|
7803
|
+
const content = fs18.readFileSync(csConfigPath, "utf-8");
|
|
7725
7804
|
const csConfig = JSON.parse(content);
|
|
7726
7805
|
if (csConfig.workspace_id && !WORKSPACE_ID4) {
|
|
7727
7806
|
WORKSPACE_ID4 = csConfig.workspace_id;
|
|
@@ -7733,15 +7812,15 @@ function loadConfigFromMcpJson4(cwd) {
|
|
|
7733
7812
|
}
|
|
7734
7813
|
}
|
|
7735
7814
|
}
|
|
7736
|
-
const parentDir =
|
|
7815
|
+
const parentDir = path18.dirname(searchDir);
|
|
7737
7816
|
if (parentDir === searchDir) break;
|
|
7738
7817
|
searchDir = parentDir;
|
|
7739
7818
|
}
|
|
7740
7819
|
if (!API_KEY5) {
|
|
7741
|
-
const homeMcpPath =
|
|
7742
|
-
if (
|
|
7820
|
+
const homeMcpPath = path18.join(homedir16(), ".mcp.json");
|
|
7821
|
+
if (fs18.existsSync(homeMcpPath)) {
|
|
7743
7822
|
try {
|
|
7744
|
-
const content =
|
|
7823
|
+
const content = fs18.readFileSync(homeMcpPath, "utf-8");
|
|
7745
7824
|
const config = JSON.parse(content);
|
|
7746
7825
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
7747
7826
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -7860,10 +7939,10 @@ function regenerateRuleFiles(folderPath) {
|
|
|
7860
7939
|
for (const editor of editors) {
|
|
7861
7940
|
const rule = generateRuleContent(editor, { mode: "bootstrap" });
|
|
7862
7941
|
if (!rule) continue;
|
|
7863
|
-
const filePath =
|
|
7864
|
-
if (!
|
|
7942
|
+
const filePath = path18.join(folderPath, rule.filename);
|
|
7943
|
+
if (!fs18.existsSync(filePath)) continue;
|
|
7865
7944
|
try {
|
|
7866
|
-
const existing =
|
|
7945
|
+
const existing = fs18.readFileSync(filePath, "utf8");
|
|
7867
7946
|
const startIdx = existing.indexOf(CONTEXTSTREAM_START_MARKER3);
|
|
7868
7947
|
const endIdx = existing.indexOf(CONTEXTSTREAM_END_MARKER3);
|
|
7869
7948
|
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) continue;
|
|
@@ -7873,7 +7952,7 @@ function regenerateRuleFiles(folderPath) {
|
|
|
7873
7952
|
${rule.content.trim()}
|
|
7874
7953
|
${CONTEXTSTREAM_END_MARKER3}`;
|
|
7875
7954
|
const merged = [before, newBlock, after].filter((p) => p.length > 0).join("\n\n");
|
|
7876
|
-
|
|
7955
|
+
fs18.writeFileSync(filePath, merged.trim() + "\n", "utf8");
|
|
7877
7956
|
updated++;
|
|
7878
7957
|
} catch {
|
|
7879
7958
|
}
|
|
@@ -7995,17 +8074,17 @@ var session_end_exports = {};
|
|
|
7995
8074
|
__export(session_end_exports, {
|
|
7996
8075
|
runSessionEndHook: () => runSessionEndHook
|
|
7997
8076
|
});
|
|
7998
|
-
import * as
|
|
7999
|
-
import * as
|
|
8000
|
-
import { homedir as
|
|
8077
|
+
import * as fs19 from "node:fs";
|
|
8078
|
+
import * as path19 from "node:path";
|
|
8079
|
+
import { homedir as homedir17 } from "node:os";
|
|
8001
8080
|
function loadConfigFromMcpJson5(cwd) {
|
|
8002
|
-
let searchDir =
|
|
8081
|
+
let searchDir = path19.resolve(cwd);
|
|
8003
8082
|
for (let i = 0; i < 5; i++) {
|
|
8004
8083
|
if (!API_KEY6) {
|
|
8005
|
-
const mcpPath =
|
|
8006
|
-
if (
|
|
8084
|
+
const mcpPath = path19.join(searchDir, ".mcp.json");
|
|
8085
|
+
if (fs19.existsSync(mcpPath)) {
|
|
8007
8086
|
try {
|
|
8008
|
-
const content =
|
|
8087
|
+
const content = fs19.readFileSync(mcpPath, "utf-8");
|
|
8009
8088
|
const config = JSON.parse(content);
|
|
8010
8089
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
8011
8090
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -8019,10 +8098,10 @@ function loadConfigFromMcpJson5(cwd) {
|
|
|
8019
8098
|
}
|
|
8020
8099
|
}
|
|
8021
8100
|
if (!WORKSPACE_ID5 || !PROJECT_ID3) {
|
|
8022
|
-
const csConfigPath =
|
|
8023
|
-
if (
|
|
8101
|
+
const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
|
|
8102
|
+
if (fs19.existsSync(csConfigPath)) {
|
|
8024
8103
|
try {
|
|
8025
|
-
const content =
|
|
8104
|
+
const content = fs19.readFileSync(csConfigPath, "utf-8");
|
|
8026
8105
|
const csConfig = JSON.parse(content);
|
|
8027
8106
|
if (csConfig.workspace_id && !WORKSPACE_ID5) {
|
|
8028
8107
|
WORKSPACE_ID5 = csConfig.workspace_id;
|
|
@@ -8034,15 +8113,15 @@ function loadConfigFromMcpJson5(cwd) {
|
|
|
8034
8113
|
}
|
|
8035
8114
|
}
|
|
8036
8115
|
}
|
|
8037
|
-
const parentDir =
|
|
8116
|
+
const parentDir = path19.dirname(searchDir);
|
|
8038
8117
|
if (parentDir === searchDir) break;
|
|
8039
8118
|
searchDir = parentDir;
|
|
8040
8119
|
}
|
|
8041
8120
|
if (!API_KEY6) {
|
|
8042
|
-
const homeMcpPath =
|
|
8043
|
-
if (
|
|
8121
|
+
const homeMcpPath = path19.join(homedir17(), ".mcp.json");
|
|
8122
|
+
if (fs19.existsSync(homeMcpPath)) {
|
|
8044
8123
|
try {
|
|
8045
|
-
const content =
|
|
8124
|
+
const content = fs19.readFileSync(homeMcpPath, "utf-8");
|
|
8046
8125
|
const config = JSON.parse(content);
|
|
8047
8126
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
8048
8127
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
@@ -8065,11 +8144,11 @@ function parseTranscriptStats(transcriptPath) {
|
|
|
8065
8144
|
messages: [],
|
|
8066
8145
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8067
8146
|
};
|
|
8068
|
-
if (!transcriptPath || !
|
|
8147
|
+
if (!transcriptPath || !fs19.existsSync(transcriptPath)) {
|
|
8069
8148
|
return stats;
|
|
8070
8149
|
}
|
|
8071
8150
|
try {
|
|
8072
|
-
const content =
|
|
8151
|
+
const content = fs19.readFileSync(transcriptPath, "utf-8");
|
|
8073
8152
|
const lines = content.split("\n");
|
|
8074
8153
|
let firstTimestamp = null;
|
|
8075
8154
|
let lastTimestamp = null;
|
|
@@ -8427,9 +8506,9 @@ __export(verify_key_exports, {
|
|
|
8427
8506
|
runVerifyKey: () => runVerifyKey,
|
|
8428
8507
|
validateApiKey: () => validateApiKey
|
|
8429
8508
|
});
|
|
8430
|
-
import * as
|
|
8431
|
-
import * as
|
|
8432
|
-
import { homedir as
|
|
8509
|
+
import * as fs20 from "node:fs";
|
|
8510
|
+
import * as path20 from "node:path";
|
|
8511
|
+
import { homedir as homedir18 } from "node:os";
|
|
8433
8512
|
function maskApiKey2(key) {
|
|
8434
8513
|
if (!key || key.length < 8) return "***";
|
|
8435
8514
|
const prefixMatch = key.match(/^([a-z]{2,3}_)/i);
|
|
@@ -8462,11 +8541,11 @@ function extractFromMcpConfig(config) {
|
|
|
8462
8541
|
function getClaudeDesktopConfigPath() {
|
|
8463
8542
|
const platform2 = process.platform;
|
|
8464
8543
|
if (platform2 === "darwin") {
|
|
8465
|
-
return
|
|
8544
|
+
return path20.join(homedir18(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
8466
8545
|
} else if (platform2 === "win32") {
|
|
8467
|
-
return
|
|
8546
|
+
return path20.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
8468
8547
|
} else {
|
|
8469
|
-
return
|
|
8548
|
+
return path20.join(homedir18(), ".config", "Claude", "claude_desktop_config.json");
|
|
8470
8549
|
}
|
|
8471
8550
|
}
|
|
8472
8551
|
function loadApiKey() {
|
|
@@ -8483,10 +8562,10 @@ function loadApiKey() {
|
|
|
8483
8562
|
}
|
|
8484
8563
|
let searchDir = process.cwd();
|
|
8485
8564
|
for (let i = 0; i < 5; i++) {
|
|
8486
|
-
const projectMcpPath =
|
|
8487
|
-
if (
|
|
8565
|
+
const projectMcpPath = path20.join(searchDir, ".mcp.json");
|
|
8566
|
+
if (fs20.existsSync(projectMcpPath)) {
|
|
8488
8567
|
try {
|
|
8489
|
-
const content =
|
|
8568
|
+
const content = fs20.readFileSync(projectMcpPath, "utf-8");
|
|
8490
8569
|
const config = JSON.parse(content);
|
|
8491
8570
|
const extracted = extractFromMcpConfig(config);
|
|
8492
8571
|
if (extracted.apiKey) {
|
|
@@ -8500,14 +8579,14 @@ function loadApiKey() {
|
|
|
8500
8579
|
} catch {
|
|
8501
8580
|
}
|
|
8502
8581
|
}
|
|
8503
|
-
const parentDir =
|
|
8582
|
+
const parentDir = path20.dirname(searchDir);
|
|
8504
8583
|
if (parentDir === searchDir) break;
|
|
8505
8584
|
searchDir = parentDir;
|
|
8506
8585
|
}
|
|
8507
|
-
const globalMcpPath =
|
|
8508
|
-
if (
|
|
8586
|
+
const globalMcpPath = path20.join(homedir18(), ".mcp.json");
|
|
8587
|
+
if (fs20.existsSync(globalMcpPath)) {
|
|
8509
8588
|
try {
|
|
8510
|
-
const content =
|
|
8589
|
+
const content = fs20.readFileSync(globalMcpPath, "utf-8");
|
|
8511
8590
|
const config = JSON.parse(content);
|
|
8512
8591
|
const extracted = extractFromMcpConfig(config);
|
|
8513
8592
|
if (extracted.apiKey) {
|
|
@@ -8522,13 +8601,13 @@ function loadApiKey() {
|
|
|
8522
8601
|
}
|
|
8523
8602
|
}
|
|
8524
8603
|
const cursorPaths = [
|
|
8525
|
-
|
|
8526
|
-
|
|
8604
|
+
path20.join(process.cwd(), ".cursor", "mcp.json"),
|
|
8605
|
+
path20.join(homedir18(), ".cursor", "mcp.json")
|
|
8527
8606
|
];
|
|
8528
8607
|
for (const cursorPath of cursorPaths) {
|
|
8529
|
-
if (
|
|
8608
|
+
if (fs20.existsSync(cursorPath)) {
|
|
8530
8609
|
try {
|
|
8531
|
-
const content =
|
|
8610
|
+
const content = fs20.readFileSync(cursorPath, "utf-8");
|
|
8532
8611
|
const config = JSON.parse(content);
|
|
8533
8612
|
const extracted = extractFromMcpConfig(config);
|
|
8534
8613
|
if (extracted.apiKey) {
|
|
@@ -8544,9 +8623,9 @@ function loadApiKey() {
|
|
|
8544
8623
|
}
|
|
8545
8624
|
}
|
|
8546
8625
|
const claudeDesktopPath = getClaudeDesktopConfigPath();
|
|
8547
|
-
if (
|
|
8626
|
+
if (fs20.existsSync(claudeDesktopPath)) {
|
|
8548
8627
|
try {
|
|
8549
|
-
const content =
|
|
8628
|
+
const content = fs20.readFileSync(claudeDesktopPath, "utf-8");
|
|
8550
8629
|
const config = JSON.parse(content);
|
|
8551
8630
|
const extracted = extractFromMcpConfig(config);
|
|
8552
8631
|
if (extracted.apiKey) {
|
|
@@ -8561,14 +8640,14 @@ function loadApiKey() {
|
|
|
8561
8640
|
}
|
|
8562
8641
|
}
|
|
8563
8642
|
const vscodePaths = [
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8643
|
+
path20.join(homedir18(), ".vscode", "mcp.json"),
|
|
8644
|
+
path20.join(homedir18(), ".codeium", "windsurf", "mcp_config.json"),
|
|
8645
|
+
path20.join(homedir18(), ".continue", "config.json")
|
|
8567
8646
|
];
|
|
8568
8647
|
for (const vsPath of vscodePaths) {
|
|
8569
|
-
if (
|
|
8648
|
+
if (fs20.existsSync(vsPath)) {
|
|
8570
8649
|
try {
|
|
8571
|
-
const content =
|
|
8650
|
+
const content = fs20.readFileSync(vsPath, "utf-8");
|
|
8572
8651
|
const config = JSON.parse(content);
|
|
8573
8652
|
const extracted = extractFromMcpConfig(config);
|
|
8574
8653
|
if (extracted.apiKey) {
|
|
@@ -8583,10 +8662,10 @@ function loadApiKey() {
|
|
|
8583
8662
|
}
|
|
8584
8663
|
}
|
|
8585
8664
|
}
|
|
8586
|
-
const credentialsPath =
|
|
8587
|
-
if (
|
|
8665
|
+
const credentialsPath = path20.join(homedir18(), ".contextstream", "credentials.json");
|
|
8666
|
+
if (fs20.existsSync(credentialsPath)) {
|
|
8588
8667
|
try {
|
|
8589
|
-
const content =
|
|
8668
|
+
const content = fs20.readFileSync(credentialsPath, "utf-8");
|
|
8590
8669
|
const creds = JSON.parse(content);
|
|
8591
8670
|
if (creds.api_key) {
|
|
8592
8671
|
apiKey = creds.api_key;
|
|
@@ -9184,8 +9263,8 @@ function getErrorMap() {
|
|
|
9184
9263
|
|
|
9185
9264
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
9186
9265
|
var makeIssue = (params) => {
|
|
9187
|
-
const { data, path:
|
|
9188
|
-
const fullPath = [...
|
|
9266
|
+
const { data, path: path21, errorMaps, issueData } = params;
|
|
9267
|
+
const fullPath = [...path21, ...issueData.path || []];
|
|
9189
9268
|
const fullIssue = {
|
|
9190
9269
|
...issueData,
|
|
9191
9270
|
path: fullPath
|
|
@@ -9301,11 +9380,11 @@ var errorUtil;
|
|
|
9301
9380
|
|
|
9302
9381
|
// node_modules/zod/v3/types.js
|
|
9303
9382
|
var ParseInputLazyPath = class {
|
|
9304
|
-
constructor(parent, value,
|
|
9383
|
+
constructor(parent, value, path21, key) {
|
|
9305
9384
|
this._cachedPath = [];
|
|
9306
9385
|
this.parent = parent;
|
|
9307
9386
|
this.data = value;
|
|
9308
|
-
this._path =
|
|
9387
|
+
this._path = path21;
|
|
9309
9388
|
this._key = key;
|
|
9310
9389
|
}
|
|
9311
9390
|
get path() {
|
|
@@ -12874,12 +12953,12 @@ var BASE_DELAY = 1e3;
|
|
|
12874
12953
|
async function sleep(ms) {
|
|
12875
12954
|
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
12876
12955
|
}
|
|
12877
|
-
async function request(config,
|
|
12956
|
+
async function request(config, path21, options = {}) {
|
|
12878
12957
|
const { apiUrl, userAgent } = config;
|
|
12879
12958
|
const authOverride = getAuthOverride();
|
|
12880
12959
|
const apiKey = authOverride?.apiKey ?? config.apiKey;
|
|
12881
12960
|
const jwt = authOverride?.jwt ?? config.jwt;
|
|
12882
|
-
const rawPath =
|
|
12961
|
+
const rawPath = path21.startsWith("/") ? path21 : `/${path21}`;
|
|
12883
12962
|
const apiPath = rawPath.startsWith("/api/") ? rawPath : `/api/v1${rawPath}`;
|
|
12884
12963
|
const unauthenticatedEndpoints = ["/api/v1/auth/device/start", "/api/v1/auth/device/token"];
|
|
12885
12964
|
const isUnauthenticatedEndpoint = unauthenticatedEndpoints.some(
|
|
@@ -13034,9 +13113,9 @@ function extractErrorCode(payload) {
|
|
|
13034
13113
|
if (typeof payload.code === "string" && payload.code.trim()) return payload.code.trim();
|
|
13035
13114
|
return null;
|
|
13036
13115
|
}
|
|
13037
|
-
function detectIntegrationProvider(
|
|
13038
|
-
if (/\/github(\/|$)/i.test(
|
|
13039
|
-
if (/\/slack(\/|$)/i.test(
|
|
13116
|
+
function detectIntegrationProvider(path21) {
|
|
13117
|
+
if (/\/github(\/|$)/i.test(path21)) return "github";
|
|
13118
|
+
if (/\/slack(\/|$)/i.test(path21)) return "slack";
|
|
13040
13119
|
return null;
|
|
13041
13120
|
}
|
|
13042
13121
|
function rewriteNotFoundMessage(input) {
|
|
@@ -13056,7 +13135,70 @@ import * as fs3 from "fs";
|
|
|
13056
13135
|
import * as path3 from "path";
|
|
13057
13136
|
var CONFIG_DIR = ".contextstream";
|
|
13058
13137
|
var CONFIG_FILE = "config.json";
|
|
13138
|
+
var MODERN_GLOBAL_DIR = ".contextstream";
|
|
13139
|
+
var MODERN_GLOBAL_MAPPINGS_FILE = "mappings.json";
|
|
13059
13140
|
var GLOBAL_MAPPINGS_FILE = ".contextstream-mappings.json";
|
|
13141
|
+
function getHomeDir() {
|
|
13142
|
+
return process.env.HOME || process.env.USERPROFILE || "";
|
|
13143
|
+
}
|
|
13144
|
+
function readMappingsFile(filePath) {
|
|
13145
|
+
try {
|
|
13146
|
+
if (!fs3.existsSync(filePath)) return null;
|
|
13147
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
13148
|
+
const parsed = JSON.parse(content);
|
|
13149
|
+
if (Array.isArray(parsed)) {
|
|
13150
|
+
return { mappings: parsed };
|
|
13151
|
+
}
|
|
13152
|
+
if (parsed && typeof parsed === "object") {
|
|
13153
|
+
const obj = parsed;
|
|
13154
|
+
const mappings = Array.isArray(obj.mappings) ? obj.mappings : [];
|
|
13155
|
+
const fallback = obj.fallback_workspace && typeof obj.fallback_workspace === "object" ? obj.fallback_workspace : void 0;
|
|
13156
|
+
return { mappings, fallback_workspace: fallback };
|
|
13157
|
+
}
|
|
13158
|
+
} catch (e) {
|
|
13159
|
+
console.error(`Failed to read global mappings from ${filePath}:`, e);
|
|
13160
|
+
}
|
|
13161
|
+
return null;
|
|
13162
|
+
}
|
|
13163
|
+
function writeMappingsFile(filePath, store) {
|
|
13164
|
+
try {
|
|
13165
|
+
const dir = path3.dirname(filePath);
|
|
13166
|
+
if (!fs3.existsSync(dir)) {
|
|
13167
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
13168
|
+
}
|
|
13169
|
+
fs3.writeFileSync(filePath, JSON.stringify(store, null, 2));
|
|
13170
|
+
return true;
|
|
13171
|
+
} catch (e) {
|
|
13172
|
+
console.error(`Failed to write global mappings to ${filePath}:`, e);
|
|
13173
|
+
return false;
|
|
13174
|
+
}
|
|
13175
|
+
}
|
|
13176
|
+
function readGlobalStore() {
|
|
13177
|
+
const homeDir = getHomeDir();
|
|
13178
|
+
if (!homeDir) return { mappings: [] };
|
|
13179
|
+
const modernPath = path3.join(homeDir, MODERN_GLOBAL_DIR, MODERN_GLOBAL_MAPPINGS_FILE);
|
|
13180
|
+
const legacyPath = path3.join(homeDir, GLOBAL_MAPPINGS_FILE);
|
|
13181
|
+
const modern = readMappingsFile(modernPath);
|
|
13182
|
+
if (modern) return modern;
|
|
13183
|
+
const legacy = readMappingsFile(legacyPath);
|
|
13184
|
+
if (!legacy) return { mappings: [] };
|
|
13185
|
+
writeMappingsFile(modernPath, legacy);
|
|
13186
|
+
return legacy;
|
|
13187
|
+
}
|
|
13188
|
+
function writeGlobalStore(store) {
|
|
13189
|
+
const homeDir = getHomeDir();
|
|
13190
|
+
if (!homeDir) return false;
|
|
13191
|
+
const modernPath = path3.join(homeDir, MODERN_GLOBAL_DIR, MODERN_GLOBAL_MAPPINGS_FILE);
|
|
13192
|
+
const legacyPath = path3.join(homeDir, GLOBAL_MAPPINGS_FILE);
|
|
13193
|
+
const okModern = writeMappingsFile(modernPath, store);
|
|
13194
|
+
let okLegacy = true;
|
|
13195
|
+
try {
|
|
13196
|
+
fs3.writeFileSync(legacyPath, JSON.stringify(store.mappings, null, 2));
|
|
13197
|
+
} catch {
|
|
13198
|
+
okLegacy = false;
|
|
13199
|
+
}
|
|
13200
|
+
return okModern && okLegacy;
|
|
13201
|
+
}
|
|
13060
13202
|
function readLocalConfig(repoPath) {
|
|
13061
13203
|
const configPath = path3.join(repoPath, CONFIG_DIR, CONFIG_FILE);
|
|
13062
13204
|
try {
|
|
@@ -13084,35 +13226,29 @@ function writeLocalConfig(repoPath, config) {
|
|
|
13084
13226
|
}
|
|
13085
13227
|
}
|
|
13086
13228
|
function readGlobalMappings() {
|
|
13087
|
-
|
|
13088
|
-
const mappingsPath = path3.join(homeDir, GLOBAL_MAPPINGS_FILE);
|
|
13089
|
-
try {
|
|
13090
|
-
if (fs3.existsSync(mappingsPath)) {
|
|
13091
|
-
const content = fs3.readFileSync(mappingsPath, "utf-8");
|
|
13092
|
-
return JSON.parse(content);
|
|
13093
|
-
}
|
|
13094
|
-
} catch (e) {
|
|
13095
|
-
console.error(`Failed to read global mappings:`, e);
|
|
13096
|
-
}
|
|
13097
|
-
return [];
|
|
13098
|
-
}
|
|
13099
|
-
function writeGlobalMappings(mappings) {
|
|
13100
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
13101
|
-
const mappingsPath = path3.join(homeDir, GLOBAL_MAPPINGS_FILE);
|
|
13102
|
-
try {
|
|
13103
|
-
fs3.writeFileSync(mappingsPath, JSON.stringify(mappings, null, 2));
|
|
13104
|
-
return true;
|
|
13105
|
-
} catch (e) {
|
|
13106
|
-
console.error(`Failed to write global mappings:`, e);
|
|
13107
|
-
return false;
|
|
13108
|
-
}
|
|
13229
|
+
return readGlobalStore().mappings;
|
|
13109
13230
|
}
|
|
13110
13231
|
function addGlobalMapping(mapping) {
|
|
13111
13232
|
const normalizedPattern = path3.normalize(mapping.pattern);
|
|
13112
|
-
const
|
|
13233
|
+
const store = readGlobalStore();
|
|
13234
|
+
const mappings = store.mappings;
|
|
13113
13235
|
const filtered = mappings.filter((m) => path3.normalize(m.pattern) !== normalizedPattern);
|
|
13114
13236
|
filtered.push({ ...mapping, pattern: normalizedPattern });
|
|
13115
|
-
return
|
|
13237
|
+
return writeGlobalStore({ ...store, mappings: filtered });
|
|
13238
|
+
}
|
|
13239
|
+
function getGlobalFallbackWorkspace() {
|
|
13240
|
+
return readGlobalStore().fallback_workspace ?? null;
|
|
13241
|
+
}
|
|
13242
|
+
function setGlobalFallbackWorkspace(fallback) {
|
|
13243
|
+
const store = readGlobalStore();
|
|
13244
|
+
return writeGlobalStore({
|
|
13245
|
+
...store,
|
|
13246
|
+
fallback_workspace: {
|
|
13247
|
+
workspace_id: fallback.workspace_id,
|
|
13248
|
+
workspace_name: fallback.workspace_name,
|
|
13249
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
13250
|
+
}
|
|
13251
|
+
});
|
|
13116
13252
|
}
|
|
13117
13253
|
function findMatchingMapping(repoPath) {
|
|
13118
13254
|
const mappings = readGlobalMappings();
|
|
@@ -13308,6 +13444,12 @@ function extractEventTags(item) {
|
|
|
13308
13444
|
return tags;
|
|
13309
13445
|
}
|
|
13310
13446
|
function extractEffectiveEventType(item) {
|
|
13447
|
+
const topLevel = item.event_type;
|
|
13448
|
+
const isNormalized = typeof topLevel === "string" && topLevel.trim() === "manual_note";
|
|
13449
|
+
if (isNormalized) {
|
|
13450
|
+
const orig = item.metadata?.original_type;
|
|
13451
|
+
if (typeof orig === "string" && orig.trim()) return orig.trim();
|
|
13452
|
+
}
|
|
13311
13453
|
for (const field of ["event_type", "node_type", "type"]) {
|
|
13312
13454
|
const val = item[field];
|
|
13313
13455
|
if (typeof val === "string" && val.trim()) return val.trim();
|
|
@@ -13327,6 +13469,13 @@ function isLessonResult(item) {
|
|
|
13327
13469
|
if (content.includes("### Prevention") && content.includes("### Trigger")) return true;
|
|
13328
13470
|
return false;
|
|
13329
13471
|
}
|
|
13472
|
+
function isDecisionResult(item) {
|
|
13473
|
+
const effectiveType = extractEffectiveEventType(item);
|
|
13474
|
+
if (effectiveType === "decision") return true;
|
|
13475
|
+
const tags = extractEventTags(item);
|
|
13476
|
+
if (tags.includes("decision")) return true;
|
|
13477
|
+
return false;
|
|
13478
|
+
}
|
|
13330
13479
|
function pickString(value) {
|
|
13331
13480
|
if (typeof value !== "string") return null;
|
|
13332
13481
|
const trimmed = value.trim();
|
|
@@ -13352,6 +13501,8 @@ var INGEST_BENEFITS = [
|
|
|
13352
13501
|
"Allow the AI assistant to find relevant code without manual file navigation",
|
|
13353
13502
|
"Build a searchable knowledge base of your codebase structure"
|
|
13354
13503
|
];
|
|
13504
|
+
var GLOBAL_FALLBACK_WORKSPACE_NAME = ".contextstream-global";
|
|
13505
|
+
var GLOBAL_FALLBACK_WORKSPACE_DESCRIPTION = "Auto-managed fallback workspace for ad-hoc directories outside explicit project mappings.";
|
|
13355
13506
|
var AUTO_INDEX_FILE_CAP = 1e4;
|
|
13356
13507
|
var PROJECT_MARKERS = [
|
|
13357
13508
|
".git",
|
|
@@ -13367,10 +13518,10 @@ var PROJECT_MARKERS = [
|
|
|
13367
13518
|
];
|
|
13368
13519
|
function isMultiProjectFolder(folderPath) {
|
|
13369
13520
|
try {
|
|
13370
|
-
const
|
|
13521
|
+
const fs21 = __require("fs");
|
|
13371
13522
|
const pathModule = __require("path");
|
|
13372
|
-
const rootHasGit =
|
|
13373
|
-
const entries =
|
|
13523
|
+
const rootHasGit = fs21.existsSync(pathModule.join(folderPath, ".git"));
|
|
13524
|
+
const entries = fs21.readdirSync(folderPath, { withFileTypes: true });
|
|
13374
13525
|
const subdirs = entries.filter(
|
|
13375
13526
|
(e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules"
|
|
13376
13527
|
);
|
|
@@ -13378,7 +13529,7 @@ function isMultiProjectFolder(folderPath) {
|
|
|
13378
13529
|
for (const subdir of subdirs) {
|
|
13379
13530
|
const subdirPath = pathModule.join(folderPath, subdir.name);
|
|
13380
13531
|
for (const marker of PROJECT_MARKERS) {
|
|
13381
|
-
if (
|
|
13532
|
+
if (fs21.existsSync(pathModule.join(subdirPath, marker))) {
|
|
13382
13533
|
projectSubdirs.push(subdir.name);
|
|
13383
13534
|
break;
|
|
13384
13535
|
}
|
|
@@ -13464,6 +13615,39 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13464
13615
|
const message = String(error.message || "").toLowerCase();
|
|
13465
13616
|
return message.includes("deserialize") || message.includes("deserial");
|
|
13466
13617
|
}
|
|
13618
|
+
async ensureGlobalFallbackWorkspace() {
|
|
13619
|
+
const persisted = getGlobalFallbackWorkspace();
|
|
13620
|
+
if (persisted?.workspace_id) {
|
|
13621
|
+
try {
|
|
13622
|
+
const wsResponse = await this.getWorkspace(persisted.workspace_id);
|
|
13623
|
+
const workspace = unwrapApiResponse(wsResponse);
|
|
13624
|
+
const workspaceId = typeof workspace.id === "string" ? workspace.id : persisted.workspace_id;
|
|
13625
|
+
const workspaceName = typeof workspace.name === "string" && workspace.name.trim() ? workspace.name : persisted.workspace_name || GLOBAL_FALLBACK_WORKSPACE_NAME;
|
|
13626
|
+
setGlobalFallbackWorkspace({
|
|
13627
|
+
workspace_id: workspaceId,
|
|
13628
|
+
workspace_name: workspaceName
|
|
13629
|
+
});
|
|
13630
|
+
return { id: workspaceId, name: workspaceName, source: "global_fallback_cache" };
|
|
13631
|
+
} catch {
|
|
13632
|
+
}
|
|
13633
|
+
}
|
|
13634
|
+
try {
|
|
13635
|
+
const created = await this.createWorkspace({
|
|
13636
|
+
name: GLOBAL_FALLBACK_WORKSPACE_NAME,
|
|
13637
|
+
description: GLOBAL_FALLBACK_WORKSPACE_DESCRIPTION
|
|
13638
|
+
});
|
|
13639
|
+
if (typeof created.id === "string") {
|
|
13640
|
+
const workspaceName = typeof created.name === "string" && created.name.trim() ? created.name : GLOBAL_FALLBACK_WORKSPACE_NAME;
|
|
13641
|
+
setGlobalFallbackWorkspace({
|
|
13642
|
+
workspace_id: created.id,
|
|
13643
|
+
workspace_name: workspaceName
|
|
13644
|
+
});
|
|
13645
|
+
return { id: created.id, name: workspaceName, source: "global_fallback_created" };
|
|
13646
|
+
}
|
|
13647
|
+
} catch {
|
|
13648
|
+
}
|
|
13649
|
+
return null;
|
|
13650
|
+
}
|
|
13467
13651
|
// Auth
|
|
13468
13652
|
me() {
|
|
13469
13653
|
return request(this.config, "/auth/me");
|
|
@@ -13638,13 +13822,18 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13638
13822
|
uuidSchema.parse(projectId);
|
|
13639
13823
|
return request(this.config, `/projects/${projectId}/index`, { body: {} });
|
|
13640
13824
|
}
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
|
|
13825
|
+
async searchWithCache(endpoint, searchType, body) {
|
|
13826
|
+
const hintSig = (body.hot_paths_hint?.entries || []).slice(0, 4).map((entry) => `${entry.path}:${entry.score.toFixed(2)}`).join("|");
|
|
13827
|
+
const cacheKey = `${CacheKeys.search(
|
|
13828
|
+
body.query,
|
|
13829
|
+
body.workspace_id
|
|
13830
|
+
)}:${body.project_id || ""}:${searchType}:${body.limit || ""}:${body.offset || ""}:${hintSig}`;
|
|
13831
|
+
const cached = globalCache.get(cacheKey);
|
|
13832
|
+
if (cached) return cached;
|
|
13833
|
+
const result = await request(this.config, endpoint, {
|
|
13645
13834
|
body: {
|
|
13646
13835
|
...this.withDefaults(body),
|
|
13647
|
-
search_type:
|
|
13836
|
+
search_type: searchType,
|
|
13648
13837
|
output_format: body.output_format,
|
|
13649
13838
|
filters: body.workspace_id ? {} : {
|
|
13650
13839
|
file_types: [],
|
|
@@ -13656,57 +13845,22 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13656
13845
|
}
|
|
13657
13846
|
}
|
|
13658
13847
|
});
|
|
13848
|
+
globalCache.set(cacheKey, result, CacheTTL.SEARCH);
|
|
13849
|
+
return result;
|
|
13850
|
+
}
|
|
13851
|
+
// Search - each method adds required search_type and filters fields
|
|
13852
|
+
// Optional params: context_lines (like grep -C), exact_match_boost (boost for exact matches)
|
|
13853
|
+
searchSemantic(body) {
|
|
13854
|
+
return this.searchWithCache("/search/semantic", "semantic", body);
|
|
13659
13855
|
}
|
|
13660
13856
|
searchHybrid(body) {
|
|
13661
|
-
return
|
|
13662
|
-
body: {
|
|
13663
|
-
...this.withDefaults(body),
|
|
13664
|
-
search_type: "hybrid",
|
|
13665
|
-
output_format: body.output_format,
|
|
13666
|
-
filters: body.workspace_id ? {} : {
|
|
13667
|
-
file_types: [],
|
|
13668
|
-
languages: [],
|
|
13669
|
-
file_paths: [],
|
|
13670
|
-
exclude_paths: [],
|
|
13671
|
-
content_types: [],
|
|
13672
|
-
tags: []
|
|
13673
|
-
}
|
|
13674
|
-
}
|
|
13675
|
-
});
|
|
13857
|
+
return this.searchWithCache("/search/hybrid", "hybrid", body);
|
|
13676
13858
|
}
|
|
13677
13859
|
searchKeyword(body) {
|
|
13678
|
-
return
|
|
13679
|
-
body: {
|
|
13680
|
-
...this.withDefaults(body),
|
|
13681
|
-
search_type: "keyword",
|
|
13682
|
-
output_format: body.output_format,
|
|
13683
|
-
filters: body.workspace_id ? {} : {
|
|
13684
|
-
file_types: [],
|
|
13685
|
-
languages: [],
|
|
13686
|
-
file_paths: [],
|
|
13687
|
-
exclude_paths: [],
|
|
13688
|
-
content_types: [],
|
|
13689
|
-
tags: []
|
|
13690
|
-
}
|
|
13691
|
-
}
|
|
13692
|
-
});
|
|
13860
|
+
return this.searchWithCache("/search/keyword", "keyword", body);
|
|
13693
13861
|
}
|
|
13694
13862
|
searchPattern(body) {
|
|
13695
|
-
return
|
|
13696
|
-
body: {
|
|
13697
|
-
...this.withDefaults(body),
|
|
13698
|
-
search_type: "pattern",
|
|
13699
|
-
output_format: body.output_format,
|
|
13700
|
-
filters: body.workspace_id ? {} : {
|
|
13701
|
-
file_types: [],
|
|
13702
|
-
languages: [],
|
|
13703
|
-
file_paths: [],
|
|
13704
|
-
exclude_paths: [],
|
|
13705
|
-
content_types: [],
|
|
13706
|
-
tags: []
|
|
13707
|
-
}
|
|
13708
|
-
}
|
|
13709
|
-
});
|
|
13863
|
+
return this.searchWithCache("/search/pattern", "pattern", body);
|
|
13710
13864
|
}
|
|
13711
13865
|
/**
|
|
13712
13866
|
* Exhaustive search returns ALL matches from the index.
|
|
@@ -13714,21 +13868,7 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13714
13868
|
* Includes index_freshness to indicate result trustworthiness.
|
|
13715
13869
|
*/
|
|
13716
13870
|
searchExhaustive(body) {
|
|
13717
|
-
return
|
|
13718
|
-
body: {
|
|
13719
|
-
...this.withDefaults(body),
|
|
13720
|
-
search_type: "exhaustive",
|
|
13721
|
-
output_format: body.output_format,
|
|
13722
|
-
filters: body.workspace_id ? {} : {
|
|
13723
|
-
file_types: [],
|
|
13724
|
-
languages: [],
|
|
13725
|
-
file_paths: [],
|
|
13726
|
-
exclude_paths: [],
|
|
13727
|
-
content_types: [],
|
|
13728
|
-
tags: []
|
|
13729
|
-
}
|
|
13730
|
-
}
|
|
13731
|
-
});
|
|
13871
|
+
return this.searchWithCache("/search/exhaustive", "exhaustive", body);
|
|
13732
13872
|
}
|
|
13733
13873
|
/**
|
|
13734
13874
|
* Refactor search optimized for find-replace operations.
|
|
@@ -13736,41 +13876,13 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
13736
13876
|
* Returns matches grouped by file with line/col positions.
|
|
13737
13877
|
*/
|
|
13738
13878
|
searchRefactor(body) {
|
|
13739
|
-
return
|
|
13740
|
-
body: {
|
|
13741
|
-
...this.withDefaults(body),
|
|
13742
|
-
search_type: "refactor",
|
|
13743
|
-
output_format: body.output_format,
|
|
13744
|
-
filters: body.workspace_id ? {} : {
|
|
13745
|
-
file_types: [],
|
|
13746
|
-
languages: [],
|
|
13747
|
-
file_paths: [],
|
|
13748
|
-
exclude_paths: [],
|
|
13749
|
-
content_types: [],
|
|
13750
|
-
tags: []
|
|
13751
|
-
}
|
|
13752
|
-
}
|
|
13753
|
-
});
|
|
13879
|
+
return this.searchWithCache("/search/refactor", "refactor", body);
|
|
13754
13880
|
}
|
|
13755
13881
|
/**
|
|
13756
13882
|
* Crawl search uses deep multi-modal retrieval with a larger candidate pool.
|
|
13757
13883
|
*/
|
|
13758
13884
|
searchCrawl(body) {
|
|
13759
|
-
return
|
|
13760
|
-
body: {
|
|
13761
|
-
...this.withDefaults(body),
|
|
13762
|
-
search_type: "crawl",
|
|
13763
|
-
output_format: body.output_format,
|
|
13764
|
-
filters: body.workspace_id ? {} : {
|
|
13765
|
-
file_types: [],
|
|
13766
|
-
languages: [],
|
|
13767
|
-
file_paths: [],
|
|
13768
|
-
exclude_paths: [],
|
|
13769
|
-
content_types: [],
|
|
13770
|
-
tags: []
|
|
13771
|
-
}
|
|
13772
|
-
}
|
|
13773
|
-
});
|
|
13885
|
+
return this.searchWithCache("/search/crawl", "crawl", body);
|
|
13774
13886
|
}
|
|
13775
13887
|
// Flash / instruction cache
|
|
13776
13888
|
async flashBootstrap(params) {
|
|
@@ -14709,8 +14821,12 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14709
14821
|
* ensuring cross-project decisions and memory are available.
|
|
14710
14822
|
*/
|
|
14711
14823
|
async initSession(params, ideRoots = []) {
|
|
14712
|
-
|
|
14713
|
-
|
|
14824
|
+
const explicitWorkspaceId = params.workspace_id;
|
|
14825
|
+
const explicitProjectId = params.project_id;
|
|
14826
|
+
const defaultWorkspaceId = this.config.defaultWorkspaceId;
|
|
14827
|
+
const defaultProjectId = this.config.defaultProjectId;
|
|
14828
|
+
let workspaceId = explicitWorkspaceId || defaultWorkspaceId;
|
|
14829
|
+
let projectId = explicitProjectId || defaultProjectId;
|
|
14714
14830
|
let workspaceName;
|
|
14715
14831
|
const context = {
|
|
14716
14832
|
session_id: params.session_id || randomUUID(),
|
|
@@ -14718,102 +14834,90 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14718
14834
|
};
|
|
14719
14835
|
this.sessionStartTime = Date.now();
|
|
14720
14836
|
const rootPath = ideRoots.length > 0 ? ideRoots[0] : void 0;
|
|
14721
|
-
if (
|
|
14837
|
+
if (rootPath && !explicitWorkspaceId) {
|
|
14722
14838
|
const resolved = resolveWorkspace(rootPath);
|
|
14723
|
-
if (resolved.config) {
|
|
14839
|
+
if (resolved.config?.workspace_id) {
|
|
14840
|
+
const previousWorkspaceId = workspaceId;
|
|
14724
14841
|
workspaceId = resolved.config.workspace_id;
|
|
14725
14842
|
workspaceName = resolved.config.workspace_name;
|
|
14726
|
-
|
|
14843
|
+
if (!explicitProjectId && resolved.config.project_id) {
|
|
14844
|
+
projectId = resolved.config.project_id;
|
|
14845
|
+
}
|
|
14727
14846
|
context.workspace_source = resolved.source;
|
|
14728
14847
|
context.workspace_resolved_from = resolved.source === "local_config" ? `${rootPath}/.contextstream/config.json` : "parent_folder_mapping";
|
|
14848
|
+
if (previousWorkspaceId && previousWorkspaceId !== workspaceId) {
|
|
14849
|
+
context.workspace_scope_overridden = true;
|
|
14850
|
+
context.workspace_scope_override_reason = "folder mapping takes precedence over cached session defaults";
|
|
14851
|
+
}
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14854
|
+
if (!workspaceId && rootPath) {
|
|
14855
|
+
const fallback = await this.ensureGlobalFallbackWorkspace();
|
|
14856
|
+
if (fallback) {
|
|
14857
|
+
workspaceId = fallback.id;
|
|
14858
|
+
workspaceName = fallback.name;
|
|
14859
|
+
context.workspace_source = fallback.source;
|
|
14860
|
+
context.workspace_only_mode = true;
|
|
14861
|
+
context.project_skipped_reason = "No project mapping found for this folder; using global fallback workspace context.";
|
|
14729
14862
|
} else {
|
|
14730
14863
|
const folderName = rootPath ? path5.basename(rootPath).toLowerCase() : "";
|
|
14731
14864
|
try {
|
|
14732
14865
|
const workspaces = await this.listWorkspaces({ page_size: 50 });
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
(w) => w.name.toLowerCase().includes(folderName) || folderName.includes(w.name.toLowerCase())
|
|
14743
|
-
);
|
|
14744
|
-
if (matchedWorkspace) {
|
|
14745
|
-
matchSource = "workspace_name_partial";
|
|
14746
|
-
}
|
|
14747
|
-
}
|
|
14748
|
-
if (!matchedWorkspace) {
|
|
14749
|
-
for (const ws of workspaces.items) {
|
|
14750
|
-
try {
|
|
14751
|
-
const projects = await this.listProjects({
|
|
14752
|
-
workspace_id: ws.id,
|
|
14753
|
-
page_size: 50
|
|
14754
|
-
});
|
|
14755
|
-
const matchingProject = projects.items?.find(
|
|
14756
|
-
(p) => p.name.toLowerCase() === folderName || p.name.toLowerCase().includes(folderName) || folderName.includes(p.name.toLowerCase())
|
|
14757
|
-
);
|
|
14758
|
-
if (matchingProject) {
|
|
14759
|
-
matchedWorkspace = ws;
|
|
14760
|
-
matchSource = "project_name_match";
|
|
14761
|
-
projectId = matchingProject.id;
|
|
14762
|
-
context.project_source = "matched_existing";
|
|
14763
|
-
break;
|
|
14764
|
-
}
|
|
14765
|
-
} catch {
|
|
14766
|
-
}
|
|
14767
|
-
}
|
|
14768
|
-
}
|
|
14769
|
-
if (matchedWorkspace) {
|
|
14770
|
-
workspaceId = matchedWorkspace.id;
|
|
14771
|
-
workspaceName = matchedWorkspace.name;
|
|
14772
|
-
context.workspace_source = matchSource;
|
|
14773
|
-
context.workspace_auto_matched = true;
|
|
14774
|
-
writeLocalConfig(rootPath, {
|
|
14775
|
-
workspace_id: matchedWorkspace.id,
|
|
14776
|
-
workspace_name: matchedWorkspace.name,
|
|
14777
|
-
associated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
14778
|
-
});
|
|
14779
|
-
} else {
|
|
14780
|
-
context.status = "requires_workspace_selection";
|
|
14781
|
-
context.workspace_candidates = workspaces.items.map((w) => ({
|
|
14782
|
-
id: w.id,
|
|
14783
|
-
name: w.name,
|
|
14784
|
-
description: w.description
|
|
14785
|
-
}));
|
|
14786
|
-
context.message = `New folder detected: "${rootPath ? path5.basename(rootPath) : "this folder"}". Please select which workspace this belongs to, or create a new one.`;
|
|
14787
|
-
context.ide_roots = ideRoots;
|
|
14788
|
-
context.folder_name = rootPath ? path5.basename(rootPath) : void 0;
|
|
14789
|
-
return context;
|
|
14790
|
-
}
|
|
14791
|
-
} else {
|
|
14792
|
-
const folderDisplayName = rootPath ? path5.basename(rootPath) || "this folder" : "this folder";
|
|
14793
|
-
context.status = "requires_workspace_name";
|
|
14794
|
-
context.workspace_source = "none_found";
|
|
14866
|
+
const items = Array.isArray(workspaces.items) ? workspaces.items : [];
|
|
14867
|
+
if (items.length > 0) {
|
|
14868
|
+
context.status = "requires_workspace_selection";
|
|
14869
|
+
context.workspace_candidates = items.map((w) => ({
|
|
14870
|
+
id: w.id,
|
|
14871
|
+
name: w.name,
|
|
14872
|
+
description: w.description
|
|
14873
|
+
}));
|
|
14874
|
+
context.message = `New folder detected: "${folderName || "this folder"}". Please select which workspace this belongs to, or create a new one.`;
|
|
14795
14875
|
context.ide_roots = ideRoots;
|
|
14796
|
-
context.folder_name =
|
|
14797
|
-
context.folder_path = rootPath;
|
|
14798
|
-
context.suggested_project_name = folderDisplayName;
|
|
14799
|
-
context.message = `No workspaces found for this account. Ask the user for a name for a new workspace, then create a project for "${folderDisplayName}".`;
|
|
14876
|
+
context.folder_name = rootPath ? path5.basename(rootPath) : void 0;
|
|
14800
14877
|
return context;
|
|
14801
14878
|
}
|
|
14879
|
+
context.status = "requires_workspace_name";
|
|
14880
|
+
context.workspace_source = "none_found";
|
|
14881
|
+
context.ide_roots = ideRoots;
|
|
14882
|
+
context.folder_name = rootPath ? path5.basename(rootPath) || "this folder" : "this folder";
|
|
14883
|
+
context.folder_path = rootPath;
|
|
14884
|
+
context.suggested_project_name = context.folder_name;
|
|
14885
|
+
context.message = `No workspaces found for this account and fallback workspace creation failed. Ask the user for a workspace name to continue.`;
|
|
14886
|
+
return context;
|
|
14802
14887
|
} catch (e) {
|
|
14803
14888
|
context.workspace_error = String(e);
|
|
14804
14889
|
}
|
|
14805
14890
|
}
|
|
14806
14891
|
}
|
|
14807
14892
|
if (!workspaceId && !rootPath) {
|
|
14808
|
-
|
|
14809
|
-
|
|
14810
|
-
|
|
14811
|
-
|
|
14812
|
-
|
|
14813
|
-
|
|
14893
|
+
const fallback = await this.ensureGlobalFallbackWorkspace();
|
|
14894
|
+
if (fallback) {
|
|
14895
|
+
workspaceId = fallback.id;
|
|
14896
|
+
workspaceName = fallback.name;
|
|
14897
|
+
context.workspace_source = fallback.source;
|
|
14898
|
+
context.workspace_only_mode = true;
|
|
14899
|
+
context.project_skipped_reason = "No folder context available; using global fallback workspace context.";
|
|
14900
|
+
} else {
|
|
14901
|
+
try {
|
|
14902
|
+
const workspaces = await this.listWorkspaces({ page_size: 1 });
|
|
14903
|
+
if (workspaces.items && workspaces.items.length > 0) {
|
|
14904
|
+
workspaceId = workspaces.items[0].id;
|
|
14905
|
+
workspaceName = workspaces.items[0].name;
|
|
14906
|
+
context.workspace_source = "fallback_first";
|
|
14907
|
+
}
|
|
14908
|
+
} catch (e) {
|
|
14909
|
+
context.workspace_error = String(e);
|
|
14814
14910
|
}
|
|
14815
|
-
}
|
|
14816
|
-
|
|
14911
|
+
}
|
|
14912
|
+
}
|
|
14913
|
+
if (!workspaceId && !params.allow_no_workspace) {
|
|
14914
|
+
const fallback = await this.ensureGlobalFallbackWorkspace();
|
|
14915
|
+
if (fallback) {
|
|
14916
|
+
workspaceId = fallback.id;
|
|
14917
|
+
workspaceName = fallback.name;
|
|
14918
|
+
context.workspace_source = fallback.source;
|
|
14919
|
+
context.workspace_only_mode = true;
|
|
14920
|
+
context.project_skipped_reason = "No explicit workspace/project mapping found; using global fallback workspace context.";
|
|
14817
14921
|
}
|
|
14818
14922
|
}
|
|
14819
14923
|
if (!workspaceId && !params.allow_no_workspace) {
|
|
@@ -14987,6 +15091,14 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
14987
15091
|
context.workspace_name = workspaceName;
|
|
14988
15092
|
context.project_id = projectId;
|
|
14989
15093
|
context.ide_roots = ideRoots;
|
|
15094
|
+
if (!projectId && workspaceId) {
|
|
15095
|
+
context.workspace_only_mode = true;
|
|
15096
|
+
context.project_scope_hint = {
|
|
15097
|
+
reason: "No project is associated with the current folder context.",
|
|
15098
|
+
create_project_command: rootPath && rootPath.trim() ? `project(action="create", name="${path5.basename(rootPath) || "default-project"}", workspace_id="${workspaceId}", folder_path="${rootPath}")` : `project(action="create", name="default-project", workspace_id="${workspaceId}")`,
|
|
15099
|
+
continue_workspace_only_command: 'context(user_message="...")'
|
|
15100
|
+
};
|
|
15101
|
+
}
|
|
14990
15102
|
this.sessionProjectId = projectId;
|
|
14991
15103
|
this.sessionRootPath = rootPath;
|
|
14992
15104
|
if (!workspaceId) {
|
|
@@ -15309,15 +15421,15 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
15309
15421
|
case "preference":
|
|
15310
15422
|
case "note":
|
|
15311
15423
|
case "implementation":
|
|
15312
|
-
apiEventType =
|
|
15424
|
+
apiEventType = params.event_type;
|
|
15313
15425
|
tags.push(params.event_type);
|
|
15314
15426
|
break;
|
|
15315
|
-
// Lesson system types
|
|
15427
|
+
// Lesson system types — store with original type + lesson_system tag
|
|
15316
15428
|
case "correction":
|
|
15317
15429
|
case "lesson":
|
|
15318
15430
|
case "warning":
|
|
15319
15431
|
case "frustration":
|
|
15320
|
-
apiEventType =
|
|
15432
|
+
apiEventType = params.event_type;
|
|
15321
15433
|
tags.push(params.event_type);
|
|
15322
15434
|
if (!tags.includes("lesson_system")) {
|
|
15323
15435
|
tags.push("lesson_system");
|
|
@@ -15784,9 +15896,9 @@ var ContextStreamClient = class _ContextStreamClient {
|
|
|
15784
15896
|
candidateParts.push("## Relevant Code\n");
|
|
15785
15897
|
currentChars += 18;
|
|
15786
15898
|
const codeEntries = code.results.map((c) => {
|
|
15787
|
-
const
|
|
15899
|
+
const path21 = c.file_path || "file";
|
|
15788
15900
|
const content = c.content?.slice(0, 150) || "";
|
|
15789
|
-
return { path:
|
|
15901
|
+
return { path: path21, entry: `\u2022 ${path21}: ${content}...
|
|
15790
15902
|
` };
|
|
15791
15903
|
});
|
|
15792
15904
|
for (const c of codeEntries) {
|
|
@@ -16702,7 +16814,7 @@ ${context}`;
|
|
|
16702
16814
|
if (!withDefaults.workspace_id) {
|
|
16703
16815
|
throw new Error("workspace_id is required for integrations status");
|
|
16704
16816
|
}
|
|
16705
|
-
const result = await request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/
|
|
16817
|
+
const result = await request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/status`, {
|
|
16706
16818
|
method: "GET"
|
|
16707
16819
|
});
|
|
16708
16820
|
return unwrapApiResponse(result);
|
|
@@ -16719,7 +16831,7 @@ ${context}`;
|
|
|
16719
16831
|
if (params?.days) query.set("days", String(params.days));
|
|
16720
16832
|
if (params?.repo) query.set("repo", params.repo);
|
|
16721
16833
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
16722
|
-
return request(this.config, `/github/summary${suffix}`, { method: "GET" });
|
|
16834
|
+
return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/github/summary${suffix}`, { method: "GET" });
|
|
16723
16835
|
}
|
|
16724
16836
|
/**
|
|
16725
16837
|
* Get Slack summary for a workspace
|
|
@@ -16733,7 +16845,7 @@ ${context}`;
|
|
|
16733
16845
|
if (params?.days) query.set("days", String(params.days));
|
|
16734
16846
|
if (params?.channel) query.set("channel", params.channel);
|
|
16735
16847
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
16736
|
-
return request(this.config, `/slack/summary${suffix}`, { method: "GET" });
|
|
16848
|
+
return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/slack/summary${suffix}`, { method: "GET" });
|
|
16737
16849
|
}
|
|
16738
16850
|
/**
|
|
16739
16851
|
* Cross-source search across all integrations
|
|
@@ -17660,10 +17772,10 @@ ${context}`;
|
|
|
17660
17772
|
};
|
|
17661
17773
|
|
|
17662
17774
|
// src/tools.ts
|
|
17663
|
-
import * as
|
|
17664
|
-
import * as
|
|
17775
|
+
import * as fs6 from "node:fs";
|
|
17776
|
+
import * as path7 from "node:path";
|
|
17665
17777
|
import { execFile } from "node:child_process";
|
|
17666
|
-
import { homedir as
|
|
17778
|
+
import { homedir as homedir5 } from "node:os";
|
|
17667
17779
|
import { promisify as promisify2 } from "node:util";
|
|
17668
17780
|
init_files();
|
|
17669
17781
|
init_rules_templates();
|
|
@@ -18245,6 +18357,148 @@ function resolveTodoCompletionUpdate(input) {
|
|
|
18245
18357
|
};
|
|
18246
18358
|
}
|
|
18247
18359
|
|
|
18360
|
+
// src/hot-paths.ts
|
|
18361
|
+
import * as fs5 from "node:fs";
|
|
18362
|
+
import * as path6 from "node:path";
|
|
18363
|
+
import { homedir as homedir4 } from "node:os";
|
|
18364
|
+
var STORE_VERSION = 1;
|
|
18365
|
+
var STORE_DIR = path6.join(homedir4(), ".contextstream");
|
|
18366
|
+
var STORE_FILE = path6.join(STORE_DIR, "hot-paths.json");
|
|
18367
|
+
var MAX_PATHS_PER_SCOPE = 400;
|
|
18368
|
+
var HALF_LIFE_MS = 3 * 24 * 60 * 60 * 1e3;
|
|
18369
|
+
function clamp(value, min, max) {
|
|
18370
|
+
return Math.max(min, Math.min(max, value));
|
|
18371
|
+
}
|
|
18372
|
+
function normalizePathKey(input) {
|
|
18373
|
+
return input.replace(/\\/g, "/").trim();
|
|
18374
|
+
}
|
|
18375
|
+
function toScopeKey(input) {
|
|
18376
|
+
const workspace = input.workspace_id || "none";
|
|
18377
|
+
const project = input.project_id || "none";
|
|
18378
|
+
return `${workspace}:${project}`;
|
|
18379
|
+
}
|
|
18380
|
+
function signalWeight(signal) {
|
|
18381
|
+
switch (signal) {
|
|
18382
|
+
case "search_result":
|
|
18383
|
+
return 1.8;
|
|
18384
|
+
case "activity_edit":
|
|
18385
|
+
return 1.4;
|
|
18386
|
+
case "activity_focus":
|
|
18387
|
+
return 1.2;
|
|
18388
|
+
case "activity_read":
|
|
18389
|
+
default:
|
|
18390
|
+
return 1;
|
|
18391
|
+
}
|
|
18392
|
+
}
|
|
18393
|
+
function looksBroadQuery(query) {
|
|
18394
|
+
const q = query.trim().toLowerCase();
|
|
18395
|
+
if (!q) return true;
|
|
18396
|
+
if (q.length <= 2) return true;
|
|
18397
|
+
if (/^\*+$/.test(q) || q === "all files" || q === "everything") return true;
|
|
18398
|
+
const tokens = q.split(/\s+/).filter(Boolean);
|
|
18399
|
+
return tokens.length > 12;
|
|
18400
|
+
}
|
|
18401
|
+
var HotPathStore = class {
|
|
18402
|
+
constructor() {
|
|
18403
|
+
this.data = { version: STORE_VERSION, scopes: {} };
|
|
18404
|
+
this.load();
|
|
18405
|
+
}
|
|
18406
|
+
recordPaths(scope, paths, signal) {
|
|
18407
|
+
if (paths.length === 0) return;
|
|
18408
|
+
const now = Date.now();
|
|
18409
|
+
const scopeKey = toScopeKey(scope);
|
|
18410
|
+
const profile = this.ensureScope(scopeKey);
|
|
18411
|
+
const weight = signalWeight(signal);
|
|
18412
|
+
for (const raw of paths) {
|
|
18413
|
+
const pathKey = normalizePathKey(raw);
|
|
18414
|
+
if (!pathKey) continue;
|
|
18415
|
+
const current = profile.paths[pathKey] || { score: 0, last_seen: now, hits: 0 };
|
|
18416
|
+
const decayed = this.decayedScore(current.score, current.last_seen, now);
|
|
18417
|
+
profile.paths[pathKey] = {
|
|
18418
|
+
score: decayed + weight,
|
|
18419
|
+
last_seen: now,
|
|
18420
|
+
hits: current.hits + 1
|
|
18421
|
+
};
|
|
18422
|
+
}
|
|
18423
|
+
profile.updated_at = now;
|
|
18424
|
+
this.pruneScope(profile);
|
|
18425
|
+
this.persist();
|
|
18426
|
+
}
|
|
18427
|
+
buildHint(input) {
|
|
18428
|
+
const scopeKey = toScopeKey(input);
|
|
18429
|
+
const profile = this.data.scopes[scopeKey];
|
|
18430
|
+
if (!profile) return void 0;
|
|
18431
|
+
const now = Date.now();
|
|
18432
|
+
const baseEntries = Object.entries(profile.paths).map(([filePath, entry]) => ({
|
|
18433
|
+
path: filePath,
|
|
18434
|
+
score: this.decayedScore(entry.score, entry.last_seen, now)
|
|
18435
|
+
})).filter((entry) => entry.score > 0.05).sort((a, b) => b.score - a.score);
|
|
18436
|
+
const active = (input.active_paths || []).map(normalizePathKey).filter(Boolean);
|
|
18437
|
+
const merged = /* @__PURE__ */ new Map();
|
|
18438
|
+
for (const entry of baseEntries.slice(0, Math.max(12, input.limit || 8))) {
|
|
18439
|
+
merged.set(entry.path, {
|
|
18440
|
+
path: entry.path,
|
|
18441
|
+
score: Number(entry.score.toFixed(4)),
|
|
18442
|
+
source: "history"
|
|
18443
|
+
});
|
|
18444
|
+
}
|
|
18445
|
+
for (const activePath of active) {
|
|
18446
|
+
const existing = merged.get(activePath);
|
|
18447
|
+
if (existing) {
|
|
18448
|
+
existing.score = Number((existing.score + 0.9).toFixed(4));
|
|
18449
|
+
} else {
|
|
18450
|
+
merged.set(activePath, { path: activePath, score: 0.9, source: "active" });
|
|
18451
|
+
}
|
|
18452
|
+
}
|
|
18453
|
+
const limit = clamp(input.limit ?? 8, 1, 12);
|
|
18454
|
+
const entries = [...merged.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
18455
|
+
if (entries.length === 0) return void 0;
|
|
18456
|
+
const scoreSum = entries.reduce((sum, item) => sum + item.score, 0);
|
|
18457
|
+
const normalized = clamp(scoreSum / (limit * 2.5), 0, 1);
|
|
18458
|
+
const confidencePenalty = looksBroadQuery(input.query) ? 0.55 : 1;
|
|
18459
|
+
const confidence = Number((normalized * confidencePenalty).toFixed(3));
|
|
18460
|
+
return {
|
|
18461
|
+
entries,
|
|
18462
|
+
confidence,
|
|
18463
|
+
generated_at: new Date(now).toISOString(),
|
|
18464
|
+
profile_version: STORE_VERSION
|
|
18465
|
+
};
|
|
18466
|
+
}
|
|
18467
|
+
decayedScore(score, lastSeenMs, nowMs) {
|
|
18468
|
+
const elapsed = Math.max(0, nowMs - lastSeenMs);
|
|
18469
|
+
const decay = Math.pow(0.5, elapsed / HALF_LIFE_MS);
|
|
18470
|
+
return score * decay;
|
|
18471
|
+
}
|
|
18472
|
+
ensureScope(scopeKey) {
|
|
18473
|
+
if (!this.data.scopes[scopeKey]) {
|
|
18474
|
+
this.data.scopes[scopeKey] = { paths: {}, updated_at: Date.now() };
|
|
18475
|
+
}
|
|
18476
|
+
return this.data.scopes[scopeKey];
|
|
18477
|
+
}
|
|
18478
|
+
pruneScope(profile) {
|
|
18479
|
+
const entries = Object.entries(profile.paths);
|
|
18480
|
+
if (entries.length <= MAX_PATHS_PER_SCOPE) return;
|
|
18481
|
+
entries.sort((a, b) => b[1].score - a[1].score).slice(MAX_PATHS_PER_SCOPE).forEach(([key]) => delete profile.paths[key]);
|
|
18482
|
+
}
|
|
18483
|
+
load() {
|
|
18484
|
+
try {
|
|
18485
|
+
if (!fs5.existsSync(STORE_FILE)) return;
|
|
18486
|
+
const parsed = JSON.parse(fs5.readFileSync(STORE_FILE, "utf-8"));
|
|
18487
|
+
if (parsed?.version !== STORE_VERSION || !parsed.scopes) return;
|
|
18488
|
+
this.data = parsed;
|
|
18489
|
+
} catch {
|
|
18490
|
+
}
|
|
18491
|
+
}
|
|
18492
|
+
persist() {
|
|
18493
|
+
try {
|
|
18494
|
+
fs5.mkdirSync(STORE_DIR, { recursive: true });
|
|
18495
|
+
fs5.writeFileSync(STORE_FILE, JSON.stringify(this.data));
|
|
18496
|
+
} catch {
|
|
18497
|
+
}
|
|
18498
|
+
}
|
|
18499
|
+
};
|
|
18500
|
+
var globalHotPathStore = new HotPathStore();
|
|
18501
|
+
|
|
18248
18502
|
// src/tools.ts
|
|
18249
18503
|
var execFileAsync = promisify2(execFile);
|
|
18250
18504
|
function parseBoolEnvDefault(raw, fallback) {
|
|
@@ -18546,17 +18800,17 @@ var CONTEXTSTREAM_END_MARKER = "<!-- END ContextStream -->";
|
|
|
18546
18800
|
var RULES_PROJECT_FILES = {
|
|
18547
18801
|
codex: "AGENTS.md",
|
|
18548
18802
|
claude: "CLAUDE.md",
|
|
18549
|
-
copilot:
|
|
18803
|
+
copilot: path7.join(".github", "copilot-instructions.md"),
|
|
18550
18804
|
cursor: ".cursorrules",
|
|
18551
18805
|
cline: ".clinerules",
|
|
18552
|
-
kilo:
|
|
18553
|
-
roo:
|
|
18806
|
+
kilo: path7.join(".kilocode", "rules", "contextstream.md"),
|
|
18807
|
+
roo: path7.join(".roo", "rules", "contextstream.md"),
|
|
18554
18808
|
aider: ".aider.conf.yml"
|
|
18555
18809
|
};
|
|
18556
18810
|
var RULES_GLOBAL_FILES = {
|
|
18557
|
-
codex: [
|
|
18558
|
-
kilo: [
|
|
18559
|
-
roo: [
|
|
18811
|
+
codex: [path7.join(homedir5(), ".codex", "AGENTS.md")],
|
|
18812
|
+
kilo: [path7.join(homedir5(), ".kilocode", "rules", "contextstream.md")],
|
|
18813
|
+
roo: [path7.join(homedir5(), ".roo", "rules", "contextstream.md")]
|
|
18560
18814
|
};
|
|
18561
18815
|
var rulesNoticeCache = /* @__PURE__ */ new Map();
|
|
18562
18816
|
function compareVersions2(v1, v2) {
|
|
@@ -18608,7 +18862,7 @@ function resolveRulesCandidatePaths(folderPath, editorKey) {
|
|
|
18608
18862
|
if (!folderPath) return;
|
|
18609
18863
|
const rel = RULES_PROJECT_FILES[key];
|
|
18610
18864
|
if (rel) {
|
|
18611
|
-
candidates.add(
|
|
18865
|
+
candidates.add(path7.join(folderPath, rel));
|
|
18612
18866
|
}
|
|
18613
18867
|
};
|
|
18614
18868
|
const addGlobal = (key) => {
|
|
@@ -18640,7 +18894,7 @@ function resolveFolderPath(inputPath, sessionManager) {
|
|
|
18640
18894
|
const indicators = [".git", "package.json", "Cargo.toml", "pyproject.toml", ".contextstream"];
|
|
18641
18895
|
const hasIndicator = indicators.some((entry) => {
|
|
18642
18896
|
try {
|
|
18643
|
-
return
|
|
18897
|
+
return fs6.existsSync(path7.join(cwd, entry));
|
|
18644
18898
|
} catch {
|
|
18645
18899
|
return false;
|
|
18646
18900
|
}
|
|
@@ -18659,7 +18913,7 @@ function getRulesNotice(folderPath, clientName) {
|
|
|
18659
18913
|
return cached.notice;
|
|
18660
18914
|
}
|
|
18661
18915
|
const candidates = resolveRulesCandidatePaths(folderPath, editorKey);
|
|
18662
|
-
const existing = candidates.filter((filePath) =>
|
|
18916
|
+
const existing = candidates.filter((filePath) => fs6.existsSync(filePath));
|
|
18663
18917
|
if (existing.length === 0) {
|
|
18664
18918
|
const updateCommand2 = "generate_rules()";
|
|
18665
18919
|
const notice2 = {
|
|
@@ -18681,7 +18935,7 @@ function getRulesNotice(folderPath, clientName) {
|
|
|
18681
18935
|
const versions = [];
|
|
18682
18936
|
for (const filePath of existing) {
|
|
18683
18937
|
try {
|
|
18684
|
-
const content =
|
|
18938
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
18685
18939
|
const version = extractRulesVersion(content);
|
|
18686
18940
|
if (!version) {
|
|
18687
18941
|
filesMissingVersion.push(filePath);
|
|
@@ -18873,32 +19127,32 @@ function replaceContextStreamBlock(existing, content) {
|
|
|
18873
19127
|
return { content: appended, status: "appended" };
|
|
18874
19128
|
}
|
|
18875
19129
|
async function upsertRuleFile(filePath, content, writeMode = "contextstream_block") {
|
|
18876
|
-
await
|
|
19130
|
+
await fs6.promises.mkdir(path7.dirname(filePath), { recursive: true });
|
|
18877
19131
|
if (writeMode === "full_file") {
|
|
18878
19132
|
let existing2 = "";
|
|
18879
19133
|
try {
|
|
18880
|
-
existing2 = await
|
|
19134
|
+
existing2 = await fs6.promises.readFile(filePath, "utf8");
|
|
18881
19135
|
} catch {
|
|
18882
19136
|
}
|
|
18883
|
-
await
|
|
19137
|
+
await fs6.promises.writeFile(filePath, content.trimEnd() + "\n", "utf8");
|
|
18884
19138
|
return existing2 ? "updated" : "created";
|
|
18885
19139
|
}
|
|
18886
19140
|
const wrappedContent = wrapWithMarkers(content);
|
|
18887
19141
|
let existing = "";
|
|
18888
19142
|
try {
|
|
18889
|
-
existing = await
|
|
19143
|
+
existing = await fs6.promises.readFile(filePath, "utf8");
|
|
18890
19144
|
} catch {
|
|
18891
19145
|
}
|
|
18892
19146
|
if (!existing) {
|
|
18893
|
-
await
|
|
19147
|
+
await fs6.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
|
|
18894
19148
|
return "created";
|
|
18895
19149
|
}
|
|
18896
19150
|
if (!existing.trim()) {
|
|
18897
|
-
await
|
|
19151
|
+
await fs6.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
|
|
18898
19152
|
return "updated";
|
|
18899
19153
|
}
|
|
18900
19154
|
const replaced = replaceContextStreamBlock(existing, content);
|
|
18901
|
-
await
|
|
19155
|
+
await fs6.promises.writeFile(filePath, replaced.content, "utf8");
|
|
18902
19156
|
return replaced.status;
|
|
18903
19157
|
}
|
|
18904
19158
|
async function writeEditorRules(options) {
|
|
@@ -18917,8 +19171,8 @@ async function writeEditorRules(options) {
|
|
|
18917
19171
|
continue;
|
|
18918
19172
|
}
|
|
18919
19173
|
for (const rule of ruleFiles) {
|
|
18920
|
-
const filePath =
|
|
18921
|
-
if (
|
|
19174
|
+
const filePath = path7.join(options.folderPath, rule.filename);
|
|
19175
|
+
if (fs6.existsSync(filePath) && !options.overwriteExisting) {
|
|
18922
19176
|
results.push({ editor, filename: rule.filename, status: "skipped (exists)" });
|
|
18923
19177
|
continue;
|
|
18924
19178
|
}
|
|
@@ -18975,7 +19229,7 @@ async function writeGlobalRules(options) {
|
|
|
18975
19229
|
continue;
|
|
18976
19230
|
}
|
|
18977
19231
|
for (const filePath of globalPaths) {
|
|
18978
|
-
if (
|
|
19232
|
+
if (fs6.existsSync(filePath) && !options.overwriteExisting) {
|
|
18979
19233
|
results.push({ editor, filename: filePath, status: "skipped (exists)", scope: "global" });
|
|
18980
19234
|
continue;
|
|
18981
19235
|
}
|
|
@@ -19068,9 +19322,9 @@ function humanizeKey(raw) {
|
|
|
19068
19322
|
const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
|
|
19069
19323
|
return withSpaces.toLowerCase();
|
|
19070
19324
|
}
|
|
19071
|
-
function buildParamDescription(key,
|
|
19325
|
+
function buildParamDescription(key, path21) {
|
|
19072
19326
|
const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
|
|
19073
|
-
const parent =
|
|
19327
|
+
const parent = path21[path21.length - 1];
|
|
19074
19328
|
if (parent === "target") {
|
|
19075
19329
|
if (key === "id") return "Target identifier (module path, function id, etc.).";
|
|
19076
19330
|
if (key === "type") return "Target type (module, file, function, type, variable).";
|
|
@@ -19101,7 +19355,7 @@ function getDescription(schema) {
|
|
|
19101
19355
|
if (def?.description && def.description.trim()) return def.description;
|
|
19102
19356
|
return void 0;
|
|
19103
19357
|
}
|
|
19104
|
-
function applyParamDescriptions(schema,
|
|
19358
|
+
function applyParamDescriptions(schema, path21 = []) {
|
|
19105
19359
|
if (!(schema instanceof external_exports.ZodObject)) {
|
|
19106
19360
|
return schema;
|
|
19107
19361
|
}
|
|
@@ -19112,7 +19366,7 @@ function applyParamDescriptions(schema, path20 = []) {
|
|
|
19112
19366
|
let nextField = field;
|
|
19113
19367
|
const existingDescription = getDescription(field);
|
|
19114
19368
|
if (field instanceof external_exports.ZodObject) {
|
|
19115
|
-
const nested = applyParamDescriptions(field, [...
|
|
19369
|
+
const nested = applyParamDescriptions(field, [...path21, key]);
|
|
19116
19370
|
if (nested !== field) {
|
|
19117
19371
|
nextField = nested;
|
|
19118
19372
|
changed = true;
|
|
@@ -19124,7 +19378,7 @@ function applyParamDescriptions(schema, path20 = []) {
|
|
|
19124
19378
|
changed = true;
|
|
19125
19379
|
}
|
|
19126
19380
|
} else {
|
|
19127
|
-
nextField = nextField.describe(buildParamDescription(key,
|
|
19381
|
+
nextField = nextField.describe(buildParamDescription(key, path21));
|
|
19128
19382
|
changed = true;
|
|
19129
19383
|
}
|
|
19130
19384
|
nextShape[key] = nextField;
|
|
@@ -19773,8 +20027,10 @@ var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
19773
20027
|
// Consolidates media indexing, search, and clip retrieval for Remotion/FFmpeg
|
|
19774
20028
|
"skill",
|
|
19775
20029
|
// Skill management: list, get, create, update, run, delete, import, export, share
|
|
19776
|
-
"help"
|
|
20030
|
+
"help",
|
|
19777
20031
|
// Consolidates session_tools, auth_me, mcp_server_version, etc.
|
|
20032
|
+
"vcs"
|
|
20033
|
+
// Version control: status, diff, log, blame, branches, stash_list
|
|
19778
20034
|
]);
|
|
19779
20035
|
function mapToolToConsolidatedDomain(toolName) {
|
|
19780
20036
|
if (CONSOLIDATED_TOOLS.has(toolName)) return toolName;
|
|
@@ -19882,6 +20138,29 @@ function formatContent(data, forceFormat) {
|
|
|
19882
20138
|
const usePretty = forceFormat === "pretty" || !forceFormat && !COMPACT_OUTPUT;
|
|
19883
20139
|
return usePretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
19884
20140
|
}
|
|
20141
|
+
var GHOST_TITLE_PATTERNS = [
|
|
20142
|
+
/\(No assistant output found/i,
|
|
20143
|
+
/^\s*\(no title\)\s*$/i,
|
|
20144
|
+
/^\s*undefined\s*$/i,
|
|
20145
|
+
/^\s*null\s*$/i
|
|
20146
|
+
];
|
|
20147
|
+
function sanitizeTitle(title, fallbackPrefix = "Untitled") {
|
|
20148
|
+
if (!title || typeof title !== "string") return `${fallbackPrefix} item`;
|
|
20149
|
+
const trimmed = title.trim();
|
|
20150
|
+
if (!trimmed) return `${fallbackPrefix} item`;
|
|
20151
|
+
for (const pat of GHOST_TITLE_PATTERNS) {
|
|
20152
|
+
if (pat.test(trimmed)) return `${fallbackPrefix} plan`;
|
|
20153
|
+
}
|
|
20154
|
+
return trimmed;
|
|
20155
|
+
}
|
|
20156
|
+
function sanitizeItemTitles(items, prefix = "Untitled") {
|
|
20157
|
+
return items.map((item) => {
|
|
20158
|
+
if (item && typeof item === "object" && "title" in item) {
|
|
20159
|
+
return { ...item, title: sanitizeTitle(item.title, prefix) };
|
|
20160
|
+
}
|
|
20161
|
+
return item;
|
|
20162
|
+
});
|
|
20163
|
+
}
|
|
19885
20164
|
function toStructured(data) {
|
|
19886
20165
|
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
19887
20166
|
return data;
|
|
@@ -20676,6 +20955,16 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20676
20955
|
typeof ctx?.project_id === "string" ? ctx.project_id : void 0
|
|
20677
20956
|
);
|
|
20678
20957
|
}
|
|
20958
|
+
function projectScopeRequiredResult(action, workspaceId, folderPath) {
|
|
20959
|
+
const suggestedProjectName = folderPath ? path7.basename(folderPath) || "default-project" : "default-project";
|
|
20960
|
+
const createCommand = workspaceId ? folderPath ? `project(action="create", name="${suggestedProjectName}", workspace_id="${workspaceId}", folder_path="${folderPath}")` : `project(action="create", name="${suggestedProjectName}", workspace_id="${workspaceId}")` : folderPath ? `init(folder_path="${folderPath}")` : `init(folder_path="<your_project_path>")`;
|
|
20961
|
+
const scopeHint = workspaceId ? "Workspace-only fallback is active for this folder, but this action requires a project scope." : "No workspace/project scope is currently resolved for this folder.";
|
|
20962
|
+
return errorResult(
|
|
20963
|
+
`${action} requires a project scope.
|
|
20964
|
+
${scopeHint}
|
|
20965
|
+
Next step: ${createCommand}`
|
|
20966
|
+
);
|
|
20967
|
+
}
|
|
20679
20968
|
function isNotFoundError(error) {
|
|
20680
20969
|
return error instanceof HttpError && error.status === 404;
|
|
20681
20970
|
}
|
|
@@ -20688,6 +20977,53 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20688
20977
|
function appendNote(existing, extra) {
|
|
20689
20978
|
return existing ? `${existing} ${extra}` : extra;
|
|
20690
20979
|
}
|
|
20980
|
+
function containsCodeIdentifiers(query) {
|
|
20981
|
+
const tokens = query.split(/\s+/);
|
|
20982
|
+
return tokens.some(
|
|
20983
|
+
(t) => /[A-Z][a-z]/.test(t) || t.includes("_") || t.includes(".") || t.includes("::")
|
|
20984
|
+
);
|
|
20985
|
+
}
|
|
20986
|
+
async function runLocalRipgrep(query, cwd, limit = 10) {
|
|
20987
|
+
try {
|
|
20988
|
+
const pattern = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20989
|
+
const { stdout: stdout2 } = await execFileAsync("rg", [
|
|
20990
|
+
"--json",
|
|
20991
|
+
"--max-count",
|
|
20992
|
+
"3",
|
|
20993
|
+
"--max-filesize",
|
|
20994
|
+
"512K",
|
|
20995
|
+
"-i",
|
|
20996
|
+
pattern,
|
|
20997
|
+
cwd
|
|
20998
|
+
], { timeout: 5e3, maxBuffer: 1024 * 1024 });
|
|
20999
|
+
const results = [];
|
|
21000
|
+
for (const line of stdout2.split("\n")) {
|
|
21001
|
+
if (!line.trim()) continue;
|
|
21002
|
+
try {
|
|
21003
|
+
const parsed = JSON.parse(line);
|
|
21004
|
+
if (parsed.type === "match" && parsed.data) {
|
|
21005
|
+
const filePath = parsed.data.path?.text || "";
|
|
21006
|
+
const lineNum = parsed.data.line_number || 0;
|
|
21007
|
+
const text = parsed.data.lines?.text?.trim() || "";
|
|
21008
|
+
if (filePath && text) {
|
|
21009
|
+
const relative2 = filePath.startsWith(cwd) ? filePath.slice(cwd.length).replace(/^\//, "") : filePath;
|
|
21010
|
+
results.push({
|
|
21011
|
+
file_path: relative2,
|
|
21012
|
+
line: lineNum,
|
|
21013
|
+
content: text.slice(0, 200),
|
|
21014
|
+
score: 0.5
|
|
21015
|
+
});
|
|
21016
|
+
}
|
|
21017
|
+
}
|
|
21018
|
+
} catch {
|
|
21019
|
+
continue;
|
|
21020
|
+
}
|
|
21021
|
+
}
|
|
21022
|
+
return results.slice(0, limit);
|
|
21023
|
+
} catch {
|
|
21024
|
+
return [];
|
|
21025
|
+
}
|
|
21026
|
+
}
|
|
20691
21027
|
function extractSearchEnvelope(result) {
|
|
20692
21028
|
const data = result?.data ?? result ?? {};
|
|
20693
21029
|
const results = Array.isArray(data?.results) ? data.results : [];
|
|
@@ -20824,6 +21160,12 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
20824
21160
|
reason: "Detected identifier-like query; refactor mode is more precise."
|
|
20825
21161
|
};
|
|
20826
21162
|
}
|
|
21163
|
+
if (wordCount >= 2 && containsCodeIdentifiers(trimmed)) {
|
|
21164
|
+
return {
|
|
21165
|
+
mode: "hybrid",
|
|
21166
|
+
reason: "Multi-word query contains code identifiers (camelCase/snake_case); hybrid provides better coverage than pure semantic."
|
|
21167
|
+
};
|
|
21168
|
+
}
|
|
20827
21169
|
if (QUESTION_WORDS.some((w) => lower.startsWith(w)) || trimmed.endsWith("?") || wordCount >= 3) {
|
|
20828
21170
|
return {
|
|
20829
21171
|
mode: "semantic",
|
|
@@ -21016,11 +21358,11 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
21016
21358
|
async function indexedProjectIdForFolder(folderPath) {
|
|
21017
21359
|
const status = await readIndexStatus();
|
|
21018
21360
|
const projects = status.projects ?? {};
|
|
21019
|
-
const resolvedFolder =
|
|
21361
|
+
const resolvedFolder = path7.resolve(folderPath);
|
|
21020
21362
|
let bestMatch;
|
|
21021
21363
|
for (const [projectPath, info] of Object.entries(projects)) {
|
|
21022
|
-
const resolvedProjectPath =
|
|
21023
|
-
const matches = resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${
|
|
21364
|
+
const resolvedProjectPath = path7.resolve(projectPath);
|
|
21365
|
+
const matches = resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${path7.sep}`) || resolvedProjectPath.startsWith(`${resolvedFolder}${path7.sep}`);
|
|
21024
21366
|
if (!matches) continue;
|
|
21025
21367
|
if (!info?.indexed_at) continue;
|
|
21026
21368
|
const indexedAt = new Date(info.indexed_at);
|
|
@@ -21050,7 +21392,15 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
21050
21392
|
case "exhaustive":
|
|
21051
21393
|
return client.searchExhaustive(params);
|
|
21052
21394
|
case "refactor":
|
|
21053
|
-
|
|
21395
|
+
try {
|
|
21396
|
+
return await client.searchRefactor(params);
|
|
21397
|
+
} catch (err) {
|
|
21398
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21399
|
+
if (msg.includes("404") || msg.includes("not found") || msg.includes("Not Found")) {
|
|
21400
|
+
return client.searchKeyword(params);
|
|
21401
|
+
}
|
|
21402
|
+
throw err;
|
|
21403
|
+
}
|
|
21054
21404
|
case "crawl":
|
|
21055
21405
|
return client.searchCrawl(params);
|
|
21056
21406
|
default:
|
|
@@ -21058,10 +21408,10 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
21058
21408
|
}
|
|
21059
21409
|
}
|
|
21060
21410
|
async function validateReadableDirectory(inputPath) {
|
|
21061
|
-
const resolvedPath =
|
|
21411
|
+
const resolvedPath = path7.resolve(inputPath);
|
|
21062
21412
|
let stats;
|
|
21063
21413
|
try {
|
|
21064
|
-
stats = await
|
|
21414
|
+
stats = await fs6.promises.stat(resolvedPath);
|
|
21065
21415
|
} catch (error) {
|
|
21066
21416
|
if (error?.code === "ENOENT") {
|
|
21067
21417
|
return { ok: false, error: `Error: path does not exist: ${inputPath}` };
|
|
@@ -21075,7 +21425,7 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
21075
21425
|
return { ok: false, error: `Error: path is not a directory: ${inputPath}` };
|
|
21076
21426
|
}
|
|
21077
21427
|
try {
|
|
21078
|
-
await
|
|
21428
|
+
await fs6.promises.access(resolvedPath, fs6.constants.R_OK | fs6.constants.X_OK);
|
|
21079
21429
|
} catch (error) {
|
|
21080
21430
|
return {
|
|
21081
21431
|
ok: false,
|
|
@@ -22077,17 +22427,17 @@ Access: Free`,
|
|
|
22077
22427
|
let rulesSkipped = [];
|
|
22078
22428
|
if (input.folder_path && projectData.id) {
|
|
22079
22429
|
try {
|
|
22080
|
-
const configDir =
|
|
22081
|
-
const configPath =
|
|
22082
|
-
if (!
|
|
22083
|
-
|
|
22430
|
+
const configDir = path7.join(input.folder_path, ".contextstream");
|
|
22431
|
+
const configPath = path7.join(configDir, "config.json");
|
|
22432
|
+
if (!fs6.existsSync(configDir)) {
|
|
22433
|
+
fs6.mkdirSync(configDir, { recursive: true });
|
|
22084
22434
|
}
|
|
22085
22435
|
const config = {
|
|
22086
22436
|
workspace_id: workspaceId,
|
|
22087
22437
|
project_id: projectData.id,
|
|
22088
22438
|
project_name: input.name
|
|
22089
22439
|
};
|
|
22090
|
-
|
|
22440
|
+
fs6.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
22091
22441
|
if (input.generate_editor_rules) {
|
|
22092
22442
|
const ruleResults = await writeEditorRules({
|
|
22093
22443
|
folderPath: input.folder_path,
|
|
@@ -22168,9 +22518,9 @@ Access: Free`,
|
|
|
22168
22518
|
async (input) => {
|
|
22169
22519
|
const projectId = resolveProjectId(input.project_id);
|
|
22170
22520
|
if (!projectId) {
|
|
22171
|
-
|
|
22172
|
-
|
|
22173
|
-
);
|
|
22521
|
+
const folderPath = resolveFolderPath(void 0, sessionManager) || void 0;
|
|
22522
|
+
const workspaceId = resolveWorkspaceId(void 0);
|
|
22523
|
+
return projectScopeRequiredResult("index", workspaceId, folderPath);
|
|
22174
22524
|
}
|
|
22175
22525
|
const result = await client.indexProject(projectId);
|
|
22176
22526
|
return {
|
|
@@ -22201,7 +22551,8 @@ Access: Free`,
|
|
|
22201
22551
|
content_max_chars: contentMax,
|
|
22202
22552
|
context_lines: contextLines,
|
|
22203
22553
|
exact_match_boost: exactMatchBoost,
|
|
22204
|
-
output_format: input.output_format
|
|
22554
|
+
output_format: input.output_format,
|
|
22555
|
+
hot_paths_hint: input.hot_paths_hint
|
|
22205
22556
|
};
|
|
22206
22557
|
}
|
|
22207
22558
|
function getSearchAuthError() {
|
|
@@ -23587,7 +23938,7 @@ This does semantic search on the first message. You only need context on subsequ
|
|
|
23587
23938
|
formatContent(result)
|
|
23588
23939
|
].join("\n");
|
|
23589
23940
|
} else if (status === "requires_workspace_selection") {
|
|
23590
|
-
const folderName = typeof result.folder_name === "string" ? result.folder_name : typeof input.folder_path === "string" ?
|
|
23941
|
+
const folderName = typeof result.folder_name === "string" ? result.folder_name : typeof input.folder_path === "string" ? path7.basename(input.folder_path) || "this folder" : "this folder";
|
|
23591
23942
|
const candidates = Array.isArray(result.workspace_candidates) ? result.workspace_candidates : [];
|
|
23592
23943
|
const lines = [];
|
|
23593
23944
|
lines.push(
|
|
@@ -23840,7 +24191,7 @@ Behavior:
|
|
|
23840
24191
|
"Error: folder_path is required. Provide folder_path or run from a project directory."
|
|
23841
24192
|
);
|
|
23842
24193
|
}
|
|
23843
|
-
const folderName =
|
|
24194
|
+
const folderName = path7.basename(folderPath) || "My Project";
|
|
23844
24195
|
let newWorkspace;
|
|
23845
24196
|
try {
|
|
23846
24197
|
newWorkspace = await client.createWorkspace({
|
|
@@ -26569,9 +26920,22 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26569
26920
|
};
|
|
26570
26921
|
}
|
|
26571
26922
|
const dispatchMode = mode === "hybrid" ? "hybrid" : mode;
|
|
26572
|
-
let result
|
|
26923
|
+
let result;
|
|
26573
26924
|
let executedMode = dispatchMode;
|
|
26574
26925
|
let fallbackNote;
|
|
26926
|
+
try {
|
|
26927
|
+
result = await executeSearchMode(dispatchMode, baseParams2);
|
|
26928
|
+
} catch (execError) {
|
|
26929
|
+
const execMsg = execError instanceof Error ? execError.message : String(execError);
|
|
26930
|
+
const isEmbedTimeout = execMsg.toLowerCase().includes("embedding timed out") || execMsg.toLowerCase().includes("embedding timeout");
|
|
26931
|
+
if (isEmbedTimeout && dispatchMode !== "keyword" && dispatchMode !== "exhaustive") {
|
|
26932
|
+
result = await executeSearchMode("keyword", baseParams2);
|
|
26933
|
+
executedMode = "keyword";
|
|
26934
|
+
fallbackNote = `${dispatchMode} mode failed with embedding timeout; fell back to keyword search.`;
|
|
26935
|
+
} else {
|
|
26936
|
+
throw execError;
|
|
26937
|
+
}
|
|
26938
|
+
}
|
|
26575
26939
|
if (shouldRetrySemanticFallback(input.query, dispatchMode, result)) {
|
|
26576
26940
|
try {
|
|
26577
26941
|
const semanticResult = await executeSearchMode("semantic", baseParams2);
|
|
@@ -26719,10 +27083,19 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26719
27083
|
};
|
|
26720
27084
|
let selected;
|
|
26721
27085
|
let explicitScopeHadNoResults = false;
|
|
27086
|
+
const activePaths = sessionManager?.getActiveFiles?.() || [];
|
|
27087
|
+
const hotPathsHint = globalHotPathStore.buildHint({
|
|
27088
|
+
workspace_id: workspaceId,
|
|
27089
|
+
project_id: explicitProjectId || resolvedFolderProjectId || localIndexProjectId || sessionProjectId,
|
|
27090
|
+
query: input.query,
|
|
27091
|
+
active_paths: activePaths,
|
|
27092
|
+
limit: 8
|
|
27093
|
+
});
|
|
26722
27094
|
const baseParams = normalizeSearchParams({
|
|
26723
27095
|
...input,
|
|
26724
27096
|
workspace_id: workspaceId,
|
|
26725
27097
|
project_id: void 0,
|
|
27098
|
+
hot_paths_hint: hotPathsHint,
|
|
26726
27099
|
output_format: input.output_format || suggestOutputFormat(input.query, requestedMode === "team" ? "hybrid" : requestedMode)
|
|
26727
27100
|
});
|
|
26728
27101
|
if (requestedMode === "team") {
|
|
@@ -26778,6 +27151,36 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26778
27151
|
if (isNotFoundError(error)) {
|
|
26779
27152
|
continue;
|
|
26780
27153
|
}
|
|
27154
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
27155
|
+
const isEmbeddingTimeout = errMsg.toLowerCase().includes("embedding timed out") || errMsg.toLowerCase().includes("embedding timeout");
|
|
27156
|
+
if (isEmbeddingTimeout && requestedMode !== "keyword" && requestedMode !== "exhaustive") {
|
|
27157
|
+
try {
|
|
27158
|
+
const keywordFallback = await runSearchForMode("keyword", paramsForCandidate);
|
|
27159
|
+
const kwEnvelope = extractSearchEnvelope(keywordFallback.result);
|
|
27160
|
+
selected = {
|
|
27161
|
+
index,
|
|
27162
|
+
project_id: candidateProjectId,
|
|
27163
|
+
result: keywordFallback.result,
|
|
27164
|
+
executedMode: "keyword",
|
|
27165
|
+
fallbackNote: appendNote(
|
|
27166
|
+
keywordFallback.fallbackNote,
|
|
27167
|
+
`${requestedMode} mode failed with embedding timeout; fell back to keyword search.`
|
|
27168
|
+
)
|
|
27169
|
+
};
|
|
27170
|
+
if (kwEnvelope.results.length > 0) break;
|
|
27171
|
+
} catch {
|
|
27172
|
+
if (!selected) {
|
|
27173
|
+
selected = {
|
|
27174
|
+
index,
|
|
27175
|
+
project_id: candidateProjectId,
|
|
27176
|
+
result: { data: { results: [], total: 0 } },
|
|
27177
|
+
executedMode: "keyword",
|
|
27178
|
+
fallbackNote: "Embedding timed out and keyword fallback also failed."
|
|
27179
|
+
};
|
|
27180
|
+
}
|
|
27181
|
+
}
|
|
27182
|
+
continue;
|
|
27183
|
+
}
|
|
26781
27184
|
throw error;
|
|
26782
27185
|
}
|
|
26783
27186
|
}
|
|
@@ -26820,7 +27223,32 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26820
27223
|
}
|
|
26821
27224
|
const docsFallback = requestedMode !== "team" && isDocLookupQuery(input.query) && extractSearchEnvelope(selected.result).results.length === 0 ? await findDocsFallback(workspaceId, candidateProjectIds, input.query, input.limit) : void 0;
|
|
26822
27225
|
const roundTripMs = Date.now() - startTime;
|
|
26823
|
-
|
|
27226
|
+
let { results, total } = extractSearchEnvelope(selected.result);
|
|
27227
|
+
if (results.length === 0 && folderPath) {
|
|
27228
|
+
const rgResults = await runLocalRipgrep(input.query, folderPath, input.limit || 10);
|
|
27229
|
+
if (rgResults.length > 0) {
|
|
27230
|
+
results = rgResults.map((r) => ({
|
|
27231
|
+
file_path: r.file_path,
|
|
27232
|
+
start_line: r.line,
|
|
27233
|
+
content: r.content,
|
|
27234
|
+
score: r.score,
|
|
27235
|
+
source: "local_ripgrep"
|
|
27236
|
+
}));
|
|
27237
|
+
total = results.length;
|
|
27238
|
+
modeFallbackNote = appendNote(
|
|
27239
|
+
modeFallbackNote,
|
|
27240
|
+
`API search returned no results; found ${results.length} local match(es) via ripgrep.`
|
|
27241
|
+
);
|
|
27242
|
+
}
|
|
27243
|
+
}
|
|
27244
|
+
const resultPaths = results.map((item) => typeof item?.file_path === "string" ? item.file_path : "").filter(Boolean);
|
|
27245
|
+
if (resultPaths.length > 0) {
|
|
27246
|
+
globalHotPathStore.recordPaths(
|
|
27247
|
+
{ workspace_id: workspaceId, project_id: selected.project_id || sessionProjectId },
|
|
27248
|
+
resultPaths.slice(0, 20),
|
|
27249
|
+
"search_result"
|
|
27250
|
+
);
|
|
27251
|
+
}
|
|
26824
27252
|
const lines = [];
|
|
26825
27253
|
if (SHOW_TIMING) {
|
|
26826
27254
|
lines.push(`\u2713 ${total} results in ${roundTripMs}ms`);
|
|
@@ -26833,6 +27261,14 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26833
27261
|
if (modeFallbackNote) {
|
|
26834
27262
|
lines.push(modeFallbackNote);
|
|
26835
27263
|
}
|
|
27264
|
+
if (hotPathsHint?.entries?.length) {
|
|
27265
|
+
lines.push(
|
|
27266
|
+
`Hot-path hint active (${hotPathsHint.entries.length} paths, confidence ${(hotPathsHint.confidence * 100).toFixed(0)}%).`
|
|
27267
|
+
);
|
|
27268
|
+
lines.push(
|
|
27269
|
+
"Hot-path weighting is bounded and additive only; if hotspot paths do not match this query, baseline search ranking is used."
|
|
27270
|
+
);
|
|
27271
|
+
}
|
|
26836
27272
|
if (results.length > 0) {
|
|
26837
27273
|
lines.push("");
|
|
26838
27274
|
results.forEach((r, i) => {
|
|
@@ -26863,7 +27299,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26863
27299
|
`Use memory(action="get_doc", doc_id="...") to open a specific doc or memory(action="list_docs") to browse more.`
|
|
26864
27300
|
);
|
|
26865
27301
|
} else {
|
|
26866
|
-
lines.push("No results found. Try a different query or search
|
|
27302
|
+
lines.push("No results found. Try a different query, search mode, or broader scope (remove project_id to search workspace-wide).");
|
|
26867
27303
|
if (isDocLookupQuery(input.query)) {
|
|
26868
27304
|
lines.push(
|
|
26869
27305
|
`This query appears to target saved docs. Try memory(action="list_docs") and then memory(action="get_doc", doc_id="...").`
|
|
@@ -26897,6 +27333,20 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
26897
27333
|
count: docsFallback.docs.length
|
|
26898
27334
|
};
|
|
26899
27335
|
}
|
|
27336
|
+
if (hotPathsHint?.entries?.length) {
|
|
27337
|
+
structuredData.hot_path_hint = {
|
|
27338
|
+
active: true,
|
|
27339
|
+
path_count: hotPathsHint.entries.length,
|
|
27340
|
+
confidence: hotPathsHint.confidence,
|
|
27341
|
+
guardrail: "bounded_additive_prior",
|
|
27342
|
+
fallback_behavior: "if no affinity match, baseline ranking remains unchanged"
|
|
27343
|
+
};
|
|
27344
|
+
} else {
|
|
27345
|
+
structuredData.hot_path_hint = {
|
|
27346
|
+
active: false,
|
|
27347
|
+
fallback_behavior: "baseline ranking only"
|
|
27348
|
+
};
|
|
27349
|
+
}
|
|
26900
27350
|
const resultWithMeta = {
|
|
26901
27351
|
...selected.result,
|
|
26902
27352
|
data: structuredData
|
|
@@ -27129,7 +27579,18 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27129
27579
|
context_hint: input.query,
|
|
27130
27580
|
limit: input.limit
|
|
27131
27581
|
});
|
|
27132
|
-
|
|
27582
|
+
let lessons = result?.data?.lessons || result?.lessons || [];
|
|
27583
|
+
if (Array.isArray(lessons) && lessons.length > 1) {
|
|
27584
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27585
|
+
lessons = lessons.filter((lesson) => {
|
|
27586
|
+
const key = (lesson.title || "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
27587
|
+
if (!key || seen.has(key)) return false;
|
|
27588
|
+
seen.add(key);
|
|
27589
|
+
return true;
|
|
27590
|
+
});
|
|
27591
|
+
if (result?.data?.lessons) result.data.lessons = lessons;
|
|
27592
|
+
else if (result?.lessons) result.lessons = lessons;
|
|
27593
|
+
}
|
|
27133
27594
|
const resultWithHint = Array.isArray(lessons) && lessons.length === 0 ? { ...result, hint: getEmptyStateHint("get_lessons") } : result;
|
|
27134
27595
|
return {
|
|
27135
27596
|
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
@@ -27146,8 +27607,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27146
27607
|
include_related: input.include_related,
|
|
27147
27608
|
include_decisions: input.include_decisions
|
|
27148
27609
|
});
|
|
27149
|
-
const
|
|
27150
|
-
const
|
|
27610
|
+
const r = result;
|
|
27611
|
+
const recallResults = r?.data?.results || r?.results || [];
|
|
27612
|
+
const memoryResults = r?.memory_results?.data?.results || r?.memory_results?.results || [];
|
|
27613
|
+
const hasAnyResults = Array.isArray(recallResults) && recallResults.length > 0 || Array.isArray(memoryResults) && memoryResults.length > 0;
|
|
27614
|
+
const recallWithHint = hasAnyResults ? result : { ...result, hint: getEmptyStateHint("recall") };
|
|
27151
27615
|
const outputText = formatContent(recallWithHint);
|
|
27152
27616
|
trackToolTokenSavings(client, "session_recall", outputText, {
|
|
27153
27617
|
workspace_id: workspaceId,
|
|
@@ -27191,6 +27655,32 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27191
27655
|
project_id: projectId,
|
|
27192
27656
|
max_tokens: input.max_tokens
|
|
27193
27657
|
});
|
|
27658
|
+
const summaryObj = result;
|
|
27659
|
+
if (summaryObj && (summaryObj.decision_count === 0 || summaryObj.memory_count === 0)) {
|
|
27660
|
+
const events = await client.listMemoryEvents({
|
|
27661
|
+
workspace_id: workspaceId,
|
|
27662
|
+
project_id: projectId,
|
|
27663
|
+
limit: 100
|
|
27664
|
+
}).catch(() => null);
|
|
27665
|
+
if (events?.items && Array.isArray(events.items)) {
|
|
27666
|
+
let decisionCount = 0;
|
|
27667
|
+
let lessonCount = 0;
|
|
27668
|
+
let memoryCount = events.items.length;
|
|
27669
|
+
for (const item of events.items) {
|
|
27670
|
+
if (isDecisionResult(item)) decisionCount++;
|
|
27671
|
+
if (isLessonResult(item)) lessonCount++;
|
|
27672
|
+
}
|
|
27673
|
+
if (decisionCount > 0 && summaryObj.decision_count === 0) {
|
|
27674
|
+
summaryObj.decision_count = decisionCount;
|
|
27675
|
+
}
|
|
27676
|
+
if (summaryObj.memory_count === 0) {
|
|
27677
|
+
summaryObj.memory_count = memoryCount;
|
|
27678
|
+
}
|
|
27679
|
+
if (lessonCount > 0 && !summaryObj.lesson_count) {
|
|
27680
|
+
summaryObj.lesson_count = lessonCount;
|
|
27681
|
+
}
|
|
27682
|
+
}
|
|
27683
|
+
}
|
|
27194
27684
|
const outputText = formatContent(result);
|
|
27195
27685
|
trackToolTokenSavings(client, "session_summary", outputText, {
|
|
27196
27686
|
workspace_id: workspaceId,
|
|
@@ -27252,16 +27742,43 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27252
27742
|
if (!input.query) {
|
|
27253
27743
|
return errorResult("decision_trace requires: query");
|
|
27254
27744
|
}
|
|
27255
|
-
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
|
|
27263
|
-
|
|
27264
|
-
|
|
27745
|
+
try {
|
|
27746
|
+
const result = await client.decisionTrace({
|
|
27747
|
+
workspace_id: workspaceId,
|
|
27748
|
+
project_id: projectId,
|
|
27749
|
+
query: input.query,
|
|
27750
|
+
include_impact: input.include_impact,
|
|
27751
|
+
limit: input.limit
|
|
27752
|
+
});
|
|
27753
|
+
return {
|
|
27754
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
27755
|
+
};
|
|
27756
|
+
} catch (err) {
|
|
27757
|
+
const isTimeout = err?.message?.toLowerCase().includes("timeout") || err?.message?.toLowerCase().includes("embedding timed out");
|
|
27758
|
+
if (!isTimeout) throw err;
|
|
27759
|
+
const events = await client.listMemoryEvents({
|
|
27760
|
+
workspace_id: workspaceId,
|
|
27761
|
+
project_id: projectId,
|
|
27762
|
+
limit: 50
|
|
27763
|
+
}).catch(() => ({ items: [] }));
|
|
27764
|
+
const decisions = (events?.items || []).filter((item) => isDecisionResult(item));
|
|
27765
|
+
const queryLower = input.query.toLowerCase();
|
|
27766
|
+
const matched = decisions.filter((d) => {
|
|
27767
|
+
const text = `${d.title || ""} ${d.content || ""}`.toLowerCase();
|
|
27768
|
+
return queryLower.split(/\s+/).some((w) => w.length > 2 && text.includes(w));
|
|
27769
|
+
}).slice(0, input.limit || 10);
|
|
27770
|
+
return {
|
|
27771
|
+
content: [{
|
|
27772
|
+
type: "text",
|
|
27773
|
+
text: formatContent({
|
|
27774
|
+
decisions: matched,
|
|
27775
|
+
total: matched.length,
|
|
27776
|
+
fallback_reason: "embedding_timeout",
|
|
27777
|
+
hint: "Decision trace fell back to keyword search due to embedding timeout."
|
|
27778
|
+
})
|
|
27779
|
+
}]
|
|
27780
|
+
};
|
|
27781
|
+
}
|
|
27265
27782
|
}
|
|
27266
27783
|
// Plan actions
|
|
27267
27784
|
case "capture_plan": {
|
|
@@ -27336,7 +27853,12 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27336
27853
|
is_personal: input.is_personal
|
|
27337
27854
|
});
|
|
27338
27855
|
const plans = result?.data?.plans || result?.plans || result?.data?.items || result?.items || [];
|
|
27339
|
-
const
|
|
27856
|
+
const sanitized = sanitizeItemTitles(plans, "Untitled");
|
|
27857
|
+
if (result?.data?.plans) result.data.plans = sanitized;
|
|
27858
|
+
else if (result?.plans) result.plans = sanitized;
|
|
27859
|
+
else if (result?.data?.items) result.data.items = sanitized;
|
|
27860
|
+
else if (result?.items) result.items = sanitized;
|
|
27861
|
+
const resultWithHint = sanitized.length === 0 ? { ...result, hint: getEmptyStateHint("list_plans") } : result;
|
|
27340
27862
|
return {
|
|
27341
27863
|
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
27342
27864
|
};
|
|
@@ -27920,8 +28442,32 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
27920
28442
|
const result = await client.listMemoryEvents({
|
|
27921
28443
|
workspace_id: workspaceId,
|
|
27922
28444
|
project_id: projectId,
|
|
27923
|
-
limit: input.limit
|
|
28445
|
+
limit: input.limit,
|
|
28446
|
+
tags: input.tags,
|
|
28447
|
+
event_type: input.event_type
|
|
27924
28448
|
});
|
|
28449
|
+
const items = result?.data?.items || result?.items || [];
|
|
28450
|
+
if (Array.isArray(items)) {
|
|
28451
|
+
let filtered = items;
|
|
28452
|
+
if (input.tags && input.tags.length > 0) {
|
|
28453
|
+
const requiredTags = input.tags;
|
|
28454
|
+
filtered = filtered.filter((item) => {
|
|
28455
|
+
const itemTags = extractEventTags(item);
|
|
28456
|
+
return requiredTags.every((tag) => itemTags.includes(tag));
|
|
28457
|
+
});
|
|
28458
|
+
}
|
|
28459
|
+
if (input.event_type) {
|
|
28460
|
+
const targetType = input.event_type.toLowerCase();
|
|
28461
|
+
filtered = filtered.filter((item) => {
|
|
28462
|
+
const effective = extractEffectiveEventType(item).toLowerCase();
|
|
28463
|
+
return effective === targetType;
|
|
28464
|
+
});
|
|
28465
|
+
}
|
|
28466
|
+
if (filtered.length !== items.length) {
|
|
28467
|
+
if (result?.data?.items) result.data.items = filtered;
|
|
28468
|
+
else if (result?.items) result.items = filtered;
|
|
28469
|
+
}
|
|
28470
|
+
}
|
|
27925
28471
|
const events = result?.data?.items || result?.items || result?.data || [];
|
|
27926
28472
|
const resultWithHint = Array.isArray(events) && events.length === 0 ? { ...result, hint: getEmptyStateHint("list_events") } : result;
|
|
27927
28473
|
return {
|
|
@@ -28753,6 +29299,15 @@ ${formatContent({
|
|
|
28753
29299
|
limit: input.limit
|
|
28754
29300
|
});
|
|
28755
29301
|
const transcripts = result?.data?.items || result?.items || result?.data || [];
|
|
29302
|
+
if (Array.isArray(transcripts)) {
|
|
29303
|
+
for (const t of transcripts) {
|
|
29304
|
+
if (t && (!t.title || /^\s*\(no title\)\s*$/i.test(t.title))) {
|
|
29305
|
+
const typeStr = t.client_name || t.type || "session";
|
|
29306
|
+
const dateStr = t.started_at ? new Date(t.started_at).toLocaleDateString() : "unknown date";
|
|
29307
|
+
t.title = `${typeStr} transcript \u2014 ${dateStr}`;
|
|
29308
|
+
}
|
|
29309
|
+
}
|
|
29310
|
+
}
|
|
28756
29311
|
const resultWithHint = Array.isArray(transcripts) && transcripts.length === 0 ? { ...result, hint: "No transcripts found. Enable save_exchange in context() calls to save conversations." } : result;
|
|
28757
29312
|
return {
|
|
28758
29313
|
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
@@ -28947,6 +29502,20 @@ ${formatContent({
|
|
|
28947
29502
|
project_id: projectId,
|
|
28948
29503
|
limit: input.limit
|
|
28949
29504
|
});
|
|
29505
|
+
const graphData = result?.data || result;
|
|
29506
|
+
const graphItems = Array.isArray(graphData) ? graphData : graphData?.items || graphData?.results || [];
|
|
29507
|
+
if (Array.isArray(graphItems) && graphItems.length === 0) {
|
|
29508
|
+
const memFallback = await client.memoryDecisions({
|
|
29509
|
+
workspace_id: workspaceId,
|
|
29510
|
+
project_id: projectId,
|
|
29511
|
+
limit: input.limit
|
|
29512
|
+
}).catch(() => null);
|
|
29513
|
+
if (memFallback) {
|
|
29514
|
+
return {
|
|
29515
|
+
content: [{ type: "text", text: formatContent(memFallback) }]
|
|
29516
|
+
};
|
|
29517
|
+
}
|
|
29518
|
+
}
|
|
28950
29519
|
return {
|
|
28951
29520
|
content: [{ type: "text", text: formatContent(result) }]
|
|
28952
29521
|
};
|
|
@@ -28976,10 +29545,7 @@ ${formatContent({
|
|
|
28976
29545
|
};
|
|
28977
29546
|
}
|
|
28978
29547
|
case "contradictions": {
|
|
28979
|
-
|
|
28980
|
-
return errorResult("contradictions requires: node_id");
|
|
28981
|
-
}
|
|
28982
|
-
const result = await client.findContradictions(input.node_id);
|
|
29548
|
+
const result = input.node_id ? await client.findContradictions(input.node_id) : { contradictions: [], hint: "Pass node_id to check a specific node for contradictions." };
|
|
28983
29549
|
return {
|
|
28984
29550
|
content: [{ type: "text", text: formatContent(result) }]
|
|
28985
29551
|
};
|
|
@@ -29059,6 +29625,17 @@ ${formatContent({
|
|
|
29059
29625
|
const explicitProjectId = normalizeUuid(input.project_id);
|
|
29060
29626
|
let projectId = explicitProjectId || resolveProjectId(void 0);
|
|
29061
29627
|
const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
|
|
29628
|
+
if (folderPath) {
|
|
29629
|
+
const mapping = resolveWorkspace(folderPath);
|
|
29630
|
+
const mappedWorkspaceId = normalizeUuid(mapping.config?.workspace_id);
|
|
29631
|
+
const mappedProjectId = normalizeUuid(mapping.config?.project_id);
|
|
29632
|
+
if (!input.workspace_id && mappedWorkspaceId) {
|
|
29633
|
+
workspaceId = mappedWorkspaceId;
|
|
29634
|
+
}
|
|
29635
|
+
if (!explicitProjectId && mappedProjectId) {
|
|
29636
|
+
projectId = mappedProjectId;
|
|
29637
|
+
}
|
|
29638
|
+
}
|
|
29062
29639
|
switch (input.action) {
|
|
29063
29640
|
case "list": {
|
|
29064
29641
|
const result = await client.listProjects({
|
|
@@ -29072,7 +29649,7 @@ ${formatContent({
|
|
|
29072
29649
|
}
|
|
29073
29650
|
case "get": {
|
|
29074
29651
|
if (!projectId) {
|
|
29075
|
-
return
|
|
29652
|
+
return projectScopeRequiredResult("get", workspaceId, folderPath);
|
|
29076
29653
|
}
|
|
29077
29654
|
const result = await client.getProject(projectId);
|
|
29078
29655
|
return {
|
|
@@ -29094,7 +29671,7 @@ ${formatContent({
|
|
|
29094
29671
|
}
|
|
29095
29672
|
case "update": {
|
|
29096
29673
|
if (!projectId) {
|
|
29097
|
-
return
|
|
29674
|
+
return projectScopeRequiredResult("update", workspaceId, folderPath);
|
|
29098
29675
|
}
|
|
29099
29676
|
const result = await client.updateProject(projectId, {
|
|
29100
29677
|
name: input.name,
|
|
@@ -29106,7 +29683,7 @@ ${formatContent({
|
|
|
29106
29683
|
}
|
|
29107
29684
|
case "delete": {
|
|
29108
29685
|
if (!projectId) {
|
|
29109
|
-
return
|
|
29686
|
+
return projectScopeRequiredResult("delete", workspaceId, folderPath);
|
|
29110
29687
|
}
|
|
29111
29688
|
const result = await client.deleteProject(projectId);
|
|
29112
29689
|
const normalized = result && typeof result === "object" ? result : {
|
|
@@ -29122,7 +29699,7 @@ ${formatContent({
|
|
|
29122
29699
|
}
|
|
29123
29700
|
case "index": {
|
|
29124
29701
|
if (!projectId) {
|
|
29125
|
-
return
|
|
29702
|
+
return projectScopeRequiredResult("index", workspaceId, folderPath);
|
|
29126
29703
|
}
|
|
29127
29704
|
try {
|
|
29128
29705
|
const result = await client.indexProject(projectId);
|
|
@@ -29171,7 +29748,7 @@ ${formatContent({
|
|
|
29171
29748
|
}
|
|
29172
29749
|
case "overview": {
|
|
29173
29750
|
if (!projectId) {
|
|
29174
|
-
return
|
|
29751
|
+
return projectScopeRequiredResult("overview", workspaceId, folderPath);
|
|
29175
29752
|
}
|
|
29176
29753
|
const result = await client.projectOverview(projectId);
|
|
29177
29754
|
return {
|
|
@@ -29180,7 +29757,7 @@ ${formatContent({
|
|
|
29180
29757
|
}
|
|
29181
29758
|
case "statistics": {
|
|
29182
29759
|
if (!projectId) {
|
|
29183
|
-
return
|
|
29760
|
+
return projectScopeRequiredResult("statistics", workspaceId, folderPath);
|
|
29184
29761
|
}
|
|
29185
29762
|
const result = await client.projectStatistics(projectId);
|
|
29186
29763
|
return {
|
|
@@ -29189,7 +29766,7 @@ ${formatContent({
|
|
|
29189
29766
|
}
|
|
29190
29767
|
case "files": {
|
|
29191
29768
|
if (!projectId) {
|
|
29192
|
-
return
|
|
29769
|
+
return projectScopeRequiredResult("files", workspaceId, folderPath);
|
|
29193
29770
|
}
|
|
29194
29771
|
const result = await client.projectFiles(projectId);
|
|
29195
29772
|
return {
|
|
@@ -29317,7 +29894,7 @@ ${formatContent(response)}` }],
|
|
|
29317
29894
|
}
|
|
29318
29895
|
case "index_history": {
|
|
29319
29896
|
if (!projectId) {
|
|
29320
|
-
return
|
|
29897
|
+
return projectScopeRequiredResult("index_history", workspaceId, folderPath);
|
|
29321
29898
|
}
|
|
29322
29899
|
const result = await client.projectIndexHistory(projectId, {
|
|
29323
29900
|
machine_id: input.machine_id,
|
|
@@ -29360,7 +29937,7 @@ ${formatContent(response)}`
|
|
|
29360
29937
|
return errorResult("ingest_local requires: path");
|
|
29361
29938
|
}
|
|
29362
29939
|
if (!projectId) {
|
|
29363
|
-
return
|
|
29940
|
+
return projectScopeRequiredResult("ingest_local", workspaceId, ingestPath);
|
|
29364
29941
|
}
|
|
29365
29942
|
const validPath = await validateReadableDirectory(ingestPath);
|
|
29366
29943
|
if (!validPath.ok) {
|
|
@@ -29413,13 +29990,9 @@ ${formatContent(response)}`
|
|
|
29413
29990
|
};
|
|
29414
29991
|
}
|
|
29415
29992
|
case "recent_changes": {
|
|
29416
|
-
|
|
29417
|
-
return errorResult(
|
|
29418
|
-
"recent_changes requires: folder_path (or path). Call init(...) first or pass a repo path explicitly."
|
|
29419
|
-
);
|
|
29420
|
-
}
|
|
29993
|
+
const recentChangesPath = folderPath || process.cwd();
|
|
29421
29994
|
try {
|
|
29422
|
-
const changes = await getRecentProjectChanges(
|
|
29995
|
+
const changes = await getRecentProjectChanges(recentChangesPath, input.limit ?? 10, input.since);
|
|
29423
29996
|
let text = "";
|
|
29424
29997
|
if (changes.commits.length === 0) {
|
|
29425
29998
|
text = "No recent commits found.";
|
|
@@ -30420,13 +30993,13 @@ Example workflow:
|
|
|
30420
30993
|
);
|
|
30421
30994
|
}
|
|
30422
30995
|
if (input.file_path) {
|
|
30423
|
-
const
|
|
30996
|
+
const fs21 = await import("fs/promises");
|
|
30424
30997
|
const pathModule = await import("path");
|
|
30425
30998
|
const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
|
|
30426
30999
|
const resolvedPath = pathModule.resolve(filePath);
|
|
30427
31000
|
let fileStats;
|
|
30428
31001
|
try {
|
|
30429
|
-
fileStats = await
|
|
31002
|
+
fileStats = await fs21.stat(resolvedPath);
|
|
30430
31003
|
} catch {
|
|
30431
31004
|
return errorResult(`File not found: ${resolvedPath}`);
|
|
30432
31005
|
}
|
|
@@ -30473,7 +31046,7 @@ Example workflow:
|
|
|
30473
31046
|
mime_type: mimeType,
|
|
30474
31047
|
tags: input.tags
|
|
30475
31048
|
});
|
|
30476
|
-
const fileBuffer = await
|
|
31049
|
+
const fileBuffer = await fs21.readFile(resolvedPath);
|
|
30477
31050
|
const uploadResponse = await fetch(uploadInit.upload_url, {
|
|
30478
31051
|
method: "PUT",
|
|
30479
31052
|
headers: uploadInit.headers,
|
|
@@ -30948,6 +31521,93 @@ Each domain tool has an 'action' parameter for specific operations.` : "";
|
|
|
30948
31521
|
);
|
|
30949
31522
|
logDebug(`Consolidated mode: ${CONSOLIDATED_TOOLS.size} domain tools`);
|
|
30950
31523
|
}
|
|
31524
|
+
registerTool(
|
|
31525
|
+
"vcs",
|
|
31526
|
+
{
|
|
31527
|
+
title: "Version Control",
|
|
31528
|
+
description: `Git version control operations. Actions: status (working tree status), diff (show changes), log (commit history), blame (line-by-line authorship), branches (list branches), stash_list (list stashes).`,
|
|
31529
|
+
inputSchema: external_exports.object({
|
|
31530
|
+
action: external_exports.enum(["status", "diff", "log", "blame", "branches", "stash_list"]).describe("VCS action to perform"),
|
|
31531
|
+
path: external_exports.string().optional().describe("File path for diff/blame (relative to repo root)"),
|
|
31532
|
+
ref: external_exports.string().optional().describe("Git ref (branch, tag, commit) for log/diff"),
|
|
31533
|
+
limit: external_exports.number().optional().describe("Max entries for log (default: 20)"),
|
|
31534
|
+
staged: external_exports.boolean().optional().describe("Show staged changes only (for diff)")
|
|
31535
|
+
})
|
|
31536
|
+
},
|
|
31537
|
+
async (input) => {
|
|
31538
|
+
const cwd = resolveFolderPath(void 0, sessionManager) || process.cwd();
|
|
31539
|
+
const runGit = async (args) => {
|
|
31540
|
+
try {
|
|
31541
|
+
const { stdout: stdout2 } = await execFileAsync("git", args, {
|
|
31542
|
+
cwd,
|
|
31543
|
+
timeout: 1e4,
|
|
31544
|
+
maxBuffer: 512 * 1024
|
|
31545
|
+
});
|
|
31546
|
+
return stdout2.trim();
|
|
31547
|
+
} catch (err) {
|
|
31548
|
+
if (err.stderr?.includes("not a git repository")) {
|
|
31549
|
+
throw new Error("Not a git repository. Run this from a git project directory.");
|
|
31550
|
+
}
|
|
31551
|
+
throw err;
|
|
31552
|
+
}
|
|
31553
|
+
};
|
|
31554
|
+
switch (input.action) {
|
|
31555
|
+
case "status": {
|
|
31556
|
+
const output = await runGit(["status", "--porcelain=v2", "--branch"]);
|
|
31557
|
+
return { content: [{ type: "text", text: output || "Working tree clean." }] };
|
|
31558
|
+
}
|
|
31559
|
+
case "diff": {
|
|
31560
|
+
const args = ["diff"];
|
|
31561
|
+
if (input.staged) args.push("--staged");
|
|
31562
|
+
if (input.ref) args.push(input.ref);
|
|
31563
|
+
if (input.path) args.push("--", input.path);
|
|
31564
|
+
const output = await runGit(args);
|
|
31565
|
+
return { content: [{ type: "text", text: output || "No changes." }] };
|
|
31566
|
+
}
|
|
31567
|
+
case "log": {
|
|
31568
|
+
const limit = input.limit || 20;
|
|
31569
|
+
const args = ["log", `--max-count=${limit}`, "--format=%H %ai %an <%ae>%n %s", "--no-decorate"];
|
|
31570
|
+
if (input.ref) args.push(input.ref);
|
|
31571
|
+
if (input.path) args.push("--", input.path);
|
|
31572
|
+
const output = await runGit(args);
|
|
31573
|
+
return { content: [{ type: "text", text: output || "No commits." }] };
|
|
31574
|
+
}
|
|
31575
|
+
case "blame": {
|
|
31576
|
+
if (!input.path) return errorResult("blame requires: path");
|
|
31577
|
+
const args = ["blame", "--porcelain", input.path];
|
|
31578
|
+
if (input.ref) args.splice(1, 0, input.ref);
|
|
31579
|
+
const output = await runGit(args);
|
|
31580
|
+
const summaryLines = [];
|
|
31581
|
+
const blameLines = output.split("\n");
|
|
31582
|
+
let currentCommit = "";
|
|
31583
|
+
let currentAuthor = "";
|
|
31584
|
+
let lineNum = 0;
|
|
31585
|
+
for (const line of blameLines) {
|
|
31586
|
+
if (/^[0-9a-f]{40}\s/.test(line)) {
|
|
31587
|
+
const parts = line.split(" ");
|
|
31588
|
+
currentCommit = parts[0].slice(0, 8);
|
|
31589
|
+
lineNum = parseInt(parts[2] || "0", 10);
|
|
31590
|
+
} else if (line.startsWith("author ")) {
|
|
31591
|
+
currentAuthor = line.slice(7);
|
|
31592
|
+
} else if (line.startsWith(" ")) {
|
|
31593
|
+
summaryLines.push(`${currentCommit} (${currentAuthor}) L${lineNum}: ${line.slice(1)}`);
|
|
31594
|
+
}
|
|
31595
|
+
}
|
|
31596
|
+
return { content: [{ type: "text", text: summaryLines.join("\n") || output }] };
|
|
31597
|
+
}
|
|
31598
|
+
case "branches": {
|
|
31599
|
+
const output = await runGit(["branch", "-a", "--format=%(refname:short) %(objectname:short) %(subject)"]);
|
|
31600
|
+
return { content: [{ type: "text", text: output || "No branches." }] };
|
|
31601
|
+
}
|
|
31602
|
+
case "stash_list": {
|
|
31603
|
+
const output = await runGit(["stash", "list"]);
|
|
31604
|
+
return { content: [{ type: "text", text: output || "No stashes." }] };
|
|
31605
|
+
}
|
|
31606
|
+
default:
|
|
31607
|
+
return errorResult(`Unknown vcs action: ${input.action}`);
|
|
31608
|
+
}
|
|
31609
|
+
}
|
|
31610
|
+
);
|
|
30951
31611
|
applyToolSurfaceProfile(activeSurfaceProfile, false);
|
|
30952
31612
|
}
|
|
30953
31613
|
function registerLimitedTools(server) {
|
|
@@ -31938,8 +32598,8 @@ var SessionManager = class _SessionManager {
|
|
|
31938
32598
|
/**
|
|
31939
32599
|
* Set the folder path hint (can be passed from tools that know the workspace path)
|
|
31940
32600
|
*/
|
|
31941
|
-
setFolderPath(
|
|
31942
|
-
this.folderPath =
|
|
32601
|
+
setFolderPath(path21) {
|
|
32602
|
+
this.folderPath = path21;
|
|
31943
32603
|
}
|
|
31944
32604
|
/**
|
|
31945
32605
|
* Mark that context_smart has been called in this session.
|
|
@@ -32129,7 +32789,7 @@ var SessionManager = class _SessionManager {
|
|
|
32129
32789
|
}
|
|
32130
32790
|
if (this.ideRoots.length === 0) {
|
|
32131
32791
|
const cwd = process.cwd();
|
|
32132
|
-
const
|
|
32792
|
+
const fs21 = await import("fs");
|
|
32133
32793
|
const projectIndicators = [
|
|
32134
32794
|
".git",
|
|
32135
32795
|
"package.json",
|
|
@@ -32139,7 +32799,7 @@ var SessionManager = class _SessionManager {
|
|
|
32139
32799
|
];
|
|
32140
32800
|
const hasProjectIndicator = projectIndicators.some((f) => {
|
|
32141
32801
|
try {
|
|
32142
|
-
return
|
|
32802
|
+
return fs21.existsSync(`${cwd}/${f}`);
|
|
32143
32803
|
} catch {
|
|
32144
32804
|
return false;
|
|
32145
32805
|
}
|
|
@@ -32342,6 +33002,14 @@ var SessionManager = class _SessionManager {
|
|
|
32342
33002
|
const filePath = input.file_path || input.notebook_path || input.path;
|
|
32343
33003
|
if (filePath && typeof filePath === "string") {
|
|
32344
33004
|
this.activeFiles.add(filePath);
|
|
33005
|
+
globalHotPathStore.recordPaths(
|
|
33006
|
+
{
|
|
33007
|
+
workspace_id: typeof this.context?.workspace_id === "string" ? this.context.workspace_id : void 0,
|
|
33008
|
+
project_id: typeof this.context?.project_id === "string" ? this.context.project_id : void 0
|
|
33009
|
+
},
|
|
33010
|
+
[filePath],
|
|
33011
|
+
"activity_focus"
|
|
33012
|
+
);
|
|
32345
33013
|
if (this.activeFiles.size > 30) {
|
|
32346
33014
|
const arr = Array.from(this.activeFiles);
|
|
32347
33015
|
this.activeFiles = new Set(arr.slice(-30));
|
|
@@ -32734,28 +33402,28 @@ async function runHttpGateway() {
|
|
|
32734
33402
|
|
|
32735
33403
|
// src/index.ts
|
|
32736
33404
|
init_version();
|
|
32737
|
-
import { existsSync as
|
|
32738
|
-
import { homedir as
|
|
32739
|
-
import { join as
|
|
33405
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
33406
|
+
import { homedir as homedir19 } from "os";
|
|
33407
|
+
import { join as join22 } from "path";
|
|
32740
33408
|
|
|
32741
33409
|
// src/setup.ts
|
|
32742
|
-
import * as
|
|
32743
|
-
import * as
|
|
32744
|
-
import { homedir as
|
|
33410
|
+
import * as fs8 from "node:fs/promises";
|
|
33411
|
+
import * as path9 from "node:path";
|
|
33412
|
+
import { homedir as homedir7 } from "node:os";
|
|
32745
33413
|
import { stdin, stdout } from "node:process";
|
|
32746
33414
|
import { createInterface } from "node:readline/promises";
|
|
32747
33415
|
init_rules_templates();
|
|
32748
33416
|
init_version();
|
|
32749
33417
|
|
|
32750
33418
|
// src/credentials.ts
|
|
32751
|
-
import * as
|
|
32752
|
-
import * as
|
|
32753
|
-
import { homedir as
|
|
33419
|
+
import * as fs7 from "node:fs/promises";
|
|
33420
|
+
import * as path8 from "node:path";
|
|
33421
|
+
import { homedir as homedir6 } from "node:os";
|
|
32754
33422
|
function normalizeApiUrl(input) {
|
|
32755
33423
|
return String(input ?? "").trim().replace(/\/+$/, "");
|
|
32756
33424
|
}
|
|
32757
33425
|
function credentialsFilePath() {
|
|
32758
|
-
return
|
|
33426
|
+
return path8.join(homedir6(), ".contextstream", "credentials.json");
|
|
32759
33427
|
}
|
|
32760
33428
|
function isRecord(value) {
|
|
32761
33429
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -32763,7 +33431,7 @@ function isRecord(value) {
|
|
|
32763
33431
|
async function readSavedCredentials() {
|
|
32764
33432
|
const filePath = credentialsFilePath();
|
|
32765
33433
|
try {
|
|
32766
|
-
const raw = await
|
|
33434
|
+
const raw = await fs7.readFile(filePath, "utf8");
|
|
32767
33435
|
const parsed = JSON.parse(raw);
|
|
32768
33436
|
if (!isRecord(parsed)) return null;
|
|
32769
33437
|
const version = parsed.version;
|
|
@@ -32789,7 +33457,7 @@ async function readSavedCredentials() {
|
|
|
32789
33457
|
}
|
|
32790
33458
|
async function writeSavedCredentials(input) {
|
|
32791
33459
|
const filePath = credentialsFilePath();
|
|
32792
|
-
await
|
|
33460
|
+
await fs7.mkdir(path8.dirname(filePath), { recursive: true });
|
|
32793
33461
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32794
33462
|
const existing = await readSavedCredentials();
|
|
32795
33463
|
const value = {
|
|
@@ -32801,9 +33469,9 @@ async function writeSavedCredentials(input) {
|
|
|
32801
33469
|
updated_at: now
|
|
32802
33470
|
};
|
|
32803
33471
|
const body = JSON.stringify(value, null, 2) + "\n";
|
|
32804
|
-
await
|
|
33472
|
+
await fs7.writeFile(filePath, body, { encoding: "utf8", mode: 384 });
|
|
32805
33473
|
try {
|
|
32806
|
-
await
|
|
33474
|
+
await fs7.chmod(filePath, 384);
|
|
32807
33475
|
} catch {
|
|
32808
33476
|
}
|
|
32809
33477
|
return { path: filePath, value };
|
|
@@ -32852,7 +33520,7 @@ function parseNumberList(input, max) {
|
|
|
32852
33520
|
}
|
|
32853
33521
|
async function fileExists(filePath) {
|
|
32854
33522
|
try {
|
|
32855
|
-
await
|
|
33523
|
+
await fs8.stat(filePath);
|
|
32856
33524
|
return true;
|
|
32857
33525
|
} catch {
|
|
32858
33526
|
return false;
|
|
@@ -33017,47 +33685,47 @@ function replaceContextStreamBlock2(existing, content) {
|
|
|
33017
33685
|
return { content: appended, status: "appended" };
|
|
33018
33686
|
}
|
|
33019
33687
|
async function upsertTextFile(filePath, content, _marker) {
|
|
33020
|
-
await
|
|
33688
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
33021
33689
|
const exists = await fileExists(filePath);
|
|
33022
33690
|
const wrappedContent = wrapWithMarkers2(content);
|
|
33023
33691
|
if (!exists) {
|
|
33024
|
-
await
|
|
33692
|
+
await fs8.writeFile(filePath, wrappedContent + "\n", "utf8");
|
|
33025
33693
|
return "created";
|
|
33026
33694
|
}
|
|
33027
|
-
const existing = await
|
|
33695
|
+
const existing = await fs8.readFile(filePath, "utf8").catch(() => "");
|
|
33028
33696
|
if (!existing.trim()) {
|
|
33029
|
-
await
|
|
33697
|
+
await fs8.writeFile(filePath, wrappedContent + "\n", "utf8");
|
|
33030
33698
|
return "updated";
|
|
33031
33699
|
}
|
|
33032
33700
|
const replaced = replaceContextStreamBlock2(existing, content);
|
|
33033
|
-
await
|
|
33701
|
+
await fs8.writeFile(filePath, replaced.content, "utf8");
|
|
33034
33702
|
return replaced.status;
|
|
33035
33703
|
}
|
|
33036
33704
|
function globalRulesPathForEditor(editor) {
|
|
33037
|
-
const home =
|
|
33705
|
+
const home = homedir7();
|
|
33038
33706
|
switch (editor) {
|
|
33039
33707
|
case "codex":
|
|
33040
|
-
return
|
|
33708
|
+
return path9.join(home, ".codex", "AGENTS.md");
|
|
33041
33709
|
case "copilot":
|
|
33042
33710
|
return null;
|
|
33043
33711
|
case "opencode":
|
|
33044
|
-
return
|
|
33712
|
+
return path9.join(home, ".opencode", "AGENTS.md");
|
|
33045
33713
|
case "claude":
|
|
33046
|
-
return
|
|
33714
|
+
return path9.join(home, ".claude", "CLAUDE.md");
|
|
33047
33715
|
case "cline":
|
|
33048
|
-
return
|
|
33716
|
+
return path9.join(home, "Documents", "Cline", "Rules", "contextstream.md");
|
|
33049
33717
|
case "kilo":
|
|
33050
|
-
return
|
|
33718
|
+
return path9.join(kiloConfigDir(), "rules", "contextstream.md");
|
|
33051
33719
|
case "roo":
|
|
33052
|
-
return
|
|
33720
|
+
return path9.join(home, ".roo", "rules", "contextstream.md");
|
|
33053
33721
|
case "aider":
|
|
33054
|
-
return
|
|
33722
|
+
return path9.join(home, ".aider.conf.yml");
|
|
33055
33723
|
case "antigravity":
|
|
33056
|
-
return
|
|
33724
|
+
return path9.join(home, ".gemini", "GEMINI.md");
|
|
33057
33725
|
case "cursor":
|
|
33058
33726
|
return null;
|
|
33059
33727
|
case "windsurf":
|
|
33060
|
-
return
|
|
33728
|
+
return path9.join(home, ".codeium", "windsurf", "memories", "global_rules.md");
|
|
33061
33729
|
default:
|
|
33062
33730
|
return null;
|
|
33063
33731
|
}
|
|
@@ -33069,100 +33737,104 @@ async function anyPathExists(paths) {
|
|
|
33069
33737
|
return false;
|
|
33070
33738
|
}
|
|
33071
33739
|
async function isCodexInstalled() {
|
|
33072
|
-
const home =
|
|
33740
|
+
const home = homedir7();
|
|
33073
33741
|
const envHome = process.env.CODEX_HOME;
|
|
33074
33742
|
const candidates = [
|
|
33075
33743
|
envHome,
|
|
33076
|
-
|
|
33077
|
-
|
|
33078
|
-
|
|
33744
|
+
path9.join(home, ".codex"),
|
|
33745
|
+
path9.join(home, ".codex", "config.toml"),
|
|
33746
|
+
path9.join(home, ".config", "codex")
|
|
33079
33747
|
].filter((candidate) => Boolean(candidate));
|
|
33080
33748
|
return anyPathExists(candidates);
|
|
33081
33749
|
}
|
|
33082
33750
|
function openCodeConfigPath() {
|
|
33083
|
-
const home =
|
|
33751
|
+
const home = homedir7();
|
|
33084
33752
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
33085
33753
|
if (process.platform === "win32") {
|
|
33086
|
-
const appData = process.env.APPDATA ||
|
|
33087
|
-
return
|
|
33754
|
+
const appData = process.env.APPDATA || path9.join(home, "AppData", "Roaming");
|
|
33755
|
+
return path9.join(appData, "opencode", "opencode.json");
|
|
33088
33756
|
}
|
|
33089
|
-
const configRoot = xdgConfigHome ||
|
|
33090
|
-
return
|
|
33757
|
+
const configRoot = xdgConfigHome || path9.join(home, ".config");
|
|
33758
|
+
return path9.join(configRoot, "opencode", "opencode.json");
|
|
33091
33759
|
}
|
|
33092
33760
|
async function isOpenCodeInstalled() {
|
|
33093
33761
|
const configPath = openCodeConfigPath();
|
|
33094
|
-
const configDir =
|
|
33095
|
-
const home =
|
|
33762
|
+
const configDir = path9.dirname(configPath);
|
|
33763
|
+
const home = homedir7();
|
|
33096
33764
|
const candidates = [
|
|
33097
33765
|
configDir,
|
|
33098
33766
|
configPath,
|
|
33099
|
-
|
|
33100
|
-
|
|
33767
|
+
path9.join(home, ".bun", "bin", "opencode"),
|
|
33768
|
+
path9.join(home, ".local", "bin", "opencode")
|
|
33101
33769
|
];
|
|
33102
33770
|
return anyPathExists(candidates);
|
|
33103
33771
|
}
|
|
33104
33772
|
async function isClaudeInstalled() {
|
|
33105
|
-
const home =
|
|
33106
|
-
const candidates = [
|
|
33773
|
+
const home = homedir7();
|
|
33774
|
+
const candidates = [path9.join(home, ".claude"), path9.join(home, ".config", "claude")];
|
|
33107
33775
|
const desktopConfig = claudeDesktopConfigPath();
|
|
33108
33776
|
if (desktopConfig) candidates.push(desktopConfig);
|
|
33109
33777
|
if (process.platform === "darwin") {
|
|
33110
|
-
candidates.push(
|
|
33778
|
+
candidates.push(path9.join(home, "Library", "Application Support", "Claude"));
|
|
33111
33779
|
} else if (process.platform === "win32") {
|
|
33112
33780
|
const appData = process.env.APPDATA;
|
|
33113
|
-
if (appData) candidates.push(
|
|
33781
|
+
if (appData) candidates.push(path9.join(appData, "Claude"));
|
|
33114
33782
|
}
|
|
33115
33783
|
return anyPathExists(candidates);
|
|
33116
33784
|
}
|
|
33117
33785
|
async function isClineInstalled() {
|
|
33118
|
-
const home =
|
|
33786
|
+
const home = homedir7();
|
|
33119
33787
|
const candidates = [
|
|
33120
|
-
|
|
33121
|
-
|
|
33122
|
-
|
|
33788
|
+
path9.join(home, "Documents", "Cline"),
|
|
33789
|
+
path9.join(home, ".cline"),
|
|
33790
|
+
path9.join(home, ".config", "cline")
|
|
33123
33791
|
];
|
|
33124
33792
|
return anyPathExists(candidates);
|
|
33125
33793
|
}
|
|
33126
33794
|
async function isCopilotInstalled() {
|
|
33127
|
-
const home =
|
|
33795
|
+
const home = homedir7();
|
|
33128
33796
|
const candidates = [
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33797
|
+
path9.join(home, ".copilot"),
|
|
33798
|
+
path9.join(home, ".config", "github-copilot"),
|
|
33799
|
+
path9.join(home, ".vscode", "extensions", "github.copilot-chat"),
|
|
33800
|
+
path9.join(home, ".vscode", "extensions", "github.copilot")
|
|
33133
33801
|
];
|
|
33134
33802
|
return anyPathExists(candidates);
|
|
33135
33803
|
}
|
|
33136
33804
|
async function isKiloInstalled() {
|
|
33137
|
-
const home =
|
|
33138
|
-
const candidates = [
|
|
33805
|
+
const home = homedir7();
|
|
33806
|
+
const candidates = [
|
|
33807
|
+
kiloConfigDir(),
|
|
33808
|
+
path9.join(home, ".kilocode"),
|
|
33809
|
+
path9.join(home, ".config", "kilocode")
|
|
33810
|
+
];
|
|
33139
33811
|
return anyPathExists(candidates);
|
|
33140
33812
|
}
|
|
33141
33813
|
async function isRooInstalled() {
|
|
33142
|
-
const home =
|
|
33143
|
-
const candidates = [
|
|
33814
|
+
const home = homedir7();
|
|
33815
|
+
const candidates = [path9.join(home, ".roo"), path9.join(home, ".config", "roo")];
|
|
33144
33816
|
return anyPathExists(candidates);
|
|
33145
33817
|
}
|
|
33146
33818
|
async function isAiderInstalled() {
|
|
33147
|
-
const home =
|
|
33148
|
-
const candidates = [
|
|
33819
|
+
const home = homedir7();
|
|
33820
|
+
const candidates = [path9.join(home, ".aider.conf.yml"), path9.join(home, ".config", "aider")];
|
|
33149
33821
|
return anyPathExists(candidates);
|
|
33150
33822
|
}
|
|
33151
33823
|
async function isCursorInstalled() {
|
|
33152
|
-
const home =
|
|
33153
|
-
const candidates = [
|
|
33824
|
+
const home = homedir7();
|
|
33825
|
+
const candidates = [path9.join(home, ".cursor")];
|
|
33154
33826
|
if (process.platform === "darwin") {
|
|
33155
33827
|
candidates.push("/Applications/Cursor.app");
|
|
33156
|
-
candidates.push(
|
|
33157
|
-
candidates.push(
|
|
33828
|
+
candidates.push(path9.join(home, "Applications", "Cursor.app"));
|
|
33829
|
+
candidates.push(path9.join(home, "Library", "Application Support", "Cursor"));
|
|
33158
33830
|
} else if (process.platform === "win32") {
|
|
33159
33831
|
const localApp = process.env.LOCALAPPDATA;
|
|
33160
33832
|
const programFiles = process.env.ProgramFiles;
|
|
33161
33833
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
33162
|
-
if (localApp) candidates.push(
|
|
33163
|
-
if (localApp) candidates.push(
|
|
33164
|
-
if (programFiles) candidates.push(
|
|
33165
|
-
if (programFilesX86) candidates.push(
|
|
33834
|
+
if (localApp) candidates.push(path9.join(localApp, "Programs", "Cursor", "Cursor.exe"));
|
|
33835
|
+
if (localApp) candidates.push(path9.join(localApp, "Cursor", "Cursor.exe"));
|
|
33836
|
+
if (programFiles) candidates.push(path9.join(programFiles, "Cursor", "Cursor.exe"));
|
|
33837
|
+
if (programFilesX86) candidates.push(path9.join(programFilesX86, "Cursor", "Cursor.exe"));
|
|
33166
33838
|
} else {
|
|
33167
33839
|
candidates.push("/usr/bin/cursor");
|
|
33168
33840
|
candidates.push("/usr/local/bin/cursor");
|
|
@@ -33172,20 +33844,20 @@ async function isCursorInstalled() {
|
|
|
33172
33844
|
return anyPathExists(candidates);
|
|
33173
33845
|
}
|
|
33174
33846
|
async function isWindsurfInstalled() {
|
|
33175
|
-
const home =
|
|
33176
|
-
const candidates = [
|
|
33847
|
+
const home = homedir7();
|
|
33848
|
+
const candidates = [path9.join(home, ".codeium", "windsurf")];
|
|
33177
33849
|
if (process.platform === "darwin") {
|
|
33178
33850
|
candidates.push("/Applications/Windsurf.app");
|
|
33179
|
-
candidates.push(
|
|
33180
|
-
candidates.push(
|
|
33851
|
+
candidates.push(path9.join(home, "Applications", "Windsurf.app"));
|
|
33852
|
+
candidates.push(path9.join(home, "Library", "Application Support", "Windsurf"));
|
|
33181
33853
|
} else if (process.platform === "win32") {
|
|
33182
33854
|
const localApp = process.env.LOCALAPPDATA;
|
|
33183
33855
|
const programFiles = process.env.ProgramFiles;
|
|
33184
33856
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
33185
|
-
if (localApp) candidates.push(
|
|
33186
|
-
if (localApp) candidates.push(
|
|
33187
|
-
if (programFiles) candidates.push(
|
|
33188
|
-
if (programFilesX86) candidates.push(
|
|
33857
|
+
if (localApp) candidates.push(path9.join(localApp, "Programs", "Windsurf", "Windsurf.exe"));
|
|
33858
|
+
if (localApp) candidates.push(path9.join(localApp, "Windsurf", "Windsurf.exe"));
|
|
33859
|
+
if (programFiles) candidates.push(path9.join(programFiles, "Windsurf", "Windsurf.exe"));
|
|
33860
|
+
if (programFilesX86) candidates.push(path9.join(programFilesX86, "Windsurf", "Windsurf.exe"));
|
|
33189
33861
|
} else {
|
|
33190
33862
|
candidates.push("/usr/bin/windsurf");
|
|
33191
33863
|
candidates.push("/usr/local/bin/windsurf");
|
|
@@ -33195,22 +33867,22 @@ async function isWindsurfInstalled() {
|
|
|
33195
33867
|
return anyPathExists(candidates);
|
|
33196
33868
|
}
|
|
33197
33869
|
async function isAntigravityInstalled() {
|
|
33198
|
-
const home =
|
|
33199
|
-
const candidates = [
|
|
33870
|
+
const home = homedir7();
|
|
33871
|
+
const candidates = [path9.join(home, ".gemini")];
|
|
33200
33872
|
if (process.platform === "darwin") {
|
|
33201
33873
|
candidates.push("/Applications/Antigravity.app");
|
|
33202
|
-
candidates.push(
|
|
33203
|
-
candidates.push(
|
|
33874
|
+
candidates.push(path9.join(home, "Applications", "Antigravity.app"));
|
|
33875
|
+
candidates.push(path9.join(home, "Library", "Application Support", "Antigravity"));
|
|
33204
33876
|
} else if (process.platform === "win32") {
|
|
33205
33877
|
const localApp = process.env.LOCALAPPDATA;
|
|
33206
33878
|
const programFiles = process.env.ProgramFiles;
|
|
33207
33879
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
33208
33880
|
if (localApp)
|
|
33209
|
-
candidates.push(
|
|
33210
|
-
if (localApp) candidates.push(
|
|
33211
|
-
if (programFiles) candidates.push(
|
|
33881
|
+
candidates.push(path9.join(localApp, "Programs", "Antigravity", "Antigravity.exe"));
|
|
33882
|
+
if (localApp) candidates.push(path9.join(localApp, "Antigravity", "Antigravity.exe"));
|
|
33883
|
+
if (programFiles) candidates.push(path9.join(programFiles, "Antigravity", "Antigravity.exe"));
|
|
33212
33884
|
if (programFilesX86)
|
|
33213
|
-
candidates.push(
|
|
33885
|
+
candidates.push(path9.join(programFilesX86, "Antigravity", "Antigravity.exe"));
|
|
33214
33886
|
} else {
|
|
33215
33887
|
candidates.push("/usr/bin/antigravity");
|
|
33216
33888
|
candidates.push("/usr/local/bin/antigravity");
|
|
@@ -33249,7 +33921,21 @@ async function isEditorInstalled(editor) {
|
|
|
33249
33921
|
}
|
|
33250
33922
|
var IS_WINDOWS = process.platform === "win32";
|
|
33251
33923
|
var DEFAULT_CONTEXTSTREAM_API_URL = "https://api.contextstream.io";
|
|
33924
|
+
var DEFAULT_CONTEXTSTREAM_MCP_URL = "https://mcp.contextstream.io/mcp?default_context_mode=fast";
|
|
33925
|
+
var DEFAULT_CONTEXTSTREAM_TOOLSET = "complete";
|
|
33926
|
+
var DEFAULT_CONTEXTSTREAM_OUTPUT_FORMAT = "compact";
|
|
33927
|
+
var DEFAULT_CONTEXTSTREAM_SEARCH_LIMIT = "15";
|
|
33928
|
+
var DEFAULT_CONTEXTSTREAM_SEARCH_MAX_CHARS = "2400";
|
|
33929
|
+
var DEFAULT_CONTEXTSTREAM_TRANSCRIPTS_ENABLED = "true";
|
|
33930
|
+
var DEFAULT_CONTEXTSTREAM_HOOK_TRANSCRIPTS_ENABLED = "true";
|
|
33931
|
+
var VSCODE_MCP_MODE_ENV = "CONTEXTSTREAM_VSCODE_MCP_MODE";
|
|
33252
33932
|
var OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
33933
|
+
var REMOTE_HEADER_TOOLSET = "X-ContextStream-Toolset";
|
|
33934
|
+
var REMOTE_HEADER_OUTPUT_FORMAT = "X-ContextStream-Output-Format";
|
|
33935
|
+
var REMOTE_HEADER_SEARCH_LIMIT = "X-ContextStream-Search-Limit";
|
|
33936
|
+
var REMOTE_HEADER_SEARCH_MAX_CHARS = "X-ContextStream-Search-Max-Chars";
|
|
33937
|
+
var REMOTE_HEADER_TRANSCRIPTS_ENABLED = "X-ContextStream-Transcripts-Enabled";
|
|
33938
|
+
var REMOTE_HEADER_CONSOLIDATED = "X-ContextStream-Consolidated";
|
|
33253
33939
|
function escapeTomlString(value) {
|
|
33254
33940
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
33255
33941
|
}
|
|
@@ -33258,11 +33944,31 @@ function formatTomlEnvLines(env) {
|
|
|
33258
33944
|
}
|
|
33259
33945
|
function buildSetupEnv(params) {
|
|
33260
33946
|
const contextPack = params.contextPackEnabled === false ? "false" : "true";
|
|
33261
|
-
|
|
33947
|
+
const env = {
|
|
33262
33948
|
CONTEXTSTREAM_API_URL: params.apiUrl,
|
|
33263
|
-
|
|
33264
|
-
|
|
33949
|
+
CONTEXTSTREAM_ALLOW_HEADER_AUTH: "false",
|
|
33950
|
+
CONTEXTSTREAM_WORKSPACE_ID: params.workspaceId ?? "",
|
|
33951
|
+
CONTEXTSTREAM_PROJECT_ID: params.projectId ?? "",
|
|
33952
|
+
CONTEXTSTREAM_USER_AGENT: `contextstream-mcp/${VERSION}`,
|
|
33953
|
+
CONTEXTSTREAM_TOOLSET: DEFAULT_CONTEXTSTREAM_TOOLSET,
|
|
33954
|
+
CONTEXTSTREAM_LOG_LEVEL: "quiet",
|
|
33955
|
+
CONTEXTSTREAM_OUTPUT_FORMAT: DEFAULT_CONTEXTSTREAM_OUTPUT_FORMAT,
|
|
33956
|
+
CONTEXTSTREAM_CONTEXT_PACK: contextPack,
|
|
33957
|
+
CONTEXTSTREAM_TRANSCRIPTS_ENABLED: DEFAULT_CONTEXTSTREAM_TRANSCRIPTS_ENABLED,
|
|
33958
|
+
CONTEXTSTREAM_HOOK_TRANSCRIPTS_ENABLED: DEFAULT_CONTEXTSTREAM_HOOK_TRANSCRIPTS_ENABLED,
|
|
33959
|
+
CONTEXTSTREAM_SHOW_TIMING: "false",
|
|
33960
|
+
CONTEXTSTREAM_PROGRESSIVE_MODE: "false",
|
|
33961
|
+
CONTEXTSTREAM_ROUTER_MODE: "false",
|
|
33962
|
+
CONTEXTSTREAM_CONSOLIDATED: "true",
|
|
33963
|
+
CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS: "true",
|
|
33964
|
+
CONTEXTSTREAM_SEARCH_LIMIT: DEFAULT_CONTEXTSTREAM_SEARCH_LIMIT,
|
|
33965
|
+
CONTEXTSTREAM_SEARCH_MAX_CHARS: DEFAULT_CONTEXTSTREAM_SEARCH_MAX_CHARS,
|
|
33966
|
+
CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT: "true"
|
|
33265
33967
|
};
|
|
33968
|
+
if (params.apiKey) {
|
|
33969
|
+
env.CONTEXTSTREAM_API_KEY = params.apiKey;
|
|
33970
|
+
}
|
|
33971
|
+
return env;
|
|
33266
33972
|
}
|
|
33267
33973
|
function buildContextStreamMcpServer(params) {
|
|
33268
33974
|
const env = buildSetupEnv(params);
|
|
@@ -33279,7 +33985,45 @@ function buildContextStreamMcpServer(params) {
|
|
|
33279
33985
|
env
|
|
33280
33986
|
};
|
|
33281
33987
|
}
|
|
33988
|
+
function resolveVsCodeMcpMode() {
|
|
33989
|
+
const rawValue = process.env[VSCODE_MCP_MODE_ENV]?.trim().toLowerCase();
|
|
33990
|
+
if (rawValue === "remote" || rawValue === "local") {
|
|
33991
|
+
return rawValue;
|
|
33992
|
+
}
|
|
33993
|
+
return "auto";
|
|
33994
|
+
}
|
|
33995
|
+
function getHostedRemoteMcpUrl() {
|
|
33996
|
+
const override = process.env.CONTEXTSTREAM_MCP_HTTP_URL?.trim();
|
|
33997
|
+
return override || DEFAULT_CONTEXTSTREAM_MCP_URL;
|
|
33998
|
+
}
|
|
33999
|
+
function buildHostedRemoteHeaders() {
|
|
34000
|
+
return {
|
|
34001
|
+
[REMOTE_HEADER_TOOLSET]: DEFAULT_CONTEXTSTREAM_TOOLSET,
|
|
34002
|
+
[REMOTE_HEADER_OUTPUT_FORMAT]: DEFAULT_CONTEXTSTREAM_OUTPUT_FORMAT,
|
|
34003
|
+
[REMOTE_HEADER_SEARCH_LIMIT]: DEFAULT_CONTEXTSTREAM_SEARCH_LIMIT,
|
|
34004
|
+
[REMOTE_HEADER_SEARCH_MAX_CHARS]: DEFAULT_CONTEXTSTREAM_SEARCH_MAX_CHARS,
|
|
34005
|
+
[REMOTE_HEADER_TRANSCRIPTS_ENABLED]: DEFAULT_CONTEXTSTREAM_TRANSCRIPTS_ENABLED,
|
|
34006
|
+
[REMOTE_HEADER_CONSOLIDATED]: "true"
|
|
34007
|
+
};
|
|
34008
|
+
}
|
|
34009
|
+
function shouldUseHostedRemoteMcp(apiUrl) {
|
|
34010
|
+
const mode = resolveVsCodeMcpMode();
|
|
34011
|
+
if (mode === "remote") {
|
|
34012
|
+
return true;
|
|
34013
|
+
}
|
|
34014
|
+
if (mode === "local") {
|
|
34015
|
+
return false;
|
|
34016
|
+
}
|
|
34017
|
+
return normalizeApiUrl(apiUrl) === DEFAULT_CONTEXTSTREAM_API_URL;
|
|
34018
|
+
}
|
|
33282
34019
|
function buildContextStreamVsCodeServer(params) {
|
|
34020
|
+
if (shouldUseHostedRemoteMcp(params.apiUrl)) {
|
|
34021
|
+
return {
|
|
34022
|
+
type: "http",
|
|
34023
|
+
url: getHostedRemoteMcpUrl(),
|
|
34024
|
+
headers: buildHostedRemoteHeaders()
|
|
34025
|
+
};
|
|
34026
|
+
}
|
|
33283
34027
|
const env = buildSetupEnv(params);
|
|
33284
34028
|
if (IS_WINDOWS) {
|
|
33285
34029
|
return {
|
|
@@ -33298,7 +34042,17 @@ function buildContextStreamVsCodeServer(params) {
|
|
|
33298
34042
|
}
|
|
33299
34043
|
function buildContextStreamOpenCodeEnvironment(params) {
|
|
33300
34044
|
const environment = {
|
|
33301
|
-
CONTEXTSTREAM_API_KEY: "{env:CONTEXTSTREAM_API_KEY}"
|
|
34045
|
+
CONTEXTSTREAM_API_KEY: "{env:CONTEXTSTREAM_API_KEY}",
|
|
34046
|
+
CONTEXTSTREAM_TOOLSET: DEFAULT_CONTEXTSTREAM_TOOLSET,
|
|
34047
|
+
CONTEXTSTREAM_LOG_LEVEL: "quiet",
|
|
34048
|
+
CONTEXTSTREAM_OUTPUT_FORMAT: DEFAULT_CONTEXTSTREAM_OUTPUT_FORMAT,
|
|
34049
|
+
CONTEXTSTREAM_TRANSCRIPTS_ENABLED: DEFAULT_CONTEXTSTREAM_TRANSCRIPTS_ENABLED,
|
|
34050
|
+
CONTEXTSTREAM_HOOK_TRANSCRIPTS_ENABLED: DEFAULT_CONTEXTSTREAM_HOOK_TRANSCRIPTS_ENABLED,
|
|
34051
|
+
CONTEXTSTREAM_CONSOLIDATED: "true",
|
|
34052
|
+
CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS: "true",
|
|
34053
|
+
CONTEXTSTREAM_SEARCH_LIMIT: DEFAULT_CONTEXTSTREAM_SEARCH_LIMIT,
|
|
34054
|
+
CONTEXTSTREAM_SEARCH_MAX_CHARS: DEFAULT_CONTEXTSTREAM_SEARCH_MAX_CHARS,
|
|
34055
|
+
CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT: "true"
|
|
33302
34056
|
};
|
|
33303
34057
|
if (normalizeApiUrl(params.apiUrl) !== DEFAULT_CONTEXTSTREAM_API_URL) {
|
|
33304
34058
|
environment.CONTEXTSTREAM_API_URL = params.apiUrl;
|
|
@@ -33316,6 +34070,46 @@ function buildContextStreamOpenCodeLocalServer(params) {
|
|
|
33316
34070
|
enabled: true
|
|
33317
34071
|
};
|
|
33318
34072
|
}
|
|
34073
|
+
function kiloConfigDir() {
|
|
34074
|
+
const home = homedir7();
|
|
34075
|
+
if (process.platform === "win32") {
|
|
34076
|
+
const appData = process.env.APPDATA || path9.join(home, "AppData", "Roaming");
|
|
34077
|
+
return path9.join(appData, "kilo");
|
|
34078
|
+
}
|
|
34079
|
+
return path9.join(home, ".config", "kilo");
|
|
34080
|
+
}
|
|
34081
|
+
function buildContextStreamKiloServer(params) {
|
|
34082
|
+
const env = buildSetupEnv(params);
|
|
34083
|
+
delete env.CONTEXTSTREAM_API_KEY;
|
|
34084
|
+
return {
|
|
34085
|
+
type: "local",
|
|
34086
|
+
command: ["npx", "-y", "@contextstream/mcp-server@latest"],
|
|
34087
|
+
environment: env,
|
|
34088
|
+
enabled: true
|
|
34089
|
+
};
|
|
34090
|
+
}
|
|
34091
|
+
async function upsertKiloMcpConfig(filePath, server) {
|
|
34092
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
34093
|
+
const exists = await fileExists(filePath);
|
|
34094
|
+
let root = {};
|
|
34095
|
+
if (exists) {
|
|
34096
|
+
const raw = await fs8.readFile(filePath, "utf8").catch(() => "");
|
|
34097
|
+
const parsed = tryParseJsonLike(raw);
|
|
34098
|
+
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
34099
|
+
root = parsed.value;
|
|
34100
|
+
}
|
|
34101
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) root = {};
|
|
34102
|
+
if (!root.mcp || typeof root.mcp !== "object" || Array.isArray(root.mcp)) root.mcp = {};
|
|
34103
|
+
if (!root.instructions) {
|
|
34104
|
+
root.instructions = [".kilo/rules/*.md"];
|
|
34105
|
+
}
|
|
34106
|
+
const before = JSON.stringify(root.mcp.contextstream ?? null);
|
|
34107
|
+
root.mcp.contextstream = server;
|
|
34108
|
+
const after = JSON.stringify(root.mcp.contextstream ?? null);
|
|
34109
|
+
await fs8.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
34110
|
+
if (!exists) return "created";
|
|
34111
|
+
return before === after ? "skipped" : "updated";
|
|
34112
|
+
}
|
|
33319
34113
|
function stripJsonComments(input) {
|
|
33320
34114
|
return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
33321
34115
|
}
|
|
@@ -33335,11 +34129,11 @@ function tryParseJsonLike(raw) {
|
|
|
33335
34129
|
}
|
|
33336
34130
|
}
|
|
33337
34131
|
async function upsertJsonMcpConfig(filePath, server) {
|
|
33338
|
-
await
|
|
34132
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
33339
34133
|
const exists = await fileExists(filePath);
|
|
33340
34134
|
let root = {};
|
|
33341
34135
|
if (exists) {
|
|
33342
|
-
const raw = await
|
|
34136
|
+
const raw = await fs8.readFile(filePath, "utf8").catch(() => "");
|
|
33343
34137
|
const parsed = tryParseJsonLike(raw);
|
|
33344
34138
|
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
33345
34139
|
root = parsed.value;
|
|
@@ -33350,16 +34144,16 @@ async function upsertJsonMcpConfig(filePath, server) {
|
|
|
33350
34144
|
const before = JSON.stringify(root.mcpServers.contextstream ?? null);
|
|
33351
34145
|
root.mcpServers.contextstream = server;
|
|
33352
34146
|
const after = JSON.stringify(root.mcpServers.contextstream ?? null);
|
|
33353
|
-
await
|
|
34147
|
+
await fs8.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
33354
34148
|
if (!exists) return "created";
|
|
33355
34149
|
return before === after ? "skipped" : "updated";
|
|
33356
34150
|
}
|
|
33357
34151
|
async function upsertJsonVsCodeMcpConfig(filePath, server) {
|
|
33358
|
-
await
|
|
34152
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
33359
34153
|
const exists = await fileExists(filePath);
|
|
33360
34154
|
let root = {};
|
|
33361
34155
|
if (exists) {
|
|
33362
|
-
const raw = await
|
|
34156
|
+
const raw = await fs8.readFile(filePath, "utf8").catch(() => "");
|
|
33363
34157
|
const parsed = tryParseJsonLike(raw);
|
|
33364
34158
|
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
33365
34159
|
root = parsed.value;
|
|
@@ -33368,18 +34162,29 @@ async function upsertJsonVsCodeMcpConfig(filePath, server) {
|
|
|
33368
34162
|
if (!root.servers || typeof root.servers !== "object" || Array.isArray(root.servers))
|
|
33369
34163
|
root.servers = {};
|
|
33370
34164
|
const before = JSON.stringify(root.servers.contextstream ?? null);
|
|
33371
|
-
root.servers.contextstream
|
|
34165
|
+
const existingServer = root.servers.contextstream;
|
|
34166
|
+
if (existingServer && typeof existingServer === "object" && !Array.isArray(existingServer) && existingServer.type === "http" && server.type === "http") {
|
|
34167
|
+
root.servers.contextstream = {
|
|
34168
|
+
...server,
|
|
34169
|
+
headers: {
|
|
34170
|
+
...server.headers,
|
|
34171
|
+
...existingServer.headers && typeof existingServer.headers === "object" ? existingServer.headers : {}
|
|
34172
|
+
}
|
|
34173
|
+
};
|
|
34174
|
+
} else {
|
|
34175
|
+
root.servers.contextstream = server;
|
|
34176
|
+
}
|
|
33372
34177
|
const after = JSON.stringify(root.servers.contextstream ?? null);
|
|
33373
|
-
await
|
|
34178
|
+
await fs8.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
33374
34179
|
if (!exists) return "created";
|
|
33375
34180
|
return before === after ? "skipped" : "updated";
|
|
33376
34181
|
}
|
|
33377
34182
|
async function upsertOpenCodeMcpConfig(filePath, server) {
|
|
33378
|
-
await
|
|
34183
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
33379
34184
|
const exists = await fileExists(filePath);
|
|
33380
34185
|
let root = {};
|
|
33381
34186
|
if (exists) {
|
|
33382
|
-
const raw = await
|
|
34187
|
+
const raw = await fs8.readFile(filePath, "utf8").catch(() => "");
|
|
33383
34188
|
const parsed = tryParseJsonLike(raw);
|
|
33384
34189
|
if (!parsed.ok) throw new Error(`Invalid JSON in ${filePath}: ${parsed.error}`);
|
|
33385
34190
|
root = parsed.value;
|
|
@@ -33396,14 +34201,14 @@ async function upsertOpenCodeMcpConfig(filePath, server) {
|
|
|
33396
34201
|
schema: root.$schema ?? null,
|
|
33397
34202
|
contextstream: root.mcp.contextstream ?? null
|
|
33398
34203
|
});
|
|
33399
|
-
await
|
|
34204
|
+
await fs8.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
|
|
33400
34205
|
if (!exists) return "created";
|
|
33401
34206
|
return before === after ? "skipped" : "updated";
|
|
33402
34207
|
}
|
|
33403
34208
|
function claudeDesktopConfigPath() {
|
|
33404
|
-
const home =
|
|
34209
|
+
const home = homedir7();
|
|
33405
34210
|
if (process.platform === "darwin") {
|
|
33406
|
-
return
|
|
34211
|
+
return path9.join(
|
|
33407
34212
|
home,
|
|
33408
34213
|
"Library",
|
|
33409
34214
|
"Application Support",
|
|
@@ -33412,18 +34217,18 @@ function claudeDesktopConfigPath() {
|
|
|
33412
34217
|
);
|
|
33413
34218
|
}
|
|
33414
34219
|
if (process.platform === "win32") {
|
|
33415
|
-
const appData = process.env.APPDATA ||
|
|
33416
|
-
return
|
|
34220
|
+
const appData = process.env.APPDATA || path9.join(home, "AppData", "Roaming");
|
|
34221
|
+
return path9.join(appData, "Claude", "claude_desktop_config.json");
|
|
33417
34222
|
}
|
|
33418
34223
|
if (process.platform === "linux") {
|
|
33419
|
-
return
|
|
34224
|
+
return path9.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
33420
34225
|
}
|
|
33421
34226
|
return null;
|
|
33422
34227
|
}
|
|
33423
34228
|
async function upsertCodexTomlConfig(filePath, params) {
|
|
33424
|
-
await
|
|
34229
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
33425
34230
|
const exists = await fileExists(filePath);
|
|
33426
|
-
const existing = exists ? await
|
|
34231
|
+
const existing = exists ? await fs8.readFile(filePath, "utf8").catch(() => "") : "";
|
|
33427
34232
|
const env = buildSetupEnv(params);
|
|
33428
34233
|
const envLines = formatTomlEnvLines(env);
|
|
33429
34234
|
const marker = "[mcp_servers.contextstream]";
|
|
@@ -33441,15 +34246,15 @@ args = ["--prefer-online", "-y", "@contextstream/mcp-server@latest"]
|
|
|
33441
34246
|
[mcp_servers.contextstream.env]
|
|
33442
34247
|
` + envLines + "\n";
|
|
33443
34248
|
if (!exists) {
|
|
33444
|
-
await
|
|
34249
|
+
await fs8.writeFile(filePath, block.trimStart(), "utf8");
|
|
33445
34250
|
return "created";
|
|
33446
34251
|
}
|
|
33447
34252
|
if (!existing.includes(marker)) {
|
|
33448
|
-
await
|
|
34253
|
+
await fs8.writeFile(filePath, existing.trimEnd() + block, "utf8");
|
|
33449
34254
|
return "updated";
|
|
33450
34255
|
}
|
|
33451
34256
|
if (!existing.includes(envMarker)) {
|
|
33452
|
-
await
|
|
34257
|
+
await fs8.writeFile(
|
|
33453
34258
|
filePath,
|
|
33454
34259
|
existing.trimEnd() + "\n\n" + envMarker + "\n" + envLines + "\n",
|
|
33455
34260
|
"utf8"
|
|
@@ -33499,18 +34304,18 @@ args = ["--prefer-online", "-y", "@contextstream/mcp-server@latest"]
|
|
|
33499
34304
|
}
|
|
33500
34305
|
const updated = out.join("\n");
|
|
33501
34306
|
if (updated === existing) return "skipped";
|
|
33502
|
-
await
|
|
34307
|
+
await fs8.writeFile(filePath, updated, "utf8");
|
|
33503
34308
|
return "updated";
|
|
33504
34309
|
}
|
|
33505
34310
|
async function discoverProjectsUnderFolder(parentFolder) {
|
|
33506
|
-
const entries = await
|
|
33507
|
-
const candidates = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) =>
|
|
34311
|
+
const entries = await fs8.readdir(parentFolder, { withFileTypes: true });
|
|
34312
|
+
const candidates = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => path9.join(parentFolder, e.name));
|
|
33508
34313
|
const projects = [];
|
|
33509
34314
|
for (const dir of candidates) {
|
|
33510
|
-
const hasGit = await fileExists(
|
|
33511
|
-
const hasPkg = await fileExists(
|
|
33512
|
-
const hasCargo = await fileExists(
|
|
33513
|
-
const hasPyProject = await fileExists(
|
|
34315
|
+
const hasGit = await fileExists(path9.join(dir, ".git"));
|
|
34316
|
+
const hasPkg = await fileExists(path9.join(dir, "package.json"));
|
|
34317
|
+
const hasCargo = await fileExists(path9.join(dir, "Cargo.toml"));
|
|
34318
|
+
const hasPyProject = await fileExists(path9.join(dir, "pyproject.toml"));
|
|
33514
34319
|
if (hasGit || hasPkg || hasCargo || hasPyProject) projects.push(dir);
|
|
33515
34320
|
}
|
|
33516
34321
|
return projects;
|
|
@@ -33555,7 +34360,7 @@ async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun
|
|
|
33555
34360
|
if (!workspaceId || workspaceId === "dry-run") {
|
|
33556
34361
|
return void 0;
|
|
33557
34362
|
}
|
|
33558
|
-
const folderName =
|
|
34363
|
+
const folderName = path9.basename(cwd) || "project";
|
|
33559
34364
|
let projects = [];
|
|
33560
34365
|
try {
|
|
33561
34366
|
const result = await client.listProjects({
|
|
@@ -33631,7 +34436,7 @@ async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun
|
|
|
33631
34436
|
return void 0;
|
|
33632
34437
|
}
|
|
33633
34438
|
async function indexProjectWithProgress(client, projectPath, workspaceId) {
|
|
33634
|
-
const projectName =
|
|
34439
|
+
const projectName = path9.basename(projectPath);
|
|
33635
34440
|
console.log(`
|
|
33636
34441
|
${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
33637
34442
|
console.log(`${colors.dim}${projectPath}${colors.reset}
|
|
@@ -34155,14 +34960,16 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34155
34960
|
"\nCopilot canonical pair disabled for this run. You can rerun setup anytime to generate both files together."
|
|
34156
34961
|
);
|
|
34157
34962
|
}
|
|
34158
|
-
const
|
|
34159
|
-
const mcpServerClaude = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
|
|
34160
|
-
const mcpServerOpenCode = buildContextStreamOpenCodeLocalServer({
|
|
34963
|
+
const setupEnvBase = {
|
|
34161
34964
|
apiUrl,
|
|
34162
34965
|
apiKey,
|
|
34163
|
-
contextPackEnabled
|
|
34164
|
-
|
|
34165
|
-
|
|
34966
|
+
contextPackEnabled,
|
|
34967
|
+
workspaceId: workspaceId && workspaceId !== "dry-run" ? workspaceId : void 0
|
|
34968
|
+
};
|
|
34969
|
+
const mcpServer = buildContextStreamMcpServer(setupEnvBase);
|
|
34970
|
+
const mcpServerClaude = buildContextStreamMcpServer(setupEnvBase);
|
|
34971
|
+
const mcpServerOpenCode = buildContextStreamOpenCodeLocalServer(setupEnvBase);
|
|
34972
|
+
const vsCodeServer = buildContextStreamVsCodeServer(setupEnvBase);
|
|
34166
34973
|
let hasPrintedOpenCodeEnvNote = false;
|
|
34167
34974
|
const printOpenCodeEnvNote = () => {
|
|
34168
34975
|
if (hasPrintedOpenCodeEnvNote) return;
|
|
@@ -34178,7 +34985,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34178
34985
|
if (mcpScope === "project" && editor !== "codex") continue;
|
|
34179
34986
|
try {
|
|
34180
34987
|
if (editor === "codex") {
|
|
34181
|
-
const filePath =
|
|
34988
|
+
const filePath = path9.join(homedir7(), ".codex", "config.toml");
|
|
34182
34989
|
if (dryRun) {
|
|
34183
34990
|
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
34184
34991
|
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
@@ -34194,7 +35001,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34194
35001
|
continue;
|
|
34195
35002
|
}
|
|
34196
35003
|
if (editor === "copilot") {
|
|
34197
|
-
const filePath =
|
|
35004
|
+
const filePath = path9.join(homedir7(), ".copilot", "mcp-config.json");
|
|
34198
35005
|
if (dryRun) {
|
|
34199
35006
|
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
34200
35007
|
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
@@ -34249,7 +35056,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34249
35056
|
continue;
|
|
34250
35057
|
}
|
|
34251
35058
|
if (editor === "cursor") {
|
|
34252
|
-
const filePath =
|
|
35059
|
+
const filePath = path9.join(homedir7(), ".cursor", "mcp.json");
|
|
34253
35060
|
if (dryRun) {
|
|
34254
35061
|
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
34255
35062
|
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
@@ -34261,7 +35068,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34261
35068
|
continue;
|
|
34262
35069
|
}
|
|
34263
35070
|
if (editor === "windsurf") {
|
|
34264
|
-
const filePath =
|
|
35071
|
+
const filePath = path9.join(homedir7(), ".codeium", "windsurf", "mcp_config.json");
|
|
34265
35072
|
if (dryRun) {
|
|
34266
35073
|
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
34267
35074
|
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
@@ -34278,7 +35085,20 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34278
35085
|
);
|
|
34279
35086
|
continue;
|
|
34280
35087
|
}
|
|
34281
|
-
if (editor === "kilo"
|
|
35088
|
+
if (editor === "kilo") {
|
|
35089
|
+
const filePath = path9.join(kiloConfigDir(), "kilo.jsonc");
|
|
35090
|
+
if (dryRun) {
|
|
35091
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status: "dry-run" });
|
|
35092
|
+
console.log(`- ${EDITOR_LABELS[editor]}: would update ${filePath}`);
|
|
35093
|
+
continue;
|
|
35094
|
+
}
|
|
35095
|
+
const kiloServer = buildContextStreamKiloServer(setupEnvBase);
|
|
35096
|
+
const status = await upsertKiloMcpConfig(filePath, kiloServer);
|
|
35097
|
+
writeActions.push({ kind: "mcp-config", target: filePath, status });
|
|
35098
|
+
console.log(`- ${EDITOR_LABELS[editor]}: ${status} ${filePath}`);
|
|
35099
|
+
continue;
|
|
35100
|
+
}
|
|
35101
|
+
if (editor === "roo") {
|
|
34282
35102
|
console.log(
|
|
34283
35103
|
`- ${EDITOR_LABELS[editor]}: project MCP config supported via file; global is managed via the app UI.`
|
|
34284
35104
|
);
|
|
@@ -34300,7 +35120,8 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34300
35120
|
windsurf: "windsurf",
|
|
34301
35121
|
cline: "cline",
|
|
34302
35122
|
roo: "roo",
|
|
34303
|
-
kilo:
|
|
35123
|
+
kilo: null,
|
|
35124
|
+
// Kilo CLI: no hooks API, enforcement via rules + skills
|
|
34304
35125
|
codex: null,
|
|
34305
35126
|
// No hooks API
|
|
34306
35127
|
copilot: null,
|
|
@@ -34331,7 +35152,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34331
35152
|
});
|
|
34332
35153
|
for (const script of result.installed) {
|
|
34333
35154
|
writeActions.push({ kind: "hooks", target: script, status: "created" });
|
|
34334
|
-
console.log(`- ${EDITOR_LABELS[editor]}: installed ${
|
|
35155
|
+
console.log(`- ${EDITOR_LABELS[editor]}: installed ${path9.basename(script)}`);
|
|
34335
35156
|
}
|
|
34336
35157
|
if (editor === "cursor") {
|
|
34337
35158
|
console.log(
|
|
@@ -34407,7 +35228,7 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34407
35228
|
await rl.question(`Add current folder as a project? [Y/n] (${process.cwd()}): `)
|
|
34408
35229
|
);
|
|
34409
35230
|
if (addCwd.toLowerCase() !== "n" && addCwd.toLowerCase() !== "no") {
|
|
34410
|
-
projectPaths.add(
|
|
35231
|
+
projectPaths.add(path9.resolve(process.cwd()));
|
|
34411
35232
|
}
|
|
34412
35233
|
while (true) {
|
|
34413
35234
|
console.log("\n 1) Add another project path");
|
|
@@ -34417,13 +35238,13 @@ Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
|
|
|
34417
35238
|
if (choice === "3") break;
|
|
34418
35239
|
if (choice === "1") {
|
|
34419
35240
|
const p = normalizeInput(await rl.question("Project folder path: "));
|
|
34420
|
-
if (p) projectPaths.add(
|
|
35241
|
+
if (p) projectPaths.add(path9.resolve(p));
|
|
34421
35242
|
continue;
|
|
34422
35243
|
}
|
|
34423
35244
|
if (choice === "2") {
|
|
34424
35245
|
const parent = normalizeInput(await rl.question("Parent folder path: "));
|
|
34425
35246
|
if (!parent) continue;
|
|
34426
|
-
const parentAbs =
|
|
35247
|
+
const parentAbs = path9.resolve(parent);
|
|
34427
35248
|
const projects2 = await discoverProjectsUnderFolder(parentAbs);
|
|
34428
35249
|
if (projects2.length === 0) {
|
|
34429
35250
|
console.log(
|
|
@@ -34456,7 +35277,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34456
35277
|
workspace_id: workspaceId,
|
|
34457
35278
|
workspace_name: workspaceName,
|
|
34458
35279
|
create_parent_mapping: createParentMapping,
|
|
34459
|
-
...
|
|
35280
|
+
...path9.resolve(projectPath) === path9.resolve(process.cwd()) && selectedCurrentProject ? {
|
|
34460
35281
|
project_id: selectedCurrentProject.id,
|
|
34461
35282
|
project_name: selectedCurrentProject.name
|
|
34462
35283
|
} : {},
|
|
@@ -34468,7 +35289,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34468
35289
|
});
|
|
34469
35290
|
writeActions.push({
|
|
34470
35291
|
kind: "workspace-config",
|
|
34471
|
-
target:
|
|
35292
|
+
target: path9.join(projectPath, ".contextstream", "config.json"),
|
|
34472
35293
|
status: "created"
|
|
34473
35294
|
});
|
|
34474
35295
|
console.log(`- Linked workspace in ${projectPath}`);
|
|
@@ -34479,24 +35300,37 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34479
35300
|
} else if (workspaceId && workspaceId !== "dry-run" && workspaceName && dryRun) {
|
|
34480
35301
|
writeActions.push({
|
|
34481
35302
|
kind: "workspace-config",
|
|
34482
|
-
target:
|
|
35303
|
+
target: path9.join(projectPath, ".contextstream", "config.json"),
|
|
34483
35304
|
status: "dry-run"
|
|
34484
35305
|
});
|
|
34485
35306
|
}
|
|
35307
|
+
const isCurrentProject = path9.resolve(projectPath) === path9.resolve(process.cwd());
|
|
35308
|
+
const projectIdForPath = isCurrentProject ? selectedCurrentProject?.id : void 0;
|
|
35309
|
+
const projectSetupEnv = {
|
|
35310
|
+
apiUrl,
|
|
35311
|
+
apiKey: "",
|
|
35312
|
+
// Omit API key from project configs — comes from global config or environment
|
|
35313
|
+
contextPackEnabled,
|
|
35314
|
+
workspaceId: workspaceId && workspaceId !== "dry-run" ? workspaceId : void 0,
|
|
35315
|
+
projectId: projectIdForPath
|
|
35316
|
+
};
|
|
35317
|
+
const projectMcpServer = buildContextStreamMcpServer(projectSetupEnv);
|
|
35318
|
+
const projectVsCodeServer = buildContextStreamVsCodeServer(projectSetupEnv);
|
|
35319
|
+
const projectMcpServerClaude = buildContextStreamMcpServer(projectSetupEnv);
|
|
34486
35320
|
if (mcpScope === "project" || mcpScope === "both" || enforceCopilotCanonicalPair) {
|
|
34487
35321
|
for (const editor of configuredEditors) {
|
|
34488
35322
|
const shouldWriteProjectMcp = mcpScope === "project" || mcpScope === "both" || enforceCopilotCanonicalPair && editor === "copilot";
|
|
34489
35323
|
if (!shouldWriteProjectMcp) continue;
|
|
34490
35324
|
try {
|
|
34491
35325
|
if (editor === "cursor") {
|
|
34492
|
-
const cursorPath =
|
|
34493
|
-
const vscodePath =
|
|
35326
|
+
const cursorPath = path9.join(projectPath, ".cursor", "mcp.json");
|
|
35327
|
+
const vscodePath = path9.join(projectPath, ".vscode", "mcp.json");
|
|
34494
35328
|
if (dryRun) {
|
|
34495
35329
|
writeActions.push({ kind: "mcp-config", target: cursorPath, status: "dry-run" });
|
|
34496
35330
|
writeActions.push({ kind: "mcp-config", target: vscodePath, status: "dry-run" });
|
|
34497
35331
|
} else {
|
|
34498
|
-
const status1 = await upsertJsonMcpConfig(cursorPath,
|
|
34499
|
-
const status2 = await upsertJsonVsCodeMcpConfig(vscodePath,
|
|
35332
|
+
const status1 = await upsertJsonMcpConfig(cursorPath, projectMcpServer);
|
|
35333
|
+
const status2 = await upsertJsonVsCodeMcpConfig(vscodePath, projectVsCodeServer);
|
|
34500
35334
|
writeActions.push({ kind: "mcp-config", target: cursorPath, status: status1 });
|
|
34501
35335
|
writeActions.push({ kind: "mcp-config", target: vscodePath, status: status2 });
|
|
34502
35336
|
}
|
|
@@ -34504,22 +35338,22 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34504
35338
|
}
|
|
34505
35339
|
if (editor === "windsurf") {
|
|
34506
35340
|
console.log(
|
|
34507
|
-
`- ${EDITOR_LABELS[editor]}: uses global MCP config only (${
|
|
35341
|
+
`- ${EDITOR_LABELS[editor]}: uses global MCP config only (${path9.join(homedir7(), ".codeium", "windsurf", "mcp_config.json")}).`
|
|
34508
35342
|
);
|
|
34509
35343
|
continue;
|
|
34510
35344
|
}
|
|
34511
35345
|
if (editor === "claude") {
|
|
34512
|
-
const mcpPath =
|
|
35346
|
+
const mcpPath = path9.join(projectPath, ".mcp.json");
|
|
34513
35347
|
if (dryRun) {
|
|
34514
35348
|
writeActions.push({ kind: "mcp-config", target: mcpPath, status: "dry-run" });
|
|
34515
35349
|
} else {
|
|
34516
|
-
const status = await upsertJsonMcpConfig(mcpPath,
|
|
35350
|
+
const status = await upsertJsonMcpConfig(mcpPath, projectMcpServerClaude);
|
|
34517
35351
|
writeActions.push({ kind: "mcp-config", target: mcpPath, status });
|
|
34518
35352
|
}
|
|
34519
35353
|
continue;
|
|
34520
35354
|
}
|
|
34521
35355
|
if (editor === "opencode") {
|
|
34522
|
-
const openCodePath =
|
|
35356
|
+
const openCodePath = path9.join(projectPath, "opencode.json");
|
|
34523
35357
|
if (dryRun) {
|
|
34524
35358
|
writeActions.push({
|
|
34525
35359
|
kind: "mcp-config",
|
|
@@ -34527,38 +35361,40 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34527
35361
|
status: "dry-run"
|
|
34528
35362
|
});
|
|
34529
35363
|
} else {
|
|
34530
|
-
const
|
|
35364
|
+
const projectOpenCode = buildContextStreamOpenCodeLocalServer(projectSetupEnv);
|
|
35365
|
+
const status = await upsertOpenCodeMcpConfig(openCodePath, projectOpenCode);
|
|
34531
35366
|
writeActions.push({ kind: "mcp-config", target: openCodePath, status });
|
|
34532
35367
|
}
|
|
34533
35368
|
printOpenCodeEnvNote();
|
|
34534
35369
|
continue;
|
|
34535
35370
|
}
|
|
34536
35371
|
if (editor === "copilot") {
|
|
34537
|
-
const vsCodePath =
|
|
35372
|
+
const vsCodePath = path9.join(projectPath, ".vscode", "mcp.json");
|
|
34538
35373
|
if (dryRun) {
|
|
34539
35374
|
writeActions.push({ kind: "mcp-config", target: vsCodePath, status: "dry-run" });
|
|
34540
35375
|
} else {
|
|
34541
|
-
const status = await upsertJsonVsCodeMcpConfig(vsCodePath,
|
|
35376
|
+
const status = await upsertJsonVsCodeMcpConfig(vsCodePath, projectVsCodeServer);
|
|
34542
35377
|
writeActions.push({ kind: "mcp-config", target: vsCodePath, status });
|
|
34543
35378
|
}
|
|
34544
35379
|
continue;
|
|
34545
35380
|
}
|
|
34546
35381
|
if (editor === "kilo") {
|
|
34547
|
-
const kiloPath =
|
|
35382
|
+
const kiloPath = path9.join(projectPath, "kilo.jsonc");
|
|
34548
35383
|
if (dryRun) {
|
|
34549
35384
|
writeActions.push({ kind: "mcp-config", target: kiloPath, status: "dry-run" });
|
|
34550
35385
|
} else {
|
|
34551
|
-
const
|
|
35386
|
+
const kiloServer = buildContextStreamKiloServer(projectSetupEnv);
|
|
35387
|
+
const status = await upsertKiloMcpConfig(kiloPath, kiloServer);
|
|
34552
35388
|
writeActions.push({ kind: "mcp-config", target: kiloPath, status });
|
|
34553
35389
|
}
|
|
34554
35390
|
continue;
|
|
34555
35391
|
}
|
|
34556
35392
|
if (editor === "roo") {
|
|
34557
|
-
const rooPath =
|
|
35393
|
+
const rooPath = path9.join(projectPath, ".roo", "mcp.json");
|
|
34558
35394
|
if (dryRun) {
|
|
34559
35395
|
writeActions.push({ kind: "mcp-config", target: rooPath, status: "dry-run" });
|
|
34560
35396
|
} else {
|
|
34561
|
-
const status = await upsertJsonMcpConfig(rooPath,
|
|
35397
|
+
const status = await upsertJsonMcpConfig(rooPath, projectMcpServer);
|
|
34562
35398
|
writeActions.push({ kind: "mcp-config", target: rooPath, status });
|
|
34563
35399
|
}
|
|
34564
35400
|
continue;
|
|
@@ -34577,11 +35413,11 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34577
35413
|
const rule = generateRuleContent(editor, {
|
|
34578
35414
|
workspaceName,
|
|
34579
35415
|
workspaceId: workspaceId && workspaceId !== "dry-run" ? workspaceId : void 0,
|
|
34580
|
-
projectName:
|
|
35416
|
+
projectName: path9.basename(projectPath),
|
|
34581
35417
|
mode: getModeForEditor(editor)
|
|
34582
35418
|
});
|
|
34583
35419
|
if (!rule) continue;
|
|
34584
|
-
const filePath =
|
|
35420
|
+
const filePath = path9.join(projectPath, rule.filename);
|
|
34585
35421
|
if (dryRun) {
|
|
34586
35422
|
writeActions.push({ kind: "rules", target: filePath, status: "dry-run" });
|
|
34587
35423
|
continue;
|
|
@@ -34720,14 +35556,14 @@ Applying to ${projects.length} project(s)...`);
|
|
|
34720
35556
|
// src/index.ts
|
|
34721
35557
|
var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
|
|
34722
35558
|
function showFirstRunMessage() {
|
|
34723
|
-
const configDir =
|
|
34724
|
-
const starShownFile =
|
|
34725
|
-
if (
|
|
35559
|
+
const configDir = join22(homedir19(), ".contextstream");
|
|
35560
|
+
const starShownFile = join22(configDir, ".star-shown");
|
|
35561
|
+
if (existsSync16(starShownFile)) {
|
|
34726
35562
|
return;
|
|
34727
35563
|
}
|
|
34728
|
-
if (!
|
|
35564
|
+
if (!existsSync16(configDir)) {
|
|
34729
35565
|
try {
|
|
34730
|
-
|
|
35566
|
+
mkdirSync8(configDir, { recursive: true });
|
|
34731
35567
|
} catch {
|
|
34732
35568
|
return;
|
|
34733
35569
|
}
|
|
@@ -34740,7 +35576,7 @@ function showFirstRunMessage() {
|
|
|
34740
35576
|
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
34741
35577
|
console.error("");
|
|
34742
35578
|
try {
|
|
34743
|
-
|
|
35579
|
+
writeFileSync9(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
34744
35580
|
} catch {
|
|
34745
35581
|
}
|
|
34746
35582
|
}
|