@contextstream/mcp-server 0.4.61 → 0.4.63
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 +1932 -413
- 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;
|
|
@@ -16811,6 +17229,170 @@ var TASK_HINTS = {
|
|
|
16811
17229
|
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
16812
17230
|
};
|
|
16813
17231
|
|
|
17232
|
+
// src/project-index-utils.ts
|
|
17233
|
+
var INDEX_FRESH_HOURS = 1;
|
|
17234
|
+
var INDEX_RECENT_HOURS = 24;
|
|
17235
|
+
var INDEX_STALE_HOURS = 24 * 7;
|
|
17236
|
+
function asRecord(value) {
|
|
17237
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
17238
|
+
}
|
|
17239
|
+
function candidateObjects(result) {
|
|
17240
|
+
const root = asRecord(result);
|
|
17241
|
+
const data = asRecord(root?.data);
|
|
17242
|
+
if (data && root) return [data, root];
|
|
17243
|
+
if (data) return [data];
|
|
17244
|
+
if (root) return [root];
|
|
17245
|
+
return [];
|
|
17246
|
+
}
|
|
17247
|
+
function readBoolean(candidates, key) {
|
|
17248
|
+
for (const candidate of candidates) {
|
|
17249
|
+
const value = candidate[key];
|
|
17250
|
+
if (typeof value === "boolean") {
|
|
17251
|
+
return value;
|
|
17252
|
+
}
|
|
17253
|
+
}
|
|
17254
|
+
return void 0;
|
|
17255
|
+
}
|
|
17256
|
+
function readNumber(candidates, keys) {
|
|
17257
|
+
for (const candidate of candidates) {
|
|
17258
|
+
for (const key of keys) {
|
|
17259
|
+
const value = candidate[key];
|
|
17260
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
17261
|
+
return value;
|
|
17262
|
+
}
|
|
17263
|
+
if (typeof value === "string" && value.trim()) {
|
|
17264
|
+
const parsed = Number(value);
|
|
17265
|
+
if (Number.isFinite(parsed)) {
|
|
17266
|
+
return parsed;
|
|
17267
|
+
}
|
|
17268
|
+
}
|
|
17269
|
+
}
|
|
17270
|
+
}
|
|
17271
|
+
return void 0;
|
|
17272
|
+
}
|
|
17273
|
+
function readString(candidates, key) {
|
|
17274
|
+
for (const candidate of candidates) {
|
|
17275
|
+
const value = candidate[key];
|
|
17276
|
+
if (typeof value === "string" && value.trim()) {
|
|
17277
|
+
return value.trim();
|
|
17278
|
+
}
|
|
17279
|
+
}
|
|
17280
|
+
return void 0;
|
|
17281
|
+
}
|
|
17282
|
+
function extractIndexTimestamp(result) {
|
|
17283
|
+
const candidates = candidateObjects(result);
|
|
17284
|
+
for (const key of ["last_updated", "indexed_at", "last_indexed"]) {
|
|
17285
|
+
const raw = readString(candidates, key);
|
|
17286
|
+
if (!raw) continue;
|
|
17287
|
+
const parsed = new Date(raw);
|
|
17288
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
17289
|
+
return parsed;
|
|
17290
|
+
}
|
|
17291
|
+
}
|
|
17292
|
+
return void 0;
|
|
17293
|
+
}
|
|
17294
|
+
function apiResultReportsIndexed(result) {
|
|
17295
|
+
const candidates = candidateObjects(result);
|
|
17296
|
+
const indexed = readBoolean(candidates, "indexed");
|
|
17297
|
+
if (indexed !== void 0) {
|
|
17298
|
+
return indexed;
|
|
17299
|
+
}
|
|
17300
|
+
const indexedFiles = readNumber(candidates, ["indexed_files", "indexed_file_count"]) ?? 0;
|
|
17301
|
+
if (indexedFiles > 0) {
|
|
17302
|
+
return true;
|
|
17303
|
+
}
|
|
17304
|
+
const totalFiles = readNumber(candidates, ["total_files"]) ?? 0;
|
|
17305
|
+
if (totalFiles > 0) {
|
|
17306
|
+
const status = readString(candidates, "status")?.toLowerCase();
|
|
17307
|
+
if (status === "completed" || status === "ready") {
|
|
17308
|
+
return true;
|
|
17309
|
+
}
|
|
17310
|
+
}
|
|
17311
|
+
return false;
|
|
17312
|
+
}
|
|
17313
|
+
function apiResultIsIndexing(result) {
|
|
17314
|
+
const candidates = candidateObjects(result);
|
|
17315
|
+
const projectIndexState = readString(candidates, "project_index_state")?.toLowerCase();
|
|
17316
|
+
if (projectIndexState === "indexing" || projectIndexState === "committing") {
|
|
17317
|
+
return true;
|
|
17318
|
+
}
|
|
17319
|
+
const status = readString(candidates, "status")?.toLowerCase();
|
|
17320
|
+
if (status === "indexing" || status === "processing") {
|
|
17321
|
+
return true;
|
|
17322
|
+
}
|
|
17323
|
+
const pendingFiles = readNumber(candidates, ["pending_files"]) ?? 0;
|
|
17324
|
+
return pendingFiles > 0;
|
|
17325
|
+
}
|
|
17326
|
+
function countFromObject(value) {
|
|
17327
|
+
const obj = asRecord(value);
|
|
17328
|
+
if (!obj) return void 0;
|
|
17329
|
+
if (Array.isArray(obj.entries)) {
|
|
17330
|
+
return obj.entries.length;
|
|
17331
|
+
}
|
|
17332
|
+
if (Array.isArray(obj.history)) {
|
|
17333
|
+
return obj.history.length;
|
|
17334
|
+
}
|
|
17335
|
+
return void 0;
|
|
17336
|
+
}
|
|
17337
|
+
function indexHistoryEntryCount(result) {
|
|
17338
|
+
const rootCount = countFromObject(result);
|
|
17339
|
+
if (typeof rootCount === "number") {
|
|
17340
|
+
return rootCount;
|
|
17341
|
+
}
|
|
17342
|
+
const root = asRecord(result);
|
|
17343
|
+
const dataCount = countFromObject(root?.data);
|
|
17344
|
+
if (typeof dataCount === "number") {
|
|
17345
|
+
return dataCount;
|
|
17346
|
+
}
|
|
17347
|
+
if (Array.isArray(result)) {
|
|
17348
|
+
return result.length;
|
|
17349
|
+
}
|
|
17350
|
+
if (Array.isArray(root?.data)) {
|
|
17351
|
+
return root.data.length;
|
|
17352
|
+
}
|
|
17353
|
+
return 0;
|
|
17354
|
+
}
|
|
17355
|
+
function classifyIndexFreshness(indexed, ageHours) {
|
|
17356
|
+
if (!indexed) {
|
|
17357
|
+
return "missing";
|
|
17358
|
+
}
|
|
17359
|
+
if (typeof ageHours !== "number" || Number.isNaN(ageHours)) {
|
|
17360
|
+
return "unknown";
|
|
17361
|
+
}
|
|
17362
|
+
if (ageHours <= INDEX_FRESH_HOURS) {
|
|
17363
|
+
return "fresh";
|
|
17364
|
+
}
|
|
17365
|
+
if (ageHours <= INDEX_RECENT_HOURS) {
|
|
17366
|
+
return "recent";
|
|
17367
|
+
}
|
|
17368
|
+
if (ageHours <= INDEX_STALE_HOURS) {
|
|
17369
|
+
return "aging";
|
|
17370
|
+
}
|
|
17371
|
+
return "stale";
|
|
17372
|
+
}
|
|
17373
|
+
function classifyIndexConfidence(indexed, apiIndexed, locallyIndexed, freshness) {
|
|
17374
|
+
if (!indexed) {
|
|
17375
|
+
return {
|
|
17376
|
+
confidence: "low",
|
|
17377
|
+
reason: "Neither API status nor local index metadata currently indicates a usable index."
|
|
17378
|
+
};
|
|
17379
|
+
}
|
|
17380
|
+
if (apiIndexed && locallyIndexed) {
|
|
17381
|
+
const reason = freshness === "stale" ? "API and local metadata agree, but index age indicates stale coverage." : "API and local metadata agree for this project scope.";
|
|
17382
|
+
return { confidence: "high", reason };
|
|
17383
|
+
}
|
|
17384
|
+
if (apiIndexed || locallyIndexed) {
|
|
17385
|
+
return {
|
|
17386
|
+
confidence: "medium",
|
|
17387
|
+
reason: "Only one source reports index readiness (API vs local metadata)."
|
|
17388
|
+
};
|
|
17389
|
+
}
|
|
17390
|
+
return {
|
|
17391
|
+
confidence: "low",
|
|
17392
|
+
reason: "Index state is inferred but lacks corroborating API/local metadata."
|
|
17393
|
+
};
|
|
17394
|
+
}
|
|
17395
|
+
|
|
16814
17396
|
// src/tools.ts
|
|
16815
17397
|
function parseBoolEnvDefault(raw, fallback) {
|
|
16816
17398
|
if (raw === void 0) return fallback;
|
|
@@ -17591,9 +18173,9 @@ function humanizeKey(raw) {
|
|
|
17591
18173
|
const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
|
|
17592
18174
|
return withSpaces.toLowerCase();
|
|
17593
18175
|
}
|
|
17594
|
-
function buildParamDescription(key,
|
|
18176
|
+
function buildParamDescription(key, path23) {
|
|
17595
18177
|
const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
|
|
17596
|
-
const parent =
|
|
18178
|
+
const parent = path23[path23.length - 1];
|
|
17597
18179
|
if (parent === "target") {
|
|
17598
18180
|
if (key === "id") return "Target identifier (module path, function id, etc.).";
|
|
17599
18181
|
if (key === "type") return "Target type (module, file, function, type, variable).";
|
|
@@ -17624,7 +18206,7 @@ function getDescription(schema) {
|
|
|
17624
18206
|
if (def?.description && def.description.trim()) return def.description;
|
|
17625
18207
|
return void 0;
|
|
17626
18208
|
}
|
|
17627
|
-
function applyParamDescriptions(schema,
|
|
18209
|
+
function applyParamDescriptions(schema, path23 = []) {
|
|
17628
18210
|
if (!(schema instanceof external_exports.ZodObject)) {
|
|
17629
18211
|
return schema;
|
|
17630
18212
|
}
|
|
@@ -17635,7 +18217,7 @@ function applyParamDescriptions(schema, path22 = []) {
|
|
|
17635
18217
|
let nextField = field;
|
|
17636
18218
|
const existingDescription = getDescription(field);
|
|
17637
18219
|
if (field instanceof external_exports.ZodObject) {
|
|
17638
|
-
const nested = applyParamDescriptions(field, [...
|
|
18220
|
+
const nested = applyParamDescriptions(field, [...path23, key]);
|
|
17639
18221
|
if (nested !== field) {
|
|
17640
18222
|
nextField = nested;
|
|
17641
18223
|
changed = true;
|
|
@@ -17647,7 +18229,7 @@ function applyParamDescriptions(schema, path22 = []) {
|
|
|
17647
18229
|
changed = true;
|
|
17648
18230
|
}
|
|
17649
18231
|
} else {
|
|
17650
|
-
nextField = nextField.describe(buildParamDescription(key,
|
|
18232
|
+
nextField = nextField.describe(buildParamDescription(key, path23));
|
|
17651
18233
|
changed = true;
|
|
17652
18234
|
}
|
|
17653
18235
|
nextShape[key] = nextField;
|
|
@@ -19008,6 +19590,288 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
|
|
|
19008
19590
|
typeof ctx?.project_id === "string" ? ctx.project_id : void 0
|
|
19009
19591
|
);
|
|
19010
19592
|
}
|
|
19593
|
+
function isNotFoundError(error) {
|
|
19594
|
+
return error instanceof HttpError && error.status === 404;
|
|
19595
|
+
}
|
|
19596
|
+
function isRequiresIngestEndpointError(error) {
|
|
19597
|
+
if (!(error instanceof HttpError)) return false;
|
|
19598
|
+
if (error.status !== 400) return false;
|
|
19599
|
+
const message = String(error.message || "").toLowerCase();
|
|
19600
|
+
return message.includes("requires using the ingest endpoint");
|
|
19601
|
+
}
|
|
19602
|
+
function appendNote(existing, extra) {
|
|
19603
|
+
return existing ? `${existing} ${extra}` : extra;
|
|
19604
|
+
}
|
|
19605
|
+
function extractSearchEnvelope(result) {
|
|
19606
|
+
const data = result?.data ?? result ?? {};
|
|
19607
|
+
const results = Array.isArray(data?.results) ? data.results : [];
|
|
19608
|
+
const total = typeof data?.total === "number" ? data.total : results.length;
|
|
19609
|
+
return { results, total };
|
|
19610
|
+
}
|
|
19611
|
+
function maxResultScore(result) {
|
|
19612
|
+
const { results } = extractSearchEnvelope(result);
|
|
19613
|
+
let max = 0;
|
|
19614
|
+
for (const entry of results) {
|
|
19615
|
+
const score = typeof entry?.score === "number" ? entry.score : 0;
|
|
19616
|
+
if (score > max) max = score;
|
|
19617
|
+
}
|
|
19618
|
+
return max;
|
|
19619
|
+
}
|
|
19620
|
+
function extractQuotedLiteral(query) {
|
|
19621
|
+
const trimmed = query.trim();
|
|
19622
|
+
if (trimmed.length < 2) return void 0;
|
|
19623
|
+
const isDoubleQuoted = trimmed.startsWith('"') && trimmed.endsWith('"');
|
|
19624
|
+
const isSingleQuoted = trimmed.startsWith("'") && trimmed.endsWith("'");
|
|
19625
|
+
if (!isDoubleQuoted && !isSingleQuoted) return void 0;
|
|
19626
|
+
const literal = trimmed.slice(1, -1).trim();
|
|
19627
|
+
return literal || void 0;
|
|
19628
|
+
}
|
|
19629
|
+
function escapeRegexLiteral(input) {
|
|
19630
|
+
return input.replace(/[\\.+*?()[\]{}|^$]/g, "\\$&");
|
|
19631
|
+
}
|
|
19632
|
+
const COUNT_QUERY_PREFIXES = ["how many ", "count ", "count of ", "number of ", "total "];
|
|
19633
|
+
const ALL_MATCH_KEYWORDS = [
|
|
19634
|
+
"all occurrences",
|
|
19635
|
+
"all matches",
|
|
19636
|
+
"find all",
|
|
19637
|
+
"every usage",
|
|
19638
|
+
"every occurrence",
|
|
19639
|
+
"all usages"
|
|
19640
|
+
];
|
|
19641
|
+
const TEAM_QUERY_KEYWORDS = [
|
|
19642
|
+
"team-wide",
|
|
19643
|
+
"teamwide",
|
|
19644
|
+
"cross-project",
|
|
19645
|
+
"cross project",
|
|
19646
|
+
"across projects",
|
|
19647
|
+
"all workspaces",
|
|
19648
|
+
"all projects"
|
|
19649
|
+
];
|
|
19650
|
+
const DOC_QUERY_KEYWORDS = ["doc", "docs", "document", "documents", "spec", "specification", "plan", "roadmap"];
|
|
19651
|
+
const DOC_LOOKUP_VERBS = ["list", "show", "find", "open", "read", "lookup", "look up", "get"];
|
|
19652
|
+
const QUESTION_WORDS = ["how", "what", "where", "why", "when", "which", "who", "does", "is", "can", "should"];
|
|
19653
|
+
const HYBRID_LOW_CONFIDENCE_SCORE = 0.35;
|
|
19654
|
+
const SEMANTIC_SWITCH_MIN_IMPROVEMENT = 0.08;
|
|
19655
|
+
function isIdentifierQuery(query) {
|
|
19656
|
+
const trimmed = query.trim();
|
|
19657
|
+
if (!trimmed || trimmed.length < 2 || trimmed.includes(" ")) return false;
|
|
19658
|
+
if (!/^[A-Za-z0-9_:]+$/.test(trimmed)) return false;
|
|
19659
|
+
const hasMixedCase = /[a-z]/.test(trimmed) && /[A-Z]/.test(trimmed);
|
|
19660
|
+
const hasUnderscore = trimmed.includes("_");
|
|
19661
|
+
const isAllCaps = trimmed.length >= 3 && /^[A-Z0-9_]+$/.test(trimmed);
|
|
19662
|
+
return hasMixedCase || hasUnderscore || isAllCaps;
|
|
19663
|
+
}
|
|
19664
|
+
function hasRegexCharacters(query) {
|
|
19665
|
+
const trimmed = query.trim();
|
|
19666
|
+
if (/[\^$+{}[\]|()\\]/.test(trimmed)) return true;
|
|
19667
|
+
if (!trimmed.includes("?")) return false;
|
|
19668
|
+
const trailingOnly = trimmed.endsWith("?") && !trimmed.slice(0, -1).includes("?");
|
|
19669
|
+
return !trailingOnly;
|
|
19670
|
+
}
|
|
19671
|
+
function isGlobLike(query) {
|
|
19672
|
+
const trimmed = query.trim();
|
|
19673
|
+
return trimmed.includes("*") || trimmed.includes("?") && !trimmed.endsWith("?");
|
|
19674
|
+
}
|
|
19675
|
+
function isCountQuery(queryLower) {
|
|
19676
|
+
return COUNT_QUERY_PREFIXES.some((prefix) => queryLower.startsWith(prefix)) || queryLower.includes("how many") && queryLower.includes("are there");
|
|
19677
|
+
}
|
|
19678
|
+
function isAllMatchesQuery(queryLower) {
|
|
19679
|
+
return ALL_MATCH_KEYWORDS.some((keyword) => queryLower.includes(keyword));
|
|
19680
|
+
}
|
|
19681
|
+
function isTeamQuery(queryLower) {
|
|
19682
|
+
return TEAM_QUERY_KEYWORDS.some((keyword) => queryLower.includes(keyword));
|
|
19683
|
+
}
|
|
19684
|
+
function isDocLookupQuery(query) {
|
|
19685
|
+
const lower = query.trim().toLowerCase();
|
|
19686
|
+
if (!lower) return false;
|
|
19687
|
+
if (lower.includes(".rs") || lower.includes(".ts") || lower.includes(".js") || lower.includes("src/") || lower.includes("crates/") || lower.includes("function ") || lower.includes("class ")) {
|
|
19688
|
+
return false;
|
|
19689
|
+
}
|
|
19690
|
+
const hasDocTerm = DOC_QUERY_KEYWORDS.some((keyword) => lower.includes(keyword));
|
|
19691
|
+
if (!hasDocTerm) return false;
|
|
19692
|
+
const hasLookupVerb = DOC_LOOKUP_VERBS.some((keyword) => lower.includes(keyword));
|
|
19693
|
+
return hasLookupVerb || lower.startsWith("docs ") || lower.startsWith("doc ");
|
|
19694
|
+
}
|
|
19695
|
+
function recommendSearchMode(query) {
|
|
19696
|
+
const trimmed = query.trim();
|
|
19697
|
+
if (!trimmed) {
|
|
19698
|
+
return { mode: "hybrid", reason: "Defaulted to hybrid for broad discovery." };
|
|
19699
|
+
}
|
|
19700
|
+
const lower = trimmed.toLowerCase();
|
|
19701
|
+
const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
|
|
19702
|
+
if (isTeamQuery(lower)) {
|
|
19703
|
+
return { mode: "team", reason: "Detected team/cross-project intent." };
|
|
19704
|
+
}
|
|
19705
|
+
if (isAllMatchesQuery(lower)) {
|
|
19706
|
+
return {
|
|
19707
|
+
mode: "exhaustive",
|
|
19708
|
+
reason: "Detected all-occurrences intent; exhaustive mode is complete."
|
|
19709
|
+
};
|
|
19710
|
+
}
|
|
19711
|
+
if (extractQuotedLiteral(trimmed)) {
|
|
19712
|
+
return { mode: "keyword", reason: "Detected quoted exact-match query." };
|
|
19713
|
+
}
|
|
19714
|
+
if (isGlobLike(trimmed) || hasRegexCharacters(trimmed)) {
|
|
19715
|
+
return { mode: "pattern", reason: "Detected glob/regex pattern." };
|
|
19716
|
+
}
|
|
19717
|
+
if (isIdentifierQuery(trimmed)) {
|
|
19718
|
+
return {
|
|
19719
|
+
mode: "refactor",
|
|
19720
|
+
reason: "Detected identifier-like query; refactor mode is more precise."
|
|
19721
|
+
};
|
|
19722
|
+
}
|
|
19723
|
+
if (QUESTION_WORDS.some((w) => lower.startsWith(w)) || trimmed.endsWith("?") || wordCount >= 3) {
|
|
19724
|
+
return {
|
|
19725
|
+
mode: "semantic",
|
|
19726
|
+
reason: "Detected natural-language query; semantic mode is a better fit."
|
|
19727
|
+
};
|
|
19728
|
+
}
|
|
19729
|
+
return { mode: "hybrid", reason: "Hybrid mode provides balanced coverage." };
|
|
19730
|
+
}
|
|
19731
|
+
function suggestOutputFormat(query, mode) {
|
|
19732
|
+
const lower = query.trim().toLowerCase();
|
|
19733
|
+
if (isCountQuery(lower)) {
|
|
19734
|
+
return "count";
|
|
19735
|
+
}
|
|
19736
|
+
if (isIdentifierQuery(query)) {
|
|
19737
|
+
if (mode === "refactor" || mode === "exhaustive") return "paths";
|
|
19738
|
+
return "minimal";
|
|
19739
|
+
}
|
|
19740
|
+
return void 0;
|
|
19741
|
+
}
|
|
19742
|
+
function shouldRetrySemanticFallback(query, mode, result) {
|
|
19743
|
+
if (mode !== "hybrid") return false;
|
|
19744
|
+
if (recommendSearchMode(query).mode !== "semantic") return false;
|
|
19745
|
+
const { results } = extractSearchEnvelope(result);
|
|
19746
|
+
return results.length === 0 || maxResultScore(result) < HYBRID_LOW_CONFIDENCE_SCORE;
|
|
19747
|
+
}
|
|
19748
|
+
function shouldPreferSemanticResults(hybridResult, semanticResult) {
|
|
19749
|
+
const hybrid = extractSearchEnvelope(hybridResult);
|
|
19750
|
+
const semantic = extractSearchEnvelope(semanticResult);
|
|
19751
|
+
if (semantic.results.length === 0) return false;
|
|
19752
|
+
if (hybrid.results.length === 0) return true;
|
|
19753
|
+
const hybridTop = maxResultScore(hybridResult);
|
|
19754
|
+
const semanticTop = maxResultScore(semanticResult);
|
|
19755
|
+
return semanticTop > hybridTop + SEMANTIC_SWITCH_MIN_IMPROVEMENT;
|
|
19756
|
+
}
|
|
19757
|
+
function shouldRetryKeywordWithSemantic(query) {
|
|
19758
|
+
return recommendSearchMode(query).mode === "semantic";
|
|
19759
|
+
}
|
|
19760
|
+
function shouldRetryKeywordWithSymbolModes(query) {
|
|
19761
|
+
return isIdentifierQuery(query);
|
|
19762
|
+
}
|
|
19763
|
+
function extractCollectionArray(value) {
|
|
19764
|
+
if (Array.isArray(value)) return value;
|
|
19765
|
+
if (!value || typeof value !== "object") return void 0;
|
|
19766
|
+
for (const key of ["items", "results", "docs"]) {
|
|
19767
|
+
if (Array.isArray(value[key])) return value[key];
|
|
19768
|
+
}
|
|
19769
|
+
if ("data" in value) return extractCollectionArray(value.data);
|
|
19770
|
+
return void 0;
|
|
19771
|
+
}
|
|
19772
|
+
function tokenizeForDocMatch(query) {
|
|
19773
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
19774
|
+
"the",
|
|
19775
|
+
"and",
|
|
19776
|
+
"for",
|
|
19777
|
+
"with",
|
|
19778
|
+
"from",
|
|
19779
|
+
"that",
|
|
19780
|
+
"this",
|
|
19781
|
+
"docs",
|
|
19782
|
+
"doc",
|
|
19783
|
+
"document",
|
|
19784
|
+
"list",
|
|
19785
|
+
"show",
|
|
19786
|
+
"find",
|
|
19787
|
+
"plan",
|
|
19788
|
+
"phase",
|
|
19789
|
+
"phases"
|
|
19790
|
+
]);
|
|
19791
|
+
return query.split(/[^A-Za-z0-9]+/).map((term) => term.trim().toLowerCase()).filter((term) => term.length >= 3 && !stopWords.has(term));
|
|
19792
|
+
}
|
|
19793
|
+
function scoreDocMatch(doc, terms) {
|
|
19794
|
+
const title = String(doc?.title ?? doc?.name ?? doc?.summary ?? "").toLowerCase();
|
|
19795
|
+
const content = String(doc?.content ?? "").toLowerCase();
|
|
19796
|
+
const haystack = `${title} ${content}`;
|
|
19797
|
+
return terms.reduce((score, term) => haystack.includes(term) ? score + 1 : score, 0);
|
|
19798
|
+
}
|
|
19799
|
+
function rankDocsForQuery(docs, query, limit) {
|
|
19800
|
+
const terms = tokenizeForDocMatch(query);
|
|
19801
|
+
if (terms.length === 0) return docs.slice(0, limit);
|
|
19802
|
+
const scored = docs.map((doc, idx) => ({ idx, score: scoreDocMatch(doc, terms) }));
|
|
19803
|
+
scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
19804
|
+
const matched = scored.filter((entry) => entry.score > 0).slice(0, limit).map((entry) => docs[entry.idx]);
|
|
19805
|
+
return matched.length > 0 ? matched : docs.slice(0, limit);
|
|
19806
|
+
}
|
|
19807
|
+
async function findDocsFallback(workspaceId, candidateProjectIds, query, limit) {
|
|
19808
|
+
const uniqueCandidates = [];
|
|
19809
|
+
for (const candidate of candidateProjectIds) {
|
|
19810
|
+
if (!uniqueCandidates.includes(candidate)) {
|
|
19811
|
+
uniqueCandidates.push(candidate);
|
|
19812
|
+
}
|
|
19813
|
+
}
|
|
19814
|
+
const maxDocs = Math.max(1, Math.min(limit ?? 20, 50));
|
|
19815
|
+
for (const candidate of uniqueCandidates) {
|
|
19816
|
+
try {
|
|
19817
|
+
const docsResponse = await client.docsList({
|
|
19818
|
+
workspace_id: workspaceId,
|
|
19819
|
+
project_id: candidate,
|
|
19820
|
+
per_page: maxDocs
|
|
19821
|
+
});
|
|
19822
|
+
const items = extractCollectionArray(docsResponse);
|
|
19823
|
+
if (!items || items.length === 0) continue;
|
|
19824
|
+
const ranked = rankDocsForQuery(items, query, maxDocs);
|
|
19825
|
+
if (ranked.length > 0) {
|
|
19826
|
+
return { docs: ranked, project_id: candidate };
|
|
19827
|
+
}
|
|
19828
|
+
} catch {
|
|
19829
|
+
}
|
|
19830
|
+
}
|
|
19831
|
+
return void 0;
|
|
19832
|
+
}
|
|
19833
|
+
async function indexedProjectIdForFolder(folderPath) {
|
|
19834
|
+
const status = await readIndexStatus();
|
|
19835
|
+
const projects = status.projects ?? {};
|
|
19836
|
+
const resolvedFolder = path6.resolve(folderPath);
|
|
19837
|
+
let bestMatch;
|
|
19838
|
+
for (const [projectPath, info] of Object.entries(projects)) {
|
|
19839
|
+
const resolvedProjectPath = path6.resolve(projectPath);
|
|
19840
|
+
const matches = resolvedFolder === resolvedProjectPath || resolvedFolder.startsWith(`${resolvedProjectPath}${path6.sep}`) || resolvedProjectPath.startsWith(`${resolvedFolder}${path6.sep}`);
|
|
19841
|
+
if (!matches) continue;
|
|
19842
|
+
if (!info?.indexed_at) continue;
|
|
19843
|
+
const indexedAt = new Date(info.indexed_at);
|
|
19844
|
+
if (!Number.isNaN(indexedAt.getTime())) {
|
|
19845
|
+
const diffDays = (Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60 * 24);
|
|
19846
|
+
if (diffDays > 7) continue;
|
|
19847
|
+
}
|
|
19848
|
+
const projectId = typeof info.project_id === "string" && info.project_id.trim() ? info.project_id.trim() : void 0;
|
|
19849
|
+
if (!projectId) continue;
|
|
19850
|
+
const matchLen = resolvedProjectPath.length;
|
|
19851
|
+
if (!bestMatch || matchLen > bestMatch.matchLen) {
|
|
19852
|
+
bestMatch = { projectId, matchLen };
|
|
19853
|
+
}
|
|
19854
|
+
}
|
|
19855
|
+
return bestMatch?.projectId;
|
|
19856
|
+
}
|
|
19857
|
+
async function executeSearchMode(mode, params) {
|
|
19858
|
+
switch (mode) {
|
|
19859
|
+
case "hybrid":
|
|
19860
|
+
return client.searchHybrid(params);
|
|
19861
|
+
case "semantic":
|
|
19862
|
+
return client.searchSemantic(params);
|
|
19863
|
+
case "keyword":
|
|
19864
|
+
return client.searchKeyword(params);
|
|
19865
|
+
case "pattern":
|
|
19866
|
+
return client.searchPattern(params);
|
|
19867
|
+
case "exhaustive":
|
|
19868
|
+
return client.searchExhaustive(params);
|
|
19869
|
+
case "refactor":
|
|
19870
|
+
return client.searchRefactor(params);
|
|
19871
|
+
default:
|
|
19872
|
+
return client.searchHybrid(params);
|
|
19873
|
+
}
|
|
19874
|
+
}
|
|
19011
19875
|
async function validateReadableDirectory(inputPath) {
|
|
19012
19876
|
const resolvedPath = path6.resolve(inputPath);
|
|
19013
19877
|
let stats;
|
|
@@ -23533,48 +24397,80 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23533
24397
|
if (authError) return authError;
|
|
23534
24398
|
client.checkAndIndexChangedFiles().catch(() => {
|
|
23535
24399
|
});
|
|
23536
|
-
const params = normalizeSearchParams(input);
|
|
23537
24400
|
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
|
-
|
|
24401
|
+
const modeInput = input.mode || "auto";
|
|
24402
|
+
const modeRecommendation = recommendSearchMode(input.query);
|
|
24403
|
+
const modeAutoSelected = modeInput === "auto";
|
|
24404
|
+
const requestedMode = modeAutoSelected ? modeRecommendation.mode : modeInput === "auto" ? "hybrid" : modeInput;
|
|
24405
|
+
let workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
24406
|
+
const sessionProjectId = resolveProjectId(void 0);
|
|
24407
|
+
const requestedExplicitProjectId = normalizeUuid(input.project_id);
|
|
24408
|
+
let explicitProjectId = requestedExplicitProjectId;
|
|
24409
|
+
let explicitProjectScopeNote;
|
|
24410
|
+
const folderPath = resolveFolderPath(void 0, sessionManager);
|
|
24411
|
+
let resolvedFolderProjectId;
|
|
24412
|
+
if (folderPath) {
|
|
24413
|
+
const mapping = resolveWorkspace(folderPath);
|
|
24414
|
+
const mappedWorkspaceId = normalizeUuid(mapping.config?.workspace_id);
|
|
24415
|
+
if (!workspaceId && mappedWorkspaceId) {
|
|
24416
|
+
workspaceId = mappedWorkspaceId;
|
|
24417
|
+
}
|
|
24418
|
+
resolvedFolderProjectId = normalizeUuid(mapping.config?.project_id);
|
|
24419
|
+
}
|
|
24420
|
+
const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
|
|
24421
|
+
if (explicitProjectId) {
|
|
24422
|
+
try {
|
|
24423
|
+
const project = await client.getProject(explicitProjectId);
|
|
24424
|
+
const projectWorkspaceId = normalizeUuid(project?.workspace_id || project?.workspaceId);
|
|
24425
|
+
if (workspaceId && projectWorkspaceId && projectWorkspaceId !== workspaceId) {
|
|
24426
|
+
explicitProjectScopeNote = `Explicit project_id ${explicitProjectId} belongs to workspace ${projectWorkspaceId}; auto-corrected to current workspace ${workspaceId}.`;
|
|
24427
|
+
explicitProjectId = void 0;
|
|
24428
|
+
}
|
|
24429
|
+
} catch (error) {
|
|
24430
|
+
if (isNotFoundError(error)) {
|
|
24431
|
+
explicitProjectScopeNote = `Explicit project_id ${explicitProjectId} was not found; auto-corrected using folder/index project mapping.`;
|
|
24432
|
+
explicitProjectId = void 0;
|
|
24433
|
+
} else {
|
|
24434
|
+
throw error;
|
|
24435
|
+
}
|
|
24436
|
+
}
|
|
24437
|
+
}
|
|
24438
|
+
const candidateProjectIds = [];
|
|
24439
|
+
const pushCandidateProjectId = (candidate) => {
|
|
24440
|
+
if (!candidateProjectIds.includes(candidate)) {
|
|
24441
|
+
candidateProjectIds.push(candidate);
|
|
24442
|
+
}
|
|
24443
|
+
};
|
|
24444
|
+
if (explicitProjectId) {
|
|
24445
|
+
pushCandidateProjectId(explicitProjectId);
|
|
24446
|
+
pushCandidateProjectId(resolvedFolderProjectId);
|
|
24447
|
+
pushCandidateProjectId(localIndexProjectId);
|
|
24448
|
+
pushCandidateProjectId(sessionProjectId);
|
|
24449
|
+
} else {
|
|
24450
|
+
pushCandidateProjectId(resolvedFolderProjectId);
|
|
24451
|
+
pushCandidateProjectId(localIndexProjectId);
|
|
24452
|
+
pushCandidateProjectId(sessionProjectId);
|
|
24453
|
+
if (candidateProjectIds.length === 0) {
|
|
24454
|
+
pushCandidateProjectId(void 0);
|
|
24455
|
+
}
|
|
24456
|
+
}
|
|
24457
|
+
pushCandidateProjectId(void 0);
|
|
24458
|
+
const modeToToolType = {
|
|
24459
|
+
hybrid: "search_hybrid",
|
|
24460
|
+
semantic: "search_semantic",
|
|
24461
|
+
keyword: "search_keyword",
|
|
24462
|
+
pattern: "search_pattern",
|
|
24463
|
+
exhaustive: "search_exhaustive",
|
|
24464
|
+
refactor: "search_refactor",
|
|
24465
|
+
team: "search_hybrid"
|
|
24466
|
+
};
|
|
24467
|
+
const runSearchForMode = async (mode, baseParams2) => {
|
|
24468
|
+
if (mode === "team") {
|
|
23568
24469
|
const isTeamPlanForSearch = await client.isTeamPlan();
|
|
23569
24470
|
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
|
-
};
|
|
24471
|
+
throw new Error(
|
|
24472
|
+
"Team search requires a Team subscription. Your current plan does not include team features.\n\nUpgrade at: https://contextstream.io/pricing"
|
|
24473
|
+
);
|
|
23578
24474
|
}
|
|
23579
24475
|
const teamWorkspacesForSearch = await client.listTeamWorkspaces({ page_size: 100 });
|
|
23580
24476
|
const workspacesForSearch = teamWorkspacesForSearch?.items || teamWorkspacesForSearch?.data?.items || [];
|
|
@@ -23583,14 +24479,15 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23583
24479
|
for (const ws of workspacesForSearch.slice(0, 10)) {
|
|
23584
24480
|
try {
|
|
23585
24481
|
const wsSearchResult = await client.searchHybrid({
|
|
23586
|
-
...
|
|
24482
|
+
...baseParams2,
|
|
23587
24483
|
workspace_id: ws.id,
|
|
24484
|
+
project_id: void 0,
|
|
23588
24485
|
limit: perWorkspaceLimit
|
|
23589
24486
|
});
|
|
23590
|
-
const wsResults = wsSearchResult
|
|
24487
|
+
const wsResults = extractSearchEnvelope(wsSearchResult).results;
|
|
23591
24488
|
allSearchResults.push(
|
|
23592
|
-
...wsResults.map((
|
|
23593
|
-
...
|
|
24489
|
+
...wsResults.map((entry) => ({
|
|
24490
|
+
...entry,
|
|
23594
24491
|
workspace_name: ws.name,
|
|
23595
24492
|
workspace_id: ws.id
|
|
23596
24493
|
}))
|
|
@@ -23598,32 +24495,284 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23598
24495
|
} catch {
|
|
23599
24496
|
}
|
|
23600
24497
|
}
|
|
23601
|
-
allSearchResults.sort((a, b) => (b
|
|
24498
|
+
allSearchResults.sort((a, b) => (b?.score || 0) - (a?.score || 0));
|
|
23602
24499
|
const limitedResults = allSearchResults.slice(0, input.limit || 20);
|
|
23603
|
-
|
|
23604
|
-
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
24500
|
+
return {
|
|
24501
|
+
result: {
|
|
24502
|
+
data: {
|
|
24503
|
+
results: limitedResults,
|
|
24504
|
+
total: limitedResults.length,
|
|
24505
|
+
workspaces_searched: workspacesForSearch.length
|
|
24506
|
+
}
|
|
24507
|
+
},
|
|
24508
|
+
executedMode: "team"
|
|
24509
|
+
};
|
|
24510
|
+
}
|
|
24511
|
+
const dispatchMode = mode === "hybrid" ? "hybrid" : mode;
|
|
24512
|
+
let result = await executeSearchMode(dispatchMode, baseParams2);
|
|
24513
|
+
let executedMode = dispatchMode;
|
|
24514
|
+
let fallbackNote;
|
|
24515
|
+
if (shouldRetrySemanticFallback(input.query, dispatchMode, result)) {
|
|
24516
|
+
try {
|
|
24517
|
+
const semanticResult = await executeSearchMode("semantic", baseParams2);
|
|
24518
|
+
if (shouldPreferSemanticResults(result, semanticResult)) {
|
|
24519
|
+
result = semanticResult;
|
|
24520
|
+
executedMode = "semantic";
|
|
24521
|
+
fallbackNote = appendNote(
|
|
24522
|
+
fallbackNote,
|
|
24523
|
+
"Hybrid results looked low-confidence for this natural-language query; retried with semantic and used semantic results."
|
|
24524
|
+
);
|
|
24525
|
+
}
|
|
24526
|
+
} catch {
|
|
24527
|
+
}
|
|
24528
|
+
}
|
|
24529
|
+
const currentResults = () => extractSearchEnvelope(result).results;
|
|
24530
|
+
if (dispatchMode === "keyword" && currentResults().length === 0) {
|
|
24531
|
+
const literal = extractQuotedLiteral(input.query);
|
|
24532
|
+
if (literal) {
|
|
24533
|
+
if (literal !== baseParams2.query) {
|
|
24534
|
+
try {
|
|
24535
|
+
const keywordUnquoted = await executeSearchMode("keyword", {
|
|
24536
|
+
...baseParams2,
|
|
24537
|
+
query: literal
|
|
24538
|
+
});
|
|
24539
|
+
if (extractSearchEnvelope(keywordUnquoted).results.length > 0) {
|
|
24540
|
+
result = keywordUnquoted;
|
|
24541
|
+
executedMode = "keyword";
|
|
24542
|
+
fallbackNote = appendNote(
|
|
24543
|
+
fallbackNote,
|
|
24544
|
+
"Quoted keyword search returned no results; retried keyword search without quotes."
|
|
24545
|
+
);
|
|
24546
|
+
}
|
|
24547
|
+
} catch {
|
|
24548
|
+
}
|
|
24549
|
+
}
|
|
24550
|
+
if (currentResults().length === 0) {
|
|
24551
|
+
try {
|
|
24552
|
+
const patternResult = await executeSearchMode("pattern", {
|
|
24553
|
+
...baseParams2,
|
|
24554
|
+
query: escapeRegexLiteral(literal)
|
|
24555
|
+
});
|
|
24556
|
+
if (extractSearchEnvelope(patternResult).results.length > 0) {
|
|
24557
|
+
result = patternResult;
|
|
24558
|
+
executedMode = "pattern";
|
|
24559
|
+
fallbackNote = appendNote(
|
|
24560
|
+
fallbackNote,
|
|
24561
|
+
"Quoted keyword search returned no results; retried literal pattern search."
|
|
24562
|
+
);
|
|
24563
|
+
}
|
|
24564
|
+
} catch {
|
|
24565
|
+
}
|
|
24566
|
+
}
|
|
24567
|
+
if (currentResults().length === 0) {
|
|
24568
|
+
try {
|
|
24569
|
+
const exhaustiveResult = await executeSearchMode("exhaustive", {
|
|
24570
|
+
...baseParams2,
|
|
24571
|
+
query: literal
|
|
24572
|
+
});
|
|
24573
|
+
if (extractSearchEnvelope(exhaustiveResult).results.length > 0) {
|
|
24574
|
+
result = exhaustiveResult;
|
|
24575
|
+
executedMode = "exhaustive";
|
|
24576
|
+
fallbackNote = appendNote(
|
|
24577
|
+
fallbackNote,
|
|
24578
|
+
"Quoted keyword search returned no results; retried exhaustive search for complete literal coverage."
|
|
24579
|
+
);
|
|
24580
|
+
}
|
|
24581
|
+
} catch {
|
|
24582
|
+
}
|
|
24583
|
+
}
|
|
24584
|
+
}
|
|
24585
|
+
}
|
|
24586
|
+
if ((dispatchMode === "refactor" || dispatchMode === "exhaustive") && currentResults().length === 0) {
|
|
24587
|
+
try {
|
|
24588
|
+
const keywordResult = await executeSearchMode("keyword", baseParams2);
|
|
24589
|
+
if (extractSearchEnvelope(keywordResult).results.length > 0) {
|
|
24590
|
+
result = keywordResult;
|
|
24591
|
+
executedMode = "keyword";
|
|
24592
|
+
fallbackNote = appendNote(
|
|
24593
|
+
fallbackNote,
|
|
24594
|
+
"Requested mode returned no results; retried keyword search and found matches."
|
|
24595
|
+
);
|
|
24596
|
+
}
|
|
24597
|
+
} catch {
|
|
24598
|
+
}
|
|
24599
|
+
}
|
|
24600
|
+
if (dispatchMode === "keyword" && currentResults().length === 0) {
|
|
24601
|
+
if (shouldRetryKeywordWithSymbolModes(input.query)) {
|
|
24602
|
+
try {
|
|
24603
|
+
const refactorResult = await executeSearchMode("refactor", baseParams2);
|
|
24604
|
+
if (extractSearchEnvelope(refactorResult).results.length > 0) {
|
|
24605
|
+
result = refactorResult;
|
|
24606
|
+
executedMode = "refactor";
|
|
24607
|
+
fallbackNote = appendNote(
|
|
24608
|
+
fallbackNote,
|
|
24609
|
+
"Keyword search returned no results; retried refactor search for identifier matching."
|
|
24610
|
+
);
|
|
24611
|
+
}
|
|
24612
|
+
} catch {
|
|
24613
|
+
}
|
|
24614
|
+
if (currentResults().length === 0) {
|
|
24615
|
+
try {
|
|
24616
|
+
const exhaustiveResult = await executeSearchMode("exhaustive", baseParams2);
|
|
24617
|
+
if (extractSearchEnvelope(exhaustiveResult).results.length > 0) {
|
|
24618
|
+
result = exhaustiveResult;
|
|
24619
|
+
executedMode = "exhaustive";
|
|
24620
|
+
fallbackNote = appendNote(
|
|
24621
|
+
fallbackNote,
|
|
24622
|
+
"Keyword search returned no results; retried exhaustive search for complete identifier coverage."
|
|
24623
|
+
);
|
|
24624
|
+
}
|
|
24625
|
+
} catch {
|
|
24626
|
+
}
|
|
24627
|
+
}
|
|
24628
|
+
}
|
|
24629
|
+
if (currentResults().length === 0 && shouldRetryKeywordWithSemantic(input.query)) {
|
|
24630
|
+
try {
|
|
24631
|
+
const semanticResult = await executeSearchMode("semantic", baseParams2);
|
|
24632
|
+
if (extractSearchEnvelope(semanticResult).results.length > 0) {
|
|
24633
|
+
result = semanticResult;
|
|
24634
|
+
executedMode = "semantic";
|
|
24635
|
+
fallbackNote = appendNote(
|
|
24636
|
+
fallbackNote,
|
|
24637
|
+
"Keyword search returned no results; retried semantic search for natural-language intent."
|
|
24638
|
+
);
|
|
24639
|
+
}
|
|
24640
|
+
} catch {
|
|
24641
|
+
}
|
|
24642
|
+
}
|
|
24643
|
+
if (currentResults().length === 0) {
|
|
24644
|
+
try {
|
|
24645
|
+
const hybridResult = await executeSearchMode("hybrid", baseParams2);
|
|
24646
|
+
if (extractSearchEnvelope(hybridResult).results.length > 0) {
|
|
24647
|
+
result = hybridResult;
|
|
24648
|
+
executedMode = "hybrid";
|
|
24649
|
+
fallbackNote = appendNote(
|
|
24650
|
+
fallbackNote,
|
|
24651
|
+
"Keyword search returned no results; retried hybrid search as a broad fallback."
|
|
24652
|
+
);
|
|
24653
|
+
}
|
|
24654
|
+
} catch {
|
|
23608
24655
|
}
|
|
24656
|
+
}
|
|
24657
|
+
}
|
|
24658
|
+
return { result, executedMode, fallbackNote };
|
|
24659
|
+
};
|
|
24660
|
+
let selected;
|
|
24661
|
+
let explicitScopeHadNoResults = false;
|
|
24662
|
+
const baseParams = normalizeSearchParams({
|
|
24663
|
+
...input,
|
|
24664
|
+
workspace_id: workspaceId,
|
|
24665
|
+
project_id: void 0,
|
|
24666
|
+
output_format: input.output_format || suggestOutputFormat(input.query, requestedMode === "team" ? "hybrid" : requestedMode)
|
|
24667
|
+
});
|
|
24668
|
+
if (requestedMode === "team") {
|
|
24669
|
+
try {
|
|
24670
|
+
const teamResult = await runSearchForMode("team", {
|
|
24671
|
+
...baseParams,
|
|
24672
|
+
project_id: void 0
|
|
24673
|
+
});
|
|
24674
|
+
selected = {
|
|
24675
|
+
index: 0,
|
|
24676
|
+
project_id: void 0,
|
|
24677
|
+
result: teamResult.result,
|
|
24678
|
+
executedMode: "team",
|
|
24679
|
+
fallbackNote: teamResult.fallbackNote
|
|
23609
24680
|
};
|
|
23610
|
-
|
|
23611
|
-
|
|
24681
|
+
} catch (error) {
|
|
24682
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24683
|
+
return errorResult(message);
|
|
23612
24684
|
}
|
|
23613
|
-
|
|
23614
|
-
|
|
23615
|
-
|
|
24685
|
+
} else {
|
|
24686
|
+
for (const [index, candidateProjectId] of candidateProjectIds.entries()) {
|
|
24687
|
+
const paramsForCandidate = {
|
|
24688
|
+
...baseParams,
|
|
24689
|
+
workspace_id: workspaceId,
|
|
24690
|
+
project_id: candidateProjectId
|
|
24691
|
+
};
|
|
24692
|
+
try {
|
|
24693
|
+
const modeResult = await runSearchForMode(requestedMode, paramsForCandidate);
|
|
24694
|
+
const envelope = extractSearchEnvelope(modeResult.result);
|
|
24695
|
+
if (explicitProjectId && index === 0 && envelope.results.length === 0) {
|
|
24696
|
+
explicitScopeHadNoResults = true;
|
|
24697
|
+
}
|
|
24698
|
+
if (envelope.results.length > 0) {
|
|
24699
|
+
selected = {
|
|
24700
|
+
index,
|
|
24701
|
+
project_id: candidateProjectId,
|
|
24702
|
+
result: modeResult.result,
|
|
24703
|
+
executedMode: modeResult.executedMode,
|
|
24704
|
+
fallbackNote: modeResult.fallbackNote
|
|
24705
|
+
};
|
|
24706
|
+
break;
|
|
24707
|
+
}
|
|
24708
|
+
if (!selected) {
|
|
24709
|
+
selected = {
|
|
24710
|
+
index,
|
|
24711
|
+
project_id: candidateProjectId,
|
|
24712
|
+
result: modeResult.result,
|
|
24713
|
+
executedMode: modeResult.executedMode,
|
|
24714
|
+
fallbackNote: modeResult.fallbackNote
|
|
24715
|
+
};
|
|
24716
|
+
}
|
|
24717
|
+
} catch (error) {
|
|
24718
|
+
if (isNotFoundError(error)) {
|
|
24719
|
+
continue;
|
|
24720
|
+
}
|
|
24721
|
+
throw error;
|
|
24722
|
+
}
|
|
24723
|
+
}
|
|
24724
|
+
}
|
|
24725
|
+
if (!selected) {
|
|
24726
|
+
const baseMessage = "Project not found for current context. Call init(...) in this folder or pass a valid project_id explicitly.";
|
|
24727
|
+
return errorResult(
|
|
24728
|
+
explicitProjectScopeNote ? `${explicitProjectScopeNote} ${baseMessage}` : baseMessage
|
|
24729
|
+
);
|
|
24730
|
+
}
|
|
24731
|
+
let modeFallbackNote = selected.fallbackNote;
|
|
24732
|
+
if (explicitProjectScopeNote) {
|
|
24733
|
+
modeFallbackNote = appendNote(modeFallbackNote, explicitProjectScopeNote);
|
|
24734
|
+
}
|
|
24735
|
+
if (explicitScopeHadNoResults && selected.index > 0) {
|
|
24736
|
+
modeFallbackNote = appendNote(
|
|
24737
|
+
modeFallbackNote,
|
|
24738
|
+
"Explicit project_id returned no results; retried folder/local project mapping."
|
|
24739
|
+
);
|
|
24740
|
+
}
|
|
24741
|
+
if (!selected.project_id && selected.index > 0) {
|
|
24742
|
+
modeFallbackNote = appendNote(
|
|
24743
|
+
modeFallbackNote,
|
|
24744
|
+
"Project-scoped search returned no results; retried workspace-wide scope."
|
|
24745
|
+
);
|
|
23616
24746
|
}
|
|
24747
|
+
if (folderPath && localIndexProjectId && selected.project_id !== localIndexProjectId) {
|
|
24748
|
+
const resolvedScope = selected.project_id || "workspace-wide scope";
|
|
24749
|
+
modeFallbackNote = appendNote(
|
|
24750
|
+
modeFallbackNote,
|
|
24751
|
+
`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}").`
|
|
24752
|
+
);
|
|
24753
|
+
}
|
|
24754
|
+
if (requestedMode !== "team" && !explicitProjectId && sessionManager && folderPath && (selected.project_id !== sessionProjectId || workspaceId !== resolveWorkspaceId(void 0))) {
|
|
24755
|
+
sessionManager.updateScope({
|
|
24756
|
+
workspace_id: workspaceId,
|
|
24757
|
+
project_id: selected.project_id || sessionProjectId,
|
|
24758
|
+
folder_path: folderPath
|
|
24759
|
+
});
|
|
24760
|
+
}
|
|
24761
|
+
const docsFallback = requestedMode !== "team" && isDocLookupQuery(input.query) && extractSearchEnvelope(selected.result).results.length === 0 ? await findDocsFallback(workspaceId, candidateProjectIds, input.query, input.limit) : void 0;
|
|
23617
24762
|
const roundTripMs = Date.now() - startTime;
|
|
23618
|
-
const
|
|
23619
|
-
const results = data?.results || [];
|
|
23620
|
-
const total = data?.total ?? results.length;
|
|
24763
|
+
const { results, total } = extractSearchEnvelope(selected.result);
|
|
23621
24764
|
const lines = [];
|
|
23622
24765
|
if (SHOW_TIMING) {
|
|
23623
24766
|
lines.push(`\u2713 ${total} results in ${roundTripMs}ms`);
|
|
23624
24767
|
} else {
|
|
23625
24768
|
lines.push(`\u{1F50D} ${total} results for "${input.query}"`);
|
|
23626
24769
|
}
|
|
24770
|
+
if (modeAutoSelected) {
|
|
24771
|
+
lines.push(`Mode auto-selected: \`${requestedMode}\`. ${modeRecommendation.reason}`);
|
|
24772
|
+
}
|
|
24773
|
+
if (modeFallbackNote) {
|
|
24774
|
+
lines.push(modeFallbackNote);
|
|
24775
|
+
}
|
|
23627
24776
|
if (results.length > 0) {
|
|
23628
24777
|
lines.push("");
|
|
23629
24778
|
results.forEach((r, i) => {
|
|
@@ -23637,15 +24786,69 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23637
24786
|
});
|
|
23638
24787
|
} else {
|
|
23639
24788
|
lines.push("");
|
|
23640
|
-
|
|
24789
|
+
if (docsFallback?.docs?.length) {
|
|
24790
|
+
const docsScope = docsFallback.project_id ? ` (project_id: ${docsFallback.project_id})` : "";
|
|
24791
|
+
lines.push(
|
|
24792
|
+
`No codebase results found. This query looks like a docs lookup; found ${docsFallback.docs.length} docs in ContextStream memory${docsScope}:`
|
|
24793
|
+
);
|
|
24794
|
+
lines.push("");
|
|
24795
|
+
docsFallback.docs.slice(0, 10).forEach((doc, i) => {
|
|
24796
|
+
const title = doc?.title || doc?.name || doc?.summary || "Untitled";
|
|
24797
|
+
const id = doc?.id || "unknown";
|
|
24798
|
+
const docType = doc?.doc_type || "doc";
|
|
24799
|
+
lines.push(`${i + 1}. ${title} (id: ${id}) [${docType}]`);
|
|
24800
|
+
});
|
|
24801
|
+
lines.push("");
|
|
24802
|
+
lines.push(
|
|
24803
|
+
`Use memory(action="get_doc", doc_id="...") to open a specific doc or memory(action="list_docs") to browse more.`
|
|
24804
|
+
);
|
|
24805
|
+
} else {
|
|
24806
|
+
lines.push("No results found. Try a different query or search mode.");
|
|
24807
|
+
if (isDocLookupQuery(input.query)) {
|
|
24808
|
+
lines.push(
|
|
24809
|
+
`This query appears to target saved docs. Try memory(action="list_docs") and then memory(action="get_doc", doc_id="...").`
|
|
24810
|
+
);
|
|
24811
|
+
}
|
|
24812
|
+
if (folderPath) {
|
|
24813
|
+
lines.push(
|
|
24814
|
+
`If results seem stale after recent edits, refresh local index with project(action="ingest_local", path="${folderPath}").`
|
|
24815
|
+
);
|
|
24816
|
+
}
|
|
24817
|
+
}
|
|
23641
24818
|
}
|
|
24819
|
+
const rawData = selected.result?.data;
|
|
24820
|
+
const structuredData = rawData && typeof rawData === "object" ? { ...rawData } : { ...selected.result };
|
|
24821
|
+
if (requestedMode !== "team") {
|
|
24822
|
+
if (requestedExplicitProjectId) {
|
|
24823
|
+
structuredData.requested_explicit_project_id = requestedExplicitProjectId;
|
|
24824
|
+
structuredData.effective_explicit_project_id = explicitProjectId;
|
|
24825
|
+
structuredData.explicit_project_autocorrected = requestedExplicitProjectId !== explicitProjectId;
|
|
24826
|
+
}
|
|
24827
|
+
if (selected.project_id !== sessionProjectId) {
|
|
24828
|
+
structuredData.original_project_id = sessionProjectId;
|
|
24829
|
+
}
|
|
24830
|
+
structuredData.resolved_project_id = selected.project_id;
|
|
24831
|
+
structuredData.resolution_rank = selected.index;
|
|
24832
|
+
}
|
|
24833
|
+
if (docsFallback?.docs?.length) {
|
|
24834
|
+
structuredData.memory_docs_fallback = {
|
|
24835
|
+
project_id: docsFallback.project_id,
|
|
24836
|
+
docs: docsFallback.docs,
|
|
24837
|
+
count: docsFallback.docs.length
|
|
24838
|
+
};
|
|
24839
|
+
}
|
|
24840
|
+
const resultWithMeta = {
|
|
24841
|
+
...selected.result,
|
|
24842
|
+
data: structuredData
|
|
24843
|
+
};
|
|
23642
24844
|
lines.push("");
|
|
23643
24845
|
lines.push("--- Full Results ---");
|
|
23644
|
-
lines.push(formatContent(
|
|
24846
|
+
lines.push(formatContent(resultWithMeta));
|
|
23645
24847
|
const outputText = lines.join("\n");
|
|
24848
|
+
const toolType = modeToToolType[selected.executedMode] || "search_hybrid";
|
|
23646
24849
|
trackToolTokenSavings(client, toolType, outputText, {
|
|
23647
|
-
workspace_id:
|
|
23648
|
-
project_id:
|
|
24850
|
+
workspace_id: workspaceId,
|
|
24851
|
+
project_id: selected.project_id
|
|
23649
24852
|
});
|
|
23650
24853
|
return {
|
|
23651
24854
|
content: [{ type: "text", text: outputText }]
|
|
@@ -23771,8 +24974,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
23771
24974
|
})
|
|
23772
24975
|
},
|
|
23773
24976
|
async (input) => {
|
|
23774
|
-
|
|
23775
|
-
const
|
|
24977
|
+
let workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
24978
|
+
const explicitProjectId = normalizeUuid(input.project_id);
|
|
24979
|
+
let projectId = explicitProjectId || resolveProjectId(void 0);
|
|
24980
|
+
const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
|
|
23776
24981
|
switch (input.action) {
|
|
23777
24982
|
case "capture": {
|
|
23778
24983
|
if (!input.event_type || !input.title || !input.content) {
|
|
@@ -24534,6 +25739,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
24534
25739
|
plan_step_id: external_exports.string().optional().describe("Which plan step this task implements"),
|
|
24535
25740
|
description: external_exports.string().optional().describe("Description for task"),
|
|
24536
25741
|
task_status: external_exports.enum(["pending", "in_progress", "completed", "blocked", "cancelled"]).optional().describe("Task status"),
|
|
25742
|
+
status: external_exports.enum(["pending", "in_progress", "completed", "blocked", "cancelled"]).optional().describe("Backward-compatible alias for task_status in task actions"),
|
|
24537
25743
|
priority: external_exports.enum(["low", "medium", "high", "urgent"]).optional().describe("Task priority"),
|
|
24538
25744
|
order: external_exports.number().optional().describe("Task order within plan"),
|
|
24539
25745
|
task_ids: external_exports.array(external_exports.string().uuid()).optional().describe("Task IDs for reorder_tasks"),
|
|
@@ -24595,8 +25801,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
24595
25801
|
})
|
|
24596
25802
|
},
|
|
24597
25803
|
async (input) => {
|
|
24598
|
-
|
|
24599
|
-
const
|
|
25804
|
+
let workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
25805
|
+
const explicitProjectId = normalizeUuid(input.project_id);
|
|
25806
|
+
let projectId = explicitProjectId || resolveProjectId(void 0);
|
|
25807
|
+
const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
|
|
24600
25808
|
switch (input.action) {
|
|
24601
25809
|
case "create_event": {
|
|
24602
25810
|
if (!input.event_type || !input.title || !input.content) {
|
|
@@ -24834,6 +26042,7 @@ ${formatContent(result)}`
|
|
|
24834
26042
|
if (!workspaceId) {
|
|
24835
26043
|
return errorResult("create_task requires workspace_id. Call session_init first.");
|
|
24836
26044
|
}
|
|
26045
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
24837
26046
|
const result = await client.createTask({
|
|
24838
26047
|
workspace_id: workspaceId,
|
|
24839
26048
|
project_id: projectId,
|
|
@@ -24842,7 +26051,7 @@ ${formatContent(result)}`
|
|
|
24842
26051
|
description: input.description,
|
|
24843
26052
|
plan_id: input.plan_id ?? void 0,
|
|
24844
26053
|
plan_step_id: input.plan_step_id,
|
|
24845
|
-
status:
|
|
26054
|
+
status: requestedTaskStatus,
|
|
24846
26055
|
priority: input.priority,
|
|
24847
26056
|
order: input.order,
|
|
24848
26057
|
code_refs: input.code_refs,
|
|
@@ -24870,12 +26079,13 @@ ${formatContent(result)}`
|
|
|
24870
26079
|
if (!input.task_id) {
|
|
24871
26080
|
return errorResult("update_task requires: task_id");
|
|
24872
26081
|
}
|
|
26082
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
24873
26083
|
const result = await client.updateTask({
|
|
24874
26084
|
task_id: input.task_id,
|
|
24875
26085
|
title: input.title,
|
|
24876
26086
|
content: input.content,
|
|
24877
26087
|
description: input.description,
|
|
24878
|
-
status:
|
|
26088
|
+
status: requestedTaskStatus,
|
|
24879
26089
|
priority: input.priority,
|
|
24880
26090
|
order: input.order,
|
|
24881
26091
|
plan_id: input.plan_id,
|
|
@@ -24885,11 +26095,11 @@ ${formatContent(result)}`
|
|
|
24885
26095
|
blocked_reason: input.blocked_reason
|
|
24886
26096
|
});
|
|
24887
26097
|
let taskUpdateHint = "Task updated.";
|
|
24888
|
-
if (
|
|
26098
|
+
if (requestedTaskStatus === "completed") {
|
|
24889
26099
|
taskUpdateHint = TASK_HINTS.completed;
|
|
24890
|
-
} else if (
|
|
26100
|
+
} else if (requestedTaskStatus === "blocked") {
|
|
24891
26101
|
taskUpdateHint = TASK_HINTS.blocked;
|
|
24892
|
-
} else if (
|
|
26102
|
+
} else if (requestedTaskStatus === "cancelled") {
|
|
24893
26103
|
taskUpdateHint = TASK_HINTS.cancelled;
|
|
24894
26104
|
}
|
|
24895
26105
|
const resultWithHint = { ...result, hint: taskUpdateHint };
|
|
@@ -24912,11 +26122,12 @@ ${formatContent(result)}`
|
|
|
24912
26122
|
if (!workspaceId) {
|
|
24913
26123
|
return errorResult("list_tasks requires workspace_id. Call session_init first.");
|
|
24914
26124
|
}
|
|
26125
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
24915
26126
|
const result = await client.listTasks({
|
|
24916
26127
|
workspace_id: workspaceId,
|
|
24917
26128
|
project_id: projectId,
|
|
24918
26129
|
plan_id: input.plan_id ?? void 0,
|
|
24919
|
-
status:
|
|
26130
|
+
status: requestedTaskStatus,
|
|
24920
26131
|
priority: input.priority,
|
|
24921
26132
|
limit: input.limit,
|
|
24922
26133
|
is_personal: input.is_personal
|
|
@@ -25203,11 +26414,12 @@ ${formatContent(result)}`
|
|
|
25203
26414
|
const teamWorkspacesForTasks = await client.listTeamWorkspaces({ page_size: 100 });
|
|
25204
26415
|
const workspacesForTasks = teamWorkspacesForTasks?.items || teamWorkspacesForTasks?.data?.items || [];
|
|
25205
26416
|
const allTasks = [];
|
|
26417
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
25206
26418
|
for (const ws of workspacesForTasks.slice(0, 10)) {
|
|
25207
26419
|
try {
|
|
25208
26420
|
const tasks = await client.listTasks({
|
|
25209
26421
|
workspace_id: ws.id,
|
|
25210
|
-
status:
|
|
26422
|
+
status: requestedTaskStatus,
|
|
25211
26423
|
limit: input.limit ? Math.ceil(input.limit / workspacesForTasks.length) : 10
|
|
25212
26424
|
});
|
|
25213
26425
|
const items = tasks?.data?.tasks || tasks?.tasks || tasks?.data?.items || tasks?.items || [];
|
|
@@ -25662,8 +26874,10 @@ ${formatContent(result)}`
|
|
|
25662
26874
|
})
|
|
25663
26875
|
},
|
|
25664
26876
|
async (input) => {
|
|
25665
|
-
|
|
25666
|
-
const
|
|
26877
|
+
let workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
26878
|
+
const explicitProjectId = normalizeUuid(input.project_id);
|
|
26879
|
+
let projectId = explicitProjectId || resolveProjectId(void 0);
|
|
26880
|
+
const folderPath = input.folder_path || input.path || resolveFolderPath(void 0, sessionManager) || void 0;
|
|
25667
26881
|
switch (input.action) {
|
|
25668
26882
|
case "list": {
|
|
25669
26883
|
const result = await client.listProjects({
|
|
@@ -25713,10 +26927,50 @@ ${formatContent(result)}`
|
|
|
25713
26927
|
if (!projectId) {
|
|
25714
26928
|
return errorResult("index requires: project_id");
|
|
25715
26929
|
}
|
|
25716
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
26930
|
+
try {
|
|
26931
|
+
const result = await client.indexProject(projectId);
|
|
26932
|
+
return {
|
|
26933
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
26934
|
+
};
|
|
26935
|
+
} catch (error) {
|
|
26936
|
+
if (!isRequiresIngestEndpointError(error)) {
|
|
26937
|
+
throw error;
|
|
26938
|
+
}
|
|
26939
|
+
if (!folderPath) {
|
|
26940
|
+
return errorResult(
|
|
26941
|
+
'Index endpoint is unavailable for this local project type and no folder context is set. Run project(action="ingest_local", path="<folder>").'
|
|
26942
|
+
);
|
|
26943
|
+
}
|
|
26944
|
+
const validPath = await validateReadableDirectory(folderPath);
|
|
26945
|
+
if (!validPath.ok) {
|
|
26946
|
+
return errorResult(
|
|
26947
|
+
`Index endpoint is unavailable for this project type and folder context path is invalid: ${folderPath}. ${validPath.error}`
|
|
26948
|
+
);
|
|
26949
|
+
}
|
|
26950
|
+
const ingestOptions = {
|
|
26951
|
+
...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
|
|
26952
|
+
...input.overwrite !== void 0 && { overwrite: input.overwrite },
|
|
26953
|
+
...input.force !== void 0 && { force: input.force }
|
|
26954
|
+
};
|
|
26955
|
+
startBackgroundIngest(projectId, validPath.resolvedPath, ingestOptions);
|
|
26956
|
+
const response = {
|
|
26957
|
+
status: "started",
|
|
26958
|
+
message: "Index endpoint unavailable for this project type; started ingest_local fallback in background",
|
|
26959
|
+
project_id: projectId,
|
|
26960
|
+
path: validPath.resolvedPath,
|
|
26961
|
+
fallback_action: "ingest_local",
|
|
26962
|
+
note: "Use 'project' with action 'index_status' to monitor progress."
|
|
26963
|
+
};
|
|
26964
|
+
return {
|
|
26965
|
+
content: [
|
|
26966
|
+
{
|
|
26967
|
+
type: "text",
|
|
26968
|
+
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.`
|
|
26969
|
+
}
|
|
26970
|
+
],
|
|
26971
|
+
structuredContent: response
|
|
26972
|
+
};
|
|
26973
|
+
}
|
|
25720
26974
|
}
|
|
25721
26975
|
case "overview": {
|
|
25722
26976
|
if (!projectId) {
|
|
@@ -25746,12 +27000,122 @@ ${formatContent(result)}`
|
|
|
25746
27000
|
};
|
|
25747
27001
|
}
|
|
25748
27002
|
case "index_status": {
|
|
25749
|
-
|
|
25750
|
-
|
|
27003
|
+
let resolvedFolderProjectId;
|
|
27004
|
+
if (folderPath) {
|
|
27005
|
+
const mapping = resolveWorkspace(folderPath);
|
|
27006
|
+
const mappedWorkspaceId = normalizeUuid(mapping.config?.workspace_id);
|
|
27007
|
+
if (!workspaceId && mappedWorkspaceId) {
|
|
27008
|
+
workspaceId = mappedWorkspaceId;
|
|
27009
|
+
}
|
|
27010
|
+
resolvedFolderProjectId = normalizeUuid(mapping.config?.project_id);
|
|
27011
|
+
}
|
|
27012
|
+
const localIndexProjectId = folderPath ? await indexedProjectIdForFolder(folderPath) : void 0;
|
|
27013
|
+
const candidateIds = [];
|
|
27014
|
+
const pushCandidate = (id) => {
|
|
27015
|
+
if (id && !candidateIds.includes(id)) {
|
|
27016
|
+
candidateIds.push(id);
|
|
27017
|
+
}
|
|
27018
|
+
};
|
|
27019
|
+
if (explicitProjectId) {
|
|
27020
|
+
pushCandidate(explicitProjectId);
|
|
27021
|
+
pushCandidate(resolvedFolderProjectId);
|
|
27022
|
+
pushCandidate(localIndexProjectId);
|
|
27023
|
+
pushCandidate(projectId);
|
|
27024
|
+
} else {
|
|
27025
|
+
pushCandidate(resolvedFolderProjectId);
|
|
27026
|
+
pushCandidate(localIndexProjectId);
|
|
27027
|
+
pushCandidate(projectId);
|
|
27028
|
+
}
|
|
27029
|
+
if (candidateIds.length === 0) {
|
|
27030
|
+
return errorResult(
|
|
27031
|
+
"index_status requires: project_id. Call init(...) first or pass project_id explicitly."
|
|
27032
|
+
);
|
|
27033
|
+
}
|
|
27034
|
+
let selected;
|
|
27035
|
+
for (const [index, candidateId] of candidateIds.entries()) {
|
|
27036
|
+
try {
|
|
27037
|
+
const statusResult = await client.projectIndexStatus(candidateId);
|
|
27038
|
+
const apiIndexed = apiResultReportsIndexed(statusResult);
|
|
27039
|
+
if (apiIndexed) {
|
|
27040
|
+
selected = { index, projectId: candidateId, result: statusResult, apiIndexed };
|
|
27041
|
+
break;
|
|
27042
|
+
}
|
|
27043
|
+
if (!selected) {
|
|
27044
|
+
selected = { index, projectId: candidateId, result: statusResult, apiIndexed };
|
|
27045
|
+
}
|
|
27046
|
+
} catch (error) {
|
|
27047
|
+
if (isNotFoundError(error)) {
|
|
27048
|
+
continue;
|
|
27049
|
+
}
|
|
27050
|
+
throw error;
|
|
27051
|
+
}
|
|
27052
|
+
}
|
|
27053
|
+
if (!selected) {
|
|
27054
|
+
return errorResult(
|
|
27055
|
+
"Project not found for current context. Call init(...) in this folder or pass a valid project_id explicitly."
|
|
27056
|
+
);
|
|
27057
|
+
}
|
|
27058
|
+
const originalProjectId = projectId;
|
|
27059
|
+
if (projectId !== selected.projectId && folderPath) {
|
|
27060
|
+
projectId = selected.projectId;
|
|
27061
|
+
sessionManager?.updateScope({
|
|
27062
|
+
workspace_id: workspaceId,
|
|
27063
|
+
project_id: projectId,
|
|
27064
|
+
folder_path: folderPath
|
|
27065
|
+
});
|
|
27066
|
+
}
|
|
27067
|
+
const locallyIndexed = localIndexProjectId !== void 0 ? localIndexProjectId === selected.projectId : Boolean(folderPath && await indexedProjectIdForFolder(folderPath));
|
|
27068
|
+
const apiIndexing = apiResultIsIndexing(selected.result);
|
|
27069
|
+
const indexed = selected.apiIndexed || locallyIndexed;
|
|
27070
|
+
const indexedAt = extractIndexTimestamp(selected.result);
|
|
27071
|
+
const ageHours = indexedAt !== void 0 ? Math.floor((Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60)) : void 0;
|
|
27072
|
+
const freshness = classifyIndexFreshness(indexed, ageHours);
|
|
27073
|
+
const { confidence: confidenceLevel, reason: confidenceReason } = classifyIndexConfidence(
|
|
27074
|
+
indexed,
|
|
27075
|
+
selected.apiIndexed,
|
|
27076
|
+
locallyIndexed,
|
|
27077
|
+
freshness
|
|
27078
|
+
);
|
|
27079
|
+
const response = selected.result && typeof selected.result === "object" ? { ...selected.result } : {};
|
|
27080
|
+
const responseData = response.data && typeof response.data === "object" ? { ...response.data } : {};
|
|
27081
|
+
responseData.indexed = indexed;
|
|
27082
|
+
if (locallyIndexed && !selected.apiIndexed) {
|
|
27083
|
+
responseData.indexed_source = "local";
|
|
27084
|
+
}
|
|
27085
|
+
if (originalProjectId && originalProjectId !== selected.projectId) {
|
|
27086
|
+
responseData.original_project_id = originalProjectId;
|
|
27087
|
+
}
|
|
27088
|
+
responseData.resolved_project_id = selected.projectId;
|
|
27089
|
+
responseData.resolution_rank = selected.index;
|
|
27090
|
+
responseData.index_freshness = freshness;
|
|
27091
|
+
responseData.index_age_hours = ageHours ?? null;
|
|
27092
|
+
responseData.index_confidence = confidenceLevel;
|
|
27093
|
+
responseData.index_confidence_reason = confidenceReason;
|
|
27094
|
+
responseData.index_timestamp = indexedAt ? indexedAt.toISOString() : null;
|
|
27095
|
+
responseData.index_in_progress = apiIndexing;
|
|
27096
|
+
response.data = responseData;
|
|
27097
|
+
let text = "";
|
|
27098
|
+
if (apiIndexing) {
|
|
27099
|
+
text = indexed ? "Project indexing is in progress. Search is using the latest committed generation." : "Project indexing is in progress. Semantic search will be available after the first commit.";
|
|
27100
|
+
} else if (indexed) {
|
|
27101
|
+
text = locallyIndexed && !selected.apiIndexed ? "Project index is ready (local state). Semantic search is available." : "Project index is ready. Semantic search is available.";
|
|
27102
|
+
} else {
|
|
27103
|
+
text = 'Project index not found. Run project(action="ingest_local", path="<folder>") to start indexing.';
|
|
27104
|
+
}
|
|
27105
|
+
const ageDisplay = typeof ageHours === "number" ? `${ageHours}h` : "unknown";
|
|
27106
|
+
text += ` Freshness: ${freshness} (${ageDisplay}). Confidence: ${confidenceLevel}.`;
|
|
27107
|
+
if (confidenceLevel !== "high") {
|
|
27108
|
+
text += ` ${confidenceReason}`;
|
|
27109
|
+
}
|
|
27110
|
+
if (freshness === "stale" || freshness === "missing") {
|
|
27111
|
+
const ingestPath = folderPath || "<folder>";
|
|
27112
|
+
text += ` Refresh with project(action="ingest_local", path="${ingestPath}").`;
|
|
25751
27113
|
}
|
|
25752
|
-
const result = await client.projectIndexStatus(projectId);
|
|
25753
27114
|
return {
|
|
25754
|
-
content: [{ type: "text", text:
|
|
27115
|
+
content: [{ type: "text", text: `${text}
|
|
27116
|
+
|
|
27117
|
+
${formatContent(response)}` }],
|
|
27118
|
+
structuredContent: response
|
|
25755
27119
|
};
|
|
25756
27120
|
}
|
|
25757
27121
|
case "index_history": {
|
|
@@ -25769,18 +27133,39 @@ ${formatContent(result)}`
|
|
|
25769
27133
|
page: input.page,
|
|
25770
27134
|
limit: input.page_size
|
|
25771
27135
|
});
|
|
27136
|
+
const count = indexHistoryEntryCount(result);
|
|
27137
|
+
const response = result && typeof result === "object" ? Array.isArray(result) ? [...result] : { ...result } : result;
|
|
27138
|
+
if (response && typeof response === "object" && !Array.isArray(response)) {
|
|
27139
|
+
response.entries_count = count;
|
|
27140
|
+
if (response.data && typeof response.data === "object") {
|
|
27141
|
+
response.data = {
|
|
27142
|
+
...response.data,
|
|
27143
|
+
entries_count: count
|
|
27144
|
+
};
|
|
27145
|
+
}
|
|
27146
|
+
}
|
|
27147
|
+
const structuredHistory = response && typeof response === "object" && !Array.isArray(response) ? response : void 0;
|
|
25772
27148
|
return {
|
|
25773
|
-
content: [
|
|
27149
|
+
content: [
|
|
27150
|
+
{
|
|
27151
|
+
type: "text",
|
|
27152
|
+
text: `Found ${count} index history entries.
|
|
27153
|
+
|
|
27154
|
+
${formatContent(response)}`
|
|
27155
|
+
}
|
|
27156
|
+
],
|
|
27157
|
+
...structuredHistory ? { structuredContent: structuredHistory } : {}
|
|
25774
27158
|
};
|
|
25775
27159
|
}
|
|
25776
27160
|
case "ingest_local": {
|
|
25777
|
-
|
|
27161
|
+
const ingestPath = input.path || input.folder_path;
|
|
27162
|
+
if (!ingestPath) {
|
|
25778
27163
|
return errorResult("ingest_local requires: path");
|
|
25779
27164
|
}
|
|
25780
27165
|
if (!projectId) {
|
|
25781
27166
|
return errorResult("ingest_local requires: project_id");
|
|
25782
27167
|
}
|
|
25783
|
-
const validPath = await validateReadableDirectory(
|
|
27168
|
+
const validPath = await validateReadableDirectory(ingestPath);
|
|
25784
27169
|
if (!validPath.ok) {
|
|
25785
27170
|
return errorResult(validPath.error);
|
|
25786
27171
|
}
|
|
@@ -25793,7 +27178,7 @@ ${formatContent(result)}`
|
|
|
25793
27178
|
const forceNote = input.force ? " (force mode - version checks bypassed)" : "";
|
|
25794
27179
|
const result = {
|
|
25795
27180
|
status: "started",
|
|
25796
|
-
message: `
|
|
27181
|
+
message: `Updating index in background${forceNote}`,
|
|
25797
27182
|
project_id: projectId,
|
|
25798
27183
|
path: validPath.resolvedPath,
|
|
25799
27184
|
...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
|
|
@@ -25805,7 +27190,7 @@ ${formatContent(result)}`
|
|
|
25805
27190
|
content: [
|
|
25806
27191
|
{
|
|
25807
27192
|
type: "text",
|
|
25808
|
-
text: `
|
|
27193
|
+
text: `Updating index in background${forceNote} for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
|
|
25809
27194
|
}
|
|
25810
27195
|
]
|
|
25811
27196
|
};
|
|
@@ -26770,13 +28155,13 @@ Example workflow:
|
|
|
26770
28155
|
);
|
|
26771
28156
|
}
|
|
26772
28157
|
if (input.file_path) {
|
|
26773
|
-
const
|
|
28158
|
+
const fs22 = await import("fs/promises");
|
|
26774
28159
|
const pathModule = await import("path");
|
|
26775
28160
|
const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
|
|
26776
28161
|
const resolvedPath = pathModule.resolve(filePath);
|
|
26777
28162
|
let fileStats;
|
|
26778
28163
|
try {
|
|
26779
|
-
fileStats = await
|
|
28164
|
+
fileStats = await fs22.stat(resolvedPath);
|
|
26780
28165
|
} catch {
|
|
26781
28166
|
return errorResult(`File not found: ${resolvedPath}`);
|
|
26782
28167
|
}
|
|
@@ -26823,7 +28208,7 @@ Example workflow:
|
|
|
26823
28208
|
mime_type: mimeType,
|
|
26824
28209
|
tags: input.tags
|
|
26825
28210
|
});
|
|
26826
|
-
const fileBuffer = await
|
|
28211
|
+
const fileBuffer = await fs22.readFile(resolvedPath);
|
|
26827
28212
|
const uploadResponse = await fetch(uploadInit.upload_url, {
|
|
26828
28213
|
method: "PUT",
|
|
26829
28214
|
headers: uploadInit.headers,
|
|
@@ -28227,11 +29612,40 @@ var SessionManager = class _SessionManager {
|
|
|
28227
29612
|
this.folderPath = contextFolderPath;
|
|
28228
29613
|
}
|
|
28229
29614
|
}
|
|
29615
|
+
/**
|
|
29616
|
+
* Update active workspace/project scope without resetting session identity.
|
|
29617
|
+
* Used when tools auto-resolve stale scope from folder/local index context.
|
|
29618
|
+
*/
|
|
29619
|
+
updateScope(input) {
|
|
29620
|
+
const nextWorkspaceId = typeof input.workspace_id === "string" && input.workspace_id.trim() ? input.workspace_id : void 0;
|
|
29621
|
+
const nextProjectId = typeof input.project_id === "string" && input.project_id.trim() ? input.project_id : void 0;
|
|
29622
|
+
const nextFolderPath = typeof input.folder_path === "string" && input.folder_path.trim() ? input.folder_path : void 0;
|
|
29623
|
+
if (!this.context) {
|
|
29624
|
+
this.context = {};
|
|
29625
|
+
}
|
|
29626
|
+
if (nextWorkspaceId) {
|
|
29627
|
+
this.context.workspace_id = nextWorkspaceId;
|
|
29628
|
+
}
|
|
29629
|
+
if (nextProjectId) {
|
|
29630
|
+
this.context.project_id = nextProjectId;
|
|
29631
|
+
}
|
|
29632
|
+
if (nextFolderPath) {
|
|
29633
|
+
this.context.folder_path = nextFolderPath;
|
|
29634
|
+
this.folderPath = nextFolderPath;
|
|
29635
|
+
}
|
|
29636
|
+
if (nextWorkspaceId || nextProjectId) {
|
|
29637
|
+
this.initialized = true;
|
|
29638
|
+
this.client.setDefaults({
|
|
29639
|
+
workspace_id: typeof this.context.workspace_id === "string" ? this.context.workspace_id : void 0,
|
|
29640
|
+
project_id: typeof this.context.project_id === "string" ? this.context.project_id : void 0
|
|
29641
|
+
});
|
|
29642
|
+
}
|
|
29643
|
+
}
|
|
28230
29644
|
/**
|
|
28231
29645
|
* Set the folder path hint (can be passed from tools that know the workspace path)
|
|
28232
29646
|
*/
|
|
28233
|
-
setFolderPath(
|
|
28234
|
-
this.folderPath =
|
|
29647
|
+
setFolderPath(path23) {
|
|
29648
|
+
this.folderPath = path23;
|
|
28235
29649
|
}
|
|
28236
29650
|
/**
|
|
28237
29651
|
* Mark that context_smart has been called in this session.
|
|
@@ -28421,7 +29835,7 @@ var SessionManager = class _SessionManager {
|
|
|
28421
29835
|
}
|
|
28422
29836
|
if (this.ideRoots.length === 0) {
|
|
28423
29837
|
const cwd = process.cwd();
|
|
28424
|
-
const
|
|
29838
|
+
const fs22 = await import("fs");
|
|
28425
29839
|
const projectIndicators = [
|
|
28426
29840
|
".git",
|
|
28427
29841
|
"package.json",
|
|
@@ -28431,7 +29845,7 @@ var SessionManager = class _SessionManager {
|
|
|
28431
29845
|
];
|
|
28432
29846
|
const hasProjectIndicator = projectIndicators.some((f) => {
|
|
28433
29847
|
try {
|
|
28434
|
-
return
|
|
29848
|
+
return fs22.existsSync(`${cwd}/${f}`);
|
|
28435
29849
|
} catch {
|
|
28436
29850
|
return false;
|
|
28437
29851
|
}
|
|
@@ -29010,9 +30424,9 @@ async function runHttpGateway() {
|
|
|
29010
30424
|
|
|
29011
30425
|
// src/index.ts
|
|
29012
30426
|
init_version();
|
|
29013
|
-
import { existsSync as existsSync18, mkdirSync as
|
|
29014
|
-
import { homedir as
|
|
29015
|
-
import { join as
|
|
30427
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
30428
|
+
import { homedir as homedir21 } from "os";
|
|
30429
|
+
import { join as join24 } from "path";
|
|
29016
30430
|
|
|
29017
30431
|
// src/setup.ts
|
|
29018
30432
|
import * as fs7 from "node:fs/promises";
|
|
@@ -29705,10 +31119,91 @@ function createProgressBar(progress, width = 30) {
|
|
|
29705
31119
|
const emptyBar = "\u2591".repeat(empty);
|
|
29706
31120
|
return `${colors.cyan}${filledBar}${colors.dim}${emptyBar}${colors.reset}`;
|
|
29707
31121
|
}
|
|
31122
|
+
async function selectProjectForCurrentDirectory(client, cwd, workspaceId, dryRun, rl) {
|
|
31123
|
+
if (!workspaceId || workspaceId === "dry-run") {
|
|
31124
|
+
return void 0;
|
|
31125
|
+
}
|
|
31126
|
+
const folderName = path8.basename(cwd) || "project";
|
|
31127
|
+
let projects = [];
|
|
31128
|
+
try {
|
|
31129
|
+
const result = await client.listProjects({
|
|
31130
|
+
workspace_id: workspaceId,
|
|
31131
|
+
page_size: 200
|
|
31132
|
+
});
|
|
31133
|
+
projects = Array.isArray(result?.items) ? result.items : Array.isArray(result?.data?.items) ? result.data.items : [];
|
|
31134
|
+
} catch {
|
|
31135
|
+
projects = [];
|
|
31136
|
+
}
|
|
31137
|
+
projects = projects.filter((p) => typeof p?.id === "string" && typeof p?.name === "string").sort((a, b) => (a.name || "").toLowerCase().localeCompare((b.name || "").toLowerCase()));
|
|
31138
|
+
const local = readLocalConfig(cwd);
|
|
31139
|
+
const linked = local?.project_id ? projects.find((p) => p.id === local.project_id) : void 0;
|
|
31140
|
+
const folderMatch = projects.find((p) => p.name?.toLowerCase() === folderName.toLowerCase());
|
|
31141
|
+
const options = [];
|
|
31142
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31143
|
+
if (linked?.id && linked?.name) {
|
|
31144
|
+
seen.add(linked.id);
|
|
31145
|
+
options.push({
|
|
31146
|
+
label: `Use currently linked project: ${linked.name} (${linked.id})`,
|
|
31147
|
+
kind: "existing",
|
|
31148
|
+
project: { id: linked.id, name: linked.name }
|
|
31149
|
+
});
|
|
31150
|
+
}
|
|
31151
|
+
if (folderMatch?.id && folderMatch?.name && !seen.has(folderMatch.id)) {
|
|
31152
|
+
seen.add(folderMatch.id);
|
|
31153
|
+
options.push({
|
|
31154
|
+
label: `Use project matching this folder: ${folderMatch.name} (${folderMatch.id})`,
|
|
31155
|
+
kind: "existing",
|
|
31156
|
+
project: { id: folderMatch.id, name: folderMatch.name }
|
|
31157
|
+
});
|
|
31158
|
+
}
|
|
31159
|
+
for (const project of projects) {
|
|
31160
|
+
if (!project.id || !project.name || seen.has(project.id)) continue;
|
|
31161
|
+
seen.add(project.id);
|
|
31162
|
+
options.push({
|
|
31163
|
+
label: `Use existing project: ${project.name} (${project.id})`,
|
|
31164
|
+
kind: "existing",
|
|
31165
|
+
project: { id: project.id, name: project.name }
|
|
31166
|
+
});
|
|
31167
|
+
}
|
|
31168
|
+
options.push({ label: `Create new project: ${folderName}`, kind: "create" });
|
|
31169
|
+
options.push({ label: "Skip project selection", kind: "skip" });
|
|
31170
|
+
if (options.length === 0) {
|
|
31171
|
+
return void 0;
|
|
31172
|
+
}
|
|
31173
|
+
console.log("\nProject selection (current directory):");
|
|
31174
|
+
options.forEach((opt, i) => console.log(` ${i + 1}) ${opt.label}`));
|
|
31175
|
+
const choiceRaw = normalizeInput(
|
|
31176
|
+
await rl.question(`Choose [1-${options.length}] (default 1): `)
|
|
31177
|
+
);
|
|
31178
|
+
const choiceNum = Number.parseInt(choiceRaw || "1", 10);
|
|
31179
|
+
const selected = Number.isFinite(choiceNum) ? options[choiceNum - 1] : options[0];
|
|
31180
|
+
if (!selected || selected.kind === "skip") {
|
|
31181
|
+
return void 0;
|
|
31182
|
+
}
|
|
31183
|
+
if (selected.kind === "existing" && selected.project) {
|
|
31184
|
+
return selected.project;
|
|
31185
|
+
}
|
|
31186
|
+
if (selected.kind === "create") {
|
|
31187
|
+
if (dryRun) {
|
|
31188
|
+
return { id: "dry-run-project", name: folderName };
|
|
31189
|
+
}
|
|
31190
|
+
const created = await client.createProject({
|
|
31191
|
+
workspace_id: workspaceId,
|
|
31192
|
+
name: folderName,
|
|
31193
|
+
description: `Project linked from ${cwd}`
|
|
31194
|
+
});
|
|
31195
|
+
const projectId = typeof created?.id === "string" ? created.id : typeof created?.data?.id === "string" ? created.data.id : void 0;
|
|
31196
|
+
const projectName = typeof created?.name === "string" ? created.name : typeof created?.data?.name === "string" ? created.data.name : folderName;
|
|
31197
|
+
if (projectId) {
|
|
31198
|
+
return { id: projectId, name: projectName };
|
|
31199
|
+
}
|
|
31200
|
+
}
|
|
31201
|
+
return void 0;
|
|
31202
|
+
}
|
|
29708
31203
|
async function indexProjectWithProgress(client, projectPath, workspaceId) {
|
|
29709
31204
|
const projectName = path8.basename(projectPath);
|
|
29710
31205
|
console.log(`
|
|
29711
|
-
${colors.bright}
|
|
31206
|
+
${colors.bright}Updating index for '${projectName}'...${colors.reset}`);
|
|
29712
31207
|
console.log(`${colors.dim}${projectPath}${colors.reset}
|
|
29713
31208
|
`);
|
|
29714
31209
|
let projectId;
|
|
@@ -29758,12 +31253,12 @@ ${colors.bright}Indexing: ${projectName}${colors.reset}`);
|
|
|
29758
31253
|
const countResult = await countIndexableFiles(projectPath, { maxFiles: 5e4 });
|
|
29759
31254
|
totalFiles = countResult.count;
|
|
29760
31255
|
if (countResult.stopped) {
|
|
29761
|
-
console.log(`${colors.dim}Found 50,000+
|
|
31256
|
+
console.log(`${colors.dim}Found 50,000+ files for indexing${colors.reset}`);
|
|
29762
31257
|
} else if (totalFiles === 0) {
|
|
29763
31258
|
console.log(`${colors.dim}No indexable files found${colors.reset}`);
|
|
29764
31259
|
return;
|
|
29765
31260
|
} else {
|
|
29766
|
-
console.log(`${colors.dim}Found ${totalFiles.toLocaleString()}
|
|
31261
|
+
console.log(`${colors.dim}Found ${totalFiles.toLocaleString()} files for indexing${colors.reset}`);
|
|
29767
31262
|
}
|
|
29768
31263
|
} catch {
|
|
29769
31264
|
console.log(`${colors.dim}Scanning files...${colors.reset}`);
|
|
@@ -29826,15 +31321,19 @@ ${colors.bright}Indexing: ${projectName}${colors.reset}`);
|
|
|
29826
31321
|
const finalSize = formatBytes(bytesIndexed);
|
|
29827
31322
|
process.stdout.write("\r" + " ".repeat(120) + "\r");
|
|
29828
31323
|
if (failedBatches > 0) {
|
|
29829
|
-
console.log(
|
|
31324
|
+
console.log(
|
|
31325
|
+
`${colors.yellow}\u2713${colors.reset} Index update complete: ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files indexed (${finalSize}) in ${elapsed}s (${failedBatches} batches had errors)`
|
|
31326
|
+
);
|
|
29830
31327
|
} else {
|
|
29831
|
-
console.log(
|
|
31328
|
+
console.log(
|
|
31329
|
+
`${colors.green}\u2713${colors.reset} Index update complete: ${colors.bright}${filesIndexed.toLocaleString()}${colors.reset} files indexed (${finalSize}) in ${elapsed}s`
|
|
31330
|
+
);
|
|
29832
31331
|
}
|
|
29833
31332
|
} catch (err) {
|
|
29834
31333
|
clearInterval(progressInterval);
|
|
29835
31334
|
process.stdout.write("\r" + " ".repeat(120) + "\r");
|
|
29836
31335
|
const msg = err instanceof Error ? err.message : String(err);
|
|
29837
|
-
console.log(`${colors.yellow}!
|
|
31336
|
+
console.log(`${colors.yellow}! Index update failed: ${msg}${colors.reset}`);
|
|
29838
31337
|
}
|
|
29839
31338
|
}
|
|
29840
31339
|
async function runSetupWizard(args) {
|
|
@@ -29973,10 +31472,10 @@ Code: ${device.user_code}`);
|
|
|
29973
31472
|
if (poll && poll.status === "pending") {
|
|
29974
31473
|
const intervalSeconds = typeof poll.interval === "number" ? poll.interval : 5;
|
|
29975
31474
|
const waitMs = Math.max(1, intervalSeconds) * 1e3;
|
|
29976
|
-
await new Promise((
|
|
31475
|
+
await new Promise((resolve18) => setTimeout(resolve18, waitMs));
|
|
29977
31476
|
continue;
|
|
29978
31477
|
}
|
|
29979
|
-
await new Promise((
|
|
31478
|
+
await new Promise((resolve18) => setTimeout(resolve18, 1e3));
|
|
29980
31479
|
}
|
|
29981
31480
|
if (!accessToken) {
|
|
29982
31481
|
throw new Error(
|
|
@@ -30024,6 +31523,7 @@ Code: ${device.user_code}`);
|
|
|
30024
31523
|
}
|
|
30025
31524
|
let workspaceId;
|
|
30026
31525
|
let workspaceName;
|
|
31526
|
+
let selectedCurrentProject;
|
|
30027
31527
|
console.log("Workspace setup:");
|
|
30028
31528
|
console.log(" 1) Create a new workspace");
|
|
30029
31529
|
console.log(" 2) Select an existing workspace");
|
|
@@ -30081,6 +31581,21 @@ Code: ${device.user_code}`);
|
|
|
30081
31581
|
}
|
|
30082
31582
|
}
|
|
30083
31583
|
}
|
|
31584
|
+
if (workspaceId && workspaceId !== "dry-run") {
|
|
31585
|
+
selectedCurrentProject = await selectProjectForCurrentDirectory(
|
|
31586
|
+
client,
|
|
31587
|
+
process.cwd(),
|
|
31588
|
+
workspaceId,
|
|
31589
|
+
dryRun,
|
|
31590
|
+
rl
|
|
31591
|
+
);
|
|
31592
|
+
if (selectedCurrentProject) {
|
|
31593
|
+
console.log(
|
|
31594
|
+
`Selected project: ${selectedCurrentProject.name} (${selectedCurrentProject.id})
|
|
31595
|
+
`
|
|
31596
|
+
);
|
|
31597
|
+
}
|
|
31598
|
+
}
|
|
30084
31599
|
const NO_HOOKS_EDITORS2 = ["codex", "aider", "antigravity"];
|
|
30085
31600
|
const getModeForEditor = (editor) => NO_HOOKS_EDITORS2.includes(editor) ? "full" : "bootstrap";
|
|
30086
31601
|
const detectedPlanName = await client.getPlanName();
|
|
@@ -30399,6 +31914,10 @@ Applying to ${projects.length} project(s)...`);
|
|
|
30399
31914
|
workspace_id: workspaceId,
|
|
30400
31915
|
workspace_name: workspaceName,
|
|
30401
31916
|
create_parent_mapping: createParentMapping,
|
|
31917
|
+
...path8.resolve(projectPath) === path8.resolve(process.cwd()) && selectedCurrentProject ? {
|
|
31918
|
+
project_id: selectedCurrentProject.id,
|
|
31919
|
+
project_name: selectedCurrentProject.name
|
|
31920
|
+
} : {},
|
|
30402
31921
|
// Include version and config info for desktop app compatibility
|
|
30403
31922
|
version: VERSION,
|
|
30404
31923
|
configured_editors: configuredEditors,
|
|
@@ -30538,7 +32057,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
30538
32057
|
if (indexingEnabled) {
|
|
30539
32058
|
await indexProjectWithProgress(client, process.cwd(), cwdConfig.workspace_id);
|
|
30540
32059
|
} else {
|
|
30541
|
-
console.log("\
|
|
32060
|
+
console.log("\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>");
|
|
30542
32061
|
}
|
|
30543
32062
|
}
|
|
30544
32063
|
} catch {
|
|
@@ -30553,7 +32072,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
30553
32072
|
console.log("though larger projects may take a bit longer.\n");
|
|
30554
32073
|
console.log("Your code is private and securely stored.\n");
|
|
30555
32074
|
const indexChoice = normalizeInput(
|
|
30556
|
-
await rl.question("
|
|
32075
|
+
await rl.question("Update index for full-featured context now? [Y/n]: ")
|
|
30557
32076
|
).toLowerCase();
|
|
30558
32077
|
const indexingEnabled = indexChoice !== "n" && indexChoice !== "no";
|
|
30559
32078
|
for (const projectPath of projects) {
|
|
@@ -30571,7 +32090,7 @@ Applying to ${projects.length} project(s)...`);
|
|
|
30571
32090
|
await indexProjectWithProgress(client, projectPath, workspaceId);
|
|
30572
32091
|
}
|
|
30573
32092
|
} else {
|
|
30574
|
-
console.log("\
|
|
32093
|
+
console.log("\nIndexing skipped for now. You can start it later with: contextstream-mcp index <path>");
|
|
30575
32094
|
}
|
|
30576
32095
|
}
|
|
30577
32096
|
console.log("\nDone.");
|
|
@@ -30602,14 +32121,14 @@ Applying to ${projects.length} project(s)...`);
|
|
|
30602
32121
|
// src/index.ts
|
|
30603
32122
|
var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
|
|
30604
32123
|
function showFirstRunMessage() {
|
|
30605
|
-
const configDir =
|
|
30606
|
-
const starShownFile =
|
|
32124
|
+
const configDir = join24(homedir21(), ".contextstream");
|
|
32125
|
+
const starShownFile = join24(configDir, ".star-shown");
|
|
30607
32126
|
if (existsSync18(starShownFile)) {
|
|
30608
32127
|
return;
|
|
30609
32128
|
}
|
|
30610
32129
|
if (!existsSync18(configDir)) {
|
|
30611
32130
|
try {
|
|
30612
|
-
|
|
32131
|
+
mkdirSync7(configDir, { recursive: true });
|
|
30613
32132
|
} catch {
|
|
30614
32133
|
return;
|
|
30615
32134
|
}
|
|
@@ -30622,7 +32141,7 @@ function showFirstRunMessage() {
|
|
|
30622
32141
|
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
32142
|
console.error("");
|
|
30624
32143
|
try {
|
|
30625
|
-
|
|
32144
|
+
writeFileSync8(starShownFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
30626
32145
|
} catch {
|
|
30627
32146
|
}
|
|
30628
32147
|
}
|