@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/hooks/auto-rules.js +1 -1
- package/dist/hooks/post-write.js +1 -1
- package/dist/hooks/pre-tool-use.js +308 -32
- package/dist/hooks/runner.js +714 -380
- package/dist/hooks/session-init.js +168 -48
- package/dist/hooks/user-prompt-submit.js +127 -17
- package/dist/index.js +1711 -412
- package/package.json +1 -1
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((
|
|
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
|
-
|
|
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(() =>
|
|
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(
|
|
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(
|
|
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 = (
|
|
767
|
-
if (!isString(
|
|
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 (!
|
|
773
|
+
if (!path23) {
|
|
774
774
|
return doThrow(`path must not be empty`, TypeError);
|
|
775
775
|
}
|
|
776
|
-
if (checkPath.isNotRelative(
|
|
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 = (
|
|
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
|
|
815
|
+
const path23 = originalPath && checkPath.convert(originalPath);
|
|
816
816
|
checkPath(
|
|
817
|
-
|
|
817
|
+
path23,
|
|
818
818
|
originalPath,
|
|
819
819
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
820
820
|
);
|
|
821
|
-
return this._t(
|
|
821
|
+
return this._t(path23, cache, checkUnignored, slices);
|
|
822
822
|
}
|
|
823
|
-
checkIgnore(
|
|
824
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
825
|
-
return this.test(
|
|
823
|
+
checkIgnore(path23) {
|
|
824
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path23)) {
|
|
825
|
+
return this.test(path23);
|
|
826
826
|
}
|
|
827
|
-
const slices =
|
|
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(
|
|
840
|
+
return this._rules.test(path23, false, MODE_CHECK_IGNORE);
|
|
841
841
|
}
|
|
842
|
-
_t(
|
|
843
|
-
if (
|
|
844
|
-
return cache[
|
|
842
|
+
_t(path23, cache, checkUnignored, slices) {
|
|
843
|
+
if (path23 in cache) {
|
|
844
|
+
return cache[path23];
|
|
845
845
|
}
|
|
846
846
|
if (!slices) {
|
|
847
|
-
slices =
|
|
847
|
+
slices = path23.split(SLASH).filter(Boolean);
|
|
848
848
|
}
|
|
849
849
|
slices.pop();
|
|
850
850
|
if (!slices.length) {
|
|
851
|
-
return cache[
|
|
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[
|
|
859
|
+
return cache[path23] = parent.ignored ? parent : this._rules.test(path23, checkUnignored, MODE_IGNORE);
|
|
860
860
|
}
|
|
861
|
-
ignores(
|
|
862
|
-
return this._test(
|
|
861
|
+
ignores(path23) {
|
|
862
|
+
return this._test(path23, this._ignoreCache, false).ignored;
|
|
863
863
|
}
|
|
864
864
|
createFilter() {
|
|
865
|
-
return (
|
|
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(
|
|
872
|
-
return this._test(
|
|
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 = (
|
|
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 = (
|
|
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
|
-
| **
|
|
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:
|
|
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
|
-
|
|
|
3305
|
-
| **
|
|
3306
|
-
|
|
3307
|
-
**
|
|
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
|
-
|
|
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
|
|
3312
|
+
## Why Default Context-First
|
|
3315
3313
|
|
|
3316
|
-
\u274C **
|
|
3317
|
-
\u2705 **
|
|
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()\`
|
|
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
|
-
**
|
|
3832
|
-
1. \`init()\`
|
|
3833
|
-
2. \`context(user_message="<msg>")\`
|
|
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
|
-
|
|
|
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
|
-
##
|
|
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
|
-
|
|
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. **
|
|
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
|
|
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
|
|
4576
|
-
import * as
|
|
4577
|
-
import { homedir as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
4801
|
+
const cwdPath = path11.resolve(cwd);
|
|
4615
4802
|
for (const [projectPath, info] of Object.entries(projects)) {
|
|
4616
4803
|
try {
|
|
4617
|
-
const indexedPath =
|
|
4618
|
-
if (cwdPath === indexedPath || cwdPath.startsWith(indexedPath +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5051
|
+
fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}
|
|
4726
5052
|
`);
|
|
4727
5053
|
if (!isIndexed) {
|
|
4728
|
-
|
|
5054
|
+
fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Project not indexed, allowing
|
|
4729
5055
|
`);
|
|
4730
|
-
|
|
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
|
-
|
|
5060
|
+
fs10.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
4740
5061
|
`);
|
|
4741
5062
|
if (isDiscoveryGlob(pattern)) {
|
|
4742
|
-
const msg = `
|
|
4743
|
-
|
|
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 = `
|
|
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 = '
|
|
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 = '
|
|
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 = '
|
|
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}
|
|
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
|
-
|
|
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 =
|
|
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
|
|
4843
|
-
import * as
|
|
4844
|
-
import { homedir as
|
|
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 =
|
|
5165
|
+
let searchDir = path12.resolve(cwd);
|
|
4847
5166
|
for (let i = 0; i < 5; i++) {
|
|
4848
5167
|
if (!API_KEY2) {
|
|
4849
|
-
const mcpPath =
|
|
4850
|
-
if (
|
|
5168
|
+
const mcpPath = path12.join(searchDir, ".mcp.json");
|
|
5169
|
+
if (fs11.existsSync(mcpPath)) {
|
|
4851
5170
|
try {
|
|
4852
|
-
const content =
|
|
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 =
|
|
4870
|
-
if (
|
|
5188
|
+
const csConfigPath = path12.join(searchDir, ".contextstream", "config.json");
|
|
5189
|
+
if (fs11.existsSync(csConfigPath)) {
|
|
4871
5190
|
try {
|
|
4872
|
-
const content =
|
|
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 =
|
|
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 =
|
|
4890
|
-
if (
|
|
5208
|
+
const homeMcpPath = path12.join(homedir10(), ".mcp.json");
|
|
5209
|
+
if (fs11.existsSync(homeMcpPath)) {
|
|
4891
5210
|
try {
|
|
4892
|
-
const content =
|
|
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 =
|
|
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]
|
|
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>")
|
|
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
|
|
5537
|
-
import * as
|
|
5538
|
-
import { homedir as
|
|
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 =
|
|
5867
|
+
let searchDir = path13.resolve(cwd);
|
|
5541
5868
|
for (let i = 0; i < 5; i++) {
|
|
5542
5869
|
if (!API_KEY3) {
|
|
5543
|
-
const mcpPath =
|
|
5544
|
-
if (
|
|
5870
|
+
const mcpPath = path13.join(searchDir, ".mcp.json");
|
|
5871
|
+
if (fs12.existsSync(mcpPath)) {
|
|
5545
5872
|
try {
|
|
5546
|
-
const content =
|
|
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 =
|
|
5561
|
-
if (
|
|
5887
|
+
const csConfigPath = path13.join(searchDir, ".contextstream", "config.json");
|
|
5888
|
+
if (fs12.existsSync(csConfigPath)) {
|
|
5562
5889
|
try {
|
|
5563
|
-
const content =
|
|
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 =
|
|
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 =
|
|
5578
|
-
if (
|
|
5904
|
+
const homeMcpPath = path13.join(homedir11(), ".mcp.json");
|
|
5905
|
+
if (fs12.existsSync(homeMcpPath)) {
|
|
5579
5906
|
try {
|
|
5580
|
-
const content =
|
|
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 =
|
|
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 &&
|
|
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
|
|
5863
|
-
import * as
|
|
5864
|
-
import { homedir as
|
|
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 (!
|
|
5868
|
-
const stat2 =
|
|
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 =
|
|
5878
|
-
if (!
|
|
5879
|
-
|
|
6204
|
+
const dir = path14.dirname(MARKER_FILE);
|
|
6205
|
+
if (!fs13.existsSync(dir)) {
|
|
6206
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
5880
6207
|
}
|
|
5881
|
-
|
|
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 (!
|
|
5911
|
-
const content =
|
|
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 =
|
|
5936
|
-
const projectSettingsPath =
|
|
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 =
|
|
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
|
|
6016
|
-
import * as
|
|
6017
|
-
import { homedir as
|
|
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 =
|
|
6346
|
+
let searchDir = path15.resolve(cwd);
|
|
6020
6347
|
for (let i = 0; i < 5; i++) {
|
|
6021
6348
|
if (!API_KEY5) {
|
|
6022
|
-
const mcpPath =
|
|
6023
|
-
if (
|
|
6349
|
+
const mcpPath = path15.join(searchDir, ".mcp.json");
|
|
6350
|
+
if (fs14.existsSync(mcpPath)) {
|
|
6024
6351
|
try {
|
|
6025
|
-
const content =
|
|
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 =
|
|
6040
|
-
if (
|
|
6366
|
+
const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
|
|
6367
|
+
if (fs14.existsSync(csConfigPath)) {
|
|
6041
6368
|
try {
|
|
6042
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6057
|
-
if (
|
|
6383
|
+
const homeMcpPath = path15.join(homedir13(), ".mcp.json");
|
|
6384
|
+
if (fs14.existsSync(homeMcpPath)) {
|
|
6058
6385
|
try {
|
|
6059
|
-
const content =
|
|
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
|
|
6194
|
-
import * as
|
|
6195
|
-
import { homedir as
|
|
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 =
|
|
6524
|
+
let searchDir = path16.resolve(cwd);
|
|
6198
6525
|
for (let i = 0; i < 5; i++) {
|
|
6199
6526
|
if (!API_KEY6) {
|
|
6200
|
-
const mcpPath =
|
|
6201
|
-
if (
|
|
6527
|
+
const mcpPath = path16.join(searchDir, ".mcp.json");
|
|
6528
|
+
if (fs15.existsSync(mcpPath)) {
|
|
6202
6529
|
try {
|
|
6203
|
-
const content =
|
|
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 =
|
|
6218
|
-
if (
|
|
6544
|
+
const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
|
|
6545
|
+
if (fs15.existsSync(csConfigPath)) {
|
|
6219
6546
|
try {
|
|
6220
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6235
|
-
if (
|
|
6561
|
+
const homeMcpPath = path16.join(homedir14(), ".mcp.json");
|
|
6562
|
+
if (fs15.existsSync(homeMcpPath)) {
|
|
6236
6563
|
try {
|
|
6237
|
-
const content =
|
|
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
|
|
6390
|
-
import * as
|
|
6391
|
-
import { homedir as
|
|
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 =
|
|
6720
|
+
let searchDir = path17.resolve(cwd);
|
|
6394
6721
|
for (let i = 0; i < 5; i++) {
|
|
6395
6722
|
if (!API_KEY7) {
|
|
6396
|
-
const mcpPath =
|
|
6397
|
-
if (
|
|
6723
|
+
const mcpPath = path17.join(searchDir, ".mcp.json");
|
|
6724
|
+
if (fs16.existsSync(mcpPath)) {
|
|
6398
6725
|
try {
|
|
6399
|
-
const content =
|
|
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 =
|
|
6414
|
-
if (
|
|
6740
|
+
const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
|
|
6741
|
+
if (fs16.existsSync(csConfigPath)) {
|
|
6415
6742
|
try {
|
|
6416
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6431
|
-
if (
|
|
6757
|
+
const homeMcpPath = path17.join(homedir15(), ".mcp.json");
|
|
6758
|
+
if (fs16.existsSync(homeMcpPath)) {
|
|
6432
6759
|
try {
|
|
6433
|
-
const content =
|
|
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
|
|
6535
|
-
import * as
|
|
6536
|
-
import { homedir as
|
|
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 =
|
|
6865
|
+
let searchDir = path18.resolve(cwd);
|
|
6539
6866
|
for (let i = 0; i < 5; i++) {
|
|
6540
6867
|
if (!API_KEY8) {
|
|
6541
|
-
const mcpPath =
|
|
6542
|
-
if (
|
|
6868
|
+
const mcpPath = path18.join(searchDir, ".mcp.json");
|
|
6869
|
+
if (fs17.existsSync(mcpPath)) {
|
|
6543
6870
|
try {
|
|
6544
|
-
const content =
|
|
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 =
|
|
6559
|
-
if (
|
|
6885
|
+
const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
|
|
6886
|
+
if (fs17.existsSync(csConfigPath)) {
|
|
6560
6887
|
try {
|
|
6561
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6576
|
-
if (
|
|
6902
|
+
const homeMcpPath = path18.join(homedir16(), ".mcp.json");
|
|
6903
|
+
if (fs17.existsSync(homeMcpPath)) {
|
|
6577
6904
|
try {
|
|
6578
|
-
const content =
|
|
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
|
|
6704
|
-
import * as
|
|
6705
|
-
import { homedir as
|
|
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 =
|
|
7034
|
+
let searchDir = path19.resolve(cwd);
|
|
6708
7035
|
for (let i = 0; i < 5; i++) {
|
|
6709
7036
|
if (!API_KEY9) {
|
|
6710
|
-
const mcpPath =
|
|
6711
|
-
if (
|
|
7037
|
+
const mcpPath = path19.join(searchDir, ".mcp.json");
|
|
7038
|
+
if (fs18.existsSync(mcpPath)) {
|
|
6712
7039
|
try {
|
|
6713
|
-
const content =
|
|
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 =
|
|
6728
|
-
if (
|
|
7054
|
+
const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
|
|
7055
|
+
if (fs18.existsSync(csConfigPath)) {
|
|
6729
7056
|
try {
|
|
6730
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6745
|
-
if (
|
|
7071
|
+
const homeMcpPath = path19.join(homedir17(), ".mcp.json");
|
|
7072
|
+
if (fs18.existsSync(homeMcpPath)) {
|
|
6746
7073
|
try {
|
|
6747
|
-
const content =
|
|
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
|
|
6865
|
-
import * as
|
|
6866
|
-
import { homedir as
|
|
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 =
|
|
7195
|
+
let searchDir = path20.resolve(cwd);
|
|
6869
7196
|
for (let i = 0; i < 5; i++) {
|
|
6870
7197
|
if (!API_KEY10) {
|
|
6871
|
-
const mcpPath =
|
|
6872
|
-
if (
|
|
7198
|
+
const mcpPath = path20.join(searchDir, ".mcp.json");
|
|
7199
|
+
if (fs19.existsSync(mcpPath)) {
|
|
6873
7200
|
try {
|
|
6874
|
-
const content =
|
|
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 =
|
|
6892
|
-
if (
|
|
7218
|
+
const csConfigPath = path20.join(searchDir, ".contextstream", "config.json");
|
|
7219
|
+
if (fs19.existsSync(csConfigPath)) {
|
|
6893
7220
|
try {
|
|
6894
|
-
const content =
|
|
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 =
|
|
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 =
|
|
6912
|
-
if (
|
|
7238
|
+
const homeMcpPath = path20.join(homedir18(), ".mcp.json");
|
|
7239
|
+
if (fs19.existsSync(homeMcpPath)) {
|
|
6913
7240
|
try {
|
|
6914
|
-
const content =
|
|
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(
|
|
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(
|
|
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 =
|
|
7030
|
-
if (!
|
|
7360
|
+
const filePath = path20.join(folderPath, rule.filename);
|
|
7361
|
+
if (!fs19.existsSync(filePath)) continue;
|
|
7031
7362
|
try {
|
|
7032
|
-
const existing =
|
|
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
|
-
|
|
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
|
|
7120
|
-
import * as
|
|
7121
|
-
import { homedir as
|
|
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 =
|
|
7457
|
+
let searchDir = path21.resolve(cwd);
|
|
7124
7458
|
for (let i = 0; i < 5; i++) {
|
|
7125
7459
|
if (!API_KEY11) {
|
|
7126
|
-
const mcpPath =
|
|
7127
|
-
if (
|
|
7460
|
+
const mcpPath = path21.join(searchDir, ".mcp.json");
|
|
7461
|
+
if (fs20.existsSync(mcpPath)) {
|
|
7128
7462
|
try {
|
|
7129
|
-
const content =
|
|
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 =
|
|
7144
|
-
if (
|
|
7477
|
+
const csConfigPath = path21.join(searchDir, ".contextstream", "config.json");
|
|
7478
|
+
if (fs20.existsSync(csConfigPath)) {
|
|
7145
7479
|
try {
|
|
7146
|
-
const content =
|
|
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 =
|
|
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 =
|
|
7164
|
-
if (
|
|
7497
|
+
const homeMcpPath = path21.join(homedir19(), ".mcp.json");
|
|
7498
|
+
if (fs20.existsSync(homeMcpPath)) {
|
|
7165
7499
|
try {
|
|
7166
|
-
const content =
|
|
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 || !
|
|
7523
|
+
if (!transcriptPath || !fs20.existsSync(transcriptPath)) {
|
|
7190
7524
|
return stats;
|
|
7191
7525
|
}
|
|
7192
7526
|
try {
|
|
7193
|
-
const content =
|
|
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
|
|
7552
|
-
import * as
|
|
7553
|
-
import { homedir as
|
|
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
|
|
7920
|
+
return path22.join(homedir20(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
7587
7921
|
} else if (platform2 === "win32") {
|
|
7588
|
-
return
|
|
7922
|
+
return path22.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
7589
7923
|
} else {
|
|
7590
|
-
return
|
|
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 =
|
|
7608
|
-
if (
|
|
7941
|
+
const projectMcpPath = path22.join(searchDir, ".mcp.json");
|
|
7942
|
+
if (fs21.existsSync(projectMcpPath)) {
|
|
7609
7943
|
try {
|
|
7610
|
-
const content =
|
|
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 =
|
|
7958
|
+
const parentDir = path22.dirname(searchDir);
|
|
7625
7959
|
if (parentDir === searchDir) break;
|
|
7626
7960
|
searchDir = parentDir;
|
|
7627
7961
|
}
|
|
7628
|
-
const globalMcpPath =
|
|
7629
|
-
if (
|
|
7962
|
+
const globalMcpPath = path22.join(homedir20(), ".mcp.json");
|
|
7963
|
+
if (fs21.existsSync(globalMcpPath)) {
|
|
7630
7964
|
try {
|
|
7631
|
-
const content =
|
|
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
|
-
|
|
7647
|
-
|
|
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 (
|
|
7984
|
+
if (fs21.existsSync(cursorPath)) {
|
|
7651
7985
|
try {
|
|
7652
|
-
const content =
|
|
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 (
|
|
8002
|
+
if (fs21.existsSync(claudeDesktopPath)) {
|
|
7669
8003
|
try {
|
|
7670
|
-
const content =
|
|
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
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
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 (
|
|
8024
|
+
if (fs21.existsSync(vsPath)) {
|
|
7691
8025
|
try {
|
|
7692
|
-
const content =
|
|
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 =
|
|
7708
|
-
if (
|
|
8041
|
+
const credentialsPath = path22.join(homedir20(), ".contextstream", "credentials.json");
|
|
8042
|
+
if (fs21.existsSync(credentialsPath)) {
|
|
7709
8043
|
try {
|
|
7710
|
-
const content =
|
|
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:
|
|
8309
|
-
const fullPath = [...
|
|
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,
|
|
8759
|
+
constructor(parent, value, path23, key) {
|
|
8426
8760
|
this._cachedPath = [];
|
|
8427
8761
|
this.parent = parent;
|
|
8428
8762
|
this.data = value;
|
|
8429
|
-
this._path =
|
|
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((
|
|
12328
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
11995
12329
|
}
|
|
11996
|
-
async function request(config,
|
|
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 =
|
|
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(
|
|
12157
|
-
if (/\/github(\/|$)/i.test(
|
|
12158
|
-
if (/\/slack(\/|$)/i.test(
|
|
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 =
|
|
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
|
|
12783
|
+
const fs22 = __require("fs");
|
|
12450
12784
|
const pathModule = __require("path");
|
|
12451
|
-
const rootHasGit =
|
|
12452
|
-
const entries =
|
|
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 (
|
|
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
|
|
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 =
|
|
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 {
|
|
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
|
|
15072
|
+
const path23 = c.file_path || "file";
|
|
14665
15073
|
const content = c.content?.slice(0, 150) || "";
|
|
14666
|
-
return { path:
|
|
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,
|
|
18012
|
+
function buildParamDescription(key, path23) {
|
|
17595
18013
|
const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
|
|
17596
|
-
const parent =
|
|
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,
|
|
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, [...
|
|
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,
|
|
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
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23563
|
-
|
|
23564
|
-
|
|
23565
|
-
|
|
23566
|
-
|
|
23567
|
-
|
|
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
|
-
|
|
23571
|
-
|
|
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
|
-
...
|
|
24318
|
+
...baseParams2,
|
|
23587
24319
|
workspace_id: ws.id,
|
|
24320
|
+
project_id: void 0,
|
|
23588
24321
|
limit: perWorkspaceLimit
|
|
23589
24322
|
});
|
|
23590
|
-
const wsResults = wsSearchResult
|
|
24323
|
+
const wsResults = extractSearchEnvelope(wsSearchResult).results;
|
|
23591
24324
|
allSearchResults.push(
|
|
23592
|
-
...wsResults.map((
|
|
23593
|
-
...
|
|
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
|
|
24334
|
+
allSearchResults.sort((a, b) => (b?.score || 0) - (a?.score || 0));
|
|
23602
24335
|
const limitedResults = allSearchResults.slice(0, input.limit || 20);
|
|
23603
|
-
|
|
23604
|
-
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
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
|
-
|
|
23611
|
-
|
|
24517
|
+
} catch (error) {
|
|
24518
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24519
|
+
return errorResult(message);
|
|
23612
24520
|
}
|
|
23613
|
-
|
|
23614
|
-
|
|
23615
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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:
|
|
23648
|
-
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
|
-
|
|
23775
|
-
const
|
|
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
|
-
|
|
24599
|
-
const
|
|
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
|
-
|
|
25666
|
-
const
|
|
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
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
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
|
-
|
|
25750
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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: `
|
|
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: `
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
28234
|
-
this.folderPath =
|
|
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
|
|
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
|
|
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
|
|
29014
|
-
import { homedir as
|
|
29015
|
-
import { join as
|
|
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}
|
|
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+
|
|
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()}
|
|
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(
|
|
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(
|
|
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}!
|
|
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((
|
|
31255
|
+
await new Promise((resolve18) => setTimeout(resolve18, waitMs));
|
|
29977
31256
|
continue;
|
|
29978
31257
|
}
|
|
29979
|
-
await new Promise((
|
|
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("\
|
|
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("
|
|
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("\
|
|
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 =
|
|
30606
|
-
const starShownFile =
|
|
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
|
-
|
|
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
|
-
|
|
31924
|
+
writeFileSync8(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
30626
31925
|
} catch {
|
|
30627
31926
|
}
|
|
30628
31927
|
}
|