@contextstream/mcp-server 0.4.61 → 0.4.62

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
@@ -335,7 +335,7 @@ function detectUpdateMethod() {
335
335
  return "curl";
336
336
  }
337
337
  async function runUpdate(method) {
338
- return new Promise((resolve16, reject) => {
338
+ return new Promise((resolve18, reject) => {
339
339
  let command;
340
340
  let args;
341
341
  let shell;
@@ -366,13 +366,13 @@ async function runUpdate(method) {
366
366
  });
367
367
  proc.on("close", (code) => {
368
368
  if (code === 0) {
369
- resolve16();
369
+ resolve18();
370
370
  } else {
371
371
  reject(new Error(`Update process exited with code ${code}`));
372
372
  }
373
373
  });
374
374
  proc.unref();
375
- setTimeout(() => resolve16(), 1e3);
375
+ setTimeout(() => resolve18(), 1e3);
376
376
  });
377
377
  }
378
378
  function writeUpdateMarker(previousVersion, newVersion) {
@@ -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(path22, checkUnignored, mode) {
736
+ test(path23, 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(path22);
745
+ const matched = rule[mode].test(path23);
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 = (path22, originalPath, doThrow) => {
767
- if (!isString(path22)) {
766
+ var checkPath = (path23, originalPath, doThrow) => {
767
+ if (!isString(path23)) {
768
768
  return doThrow(
769
769
  `path must be a string, but got \`${originalPath}\``,
770
770
  TypeError
771
771
  );
772
772
  }
773
- if (!path22) {
773
+ if (!path23) {
774
774
  return doThrow(`path must not be empty`, TypeError);
775
775
  }
776
- if (checkPath.isNotRelative(path22)) {
776
+ if (checkPath.isNotRelative(path23)) {
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 = (path22) => REGEX_TEST_INVALID_PATH.test(path22);
785
+ var isNotRelative = (path23) => REGEX_TEST_INVALID_PATH.test(path23);
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 path22 = originalPath && checkPath.convert(originalPath);
815
+ const path23 = originalPath && checkPath.convert(originalPath);
816
816
  checkPath(
817
- path22,
817
+ path23,
818
818
  originalPath,
819
819
  this._strictPathCheck ? throwError : RETURN_FALSE
820
820
  );
821
- return this._t(path22, cache, checkUnignored, slices);
821
+ return this._t(path23, cache, checkUnignored, slices);
822
822
  }
823
- checkIgnore(path22) {
824
- if (!REGEX_TEST_TRAILING_SLASH.test(path22)) {
825
- return this.test(path22);
823
+ checkIgnore(path23) {
824
+ if (!REGEX_TEST_TRAILING_SLASH.test(path23)) {
825
+ return this.test(path23);
826
826
  }
827
- const slices = path22.split(SLASH).filter(Boolean);
827
+ const slices = path23.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(path22, false, MODE_CHECK_IGNORE);
840
+ return this._rules.test(path23, false, MODE_CHECK_IGNORE);
841
841
  }
842
- _t(path22, cache, checkUnignored, slices) {
843
- if (path22 in cache) {
844
- return cache[path22];
842
+ _t(path23, cache, checkUnignored, slices) {
843
+ if (path23 in cache) {
844
+ return cache[path23];
845
845
  }
846
846
  if (!slices) {
847
- slices = path22.split(SLASH).filter(Boolean);
847
+ slices = path23.split(SLASH).filter(Boolean);
848
848
  }
849
849
  slices.pop();
850
850
  if (!slices.length) {
851
- return cache[path22] = this._rules.test(path22, checkUnignored, MODE_IGNORE);
851
+ return cache[path23] = this._rules.test(path23, 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[path22] = parent.ignored ? parent : this._rules.test(path22, checkUnignored, MODE_IGNORE);
859
+ return cache[path23] = parent.ignored ? parent : this._rules.test(path23, checkUnignored, MODE_IGNORE);
860
860
  }
861
- ignores(path22) {
862
- return this._test(path22, this._ignoreCache, false).ignored;
861
+ ignores(path23) {
862
+ return this._test(path23, this._ignoreCache, false).ignored;
863
863
  }
864
864
  createFilter() {
865
- return (path22) => !this.ignores(path22);
865
+ return (path23) => !this.ignores(path23);
866
866
  }
867
867
  filter(paths) {
868
868
  return makeArray(paths).filter(this.createFilter());
869
869
  }
870
870
  // @returns {TestResult}
871
- test(path22) {
872
- return this._test(path22, this._testCache, true);
871
+ test(path23) {
872
+ return this._test(path23, this._testCache, true);
873
873
  }
874
874
  };
875
875
  var factory = (options) => new Ignore2(options);
876
- var isPathValid = (path22) => checkPath(path22 && checkPath.convert(path22), path22, RETURN_FALSE);
876
+ var isPathValid = (path23) => checkPath(path23 && checkPath.convert(path23), path23, 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 = (path22) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path22) || isNotRelative(path22);
881
+ checkPath.isNotRelative = (path23) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path23) || isNotRelative(path23);
882
882
  };
883
883
  if (
884
884
  // Detect `process` so that it can run in browsers.
@@ -1549,7 +1549,7 @@ var init_files = __esm({
1549
1549
  "Gemfile.lock",
1550
1550
  "composer.lock"
1551
1551
  ]);
1552
- MAX_FILE_SIZE = 1024 * 1024;
1552
+ MAX_FILE_SIZE = 5 * 1024 * 1024;
1553
1553
  MAX_BATCH_BYTES = 10 * 1024 * 1024;
1554
1554
  LARGE_FILE_THRESHOLD = 2 * 1024 * 1024;
1555
1555
  MAX_FILES_PER_BATCH = 200;
@@ -3280,7 +3280,7 @@ var init_rules_templates = __esm({
3280
3280
  | Message | Required |
3281
3281
  |---------|----------|
3282
3282
  | **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
3283
- | **Every message** | \`context(user_message="...")\` FIRST |
3283
+ | **Subsequent messages (default)** | \`context(user_message="...")\` FIRST (narrow read-only bypass when context is fresh and no state-changing tool has run) |
3284
3284
  | **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read |
3285
3285
  </contextstream_rules>
3286
3286
 
@@ -3294,27 +3294,25 @@ v${RULES_VERSION}
3294
3294
  `.trim();
3295
3295
  CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
3296
3296
  CONTEXTSTREAM_RULES_FULL = `
3297
- ## \u{1F6A8} MANDATORY: CALL CONTEXT EVERY MESSAGE \u{1F6A8}
3297
+ ## \u{1F6A8} MANDATORY STARTUP: CONTEXT-FIRST FLOW \u{1F6A8}
3298
3298
 
3299
3299
  <contextstream_rules>
3300
- **EVERY response MUST start with \`context(user_message="...")\`**
3301
-
3302
3300
  | Message | What to Call |
3303
3301
  |---------|--------------|
3304
- | 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
3305
- | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
3306
-
3307
- **BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="auto")\` FIRST
3302
+ | **First message in session** | \`init()\` \u2192 \`context(user_message="<msg>")\` BEFORE any other tool |
3303
+ | **Subsequent messages (default)** | \`context(user_message="<msg>")\` FIRST, then other tools |
3304
+ | **Narrow bypass** | Immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run |
3305
+ | **Before Glob/Grep/Read/Search** | \`search(mode="auto", query="...")\` FIRST |
3308
3306
  </contextstream_rules>
3309
3307
 
3310
- **This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
3308
+ Use \`context()\` by default to get task-specific rules, lessons from past mistakes, and relevant decisions.
3311
3309
 
3312
3310
  ---
3313
3311
 
3314
- ## Why \`context()\` is MANDATORY Every Message
3312
+ ## Why Default Context-First
3315
3313
 
3316
- \u274C **WRONG:** "I already called init, I don't need context"
3317
- \u2705 **CORRECT:** \`context()\` is required EVERY message, not just the first
3314
+ \u274C **Wrong:** "I already called init, so I can skip context for everything"
3315
+ \u2705 **Correct:** \`context()\` is the default first call for subsequent messages, with a narrow read-only bypass when context is still fresh and state is unchanged
3318
3316
 
3319
3317
  **What you lose without \`context()\`:**
3320
3318
  - Dynamic rules matched to your current task
@@ -3322,7 +3320,7 @@ v${RULES_VERSION}
3322
3320
  - Semantically relevant decisions and context
3323
3321
  - Warnings about risky operations
3324
3322
 
3325
- **\`init()\` gives recent items by TIME. \`context()\` finds items RELEVANT to THIS message.**
3323
+ **\`init()\` returns recent items by time. \`context()\` finds items semantically relevant to this message.**
3326
3324
 
3327
3325
  ---
3328
3326
 
@@ -3828,9 +3826,10 @@ See full documentation: https://contextstream.io/docs/mcp/tools
3828
3826
  ## \u{1F6A8} CRITICAL: These Rules Are Non-Negotiable \u{1F6A8}
3829
3827
 
3830
3828
  <contextstream_protocol>
3831
- **EVERY coding response MUST start with:**
3832
- 1. \`init()\` (1st message only) \u2192 then \`context(user_message="<msg>")\`
3833
- 2. \`context(user_message="<msg>")\` (EVERY subsequent message)
3829
+ **MANDATORY STARTUP:**
3830
+ 1. First message in session: \`init()\` \u2192 then \`context(user_message="<msg>")\`
3831
+ 2. Subsequent messages (default): \`context(user_message="<msg>")\` FIRST
3832
+ 3. Narrow bypass: immediate read-only ContextStream calls are allowed only when prior context is fresh and no state-changing tool has run
3834
3833
 
3835
3834
  **BEFORE Glob/Grep/Read/Search/Explore:**
3836
3835
  \u2192 \`search(mode="auto", query="...")\` FIRST \u2014 local tools ONLY if 0 results
@@ -3860,8 +3859,9 @@ Rules Version: ${RULES_VERSION}
3860
3859
 
3861
3860
  | When | Call |
3862
3861
  |------|------|
3863
- | 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
3864
- | Every message after | \`context(user_message="...")\` |
3862
+ | 1st message in session | \`init()\` \u2192 \`context(user_message="...")\` |
3863
+ | Subsequent messages (default) | \`context(user_message="...")\` first |
3864
+ | Narrow bypass | Immediate read-only ContextStream calls when context is fresh and no state-changing tool has run |
3865
3865
  | Before ANY file discovery | \`search(mode="auto", query="...")\` |
3866
3866
  | On \`<system-reminder>\` | **Follow instructions inside** |
3867
3867
  | Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
@@ -3884,12 +3884,10 @@ Rules Version: ${RULES_VERSION}
3884
3884
  - **[RULES_NOTICE]** \u2192 Run \`generate_rules()\`
3885
3885
  - **[VERSION_NOTICE]** \u2192 Tell user to update MCP
3886
3886
 
3887
- ## Fast Path (Simple Utilities Only)
3888
-
3889
- Skip init/context ONLY for: "list workspaces", "show version", "list reminders"
3890
- \u2192 Just call: \`workspace(action="list")\`, \`help(action="version")\`, etc.
3887
+ ## Read-Only Examples
3891
3888
 
3892
- Everything else = full protocol (init \u2192 context \u2192 search \u2192 work)
3889
+ Default behavior is context-first. Narrow bypass applies only for immediate read-only ContextStream calls when context is fresh and state is unchanged.
3890
+ Examples: \`workspace(action="list"|"get")\`, \`help(action="version"|"tools"|"auth")\`, \`project(action="index_status")\`.
3893
3891
 
3894
3892
  ### Lessons (Past Mistakes)
3895
3893
 
@@ -3978,7 +3976,7 @@ You MUST follow these rules manually - there is no automatic enforcement.
3978
3976
  - Check for [RULES_NOTICE] - update rules if needed
3979
3977
  - **save_exchange=true** saves each conversation turn for later retrieval
3980
3978
 
3981
- 4. **NEVER skip init/context** - you will miss critical context
3979
+ 4. **Default behavior:** call \`context(...)\` first on each message. Narrow bypass is allowed only for immediate read-only ContextStream calls when previous context is still fresh and no state-changing tool has run.
3982
3980
 
3983
3981
  ---
3984
3982
 
@@ -3987,7 +3985,7 @@ You MUST follow these rules manually - there is no automatic enforcement.
3987
3985
  **This editor does NOT have hooks to auto-save transcripts.**
3988
3986
  You MUST save each conversation turn manually:
3989
3987
 
3990
- ### On EVERY message (including the first):
3988
+ ### On MOST messages (including the first):
3991
3989
  \`\`\`
3992
3990
  context(user_message="<user's message>", save_exchange=true, session_id="<session-id>")
3993
3991
  \`\`\`
@@ -4062,6 +4060,28 @@ search(mode="auto", query="what you're looking for")
4062
4060
  **IF ContextStream search returns 0 results or errors:**
4063
4061
  \u2192 Use local tools (Glob/Grep/Read) as fallback
4064
4062
 
4063
+ ### Choose Search Mode Intelligently:
4064
+ - \`auto\` (recommended): query-aware mode selection
4065
+ - \`hybrid\`: mixed semantic + keyword retrieval for broad discovery
4066
+ - \`semantic\`: conceptual questions ("how does X work?")
4067
+ - \`keyword\`: exact text / quoted string
4068
+ - \`pattern\`: glob or regex (\`*.ts\`, \`foo\\s+bar\`)
4069
+ - \`refactor\`: symbol usage / rename-safe lookup
4070
+ - \`exhaustive\`: all occurrences / complete match coverage
4071
+ - \`team\`: cross-project team search
4072
+
4073
+ ### Output Format Hints:
4074
+ - Use \`output_format="paths"\` for file listings and rename targets
4075
+ - Use \`output_format="count"\` for "how many" queries
4076
+
4077
+ ### Two-Phase Search Pattern (for precision):
4078
+ - Pass 1 (discovery): \`search(mode="auto", query="<concept + module>", output_format="paths", limit=10)\`
4079
+ - Pass 2 (precision): use one of:
4080
+ - exact text/symbol: \`search(mode="keyword", query="\\"exact_text\\"", include_content=true)\`
4081
+ - symbol usage: \`search(mode="refactor", query="SymbolName", output_format="paths")\`
4082
+ - all occurrences: \`search(mode="exhaustive", query="symbol_or_text")\`
4083
+ - Then use local Read/Grep only on paths returned by ContextStream.
4084
+
4065
4085
  ### When Local Tools Are OK:
4066
4086
  \u2705 Project is not indexed
4067
4087
  \u2705 Index is stale/outdated (>7 days old)
@@ -4559,7 +4579,7 @@ var init_post_write = __esm({
4559
4579
  ".prisma",
4560
4580
  ".proto"
4561
4581
  ]);
4562
- MAX_FILE_SIZE2 = 1024 * 1024;
4582
+ MAX_FILE_SIZE2 = 5 * 1024 * 1024;
4563
4583
  isDirectRun = process.argv[1]?.includes("post-write") || process.argv[2] === "post-write";
4564
4584
  if (isDirectRun) {
4565
4585
  runPostWriteHook().catch(() => process.exit(0));
@@ -4567,14 +4587,181 @@ var init_post_write = __esm({
4567
4587
  }
4568
4588
  });
4569
4589
 
4590
+ // src/hooks/prompt-state.ts
4591
+ import * as fs9 from "node:fs";
4592
+ import * as path10 from "node:path";
4593
+ import { homedir as homedir8 } from "node:os";
4594
+ function defaultState() {
4595
+ return { workspaces: {} };
4596
+ }
4597
+ function nowIso() {
4598
+ return (/* @__PURE__ */ new Date()).toISOString();
4599
+ }
4600
+ function ensureStateDir() {
4601
+ try {
4602
+ fs9.mkdirSync(path10.dirname(STATE_PATH), { recursive: true });
4603
+ } catch {
4604
+ }
4605
+ }
4606
+ function normalizePath(input) {
4607
+ try {
4608
+ return path10.resolve(input);
4609
+ } catch {
4610
+ return input;
4611
+ }
4612
+ }
4613
+ function workspacePathsMatch(a, b) {
4614
+ const left = normalizePath(a);
4615
+ const right = normalizePath(b);
4616
+ return left === right || left.startsWith(`${right}${path10.sep}`) || right.startsWith(`${left}${path10.sep}`);
4617
+ }
4618
+ function readState() {
4619
+ try {
4620
+ const content = fs9.readFileSync(STATE_PATH, "utf8");
4621
+ const parsed = JSON.parse(content);
4622
+ if (!parsed || typeof parsed !== "object" || !parsed.workspaces) {
4623
+ return defaultState();
4624
+ }
4625
+ return parsed;
4626
+ } catch {
4627
+ return defaultState();
4628
+ }
4629
+ }
4630
+ function writeState(state) {
4631
+ try {
4632
+ ensureStateDir();
4633
+ fs9.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
4634
+ } catch {
4635
+ }
4636
+ }
4637
+ function getOrCreateEntry(state, cwd) {
4638
+ if (!cwd.trim()) return null;
4639
+ const exact = state.workspaces[cwd];
4640
+ if (exact) return { key: cwd, entry: exact };
4641
+ for (const [trackedCwd, trackedEntry] of Object.entries(state.workspaces)) {
4642
+ if (workspacePathsMatch(trackedCwd, cwd)) {
4643
+ return { key: trackedCwd, entry: trackedEntry };
4644
+ }
4645
+ }
4646
+ const created = {
4647
+ require_context: false,
4648
+ require_init: false,
4649
+ last_context_at: void 0,
4650
+ last_state_change_at: void 0,
4651
+ updated_at: nowIso()
4652
+ };
4653
+ state.workspaces[cwd] = created;
4654
+ return { key: cwd, entry: created };
4655
+ }
4656
+ function cleanupStale(maxAgeSeconds) {
4657
+ const state = readState();
4658
+ const now = Date.now();
4659
+ let changed = false;
4660
+ for (const [cwd, entry] of Object.entries(state.workspaces)) {
4661
+ const updated = new Date(entry.updated_at);
4662
+ if (Number.isNaN(updated.getTime())) continue;
4663
+ const ageSeconds = (now - updated.getTime()) / 1e3;
4664
+ if (ageSeconds > maxAgeSeconds) {
4665
+ delete state.workspaces[cwd];
4666
+ changed = true;
4667
+ }
4668
+ }
4669
+ if (changed) {
4670
+ writeState(state);
4671
+ }
4672
+ }
4673
+ function markContextRequired(cwd) {
4674
+ if (!cwd.trim()) return;
4675
+ const state = readState();
4676
+ const target = getOrCreateEntry(state, cwd);
4677
+ if (!target) return;
4678
+ target.entry.require_context = true;
4679
+ target.entry.updated_at = nowIso();
4680
+ writeState(state);
4681
+ }
4682
+ function clearContextRequired(cwd) {
4683
+ if (!cwd.trim()) return;
4684
+ const state = readState();
4685
+ const target = getOrCreateEntry(state, cwd);
4686
+ if (!target) return;
4687
+ target.entry.require_context = false;
4688
+ target.entry.last_context_at = nowIso();
4689
+ target.entry.updated_at = nowIso();
4690
+ writeState(state);
4691
+ }
4692
+ function isContextRequired(cwd) {
4693
+ if (!cwd.trim()) return false;
4694
+ const state = readState();
4695
+ const target = getOrCreateEntry(state, cwd);
4696
+ return Boolean(target?.entry.require_context);
4697
+ }
4698
+ function markInitRequired(cwd) {
4699
+ if (!cwd.trim()) return;
4700
+ const state = readState();
4701
+ const target = getOrCreateEntry(state, cwd);
4702
+ if (!target) return;
4703
+ target.entry.require_init = true;
4704
+ target.entry.updated_at = nowIso();
4705
+ writeState(state);
4706
+ }
4707
+ function clearInitRequired(cwd) {
4708
+ if (!cwd.trim()) return;
4709
+ const state = readState();
4710
+ const target = getOrCreateEntry(state, cwd);
4711
+ if (!target) return;
4712
+ target.entry.require_init = false;
4713
+ target.entry.updated_at = nowIso();
4714
+ writeState(state);
4715
+ }
4716
+ function isInitRequired(cwd) {
4717
+ if (!cwd.trim()) return false;
4718
+ const state = readState();
4719
+ const target = getOrCreateEntry(state, cwd);
4720
+ return Boolean(target?.entry.require_init);
4721
+ }
4722
+ function markStateChanged(cwd) {
4723
+ if (!cwd.trim()) return;
4724
+ const state = readState();
4725
+ const target = getOrCreateEntry(state, cwd);
4726
+ if (!target) return;
4727
+ target.entry.last_state_change_at = nowIso();
4728
+ target.entry.updated_at = nowIso();
4729
+ writeState(state);
4730
+ }
4731
+ function isContextFreshAndClean(cwd, maxAgeSeconds) {
4732
+ if (!cwd.trim()) return false;
4733
+ const state = readState();
4734
+ const target = getOrCreateEntry(state, cwd);
4735
+ const entry = target?.entry;
4736
+ if (!entry?.last_context_at) return false;
4737
+ const contextAt = new Date(entry.last_context_at);
4738
+ if (Number.isNaN(contextAt.getTime())) return false;
4739
+ const ageSeconds = (Date.now() - contextAt.getTime()) / 1e3;
4740
+ if (ageSeconds < 0 || ageSeconds > maxAgeSeconds) return false;
4741
+ if (entry.last_state_change_at) {
4742
+ const changedAt = new Date(entry.last_state_change_at);
4743
+ if (!Number.isNaN(changedAt.getTime()) && changedAt.getTime() > contextAt.getTime()) {
4744
+ return false;
4745
+ }
4746
+ }
4747
+ return true;
4748
+ }
4749
+ var STATE_PATH;
4750
+ var init_prompt_state = __esm({
4751
+ "src/hooks/prompt-state.ts"() {
4752
+ "use strict";
4753
+ STATE_PATH = path10.join(homedir8(), ".contextstream", "prompt-state.json");
4754
+ }
4755
+ });
4756
+
4570
4757
  // src/hooks/pre-tool-use.ts
4571
4758
  var pre_tool_use_exports = {};
4572
4759
  __export(pre_tool_use_exports, {
4573
4760
  runPreToolUseHook: () => runPreToolUseHook
4574
4761
  });
4575
- import * as fs9 from "node:fs";
4576
- import * as path10 from "node:path";
4577
- import { homedir as homedir8 } from "node:os";
4762
+ import * as fs10 from "node:fs";
4763
+ import * as path11 from "node:path";
4764
+ import { homedir as homedir9 } from "node:os";
4578
4765
  function isDiscoveryGlob(pattern) {
4579
4766
  const patternLower = pattern.toLowerCase();
4580
4767
  for (const p of DISCOVERY_PATTERNS) {
@@ -4600,22 +4787,22 @@ function isDiscoveryGrep(filePath) {
4600
4787
  return false;
4601
4788
  }
4602
4789
  function isProjectIndexed(cwd) {
4603
- if (!fs9.existsSync(INDEX_STATUS_FILE)) {
4790
+ if (!fs10.existsSync(INDEX_STATUS_FILE)) {
4604
4791
  return { isIndexed: false, isStale: false };
4605
4792
  }
4606
4793
  let data;
4607
4794
  try {
4608
- const content = fs9.readFileSync(INDEX_STATUS_FILE, "utf-8");
4795
+ const content = fs10.readFileSync(INDEX_STATUS_FILE, "utf-8");
4609
4796
  data = JSON.parse(content);
4610
4797
  } catch {
4611
4798
  return { isIndexed: false, isStale: false };
4612
4799
  }
4613
4800
  const projects = data.projects || {};
4614
- const cwdPath = path10.resolve(cwd);
4801
+ const cwdPath = path11.resolve(cwd);
4615
4802
  for (const [projectPath, info] of Object.entries(projects)) {
4616
4803
  try {
4617
- const indexedPath = path10.resolve(projectPath);
4618
- if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath + path10.sep)) {
4804
+ const indexedPath = path11.resolve(projectPath);
4805
+ if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath + path11.sep)) {
4619
4806
  const indexedAt = info.indexed_at;
4620
4807
  if (indexedAt) {
4621
4808
  try {
@@ -4648,6 +4835,97 @@ function extractToolName(input) {
4648
4835
  function extractToolInput(input) {
4649
4836
  return input.tool_input || input.parameters || input.toolParameters || {};
4650
4837
  }
4838
+ function normalizeContextstreamToolName(toolName) {
4839
+ const trimmed = toolName.trim();
4840
+ if (!trimmed) return null;
4841
+ const lower = trimmed.toLowerCase();
4842
+ const prefixed = "mcp__contextstream__";
4843
+ if (lower.startsWith(prefixed)) {
4844
+ return lower.slice(prefixed.length);
4845
+ }
4846
+ if (lower.startsWith("contextstream__")) {
4847
+ return lower.slice("contextstream__".length);
4848
+ }
4849
+ if (lower === "init" || lower === "context") {
4850
+ return lower;
4851
+ }
4852
+ return null;
4853
+ }
4854
+ function actionFromToolInput(toolInput) {
4855
+ const maybeAction = toolInput?.action;
4856
+ return typeof maybeAction === "string" ? maybeAction.trim().toLowerCase() : "";
4857
+ }
4858
+ function isContextstreamReadOnlyOperation(toolName, toolInput) {
4859
+ const action = actionFromToolInput(toolInput);
4860
+ switch (toolName) {
4861
+ case "workspace":
4862
+ return action === "list" || action === "get";
4863
+ case "memory":
4864
+ return action === "list_docs" || action === "list_events" || action === "list_todos" || action === "list_tasks" || action === "list_transcripts" || action === "list_nodes" || action === "decisions" || action === "get_doc" || action === "get_event" || action === "get_task" || action === "get_todo" || action === "get_transcript";
4865
+ case "session":
4866
+ return action === "get_lessons" || action === "get_plan" || action === "list_plans" || action === "recall";
4867
+ case "help":
4868
+ return action === "version" || action === "tools" || action === "auth";
4869
+ case "project":
4870
+ return action === "list" || action === "get" || action === "index_status";
4871
+ case "reminder":
4872
+ return action === "list" || action === "active";
4873
+ case "context":
4874
+ case "init":
4875
+ return true;
4876
+ default:
4877
+ return false;
4878
+ }
4879
+ }
4880
+ function isLikelyStateChangingTool(toolLower, toolInput, isContextstreamCall, normalizedContextstreamTool) {
4881
+ if (isContextstreamCall && normalizedContextstreamTool) {
4882
+ return !isContextstreamReadOnlyOperation(normalizedContextstreamTool, toolInput);
4883
+ }
4884
+ if ([
4885
+ "read",
4886
+ "read_file",
4887
+ "grep",
4888
+ "glob",
4889
+ "search",
4890
+ "grep_search",
4891
+ "code_search",
4892
+ "semanticsearch",
4893
+ "codebase_search",
4894
+ "list_files",
4895
+ "search_files",
4896
+ "search_files_content",
4897
+ "find_files",
4898
+ "find_by_name",
4899
+ "ls",
4900
+ "cat",
4901
+ "view"
4902
+ ].includes(toolLower)) {
4903
+ return false;
4904
+ }
4905
+ const writeMarkers = [
4906
+ "write",
4907
+ "edit",
4908
+ "create",
4909
+ "delete",
4910
+ "remove",
4911
+ "rename",
4912
+ "move",
4913
+ "patch",
4914
+ "apply",
4915
+ "insert",
4916
+ "append",
4917
+ "replace",
4918
+ "update",
4919
+ "commit",
4920
+ "push",
4921
+ "install",
4922
+ "exec",
4923
+ "run",
4924
+ "bash",
4925
+ "shell"
4926
+ ];
4927
+ return writeMarkers.some((marker) => toolLower.includes(marker));
4928
+ }
4651
4929
  function blockClaudeCode(message) {
4652
4930
  const response = {
4653
4931
  hookSpecificOutput: {
@@ -4656,7 +4934,7 @@ function blockClaudeCode(message) {
4656
4934
  additionalContext: `[CONTEXTSTREAM] ${message}`
4657
4935
  }
4658
4936
  };
4659
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] REDIRECT (additionalContext): ${JSON.stringify(response)}
4937
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] REDIRECT (additionalContext): ${JSON.stringify(response)}
4660
4938
  `);
4661
4939
  console.log(JSON.stringify(response));
4662
4940
  process.exit(0);
@@ -4684,6 +4962,25 @@ function outputCursorAllow() {
4684
4962
  console.log(JSON.stringify({ decision: "allow" }));
4685
4963
  process.exit(0);
4686
4964
  }
4965
+ function blockWithMessage(editorFormat, message) {
4966
+ if (editorFormat === "cline") {
4967
+ outputClineBlock(message, "[CONTEXTSTREAM] Follow ContextStream startup requirements.");
4968
+ } else if (editorFormat === "cursor") {
4969
+ outputCursorBlock(message);
4970
+ }
4971
+ blockClaudeCode(message);
4972
+ }
4973
+ function allowTool(editorFormat, cwd, recordStateChange) {
4974
+ if (recordStateChange) {
4975
+ markStateChanged(cwd);
4976
+ }
4977
+ if (editorFormat === "cline") {
4978
+ outputClineAllow();
4979
+ } else if (editorFormat === "cursor") {
4980
+ outputCursorAllow();
4981
+ }
4982
+ process.exit(0);
4983
+ }
4687
4984
  function detectEditorFormat(input) {
4688
4985
  if (input.hookName !== void 0 || input.toolName !== void 0) {
4689
4986
  return "cline";
@@ -4694,11 +4991,11 @@ function detectEditorFormat(input) {
4694
4991
  return "claude";
4695
4992
  }
4696
4993
  async function runPreToolUseHook() {
4697
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Hook invoked at ${(/* @__PURE__ */ new Date()).toISOString()}
4994
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Hook invoked at ${(/* @__PURE__ */ new Date()).toISOString()}
4698
4995
  `);
4699
4996
  console.error("[PreToolUse] Hook invoked at", (/* @__PURE__ */ new Date()).toISOString());
4700
4997
  if (!ENABLED2) {
4701
- fs9.appendFileSync(DEBUG_FILE, "[PreToolUse] Hook disabled, exiting\n");
4998
+ fs10.appendFileSync(DEBUG_FILE, "[PreToolUse] Hook disabled, exiting\n");
4702
4999
  console.error("[PreToolUse] Hook disabled, exiting");
4703
5000
  process.exit(0);
4704
5001
  }
@@ -4719,28 +5016,52 @@ async function runPreToolUseHook() {
4719
5016
  const cwd = extractCwd2(input);
4720
5017
  const tool = extractToolName(input);
4721
5018
  const toolInput = extractToolInput(input);
4722
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] tool=${tool}, cwd=${cwd}, editorFormat=${editorFormat}
5019
+ const toolLower = tool.toLowerCase();
5020
+ const normalizedContextstreamTool = normalizeContextstreamToolName(tool);
5021
+ const isContextstreamCall = normalizedContextstreamTool !== null;
5022
+ const recordStateChange = isLikelyStateChangingTool(
5023
+ toolLower,
5024
+ toolInput,
5025
+ isContextstreamCall,
5026
+ normalizedContextstreamTool
5027
+ );
5028
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] tool=${tool}, cwd=${cwd}, editorFormat=${editorFormat}
4723
5029
  `);
5030
+ cleanupStale(180);
5031
+ if (isInitRequired(cwd)) {
5032
+ if (isContextstreamCall && normalizedContextstreamTool === "init") {
5033
+ clearInitRequired(cwd);
5034
+ } else {
5035
+ const required = "mcp__contextstream__init(...)";
5036
+ const msg = `First call required for this session: ${required}. Run it before any other MCP tool. Then call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>").`;
5037
+ blockWithMessage(editorFormat, msg);
5038
+ }
5039
+ }
5040
+ if (isContextRequired(cwd)) {
5041
+ if (isContextstreamCall && normalizedContextstreamTool === "context") {
5042
+ clearContextRequired(cwd);
5043
+ } else if (isContextstreamCall && normalizedContextstreamTool === "init") {
5044
+ } else if (isContextstreamCall && normalizedContextstreamTool && isContextstreamReadOnlyOperation(normalizedContextstreamTool, toolInput) && isContextFreshAndClean(cwd, CONTEXT_FRESHNESS_SECONDS)) {
5045
+ } else {
5046
+ const msg = 'First call required for this prompt: mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>"). Run it before any other MCP tool.';
5047
+ blockWithMessage(editorFormat, msg);
5048
+ }
5049
+ }
4724
5050
  const { isIndexed } = isProjectIndexed(cwd);
4725
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}
5051
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}
4726
5052
  `);
4727
5053
  if (!isIndexed) {
4728
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Project not indexed, allowing
5054
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Project not indexed, allowing
4729
5055
  `);
4730
- if (editorFormat === "cline") {
4731
- outputClineAllow();
4732
- } else if (editorFormat === "cursor") {
4733
- outputCursorAllow();
4734
- }
4735
- process.exit(0);
5056
+ allowTool(editorFormat, cwd, recordStateChange);
4736
5057
  }
4737
5058
  if (tool === "Glob") {
4738
5059
  const pattern = toolInput?.pattern || "";
4739
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
5060
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
4740
5061
  `);
4741
5062
  if (isDiscoveryGlob(pattern)) {
4742
- const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
4743
- fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
5063
+ const msg = `This project index is current. Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob for faster, richer code results.`;
5064
+ fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
4744
5065
  `);
4745
5066
  if (editorFormat === "cline") {
4746
5067
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
@@ -4762,7 +5083,7 @@ async function runPreToolUseHook() {
4762
5083
  }
4763
5084
  blockClaudeCode(msg);
4764
5085
  } else {
4765
- const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
5086
+ const msg = `This project index is current. Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool} for faster, richer code results.`;
4766
5087
  if (editorFormat === "cline") {
4767
5088
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4768
5089
  } else if (editorFormat === "cursor") {
@@ -4774,7 +5095,7 @@ async function runPreToolUseHook() {
4774
5095
  } else if (tool === "Task") {
4775
5096
  const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
4776
5097
  if (subagentType === "explore") {
4777
- const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
5098
+ const msg = 'Project index is current. Use mcp__contextstream__search(mode="auto") instead of Task(Explore) for broad discovery.';
4778
5099
  if (editorFormat === "cline") {
4779
5100
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4780
5101
  } else if (editorFormat === "cursor") {
@@ -4783,7 +5104,7 @@ async function runPreToolUseHook() {
4783
5104
  blockClaudeCode(msg);
4784
5105
  }
4785
5106
  if (subagentType === "plan") {
4786
- const msg = 'STOP: Use mcp__contextstream__session(action="capture_plan") for planning. ContextStream plans persist across sessions.';
5107
+ const msg = 'After your plan is ready, save it with mcp__contextstream__session(action="capture_plan"). Then create tasks with mcp__contextstream__memory(action="create_task", title="...", plan_id="...").';
4787
5108
  if (editorFormat === "cline") {
4788
5109
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream plans for persistence.");
4789
5110
  } else if (editorFormat === "cursor") {
@@ -4792,7 +5113,7 @@ async function runPreToolUseHook() {
4792
5113
  blockClaudeCode(msg);
4793
5114
  }
4794
5115
  } else if (tool === "EnterPlanMode") {
4795
- const msg = 'STOP: Use mcp__contextstream__session(action="capture_plan", title="...", steps=[...]) instead of EnterPlanMode. ContextStream plans persist across sessions and are searchable.';
5116
+ const msg = 'After finalizing your plan, save it to ContextStream (not a local markdown file): mcp__contextstream__session(action="capture_plan", title="...", steps=[...]). Then create tasks with mcp__contextstream__memory(action="create_task", title="...", plan_id="...").';
4796
5117
  if (editorFormat === "cline") {
4797
5118
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream plans for persistence.");
4798
5119
  } else if (editorFormat === "cursor") {
@@ -4803,29 +5124,27 @@ async function runPreToolUseHook() {
4803
5124
  if (tool === "list_files" || tool === "search_files") {
4804
5125
  const pattern = toolInput?.path || toolInput?.regex || "";
4805
5126
  if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
4806
- const msg = `Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
5127
+ const msg = `Project index is current. Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool} for faster, richer code results.`;
4807
5128
  if (editorFormat === "cline") {
4808
5129
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4809
5130
  } else if (editorFormat === "cursor") {
4810
5131
  outputCursorBlock(msg);
4811
5132
  }
5133
+ blockClaudeCode(msg);
4812
5134
  }
4813
5135
  }
4814
- if (editorFormat === "cline") {
4815
- outputClineAllow();
4816
- } else if (editorFormat === "cursor") {
4817
- outputCursorAllow();
4818
- }
4819
- process.exit(0);
5136
+ allowTool(editorFormat, cwd, recordStateChange);
4820
5137
  }
4821
- var ENABLED2, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, DISCOVERY_PATTERNS, isDirectRun2;
5138
+ var ENABLED2, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, CONTEXT_FRESHNESS_SECONDS, DISCOVERY_PATTERNS, isDirectRun2;
4822
5139
  var init_pre_tool_use = __esm({
4823
5140
  "src/hooks/pre-tool-use.ts"() {
4824
5141
  "use strict";
5142
+ init_prompt_state();
4825
5143
  ENABLED2 = process.env.CONTEXTSTREAM_HOOK_ENABLED !== "false";
4826
- INDEX_STATUS_FILE = path10.join(homedir8(), ".contextstream", "indexed-projects.json");
5144
+ INDEX_STATUS_FILE = path11.join(homedir9(), ".contextstream", "indexed-projects.json");
4827
5145
  DEBUG_FILE = "/tmp/pretooluse-hook-debug.log";
4828
5146
  STALE_THRESHOLD_DAYS = 7;
5147
+ CONTEXT_FRESHNESS_SECONDS = 120;
4829
5148
  DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"];
4830
5149
  isDirectRun2 = process.argv[1]?.includes("pre-tool-use") || process.argv[2] === "pre-tool-use";
4831
5150
  if (isDirectRun2) {
@@ -4839,17 +5158,17 @@ var user_prompt_submit_exports = {};
4839
5158
  __export(user_prompt_submit_exports, {
4840
5159
  runUserPromptSubmitHook: () => runUserPromptSubmitHook
4841
5160
  });
4842
- import * as fs10 from "node:fs";
4843
- import * as path11 from "node:path";
4844
- import { homedir as homedir9 } from "node:os";
5161
+ import * as fs11 from "node:fs";
5162
+ import * as path12 from "node:path";
5163
+ import { homedir as homedir10 } from "node:os";
4845
5164
  function loadConfigFromMcpJson(cwd) {
4846
- let searchDir = path11.resolve(cwd);
5165
+ let searchDir = path12.resolve(cwd);
4847
5166
  for (let i = 0; i < 5; i++) {
4848
5167
  if (!API_KEY2) {
4849
- const mcpPath = path11.join(searchDir, ".mcp.json");
4850
- if (fs10.existsSync(mcpPath)) {
5168
+ const mcpPath = path12.join(searchDir, ".mcp.json");
5169
+ if (fs11.existsSync(mcpPath)) {
4851
5170
  try {
4852
- const content = fs10.readFileSync(mcpPath, "utf-8");
5171
+ const content = fs11.readFileSync(mcpPath, "utf-8");
4853
5172
  const config = JSON.parse(content);
4854
5173
  const csEnv = config.mcpServers?.contextstream?.env;
4855
5174
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -4866,10 +5185,10 @@ function loadConfigFromMcpJson(cwd) {
4866
5185
  }
4867
5186
  }
4868
5187
  if (!WORKSPACE_ID || !PROJECT_ID) {
4869
- const csConfigPath = path11.join(searchDir, ".contextstream", "config.json");
4870
- if (fs10.existsSync(csConfigPath)) {
5188
+ const csConfigPath = path12.join(searchDir, ".contextstream", "config.json");
5189
+ if (fs11.existsSync(csConfigPath)) {
4871
5190
  try {
4872
- const content = fs10.readFileSync(csConfigPath, "utf-8");
5191
+ const content = fs11.readFileSync(csConfigPath, "utf-8");
4873
5192
  const csConfig = JSON.parse(content);
4874
5193
  if (csConfig.workspace_id && !WORKSPACE_ID) {
4875
5194
  WORKSPACE_ID = csConfig.workspace_id;
@@ -4881,15 +5200,15 @@ function loadConfigFromMcpJson(cwd) {
4881
5200
  }
4882
5201
  }
4883
5202
  }
4884
- const parentDir = path11.dirname(searchDir);
5203
+ const parentDir = path12.dirname(searchDir);
4885
5204
  if (parentDir === searchDir) break;
4886
5205
  searchDir = parentDir;
4887
5206
  }
4888
5207
  if (!API_KEY2) {
4889
- const homeMcpPath = path11.join(homedir9(), ".mcp.json");
4890
- if (fs10.existsSync(homeMcpPath)) {
5208
+ const homeMcpPath = path12.join(homedir10(), ".mcp.json");
5209
+ if (fs11.existsSync(homeMcpPath)) {
4891
5210
  try {
4892
- const content = fs10.readFileSync(homeMcpPath, "utf-8");
5211
+ const content = fs11.readFileSync(homeMcpPath, "utf-8");
4893
5212
  const config = JSON.parse(content);
4894
5213
  const csEnv = config.mcpServers?.contextstream?.env;
4895
5214
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -4905,7 +5224,7 @@ function loadConfigFromMcpJson(cwd) {
4905
5224
  }
4906
5225
  function readTranscriptFile(transcriptPath) {
4907
5226
  try {
4908
- const content = fs10.readFileSync(transcriptPath, "utf-8");
5227
+ const content = fs11.readFileSync(transcriptPath, "utf-8");
4909
5228
  const lines = content.trim().split("\n");
4910
5229
  const messages = [];
4911
5230
  for (const line of lines) {
@@ -5313,6 +5632,11 @@ async function runUserPromptSubmitHook() {
5313
5632
  }
5314
5633
  const editorFormat = detectEditorFormat2(input);
5315
5634
  const cwd = input.cwd || process.cwd();
5635
+ cleanupStale(180);
5636
+ markContextRequired(cwd);
5637
+ if (isNewSession(input, editorFormat)) {
5638
+ markInitRequired(cwd);
5639
+ }
5316
5640
  if (editorFormat === "claude") {
5317
5641
  loadConfigFromMcpJson(cwd);
5318
5642
  let context = REMINDER;
@@ -5369,20 +5693,23 @@ var init_user_prompt_submit = __esm({
5369
5693
  "src/hooks/user-prompt-submit.ts"() {
5370
5694
  "use strict";
5371
5695
  init_version();
5696
+ init_prompt_state();
5372
5697
  ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
5373
5698
  API_URL2 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
5374
5699
  API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
5375
5700
  WORKSPACE_ID = null;
5376
5701
  PROJECT_ID = null;
5377
- REMINDER = `[CONTEXTSTREAM] Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. Response contains dynamic rules, lessons, preferences. For search: use search(mode="auto") if indexed, else local tools.
5702
+ REMINDER = `[CONTEXTSTREAM] On the first message in every session call mcp__contextstream__init(...), then call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. On subsequent messages, default to context first. Narrow bypass is allowed only for immediate read-only ContextStream calls when prior context is fresh and no state-changing tool has run. Response contains dynamic rules, lessons, preferences.
5703
+ COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via session(action="get_lessons"), list plans via session(action="list_plans"), list tasks/todos via memory(action="list_tasks"|"list_todos").
5378
5704
  [END]`;
5379
5705
  FULL_REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
5380
5706
 
5381
- 1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
5707
+ 1. FIRST MESSAGE IN SESSION: Call mcp__contextstream__init(...) then mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>")
5382
5708
  - Returns: dynamic rules, lessons from past mistakes, relevant context
5383
5709
  - Check response for: [LESSONS_WARNING], [RULES_NOTICE], preferences
5384
5710
  - save_exchange=true saves each conversation turn for later retrieval
5385
5711
  - Use a consistent session_id for the entire conversation (generate once on first message)
5712
+ - On subsequent messages, default to context() first. Narrow bypass: immediate read-only ContextStream calls when context is fresh and no state-changing tool has run.
5386
5713
 
5387
5714
  2. FOR CODE SEARCH: Check index status, then search appropriately
5388
5715
  \u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
@@ -5533,17 +5860,17 @@ var pre_compact_exports = {};
5533
5860
  __export(pre_compact_exports, {
5534
5861
  runPreCompactHook: () => runPreCompactHook
5535
5862
  });
5536
- import * as fs11 from "node:fs";
5537
- import * as path12 from "node:path";
5538
- import { homedir as homedir10 } from "node:os";
5863
+ import * as fs12 from "node:fs";
5864
+ import * as path13 from "node:path";
5865
+ import { homedir as homedir11 } from "node:os";
5539
5866
  function loadConfigFromMcpJson2(cwd) {
5540
- let searchDir = path12.resolve(cwd);
5867
+ let searchDir = path13.resolve(cwd);
5541
5868
  for (let i = 0; i < 5; i++) {
5542
5869
  if (!API_KEY3) {
5543
- const mcpPath = path12.join(searchDir, ".mcp.json");
5544
- if (fs11.existsSync(mcpPath)) {
5870
+ const mcpPath = path13.join(searchDir, ".mcp.json");
5871
+ if (fs12.existsSync(mcpPath)) {
5545
5872
  try {
5546
- const content = fs11.readFileSync(mcpPath, "utf-8");
5873
+ const content = fs12.readFileSync(mcpPath, "utf-8");
5547
5874
  const config = JSON.parse(content);
5548
5875
  const csEnv = config.mcpServers?.contextstream?.env;
5549
5876
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -5557,10 +5884,10 @@ function loadConfigFromMcpJson2(cwd) {
5557
5884
  }
5558
5885
  }
5559
5886
  if (!WORKSPACE_ID2) {
5560
- const csConfigPath = path12.join(searchDir, ".contextstream", "config.json");
5561
- if (fs11.existsSync(csConfigPath)) {
5887
+ const csConfigPath = path13.join(searchDir, ".contextstream", "config.json");
5888
+ if (fs12.existsSync(csConfigPath)) {
5562
5889
  try {
5563
- const content = fs11.readFileSync(csConfigPath, "utf-8");
5890
+ const content = fs12.readFileSync(csConfigPath, "utf-8");
5564
5891
  const csConfig = JSON.parse(content);
5565
5892
  if (csConfig.workspace_id) {
5566
5893
  WORKSPACE_ID2 = csConfig.workspace_id;
@@ -5569,15 +5896,15 @@ function loadConfigFromMcpJson2(cwd) {
5569
5896
  }
5570
5897
  }
5571
5898
  }
5572
- const parentDir = path12.dirname(searchDir);
5899
+ const parentDir = path13.dirname(searchDir);
5573
5900
  if (parentDir === searchDir) break;
5574
5901
  searchDir = parentDir;
5575
5902
  }
5576
5903
  if (!API_KEY3) {
5577
- const homeMcpPath = path12.join(homedir10(), ".mcp.json");
5578
- if (fs11.existsSync(homeMcpPath)) {
5904
+ const homeMcpPath = path13.join(homedir11(), ".mcp.json");
5905
+ if (fs12.existsSync(homeMcpPath)) {
5579
5906
  try {
5580
- const content = fs11.readFileSync(homeMcpPath, "utf-8");
5907
+ const content = fs12.readFileSync(homeMcpPath, "utf-8");
5581
5908
  const config = JSON.parse(content);
5582
5909
  const csEnv = config.mcpServers?.contextstream?.env;
5583
5910
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -5599,7 +5926,7 @@ function parseTranscript(transcriptPath) {
5599
5926
  let startedAt = (/* @__PURE__ */ new Date()).toISOString();
5600
5927
  let firstTimestamp = true;
5601
5928
  try {
5602
- const content = fs11.readFileSync(transcriptPath, "utf-8");
5929
+ const content = fs12.readFileSync(transcriptPath, "utf-8");
5603
5930
  const lines = content.split("\n");
5604
5931
  for (const line of lines) {
5605
5932
  if (!line.trim()) continue;
@@ -5800,7 +6127,7 @@ async function runPreCompactHook() {
5800
6127
  messages: [],
5801
6128
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
5802
6129
  };
5803
- if (transcriptPath && fs11.existsSync(transcriptPath)) {
6130
+ if (transcriptPath && fs12.existsSync(transcriptPath)) {
5804
6131
  transcriptData = parseTranscript(transcriptPath);
5805
6132
  }
5806
6133
  let autoSaveStatus = "";
@@ -5859,13 +6186,13 @@ var auto_rules_exports = {};
5859
6186
  __export(auto_rules_exports, {
5860
6187
  runAutoRulesHook: () => runAutoRulesHook
5861
6188
  });
5862
- import * as fs12 from "node:fs";
5863
- import * as path13 from "node:path";
5864
- import { homedir as homedir11 } from "node:os";
6189
+ import * as fs13 from "node:fs";
6190
+ import * as path14 from "node:path";
6191
+ import { homedir as homedir12 } from "node:os";
5865
6192
  function hasRunRecently() {
5866
6193
  try {
5867
- if (!fs12.existsSync(MARKER_FILE)) return false;
5868
- const stat2 = fs12.statSync(MARKER_FILE);
6194
+ if (!fs13.existsSync(MARKER_FILE)) return false;
6195
+ const stat2 = fs13.statSync(MARKER_FILE);
5869
6196
  const age = Date.now() - stat2.mtimeMs;
5870
6197
  return age < COOLDOWN_MS;
5871
6198
  } catch {
@@ -5874,11 +6201,11 @@ function hasRunRecently() {
5874
6201
  }
5875
6202
  function markAsRan() {
5876
6203
  try {
5877
- const dir = path13.dirname(MARKER_FILE);
5878
- if (!fs12.existsSync(dir)) {
5879
- fs12.mkdirSync(dir, { recursive: true });
6204
+ const dir = path14.dirname(MARKER_FILE);
6205
+ if (!fs13.existsSync(dir)) {
6206
+ fs13.mkdirSync(dir, { recursive: true });
5880
6207
  }
5881
- fs12.writeFileSync(MARKER_FILE, (/* @__PURE__ */ new Date()).toISOString());
6208
+ fs13.writeFileSync(MARKER_FILE, (/* @__PURE__ */ new Date()).toISOString());
5882
6209
  } catch {
5883
6210
  }
5884
6211
  }
@@ -5907,8 +6234,8 @@ function extractCwd3(input) {
5907
6234
  }
5908
6235
  function hasPythonHooks(settingsPath) {
5909
6236
  try {
5910
- if (!fs12.existsSync(settingsPath)) return false;
5911
- const content = fs12.readFileSync(settingsPath, "utf-8");
6237
+ if (!fs13.existsSync(settingsPath)) return false;
6238
+ const content = fs13.readFileSync(settingsPath, "utf-8");
5912
6239
  const settings = JSON.parse(content);
5913
6240
  const hooks = settings.hooks;
5914
6241
  if (!hooks) return false;
@@ -5932,8 +6259,8 @@ function hasPythonHooks(settingsPath) {
5932
6259
  }
5933
6260
  }
5934
6261
  function detectPythonHooks(cwd) {
5935
- const globalSettingsPath = path13.join(homedir11(), ".claude", "settings.json");
5936
- const projectSettingsPath = path13.join(cwd, ".claude", "settings.json");
6262
+ const globalSettingsPath = path14.join(homedir12(), ".claude", "settings.json");
6263
+ const projectSettingsPath = path14.join(cwd, ".claude", "settings.json");
5937
6264
  return {
5938
6265
  global: hasPythonHooks(globalSettingsPath),
5939
6266
  project: hasPythonHooks(projectSettingsPath)
@@ -5998,7 +6325,7 @@ var init_auto_rules = __esm({
5998
6325
  API_URL4 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
5999
6326
  API_KEY4 = process.env.CONTEXTSTREAM_API_KEY || "";
6000
6327
  ENABLED6 = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
6001
- MARKER_FILE = path13.join(homedir11(), ".contextstream", ".auto-rules-ran");
6328
+ MARKER_FILE = path14.join(homedir12(), ".contextstream", ".auto-rules-ran");
6002
6329
  COOLDOWN_MS = 4 * 60 * 60 * 1e3;
6003
6330
  isDirectRun6 = process.argv[1]?.includes("auto-rules") || process.argv[2] === "auto-rules";
6004
6331
  if (isDirectRun6) {
@@ -6012,17 +6339,17 @@ var post_compact_exports = {};
6012
6339
  __export(post_compact_exports, {
6013
6340
  runPostCompactHook: () => runPostCompactHook
6014
6341
  });
6015
- import * as fs13 from "node:fs";
6016
- import * as path14 from "node:path";
6017
- import { homedir as homedir12 } from "node:os";
6342
+ import * as fs14 from "node:fs";
6343
+ import * as path15 from "node:path";
6344
+ import { homedir as homedir13 } from "node:os";
6018
6345
  function loadConfigFromMcpJson3(cwd) {
6019
- let searchDir = path14.resolve(cwd);
6346
+ let searchDir = path15.resolve(cwd);
6020
6347
  for (let i = 0; i < 5; i++) {
6021
6348
  if (!API_KEY5) {
6022
- const mcpPath = path14.join(searchDir, ".mcp.json");
6023
- if (fs13.existsSync(mcpPath)) {
6349
+ const mcpPath = path15.join(searchDir, ".mcp.json");
6350
+ if (fs14.existsSync(mcpPath)) {
6024
6351
  try {
6025
- const content = fs13.readFileSync(mcpPath, "utf-8");
6352
+ const content = fs14.readFileSync(mcpPath, "utf-8");
6026
6353
  const config = JSON.parse(content);
6027
6354
  const csEnv = config.mcpServers?.contextstream?.env;
6028
6355
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6036,10 +6363,10 @@ function loadConfigFromMcpJson3(cwd) {
6036
6363
  }
6037
6364
  }
6038
6365
  if (!WORKSPACE_ID3) {
6039
- const csConfigPath = path14.join(searchDir, ".contextstream", "config.json");
6040
- if (fs13.existsSync(csConfigPath)) {
6366
+ const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
6367
+ if (fs14.existsSync(csConfigPath)) {
6041
6368
  try {
6042
- const content = fs13.readFileSync(csConfigPath, "utf-8");
6369
+ const content = fs14.readFileSync(csConfigPath, "utf-8");
6043
6370
  const csConfig = JSON.parse(content);
6044
6371
  if (csConfig.workspace_id) {
6045
6372
  WORKSPACE_ID3 = csConfig.workspace_id;
@@ -6048,15 +6375,15 @@ function loadConfigFromMcpJson3(cwd) {
6048
6375
  }
6049
6376
  }
6050
6377
  }
6051
- const parentDir = path14.dirname(searchDir);
6378
+ const parentDir = path15.dirname(searchDir);
6052
6379
  if (parentDir === searchDir) break;
6053
6380
  searchDir = parentDir;
6054
6381
  }
6055
6382
  if (!API_KEY5) {
6056
- const homeMcpPath = path14.join(homedir12(), ".mcp.json");
6057
- if (fs13.existsSync(homeMcpPath)) {
6383
+ const homeMcpPath = path15.join(homedir13(), ".mcp.json");
6384
+ if (fs14.existsSync(homeMcpPath)) {
6058
6385
  try {
6059
- const content = fs13.readFileSync(homeMcpPath, "utf-8");
6386
+ const content = fs14.readFileSync(homeMcpPath, "utf-8");
6060
6387
  const config = JSON.parse(content);
6061
6388
  const csEnv = config.mcpServers?.contextstream?.env;
6062
6389
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6190,17 +6517,17 @@ var on_bash_exports = {};
6190
6517
  __export(on_bash_exports, {
6191
6518
  runOnBashHook: () => runOnBashHook
6192
6519
  });
6193
- import * as fs14 from "node:fs";
6194
- import * as path15 from "node:path";
6195
- import { homedir as homedir13 } from "node:os";
6520
+ import * as fs15 from "node:fs";
6521
+ import * as path16 from "node:path";
6522
+ import { homedir as homedir14 } from "node:os";
6196
6523
  function loadConfigFromMcpJson4(cwd) {
6197
- let searchDir = path15.resolve(cwd);
6524
+ let searchDir = path16.resolve(cwd);
6198
6525
  for (let i = 0; i < 5; i++) {
6199
6526
  if (!API_KEY6) {
6200
- const mcpPath = path15.join(searchDir, ".mcp.json");
6201
- if (fs14.existsSync(mcpPath)) {
6527
+ const mcpPath = path16.join(searchDir, ".mcp.json");
6528
+ if (fs15.existsSync(mcpPath)) {
6202
6529
  try {
6203
- const content = fs14.readFileSync(mcpPath, "utf-8");
6530
+ const content = fs15.readFileSync(mcpPath, "utf-8");
6204
6531
  const config = JSON.parse(content);
6205
6532
  const csEnv = config.mcpServers?.contextstream?.env;
6206
6533
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6214,10 +6541,10 @@ function loadConfigFromMcpJson4(cwd) {
6214
6541
  }
6215
6542
  }
6216
6543
  if (!WORKSPACE_ID4) {
6217
- const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
6218
- if (fs14.existsSync(csConfigPath)) {
6544
+ const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
6545
+ if (fs15.existsSync(csConfigPath)) {
6219
6546
  try {
6220
- const content = fs14.readFileSync(csConfigPath, "utf-8");
6547
+ const content = fs15.readFileSync(csConfigPath, "utf-8");
6221
6548
  const csConfig = JSON.parse(content);
6222
6549
  if (csConfig.workspace_id) {
6223
6550
  WORKSPACE_ID4 = csConfig.workspace_id;
@@ -6226,15 +6553,15 @@ function loadConfigFromMcpJson4(cwd) {
6226
6553
  }
6227
6554
  }
6228
6555
  }
6229
- const parentDir = path15.dirname(searchDir);
6556
+ const parentDir = path16.dirname(searchDir);
6230
6557
  if (parentDir === searchDir) break;
6231
6558
  searchDir = parentDir;
6232
6559
  }
6233
6560
  if (!API_KEY6) {
6234
- const homeMcpPath = path15.join(homedir13(), ".mcp.json");
6235
- if (fs14.existsSync(homeMcpPath)) {
6561
+ const homeMcpPath = path16.join(homedir14(), ".mcp.json");
6562
+ if (fs15.existsSync(homeMcpPath)) {
6236
6563
  try {
6237
- const content = fs14.readFileSync(homeMcpPath, "utf-8");
6564
+ const content = fs15.readFileSync(homeMcpPath, "utf-8");
6238
6565
  const config = JSON.parse(content);
6239
6566
  const csEnv = config.mcpServers?.contextstream?.env;
6240
6567
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6386,17 +6713,17 @@ var on_task_exports = {};
6386
6713
  __export(on_task_exports, {
6387
6714
  runOnTaskHook: () => runOnTaskHook
6388
6715
  });
6389
- import * as fs15 from "node:fs";
6390
- import * as path16 from "node:path";
6391
- import { homedir as homedir14 } from "node:os";
6716
+ import * as fs16 from "node:fs";
6717
+ import * as path17 from "node:path";
6718
+ import { homedir as homedir15 } from "node:os";
6392
6719
  function loadConfigFromMcpJson5(cwd) {
6393
- let searchDir = path16.resolve(cwd);
6720
+ let searchDir = path17.resolve(cwd);
6394
6721
  for (let i = 0; i < 5; i++) {
6395
6722
  if (!API_KEY7) {
6396
- const mcpPath = path16.join(searchDir, ".mcp.json");
6397
- if (fs15.existsSync(mcpPath)) {
6723
+ const mcpPath = path17.join(searchDir, ".mcp.json");
6724
+ if (fs16.existsSync(mcpPath)) {
6398
6725
  try {
6399
- const content = fs15.readFileSync(mcpPath, "utf-8");
6726
+ const content = fs16.readFileSync(mcpPath, "utf-8");
6400
6727
  const config = JSON.parse(content);
6401
6728
  const csEnv = config.mcpServers?.contextstream?.env;
6402
6729
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6410,10 +6737,10 @@ function loadConfigFromMcpJson5(cwd) {
6410
6737
  }
6411
6738
  }
6412
6739
  if (!WORKSPACE_ID5) {
6413
- const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
6414
- if (fs15.existsSync(csConfigPath)) {
6740
+ const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
6741
+ if (fs16.existsSync(csConfigPath)) {
6415
6742
  try {
6416
- const content = fs15.readFileSync(csConfigPath, "utf-8");
6743
+ const content = fs16.readFileSync(csConfigPath, "utf-8");
6417
6744
  const csConfig = JSON.parse(content);
6418
6745
  if (csConfig.workspace_id) {
6419
6746
  WORKSPACE_ID5 = csConfig.workspace_id;
@@ -6422,15 +6749,15 @@ function loadConfigFromMcpJson5(cwd) {
6422
6749
  }
6423
6750
  }
6424
6751
  }
6425
- const parentDir = path16.dirname(searchDir);
6752
+ const parentDir = path17.dirname(searchDir);
6426
6753
  if (parentDir === searchDir) break;
6427
6754
  searchDir = parentDir;
6428
6755
  }
6429
6756
  if (!API_KEY7) {
6430
- const homeMcpPath = path16.join(homedir14(), ".mcp.json");
6431
- if (fs15.existsSync(homeMcpPath)) {
6757
+ const homeMcpPath = path17.join(homedir15(), ".mcp.json");
6758
+ if (fs16.existsSync(homeMcpPath)) {
6432
6759
  try {
6433
- const content = fs15.readFileSync(homeMcpPath, "utf-8");
6760
+ const content = fs16.readFileSync(homeMcpPath, "utf-8");
6434
6761
  const config = JSON.parse(content);
6435
6762
  const csEnv = config.mcpServers?.contextstream?.env;
6436
6763
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6531,17 +6858,17 @@ var on_read_exports = {};
6531
6858
  __export(on_read_exports, {
6532
6859
  runOnReadHook: () => runOnReadHook
6533
6860
  });
6534
- import * as fs16 from "node:fs";
6535
- import * as path17 from "node:path";
6536
- import { homedir as homedir15 } from "node:os";
6861
+ import * as fs17 from "node:fs";
6862
+ import * as path18 from "node:path";
6863
+ import { homedir as homedir16 } from "node:os";
6537
6864
  function loadConfigFromMcpJson6(cwd) {
6538
- let searchDir = path17.resolve(cwd);
6865
+ let searchDir = path18.resolve(cwd);
6539
6866
  for (let i = 0; i < 5; i++) {
6540
6867
  if (!API_KEY8) {
6541
- const mcpPath = path17.join(searchDir, ".mcp.json");
6542
- if (fs16.existsSync(mcpPath)) {
6868
+ const mcpPath = path18.join(searchDir, ".mcp.json");
6869
+ if (fs17.existsSync(mcpPath)) {
6543
6870
  try {
6544
- const content = fs16.readFileSync(mcpPath, "utf-8");
6871
+ const content = fs17.readFileSync(mcpPath, "utf-8");
6545
6872
  const config = JSON.parse(content);
6546
6873
  const csEnv = config.mcpServers?.contextstream?.env;
6547
6874
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6555,10 +6882,10 @@ function loadConfigFromMcpJson6(cwd) {
6555
6882
  }
6556
6883
  }
6557
6884
  if (!WORKSPACE_ID6) {
6558
- const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
6559
- if (fs16.existsSync(csConfigPath)) {
6885
+ const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
6886
+ if (fs17.existsSync(csConfigPath)) {
6560
6887
  try {
6561
- const content = fs16.readFileSync(csConfigPath, "utf-8");
6888
+ const content = fs17.readFileSync(csConfigPath, "utf-8");
6562
6889
  const csConfig = JSON.parse(content);
6563
6890
  if (csConfig.workspace_id) {
6564
6891
  WORKSPACE_ID6 = csConfig.workspace_id;
@@ -6567,15 +6894,15 @@ function loadConfigFromMcpJson6(cwd) {
6567
6894
  }
6568
6895
  }
6569
6896
  }
6570
- const parentDir = path17.dirname(searchDir);
6897
+ const parentDir = path18.dirname(searchDir);
6571
6898
  if (parentDir === searchDir) break;
6572
6899
  searchDir = parentDir;
6573
6900
  }
6574
6901
  if (!API_KEY8) {
6575
- const homeMcpPath = path17.join(homedir15(), ".mcp.json");
6576
- if (fs16.existsSync(homeMcpPath)) {
6902
+ const homeMcpPath = path18.join(homedir16(), ".mcp.json");
6903
+ if (fs17.existsSync(homeMcpPath)) {
6577
6904
  try {
6578
- const content = fs16.readFileSync(homeMcpPath, "utf-8");
6905
+ const content = fs17.readFileSync(homeMcpPath, "utf-8");
6579
6906
  const config = JSON.parse(content);
6580
6907
  const csEnv = config.mcpServers?.contextstream?.env;
6581
6908
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6700,17 +7027,17 @@ var on_web_exports = {};
6700
7027
  __export(on_web_exports, {
6701
7028
  runOnWebHook: () => runOnWebHook
6702
7029
  });
6703
- import * as fs17 from "node:fs";
6704
- import * as path18 from "node:path";
6705
- import { homedir as homedir16 } from "node:os";
7030
+ import * as fs18 from "node:fs";
7031
+ import * as path19 from "node:path";
7032
+ import { homedir as homedir17 } from "node:os";
6706
7033
  function loadConfigFromMcpJson7(cwd) {
6707
- let searchDir = path18.resolve(cwd);
7034
+ let searchDir = path19.resolve(cwd);
6708
7035
  for (let i = 0; i < 5; i++) {
6709
7036
  if (!API_KEY9) {
6710
- const mcpPath = path18.join(searchDir, ".mcp.json");
6711
- if (fs17.existsSync(mcpPath)) {
7037
+ const mcpPath = path19.join(searchDir, ".mcp.json");
7038
+ if (fs18.existsSync(mcpPath)) {
6712
7039
  try {
6713
- const content = fs17.readFileSync(mcpPath, "utf-8");
7040
+ const content = fs18.readFileSync(mcpPath, "utf-8");
6714
7041
  const config = JSON.parse(content);
6715
7042
  const csEnv = config.mcpServers?.contextstream?.env;
6716
7043
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6724,10 +7051,10 @@ function loadConfigFromMcpJson7(cwd) {
6724
7051
  }
6725
7052
  }
6726
7053
  if (!WORKSPACE_ID7) {
6727
- const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
6728
- if (fs17.existsSync(csConfigPath)) {
7054
+ const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
7055
+ if (fs18.existsSync(csConfigPath)) {
6729
7056
  try {
6730
- const content = fs17.readFileSync(csConfigPath, "utf-8");
7057
+ const content = fs18.readFileSync(csConfigPath, "utf-8");
6731
7058
  const csConfig = JSON.parse(content);
6732
7059
  if (csConfig.workspace_id) {
6733
7060
  WORKSPACE_ID7 = csConfig.workspace_id;
@@ -6736,15 +7063,15 @@ function loadConfigFromMcpJson7(cwd) {
6736
7063
  }
6737
7064
  }
6738
7065
  }
6739
- const parentDir = path18.dirname(searchDir);
7066
+ const parentDir = path19.dirname(searchDir);
6740
7067
  if (parentDir === searchDir) break;
6741
7068
  searchDir = parentDir;
6742
7069
  }
6743
7070
  if (!API_KEY9) {
6744
- const homeMcpPath = path18.join(homedir16(), ".mcp.json");
6745
- if (fs17.existsSync(homeMcpPath)) {
7071
+ const homeMcpPath = path19.join(homedir17(), ".mcp.json");
7072
+ if (fs18.existsSync(homeMcpPath)) {
6746
7073
  try {
6747
- const content = fs17.readFileSync(homeMcpPath, "utf-8");
7074
+ const content = fs18.readFileSync(homeMcpPath, "utf-8");
6748
7075
  const config = JSON.parse(content);
6749
7076
  const csEnv = config.mcpServers?.contextstream?.env;
6750
7077
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6861,17 +7188,17 @@ var session_init_exports = {};
6861
7188
  __export(session_init_exports, {
6862
7189
  runSessionInitHook: () => runSessionInitHook
6863
7190
  });
6864
- import * as fs18 from "node:fs";
6865
- import * as path19 from "node:path";
6866
- import { homedir as homedir17 } from "node:os";
7191
+ import * as fs19 from "node:fs";
7192
+ import * as path20 from "node:path";
7193
+ import { homedir as homedir18 } from "node:os";
6867
7194
  function loadConfigFromMcpJson8(cwd) {
6868
- let searchDir = path19.resolve(cwd);
7195
+ let searchDir = path20.resolve(cwd);
6869
7196
  for (let i = 0; i < 5; i++) {
6870
7197
  if (!API_KEY10) {
6871
- const mcpPath = path19.join(searchDir, ".mcp.json");
6872
- if (fs18.existsSync(mcpPath)) {
7198
+ const mcpPath = path20.join(searchDir, ".mcp.json");
7199
+ if (fs19.existsSync(mcpPath)) {
6873
7200
  try {
6874
- const content = fs18.readFileSync(mcpPath, "utf-8");
7201
+ const content = fs19.readFileSync(mcpPath, "utf-8");
6875
7202
  const config = JSON.parse(content);
6876
7203
  const csEnv = config.mcpServers?.contextstream?.env;
6877
7204
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6888,10 +7215,10 @@ function loadConfigFromMcpJson8(cwd) {
6888
7215
  }
6889
7216
  }
6890
7217
  if (!WORKSPACE_ID8 || !PROJECT_ID2) {
6891
- const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
6892
- if (fs18.existsSync(csConfigPath)) {
7218
+ const csConfigPath = path20.join(searchDir, ".contextstream", "config.json");
7219
+ if (fs19.existsSync(csConfigPath)) {
6893
7220
  try {
6894
- const content = fs18.readFileSync(csConfigPath, "utf-8");
7221
+ const content = fs19.readFileSync(csConfigPath, "utf-8");
6895
7222
  const csConfig = JSON.parse(content);
6896
7223
  if (csConfig.workspace_id && !WORKSPACE_ID8) {
6897
7224
  WORKSPACE_ID8 = csConfig.workspace_id;
@@ -6903,15 +7230,15 @@ function loadConfigFromMcpJson8(cwd) {
6903
7230
  }
6904
7231
  }
6905
7232
  }
6906
- const parentDir = path19.dirname(searchDir);
7233
+ const parentDir = path20.dirname(searchDir);
6907
7234
  if (parentDir === searchDir) break;
6908
7235
  searchDir = parentDir;
6909
7236
  }
6910
7237
  if (!API_KEY10) {
6911
- const homeMcpPath = path19.join(homedir17(), ".mcp.json");
6912
- if (fs18.existsSync(homeMcpPath)) {
7238
+ const homeMcpPath = path20.join(homedir18(), ".mcp.json");
7239
+ if (fs19.existsSync(homeMcpPath)) {
6913
7240
  try {
6914
- const content = fs18.readFileSync(homeMcpPath, "utf-8");
7241
+ const content = fs19.readFileSync(homeMcpPath, "utf-8");
6915
7242
  const config = JSON.parse(content);
6916
7243
  const csEnv = config.mcpServers?.contextstream?.env;
6917
7244
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -6989,7 +7316,9 @@ function formatContext(ctx, options = {}) {
6989
7316
  }
6990
7317
  }
6991
7318
  if (!ctx) {
6992
- parts.push('\nNo stored context found. Call `mcp__contextstream__context(user_message="starting new session")` to initialize.');
7319
+ parts.push(
7320
+ '\nNo saved context found yet. On the first message in this session call `mcp__contextstream__init(...)` then `mcp__contextstream__context(user_message="starting new session")`.'
7321
+ );
6993
7322
  return parts.join("\n");
6994
7323
  }
6995
7324
  if (ctx.lessons && ctx.lessons.length > 0) {
@@ -7017,7 +7346,9 @@ function formatContext(ctx, options = {}) {
7017
7346
  }
7018
7347
  }
7019
7348
  parts.push("\n---");
7020
- parts.push('Call `mcp__contextstream__context(user_message="...")` for task-specific context.');
7349
+ parts.push(
7350
+ 'On the first message in a new session call `mcp__contextstream__init(...)` then `mcp__contextstream__context(user_message="...")`. After that, call `mcp__contextstream__context(user_message="...")` on every message.'
7351
+ );
7021
7352
  return parts.join("\n");
7022
7353
  }
7023
7354
  function regenerateRuleFiles(folderPath) {
@@ -7026,10 +7357,10 @@ function regenerateRuleFiles(folderPath) {
7026
7357
  for (const editor of editors) {
7027
7358
  const rule = generateRuleContent(editor, { mode: "bootstrap" });
7028
7359
  if (!rule) continue;
7029
- const filePath = path19.join(folderPath, rule.filename);
7030
- if (!fs18.existsSync(filePath)) continue;
7360
+ const filePath = path20.join(folderPath, rule.filename);
7361
+ if (!fs19.existsSync(filePath)) continue;
7031
7362
  try {
7032
- const existing = fs18.readFileSync(filePath, "utf8");
7363
+ const existing = fs19.readFileSync(filePath, "utf8");
7033
7364
  const startIdx = existing.indexOf(CONTEXTSTREAM_START_MARKER3);
7034
7365
  const endIdx = existing.indexOf(CONTEXTSTREAM_END_MARKER3);
7035
7366
  if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) continue;
@@ -7039,7 +7370,7 @@ function regenerateRuleFiles(folderPath) {
7039
7370
  ${rule.content.trim()}
7040
7371
  ${CONTEXTSTREAM_END_MARKER3}`;
7041
7372
  const merged = [before, newBlock, after].filter((p) => p.length > 0).join("\n\n");
7042
- fs18.writeFileSync(filePath, merged.trim() + "\n", "utf8");
7373
+ fs19.writeFileSync(filePath, merged.trim() + "\n", "utf8");
7043
7374
  updated++;
7044
7375
  } catch {
7045
7376
  }
@@ -7064,6 +7395,8 @@ async function runSessionInitHook() {
7064
7395
  process.exit(0);
7065
7396
  }
7066
7397
  const cwd = input.cwd || process.cwd();
7398
+ cleanupStale(360);
7399
+ markInitRequired(cwd);
7067
7400
  loadConfigFromMcpJson8(cwd);
7068
7401
  const updateMarker = checkUpdateMarker();
7069
7402
  if (updateMarker) {
@@ -7097,6 +7430,7 @@ var init_session_init = __esm({
7097
7430
  "use strict";
7098
7431
  init_version();
7099
7432
  init_rules_templates();
7433
+ init_prompt_state();
7100
7434
  ENABLED12 = process.env.CONTEXTSTREAM_SESSION_INIT_ENABLED !== "false";
7101
7435
  API_URL10 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
7102
7436
  API_KEY10 = process.env.CONTEXTSTREAM_API_KEY || "";
@@ -7116,17 +7450,17 @@ var session_end_exports = {};
7116
7450
  __export(session_end_exports, {
7117
7451
  runSessionEndHook: () => runSessionEndHook
7118
7452
  });
7119
- import * as fs19 from "node:fs";
7120
- import * as path20 from "node:path";
7121
- import { homedir as homedir18 } from "node:os";
7453
+ import * as fs20 from "node:fs";
7454
+ import * as path21 from "node:path";
7455
+ import { homedir as homedir19 } from "node:os";
7122
7456
  function loadConfigFromMcpJson9(cwd) {
7123
- let searchDir = path20.resolve(cwd);
7457
+ let searchDir = path21.resolve(cwd);
7124
7458
  for (let i = 0; i < 5; i++) {
7125
7459
  if (!API_KEY11) {
7126
- const mcpPath = path20.join(searchDir, ".mcp.json");
7127
- if (fs19.existsSync(mcpPath)) {
7460
+ const mcpPath = path21.join(searchDir, ".mcp.json");
7461
+ if (fs20.existsSync(mcpPath)) {
7128
7462
  try {
7129
- const content = fs19.readFileSync(mcpPath, "utf-8");
7463
+ const content = fs20.readFileSync(mcpPath, "utf-8");
7130
7464
  const config = JSON.parse(content);
7131
7465
  const csEnv = config.mcpServers?.contextstream?.env;
7132
7466
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -7140,10 +7474,10 @@ function loadConfigFromMcpJson9(cwd) {
7140
7474
  }
7141
7475
  }
7142
7476
  if (!WORKSPACE_ID9 || !PROJECT_ID3) {
7143
- const csConfigPath = path20.join(searchDir, ".contextstream", "config.json");
7144
- if (fs19.existsSync(csConfigPath)) {
7477
+ const csConfigPath = path21.join(searchDir, ".contextstream", "config.json");
7478
+ if (fs20.existsSync(csConfigPath)) {
7145
7479
  try {
7146
- const content = fs19.readFileSync(csConfigPath, "utf-8");
7480
+ const content = fs20.readFileSync(csConfigPath, "utf-8");
7147
7481
  const csConfig = JSON.parse(content);
7148
7482
  if (csConfig.workspace_id && !WORKSPACE_ID9) {
7149
7483
  WORKSPACE_ID9 = csConfig.workspace_id;
@@ -7155,15 +7489,15 @@ function loadConfigFromMcpJson9(cwd) {
7155
7489
  }
7156
7490
  }
7157
7491
  }
7158
- const parentDir = path20.dirname(searchDir);
7492
+ const parentDir = path21.dirname(searchDir);
7159
7493
  if (parentDir === searchDir) break;
7160
7494
  searchDir = parentDir;
7161
7495
  }
7162
7496
  if (!API_KEY11) {
7163
- const homeMcpPath = path20.join(homedir18(), ".mcp.json");
7164
- if (fs19.existsSync(homeMcpPath)) {
7497
+ const homeMcpPath = path21.join(homedir19(), ".mcp.json");
7498
+ if (fs20.existsSync(homeMcpPath)) {
7165
7499
  try {
7166
- const content = fs19.readFileSync(homeMcpPath, "utf-8");
7500
+ const content = fs20.readFileSync(homeMcpPath, "utf-8");
7167
7501
  const config = JSON.parse(content);
7168
7502
  const csEnv = config.mcpServers?.contextstream?.env;
7169
7503
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -7186,11 +7520,11 @@ function parseTranscriptStats(transcriptPath) {
7186
7520
  messages: [],
7187
7521
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
7188
7522
  };
7189
- if (!transcriptPath || !fs19.existsSync(transcriptPath)) {
7523
+ if (!transcriptPath || !fs20.existsSync(transcriptPath)) {
7190
7524
  return stats;
7191
7525
  }
7192
7526
  try {
7193
- const content = fs19.readFileSync(transcriptPath, "utf-8");
7527
+ const content = fs20.readFileSync(transcriptPath, "utf-8");
7194
7528
  const lines = content.split("\n");
7195
7529
  let firstTimestamp = null;
7196
7530
  let lastTimestamp = null;
@@ -7548,9 +7882,9 @@ __export(verify_key_exports, {
7548
7882
  runVerifyKey: () => runVerifyKey,
7549
7883
  validateApiKey: () => validateApiKey
7550
7884
  });
7551
- import * as fs20 from "node:fs";
7552
- import * as path21 from "node:path";
7553
- import { homedir as homedir19 } from "node:os";
7885
+ import * as fs21 from "node:fs";
7886
+ import * as path22 from "node:path";
7887
+ import { homedir as homedir20 } from "node:os";
7554
7888
  function maskApiKey2(key) {
7555
7889
  if (!key || key.length < 8) return "***";
7556
7890
  const prefixMatch = key.match(/^([a-z]{2,3}_)/i);
@@ -7583,11 +7917,11 @@ function extractFromMcpConfig(config) {
7583
7917
  function getClaudeDesktopConfigPath() {
7584
7918
  const platform2 = process.platform;
7585
7919
  if (platform2 === "darwin") {
7586
- return path21.join(homedir19(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
7920
+ return path22.join(homedir20(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
7587
7921
  } else if (platform2 === "win32") {
7588
- return path21.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
7922
+ return path22.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
7589
7923
  } else {
7590
- return path21.join(homedir19(), ".config", "Claude", "claude_desktop_config.json");
7924
+ return path22.join(homedir20(), ".config", "Claude", "claude_desktop_config.json");
7591
7925
  }
7592
7926
  }
7593
7927
  function loadApiKey() {
@@ -7604,10 +7938,10 @@ function loadApiKey() {
7604
7938
  }
7605
7939
  let searchDir = process.cwd();
7606
7940
  for (let i = 0; i < 5; i++) {
7607
- const projectMcpPath = path21.join(searchDir, ".mcp.json");
7608
- if (fs20.existsSync(projectMcpPath)) {
7941
+ const projectMcpPath = path22.join(searchDir, ".mcp.json");
7942
+ if (fs21.existsSync(projectMcpPath)) {
7609
7943
  try {
7610
- const content = fs20.readFileSync(projectMcpPath, "utf-8");
7944
+ const content = fs21.readFileSync(projectMcpPath, "utf-8");
7611
7945
  const config = JSON.parse(content);
7612
7946
  const extracted = extractFromMcpConfig(config);
7613
7947
  if (extracted.apiKey) {
@@ -7621,14 +7955,14 @@ function loadApiKey() {
7621
7955
  } catch {
7622
7956
  }
7623
7957
  }
7624
- const parentDir = path21.dirname(searchDir);
7958
+ const parentDir = path22.dirname(searchDir);
7625
7959
  if (parentDir === searchDir) break;
7626
7960
  searchDir = parentDir;
7627
7961
  }
7628
- const globalMcpPath = path21.join(homedir19(), ".mcp.json");
7629
- if (fs20.existsSync(globalMcpPath)) {
7962
+ const globalMcpPath = path22.join(homedir20(), ".mcp.json");
7963
+ if (fs21.existsSync(globalMcpPath)) {
7630
7964
  try {
7631
- const content = fs20.readFileSync(globalMcpPath, "utf-8");
7965
+ const content = fs21.readFileSync(globalMcpPath, "utf-8");
7632
7966
  const config = JSON.parse(content);
7633
7967
  const extracted = extractFromMcpConfig(config);
7634
7968
  if (extracted.apiKey) {
@@ -7643,13 +7977,13 @@ function loadApiKey() {
7643
7977
  }
7644
7978
  }
7645
7979
  const cursorPaths = [
7646
- path21.join(process.cwd(), ".cursor", "mcp.json"),
7647
- path21.join(homedir19(), ".cursor", "mcp.json")
7980
+ path22.join(process.cwd(), ".cursor", "mcp.json"),
7981
+ path22.join(homedir20(), ".cursor", "mcp.json")
7648
7982
  ];
7649
7983
  for (const cursorPath of cursorPaths) {
7650
- if (fs20.existsSync(cursorPath)) {
7984
+ if (fs21.existsSync(cursorPath)) {
7651
7985
  try {
7652
- const content = fs20.readFileSync(cursorPath, "utf-8");
7986
+ const content = fs21.readFileSync(cursorPath, "utf-8");
7653
7987
  const config = JSON.parse(content);
7654
7988
  const extracted = extractFromMcpConfig(config);
7655
7989
  if (extracted.apiKey) {
@@ -7665,9 +7999,9 @@ function loadApiKey() {
7665
7999
  }
7666
8000
  }
7667
8001
  const claudeDesktopPath = getClaudeDesktopConfigPath();
7668
- if (fs20.existsSync(claudeDesktopPath)) {
8002
+ if (fs21.existsSync(claudeDesktopPath)) {
7669
8003
  try {
7670
- const content = fs20.readFileSync(claudeDesktopPath, "utf-8");
8004
+ const content = fs21.readFileSync(claudeDesktopPath, "utf-8");
7671
8005
  const config = JSON.parse(content);
7672
8006
  const extracted = extractFromMcpConfig(config);
7673
8007
  if (extracted.apiKey) {
@@ -7682,14 +8016,14 @@ function loadApiKey() {
7682
8016
  }
7683
8017
  }
7684
8018
  const vscodePaths = [
7685
- path21.join(homedir19(), ".vscode", "mcp.json"),
7686
- path21.join(homedir19(), ".codeium", "windsurf", "mcp_config.json"),
7687
- path21.join(homedir19(), ".continue", "config.json")
8019
+ path22.join(homedir20(), ".vscode", "mcp.json"),
8020
+ path22.join(homedir20(), ".codeium", "windsurf", "mcp_config.json"),
8021
+ path22.join(homedir20(), ".continue", "config.json")
7688
8022
  ];
7689
8023
  for (const vsPath of vscodePaths) {
7690
- if (fs20.existsSync(vsPath)) {
8024
+ if (fs21.existsSync(vsPath)) {
7691
8025
  try {
7692
- const content = fs20.readFileSync(vsPath, "utf-8");
8026
+ const content = fs21.readFileSync(vsPath, "utf-8");
7693
8027
  const config = JSON.parse(content);
7694
8028
  const extracted = extractFromMcpConfig(config);
7695
8029
  if (extracted.apiKey) {
@@ -7704,10 +8038,10 @@ function loadApiKey() {
7704
8038
  }
7705
8039
  }
7706
8040
  }
7707
- const credentialsPath = path21.join(homedir19(), ".contextstream", "credentials.json");
7708
- if (fs20.existsSync(credentialsPath)) {
8041
+ const credentialsPath = path22.join(homedir20(), ".contextstream", "credentials.json");
8042
+ if (fs21.existsSync(credentialsPath)) {
7709
8043
  try {
7710
- const content = fs20.readFileSync(credentialsPath, "utf-8");
8044
+ const content = fs21.readFileSync(credentialsPath, "utf-8");
7711
8045
  const creds = JSON.parse(content);
7712
8046
  if (creds.api_key) {
7713
8047
  apiKey = creds.api_key;
@@ -8305,8 +8639,8 @@ function getErrorMap() {
8305
8639
 
8306
8640
  // node_modules/zod/v3/helpers/parseUtil.js
8307
8641
  var makeIssue = (params) => {
8308
- const { data, path: path22, errorMaps, issueData } = params;
8309
- const fullPath = [...path22, ...issueData.path || []];
8642
+ const { data, path: path23, errorMaps, issueData } = params;
8643
+ const fullPath = [...path23, ...issueData.path || []];
8310
8644
  const fullIssue = {
8311
8645
  ...issueData,
8312
8646
  path: fullPath
@@ -8422,11 +8756,11 @@ var errorUtil;
8422
8756
 
8423
8757
  // node_modules/zod/v3/types.js
8424
8758
  var ParseInputLazyPath = class {
8425
- constructor(parent, value, path22, key) {
8759
+ constructor(parent, value, path23, key) {
8426
8760
  this._cachedPath = [];
8427
8761
  this.parent = parent;
8428
8762
  this.data = value;
8429
- this._path = path22;
8763
+ this._path = path23;
8430
8764
  this._key = key;
8431
8765
  }
8432
8766
  get path() {
@@ -11991,14 +12325,14 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504])
11991
12325
  var MAX_RETRIES = 3;
11992
12326
  var BASE_DELAY = 1e3;
11993
12327
  async function sleep(ms) {
11994
- return new Promise((resolve16) => setTimeout(resolve16, ms));
12328
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
11995
12329
  }
11996
- async function request(config, path22, options = {}) {
12330
+ async function request(config, path23, options = {}) {
11997
12331
  const { apiUrl, userAgent } = config;
11998
12332
  const authOverride = getAuthOverride();
11999
12333
  const apiKey = authOverride?.apiKey ?? config.apiKey;
12000
12334
  const jwt = authOverride?.jwt ?? config.jwt;
12001
- const rawPath = path22.startsWith("/") ? path22 : `/${path22}`;
12335
+ const rawPath = path23.startsWith("/") ? path23 : `/${path23}`;
12002
12336
  const apiPath = rawPath.startsWith("/api/") ? rawPath : `/api/v1${rawPath}`;
12003
12337
  const unauthenticatedEndpoints = ["/api/v1/auth/device/start", "/api/v1/auth/device/token"];
12004
12338
  const isUnauthenticatedEndpoint = unauthenticatedEndpoints.some(
@@ -12153,9 +12487,9 @@ function extractErrorCode(payload) {
12153
12487
  if (typeof payload.code === "string" && payload.code.trim()) return payload.code.trim();
12154
12488
  return null;
12155
12489
  }
12156
- function detectIntegrationProvider(path22) {
12157
- if (/\/github(\/|$)/i.test(path22)) return "github";
12158
- if (/\/slack(\/|$)/i.test(path22)) return "slack";
12490
+ function detectIntegrationProvider(path23) {
12491
+ if (/\/github(\/|$)/i.test(path23)) return "github";
12492
+ if (/\/slack(\/|$)/i.test(path23)) return "slack";
12159
12493
  return null;
12160
12494
  }
12161
12495
  function rewriteNotFoundMessage(input) {
@@ -12431,7 +12765,7 @@ var INGEST_BENEFITS = [
12431
12765
  "Allow the AI assistant to find relevant code without manual file navigation",
12432
12766
  "Build a searchable knowledge base of your codebase structure"
12433
12767
  ];
12434
- var AUTO_INDEX_FILE_CAP = 2e3;
12768
+ var AUTO_INDEX_FILE_CAP = 1e4;
12435
12769
  var PROJECT_MARKERS = [
12436
12770
  ".git",
12437
12771
  "package.json",
@@ -12446,10 +12780,10 @@ var PROJECT_MARKERS = [
12446
12780
  ];
12447
12781
  function isMultiProjectFolder(folderPath) {
12448
12782
  try {
12449
- const fs21 = __require("fs");
12783
+ const fs22 = __require("fs");
12450
12784
  const pathModule = __require("path");
12451
- const rootHasGit = fs21.existsSync(pathModule.join(folderPath, ".git"));
12452
- const entries = fs21.readdirSync(folderPath, { withFileTypes: true });
12785
+ const rootHasGit = fs22.existsSync(pathModule.join(folderPath, ".git"));
12786
+ const entries = fs22.readdirSync(folderPath, { withFileTypes: true });
12453
12787
  const subdirs = entries.filter(
12454
12788
  (e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules"
12455
12789
  );
@@ -12457,7 +12791,7 @@ function isMultiProjectFolder(folderPath) {
12457
12791
  for (const subdir of subdirs) {
12458
12792
  const subdirPath = pathModule.join(folderPath, subdir.name);
12459
12793
  for (const marker of PROJECT_MARKERS) {
12460
- if (fs21.existsSync(pathModule.join(subdirPath, marker))) {
12794
+ if (fs22.existsSync(pathModule.join(subdirPath, marker))) {
12461
12795
  projectSubdirs.push(subdir.name);
12462
12796
  break;
12463
12797
  }
@@ -12473,7 +12807,7 @@ function isMultiProjectFolder(folderPath) {
12473
12807
  return { isMultiProject: false, projectCount: 0, projectNames: [] };
12474
12808
  }
12475
12809
  }
12476
- var ContextStreamClient = class {
12810
+ var ContextStreamClient = class _ContextStreamClient {
12477
12811
  constructor(config) {
12478
12812
  this.config = config;
12479
12813
  this.indexRefreshInProgress = false;
@@ -13391,7 +13725,7 @@ var ContextStreamClient = class {
13391
13725
  * Check for changed files since last index and queue them for indexing.
13392
13726
  * This is used as a fallback for editors that don't support PostToolUse hooks.
13393
13727
  *
13394
- * Throttled to run at most every 10 seconds and caps at 20 files per check.
13728
+ * Throttled to run at most every 10 seconds and caps at 100 files per check.
13395
13729
  * Runs in fire-and-forget mode to avoid blocking tool responses.
13396
13730
  *
13397
13731
  * Call this from tool handlers like contextSmart() and search() to enable
@@ -13401,6 +13735,9 @@ var ContextStreamClient = class {
13401
13735
  if (!this.sessionProjectId || !this.sessionRootPath) {
13402
13736
  return;
13403
13737
  }
13738
+ if (!await this.hasBaselineIndex(this.sessionRootPath)) {
13739
+ return;
13740
+ }
13404
13741
  const now = Date.now();
13405
13742
  if (this.lastIndexCheckTime && now - this.lastIndexCheckTime < 1e4) {
13406
13743
  return;
@@ -13430,7 +13767,7 @@ var ContextStreamClient = class {
13430
13767
  if (!this.sessionProjectId || !this.sessionRootPath || !this.lastIndexedTime) {
13431
13768
  return;
13432
13769
  }
13433
- const MAX_FILES = 20;
13770
+ const MAX_FILES = 100;
13434
13771
  const filesToIndex = [];
13435
13772
  try {
13436
13773
  for await (const batch of readChangedFilesInBatches(
@@ -13452,6 +13789,62 @@ var ContextStreamClient = class {
13452
13789
  } catch {
13453
13790
  }
13454
13791
  }
13792
+ /**
13793
+ * Check local index-status tracking to confirm a baseline index exists.
13794
+ */
13795
+ async hasBaselineIndex(rootPath) {
13796
+ try {
13797
+ const localProjectId = await _ContextStreamClient.indexedProjectIdForFolder(rootPath);
13798
+ if (localProjectId) {
13799
+ return true;
13800
+ }
13801
+ const status = await readIndexStatus();
13802
+ const resolvedRoot = path5.resolve(rootPath);
13803
+ for (const indexedPath of Object.keys(status.projects ?? {})) {
13804
+ const resolvedIndexed = path5.resolve(indexedPath);
13805
+ if (resolvedRoot === resolvedIndexed || resolvedRoot.startsWith(`${resolvedIndexed}${path5.sep}`) || resolvedIndexed.startsWith(`${resolvedRoot}${path5.sep}`)) {
13806
+ return true;
13807
+ }
13808
+ }
13809
+ } catch {
13810
+ }
13811
+ return false;
13812
+ }
13813
+ /**
13814
+ * Return the locally tracked project_id for a folder from indexed-projects.json.
13815
+ * Uses longest-prefix path matching and ignores stale entries (>7 days old).
13816
+ */
13817
+ static async indexedProjectIdForFolder(folderPath) {
13818
+ const status = await readIndexStatus();
13819
+ const projects = status.projects ?? {};
13820
+ const resolvedFolder = path5.resolve(folderPath);
13821
+ let bestMatch = null;
13822
+ for (const [projectPath, info] of Object.entries(projects)) {
13823
+ const resolvedProjectPath = path5.resolve(projectPath);
13824
+ if (!(resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${path5.sep}`) || resolvedProjectPath.startsWith(`${resolvedFolder}${path5.sep}`))) {
13825
+ continue;
13826
+ }
13827
+ if (_ContextStreamClient.isStaleIndexEntry(info?.indexed_at)) {
13828
+ continue;
13829
+ }
13830
+ const projectId = typeof info?.project_id === "string" && info.project_id.trim() ? info.project_id.trim() : void 0;
13831
+ if (!projectId) {
13832
+ continue;
13833
+ }
13834
+ const matchLen = resolvedProjectPath.length;
13835
+ if (!bestMatch || matchLen > bestMatch.matchLen) {
13836
+ bestMatch = { projectId, matchLen };
13837
+ }
13838
+ }
13839
+ return bestMatch?.projectId;
13840
+ }
13841
+ static isStaleIndexEntry(indexedAt) {
13842
+ if (!indexedAt) return false;
13843
+ const parsed = new Date(indexedAt);
13844
+ if (Number.isNaN(parsed.getTime())) return false;
13845
+ const diffDays = (Date.now() - parsed.getTime()) / (1e3 * 60 * 60 * 24);
13846
+ return diffDays > 7;
13847
+ }
13455
13848
  // Workspace extended operations (with caching)
13456
13849
  async getWorkspace(workspaceId) {
13457
13850
  uuidSchema.parse(workspaceId);
@@ -14112,15 +14505,30 @@ var ContextStreamClient = class {
14112
14505
  * Persists the selection to .contextstream/config.json for future sessions.
14113
14506
  */
14114
14507
  async associateWorkspace(params) {
14115
- const { folder_path, workspace_id, workspace_name, create_parent_mapping, version, configured_editors, context_pack, api_url } = params;
14508
+ const {
14509
+ folder_path,
14510
+ workspace_id,
14511
+ workspace_name,
14512
+ create_parent_mapping,
14513
+ project_id,
14514
+ project_name,
14515
+ version,
14516
+ configured_editors,
14517
+ context_pack,
14518
+ api_url
14519
+ } = params;
14520
+ const existing = readLocalConfig(folder_path);
14116
14521
  const saved = writeLocalConfig(folder_path, {
14117
14522
  workspace_id,
14118
14523
  workspace_name,
14524
+ project_id: project_id ?? existing?.project_id,
14525
+ project_name: project_name ?? existing?.project_name,
14119
14526
  associated_at: (/* @__PURE__ */ new Date()).toISOString(),
14120
14527
  version,
14121
14528
  configured_editors,
14122
14529
  context_pack,
14123
14530
  api_url,
14531
+ indexing_enabled: existing?.indexing_enabled,
14124
14532
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
14125
14533
  });
14126
14534
  if (create_parent_mapping) {
@@ -14661,9 +15069,9 @@ var ContextStreamClient = class {
14661
15069
  candidateParts.push("## Relevant Code\n");
14662
15070
  currentChars += 18;
14663
15071
  const codeEntries = code.results.map((c) => {
14664
- const path22 = c.file_path || "file";
15072
+ const path23 = c.file_path || "file";
14665
15073
  const content = c.content?.slice(0, 150) || "";
14666
- return { path: path22, entry: `\u2022 ${path22}: ${content}...
15074
+ return { path: path23, entry: `\u2022 ${path23}: ${content}...
14667
15075
  ` };
14668
15076
  });
14669
15077
  for (const c of codeEntries) {
@@ -14753,6 +15161,14 @@ var ContextStreamClient = class {
14753
15161
  const format = params.format || "minified";
14754
15162
  const usePackDefault = this.config.contextPackEnabled !== false && !!withDefaults.project_id;
14755
15163
  const mode = params.mode || (usePackDefault ? "pack" : "standard");
15164
+ const contextTimeoutSeconds = (() => {
15165
+ const raw = process.env.MCP_CONTEXT_SMART_TIMEOUT_SECS;
15166
+ if (!raw) return 45;
15167
+ const parsed = Number(raw.trim());
15168
+ if (!Number.isFinite(parsed)) return 45;
15169
+ if (parsed < 5 || parsed > 55) return 45;
15170
+ return Math.floor(parsed);
15171
+ })();
14756
15172
  if (!withDefaults.workspace_id) {
14757
15173
  return {
14758
15174
  context: "[NO_WORKSPACE]",
@@ -14814,7 +15230,9 @@ var ContextStreamClient = class {
14814
15230
  ...params.session_id !== void 0 && { session_id: params.session_id },
14815
15231
  ...params.client_name !== void 0 && { client_name: params.client_name },
14816
15232
  ...params.assistant_message !== void 0 && { assistant_message: params.assistant_message }
14817
- }
15233
+ },
15234
+ timeoutMs: contextTimeoutSeconds * 1e3,
15235
+ retries: 0
14818
15236
  });
14819
15237
  const data = unwrapApiResponse(apiResult);
14820
15238
  let versionNotice2 = null;
@@ -17591,9 +18009,9 @@ function humanizeKey(raw) {
17591
18009
  const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
17592
18010
  return withSpaces.toLowerCase();
17593
18011
  }
17594
- function buildParamDescription(key, path22) {
18012
+ function buildParamDescription(key, path23) {
17595
18013
  const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
17596
- const parent = path22[path22.length - 1];
18014
+ const parent = path23[path23.length - 1];
17597
18015
  if (parent === "target") {
17598
18016
  if (key === "id") return "Target identifier (module path, function id, etc.).";
17599
18017
  if (key === "type") return "Target type (module, file, function, type, variable).";
@@ -17624,7 +18042,7 @@ function getDescription(schema) {
17624
18042
  if (def?.description && def.description.trim()) return def.description;
17625
18043
  return void 0;
17626
18044
  }
17627
- function applyParamDescriptions(schema, path22 = []) {
18045
+ function applyParamDescriptions(schema, path23 = []) {
17628
18046
  if (!(schema instanceof external_exports.ZodObject)) {
17629
18047
  return schema;
17630
18048
  }
@@ -17635,7 +18053,7 @@ function applyParamDescriptions(schema, path22 = []) {
17635
18053
  let nextField = field;
17636
18054
  const existingDescription = getDescription(field);
17637
18055
  if (field instanceof external_exports.ZodObject) {
17638
- const nested = applyParamDescriptions(field, [...path22, key]);
18056
+ const nested = applyParamDescriptions(field, [...path23, key]);
17639
18057
  if (nested !== field) {
17640
18058
  nextField = nested;
17641
18059
  changed = true;
@@ -17647,7 +18065,7 @@ function applyParamDescriptions(schema, path22 = []) {
17647
18065
  changed = true;
17648
18066
  }
17649
18067
  } else {
17650
- nextField = nextField.describe(buildParamDescription(key, path22));
18068
+ nextField = nextField.describe(buildParamDescription(key, path23));
17651
18069
  changed = true;
17652
18070
  }
17653
18071
  nextShape[key] = nextField;
@@ -19000,13 +19418,295 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
19000
19418
  typeof ctx?.workspace_id === "string" ? ctx.workspace_id : void 0
19001
19419
  );
19002
19420
  }
19003
- function resolveProjectId(explicitProjectId) {
19004
- const normalizedExplicit = normalizeUuid(explicitProjectId);
19005
- if (normalizedExplicit) return normalizedExplicit;
19006
- const ctx = sessionManager?.getContext();
19007
- return normalizeUuid(
19008
- typeof ctx?.project_id === "string" ? ctx.project_id : void 0
19009
- );
19421
+ function resolveProjectId(explicitProjectId) {
19422
+ const normalizedExplicit = normalizeUuid(explicitProjectId);
19423
+ if (normalizedExplicit) return normalizedExplicit;
19424
+ const ctx = sessionManager?.getContext();
19425
+ return normalizeUuid(
19426
+ typeof ctx?.project_id === "string" ? ctx.project_id : void 0
19427
+ );
19428
+ }
19429
+ function isNotFoundError(error) {
19430
+ return error instanceof HttpError && error.status === 404;
19431
+ }
19432
+ function isRequiresIngestEndpointError(error) {
19433
+ if (!(error instanceof HttpError)) return false;
19434
+ if (error.status !== 400) return false;
19435
+ const message = String(error.message || "").toLowerCase();
19436
+ return message.includes("requires using the ingest endpoint");
19437
+ }
19438
+ function appendNote(existing, extra) {
19439
+ return existing ? `${existing} ${extra}` : extra;
19440
+ }
19441
+ function extractSearchEnvelope(result) {
19442
+ const data = result?.data ?? result ?? {};
19443
+ const results = Array.isArray(data?.results) ? data.results : [];
19444
+ const total = typeof data?.total === "number" ? data.total : results.length;
19445
+ return { results, total };
19446
+ }
19447
+ function maxResultScore(result) {
19448
+ const { results } = extractSearchEnvelope(result);
19449
+ let max = 0;
19450
+ for (const entry of results) {
19451
+ const score = typeof entry?.score === "number" ? entry.score : 0;
19452
+ if (score > max) max = score;
19453
+ }
19454
+ return max;
19455
+ }
19456
+ function extractQuotedLiteral(query) {
19457
+ const trimmed = query.trim();
19458
+ if (trimmed.length < 2) return void 0;
19459
+ const isDoubleQuoted = trimmed.startsWith('"') && trimmed.endsWith('"');
19460
+ const isSingleQuoted = trimmed.startsWith("'") && trimmed.endsWith("'");
19461
+ if (!isDoubleQuoted && !isSingleQuoted) return void 0;
19462
+ const literal = trimmed.slice(1, -1).trim();
19463
+ return literal || void 0;
19464
+ }
19465
+ function escapeRegexLiteral(input) {
19466
+ return input.replace(/[\\.+*?()[\]{}|^$]/g, "\\$&");
19467
+ }
19468
+ const COUNT_QUERY_PREFIXES = ["how many ", "count ", "count of ", "number of ", "total "];
19469
+ const ALL_MATCH_KEYWORDS = [
19470
+ "all occurrences",
19471
+ "all matches",
19472
+ "find all",
19473
+ "every usage",
19474
+ "every occurrence",
19475
+ "all usages"
19476
+ ];
19477
+ const TEAM_QUERY_KEYWORDS = [
19478
+ "team-wide",
19479
+ "teamwide",
19480
+ "cross-project",
19481
+ "cross project",
19482
+ "across projects",
19483
+ "all workspaces",
19484
+ "all projects"
19485
+ ];
19486
+ const DOC_QUERY_KEYWORDS = ["doc", "docs", "document", "documents", "spec", "specification", "plan", "roadmap"];
19487
+ const DOC_LOOKUP_VERBS = ["list", "show", "find", "open", "read", "lookup", "look up", "get"];
19488
+ const QUESTION_WORDS = ["how", "what", "where", "why", "when", "which", "who", "does", "is", "can", "should"];
19489
+ const HYBRID_LOW_CONFIDENCE_SCORE = 0.35;
19490
+ const SEMANTIC_SWITCH_MIN_IMPROVEMENT = 0.08;
19491
+ function isIdentifierQuery(query) {
19492
+ const trimmed = query.trim();
19493
+ if (!trimmed || trimmed.length < 2 || trimmed.includes(" ")) return false;
19494
+ if (!/^[A-Za-z0-9_:]+$/.test(trimmed)) return false;
19495
+ const hasMixedCase = /[a-z]/.test(trimmed) && /[A-Z]/.test(trimmed);
19496
+ const hasUnderscore = trimmed.includes("_");
19497
+ const isAllCaps = trimmed.length >= 3 && /^[A-Z0-9_]+$/.test(trimmed);
19498
+ return hasMixedCase || hasUnderscore || isAllCaps;
19499
+ }
19500
+ function hasRegexCharacters(query) {
19501
+ const trimmed = query.trim();
19502
+ if (/[\^$+{}[\]|()\\]/.test(trimmed)) return true;
19503
+ if (!trimmed.includes("?")) return false;
19504
+ const trailingOnly = trimmed.endsWith("?") && !trimmed.slice(0, -1).includes("?");
19505
+ return !trailingOnly;
19506
+ }
19507
+ function isGlobLike(query) {
19508
+ const trimmed = query.trim();
19509
+ return trimmed.includes("*") || trimmed.includes("?") && !trimmed.endsWith("?");
19510
+ }
19511
+ function isCountQuery(queryLower) {
19512
+ return COUNT_QUERY_PREFIXES.some((prefix) => queryLower.startsWith(prefix)) || queryLower.includes("how many") && queryLower.includes("are there");
19513
+ }
19514
+ function isAllMatchesQuery(queryLower) {
19515
+ return ALL_MATCH_KEYWORDS.some((keyword) => queryLower.includes(keyword));
19516
+ }
19517
+ function isTeamQuery(queryLower) {
19518
+ return TEAM_QUERY_KEYWORDS.some((keyword) => queryLower.includes(keyword));
19519
+ }
19520
+ function isDocLookupQuery(query) {
19521
+ const lower = query.trim().toLowerCase();
19522
+ if (!lower) return false;
19523
+ if (lower.includes(".rs") || lower.includes(".ts") || lower.includes(".js") || lower.includes("src/") || lower.includes("crates/") || lower.includes("function ") || lower.includes("class ")) {
19524
+ return false;
19525
+ }
19526
+ const hasDocTerm = DOC_QUERY_KEYWORDS.some((keyword) => lower.includes(keyword));
19527
+ if (!hasDocTerm) return false;
19528
+ const hasLookupVerb = DOC_LOOKUP_VERBS.some((keyword) => lower.includes(keyword));
19529
+ return hasLookupVerb || lower.startsWith("docs ") || lower.startsWith("doc ");
19530
+ }
19531
+ function recommendSearchMode(query) {
19532
+ const trimmed = query.trim();
19533
+ if (!trimmed) {
19534
+ return { mode: "hybrid", reason: "Defaulted to hybrid for broad discovery." };
19535
+ }
19536
+ const lower = trimmed.toLowerCase();
19537
+ const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
19538
+ if (isTeamQuery(lower)) {
19539
+ return { mode: "team", reason: "Detected team/cross-project intent." };
19540
+ }
19541
+ if (isAllMatchesQuery(lower)) {
19542
+ return {
19543
+ mode: "exhaustive",
19544
+ reason: "Detected all-occurrences intent; exhaustive mode is complete."
19545
+ };
19546
+ }
19547
+ if (extractQuotedLiteral(trimmed)) {
19548
+ return { mode: "keyword", reason: "Detected quoted exact-match query." };
19549
+ }
19550
+ if (isGlobLike(trimmed) || hasRegexCharacters(trimmed)) {
19551
+ return { mode: "pattern", reason: "Detected glob/regex pattern." };
19552
+ }
19553
+ if (isIdentifierQuery(trimmed)) {
19554
+ return {
19555
+ mode: "refactor",
19556
+ reason: "Detected identifier-like query; refactor mode is more precise."
19557
+ };
19558
+ }
19559
+ if (QUESTION_WORDS.some((w) => lower.startsWith(w)) || trimmed.endsWith("?") || wordCount >= 3) {
19560
+ return {
19561
+ mode: "semantic",
19562
+ reason: "Detected natural-language query; semantic mode is a better fit."
19563
+ };
19564
+ }
19565
+ return { mode: "hybrid", reason: "Hybrid mode provides balanced coverage." };
19566
+ }
19567
+ function suggestOutputFormat(query, mode) {
19568
+ const lower = query.trim().toLowerCase();
19569
+ if (isCountQuery(lower)) {
19570
+ return "count";
19571
+ }
19572
+ if (isIdentifierQuery(query)) {
19573
+ if (mode === "refactor" || mode === "exhaustive") return "paths";
19574
+ return "minimal";
19575
+ }
19576
+ return void 0;
19577
+ }
19578
+ function shouldRetrySemanticFallback(query, mode, result) {
19579
+ if (mode !== "hybrid") return false;
19580
+ if (recommendSearchMode(query).mode !== "semantic") return false;
19581
+ const { results } = extractSearchEnvelope(result);
19582
+ return results.length === 0 || maxResultScore(result) < HYBRID_LOW_CONFIDENCE_SCORE;
19583
+ }
19584
+ function shouldPreferSemanticResults(hybridResult, semanticResult) {
19585
+ const hybrid = extractSearchEnvelope(hybridResult);
19586
+ const semantic = extractSearchEnvelope(semanticResult);
19587
+ if (semantic.results.length === 0) return false;
19588
+ if (hybrid.results.length === 0) return true;
19589
+ const hybridTop = maxResultScore(hybridResult);
19590
+ const semanticTop = maxResultScore(semanticResult);
19591
+ return semanticTop > hybridTop + SEMANTIC_SWITCH_MIN_IMPROVEMENT;
19592
+ }
19593
+ function shouldRetryKeywordWithSemantic(query) {
19594
+ return recommendSearchMode(query).mode === "semantic";
19595
+ }
19596
+ function shouldRetryKeywordWithSymbolModes(query) {
19597
+ return isIdentifierQuery(query);
19598
+ }
19599
+ function extractCollectionArray(value) {
19600
+ if (Array.isArray(value)) return value;
19601
+ if (!value || typeof value !== "object") return void 0;
19602
+ for (const key of ["items", "results", "docs"]) {
19603
+ if (Array.isArray(value[key])) return value[key];
19604
+ }
19605
+ if ("data" in value) return extractCollectionArray(value.data);
19606
+ return void 0;
19607
+ }
19608
+ function tokenizeForDocMatch(query) {
19609
+ const stopWords = /* @__PURE__ */ new Set([
19610
+ "the",
19611
+ "and",
19612
+ "for",
19613
+ "with",
19614
+ "from",
19615
+ "that",
19616
+ "this",
19617
+ "docs",
19618
+ "doc",
19619
+ "document",
19620
+ "list",
19621
+ "show",
19622
+ "find",
19623
+ "plan",
19624
+ "phase",
19625
+ "phases"
19626
+ ]);
19627
+ return query.split(/[^A-Za-z0-9]+/).map((term) => term.trim().toLowerCase()).filter((term) => term.length >= 3 && !stopWords.has(term));
19628
+ }
19629
+ function scoreDocMatch(doc, terms) {
19630
+ const title = String(doc?.title ?? doc?.name ?? doc?.summary ?? "").toLowerCase();
19631
+ const content = String(doc?.content ?? "").toLowerCase();
19632
+ const haystack = `${title} ${content}`;
19633
+ return terms.reduce((score, term) => haystack.includes(term) ? score + 1 : score, 0);
19634
+ }
19635
+ function rankDocsForQuery(docs, query, limit) {
19636
+ const terms = tokenizeForDocMatch(query);
19637
+ if (terms.length === 0) return docs.slice(0, limit);
19638
+ const scored = docs.map((doc, idx) => ({ idx, score: scoreDocMatch(doc, terms) }));
19639
+ scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
19640
+ const matched = scored.filter((entry) => entry.score > 0).slice(0, limit).map((entry) => docs[entry.idx]);
19641
+ return matched.length > 0 ? matched : docs.slice(0, limit);
19642
+ }
19643
+ async function findDocsFallback(workspaceId, candidateProjectIds, query, limit) {
19644
+ const uniqueCandidates = [];
19645
+ for (const candidate of candidateProjectIds) {
19646
+ if (!uniqueCandidates.includes(candidate)) {
19647
+ uniqueCandidates.push(candidate);
19648
+ }
19649
+ }
19650
+ const maxDocs = Math.max(1, Math.min(limit ?? 20, 50));
19651
+ for (const candidate of uniqueCandidates) {
19652
+ try {
19653
+ const docsResponse = await client.docsList({
19654
+ workspace_id: workspaceId,
19655
+ project_id: candidate,
19656
+ per_page: maxDocs
19657
+ });
19658
+ const items = extractCollectionArray(docsResponse);
19659
+ if (!items || items.length === 0) continue;
19660
+ const ranked = rankDocsForQuery(items, query, maxDocs);
19661
+ if (ranked.length > 0) {
19662
+ return { docs: ranked, project_id: candidate };
19663
+ }
19664
+ } catch {
19665
+ }
19666
+ }
19667
+ return void 0;
19668
+ }
19669
+ async function indexedProjectIdForFolder(folderPath) {
19670
+ const status = await readIndexStatus();
19671
+ const projects = status.projects ?? {};
19672
+ const resolvedFolder = path6.resolve(folderPath);
19673
+ let bestMatch;
19674
+ for (const [projectPath, info] of Object.entries(projects)) {
19675
+ const resolvedProjectPath = path6.resolve(projectPath);
19676
+ const matches = resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${path6.sep}`) || resolvedProjectPath.startsWith(`${resolvedFolder}${path6.sep}`);
19677
+ if (!matches) continue;
19678
+ if (!info?.indexed_at) continue;
19679
+ const indexedAt = new Date(info.indexed_at);
19680
+ if (!Number.isNaN(indexedAt.getTime())) {
19681
+ const diffDays = (Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60 * 24);
19682
+ if (diffDays > 7) continue;
19683
+ }
19684
+ const projectId = typeof info.project_id === "string" && info.project_id.trim() ? info.project_id.trim() : void 0;
19685
+ if (!projectId) continue;
19686
+ const matchLen = resolvedProjectPath.length;
19687
+ if (!bestMatch || matchLen > bestMatch.matchLen) {
19688
+ bestMatch = { projectId, matchLen };
19689
+ }
19690
+ }
19691
+ return bestMatch?.projectId;
19692
+ }
19693
+ async function executeSearchMode(mode, params) {
19694
+ switch (mode) {
19695
+ case "hybrid":
19696
+ return client.searchHybrid(params);
19697
+ case "semantic":
19698
+ return client.searchSemantic(params);
19699
+ case "keyword":
19700
+ return client.searchKeyword(params);
19701
+ case "pattern":
19702
+ return client.searchPattern(params);
19703
+ case "exhaustive":
19704
+ return client.searchExhaustive(params);
19705
+ case "refactor":
19706
+ return client.searchRefactor(params);
19707
+ default:
19708
+ return client.searchHybrid(params);
19709
+ }
19010
19710
  }
19011
19711
  async function validateReadableDirectory(inputPath) {
19012
19712
  const resolvedPath = path6.resolve(inputPath);
@@ -23533,48 +24233,80 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23533
24233
  if (authError) return authError;
23534
24234
  client.checkAndIndexChangedFiles().catch(() => {
23535
24235
  });
23536
- const params = normalizeSearchParams(input);
23537
24236
  const startTime = Date.now();
23538
- const requestedMode = input.mode || "auto";
23539
- let result;
23540
- let toolType;
23541
- switch (requestedMode) {
23542
- case "auto":
23543
- case "hybrid":
23544
- result = await client.searchHybrid(params);
23545
- toolType = "search_hybrid";
23546
- break;
23547
- case "semantic":
23548
- result = await client.searchSemantic(params);
23549
- toolType = "search_semantic";
23550
- break;
23551
- case "keyword":
23552
- result = await client.searchKeyword(params);
23553
- toolType = "search_keyword";
23554
- break;
23555
- case "pattern":
23556
- result = await client.searchPattern(params);
23557
- toolType = "search_pattern";
23558
- break;
23559
- case "exhaustive":
23560
- result = await client.searchExhaustive(params);
23561
- toolType = "search_exhaustive";
23562
- break;
23563
- case "refactor":
23564
- result = await client.searchRefactor(params);
23565
- toolType = "search_refactor";
23566
- break;
23567
- case "team": {
24237
+ const modeInput = input.mode || "auto";
24238
+ const modeRecommendation = recommendSearchMode(input.query);
24239
+ const modeAutoSelected = modeInput === "auto";
24240
+ const requestedMode = modeAutoSelected ? modeRecommendation.mode : modeInput === "auto" ? "hybrid" : modeInput;
24241
+ let workspaceId = resolveWorkspaceId(input.workspace_id);
24242
+ const sessionProjectId = resolveProjectId(void 0);
24243
+ const requestedExplicitProjectId = normalizeUuid(input.project_id);
24244
+ let explicitProjectId = requestedExplicitProjectId;
24245
+ let explicitProjectScopeNote;
24246
+ const folderPath = resolveFolderPath(void 0, sessionManager);
24247
+ let resolvedFolderProjectId;
24248
+ if (folderPath) {
24249
+ const mapping = resolveWorkspace(folderPath);
24250
+ const mappedWorkspaceId = normalizeUuid(mapping.config?.workspace_id);
24251
+ if (!workspaceId && mappedWorkspaceId) {
24252
+ workspaceId = mappedWorkspaceId;
24253
+ }
24254
+ resolvedFolderProjectId = normalizeUuid(mapping.config?.project_id);
24255
+ }
24256
+ const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
24257
+ if (explicitProjectId) {
24258
+ try {
24259
+ const project = await client.getProject(explicitProjectId);
24260
+ const projectWorkspaceId = normalizeUuid(project?.workspace_id || project?.workspaceId);
24261
+ if (workspaceId && projectWorkspaceId && projectWorkspaceId !== workspaceId) {
24262
+ explicitProjectScopeNote = `Explicit project_id ${explicitProjectId} belongs to workspace ${projectWorkspaceId}; auto-corrected to current workspace ${workspaceId}.`;
24263
+ explicitProjectId = void 0;
24264
+ }
24265
+ } catch (error) {
24266
+ if (isNotFoundError(error)) {
24267
+ explicitProjectScopeNote = `Explicit project_id ${explicitProjectId} was not found; auto-corrected using folder/index project mapping.`;
24268
+ explicitProjectId = void 0;
24269
+ } else {
24270
+ throw error;
24271
+ }
24272
+ }
24273
+ }
24274
+ const candidateProjectIds = [];
24275
+ const pushCandidateProjectId = (candidate) => {
24276
+ if (!candidateProjectIds.includes(candidate)) {
24277
+ candidateProjectIds.push(candidate);
24278
+ }
24279
+ };
24280
+ if (explicitProjectId) {
24281
+ pushCandidateProjectId(explicitProjectId);
24282
+ pushCandidateProjectId(resolvedFolderProjectId);
24283
+ pushCandidateProjectId(localIndexProjectId);
24284
+ pushCandidateProjectId(sessionProjectId);
24285
+ } else {
24286
+ pushCandidateProjectId(resolvedFolderProjectId);
24287
+ pushCandidateProjectId(localIndexProjectId);
24288
+ pushCandidateProjectId(sessionProjectId);
24289
+ if (candidateProjectIds.length === 0) {
24290
+ pushCandidateProjectId(void 0);
24291
+ }
24292
+ }
24293
+ pushCandidateProjectId(void 0);
24294
+ const modeToToolType = {
24295
+ hybrid: "search_hybrid",
24296
+ semantic: "search_semantic",
24297
+ keyword: "search_keyword",
24298
+ pattern: "search_pattern",
24299
+ exhaustive: "search_exhaustive",
24300
+ refactor: "search_refactor",
24301
+ team: "search_hybrid"
24302
+ };
24303
+ const runSearchForMode = async (mode, baseParams2) => {
24304
+ if (mode === "team") {
23568
24305
  const isTeamPlanForSearch = await client.isTeamPlan();
23569
24306
  if (!isTeamPlanForSearch) {
23570
- return {
23571
- content: [
23572
- {
23573
- type: "text",
23574
- text: "Team search requires a Team subscription. Your current plan does not include team features.\n\nUpgrade at: https://contextstream.io/pricing"
23575
- }
23576
- ]
23577
- };
24307
+ throw new Error(
24308
+ "Team search requires a Team subscription. Your current plan does not include team features.\n\nUpgrade at: https://contextstream.io/pricing"
24309
+ );
23578
24310
  }
23579
24311
  const teamWorkspacesForSearch = await client.listTeamWorkspaces({ page_size: 100 });
23580
24312
  const workspacesForSearch = teamWorkspacesForSearch?.items || teamWorkspacesForSearch?.data?.items || [];
@@ -23583,14 +24315,15 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23583
24315
  for (const ws of workspacesForSearch.slice(0, 10)) {
23584
24316
  try {
23585
24317
  const wsSearchResult = await client.searchHybrid({
23586
- ...params,
24318
+ ...baseParams2,
23587
24319
  workspace_id: ws.id,
24320
+ project_id: void 0,
23588
24321
  limit: perWorkspaceLimit
23589
24322
  });
23590
- const wsResults = wsSearchResult?.data?.results || wsSearchResult?.results || [];
24323
+ const wsResults = extractSearchEnvelope(wsSearchResult).results;
23591
24324
  allSearchResults.push(
23592
- ...wsResults.map((r) => ({
23593
- ...r,
24325
+ ...wsResults.map((entry) => ({
24326
+ ...entry,
23594
24327
  workspace_name: ws.name,
23595
24328
  workspace_id: ws.id
23596
24329
  }))
@@ -23598,32 +24331,284 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23598
24331
  } catch {
23599
24332
  }
23600
24333
  }
23601
- allSearchResults.sort((a, b) => (b.score || 0) - (a.score || 0));
24334
+ allSearchResults.sort((a, b) => (b?.score || 0) - (a?.score || 0));
23602
24335
  const limitedResults = allSearchResults.slice(0, input.limit || 20);
23603
- result = {
23604
- data: {
23605
- results: limitedResults,
23606
- total: limitedResults.length,
23607
- workspaces_searched: workspacesForSearch.length
24336
+ return {
24337
+ result: {
24338
+ data: {
24339
+ results: limitedResults,
24340
+ total: limitedResults.length,
24341
+ workspaces_searched: workspacesForSearch.length
24342
+ }
24343
+ },
24344
+ executedMode: "team"
24345
+ };
24346
+ }
24347
+ const dispatchMode = mode === "hybrid" ? "hybrid" : mode;
24348
+ let result = await executeSearchMode(dispatchMode, baseParams2);
24349
+ let executedMode = dispatchMode;
24350
+ let fallbackNote;
24351
+ if (shouldRetrySemanticFallback(input.query, dispatchMode, result)) {
24352
+ try {
24353
+ const semanticResult = await executeSearchMode("semantic", baseParams2);
24354
+ if (shouldPreferSemanticResults(result, semanticResult)) {
24355
+ result = semanticResult;
24356
+ executedMode = "semantic";
24357
+ fallbackNote = appendNote(
24358
+ fallbackNote,
24359
+ "Hybrid results looked low-confidence for this natural-language query; retried with semantic and used semantic results."
24360
+ );
24361
+ }
24362
+ } catch {
24363
+ }
24364
+ }
24365
+ const currentResults = () => extractSearchEnvelope(result).results;
24366
+ if (dispatchMode === "keyword" && currentResults().length === 0) {
24367
+ const literal = extractQuotedLiteral(input.query);
24368
+ if (literal) {
24369
+ if (literal !== baseParams2.query) {
24370
+ try {
24371
+ const keywordUnquoted = await executeSearchMode("keyword", {
24372
+ ...baseParams2,
24373
+ query: literal
24374
+ });
24375
+ if (extractSearchEnvelope(keywordUnquoted).results.length > 0) {
24376
+ result = keywordUnquoted;
24377
+ executedMode = "keyword";
24378
+ fallbackNote = appendNote(
24379
+ fallbackNote,
24380
+ "Quoted keyword search returned no results; retried keyword search without quotes."
24381
+ );
24382
+ }
24383
+ } catch {
24384
+ }
24385
+ }
24386
+ if (currentResults().length === 0) {
24387
+ try {
24388
+ const patternResult = await executeSearchMode("pattern", {
24389
+ ...baseParams2,
24390
+ query: escapeRegexLiteral(literal)
24391
+ });
24392
+ if (extractSearchEnvelope(patternResult).results.length > 0) {
24393
+ result = patternResult;
24394
+ executedMode = "pattern";
24395
+ fallbackNote = appendNote(
24396
+ fallbackNote,
24397
+ "Quoted keyword search returned no results; retried literal pattern search."
24398
+ );
24399
+ }
24400
+ } catch {
24401
+ }
24402
+ }
24403
+ if (currentResults().length === 0) {
24404
+ try {
24405
+ const exhaustiveResult = await executeSearchMode("exhaustive", {
24406
+ ...baseParams2,
24407
+ query: literal
24408
+ });
24409
+ if (extractSearchEnvelope(exhaustiveResult).results.length > 0) {
24410
+ result = exhaustiveResult;
24411
+ executedMode = "exhaustive";
24412
+ fallbackNote = appendNote(
24413
+ fallbackNote,
24414
+ "Quoted keyword search returned no results; retried exhaustive search for complete literal coverage."
24415
+ );
24416
+ }
24417
+ } catch {
24418
+ }
24419
+ }
24420
+ }
24421
+ }
24422
+ if ((dispatchMode === "refactor" || dispatchMode === "exhaustive") && currentResults().length === 0) {
24423
+ try {
24424
+ const keywordResult = await executeSearchMode("keyword", baseParams2);
24425
+ if (extractSearchEnvelope(keywordResult).results.length > 0) {
24426
+ result = keywordResult;
24427
+ executedMode = "keyword";
24428
+ fallbackNote = appendNote(
24429
+ fallbackNote,
24430
+ "Requested mode returned no results; retried keyword search and found matches."
24431
+ );
24432
+ }
24433
+ } catch {
24434
+ }
24435
+ }
24436
+ if (dispatchMode === "keyword" && currentResults().length === 0) {
24437
+ if (shouldRetryKeywordWithSymbolModes(input.query)) {
24438
+ try {
24439
+ const refactorResult = await executeSearchMode("refactor", baseParams2);
24440
+ if (extractSearchEnvelope(refactorResult).results.length > 0) {
24441
+ result = refactorResult;
24442
+ executedMode = "refactor";
24443
+ fallbackNote = appendNote(
24444
+ fallbackNote,
24445
+ "Keyword search returned no results; retried refactor search for identifier matching."
24446
+ );
24447
+ }
24448
+ } catch {
24449
+ }
24450
+ if (currentResults().length === 0) {
24451
+ try {
24452
+ const exhaustiveResult = await executeSearchMode("exhaustive", baseParams2);
24453
+ if (extractSearchEnvelope(exhaustiveResult).results.length > 0) {
24454
+ result = exhaustiveResult;
24455
+ executedMode = "exhaustive";
24456
+ fallbackNote = appendNote(
24457
+ fallbackNote,
24458
+ "Keyword search returned no results; retried exhaustive search for complete identifier coverage."
24459
+ );
24460
+ }
24461
+ } catch {
24462
+ }
24463
+ }
24464
+ }
24465
+ if (currentResults().length === 0 && shouldRetryKeywordWithSemantic(input.query)) {
24466
+ try {
24467
+ const semanticResult = await executeSearchMode("semantic", baseParams2);
24468
+ if (extractSearchEnvelope(semanticResult).results.length > 0) {
24469
+ result = semanticResult;
24470
+ executedMode = "semantic";
24471
+ fallbackNote = appendNote(
24472
+ fallbackNote,
24473
+ "Keyword search returned no results; retried semantic search for natural-language intent."
24474
+ );
24475
+ }
24476
+ } catch {
24477
+ }
24478
+ }
24479
+ if (currentResults().length === 0) {
24480
+ try {
24481
+ const hybridResult = await executeSearchMode("hybrid", baseParams2);
24482
+ if (extractSearchEnvelope(hybridResult).results.length > 0) {
24483
+ result = hybridResult;
24484
+ executedMode = "hybrid";
24485
+ fallbackNote = appendNote(
24486
+ fallbackNote,
24487
+ "Keyword search returned no results; retried hybrid search as a broad fallback."
24488
+ );
24489
+ }
24490
+ } catch {
23608
24491
  }
24492
+ }
24493
+ }
24494
+ return { result, executedMode, fallbackNote };
24495
+ };
24496
+ let selected;
24497
+ let explicitScopeHadNoResults = false;
24498
+ const baseParams = normalizeSearchParams({
24499
+ ...input,
24500
+ workspace_id: workspaceId,
24501
+ project_id: void 0,
24502
+ output_format: input.output_format || suggestOutputFormat(input.query, requestedMode === "team" ? "hybrid" : requestedMode)
24503
+ });
24504
+ if (requestedMode === "team") {
24505
+ try {
24506
+ const teamResult = await runSearchForMode("team", {
24507
+ ...baseParams,
24508
+ project_id: void 0
24509
+ });
24510
+ selected = {
24511
+ index: 0,
24512
+ project_id: void 0,
24513
+ result: teamResult.result,
24514
+ executedMode: "team",
24515
+ fallbackNote: teamResult.fallbackNote
23609
24516
  };
23610
- toolType = "search_hybrid";
23611
- break;
24517
+ } catch (error) {
24518
+ const message = error instanceof Error ? error.message : String(error);
24519
+ return errorResult(message);
23612
24520
  }
23613
- default:
23614
- result = await client.searchHybrid(params);
23615
- toolType = "search_hybrid";
24521
+ } else {
24522
+ for (const [index, candidateProjectId] of candidateProjectIds.entries()) {
24523
+ const paramsForCandidate = {
24524
+ ...baseParams,
24525
+ workspace_id: workspaceId,
24526
+ project_id: candidateProjectId
24527
+ };
24528
+ try {
24529
+ const modeResult = await runSearchForMode(requestedMode, paramsForCandidate);
24530
+ const envelope = extractSearchEnvelope(modeResult.result);
24531
+ if (explicitProjectId && index === 0 && envelope.results.length === 0) {
24532
+ explicitScopeHadNoResults = true;
24533
+ }
24534
+ if (envelope.results.length > 0) {
24535
+ selected = {
24536
+ index,
24537
+ project_id: candidateProjectId,
24538
+ result: modeResult.result,
24539
+ executedMode: modeResult.executedMode,
24540
+ fallbackNote: modeResult.fallbackNote
24541
+ };
24542
+ break;
24543
+ }
24544
+ if (!selected) {
24545
+ selected = {
24546
+ index,
24547
+ project_id: candidateProjectId,
24548
+ result: modeResult.result,
24549
+ executedMode: modeResult.executedMode,
24550
+ fallbackNote: modeResult.fallbackNote
24551
+ };
24552
+ }
24553
+ } catch (error) {
24554
+ if (isNotFoundError(error)) {
24555
+ continue;
24556
+ }
24557
+ throw error;
24558
+ }
24559
+ }
24560
+ }
24561
+ if (!selected) {
24562
+ const baseMessage = "Project not found for current context. Call init(...) in this folder or pass a valid project_id explicitly.";
24563
+ return errorResult(
24564
+ explicitProjectScopeNote ? `${explicitProjectScopeNote} ${baseMessage}` : baseMessage
24565
+ );
24566
+ }
24567
+ let modeFallbackNote = selected.fallbackNote;
24568
+ if (explicitProjectScopeNote) {
24569
+ modeFallbackNote = appendNote(modeFallbackNote, explicitProjectScopeNote);
24570
+ }
24571
+ if (explicitScopeHadNoResults && selected.index > 0) {
24572
+ modeFallbackNote = appendNote(
24573
+ modeFallbackNote,
24574
+ "Explicit project_id returned no results; retried folder/local project mapping."
24575
+ );
24576
+ }
24577
+ if (!selected.project_id && selected.index > 0) {
24578
+ modeFallbackNote = appendNote(
24579
+ modeFallbackNote,
24580
+ "Project-scoped search returned no results; retried workspace-wide scope."
24581
+ );
24582
+ }
24583
+ if (folderPath && localIndexProjectId && selected.project_id !== localIndexProjectId) {
24584
+ const resolvedScope = selected.project_id || "workspace-wide scope";
24585
+ modeFallbackNote = appendNote(
24586
+ modeFallbackNote,
24587
+ `Local index mapping for this folder points to project_id ${localIndexProjectId}; search resolved ${resolvedScope}. If results look stale, run project(action="ingest_local", path="${folderPath}").`
24588
+ );
24589
+ }
24590
+ if (requestedMode !== "team" && !explicitProjectId && sessionManager && folderPath && (selected.project_id !== sessionProjectId || workspaceId !== resolveWorkspaceId(void 0))) {
24591
+ sessionManager.updateScope({
24592
+ workspace_id: workspaceId,
24593
+ project_id: selected.project_id || sessionProjectId,
24594
+ folder_path: folderPath
24595
+ });
23616
24596
  }
24597
+ const docsFallback = requestedMode !== "team" && isDocLookupQuery(input.query) && extractSearchEnvelope(selected.result).results.length === 0 ? await findDocsFallback(workspaceId, candidateProjectIds, input.query, input.limit) : void 0;
23617
24598
  const roundTripMs = Date.now() - startTime;
23618
- const data = result?.data || result;
23619
- const results = data?.results || [];
23620
- const total = data?.total ?? results.length;
24599
+ const { results, total } = extractSearchEnvelope(selected.result);
23621
24600
  const lines = [];
23622
24601
  if (SHOW_TIMING) {
23623
24602
  lines.push(`\u2713 ${total} results in ${roundTripMs}ms`);
23624
24603
  } else {
23625
24604
  lines.push(`\u{1F50D} ${total} results for "${input.query}"`);
23626
24605
  }
24606
+ if (modeAutoSelected) {
24607
+ lines.push(`Mode auto-selected: \`${requestedMode}\`. ${modeRecommendation.reason}`);
24608
+ }
24609
+ if (modeFallbackNote) {
24610
+ lines.push(modeFallbackNote);
24611
+ }
23627
24612
  if (results.length > 0) {
23628
24613
  lines.push("");
23629
24614
  results.forEach((r, i) => {
@@ -23637,15 +24622,69 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23637
24622
  });
23638
24623
  } else {
23639
24624
  lines.push("");
23640
- lines.push("No results found. Try a different query or search mode.");
24625
+ if (docsFallback?.docs?.length) {
24626
+ const docsScope = docsFallback.project_id ? ` (project_id: ${docsFallback.project_id})` : "";
24627
+ lines.push(
24628
+ `No codebase results found. This query looks like a docs lookup; found ${docsFallback.docs.length} docs in ContextStream memory${docsScope}:`
24629
+ );
24630
+ lines.push("");
24631
+ docsFallback.docs.slice(0, 10).forEach((doc, i) => {
24632
+ const title = doc?.title || doc?.name || doc?.summary || "Untitled";
24633
+ const id = doc?.id || "unknown";
24634
+ const docType = doc?.doc_type || "doc";
24635
+ lines.push(`${i + 1}. ${title} (id: ${id}) [${docType}]`);
24636
+ });
24637
+ lines.push("");
24638
+ lines.push(
24639
+ `Use memory(action="get_doc", doc_id="...") to open a specific doc or memory(action="list_docs") to browse more.`
24640
+ );
24641
+ } else {
24642
+ lines.push("No results found. Try a different query or search mode.");
24643
+ if (isDocLookupQuery(input.query)) {
24644
+ lines.push(
24645
+ `This query appears to target saved docs. Try memory(action="list_docs") and then memory(action="get_doc", doc_id="...").`
24646
+ );
24647
+ }
24648
+ if (folderPath) {
24649
+ lines.push(
24650
+ `If results seem stale after recent edits, refresh local index with project(action="ingest_local", path="${folderPath}").`
24651
+ );
24652
+ }
24653
+ }
24654
+ }
24655
+ const rawData = selected.result?.data;
24656
+ const structuredData = rawData && typeof rawData === "object" ? { ...rawData } : { ...selected.result };
24657
+ if (requestedMode !== "team") {
24658
+ if (requestedExplicitProjectId) {
24659
+ structuredData.requested_explicit_project_id = requestedExplicitProjectId;
24660
+ structuredData.effective_explicit_project_id = explicitProjectId;
24661
+ structuredData.explicit_project_autocorrected = requestedExplicitProjectId !== explicitProjectId;
24662
+ }
24663
+ if (selected.project_id !== sessionProjectId) {
24664
+ structuredData.original_project_id = sessionProjectId;
24665
+ }
24666
+ structuredData.resolved_project_id = selected.project_id;
24667
+ structuredData.resolution_rank = selected.index;
24668
+ }
24669
+ if (docsFallback?.docs?.length) {
24670
+ structuredData.memory_docs_fallback = {
24671
+ project_id: docsFallback.project_id,
24672
+ docs: docsFallback.docs,
24673
+ count: docsFallback.docs.length
24674
+ };
23641
24675
  }
24676
+ const resultWithMeta = {
24677
+ ...selected.result,
24678
+ data: structuredData
24679
+ };
23642
24680
  lines.push("");
23643
24681
  lines.push("--- Full Results ---");
23644
- lines.push(formatContent(result));
24682
+ lines.push(formatContent(resultWithMeta));
23645
24683
  const outputText = lines.join("\n");
24684
+ const toolType = modeToToolType[selected.executedMode] || "search_hybrid";
23646
24685
  trackToolTokenSavings(client, toolType, outputText, {
23647
- workspace_id: params.workspace_id,
23648
- project_id: params.project_id
24686
+ workspace_id: workspaceId,
24687
+ project_id: selected.project_id
23649
24688
  });
23650
24689
  return {
23651
24690
  content: [{ type: "text", text: outputText }]
@@ -23771,8 +24810,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23771
24810
  })
23772
24811
  },
23773
24812
  async (input) => {
23774
- const workspaceId = resolveWorkspaceId(input.workspace_id);
23775
- const projectId = resolveProjectId(input.project_id);
24813
+ let workspaceId = resolveWorkspaceId(input.workspace_id);
24814
+ const explicitProjectId = normalizeUuid(input.project_id);
24815
+ let projectId = explicitProjectId || resolveProjectId(void 0);
24816
+ const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
23776
24817
  switch (input.action) {
23777
24818
  case "capture": {
23778
24819
  if (!input.event_type || !input.title || !input.content) {
@@ -24595,8 +25636,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
24595
25636
  })
24596
25637
  },
24597
25638
  async (input) => {
24598
- const workspaceId = resolveWorkspaceId(input.workspace_id);
24599
- const projectId = resolveProjectId(input.project_id);
25639
+ let workspaceId = resolveWorkspaceId(input.workspace_id);
25640
+ const explicitProjectId = normalizeUuid(input.project_id);
25641
+ let projectId = explicitProjectId || resolveProjectId(void 0);
25642
+ const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
24600
25643
  switch (input.action) {
24601
25644
  case "create_event": {
24602
25645
  if (!input.event_type || !input.title || !input.content) {
@@ -25662,8 +26705,10 @@ ${formatContent(result)}`
25662
26705
  })
25663
26706
  },
25664
26707
  async (input) => {
25665
- const workspaceId = resolveWorkspaceId(input.workspace_id);
25666
- const projectId = resolveProjectId(input.project_id);
26708
+ let workspaceId = resolveWorkspaceId(input.workspace_id);
26709
+ const explicitProjectId = normalizeUuid(input.project_id);
26710
+ let projectId = explicitProjectId || resolveProjectId(void 0);
26711
+ const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
25667
26712
  switch (input.action) {
25668
26713
  case "list": {
25669
26714
  const result = await client.listProjects({
@@ -25713,10 +26758,50 @@ ${formatContent(result)}`
25713
26758
  if (!projectId) {
25714
26759
  return errorResult("index requires: project_id");
25715
26760
  }
25716
- const result = await client.indexProject(projectId);
25717
- return {
25718
- content: [{ type: "text", text: formatContent(result) }]
25719
- };
26761
+ try {
26762
+ const result = await client.indexProject(projectId);
26763
+ return {
26764
+ content: [{ type: "text", text: formatContent(result) }]
26765
+ };
26766
+ } catch (error) {
26767
+ if (!isRequiresIngestEndpointError(error)) {
26768
+ throw error;
26769
+ }
26770
+ if (!folderPath) {
26771
+ return errorResult(
26772
+ 'Index endpoint is unavailable for this local project type and no folder context is set. Run project(action="ingest_local", path="<folder>").'
26773
+ );
26774
+ }
26775
+ const validPath = await validateReadableDirectory(folderPath);
26776
+ if (!validPath.ok) {
26777
+ return errorResult(
26778
+ `Index endpoint is unavailable for this project type and folder context path is invalid: ${folderPath}. ${validPath.error}`
26779
+ );
26780
+ }
26781
+ const ingestOptions = {
26782
+ ...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
26783
+ ...input.overwrite !== void 0 && { overwrite: input.overwrite },
26784
+ ...input.force !== void 0 && { force: input.force }
26785
+ };
26786
+ startBackgroundIngest(projectId, validPath.resolvedPath, ingestOptions);
26787
+ const response = {
26788
+ status: "started",
26789
+ message: "Index endpoint unavailable for this project type; started ingest_local fallback in background",
26790
+ project_id: projectId,
26791
+ path: validPath.resolvedPath,
26792
+ fallback_action: "ingest_local",
26793
+ note: "Use 'project' with action 'index_status' to monitor progress."
26794
+ };
26795
+ return {
26796
+ content: [
26797
+ {
26798
+ type: "text",
26799
+ text: `Index endpoint is unavailable for this local project type. Started ingest_local in background for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
26800
+ }
26801
+ ],
26802
+ structuredContent: response
26803
+ };
26804
+ }
25720
26805
  }
25721
26806
  case "overview": {
25722
26807
  if (!projectId) {
@@ -25746,12 +26831,91 @@ ${formatContent(result)}`
25746
26831
  };
25747
26832
  }
25748
26833
  case "index_status": {
25749
- if (!projectId) {
25750
- return errorResult("index_status requires: project_id");
26834
+ let resolvedFolderProjectId;
26835
+ if (folderPath) {
26836
+ const mapping = resolveWorkspace(folderPath);
26837
+ const mappedWorkspaceId = normalizeUuid(mapping.config?.workspace_id);
26838
+ if (!workspaceId && mappedWorkspaceId) {
26839
+ workspaceId = mappedWorkspaceId;
26840
+ }
26841
+ resolvedFolderProjectId = normalizeUuid(mapping.config?.project_id);
26842
+ }
26843
+ const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
26844
+ const candidateIds = [];
26845
+ const pushCandidate = (id) => {
26846
+ if (id && !candidateIds.includes(id)) {
26847
+ candidateIds.push(id);
26848
+ }
26849
+ };
26850
+ if (explicitProjectId) {
26851
+ pushCandidate(explicitProjectId);
26852
+ pushCandidate(resolvedFolderProjectId);
26853
+ pushCandidate(localIndexProjectId);
26854
+ pushCandidate(projectId);
26855
+ } else {
26856
+ pushCandidate(resolvedFolderProjectId);
26857
+ pushCandidate(localIndexProjectId);
26858
+ pushCandidate(projectId);
26859
+ }
26860
+ if (candidateIds.length === 0) {
26861
+ return errorResult(
26862
+ "index_status requires: project_id. Call init(...) first or pass project_id explicitly."
26863
+ );
26864
+ }
26865
+ let selected;
26866
+ for (const [index, candidateId] of candidateIds.entries()) {
26867
+ try {
26868
+ const statusResult = await client.projectIndexStatus(candidateId);
26869
+ const data = statusResult?.data ?? statusResult;
26870
+ const apiIndexed = Boolean(data?.indexed);
26871
+ if (apiIndexed) {
26872
+ selected = { index, projectId: candidateId, result: statusResult, apiIndexed };
26873
+ break;
26874
+ }
26875
+ if (!selected) {
26876
+ selected = { index, projectId: candidateId, result: statusResult, apiIndexed };
26877
+ }
26878
+ } catch (error) {
26879
+ if (isNotFoundError(error)) {
26880
+ continue;
26881
+ }
26882
+ throw error;
26883
+ }
26884
+ }
26885
+ if (!selected) {
26886
+ return errorResult(
26887
+ "Project not found for current context. Call init(...) in this folder or pass a valid project_id explicitly."
26888
+ );
26889
+ }
26890
+ const originalProjectId = projectId;
26891
+ if (projectId !== selected.projectId && folderPath) {
26892
+ projectId = selected.projectId;
26893
+ sessionManager?.updateScope({
26894
+ workspace_id: workspaceId,
26895
+ project_id: projectId,
26896
+ folder_path: folderPath
26897
+ });
26898
+ }
26899
+ const locallyIndexed = localIndexProjectId !== void 0 ? localIndexProjectId === selected.projectId : Boolean(folderPath && await indexedProjectIdForFolder(folderPath));
26900
+ const indexed = selected.apiIndexed || locallyIndexed;
26901
+ const response = selected.result && typeof selected.result === "object" ? { ...selected.result } : {};
26902
+ const responseData = response.data && typeof response.data === "object" ? { ...response.data } : {};
26903
+ responseData.indexed = indexed;
26904
+ if (locallyIndexed && !selected.apiIndexed) {
26905
+ responseData.indexed_source = "local";
25751
26906
  }
25752
- const result = await client.projectIndexStatus(projectId);
26907
+ if (originalProjectId && originalProjectId !== selected.projectId) {
26908
+ responseData.original_project_id = originalProjectId;
26909
+ }
26910
+ responseData.resolved_project_id = selected.projectId;
26911
+ responseData.resolution_rank = selected.index;
26912
+ response.data = responseData;
26913
+ const text = indexed ? locallyIndexed && !selected.apiIndexed ? "Project index is ready (local state). Semantic search is available." : "Project index is ready. Semantic search is available." : 'Project index not found. Run project(action="ingest_local", path="<folder>") to start indexing.';
25753
26914
  return {
25754
- content: [{ type: "text", text: formatContent(result) }]
26915
+ content: [{ type: "text", text: `${text}
26916
+
26917
+ ${formatContent(response)}` }],
26918
+ structuredContent: response
25755
26919
  };
25756
26920
  }
25757
26921
  case "index_history": {
@@ -25774,13 +26938,14 @@ ${formatContent(result)}`
25774
26938
  };
25775
26939
  }
25776
26940
  case "ingest_local": {
25777
- if (!input.path) {
26941
+ const ingestPath = input.path || input.folder_path;
26942
+ if (!ingestPath) {
25778
26943
  return errorResult("ingest_local requires: path");
25779
26944
  }
25780
26945
  if (!projectId) {
25781
26946
  return errorResult("ingest_local requires: project_id");
25782
26947
  }
25783
- const validPath = await validateReadableDirectory(input.path);
26948
+ const validPath = await validateReadableDirectory(ingestPath);
25784
26949
  if (!validPath.ok) {
25785
26950
  return errorResult(validPath.error);
25786
26951
  }
@@ -25793,7 +26958,7 @@ ${formatContent(result)}`
25793
26958
  const forceNote = input.force ? " (force mode - version checks bypassed)" : "";
25794
26959
  const result = {
25795
26960
  status: "started",
25796
- message: `Ingestion running in background${forceNote}`,
26961
+ message: `Updating index in background${forceNote}`,
25797
26962
  project_id: projectId,
25798
26963
  path: validPath.resolvedPath,
25799
26964
  ...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
@@ -25805,7 +26970,7 @@ ${formatContent(result)}`
25805
26970
  content: [
25806
26971
  {
25807
26972
  type: "text",
25808
- text: `Ingestion started in background${forceNote} for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
26973
+ text: `Updating index in background${forceNote} for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
25809
26974
  }
25810
26975
  ]
25811
26976
  };
@@ -26770,13 +27935,13 @@ Example workflow:
26770
27935
  );
26771
27936
  }
26772
27937
  if (input.file_path) {
26773
- const fs21 = await import("fs/promises");
27938
+ const fs22 = await import("fs/promises");
26774
27939
  const pathModule = await import("path");
26775
27940
  const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
26776
27941
  const resolvedPath = pathModule.resolve(filePath);
26777
27942
  let fileStats;
26778
27943
  try {
26779
- fileStats = await fs21.stat(resolvedPath);
27944
+ fileStats = await fs22.stat(resolvedPath);
26780
27945
  } catch {
26781
27946
  return errorResult(`File not found: ${resolvedPath}`);
26782
27947
  }
@@ -26823,7 +27988,7 @@ Example workflow:
26823
27988
  mime_type: mimeType,
26824
27989
  tags: input.tags
26825
27990
  });
26826
- const fileBuffer = await fs21.readFile(resolvedPath);
27991
+ const fileBuffer = await fs22.readFile(resolvedPath);
26827
27992
  const uploadResponse = await fetch(uploadInit.upload_url, {
26828
27993
  method: "PUT",
26829
27994
  headers: uploadInit.headers,
@@ -28227,11 +29392,40 @@ var SessionManager = class _SessionManager {
28227
29392
  this.folderPath = contextFolderPath;
28228
29393
  }
28229
29394
  }
29395
+ /**
29396
+ * Update active workspace/project scope without resetting session identity.
29397
+ * Used when tools auto-resolve stale scope from folder/local index context.
29398
+ */
29399
+ updateScope(input) {
29400
+ const nextWorkspaceId = typeof input.workspace_id === "string" && input.workspace_id.trim() ? input.workspace_id : void 0;
29401
+ const nextProjectId = typeof input.project_id === "string" && input.project_id.trim() ? input.project_id : void 0;
29402
+ const nextFolderPath = typeof input.folder_path === "string" && input.folder_path.trim() ? input.folder_path : void 0;
29403
+ if (!this.context) {
29404
+ this.context = {};
29405
+ }
29406
+ if (nextWorkspaceId) {
29407
+ this.context.workspace_id = nextWorkspaceId;
29408
+ }
29409
+ if (nextProjectId) {
29410
+ this.context.project_id = nextProjectId;
29411
+ }
29412
+ if (nextFolderPath) {
29413
+ this.context.folder_path = nextFolderPath;
29414
+ this.folderPath = nextFolderPath;
29415
+ }
29416
+ if (nextWorkspaceId || nextProjectId) {
29417
+ this.initialized = true;
29418
+ this.client.setDefaults({
29419
+ workspace_id: typeof this.context.workspace_id === "string" ? this.context.workspace_id : void 0,
29420
+ project_id: typeof this.context.project_id === "string" ? this.context.project_id : void 0
29421
+ });
29422
+ }
29423
+ }
28230
29424
  /**
28231
29425
  * Set the folder path hint (can be passed from tools that know the workspace path)
28232
29426
  */
28233
- setFolderPath(path22) {
28234
- this.folderPath = path22;
29427
+ setFolderPath(path23) {
29428
+ this.folderPath = path23;
28235
29429
  }
28236
29430
  /**
28237
29431
  * Mark that context_smart has been called in this session.
@@ -28421,7 +29615,7 @@ var SessionManager = class _SessionManager {
28421
29615
  }
28422
29616
  if (this.ideRoots.length === 0) {
28423
29617
  const cwd = process.cwd();
28424
- const fs21 = await import("fs");
29618
+ const fs22 = await import("fs");
28425
29619
  const projectIndicators = [
28426
29620
  ".git",
28427
29621
  "package.json",
@@ -28431,7 +29625,7 @@ var SessionManager = class _SessionManager {
28431
29625
  ];
28432
29626
  const hasProjectIndicator = projectIndicators.some((f) => {
28433
29627
  try {
28434
- return fs21.existsSync(`${cwd}/${f}`);
29628
+ return fs22.existsSync(`${cwd}/${f}`);
28435
29629
  } catch {
28436
29630
  return false;
28437
29631
  }
@@ -29010,9 +30204,9 @@ async function runHttpGateway() {
29010
30204
 
29011
30205
  // src/index.ts
29012
30206
  init_version();
29013
- import { existsSync as existsSync18, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
29014
- import { homedir as homedir20 } from "os";
29015
- import { join as join23 } from "path";
30207
+ import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
30208
+ import { homedir as homedir21 } from "os";
30209
+ import { join as join24 } from "path";
29016
30210
 
29017
30211
  // src/setup.ts
29018
30212
  import * as fs7 from "node:fs/promises";
@@ -29705,10 +30899,91 @@ function createProgressBar(progress, width = 30) {
29705
30899
  const emptyBar = "\u2591".repeat(empty);
29706
30900
  return `${colors.cyan}${filledBar}${colors.dim}${emptyBar}${colors.reset}`;
29707
30901
  }
30902
+ async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun, rl) {
30903
+ if (!workspaceId || workspaceId === "dry-run") {
30904
+ return void 0;
30905
+ }
30906
+ const folderName = path8.basename(cwd) || "project";
30907
+ let projects = [];
30908
+ try {
30909
+ const result = await client.listProjects({
30910
+ workspace_id: workspaceId,
30911
+ page_size: 200
30912
+ });
30913
+ projects = Array.isArray(result?.items) ? result.items : Array.isArray(result?.data?.items) ? result.data.items : [];
30914
+ } catch {
30915
+ projects = [];
30916
+ }
30917
+ projects = projects.filter((p) => typeof p?.id === "string" && typeof p?.name === "string").sort((a, b) => (a.name || "").toLowerCase().localeCompare((b.name || "").toLowerCase()));
30918
+ const local = readLocalConfig(cwd);
30919
+ const linked = local?.project_id ? projects.find((p) => p.id === local.project_id) : void 0;
30920
+ const folderMatch = projects.find((p) => p.name?.toLowerCase() === folderName.toLowerCase());
30921
+ const options = [];
30922
+ const seen = /* @__PURE__ */ new Set();
30923
+ if (linked?.id && linked?.name) {
30924
+ seen.add(linked.id);
30925
+ options.push({
30926
+ label: `Use currently linked project: ${linked.name} (${linked.id})`,
30927
+ kind: "existing",
30928
+ project: { id: linked.id, name: linked.name }
30929
+ });
30930
+ }
30931
+ if (folderMatch?.id && folderMatch?.name && !seen.has(folderMatch.id)) {
30932
+ seen.add(folderMatch.id);
30933
+ options.push({
30934
+ label: `Use project matching this folder: ${folderMatch.name} (${folderMatch.id})`,
30935
+ kind: "existing",
30936
+ project: { id: folderMatch.id, name: folderMatch.name }
30937
+ });
30938
+ }
30939
+ for (const project of projects) {
30940
+ if (!project.id || !project.name || seen.has(project.id)) continue;
30941
+ seen.add(project.id);
30942
+ options.push({
30943
+ label: `Use existing project: ${project.name} (${project.id})`,
30944
+ kind: "existing",
30945
+ project: { id: project.id, name: project.name }
30946
+ });
30947
+ }
30948
+ options.push({ label: `Create new project: ${folderName}`, kind: "create" });
30949
+ options.push({ label: "Skip project selection", kind: "skip" });
30950
+ if (options.length === 0) {
30951
+ return void 0;
30952
+ }
30953
+ console.log("\nProject selection (current directory):");
30954
+ options.forEach((opt, i) => console.log(` ${i + 1}) ${opt.label}`));
30955
+ const choiceRaw = normalizeInput(
30956
+ await rl.question(`Choose [1-${options.length}] (default 1): `)
30957
+ );
30958
+ const choiceNum = Number.parseInt(choiceRaw || "1", 10);
30959
+ const selected = Number.isFinite(choiceNum) ? options[choiceNum - 1] : options[0];
30960
+ if (!selected || selected.kind === "skip") {
30961
+ return void 0;
30962
+ }
30963
+ if (selected.kind === "existing" && selected.project) {
30964
+ return selected.project;
30965
+ }
30966
+ if (selected.kind === "create") {
30967
+ if (dryRun) {
30968
+ return { id: "dry-run-project", name: folderName };
30969
+ }
30970
+ const created = await client.createProject({
30971
+ workspace_id: workspaceId,
30972
+ name: folderName,
30973
+ description: `Project linked from ${cwd}`
30974
+ });
30975
+ const projectId = typeof created?.id === "string" ? created.id : typeof created?.data?.id === "string" ? created.data.id : void 0;
30976
+ const projectName = typeof created?.name === "string" ? created.name : typeof created?.data?.name === "string" ? created.data.name : folderName;
30977
+ if (projectId) {
30978
+ return { id: projectId, name: projectName };
30979
+ }
30980
+ }
30981
+ return void 0;
30982
+ }
29708
30983
  async function indexProjectWithProgress(client, projectPath, workspaceId) {
29709
30984
  const projectName = path8.basename(projectPath);
29710
30985
  console.log(`
29711
- ${colors.bright}Indexing: ${projectName}${colors.reset}`);
30986
+ ${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
29712
30987
  console.log(`${colors.dim}${projectPath}${colors.reset}
29713
30988
  `);
29714
30989
  let projectId;
@@ -29758,12 +31033,12 @@ ${colors.bright}Indexing: ${projectName}${colors.reset}`);
29758
31033
  const countResult = await countIndexableFiles(projectPath, { maxFiles: 5e4 });
29759
31034
  totalFiles = countResult.count;
29760
31035
  if (countResult.stopped) {
29761
- console.log(`${colors.dim}Found 50,000+ indexable files${colors.reset}`);
31036
+ console.log(`${colors.dim}Found 50,000+ files for indexing${colors.reset}`);
29762
31037
  } else if (totalFiles === 0) {
29763
31038
  console.log(`${colors.dim}No indexable files found${colors.reset}`);
29764
31039
  return;
29765
31040
  } else {
29766
- console.log(`${colors.dim}Found ${totalFiles.toLocaleString()} indexable files${colors.reset}`);
31041
+ console.log(`${colors.dim}Found ${totalFiles.toLocaleString()} files for indexing${colors.reset}`);
29767
31042
  }
29768
31043
  } catch {
29769
31044
  console.log(`${colors.dim}Scanning files...${colors.reset}`);
@@ -29826,15 +31101,19 @@ ${colors.bright}Indexing: ${projectName}${colors.reset}`);
29826
31101
  const finalSize = formatBytes(bytesIndexed);
29827
31102
  process.stdout.write("\r" + " ".repeat(120) + "\r");
29828
31103
  if (failedBatches > 0) {
29829
- console.log(`${colors.yellow}\u2713${colors.reset} Indexed ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files (${finalSize}) in ${elapsed}s (${failedBatches} batches had errors)`);
31104
+ console.log(
31105
+ `${colors.yellow}\u2713${colors.reset} Index update complete: ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files indexed (${finalSize}) in ${elapsed}s (${failedBatches} batches had errors)`
31106
+ );
29830
31107
  } else {
29831
- console.log(`${colors.green}\u2713${colors.reset} Indexed ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files (${finalSize}) in ${elapsed}s`);
31108
+ console.log(
31109
+ `${colors.green}\u2713${colors.reset} Index update complete: ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files indexed (${finalSize}) in ${elapsed}s`
31110
+ );
29832
31111
  }
29833
31112
  } catch (err) {
29834
31113
  clearInterval(progressInterval);
29835
31114
  process.stdout.write("\r" + " ".repeat(120) + "\r");
29836
31115
  const msg = err instanceof Error ? err.message : String(err);
29837
- console.log(`${colors.yellow}! Indexing failed: ${msg}${colors.reset}`);
31116
+ console.log(`${colors.yellow}! Index update failed: ${msg}${colors.reset}`);
29838
31117
  }
29839
31118
  }
29840
31119
  async function runSetupWizard(args) {
@@ -29973,10 +31252,10 @@ Code: ${device.user_code}`);
29973
31252
  if (poll && poll.status === "pending") {
29974
31253
  const intervalSeconds = typeof poll.interval === "number" ? poll.interval : 5;
29975
31254
  const waitMs = Math.max(1, intervalSeconds) * 1e3;
29976
- await new Promise((resolve16) => setTimeout(resolve16, waitMs));
31255
+ await new Promise((resolve18) => setTimeout(resolve18, waitMs));
29977
31256
  continue;
29978
31257
  }
29979
- await new Promise((resolve16) => setTimeout(resolve16, 1e3));
31258
+ await new Promise((resolve18) => setTimeout(resolve18, 1e3));
29980
31259
  }
29981
31260
  if (!accessToken) {
29982
31261
  throw new Error(
@@ -30024,6 +31303,7 @@ Code: ${device.user_code}`);
30024
31303
  }
30025
31304
  let workspaceId;
30026
31305
  let workspaceName;
31306
+ let selectedCurrentProject;
30027
31307
  console.log("Workspace setup:");
30028
31308
  console.log(" 1) Create a new workspace");
30029
31309
  console.log(" 2) Select an existing workspace");
@@ -30081,6 +31361,21 @@ Code: ${device.user_code}`);
30081
31361
  }
30082
31362
  }
30083
31363
  }
31364
+ if (workspaceId && workspaceId !== "dry-run") {
31365
+ selectedCurrentProject = await selectProjectForCurrentDirectory(
31366
+ client,
31367
+ process.cwd(),
31368
+ workspaceId,
31369
+ dryRun,
31370
+ rl
31371
+ );
31372
+ if (selectedCurrentProject) {
31373
+ console.log(
31374
+ `Selected project: ${selectedCurrentProject.name} (${selectedCurrentProject.id})
31375
+ `
31376
+ );
31377
+ }
31378
+ }
30084
31379
  const NO_HOOKS_EDITORS2 = ["codex", "aider", "antigravity"];
30085
31380
  const getModeForEditor = (editor) => NO_HOOKS_EDITORS2.includes(editor) ? "full" : "bootstrap";
30086
31381
  const detectedPlanName = await client.getPlanName();
@@ -30399,6 +31694,10 @@ Applying to ${projects.length} project(s)...`);
30399
31694
  workspace_id: workspaceId,
30400
31695
  workspace_name: workspaceName,
30401
31696
  create_parent_mapping: createParentMapping,
31697
+ ...path8.resolve(projectPath) === path8.resolve(process.cwd()) && selectedCurrentProject ? {
31698
+ project_id: selectedCurrentProject.id,
31699
+ project_name: selectedCurrentProject.name
31700
+ } : {},
30402
31701
  // Include version and config info for desktop app compatibility
30403
31702
  version: VERSION,
30404
31703
  configured_editors: configuredEditors,
@@ -30538,7 +31837,7 @@ Applying to ${projects.length} project(s)...`);
30538
31837
  if (indexingEnabled) {
30539
31838
  await indexProjectWithProgress(client, process.cwd(), cwdConfig.workspace_id);
30540
31839
  } else {
30541
- console.log("\nSkipping indexing. You can index later with: contextstream-mcp index <path>");
31840
+ console.log("\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>");
30542
31841
  }
30543
31842
  }
30544
31843
  } catch {
@@ -30553,7 +31852,7 @@ Applying to ${projects.length} project(s)...`);
30553
31852
  console.log("though larger projects may take a bit longer.\n");
30554
31853
  console.log("Your code is private and securely stored.\n");
30555
31854
  const indexChoice = normalizeInput(
30556
- await rl.question("Perform indexing for full-featured context? [Y/n]: ")
31855
+ await rl.question("Update index for full-featured context now? [Y/n]: ")
30557
31856
  ).toLowerCase();
30558
31857
  const indexingEnabled = indexChoice !== "n" && indexChoice !== "no";
30559
31858
  for (const projectPath of projects) {
@@ -30571,7 +31870,7 @@ Applying to ${projects.length} project(s)...`);
30571
31870
  await indexProjectWithProgress(client, projectPath, workspaceId);
30572
31871
  }
30573
31872
  } else {
30574
- console.log("\nSkipping indexing. You can index later with: contextstream-mcp index <path>");
31873
+ console.log("\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>");
30575
31874
  }
30576
31875
  }
30577
31876
  console.log("\nDone.");
@@ -30602,14 +31901,14 @@ Applying to ${projects.length} project(s)...`);
30602
31901
  // src/index.ts
30603
31902
  var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
30604
31903
  function showFirstRunMessage() {
30605
- const configDir = join23(homedir20(), ".contextstream");
30606
- const starShownFile = join23(configDir, ".star-shown");
31904
+ const configDir = join24(homedir21(), ".contextstream");
31905
+ const starShownFile = join24(configDir, ".star-shown");
30607
31906
  if (existsSync18(starShownFile)) {
30608
31907
  return;
30609
31908
  }
30610
31909
  if (!existsSync18(configDir)) {
30611
31910
  try {
30612
- mkdirSync6(configDir, { recursive: true });
31911
+ mkdirSync7(configDir, { recursive: true });
30613
31912
  } catch {
30614
31913
  return;
30615
31914
  }
@@ -30622,7 +31921,7 @@ function showFirstRunMessage() {
30622
31921
  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");
30623
31922
  console.error("");
30624
31923
  try {
30625
- writeFileSync7(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
31924
+ writeFileSync8(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
30626
31925
  } catch {
30627
31926
  }
30628
31927
  }