@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/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(path20, checkUnignored, mode) {
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(path20);
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 = (path20, originalPath, doThrow) => {
767
- if (!isString(path20)) {
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 (!path20) {
773
+ if (!path21) {
774
774
  return doThrow(`path must not be empty`, TypeError);
775
775
  }
776
- if (checkPath.isNotRelative(path20)) {
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 = (path20) => REGEX_TEST_INVALID_PATH.test(path20);
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 path20 = originalPath && checkPath.convert(originalPath);
815
+ const path21 = originalPath && checkPath.convert(originalPath);
816
816
  checkPath(
817
- path20,
817
+ path21,
818
818
  originalPath,
819
819
  this._strictPathCheck ? throwError : RETURN_FALSE
820
820
  );
821
- return this._t(path20, cache, checkUnignored, slices);
821
+ return this._t(path21, cache, checkUnignored, slices);
822
822
  }
823
- checkIgnore(path20) {
824
- if (!REGEX_TEST_TRAILING_SLASH.test(path20)) {
825
- return this.test(path20);
823
+ checkIgnore(path21) {
824
+ if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
825
+ return this.test(path21);
826
826
  }
827
- const slices = path20.split(SLASH).filter(Boolean);
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(path20, false, MODE_CHECK_IGNORE);
840
+ return this._rules.test(path21, false, MODE_CHECK_IGNORE);
841
841
  }
842
- _t(path20, cache, checkUnignored, slices) {
843
- if (path20 in cache) {
844
- return cache[path20];
842
+ _t(path21, cache, checkUnignored, slices) {
843
+ if (path21 in cache) {
844
+ return cache[path21];
845
845
  }
846
846
  if (!slices) {
847
- slices = path20.split(SLASH).filter(Boolean);
847
+ slices = path21.split(SLASH).filter(Boolean);
848
848
  }
849
849
  slices.pop();
850
850
  if (!slices.length) {
851
- return cache[path20] = this._rules.test(path20, checkUnignored, MODE_IGNORE);
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[path20] = parent.ignored ? parent : this._rules.test(path20, checkUnignored, MODE_IGNORE);
859
+ return cache[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
860
860
  }
861
- ignores(path20) {
862
- return this._test(path20, this._ignoreCache, false).ignored;
861
+ ignores(path21) {
862
+ return this._test(path21, this._ignoreCache, false).ignored;
863
863
  }
864
864
  createFilter() {
865
- return (path20) => !this.ignores(path20);
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(path20) {
872
- return this._test(path20, this._testCache, true);
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 = (path20) => checkPath(path20 && checkPath.convert(path20), path20, RETURN_FALSE);
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 = (path20) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path20) || isNotRelative(path20);
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(...)\` unless search returns 0 results
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
- - Local tools are only allowed if project is not indexed or ContextStream returns 0 results
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) if ContextStream returns **0 results**
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. Use local tools ONLY if ContextStream returns 0 results.
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 if ContextStream returns no results, errors, or the user explicitly asks
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 ContextStream returned 0 results.
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 local tools ONLY if 0 results
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 Use local tools (Glob/Grep/Read) directly
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 Project is not indexed
4695
- \u2705 Index is stale/outdated (>7 days old)
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: ".kilocode/rules/contextstream.md",
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 fs8 from "node:fs";
4899
- import * as path9 from "node:path";
4900
- import { homedir as homedir7 } from "node:os";
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 = path9.resolve(startDir);
4924
+ let currentDir = path10.resolve(startDir);
4926
4925
  for (let i = 0; i < 10; i++) {
4927
- const configPath = path9.join(currentDir, ".contextstream", "config.json");
4928
- if (fs8.existsSync(configPath)) {
4926
+ const configPath = path10.join(currentDir, ".contextstream", "config.json");
4927
+ if (fs9.existsSync(configPath)) {
4929
4928
  try {
4930
- const content = fs8.readFileSync(configPath, "utf-8");
4929
+ const content = fs9.readFileSync(configPath, "utf-8");
4931
4930
  return JSON.parse(content);
4932
4931
  } catch {
4933
4932
  }
4934
4933
  }
4935
- const parentDir = path9.dirname(currentDir);
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 = path9.resolve(startDir);
4946
+ let currentDir = path10.resolve(startDir);
4948
4947
  for (let i = 0; i < 10; i++) {
4949
- const mcpPath = path9.join(currentDir, ".mcp.json");
4950
- if (fs8.existsSync(mcpPath)) {
4948
+ const mcpPath = path10.join(currentDir, ".mcp.json");
4949
+ if (fs9.existsSync(mcpPath)) {
4951
4950
  try {
4952
- const content = fs8.readFileSync(mcpPath, "utf-8");
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 = path9.dirname(currentDir);
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 = path9.join(homedir7(), ".mcp.json");
4971
- if (fs8.existsSync(homeMcpPath)) {
4969
+ const homeMcpPath = path10.join(homedir8(), ".mcp.json");
4970
+ if (fs9.existsSync(homeMcpPath)) {
4972
4971
  try {
4973
- const content = fs8.readFileSync(homeMcpPath, "utf-8");
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 = path9.extname(filePath).toLowerCase();
4988
+ const ext = path10.extname(filePath).toLowerCase();
4990
4989
  if (!INDEXABLE_EXTENSIONS.has(ext)) {
4991
- const basename5 = path9.basename(filePath).toLowerCase();
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 = fs8.statSync(filePath);
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 = path9.extname(filePath).toLowerCase();
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 = fs8.readFileSync(filePath, "utf-8");
5077
- const relativePath = path9.relative(projectRoot, filePath);
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 = path9.dirname(path9.resolve(filePath));
5108
+ let currentDir = path10.dirname(path10.resolve(filePath));
5110
5109
  for (let i = 0; i < 10; i++) {
5111
- const configPath = path9.join(currentDir, ".contextstream", "config.json");
5112
- if (fs8.existsSync(configPath)) {
5110
+ const configPath = path10.join(currentDir, ".contextstream", "config.json");
5111
+ if (fs9.existsSync(configPath)) {
5113
5112
  return currentDir;
5114
5113
  }
5115
- const parentDir = path9.dirname(currentDir);
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 = path9.isAbsolute(filePath) ? filePath : path9.resolve(cwd, filePath);
5144
- if (!fs8.existsSync(absolutePath) || !shouldIndexFile(absolutePath)) {
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 fs9 from "node:fs";
5249
- import * as path10 from "node:path";
5250
- import { homedir as homedir8 } from "node:os";
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(fs9.readFileSync(0, "utf8"));
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 = path10.resolve(cwd);
5281
+ let searchDir = path11.resolve(cwd);
5283
5282
  for (let i = 0; i < 6; i++) {
5284
5283
  if (!apiKey && !jwt) {
5285
- const mcpPath = path10.join(searchDir, ".mcp.json");
5286
- if (fs9.existsSync(mcpPath)) {
5284
+ const mcpPath = path11.join(searchDir, ".mcp.json");
5285
+ if (fs10.existsSync(mcpPath)) {
5287
5286
  try {
5288
- const config = JSON.parse(fs9.readFileSync(mcpPath, "utf8"));
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 = path10.join(searchDir, ".contextstream", "config.json");
5301
- if (fs9.existsSync(localConfigPath)) {
5299
+ const localConfigPath = path11.join(searchDir, ".contextstream", "config.json");
5300
+ if (fs10.existsSync(localConfigPath)) {
5302
5301
  try {
5303
- const localConfig = JSON.parse(fs9.readFileSync(localConfigPath, "utf8"));
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 = path10.dirname(searchDir);
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 = path10.join(homedir8(), ".mcp.json");
5316
- if (fs9.existsSync(homeMcpPath)) {
5314
+ const homeMcpPath = path11.join(homedir9(), ".mcp.json");
5315
+ if (fs10.existsSync(homeMcpPath)) {
5317
5316
  try {
5318
- const config = JSON.parse(fs9.readFileSync(homeMcpPath, "utf8"));
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 fs10 from "node:fs";
5479
- import * as path11 from "node:path";
5480
- import { homedir as homedir9 } from "node:os";
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(fs10.readFileSync(FAILURE_COUNTERS_FILE, "utf8"));
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
- fs10.mkdirSync(path11.dirname(FAILURE_COUNTERS_FILE), { recursive: true });
5497
- fs10.writeFileSync(FAILURE_COUNTERS_FILE, JSON.stringify(counters, null, 2), "utf8");
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 = path11.join(homedir9(), ".contextstream", "hook-failure-counts.json");
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 fs11 from "node:fs";
5715
+ import * as fs12 from "node:fs";
5717
5716
  function parseTranscript(transcriptPath) {
5718
5717
  try {
5719
- const content = fs11.readFileSync(transcriptPath, "utf8");
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 && fs11.existsSync(transcriptPath) ? parseTranscript(transcriptPath) : { assistantMessages: [], toolCallCount: 0 };
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 fs12 from "node:fs";
6006
- import * as path12 from "node:path";
6007
- import { homedir as homedir10 } from "node:os";
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
- fs12.mkdirSync(path12.dirname(STATE_PATH), { recursive: true });
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 path12.resolve(input);
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}${path12.sep}`) || right.startsWith(`${left}${path12.sep}`);
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 = fs12.readFileSync(STATE_PATH, "utf8");
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
- fs12.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
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 = path12.join(homedir10(), ".contextstream", "prompt-state.json");
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 fs13 from "node:fs";
6177
- import * as path13 from "node:path";
6178
- import { homedir as homedir11 } from "node:os";
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 (!fs13.existsSync(INDEX_STATUS_FILE)) {
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 = fs13.readFileSync(INDEX_STATUS_FILE, "utf-8");
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 = path13.resolve(cwd);
6283
+ const cwdPath = path14.resolve(cwd);
6216
6284
  for (const [projectPath, info] of Object.entries(projects)) {
6217
6285
  try {
6218
- const indexedPath = path13.resolve(projectPath);
6219
- if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath + path13.sep)) {
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] REDIRECT (additionalContext): ${JSON.stringify(response)}
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] Hook invoked at ${(/* @__PURE__ */ new Date()).toISOString()}
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
- fs13.appendFileSync(DEBUG_FILE, "[PreToolUse] Hook disabled, exiting\n");
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] tool=${tool}, cwd=${cwd}, editorFormat=${editorFormat}
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
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
- fs13.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
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 = path13.join(homedir11(), ".contextstream", "indexed-projects.json");
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 fs14 from "node:fs";
6585
- import * as path14 from "node:path";
6586
- import { homedir as homedir12 } from "node:os";
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 = path14.resolve(cwd);
6669
+ let searchDir = path15.resolve(cwd);
6589
6670
  for (let i = 0; i < 5; i++) {
6590
6671
  if (!API_KEY2) {
6591
- const mcpPath = path14.join(searchDir, ".mcp.json");
6592
- if (fs14.existsSync(mcpPath)) {
6672
+ const mcpPath = path15.join(searchDir, ".mcp.json");
6673
+ if (fs15.existsSync(mcpPath)) {
6593
6674
  try {
6594
- const content = fs14.readFileSync(mcpPath, "utf-8");
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 = path14.join(searchDir, ".contextstream", "config.json");
6612
- if (fs14.existsSync(csConfigPath)) {
6692
+ const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
6693
+ if (fs15.existsSync(csConfigPath)) {
6613
6694
  try {
6614
- const content = fs14.readFileSync(csConfigPath, "utf-8");
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 = path14.dirname(searchDir);
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 = path14.join(homedir12(), ".mcp.json");
6632
- if (fs14.existsSync(homeMcpPath)) {
6712
+ const homeMcpPath = path15.join(homedir13(), ".mcp.json");
6713
+ if (fs15.existsSync(homeMcpPath)) {
6633
6714
  try {
6634
- const content = fs14.readFileSync(homeMcpPath, "utf-8");
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 = fs14.readFileSync(transcriptPath, "utf-8");
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 Use local tools (Glob/Grep/Read) directly
6991
- \u2192 OR run \`project(action="index")\` first, then search
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
- - Project is NOT indexed
6998
- - Index is stale/outdated (>7 days)
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 OR wait for indexing
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: Use local tools (Glob/Grep/Read) directly
7142
- \u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
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 Project is NOT indexed (index_status.indexed=false)
7146
- \u2705 Index is stale/outdated (>7 days old)
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 fs15 from "node:fs";
7194
- import * as path15 from "node:path";
7195
- import { homedir as homedir13 } from "node:os";
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 = path15.resolve(cwd);
7276
+ let searchDir = path16.resolve(cwd);
7198
7277
  for (let i = 0; i < 5; i++) {
7199
7278
  if (!API_KEY3) {
7200
- const mcpPath = path15.join(searchDir, ".mcp.json");
7201
- if (fs15.existsSync(mcpPath)) {
7279
+ const mcpPath = path16.join(searchDir, ".mcp.json");
7280
+ if (fs16.existsSync(mcpPath)) {
7202
7281
  try {
7203
- const content = fs15.readFileSync(mcpPath, "utf-8");
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 = path15.join(searchDir, ".contextstream", "config.json");
7218
- if (fs15.existsSync(csConfigPath)) {
7296
+ const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
7297
+ if (fs16.existsSync(csConfigPath)) {
7219
7298
  try {
7220
- const content = fs15.readFileSync(csConfigPath, "utf-8");
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 = path15.dirname(searchDir);
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 = path15.join(homedir13(), ".mcp.json");
7235
- if (fs15.existsSync(homeMcpPath)) {
7313
+ const homeMcpPath = path16.join(homedir14(), ".mcp.json");
7314
+ if (fs16.existsSync(homeMcpPath)) {
7236
7315
  try {
7237
- const content = fs15.readFileSync(homeMcpPath, "utf-8");
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 = fs15.readFileSync(transcriptPath, "utf-8");
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 && fs15.existsSync(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 fs16 from "node:fs";
7517
- import * as path16 from "node:path";
7518
- import { homedir as homedir14 } from "node:os";
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 = path16.resolve(cwd);
7599
+ let searchDir = path17.resolve(cwd);
7521
7600
  for (let i = 0; i < 5; i++) {
7522
7601
  if (!API_KEY4) {
7523
- const mcpPath = path16.join(searchDir, ".mcp.json");
7524
- if (fs16.existsSync(mcpPath)) {
7602
+ const mcpPath = path17.join(searchDir, ".mcp.json");
7603
+ if (fs17.existsSync(mcpPath)) {
7525
7604
  try {
7526
- const content = fs16.readFileSync(mcpPath, "utf-8");
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 = path16.join(searchDir, ".contextstream", "config.json");
7541
- if (fs16.existsSync(csConfigPath)) {
7619
+ const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
7620
+ if (fs17.existsSync(csConfigPath)) {
7542
7621
  try {
7543
- const content = fs16.readFileSync(csConfigPath, "utf-8");
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 = path16.dirname(searchDir);
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 = path16.join(homedir14(), ".mcp.json");
7558
- if (fs16.existsSync(homeMcpPath)) {
7636
+ const homeMcpPath = path17.join(homedir15(), ".mcp.json");
7637
+ if (fs17.existsSync(homeMcpPath)) {
7559
7638
  try {
7560
- const content = fs16.readFileSync(homeMcpPath, "utf-8");
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 fs17 from "node:fs";
7695
- import * as path17 from "node:path";
7696
- import { homedir as homedir15 } from "node:os";
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 = path17.resolve(cwd);
7777
+ let searchDir = path18.resolve(cwd);
7699
7778
  for (let i = 0; i < 5; i++) {
7700
7779
  if (!API_KEY5) {
7701
- const mcpPath = path17.join(searchDir, ".mcp.json");
7702
- if (fs17.existsSync(mcpPath)) {
7780
+ const mcpPath = path18.join(searchDir, ".mcp.json");
7781
+ if (fs18.existsSync(mcpPath)) {
7703
7782
  try {
7704
- const content = fs17.readFileSync(mcpPath, "utf-8");
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 = path17.join(searchDir, ".contextstream", "config.json");
7722
- if (fs17.existsSync(csConfigPath)) {
7800
+ const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
7801
+ if (fs18.existsSync(csConfigPath)) {
7723
7802
  try {
7724
- const content = fs17.readFileSync(csConfigPath, "utf-8");
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 = path17.dirname(searchDir);
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 = path17.join(homedir15(), ".mcp.json");
7742
- if (fs17.existsSync(homeMcpPath)) {
7820
+ const homeMcpPath = path18.join(homedir16(), ".mcp.json");
7821
+ if (fs18.existsSync(homeMcpPath)) {
7743
7822
  try {
7744
- const content = fs17.readFileSync(homeMcpPath, "utf-8");
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 = path17.join(folderPath, rule.filename);
7864
- if (!fs17.existsSync(filePath)) continue;
7942
+ const filePath = path18.join(folderPath, rule.filename);
7943
+ if (!fs18.existsSync(filePath)) continue;
7865
7944
  try {
7866
- const existing = fs17.readFileSync(filePath, "utf8");
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
- fs17.writeFileSync(filePath, merged.trim() + "\n", "utf8");
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 fs18 from "node:fs";
7999
- import * as path18 from "node:path";
8000
- import { homedir as homedir16 } from "node:os";
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 = path18.resolve(cwd);
8081
+ let searchDir = path19.resolve(cwd);
8003
8082
  for (let i = 0; i < 5; i++) {
8004
8083
  if (!API_KEY6) {
8005
- const mcpPath = path18.join(searchDir, ".mcp.json");
8006
- if (fs18.existsSync(mcpPath)) {
8084
+ const mcpPath = path19.join(searchDir, ".mcp.json");
8085
+ if (fs19.existsSync(mcpPath)) {
8007
8086
  try {
8008
- const content = fs18.readFileSync(mcpPath, "utf-8");
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 = path18.join(searchDir, ".contextstream", "config.json");
8023
- if (fs18.existsSync(csConfigPath)) {
8101
+ const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
8102
+ if (fs19.existsSync(csConfigPath)) {
8024
8103
  try {
8025
- const content = fs18.readFileSync(csConfigPath, "utf-8");
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 = path18.dirname(searchDir);
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 = path18.join(homedir16(), ".mcp.json");
8043
- if (fs18.existsSync(homeMcpPath)) {
8121
+ const homeMcpPath = path19.join(homedir17(), ".mcp.json");
8122
+ if (fs19.existsSync(homeMcpPath)) {
8044
8123
  try {
8045
- const content = fs18.readFileSync(homeMcpPath, "utf-8");
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 || !fs18.existsSync(transcriptPath)) {
8147
+ if (!transcriptPath || !fs19.existsSync(transcriptPath)) {
8069
8148
  return stats;
8070
8149
  }
8071
8150
  try {
8072
- const content = fs18.readFileSync(transcriptPath, "utf-8");
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 fs19 from "node:fs";
8431
- import * as path19 from "node:path";
8432
- import { homedir as homedir17 } from "node:os";
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 path19.join(homedir17(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
8544
+ return path20.join(homedir18(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
8466
8545
  } else if (platform2 === "win32") {
8467
- return path19.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
8546
+ return path20.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
8468
8547
  } else {
8469
- return path19.join(homedir17(), ".config", "Claude", "claude_desktop_config.json");
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 = path19.join(searchDir, ".mcp.json");
8487
- if (fs19.existsSync(projectMcpPath)) {
8565
+ const projectMcpPath = path20.join(searchDir, ".mcp.json");
8566
+ if (fs20.existsSync(projectMcpPath)) {
8488
8567
  try {
8489
- const content = fs19.readFileSync(projectMcpPath, "utf-8");
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 = path19.dirname(searchDir);
8582
+ const parentDir = path20.dirname(searchDir);
8504
8583
  if (parentDir === searchDir) break;
8505
8584
  searchDir = parentDir;
8506
8585
  }
8507
- const globalMcpPath = path19.join(homedir17(), ".mcp.json");
8508
- if (fs19.existsSync(globalMcpPath)) {
8586
+ const globalMcpPath = path20.join(homedir18(), ".mcp.json");
8587
+ if (fs20.existsSync(globalMcpPath)) {
8509
8588
  try {
8510
- const content = fs19.readFileSync(globalMcpPath, "utf-8");
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
- path19.join(process.cwd(), ".cursor", "mcp.json"),
8526
- path19.join(homedir17(), ".cursor", "mcp.json")
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 (fs19.existsSync(cursorPath)) {
8608
+ if (fs20.existsSync(cursorPath)) {
8530
8609
  try {
8531
- const content = fs19.readFileSync(cursorPath, "utf-8");
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 (fs19.existsSync(claudeDesktopPath)) {
8626
+ if (fs20.existsSync(claudeDesktopPath)) {
8548
8627
  try {
8549
- const content = fs19.readFileSync(claudeDesktopPath, "utf-8");
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
- path19.join(homedir17(), ".vscode", "mcp.json"),
8565
- path19.join(homedir17(), ".codeium", "windsurf", "mcp_config.json"),
8566
- path19.join(homedir17(), ".continue", "config.json")
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 (fs19.existsSync(vsPath)) {
8648
+ if (fs20.existsSync(vsPath)) {
8570
8649
  try {
8571
- const content = fs19.readFileSync(vsPath, "utf-8");
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 = path19.join(homedir17(), ".contextstream", "credentials.json");
8587
- if (fs19.existsSync(credentialsPath)) {
8665
+ const credentialsPath = path20.join(homedir18(), ".contextstream", "credentials.json");
8666
+ if (fs20.existsSync(credentialsPath)) {
8588
8667
  try {
8589
- const content = fs19.readFileSync(credentialsPath, "utf-8");
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: path20, errorMaps, issueData } = params;
9188
- const fullPath = [...path20, ...issueData.path || []];
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, path20, key) {
9383
+ constructor(parent, value, path21, key) {
9305
9384
  this._cachedPath = [];
9306
9385
  this.parent = parent;
9307
9386
  this.data = value;
9308
- this._path = path20;
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, path20, options = {}) {
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 = path20.startsWith("/") ? path20 : `/${path20}`;
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(path20) {
13038
- if (/\/github(\/|$)/i.test(path20)) return "github";
13039
- if (/\/slack(\/|$)/i.test(path20)) return "slack";
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
- const homeDir = process.env.HOME || process.env.USERPROFILE || "";
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 mappings = readGlobalMappings();
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 writeGlobalMappings(filtered);
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 fs20 = __require("fs");
13521
+ const fs21 = __require("fs");
13371
13522
  const pathModule = __require("path");
13372
- const rootHasGit = fs20.existsSync(pathModule.join(folderPath, ".git"));
13373
- const entries = fs20.readdirSync(folderPath, { withFileTypes: true });
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 (fs20.existsSync(pathModule.join(subdirPath, marker))) {
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
- // Search - each method adds required search_type and filters fields
13642
- // Optional params: context_lines (like grep -C), exact_match_boost (boost for exact matches)
13643
- searchSemantic(body) {
13644
- return request(this.config, "/search/semantic", {
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: "semantic",
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 request(this.config, "/search/hybrid", {
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 request(this.config, "/search/keyword", {
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 request(this.config, "/search/pattern", {
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 request(this.config, "/search/exhaustive", {
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 request(this.config, "/search/refactor", {
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 request(this.config, "/search/crawl", {
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
- let workspaceId = params.workspace_id || this.config.defaultWorkspaceId;
14713
- let projectId = params.project_id || this.config.defaultProjectId;
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 (!workspaceId && rootPath) {
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
- projectId = resolved.config.project_id || projectId;
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
- if (workspaces.items && workspaces.items.length > 0) {
14734
- let matchedWorkspace;
14735
- let matchSource;
14736
- matchedWorkspace = workspaces.items.find((w) => w.name.toLowerCase() === folderName);
14737
- if (matchedWorkspace) {
14738
- matchSource = "workspace_name_exact";
14739
- }
14740
- if (!matchedWorkspace) {
14741
- matchedWorkspace = workspaces.items.find(
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 = folderDisplayName;
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
- try {
14809
- const workspaces = await this.listWorkspaces({ page_size: 1 });
14810
- if (workspaces.items && workspaces.items.length > 0) {
14811
- workspaceId = workspaces.items[0].id;
14812
- workspaceName = workspaces.items[0].name;
14813
- context.workspace_source = "fallback_first";
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
- } catch (e) {
14816
- context.workspace_error = String(e);
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 = "manual_note";
15424
+ apiEventType = params.event_type;
15313
15425
  tags.push(params.event_type);
15314
15426
  break;
15315
- // Lesson system types - all stored as manual_note with specific tags
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 = "manual_note";
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 path20 = c.file_path || "file";
15899
+ const path21 = c.file_path || "file";
15788
15900
  const content = c.content?.slice(0, 150) || "";
15789
- return { path: path20, entry: `\u2022 ${path20}: ${content}...
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}/integrations/status`, {
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 fs5 from "node:fs";
17664
- import * as path6 from "node:path";
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 homedir4 } from "node:os";
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: path6.join(".github", "copilot-instructions.md"),
18803
+ copilot: path7.join(".github", "copilot-instructions.md"),
18550
18804
  cursor: ".cursorrules",
18551
18805
  cline: ".clinerules",
18552
- kilo: path6.join(".kilocode", "rules", "contextstream.md"),
18553
- roo: path6.join(".roo", "rules", "contextstream.md"),
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: [path6.join(homedir4(), ".codex", "AGENTS.md")],
18558
- kilo: [path6.join(homedir4(), ".kilocode", "rules", "contextstream.md")],
18559
- roo: [path6.join(homedir4(), ".roo", "rules", "contextstream.md")]
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(path6.join(folderPath, rel));
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 fs5.existsSync(path6.join(cwd, entry));
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) => fs5.existsSync(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 = fs5.readFileSync(filePath, "utf-8");
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 fs5.promises.mkdir(path6.dirname(filePath), { recursive: true });
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 fs5.promises.readFile(filePath, "utf8");
19134
+ existing2 = await fs6.promises.readFile(filePath, "utf8");
18881
19135
  } catch {
18882
19136
  }
18883
- await fs5.promises.writeFile(filePath, content.trimEnd() + "\n", "utf8");
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 fs5.promises.readFile(filePath, "utf8");
19143
+ existing = await fs6.promises.readFile(filePath, "utf8");
18890
19144
  } catch {
18891
19145
  }
18892
19146
  if (!existing) {
18893
- await fs5.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
19147
+ await fs6.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
18894
19148
  return "created";
18895
19149
  }
18896
19150
  if (!existing.trim()) {
18897
- await fs5.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
19151
+ await fs6.promises.writeFile(filePath, wrappedContent + "\n", "utf8");
18898
19152
  return "updated";
18899
19153
  }
18900
19154
  const replaced = replaceContextStreamBlock(existing, content);
18901
- await fs5.promises.writeFile(filePath, replaced.content, "utf8");
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 = path6.join(options.folderPath, rule.filename);
18921
- if (fs5.existsSync(filePath) && !options.overwriteExisting) {
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 (fs5.existsSync(filePath) && !options.overwriteExisting) {
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, path20) {
19325
+ function buildParamDescription(key, path21) {
19072
19326
  const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
19073
- const parent = path20[path20.length - 1];
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, path20 = []) {
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, [...path20, key]);
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, path20));
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 = path6.resolve(folderPath);
21361
+ const resolvedFolder = path7.resolve(folderPath);
21020
21362
  let bestMatch;
21021
21363
  for (const [projectPath, info] of Object.entries(projects)) {
21022
- const resolvedProjectPath = path6.resolve(projectPath);
21023
- const matches = resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${path6.sep}`) || resolvedProjectPath.startsWith(`${resolvedFolder}${path6.sep}`);
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
- return client.searchRefactor(params);
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 = path6.resolve(inputPath);
21411
+ const resolvedPath = path7.resolve(inputPath);
21062
21412
  let stats;
21063
21413
  try {
21064
- stats = await fs5.promises.stat(resolvedPath);
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 fs5.promises.access(resolvedPath, fs5.constants.R_OK | fs5.constants.X_OK);
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 = path6.join(input.folder_path, ".contextstream");
22081
- const configPath = path6.join(configDir, "config.json");
22082
- if (!fs5.existsSync(configDir)) {
22083
- fs5.mkdirSync(configDir, { recursive: true });
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
- fs5.writeFileSync(configPath, JSON.stringify(config, null, 2));
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
- return errorResult(
22172
- "Error: project_id is required. Please call session_init first or provide project_id explicitly."
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" ? path6.basename(input.folder_path) || "this folder" : "this folder";
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 = path6.basename(folderPath) || "My Project";
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 = await executeSearchMode(dispatchMode, baseParams2);
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
- const { results, total } = extractSearchEnvelope(selected.result);
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 mode.");
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
- const lessons = result?.data?.lessons || result?.lessons || [];
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 recallResults = result?.data?.results || result?.results || [];
27150
- const recallWithHint = Array.isArray(recallResults) && recallResults.length === 0 ? { ...result, hint: getEmptyStateHint("recall") } : result;
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
- const result = await client.decisionTrace({
27256
- workspace_id: workspaceId,
27257
- project_id: projectId,
27258
- query: input.query,
27259
- include_impact: input.include_impact,
27260
- limit: input.limit
27261
- });
27262
- return {
27263
- content: [{ type: "text", text: formatContent(result) }]
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 resultWithHint = plans.length === 0 ? { ...result, hint: getEmptyStateHint("list_plans") } : result;
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
- if (!input.node_id) {
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 errorResult("get requires: project_id");
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 errorResult("update requires: project_id");
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 errorResult("delete requires: project_id");
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 errorResult("index requires: project_id");
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 errorResult("overview requires: project_id");
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 errorResult("statistics requires: project_id");
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 errorResult("files requires: project_id");
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 errorResult("index_history requires: project_id");
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 errorResult("ingest_local requires: project_id");
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
- if (!folderPath) {
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(folderPath, input.limit ?? 10, input.since);
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 fs20 = await import("fs/promises");
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 fs20.stat(resolvedPath);
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 fs20.readFile(resolvedPath);
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(path20) {
31942
- this.folderPath = path20;
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 fs20 = await import("fs");
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 fs20.existsSync(`${cwd}/${f}`);
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 existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
32738
- import { homedir as homedir18 } from "os";
32739
- import { join as join21 } from "path";
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 fs7 from "node:fs/promises";
32743
- import * as path8 from "node:path";
32744
- import { homedir as homedir6 } from "node:os";
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 fs6 from "node:fs/promises";
32752
- import * as path7 from "node:path";
32753
- import { homedir as homedir5 } from "node:os";
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 path7.join(homedir5(), ".contextstream", "credentials.json");
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 fs6.readFile(filePath, "utf8");
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 fs6.mkdir(path7.dirname(filePath), { recursive: true });
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 fs6.writeFile(filePath, body, { encoding: "utf8", mode: 384 });
33472
+ await fs7.writeFile(filePath, body, { encoding: "utf8", mode: 384 });
32805
33473
  try {
32806
- await fs6.chmod(filePath, 384);
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 fs7.stat(filePath);
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 fs7.mkdir(path8.dirname(filePath), { recursive: true });
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 fs7.writeFile(filePath, wrappedContent + "\n", "utf8");
33692
+ await fs8.writeFile(filePath, wrappedContent + "\n", "utf8");
33025
33693
  return "created";
33026
33694
  }
33027
- const existing = await fs7.readFile(filePath, "utf8").catch(() => "");
33695
+ const existing = await fs8.readFile(filePath, "utf8").catch(() => "");
33028
33696
  if (!existing.trim()) {
33029
- await fs7.writeFile(filePath, wrappedContent + "\n", "utf8");
33697
+ await fs8.writeFile(filePath, wrappedContent + "\n", "utf8");
33030
33698
  return "updated";
33031
33699
  }
33032
33700
  const replaced = replaceContextStreamBlock2(existing, content);
33033
- await fs7.writeFile(filePath, replaced.content, "utf8");
33701
+ await fs8.writeFile(filePath, replaced.content, "utf8");
33034
33702
  return replaced.status;
33035
33703
  }
33036
33704
  function globalRulesPathForEditor(editor) {
33037
- const home = homedir6();
33705
+ const home = homedir7();
33038
33706
  switch (editor) {
33039
33707
  case "codex":
33040
- return path8.join(home, ".codex", "AGENTS.md");
33708
+ return path9.join(home, ".codex", "AGENTS.md");
33041
33709
  case "copilot":
33042
33710
  return null;
33043
33711
  case "opencode":
33044
- return path8.join(home, ".opencode", "AGENTS.md");
33712
+ return path9.join(home, ".opencode", "AGENTS.md");
33045
33713
  case "claude":
33046
- return path8.join(home, ".claude", "CLAUDE.md");
33714
+ return path9.join(home, ".claude", "CLAUDE.md");
33047
33715
  case "cline":
33048
- return path8.join(home, "Documents", "Cline", "Rules", "contextstream.md");
33716
+ return path9.join(home, "Documents", "Cline", "Rules", "contextstream.md");
33049
33717
  case "kilo":
33050
- return path8.join(home, ".kilocode", "rules", "contextstream.md");
33718
+ return path9.join(kiloConfigDir(), "rules", "contextstream.md");
33051
33719
  case "roo":
33052
- return path8.join(home, ".roo", "rules", "contextstream.md");
33720
+ return path9.join(home, ".roo", "rules", "contextstream.md");
33053
33721
  case "aider":
33054
- return path8.join(home, ".aider.conf.yml");
33722
+ return path9.join(home, ".aider.conf.yml");
33055
33723
  case "antigravity":
33056
- return path8.join(home, ".gemini", "GEMINI.md");
33724
+ return path9.join(home, ".gemini", "GEMINI.md");
33057
33725
  case "cursor":
33058
33726
  return null;
33059
33727
  case "windsurf":
33060
- return path8.join(home, ".codeium", "windsurf", "memories", "global_rules.md");
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 = homedir6();
33740
+ const home = homedir7();
33073
33741
  const envHome = process.env.CODEX_HOME;
33074
33742
  const candidates = [
33075
33743
  envHome,
33076
- path8.join(home, ".codex"),
33077
- path8.join(home, ".codex", "config.toml"),
33078
- path8.join(home, ".config", "codex")
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 = homedir6();
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 || path8.join(home, "AppData", "Roaming");
33087
- return path8.join(appData, "opencode", "opencode.json");
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 || path8.join(home, ".config");
33090
- return path8.join(configRoot, "opencode", "opencode.json");
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 = path8.dirname(configPath);
33095
- const home = homedir6();
33762
+ const configDir = path9.dirname(configPath);
33763
+ const home = homedir7();
33096
33764
  const candidates = [
33097
33765
  configDir,
33098
33766
  configPath,
33099
- path8.join(home, ".bun", "bin", "opencode"),
33100
- path8.join(home, ".local", "bin", "opencode")
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 = homedir6();
33106
- const candidates = [path8.join(home, ".claude"), path8.join(home, ".config", "claude")];
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(path8.join(home, "Library", "Application Support", "Claude"));
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(path8.join(appData, "Claude"));
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 = homedir6();
33786
+ const home = homedir7();
33119
33787
  const candidates = [
33120
- path8.join(home, "Documents", "Cline"),
33121
- path8.join(home, ".cline"),
33122
- path8.join(home, ".config", "cline")
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 = homedir6();
33795
+ const home = homedir7();
33128
33796
  const candidates = [
33129
- path8.join(home, ".copilot"),
33130
- path8.join(home, ".config", "github-copilot"),
33131
- path8.join(home, ".vscode", "extensions", "github.copilot-chat"),
33132
- path8.join(home, ".vscode", "extensions", "github.copilot")
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 = homedir6();
33138
- const candidates = [path8.join(home, ".kilocode"), path8.join(home, ".config", "kilocode")];
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 = homedir6();
33143
- const candidates = [path8.join(home, ".roo"), path8.join(home, ".config", "roo")];
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 = homedir6();
33148
- const candidates = [path8.join(home, ".aider.conf.yml"), path8.join(home, ".config", "aider")];
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 = homedir6();
33153
- const candidates = [path8.join(home, ".cursor")];
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(path8.join(home, "Applications", "Cursor.app"));
33157
- candidates.push(path8.join(home, "Library", "Application Support", "Cursor"));
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(path8.join(localApp, "Programs", "Cursor", "Cursor.exe"));
33163
- if (localApp) candidates.push(path8.join(localApp, "Cursor", "Cursor.exe"));
33164
- if (programFiles) candidates.push(path8.join(programFiles, "Cursor", "Cursor.exe"));
33165
- if (programFilesX86) candidates.push(path8.join(programFilesX86, "Cursor", "Cursor.exe"));
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 = homedir6();
33176
- const candidates = [path8.join(home, ".codeium", "windsurf")];
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(path8.join(home, "Applications", "Windsurf.app"));
33180
- candidates.push(path8.join(home, "Library", "Application Support", "Windsurf"));
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(path8.join(localApp, "Programs", "Windsurf", "Windsurf.exe"));
33186
- if (localApp) candidates.push(path8.join(localApp, "Windsurf", "Windsurf.exe"));
33187
- if (programFiles) candidates.push(path8.join(programFiles, "Windsurf", "Windsurf.exe"));
33188
- if (programFilesX86) candidates.push(path8.join(programFilesX86, "Windsurf", "Windsurf.exe"));
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 = homedir6();
33199
- const candidates = [path8.join(home, ".gemini")];
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(path8.join(home, "Applications", "Antigravity.app"));
33203
- candidates.push(path8.join(home, "Library", "Application Support", "Antigravity"));
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(path8.join(localApp, "Programs", "Antigravity", "Antigravity.exe"));
33210
- if (localApp) candidates.push(path8.join(localApp, "Antigravity", "Antigravity.exe"));
33211
- if (programFiles) candidates.push(path8.join(programFiles, "Antigravity", "Antigravity.exe"));
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(path8.join(programFilesX86, "Antigravity", "Antigravity.exe"));
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
- return {
33947
+ const env = {
33262
33948
  CONTEXTSTREAM_API_URL: params.apiUrl,
33263
- CONTEXTSTREAM_API_KEY: params.apiKey,
33264
- CONTEXTSTREAM_CONTEXT_PACK: contextPack
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 fs7.mkdir(path8.dirname(filePath), { recursive: true });
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 fs7.readFile(filePath, "utf8").catch(() => "");
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 fs7.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
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 fs7.mkdir(path8.dirname(filePath), { recursive: true });
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 fs7.readFile(filePath, "utf8").catch(() => "");
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 = server;
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 fs7.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
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 fs7.mkdir(path8.dirname(filePath), { recursive: true });
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 fs7.readFile(filePath, "utf8").catch(() => "");
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 fs7.writeFile(filePath, JSON.stringify(root, null, 2) + "\n", "utf8");
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 = homedir6();
34209
+ const home = homedir7();
33405
34210
  if (process.platform === "darwin") {
33406
- return path8.join(
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 || path8.join(home, "AppData", "Roaming");
33416
- return path8.join(appData, "Claude", "claude_desktop_config.json");
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 path8.join(home, ".config", "Claude", "claude_desktop_config.json");
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 fs7.mkdir(path8.dirname(filePath), { recursive: true });
34229
+ await fs8.mkdir(path9.dirname(filePath), { recursive: true });
33425
34230
  const exists = await fileExists(filePath);
33426
- const existing = exists ? await fs7.readFile(filePath, "utf8").catch(() => "") : "";
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 fs7.writeFile(filePath, block.trimStart(), "utf8");
34249
+ await fs8.writeFile(filePath, block.trimStart(), "utf8");
33445
34250
  return "created";
33446
34251
  }
33447
34252
  if (!existing.includes(marker)) {
33448
- await fs7.writeFile(filePath, existing.trimEnd() + block, "utf8");
34253
+ await fs8.writeFile(filePath, existing.trimEnd() + block, "utf8");
33449
34254
  return "updated";
33450
34255
  }
33451
34256
  if (!existing.includes(envMarker)) {
33452
- await fs7.writeFile(
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 fs7.writeFile(filePath, updated, "utf8");
34307
+ await fs8.writeFile(filePath, updated, "utf8");
33503
34308
  return "updated";
33504
34309
  }
33505
34310
  async function discoverProjectsUnderFolder(parentFolder) {
33506
- const entries = await fs7.readdir(parentFolder, { withFileTypes: true });
33507
- const candidates = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => path8.join(parentFolder, e.name));
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(path8.join(dir, ".git"));
33511
- const hasPkg = await fileExists(path8.join(dir, "package.json"));
33512
- const hasCargo = await fileExists(path8.join(dir, "Cargo.toml"));
33513
- const hasPyProject = await fileExists(path8.join(dir, "pyproject.toml"));
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 = path8.basename(cwd) || "project";
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 = path8.basename(projectPath);
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 mcpServer = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
34159
- const mcpServerClaude = buildContextStreamMcpServer({ apiUrl, apiKey, contextPackEnabled });
34160
- const mcpServerOpenCode = buildContextStreamOpenCodeLocalServer({
34963
+ const setupEnvBase = {
34161
34964
  apiUrl,
34162
34965
  apiKey,
34163
- contextPackEnabled
34164
- });
34165
- const vsCodeServer = buildContextStreamVsCodeServer({ apiUrl, apiKey, contextPackEnabled });
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 = path8.join(homedir6(), ".codex", "config.toml");
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 = path8.join(homedir6(), ".copilot", "mcp-config.json");
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 = path8.join(homedir6(), ".cursor", "mcp.json");
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 = path8.join(homedir6(), ".codeium", "windsurf", "mcp_config.json");
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" || editor === "roo") {
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: "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 ${path8.basename(script)}`);
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(path8.resolve(process.cwd()));
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(path8.resolve(p));
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 = path8.resolve(parent);
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
- ...path8.resolve(projectPath) === path8.resolve(process.cwd()) && selectedCurrentProject ? {
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: path8.join(projectPath, ".contextstream", "config.json"),
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: path8.join(projectPath, ".contextstream", "config.json"),
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 = path8.join(projectPath, ".cursor", "mcp.json");
34493
- const vscodePath = path8.join(projectPath, ".vscode", "mcp.json");
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, mcpServer);
34499
- const status2 = await upsertJsonVsCodeMcpConfig(vscodePath, vsCodeServer);
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 (${path8.join(homedir6(), ".codeium", "windsurf", "mcp_config.json")}).`
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 = path8.join(projectPath, ".mcp.json");
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, mcpServerClaude);
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 = path8.join(projectPath, "opencode.json");
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 status = await upsertOpenCodeMcpConfig(openCodePath, mcpServerOpenCode);
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 = path8.join(projectPath, ".vscode", "mcp.json");
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, vsCodeServer);
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 = path8.join(projectPath, ".kilocode", "mcp.json");
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 status = await upsertJsonMcpConfig(kiloPath, mcpServer);
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 = path8.join(projectPath, ".roo", "mcp.json");
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, mcpServer);
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: path8.basename(projectPath),
35416
+ projectName: path9.basename(projectPath),
34581
35417
  mode: getModeForEditor(editor)
34582
35418
  });
34583
35419
  if (!rule) continue;
34584
- const filePath = path8.join(projectPath, rule.filename);
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 = join21(homedir18(), ".contextstream");
34724
- const starShownFile = join21(configDir, ".star-shown");
34725
- if (existsSync15(starShownFile)) {
35559
+ const configDir = join22(homedir19(), ".contextstream");
35560
+ const starShownFile = join22(configDir, ".star-shown");
35561
+ if (existsSync16(starShownFile)) {
34726
35562
  return;
34727
35563
  }
34728
- if (!existsSync15(configDir)) {
35564
+ if (!existsSync16(configDir)) {
34729
35565
  try {
34730
- mkdirSync7(configDir, { recursive: true });
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
- writeFileSync8(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
35579
+ writeFileSync9(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
34744
35580
  } catch {
34745
35581
  }
34746
35582
  }