@contextstream/mcp-server 0.4.50 → 0.4.53
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 +140 -25
- package/dist/hooks/on-bash.js +190 -0
- package/dist/hooks/on-read.js +163 -0
- package/dist/hooks/on-save-intent.js +132 -0
- package/dist/hooks/on-task.js +139 -0
- package/dist/hooks/on-web.js +155 -0
- package/dist/hooks/post-compact.js +172 -0
- package/dist/hooks/pre-compact.js +3 -1
- package/dist/hooks/pre-tool-use.js +29 -4
- package/dist/hooks/runner.js +3161 -0
- package/dist/hooks/session-end.js +191 -0
- package/dist/hooks/session-init.js +174 -0
- package/dist/hooks/user-prompt-submit.js +250 -9
- package/dist/index.js +2458 -198
- package/dist/test-server.js +3 -0
- package/package.json +7 -4
- package/scripts/postinstall.js +56 -0
package/dist/index.js
CHANGED
|
@@ -335,7 +335,7 @@ var require_ignore = __commonJS({
|
|
|
335
335
|
// path matching.
|
|
336
336
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
337
337
|
// @returns {TestResult} true if a file is ignored
|
|
338
|
-
test(
|
|
338
|
+
test(path22, checkUnignored, mode) {
|
|
339
339
|
let ignored = false;
|
|
340
340
|
let unignored = false;
|
|
341
341
|
let matchedRule;
|
|
@@ -344,7 +344,7 @@ var require_ignore = __commonJS({
|
|
|
344
344
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
345
345
|
return;
|
|
346
346
|
}
|
|
347
|
-
const matched = rule[mode].test(
|
|
347
|
+
const matched = rule[mode].test(path22);
|
|
348
348
|
if (!matched) {
|
|
349
349
|
return;
|
|
350
350
|
}
|
|
@@ -365,17 +365,17 @@ var require_ignore = __commonJS({
|
|
|
365
365
|
var throwError = (message, Ctor) => {
|
|
366
366
|
throw new Ctor(message);
|
|
367
367
|
};
|
|
368
|
-
var checkPath = (
|
|
369
|
-
if (!isString(
|
|
368
|
+
var checkPath = (path22, originalPath, doThrow) => {
|
|
369
|
+
if (!isString(path22)) {
|
|
370
370
|
return doThrow(
|
|
371
371
|
`path must be a string, but got \`${originalPath}\``,
|
|
372
372
|
TypeError
|
|
373
373
|
);
|
|
374
374
|
}
|
|
375
|
-
if (!
|
|
375
|
+
if (!path22) {
|
|
376
376
|
return doThrow(`path must not be empty`, TypeError);
|
|
377
377
|
}
|
|
378
|
-
if (checkPath.isNotRelative(
|
|
378
|
+
if (checkPath.isNotRelative(path22)) {
|
|
379
379
|
const r = "`path.relative()`d";
|
|
380
380
|
return doThrow(
|
|
381
381
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -384,7 +384,7 @@ var require_ignore = __commonJS({
|
|
|
384
384
|
}
|
|
385
385
|
return true;
|
|
386
386
|
};
|
|
387
|
-
var isNotRelative = (
|
|
387
|
+
var isNotRelative = (path22) => REGEX_TEST_INVALID_PATH.test(path22);
|
|
388
388
|
checkPath.isNotRelative = isNotRelative;
|
|
389
389
|
checkPath.convert = (p) => p;
|
|
390
390
|
var Ignore2 = class {
|
|
@@ -414,19 +414,19 @@ var require_ignore = __commonJS({
|
|
|
414
414
|
}
|
|
415
415
|
// @returns {TestResult}
|
|
416
416
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
417
|
-
const
|
|
417
|
+
const path22 = originalPath && checkPath.convert(originalPath);
|
|
418
418
|
checkPath(
|
|
419
|
-
|
|
419
|
+
path22,
|
|
420
420
|
originalPath,
|
|
421
421
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
422
422
|
);
|
|
423
|
-
return this._t(
|
|
423
|
+
return this._t(path22, cache, checkUnignored, slices);
|
|
424
424
|
}
|
|
425
|
-
checkIgnore(
|
|
426
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
427
|
-
return this.test(
|
|
425
|
+
checkIgnore(path22) {
|
|
426
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path22)) {
|
|
427
|
+
return this.test(path22);
|
|
428
428
|
}
|
|
429
|
-
const slices =
|
|
429
|
+
const slices = path22.split(SLASH).filter(Boolean);
|
|
430
430
|
slices.pop();
|
|
431
431
|
if (slices.length) {
|
|
432
432
|
const parent = this._t(
|
|
@@ -439,18 +439,18 @@ var require_ignore = __commonJS({
|
|
|
439
439
|
return parent;
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
|
-
return this._rules.test(
|
|
442
|
+
return this._rules.test(path22, false, MODE_CHECK_IGNORE);
|
|
443
443
|
}
|
|
444
|
-
_t(
|
|
445
|
-
if (
|
|
446
|
-
return cache[
|
|
444
|
+
_t(path22, cache, checkUnignored, slices) {
|
|
445
|
+
if (path22 in cache) {
|
|
446
|
+
return cache[path22];
|
|
447
447
|
}
|
|
448
448
|
if (!slices) {
|
|
449
|
-
slices =
|
|
449
|
+
slices = path22.split(SLASH).filter(Boolean);
|
|
450
450
|
}
|
|
451
451
|
slices.pop();
|
|
452
452
|
if (!slices.length) {
|
|
453
|
-
return cache[
|
|
453
|
+
return cache[path22] = this._rules.test(path22, checkUnignored, MODE_IGNORE);
|
|
454
454
|
}
|
|
455
455
|
const parent = this._t(
|
|
456
456
|
slices.join(SLASH) + SLASH,
|
|
@@ -458,29 +458,29 @@ var require_ignore = __commonJS({
|
|
|
458
458
|
checkUnignored,
|
|
459
459
|
slices
|
|
460
460
|
);
|
|
461
|
-
return cache[
|
|
461
|
+
return cache[path22] = parent.ignored ? parent : this._rules.test(path22, checkUnignored, MODE_IGNORE);
|
|
462
462
|
}
|
|
463
|
-
ignores(
|
|
464
|
-
return this._test(
|
|
463
|
+
ignores(path22) {
|
|
464
|
+
return this._test(path22, this._ignoreCache, false).ignored;
|
|
465
465
|
}
|
|
466
466
|
createFilter() {
|
|
467
|
-
return (
|
|
467
|
+
return (path22) => !this.ignores(path22);
|
|
468
468
|
}
|
|
469
469
|
filter(paths) {
|
|
470
470
|
return makeArray(paths).filter(this.createFilter());
|
|
471
471
|
}
|
|
472
472
|
// @returns {TestResult}
|
|
473
|
-
test(
|
|
474
|
-
return this._test(
|
|
473
|
+
test(path22) {
|
|
474
|
+
return this._test(path22, this._testCache, true);
|
|
475
475
|
}
|
|
476
476
|
};
|
|
477
477
|
var factory = (options) => new Ignore2(options);
|
|
478
|
-
var isPathValid = (
|
|
478
|
+
var isPathValid = (path22) => checkPath(path22 && checkPath.convert(path22), path22, RETURN_FALSE);
|
|
479
479
|
var setupWindows = () => {
|
|
480
480
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
481
481
|
checkPath.convert = makePosix;
|
|
482
482
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
483
|
-
checkPath.isNotRelative = (
|
|
483
|
+
checkPath.isNotRelative = (path22) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path22) || isNotRelative(path22);
|
|
484
484
|
};
|
|
485
485
|
if (
|
|
486
486
|
// Detect `process` so that it can run in browsers.
|
|
@@ -514,6 +514,7 @@ __export(hooks_config_exports, {
|
|
|
514
514
|
getClineHooksDir: () => getClineHooksDir,
|
|
515
515
|
getCursorHooksConfigPath: () => getCursorHooksConfigPath,
|
|
516
516
|
getCursorHooksDir: () => getCursorHooksDir,
|
|
517
|
+
getHookCommand: () => getHookCommand,
|
|
517
518
|
getHooksDir: () => getHooksDir,
|
|
518
519
|
getIndexStatusPath: () => getIndexStatusPath,
|
|
519
520
|
getKiloCodeHooksDir: () => getKiloCodeHooksDir,
|
|
@@ -539,6 +540,23 @@ __export(hooks_config_exports, {
|
|
|
539
540
|
import * as fs4 from "node:fs/promises";
|
|
540
541
|
import * as path5 from "node:path";
|
|
541
542
|
import { homedir as homedir2 } from "node:os";
|
|
543
|
+
import { fileURLToPath } from "node:url";
|
|
544
|
+
function getHookCommand(hookName) {
|
|
545
|
+
const fs21 = __require("node:fs");
|
|
546
|
+
const binaryPath = "/usr/local/bin/contextstream-mcp";
|
|
547
|
+
if (fs21.existsSync(binaryPath)) {
|
|
548
|
+
return `${binaryPath} hook ${hookName}`;
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
const __dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
552
|
+
const indexPath = path5.join(__dirname, "index.js");
|
|
553
|
+
if (fs21.existsSync(indexPath)) {
|
|
554
|
+
return `node ${indexPath} hook ${hookName}`;
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
return `npx @contextstream/mcp-server hook ${hookName}`;
|
|
559
|
+
}
|
|
542
560
|
function getClaudeSettingsPath(scope, projectPath) {
|
|
543
561
|
if (scope === "user") {
|
|
544
562
|
return path5.join(homedir2(), ".claude", "settings.json");
|
|
@@ -558,19 +576,31 @@ function buildHooksConfig(options) {
|
|
|
558
576
|
hooks: [
|
|
559
577
|
{
|
|
560
578
|
type: "command",
|
|
561
|
-
command: "
|
|
579
|
+
command: getHookCommand("user-prompt-submit"),
|
|
562
580
|
timeout: 5
|
|
563
581
|
}
|
|
564
582
|
]
|
|
565
583
|
}
|
|
566
584
|
];
|
|
585
|
+
if (options?.includeOnSaveIntent !== false) {
|
|
586
|
+
userPromptHooks.push({
|
|
587
|
+
matcher: "*",
|
|
588
|
+
hooks: [
|
|
589
|
+
{
|
|
590
|
+
type: "command",
|
|
591
|
+
command: getHookCommand("on-save-intent"),
|
|
592
|
+
timeout: 5
|
|
593
|
+
}
|
|
594
|
+
]
|
|
595
|
+
});
|
|
596
|
+
}
|
|
567
597
|
if (options?.includeMediaAware !== false) {
|
|
568
598
|
userPromptHooks.push({
|
|
569
599
|
matcher: "*",
|
|
570
600
|
hooks: [
|
|
571
601
|
{
|
|
572
602
|
type: "command",
|
|
573
|
-
command: "
|
|
603
|
+
command: getHookCommand("media-aware"),
|
|
574
604
|
timeout: 5
|
|
575
605
|
}
|
|
576
606
|
]
|
|
@@ -583,7 +613,7 @@ function buildHooksConfig(options) {
|
|
|
583
613
|
hooks: [
|
|
584
614
|
{
|
|
585
615
|
type: "command",
|
|
586
|
-
command: "
|
|
616
|
+
command: getHookCommand("pre-tool-use"),
|
|
587
617
|
timeout: 5
|
|
588
618
|
}
|
|
589
619
|
]
|
|
@@ -594,12 +624,39 @@ function buildHooksConfig(options) {
|
|
|
594
624
|
if (options?.includePreCompact !== false) {
|
|
595
625
|
config.PreCompact = [
|
|
596
626
|
{
|
|
597
|
-
// Match both manual (/compact) and automatic compaction
|
|
598
627
|
matcher: "*",
|
|
599
628
|
hooks: [
|
|
600
629
|
{
|
|
601
630
|
type: "command",
|
|
602
|
-
command: "
|
|
631
|
+
command: getHookCommand("pre-compact"),
|
|
632
|
+
timeout: 10
|
|
633
|
+
}
|
|
634
|
+
]
|
|
635
|
+
}
|
|
636
|
+
];
|
|
637
|
+
}
|
|
638
|
+
if (options?.includeSessionInit !== false) {
|
|
639
|
+
config.SessionStart = [
|
|
640
|
+
{
|
|
641
|
+
matcher: "*",
|
|
642
|
+
hooks: [
|
|
643
|
+
{
|
|
644
|
+
type: "command",
|
|
645
|
+
command: getHookCommand("session-init"),
|
|
646
|
+
timeout: 10
|
|
647
|
+
}
|
|
648
|
+
]
|
|
649
|
+
}
|
|
650
|
+
];
|
|
651
|
+
}
|
|
652
|
+
if (options?.includeSessionEnd !== false) {
|
|
653
|
+
config.Stop = [
|
|
654
|
+
{
|
|
655
|
+
matcher: "*",
|
|
656
|
+
hooks: [
|
|
657
|
+
{
|
|
658
|
+
type: "command",
|
|
659
|
+
command: getHookCommand("session-end"),
|
|
603
660
|
timeout: 10
|
|
604
661
|
}
|
|
605
662
|
]
|
|
@@ -613,7 +670,7 @@ function buildHooksConfig(options) {
|
|
|
613
670
|
hooks: [
|
|
614
671
|
{
|
|
615
672
|
type: "command",
|
|
616
|
-
command: "
|
|
673
|
+
command: getHookCommand("post-write"),
|
|
617
674
|
timeout: 10
|
|
618
675
|
}
|
|
619
676
|
]
|
|
@@ -625,12 +682,60 @@ function buildHooksConfig(options) {
|
|
|
625
682
|
hooks: [
|
|
626
683
|
{
|
|
627
684
|
type: "command",
|
|
628
|
-
command: "
|
|
685
|
+
command: getHookCommand("auto-rules"),
|
|
629
686
|
timeout: 15
|
|
630
687
|
}
|
|
631
688
|
]
|
|
632
689
|
});
|
|
633
690
|
}
|
|
691
|
+
if (options?.includeOnBash !== false) {
|
|
692
|
+
postToolUseHooks.push({
|
|
693
|
+
matcher: "Bash",
|
|
694
|
+
hooks: [
|
|
695
|
+
{
|
|
696
|
+
type: "command",
|
|
697
|
+
command: getHookCommand("on-bash"),
|
|
698
|
+
timeout: 5
|
|
699
|
+
}
|
|
700
|
+
]
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
if (options?.includeOnTask !== false) {
|
|
704
|
+
postToolUseHooks.push({
|
|
705
|
+
matcher: "Task",
|
|
706
|
+
hooks: [
|
|
707
|
+
{
|
|
708
|
+
type: "command",
|
|
709
|
+
command: getHookCommand("on-task"),
|
|
710
|
+
timeout: 5
|
|
711
|
+
}
|
|
712
|
+
]
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
if (options?.includeOnRead !== false) {
|
|
716
|
+
postToolUseHooks.push({
|
|
717
|
+
matcher: "Read|Glob|Grep",
|
|
718
|
+
hooks: [
|
|
719
|
+
{
|
|
720
|
+
type: "command",
|
|
721
|
+
command: getHookCommand("on-read"),
|
|
722
|
+
timeout: 5
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
if (options?.includeOnWeb !== false) {
|
|
728
|
+
postToolUseHooks.push({
|
|
729
|
+
matcher: "WebFetch|WebSearch",
|
|
730
|
+
hooks: [
|
|
731
|
+
{
|
|
732
|
+
type: "command",
|
|
733
|
+
command: getHookCommand("on-web"),
|
|
734
|
+
timeout: 5
|
|
735
|
+
}
|
|
736
|
+
]
|
|
737
|
+
});
|
|
738
|
+
}
|
|
634
739
|
if (postToolUseHooks.length > 0) {
|
|
635
740
|
config.PostToolUse = postToolUseHooks;
|
|
636
741
|
}
|
|
@@ -640,17 +745,17 @@ async function installHookScripts(options) {
|
|
|
640
745
|
const hooksDir = getHooksDir();
|
|
641
746
|
await fs4.mkdir(hooksDir, { recursive: true });
|
|
642
747
|
const result = {
|
|
643
|
-
preToolUse: "
|
|
644
|
-
userPrompt: "
|
|
748
|
+
preToolUse: getHookCommand("pre-tool-use"),
|
|
749
|
+
userPrompt: getHookCommand("user-prompt-submit")
|
|
645
750
|
};
|
|
646
751
|
if (options?.includePreCompact !== false) {
|
|
647
|
-
result.preCompact = "
|
|
752
|
+
result.preCompact = getHookCommand("pre-compact");
|
|
648
753
|
}
|
|
649
754
|
if (options?.includeMediaAware !== false) {
|
|
650
|
-
result.mediaAware = "
|
|
755
|
+
result.mediaAware = getHookCommand("media-aware");
|
|
651
756
|
}
|
|
652
757
|
if (options?.includeAutoRules !== false) {
|
|
653
|
-
result.autoRules = "
|
|
758
|
+
result.autoRules = getHookCommand("auto-rules");
|
|
654
759
|
}
|
|
655
760
|
return result;
|
|
656
761
|
}
|
|
@@ -686,20 +791,20 @@ function mergeHooksIntoSettings(existingSettings, newHooks) {
|
|
|
686
791
|
async function installClaudeCodeHooks(options) {
|
|
687
792
|
const result = { scripts: [], settings: [] };
|
|
688
793
|
result.scripts.push(
|
|
689
|
-
"
|
|
690
|
-
"
|
|
794
|
+
getHookCommand("pre-tool-use"),
|
|
795
|
+
getHookCommand("user-prompt-submit")
|
|
691
796
|
);
|
|
692
797
|
if (options.includePreCompact !== false) {
|
|
693
|
-
result.scripts.push("
|
|
798
|
+
result.scripts.push(getHookCommand("pre-compact"));
|
|
694
799
|
}
|
|
695
800
|
if (options.includeMediaAware !== false) {
|
|
696
|
-
result.scripts.push("
|
|
801
|
+
result.scripts.push(getHookCommand("media-aware"));
|
|
697
802
|
}
|
|
698
803
|
if (options.includePostWrite !== false) {
|
|
699
|
-
result.scripts.push("
|
|
804
|
+
result.scripts.push(getHookCommand("post-write"));
|
|
700
805
|
}
|
|
701
806
|
if (options.includeAutoRules !== false) {
|
|
702
|
-
result.scripts.push("
|
|
807
|
+
result.scripts.push(getHookCommand("auto-rules"));
|
|
703
808
|
}
|
|
704
809
|
const hooksConfig = buildHooksConfig({
|
|
705
810
|
includePreCompact: options.includePreCompact,
|
|
@@ -977,6 +1082,8 @@ async function installCursorHookScripts(options) {
|
|
|
977
1082
|
};
|
|
978
1083
|
const filteredPreToolUse = filterContextStreamHooks(existingConfig.hooks.preToolUse);
|
|
979
1084
|
const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
|
|
1085
|
+
const preToolUseCommand = getHookCommand("pre-tool-use");
|
|
1086
|
+
const userPromptCommand = getHookCommand("user-prompt-submit");
|
|
980
1087
|
const config = {
|
|
981
1088
|
version: 1,
|
|
982
1089
|
hooks: {
|
|
@@ -984,7 +1091,7 @@ async function installCursorHookScripts(options) {
|
|
|
984
1091
|
preToolUse: [
|
|
985
1092
|
...filteredPreToolUse,
|
|
986
1093
|
{
|
|
987
|
-
command:
|
|
1094
|
+
command: preToolUseCommand,
|
|
988
1095
|
type: "command",
|
|
989
1096
|
timeout: 5,
|
|
990
1097
|
matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
|
|
@@ -993,7 +1100,7 @@ async function installCursorHookScripts(options) {
|
|
|
993
1100
|
beforeSubmitPrompt: [
|
|
994
1101
|
...filteredBeforeSubmit,
|
|
995
1102
|
{
|
|
996
|
-
command:
|
|
1103
|
+
command: userPromptCommand,
|
|
997
1104
|
type: "command",
|
|
998
1105
|
timeout: 5
|
|
999
1106
|
}
|
|
@@ -1003,8 +1110,8 @@ async function installCursorHookScripts(options) {
|
|
|
1003
1110
|
await writeCursorHooksConfig(config, options.scope, options.projectPath);
|
|
1004
1111
|
const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
|
|
1005
1112
|
return {
|
|
1006
|
-
preToolUse:
|
|
1007
|
-
beforeSubmitPrompt:
|
|
1113
|
+
preToolUse: preToolUseCommand,
|
|
1114
|
+
beforeSubmitPrompt: userPromptCommand,
|
|
1008
1115
|
config: configPath
|
|
1009
1116
|
};
|
|
1010
1117
|
}
|
|
@@ -1813,11 +1920,13 @@ esac
|
|
|
1813
1920
|
|
|
1814
1921
|
exit 0
|
|
1815
1922
|
`;
|
|
1816
|
-
CLINE_HOOK_WRAPPER = (hookName) =>
|
|
1923
|
+
CLINE_HOOK_WRAPPER = (hookName) => {
|
|
1924
|
+
const command = getHookCommand(hookName);
|
|
1925
|
+
return `#!/bin/bash
|
|
1817
1926
|
# ContextStream ${hookName} Hook Wrapper for Cline/Roo/Kilo Code
|
|
1818
|
-
|
|
1819
|
-
exec npx @contextstream/mcp-server hook ${hookName}
|
|
1927
|
+
exec ${command}
|
|
1820
1928
|
`;
|
|
1929
|
+
};
|
|
1821
1930
|
CURSOR_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
|
|
1822
1931
|
"""
|
|
1823
1932
|
ContextStream PreToolUse Hook for Cursor
|
|
@@ -2409,8 +2518,17 @@ function extractToolInput(input) {
|
|
|
2409
2518
|
return input.tool_input || input.parameters || input.toolParameters || {};
|
|
2410
2519
|
}
|
|
2411
2520
|
function blockClaudeCode(message) {
|
|
2412
|
-
|
|
2413
|
-
|
|
2521
|
+
const response = {
|
|
2522
|
+
hookSpecificOutput: {
|
|
2523
|
+
hookEventName: "PreToolUse",
|
|
2524
|
+
// Use additionalContext instead of deny - tool runs but Claude sees the message
|
|
2525
|
+
additionalContext: `[CONTEXTSTREAM] ${message}`
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] REDIRECT (additionalContext): ${JSON.stringify(response)}
|
|
2529
|
+
`);
|
|
2530
|
+
console.log(JSON.stringify(response));
|
|
2531
|
+
process.exit(0);
|
|
2414
2532
|
}
|
|
2415
2533
|
function outputClineBlock(errorMessage, contextMod) {
|
|
2416
2534
|
const result = {
|
|
@@ -2439,13 +2557,18 @@ function detectEditorFormat(input) {
|
|
|
2439
2557
|
if (input.hookName !== void 0 || input.toolName !== void 0) {
|
|
2440
2558
|
return "cline";
|
|
2441
2559
|
}
|
|
2442
|
-
if (input.hook_event_name !== void 0) {
|
|
2443
|
-
return "
|
|
2560
|
+
if (input.hook_event_name !== void 0 || input.tool_name !== void 0) {
|
|
2561
|
+
return "claude";
|
|
2444
2562
|
}
|
|
2445
2563
|
return "claude";
|
|
2446
2564
|
}
|
|
2447
2565
|
async function runPreToolUseHook() {
|
|
2566
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Hook invoked at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
2567
|
+
`);
|
|
2568
|
+
console.error("[PreToolUse] Hook invoked at", (/* @__PURE__ */ new Date()).toISOString());
|
|
2448
2569
|
if (!ENABLED2) {
|
|
2570
|
+
fs9.appendFileSync(DEBUG_FILE, "[PreToolUse] Hook disabled, exiting\n");
|
|
2571
|
+
console.error("[PreToolUse] Hook disabled, exiting");
|
|
2449
2572
|
process.exit(0);
|
|
2450
2573
|
}
|
|
2451
2574
|
let inputData = "";
|
|
@@ -2465,8 +2588,14 @@ async function runPreToolUseHook() {
|
|
|
2465
2588
|
const cwd = extractCwd2(input);
|
|
2466
2589
|
const tool = extractToolName(input);
|
|
2467
2590
|
const toolInput = extractToolInput(input);
|
|
2591
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] tool=${tool}, cwd=${cwd}, editorFormat=${editorFormat}
|
|
2592
|
+
`);
|
|
2468
2593
|
const { isIndexed } = isProjectIndexed(cwd);
|
|
2594
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] isIndexed=${isIndexed}
|
|
2595
|
+
`);
|
|
2469
2596
|
if (!isIndexed) {
|
|
2597
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Project not indexed, allowing
|
|
2598
|
+
`);
|
|
2470
2599
|
if (editorFormat === "cline") {
|
|
2471
2600
|
outputClineAllow();
|
|
2472
2601
|
} else if (editorFormat === "cursor") {
|
|
@@ -2476,8 +2605,12 @@ async function runPreToolUseHook() {
|
|
|
2476
2605
|
}
|
|
2477
2606
|
if (tool === "Glob") {
|
|
2478
2607
|
const pattern = toolInput?.pattern || "";
|
|
2608
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
|
|
2609
|
+
`);
|
|
2479
2610
|
if (isDiscoveryGlob(pattern)) {
|
|
2480
2611
|
const msg = `STOP: Use mcp__contextstream__search(mode="hybrid", query="${pattern}") instead of Glob.`;
|
|
2612
|
+
fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
|
|
2613
|
+
`);
|
|
2481
2614
|
if (editorFormat === "cline") {
|
|
2482
2615
|
outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
|
|
2483
2616
|
} else if (editorFormat === "cursor") {
|
|
@@ -2554,12 +2687,13 @@ async function runPreToolUseHook() {
|
|
|
2554
2687
|
}
|
|
2555
2688
|
process.exit(0);
|
|
2556
2689
|
}
|
|
2557
|
-
var ENABLED2, INDEX_STATUS_FILE, STALE_THRESHOLD_DAYS, DISCOVERY_PATTERNS, isDirectRun2;
|
|
2690
|
+
var ENABLED2, INDEX_STATUS_FILE, DEBUG_FILE, STALE_THRESHOLD_DAYS, DISCOVERY_PATTERNS, isDirectRun2;
|
|
2558
2691
|
var init_pre_tool_use = __esm({
|
|
2559
2692
|
"src/hooks/pre-tool-use.ts"() {
|
|
2560
2693
|
"use strict";
|
|
2561
2694
|
ENABLED2 = process.env.CONTEXTSTREAM_HOOK_ENABLED !== "false";
|
|
2562
2695
|
INDEX_STATUS_FILE = path10.join(homedir7(), ".contextstream", "indexed-projects.json");
|
|
2696
|
+
DEBUG_FILE = "/tmp/pretooluse-hook-debug.log";
|
|
2563
2697
|
STALE_THRESHOLD_DAYS = 7;
|
|
2564
2698
|
DISCOVERY_PATTERNS = ["**/*", "**/", "src/**", "lib/**", "app/**", "components/**"];
|
|
2565
2699
|
isDirectRun2 = process.argv[1]?.includes("pre-tool-use") || process.argv[2] === "pre-tool-use";
|
|
@@ -2574,6 +2708,184 @@ var user_prompt_submit_exports = {};
|
|
|
2574
2708
|
__export(user_prompt_submit_exports, {
|
|
2575
2709
|
runUserPromptSubmitHook: () => runUserPromptSubmitHook
|
|
2576
2710
|
});
|
|
2711
|
+
import * as fs10 from "node:fs";
|
|
2712
|
+
import * as path11 from "node:path";
|
|
2713
|
+
import { homedir as homedir8 } from "node:os";
|
|
2714
|
+
function loadConfigFromMcpJson(cwd) {
|
|
2715
|
+
let searchDir = path11.resolve(cwd);
|
|
2716
|
+
for (let i = 0; i < 5; i++) {
|
|
2717
|
+
if (!API_KEY2) {
|
|
2718
|
+
const mcpPath = path11.join(searchDir, ".mcp.json");
|
|
2719
|
+
if (fs10.existsSync(mcpPath)) {
|
|
2720
|
+
try {
|
|
2721
|
+
const content = fs10.readFileSync(mcpPath, "utf-8");
|
|
2722
|
+
const config = JSON.parse(content);
|
|
2723
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
2724
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
2725
|
+
API_KEY2 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
2726
|
+
}
|
|
2727
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
2728
|
+
API_URL2 = csEnv.CONTEXTSTREAM_API_URL;
|
|
2729
|
+
}
|
|
2730
|
+
if (csEnv?.CONTEXTSTREAM_WORKSPACE_ID) {
|
|
2731
|
+
WORKSPACE_ID = csEnv.CONTEXTSTREAM_WORKSPACE_ID;
|
|
2732
|
+
}
|
|
2733
|
+
} catch {
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
if (!WORKSPACE_ID || !PROJECT_ID) {
|
|
2738
|
+
const csConfigPath = path11.join(searchDir, ".contextstream", "config.json");
|
|
2739
|
+
if (fs10.existsSync(csConfigPath)) {
|
|
2740
|
+
try {
|
|
2741
|
+
const content = fs10.readFileSync(csConfigPath, "utf-8");
|
|
2742
|
+
const csConfig = JSON.parse(content);
|
|
2743
|
+
if (csConfig.workspace_id && !WORKSPACE_ID) {
|
|
2744
|
+
WORKSPACE_ID = csConfig.workspace_id;
|
|
2745
|
+
}
|
|
2746
|
+
if (csConfig.project_id && !PROJECT_ID) {
|
|
2747
|
+
PROJECT_ID = csConfig.project_id;
|
|
2748
|
+
}
|
|
2749
|
+
} catch {
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
const parentDir = path11.dirname(searchDir);
|
|
2754
|
+
if (parentDir === searchDir) break;
|
|
2755
|
+
searchDir = parentDir;
|
|
2756
|
+
}
|
|
2757
|
+
if (!API_KEY2) {
|
|
2758
|
+
const homeMcpPath = path11.join(homedir8(), ".mcp.json");
|
|
2759
|
+
if (fs10.existsSync(homeMcpPath)) {
|
|
2760
|
+
try {
|
|
2761
|
+
const content = fs10.readFileSync(homeMcpPath, "utf-8");
|
|
2762
|
+
const config = JSON.parse(content);
|
|
2763
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
2764
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
2765
|
+
API_KEY2 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
2766
|
+
}
|
|
2767
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
2768
|
+
API_URL2 = csEnv.CONTEXTSTREAM_API_URL;
|
|
2769
|
+
}
|
|
2770
|
+
} catch {
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
async function fetchSessionContext() {
|
|
2776
|
+
if (!API_KEY2) return null;
|
|
2777
|
+
try {
|
|
2778
|
+
const controller = new AbortController();
|
|
2779
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
2780
|
+
const url = new URL(`${API_URL2}/api/v1/context`);
|
|
2781
|
+
if (WORKSPACE_ID) url.searchParams.set("workspace_id", WORKSPACE_ID);
|
|
2782
|
+
if (PROJECT_ID) url.searchParams.set("project_id", PROJECT_ID);
|
|
2783
|
+
url.searchParams.set("include_lessons", "true");
|
|
2784
|
+
url.searchParams.set("include_decisions", "true");
|
|
2785
|
+
url.searchParams.set("include_plans", "true");
|
|
2786
|
+
url.searchParams.set("include_reminders", "true");
|
|
2787
|
+
url.searchParams.set("limit", "3");
|
|
2788
|
+
const response = await fetch(url.toString(), {
|
|
2789
|
+
method: "GET",
|
|
2790
|
+
headers: {
|
|
2791
|
+
"X-API-Key": API_KEY2
|
|
2792
|
+
},
|
|
2793
|
+
signal: controller.signal
|
|
2794
|
+
});
|
|
2795
|
+
clearTimeout(timeoutId);
|
|
2796
|
+
if (response.ok) {
|
|
2797
|
+
return await response.json();
|
|
2798
|
+
}
|
|
2799
|
+
return null;
|
|
2800
|
+
} catch {
|
|
2801
|
+
return null;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
function buildEnhancedReminder(ctx, isNewSession2) {
|
|
2805
|
+
const parts = [ENHANCED_REMINDER_HEADER];
|
|
2806
|
+
if (isNewSession2) {
|
|
2807
|
+
parts.push(`## \u{1F680} NEW SESSION DETECTED
|
|
2808
|
+
1. Call \`init(folder_path="...")\` - this triggers project indexing
|
|
2809
|
+
2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
|
|
2810
|
+
3. Then call \`context(user_message="...")\` for task-specific context
|
|
2811
|
+
4. Use \`search(mode="hybrid")\` for code discovery (not Glob/Grep/Read)
|
|
2812
|
+
|
|
2813
|
+
`);
|
|
2814
|
+
}
|
|
2815
|
+
if (ctx?.lessons && ctx.lessons.length > 0) {
|
|
2816
|
+
parts.push(`## \u26A0\uFE0F LESSONS FROM PAST MISTAKES`);
|
|
2817
|
+
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
2818
|
+
parts.push(`- **${lesson.title}**: ${lesson.prevention}`);
|
|
2819
|
+
}
|
|
2820
|
+
parts.push("");
|
|
2821
|
+
}
|
|
2822
|
+
if (ctx?.active_plans && ctx.active_plans.length > 0) {
|
|
2823
|
+
parts.push(`## \u{1F4CB} Active Plans`);
|
|
2824
|
+
for (const plan of ctx.active_plans.slice(0, 3)) {
|
|
2825
|
+
parts.push(`- ${plan.title} (${plan.status})`);
|
|
2826
|
+
}
|
|
2827
|
+
parts.push("");
|
|
2828
|
+
}
|
|
2829
|
+
if (ctx?.pending_tasks && ctx.pending_tasks.length > 0) {
|
|
2830
|
+
parts.push(`## \u2705 Pending Tasks`);
|
|
2831
|
+
for (const task of ctx.pending_tasks.slice(0, 5)) {
|
|
2832
|
+
parts.push(`- ${task.title}`);
|
|
2833
|
+
}
|
|
2834
|
+
parts.push("");
|
|
2835
|
+
}
|
|
2836
|
+
if (ctx?.reminders && ctx.reminders.length > 0) {
|
|
2837
|
+
parts.push(`## \u{1F514} Reminders`);
|
|
2838
|
+
for (const reminder of ctx.reminders.slice(0, 3)) {
|
|
2839
|
+
parts.push(`- ${reminder.title}`);
|
|
2840
|
+
}
|
|
2841
|
+
parts.push("");
|
|
2842
|
+
}
|
|
2843
|
+
parts.push("---\n");
|
|
2844
|
+
parts.push(REMINDER);
|
|
2845
|
+
parts.push(`
|
|
2846
|
+
|
|
2847
|
+
---
|
|
2848
|
+
## \u{1F6A8} FILE INDEXING & SEARCH - CRITICAL (No PostToolUse Hook) \u{1F6A8}
|
|
2849
|
+
|
|
2850
|
+
**This editor does NOT have automatic file indexing after Edit/Write.**
|
|
2851
|
+
|
|
2852
|
+
### \u26A0\uFE0F BEFORE ANY SEARCH - Check Index Status:
|
|
2853
|
+
\`\`\`
|
|
2854
|
+
project(action="index_status")
|
|
2855
|
+
\`\`\`
|
|
2856
|
+
Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
|
|
2857
|
+
|
|
2858
|
+
### \u{1F50D} Search Decision Tree:
|
|
2859
|
+
|
|
2860
|
+
**IF indexed=true AND last_indexed_at is recent:**
|
|
2861
|
+
\u2192 Use \`search(mode="hybrid", query="...")\`
|
|
2862
|
+
|
|
2863
|
+
**IF indexed=false OR last_indexed_at is stale (>7 days):**
|
|
2864
|
+
\u2192 Use local tools (Glob/Grep/Read) directly
|
|
2865
|
+
\u2192 OR run \`project(action="index")\` first, then search
|
|
2866
|
+
|
|
2867
|
+
**IF search returns 0 results or errors:**
|
|
2868
|
+
\u2192 Fallback to local tools (Glob/Grep/Read)
|
|
2869
|
+
|
|
2870
|
+
### \u2705 When Local Tools (Glob/Grep/Read) Are OK:
|
|
2871
|
+
- Project is NOT indexed
|
|
2872
|
+
- Index is stale/outdated (>7 days)
|
|
2873
|
+
- ContextStream search returns 0 results
|
|
2874
|
+
- ContextStream returns errors
|
|
2875
|
+
- User explicitly requests local tools
|
|
2876
|
+
|
|
2877
|
+
### On Session Start:
|
|
2878
|
+
1. Call \`init(folder_path="...")\` - triggers initial indexing
|
|
2879
|
+
2. Check \`project(action="index_status")\` before searching
|
|
2880
|
+
3. If not indexed: use local tools OR wait for indexing
|
|
2881
|
+
|
|
2882
|
+
### After File Changes (Edit/Write/Create):
|
|
2883
|
+
Files are NOT auto-indexed. You MUST:
|
|
2884
|
+
1. After significant edits: \`project(action="index")\`
|
|
2885
|
+
2. For single file: \`project(action="ingest_local", path="<file>")\`
|
|
2886
|
+
3. Then search will find your changes`);
|
|
2887
|
+
return parts.join("\n");
|
|
2888
|
+
}
|
|
2577
2889
|
function detectEditorFormat2(input) {
|
|
2578
2890
|
if (input.hookName !== void 0) {
|
|
2579
2891
|
return "cline";
|
|
@@ -2581,8 +2893,23 @@ function detectEditorFormat2(input) {
|
|
|
2581
2893
|
if (input.hook_event_name === "beforeSubmitPrompt") {
|
|
2582
2894
|
return "cursor";
|
|
2583
2895
|
}
|
|
2896
|
+
if (input.hook_event_name === "beforeAgentAction" || input.hook_event_name === "onPromptSubmit") {
|
|
2897
|
+
return "antigravity";
|
|
2898
|
+
}
|
|
2584
2899
|
return "claude";
|
|
2585
2900
|
}
|
|
2901
|
+
function isNewSession(input, editorFormat) {
|
|
2902
|
+
if (editorFormat === "claude" && input.session?.messages) {
|
|
2903
|
+
return input.session.messages.length <= 1;
|
|
2904
|
+
}
|
|
2905
|
+
if (editorFormat === "cursor" && input.history !== void 0) {
|
|
2906
|
+
return input.history.length === 0;
|
|
2907
|
+
}
|
|
2908
|
+
if (editorFormat === "antigravity" && input.history !== void 0) {
|
|
2909
|
+
return input.history.length === 0;
|
|
2910
|
+
}
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2586
2913
|
async function runUserPromptSubmitHook() {
|
|
2587
2914
|
if (!ENABLED3) {
|
|
2588
2915
|
process.exit(0);
|
|
@@ -2601,6 +2928,7 @@ async function runUserPromptSubmitHook() {
|
|
|
2601
2928
|
process.exit(0);
|
|
2602
2929
|
}
|
|
2603
2930
|
const editorFormat = detectEditorFormat2(input);
|
|
2931
|
+
const cwd = input.cwd || process.cwd();
|
|
2604
2932
|
if (editorFormat === "claude") {
|
|
2605
2933
|
console.log(
|
|
2606
2934
|
JSON.stringify({
|
|
@@ -2611,37 +2939,84 @@ async function runUserPromptSubmitHook() {
|
|
|
2611
2939
|
})
|
|
2612
2940
|
);
|
|
2613
2941
|
} else if (editorFormat === "cline") {
|
|
2942
|
+
loadConfigFromMcpJson(cwd);
|
|
2943
|
+
const newSession = isNewSession(input, editorFormat);
|
|
2944
|
+
const ctx = await fetchSessionContext();
|
|
2945
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession);
|
|
2614
2946
|
console.log(
|
|
2615
2947
|
JSON.stringify({
|
|
2616
2948
|
cancel: false,
|
|
2617
|
-
contextModification:
|
|
2949
|
+
contextModification: enhancedReminder
|
|
2618
2950
|
})
|
|
2619
2951
|
);
|
|
2620
2952
|
} else if (editorFormat === "cursor") {
|
|
2953
|
+
loadConfigFromMcpJson(cwd);
|
|
2954
|
+
const newSession = isNewSession(input, editorFormat);
|
|
2955
|
+
const ctx = await fetchSessionContext();
|
|
2956
|
+
const cursorReminder = ctx?.lessons?.length ? `[CONTEXTSTREAM] \u26A0\uFE0F ${ctx.lessons.length} lessons from past mistakes. Use search(mode="hybrid") before Glob/Grep. Call context() first. After file edits: project(action="index") to re-index.` : `[CONTEXTSTREAM] Use search(mode="hybrid") before Glob/Grep/Read. Call context() first. After file edits: project(action="index") to re-index.`;
|
|
2621
2957
|
console.log(
|
|
2622
2958
|
JSON.stringify({
|
|
2623
2959
|
continue: true,
|
|
2624
|
-
user_message:
|
|
2960
|
+
user_message: cursorReminder
|
|
2961
|
+
})
|
|
2962
|
+
);
|
|
2963
|
+
} else if (editorFormat === "antigravity") {
|
|
2964
|
+
loadConfigFromMcpJson(cwd);
|
|
2965
|
+
const newSession = isNewSession(input, editorFormat);
|
|
2966
|
+
const ctx = await fetchSessionContext();
|
|
2967
|
+
const enhancedReminder = buildEnhancedReminder(ctx, newSession);
|
|
2968
|
+
console.log(
|
|
2969
|
+
JSON.stringify({
|
|
2970
|
+
cancel: false,
|
|
2971
|
+
contextModification: enhancedReminder
|
|
2625
2972
|
})
|
|
2626
2973
|
);
|
|
2627
2974
|
}
|
|
2628
2975
|
process.exit(0);
|
|
2629
2976
|
}
|
|
2630
|
-
var ENABLED3, REMINDER, isDirectRun3;
|
|
2977
|
+
var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun3;
|
|
2631
2978
|
var init_user_prompt_submit = __esm({
|
|
2632
2979
|
"src/hooks/user-prompt-submit.ts"() {
|
|
2633
2980
|
"use strict";
|
|
2634
2981
|
ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2982
|
+
API_URL2 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
2983
|
+
API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
2984
|
+
WORKSPACE_ID = null;
|
|
2985
|
+
PROJECT_ID = null;
|
|
2986
|
+
REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
|
|
2987
|
+
|
|
2988
|
+
1. FIRST: Call mcp__contextstream__context(user_message="...") before ANY other tool
|
|
2989
|
+
- Returns: dynamic rules, lessons from past mistakes, relevant context
|
|
2990
|
+
- Check response for: [LESSONS_WARNING], [RULES_NOTICE], preferences
|
|
2991
|
+
|
|
2992
|
+
2. FOR CODE SEARCH: Check index status, then search appropriately
|
|
2993
|
+
\u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
|
|
2994
|
+
\u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="hybrid", query="...")
|
|
2995
|
+
\u2705 IF NOT indexed OR stale: Use local tools (Glob/Grep/Read) directly
|
|
2996
|
+
\u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
|
|
2997
|
+
|
|
2998
|
+
3. WHEN LOCAL TOOLS (Glob/Grep/Read) ARE OK:
|
|
2999
|
+
\u2705 Project is NOT indexed (index_status.indexed=false)
|
|
3000
|
+
\u2705 Index is stale/outdated (>7 days old)
|
|
3001
|
+
\u2705 ContextStream search returns 0 results or errors
|
|
3002
|
+
\u2705 User explicitly requests local tools
|
|
3003
|
+
|
|
3004
|
+
4. FOR PLANS & TASKS: Use ContextStream, not file-based plans
|
|
3005
|
+
\u2705 Plans: mcp__contextstream__session(action="capture_plan", ...)
|
|
3006
|
+
\u2705 Tasks: mcp__contextstream__memory(action="create_task", ...)
|
|
3007
|
+
\u274C DO NOT use EnterPlanMode or write plans to markdown files
|
|
3008
|
+
|
|
3009
|
+
5. CHECK THESE from context() response:
|
|
3010
|
+
- Lessons: Past mistakes to avoid (shown as warnings)
|
|
3011
|
+
- Reminders: Active reminders for this project
|
|
3012
|
+
- Preferences: User's coding style and preferences
|
|
3013
|
+
- Rules: Dynamic rules matched to current task
|
|
3014
|
+
|
|
3015
|
+
6. SKIP CONTEXTSTREAM: If user preference says "skip contextstream", use local tools instead
|
|
2644
3016
|
[END]`;
|
|
3017
|
+
ENHANCED_REMINDER_HEADER = `[CONTEXTSTREAM - ENHANCED CONTEXT]
|
|
3018
|
+
|
|
3019
|
+
`;
|
|
2645
3020
|
isDirectRun3 = process.argv[1]?.includes("user-prompt-submit") || process.argv[2] === "user-prompt-submit";
|
|
2646
3021
|
if (isDirectRun3) {
|
|
2647
3022
|
runUserPromptSubmitHook().catch(() => process.exit(0));
|
|
@@ -2763,58 +3138,58 @@ var pre_compact_exports = {};
|
|
|
2763
3138
|
__export(pre_compact_exports, {
|
|
2764
3139
|
runPreCompactHook: () => runPreCompactHook
|
|
2765
3140
|
});
|
|
2766
|
-
import * as
|
|
2767
|
-
import * as
|
|
2768
|
-
import { homedir as
|
|
2769
|
-
function
|
|
2770
|
-
let searchDir =
|
|
3141
|
+
import * as fs11 from "node:fs";
|
|
3142
|
+
import * as path12 from "node:path";
|
|
3143
|
+
import { homedir as homedir9 } from "node:os";
|
|
3144
|
+
function loadConfigFromMcpJson2(cwd) {
|
|
3145
|
+
let searchDir = path12.resolve(cwd);
|
|
2771
3146
|
for (let i = 0; i < 5; i++) {
|
|
2772
|
-
if (!
|
|
2773
|
-
const mcpPath =
|
|
2774
|
-
if (
|
|
3147
|
+
if (!API_KEY3) {
|
|
3148
|
+
const mcpPath = path12.join(searchDir, ".mcp.json");
|
|
3149
|
+
if (fs11.existsSync(mcpPath)) {
|
|
2775
3150
|
try {
|
|
2776
|
-
const content =
|
|
3151
|
+
const content = fs11.readFileSync(mcpPath, "utf-8");
|
|
2777
3152
|
const config = JSON.parse(content);
|
|
2778
3153
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
2779
3154
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
2780
|
-
|
|
3155
|
+
API_KEY3 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
2781
3156
|
}
|
|
2782
3157
|
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
2783
|
-
|
|
3158
|
+
API_URL3 = csEnv.CONTEXTSTREAM_API_URL;
|
|
2784
3159
|
}
|
|
2785
3160
|
} catch {
|
|
2786
3161
|
}
|
|
2787
3162
|
}
|
|
2788
3163
|
}
|
|
2789
|
-
if (!
|
|
2790
|
-
const csConfigPath =
|
|
2791
|
-
if (
|
|
3164
|
+
if (!WORKSPACE_ID2) {
|
|
3165
|
+
const csConfigPath = path12.join(searchDir, ".contextstream", "config.json");
|
|
3166
|
+
if (fs11.existsSync(csConfigPath)) {
|
|
2792
3167
|
try {
|
|
2793
|
-
const content =
|
|
3168
|
+
const content = fs11.readFileSync(csConfigPath, "utf-8");
|
|
2794
3169
|
const csConfig = JSON.parse(content);
|
|
2795
3170
|
if (csConfig.workspace_id) {
|
|
2796
|
-
|
|
3171
|
+
WORKSPACE_ID2 = csConfig.workspace_id;
|
|
2797
3172
|
}
|
|
2798
3173
|
} catch {
|
|
2799
3174
|
}
|
|
2800
3175
|
}
|
|
2801
3176
|
}
|
|
2802
|
-
const parentDir =
|
|
3177
|
+
const parentDir = path12.dirname(searchDir);
|
|
2803
3178
|
if (parentDir === searchDir) break;
|
|
2804
3179
|
searchDir = parentDir;
|
|
2805
3180
|
}
|
|
2806
|
-
if (!
|
|
2807
|
-
const homeMcpPath =
|
|
2808
|
-
if (
|
|
3181
|
+
if (!API_KEY3) {
|
|
3182
|
+
const homeMcpPath = path12.join(homedir9(), ".mcp.json");
|
|
3183
|
+
if (fs11.existsSync(homeMcpPath)) {
|
|
2809
3184
|
try {
|
|
2810
|
-
const content =
|
|
3185
|
+
const content = fs11.readFileSync(homeMcpPath, "utf-8");
|
|
2811
3186
|
const config = JSON.parse(content);
|
|
2812
3187
|
const csEnv = config.mcpServers?.contextstream?.env;
|
|
2813
3188
|
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
2814
|
-
|
|
3189
|
+
API_KEY3 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
2815
3190
|
}
|
|
2816
3191
|
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
2817
|
-
|
|
3192
|
+
API_URL3 = csEnv.CONTEXTSTREAM_API_URL;
|
|
2818
3193
|
}
|
|
2819
3194
|
} catch {
|
|
2820
3195
|
}
|
|
@@ -2829,7 +3204,7 @@ function parseTranscript(transcriptPath) {
|
|
|
2829
3204
|
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2830
3205
|
let firstTimestamp = true;
|
|
2831
3206
|
try {
|
|
2832
|
-
const content =
|
|
3207
|
+
const content = fs11.readFileSync(transcriptPath, "utf-8");
|
|
2833
3208
|
const lines = content.split("\n");
|
|
2834
3209
|
for (const line of lines) {
|
|
2835
3210
|
if (!line.trim()) continue;
|
|
@@ -2910,7 +3285,7 @@ function parseTranscript(transcriptPath) {
|
|
|
2910
3285
|
};
|
|
2911
3286
|
}
|
|
2912
3287
|
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
2913
|
-
if (!
|
|
3288
|
+
if (!API_KEY3) {
|
|
2914
3289
|
return { success: false, message: "No API key configured" };
|
|
2915
3290
|
}
|
|
2916
3291
|
if (transcriptData.messages.length === 0) {
|
|
@@ -2929,17 +3304,17 @@ async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
|
2929
3304
|
},
|
|
2930
3305
|
tags: ["pre_compaction", trigger]
|
|
2931
3306
|
};
|
|
2932
|
-
if (
|
|
2933
|
-
payload.workspace_id =
|
|
3307
|
+
if (WORKSPACE_ID2) {
|
|
3308
|
+
payload.workspace_id = WORKSPACE_ID2;
|
|
2934
3309
|
}
|
|
2935
3310
|
try {
|
|
2936
3311
|
const controller = new AbortController();
|
|
2937
3312
|
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
2938
|
-
const response = await fetch(`${
|
|
3313
|
+
const response = await fetch(`${API_URL3}/api/v1/transcripts`, {
|
|
2939
3314
|
method: "POST",
|
|
2940
3315
|
headers: {
|
|
2941
3316
|
"Content-Type": "application/json",
|
|
2942
|
-
"X-API-Key":
|
|
3317
|
+
"X-API-Key": API_KEY3
|
|
2943
3318
|
},
|
|
2944
3319
|
body: JSON.stringify(payload),
|
|
2945
3320
|
signal: controller.signal
|
|
@@ -2954,7 +3329,7 @@ async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
|
2954
3329
|
}
|
|
2955
3330
|
}
|
|
2956
3331
|
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
2957
|
-
if (!
|
|
3332
|
+
if (!API_KEY3) {
|
|
2958
3333
|
return { success: false, message: "No API key configured" };
|
|
2959
3334
|
}
|
|
2960
3335
|
const snapshotContent = {
|
|
@@ -2975,17 +3350,17 @@ async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
|
2975
3350
|
tags: ["session_snapshot", "pre_compaction", "auto_captured"],
|
|
2976
3351
|
source_type: "hook"
|
|
2977
3352
|
};
|
|
2978
|
-
if (
|
|
2979
|
-
payload.workspace_id =
|
|
3353
|
+
if (WORKSPACE_ID2) {
|
|
3354
|
+
payload.workspace_id = WORKSPACE_ID2;
|
|
2980
3355
|
}
|
|
2981
3356
|
try {
|
|
2982
3357
|
const controller = new AbortController();
|
|
2983
3358
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2984
|
-
const response = await fetch(`${
|
|
3359
|
+
const response = await fetch(`${API_URL3}/api/v1/memory/events`, {
|
|
2985
3360
|
method: "POST",
|
|
2986
3361
|
headers: {
|
|
2987
3362
|
"Content-Type": "application/json",
|
|
2988
|
-
"X-API-Key":
|
|
3363
|
+
"X-API-Key": API_KEY3
|
|
2989
3364
|
},
|
|
2990
3365
|
body: JSON.stringify(payload),
|
|
2991
3366
|
signal: controller.signal
|
|
@@ -3017,7 +3392,7 @@ async function runPreCompactHook() {
|
|
|
3017
3392
|
process.exit(0);
|
|
3018
3393
|
}
|
|
3019
3394
|
const cwd = input.cwd || process.cwd();
|
|
3020
|
-
|
|
3395
|
+
loadConfigFromMcpJson2(cwd);
|
|
3021
3396
|
const sessionId = input.session_id || "unknown";
|
|
3022
3397
|
const transcriptPath = input.transcript_path || "";
|
|
3023
3398
|
const trigger = input.trigger || "unknown";
|
|
@@ -3026,13 +3401,15 @@ async function runPreCompactHook() {
|
|
|
3026
3401
|
activeFiles: [],
|
|
3027
3402
|
toolCallCount: 0,
|
|
3028
3403
|
messageCount: 0,
|
|
3029
|
-
lastTools: []
|
|
3404
|
+
lastTools: [],
|
|
3405
|
+
messages: [],
|
|
3406
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3030
3407
|
};
|
|
3031
|
-
if (transcriptPath &&
|
|
3408
|
+
if (transcriptPath && fs11.existsSync(transcriptPath)) {
|
|
3032
3409
|
transcriptData = parseTranscript(transcriptPath);
|
|
3033
3410
|
}
|
|
3034
3411
|
let autoSaveStatus = "";
|
|
3035
|
-
if (AUTO_SAVE &&
|
|
3412
|
+
if (AUTO_SAVE && API_KEY3) {
|
|
3036
3413
|
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
3037
3414
|
if (transcriptResult.success) {
|
|
3038
3415
|
autoSaveStatus = `
|
|
@@ -3066,15 +3443,15 @@ User instructions: ${customInstructions}` : ""}`;
|
|
|
3066
3443
|
);
|
|
3067
3444
|
process.exit(0);
|
|
3068
3445
|
}
|
|
3069
|
-
var ENABLED5, AUTO_SAVE,
|
|
3446
|
+
var ENABLED5, AUTO_SAVE, API_URL3, API_KEY3, WORKSPACE_ID2, isDirectRun5;
|
|
3070
3447
|
var init_pre_compact = __esm({
|
|
3071
3448
|
"src/hooks/pre-compact.ts"() {
|
|
3072
3449
|
"use strict";
|
|
3073
3450
|
ENABLED5 = process.env.CONTEXTSTREAM_PRECOMPACT_ENABLED !== "false";
|
|
3074
3451
|
AUTO_SAVE = process.env.CONTEXTSTREAM_PRECOMPACT_AUTO_SAVE !== "false";
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3452
|
+
API_URL3 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3453
|
+
API_KEY3 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3454
|
+
WORKSPACE_ID2 = null;
|
|
3078
3455
|
isDirectRun5 = process.argv[1]?.includes("pre-compact") || process.argv[2] === "pre-compact";
|
|
3079
3456
|
if (isDirectRun5) {
|
|
3080
3457
|
runPreCompactHook().catch(() => process.exit(0));
|
|
@@ -3087,13 +3464,13 @@ var auto_rules_exports = {};
|
|
|
3087
3464
|
__export(auto_rules_exports, {
|
|
3088
3465
|
runAutoRulesHook: () => runAutoRulesHook
|
|
3089
3466
|
});
|
|
3090
|
-
import * as
|
|
3091
|
-
import * as
|
|
3092
|
-
import { homedir as
|
|
3467
|
+
import * as fs12 from "node:fs";
|
|
3468
|
+
import * as path13 from "node:path";
|
|
3469
|
+
import { homedir as homedir10 } from "node:os";
|
|
3093
3470
|
function hasRunRecently() {
|
|
3094
3471
|
try {
|
|
3095
|
-
if (!
|
|
3096
|
-
const stat2 =
|
|
3472
|
+
if (!fs12.existsSync(MARKER_FILE)) return false;
|
|
3473
|
+
const stat2 = fs12.statSync(MARKER_FILE);
|
|
3097
3474
|
const age = Date.now() - stat2.mtimeMs;
|
|
3098
3475
|
return age < COOLDOWN_MS;
|
|
3099
3476
|
} catch {
|
|
@@ -3102,11 +3479,11 @@ function hasRunRecently() {
|
|
|
3102
3479
|
}
|
|
3103
3480
|
function markAsRan() {
|
|
3104
3481
|
try {
|
|
3105
|
-
const dir =
|
|
3106
|
-
if (!
|
|
3107
|
-
|
|
3482
|
+
const dir = path13.dirname(MARKER_FILE);
|
|
3483
|
+
if (!fs12.existsSync(dir)) {
|
|
3484
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
3108
3485
|
}
|
|
3109
|
-
|
|
3486
|
+
fs12.writeFileSync(MARKER_FILE, (/* @__PURE__ */ new Date()).toISOString());
|
|
3110
3487
|
} catch {
|
|
3111
3488
|
}
|
|
3112
3489
|
}
|
|
@@ -3135,8 +3512,8 @@ function extractCwd3(input) {
|
|
|
3135
3512
|
}
|
|
3136
3513
|
function hasPythonHooks(settingsPath) {
|
|
3137
3514
|
try {
|
|
3138
|
-
if (!
|
|
3139
|
-
const content =
|
|
3515
|
+
if (!fs12.existsSync(settingsPath)) return false;
|
|
3516
|
+
const content = fs12.readFileSync(settingsPath, "utf-8");
|
|
3140
3517
|
const settings = JSON.parse(content);
|
|
3141
3518
|
const hooks = settings.hooks;
|
|
3142
3519
|
if (!hooks) return false;
|
|
@@ -3160,8 +3537,8 @@ function hasPythonHooks(settingsPath) {
|
|
|
3160
3537
|
}
|
|
3161
3538
|
}
|
|
3162
3539
|
function detectPythonHooks(cwd) {
|
|
3163
|
-
const globalSettingsPath =
|
|
3164
|
-
const projectSettingsPath =
|
|
3540
|
+
const globalSettingsPath = path13.join(homedir10(), ".claude", "settings.json");
|
|
3541
|
+
const projectSettingsPath = path13.join(cwd, ".claude", "settings.json");
|
|
3165
3542
|
return {
|
|
3166
3543
|
global: hasPythonHooks(globalSettingsPath),
|
|
3167
3544
|
project: hasPythonHooks(projectSettingsPath)
|
|
@@ -3182,7 +3559,1297 @@ async function runAutoRulesHook() {
|
|
|
3182
3559
|
if (!ENABLED6) {
|
|
3183
3560
|
process.exit(0);
|
|
3184
3561
|
}
|
|
3185
|
-
if (hasRunRecently()) {
|
|
3562
|
+
if (hasRunRecently()) {
|
|
3563
|
+
process.exit(0);
|
|
3564
|
+
}
|
|
3565
|
+
let inputData = "";
|
|
3566
|
+
for await (const chunk of process.stdin) {
|
|
3567
|
+
inputData += chunk;
|
|
3568
|
+
}
|
|
3569
|
+
if (!inputData.trim()) {
|
|
3570
|
+
process.exit(0);
|
|
3571
|
+
}
|
|
3572
|
+
let input;
|
|
3573
|
+
try {
|
|
3574
|
+
input = JSON.parse(inputData);
|
|
3575
|
+
} catch {
|
|
3576
|
+
process.exit(0);
|
|
3577
|
+
}
|
|
3578
|
+
const toolName = input.tool_name || input.toolName || "";
|
|
3579
|
+
const isContextTool = toolName.includes("init") || toolName.includes("context") || toolName.includes("session_init") || toolName.includes("context_smart");
|
|
3580
|
+
if (!isContextTool) {
|
|
3581
|
+
process.exit(0);
|
|
3582
|
+
}
|
|
3583
|
+
const cwd = extractCwd3(input);
|
|
3584
|
+
const pythonHooks = detectPythonHooks(cwd);
|
|
3585
|
+
const hasPythonHooksToUpgrade = pythonHooks.global || pythonHooks.project;
|
|
3586
|
+
const rulesNotice = extractRulesNotice(input);
|
|
3587
|
+
const rulesNeedUpdate = rulesNotice && rulesNotice.status !== "current";
|
|
3588
|
+
if (!hasPythonHooksToUpgrade && !rulesNeedUpdate) {
|
|
3589
|
+
process.exit(0);
|
|
3590
|
+
}
|
|
3591
|
+
const folderPath = rulesNotice?.update_args?.folder_path || cwd;
|
|
3592
|
+
try {
|
|
3593
|
+
await upgradeHooksForFolder(folderPath);
|
|
3594
|
+
markAsRan();
|
|
3595
|
+
} catch {
|
|
3596
|
+
}
|
|
3597
|
+
process.exit(0);
|
|
3598
|
+
}
|
|
3599
|
+
var API_URL4, API_KEY4, ENABLED6, MARKER_FILE, COOLDOWN_MS, isDirectRun6;
|
|
3600
|
+
var init_auto_rules = __esm({
|
|
3601
|
+
"src/hooks/auto-rules.ts"() {
|
|
3602
|
+
"use strict";
|
|
3603
|
+
API_URL4 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3604
|
+
API_KEY4 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3605
|
+
ENABLED6 = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
|
|
3606
|
+
MARKER_FILE = path13.join(homedir10(), ".contextstream", ".auto-rules-ran");
|
|
3607
|
+
COOLDOWN_MS = 4 * 60 * 60 * 1e3;
|
|
3608
|
+
isDirectRun6 = process.argv[1]?.includes("auto-rules") || process.argv[2] === "auto-rules";
|
|
3609
|
+
if (isDirectRun6) {
|
|
3610
|
+
runAutoRulesHook().catch(() => process.exit(0));
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
});
|
|
3614
|
+
|
|
3615
|
+
// src/hooks/post-compact.ts
|
|
3616
|
+
var post_compact_exports = {};
|
|
3617
|
+
__export(post_compact_exports, {
|
|
3618
|
+
runPostCompactHook: () => runPostCompactHook
|
|
3619
|
+
});
|
|
3620
|
+
import * as fs13 from "node:fs";
|
|
3621
|
+
import * as path14 from "node:path";
|
|
3622
|
+
import { homedir as homedir11 } from "node:os";
|
|
3623
|
+
function loadConfigFromMcpJson3(cwd) {
|
|
3624
|
+
let searchDir = path14.resolve(cwd);
|
|
3625
|
+
for (let i = 0; i < 5; i++) {
|
|
3626
|
+
if (!API_KEY5) {
|
|
3627
|
+
const mcpPath = path14.join(searchDir, ".mcp.json");
|
|
3628
|
+
if (fs13.existsSync(mcpPath)) {
|
|
3629
|
+
try {
|
|
3630
|
+
const content = fs13.readFileSync(mcpPath, "utf-8");
|
|
3631
|
+
const config = JSON.parse(content);
|
|
3632
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3633
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3634
|
+
API_KEY5 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3635
|
+
}
|
|
3636
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3637
|
+
API_URL5 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3638
|
+
}
|
|
3639
|
+
} catch {
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
if (!WORKSPACE_ID3) {
|
|
3644
|
+
const csConfigPath = path14.join(searchDir, ".contextstream", "config.json");
|
|
3645
|
+
if (fs13.existsSync(csConfigPath)) {
|
|
3646
|
+
try {
|
|
3647
|
+
const content = fs13.readFileSync(csConfigPath, "utf-8");
|
|
3648
|
+
const csConfig = JSON.parse(content);
|
|
3649
|
+
if (csConfig.workspace_id) {
|
|
3650
|
+
WORKSPACE_ID3 = csConfig.workspace_id;
|
|
3651
|
+
}
|
|
3652
|
+
} catch {
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
const parentDir = path14.dirname(searchDir);
|
|
3657
|
+
if (parentDir === searchDir) break;
|
|
3658
|
+
searchDir = parentDir;
|
|
3659
|
+
}
|
|
3660
|
+
if (!API_KEY5) {
|
|
3661
|
+
const homeMcpPath = path14.join(homedir11(), ".mcp.json");
|
|
3662
|
+
if (fs13.existsSync(homeMcpPath)) {
|
|
3663
|
+
try {
|
|
3664
|
+
const content = fs13.readFileSync(homeMcpPath, "utf-8");
|
|
3665
|
+
const config = JSON.parse(content);
|
|
3666
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3667
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3668
|
+
API_KEY5 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3669
|
+
}
|
|
3670
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3671
|
+
API_URL5 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3672
|
+
}
|
|
3673
|
+
} catch {
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
async function fetchLastTranscript(sessionId) {
|
|
3679
|
+
if (!API_KEY5) {
|
|
3680
|
+
return null;
|
|
3681
|
+
}
|
|
3682
|
+
try {
|
|
3683
|
+
const controller = new AbortController();
|
|
3684
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
3685
|
+
const url = new URL(`${API_URL5}/api/v1/transcripts`);
|
|
3686
|
+
url.searchParams.set("session_id", sessionId);
|
|
3687
|
+
url.searchParams.set("limit", "1");
|
|
3688
|
+
url.searchParams.set("sort", "created_at:desc");
|
|
3689
|
+
if (WORKSPACE_ID3) {
|
|
3690
|
+
url.searchParams.set("workspace_id", WORKSPACE_ID3);
|
|
3691
|
+
}
|
|
3692
|
+
const response = await fetch(url.toString(), {
|
|
3693
|
+
method: "GET",
|
|
3694
|
+
headers: {
|
|
3695
|
+
"X-API-Key": API_KEY5
|
|
3696
|
+
},
|
|
3697
|
+
signal: controller.signal
|
|
3698
|
+
});
|
|
3699
|
+
clearTimeout(timeoutId);
|
|
3700
|
+
if (response.ok) {
|
|
3701
|
+
const data = await response.json();
|
|
3702
|
+
if (data.transcripts && data.transcripts.length > 0) {
|
|
3703
|
+
return data.transcripts[0];
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
return null;
|
|
3707
|
+
} catch {
|
|
3708
|
+
return null;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
function formatTranscriptSummary(transcript) {
|
|
3712
|
+
const messages = transcript.messages || [];
|
|
3713
|
+
const activeFiles = transcript.metadata?.active_files || [];
|
|
3714
|
+
const toolCallCount = transcript.metadata?.tool_call_count || 0;
|
|
3715
|
+
const userMessages = messages.filter((m) => m.role === "user").slice(-3).map((m) => `- "${m.content.slice(0, 100)}${m.content.length > 100 ? "..." : ""}"`).join("\n");
|
|
3716
|
+
const lastAssistant = messages.filter((m) => m.role === "assistant" && !m.content.startsWith("[Tool:")).slice(-1)[0];
|
|
3717
|
+
const lastWork = lastAssistant ? lastAssistant.content.slice(0, 300) + (lastAssistant.content.length > 300 ? "..." : "") : "None recorded";
|
|
3718
|
+
return `## Pre-Compaction State Restored
|
|
3719
|
+
|
|
3720
|
+
### Active Files (${activeFiles.length})
|
|
3721
|
+
${activeFiles.slice(0, 10).map((f) => `- ${f}`).join("\n") || "None tracked"}
|
|
3722
|
+
|
|
3723
|
+
### Recent User Requests
|
|
3724
|
+
${userMessages || "None recorded"}
|
|
3725
|
+
|
|
3726
|
+
### Last Work in Progress
|
|
3727
|
+
${lastWork}
|
|
3728
|
+
|
|
3729
|
+
### Session Stats
|
|
3730
|
+
- Tool calls: ${toolCallCount}
|
|
3731
|
+
- Messages: ${messages.length}
|
|
3732
|
+
- Saved at: ${transcript.created_at}`;
|
|
3733
|
+
}
|
|
3734
|
+
async function runPostCompactHook() {
|
|
3735
|
+
if (!ENABLED7) {
|
|
3736
|
+
process.exit(0);
|
|
3737
|
+
}
|
|
3738
|
+
let inputData = "";
|
|
3739
|
+
for await (const chunk of process.stdin) {
|
|
3740
|
+
inputData += chunk;
|
|
3741
|
+
}
|
|
3742
|
+
if (!inputData.trim()) {
|
|
3743
|
+
process.exit(0);
|
|
3744
|
+
}
|
|
3745
|
+
let input;
|
|
3746
|
+
try {
|
|
3747
|
+
input = JSON.parse(inputData);
|
|
3748
|
+
} catch {
|
|
3749
|
+
process.exit(0);
|
|
3750
|
+
}
|
|
3751
|
+
const cwd = input.cwd || process.cwd();
|
|
3752
|
+
loadConfigFromMcpJson3(cwd);
|
|
3753
|
+
const sessionId = input.session_id || "";
|
|
3754
|
+
let restoredContext = "";
|
|
3755
|
+
if (sessionId && API_KEY5) {
|
|
3756
|
+
const transcript = await fetchLastTranscript(sessionId);
|
|
3757
|
+
if (transcript) {
|
|
3758
|
+
restoredContext = formatTranscriptSummary(transcript);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
const context = `[POST-COMPACTION - Context Restored]
|
|
3762
|
+
|
|
3763
|
+
${restoredContext || "No saved state found. Starting fresh."}
|
|
3764
|
+
|
|
3765
|
+
**IMPORTANT:** Call \`mcp__contextstream__context(user_message="resuming after compaction")\` to get full context and any pending tasks.
|
|
3766
|
+
|
|
3767
|
+
The conversation was compacted to save memory. The above summary was automatically restored from ContextStream.`;
|
|
3768
|
+
console.log(
|
|
3769
|
+
JSON.stringify({
|
|
3770
|
+
hookSpecificOutput: {
|
|
3771
|
+
hookEventName: "PostCompact",
|
|
3772
|
+
additionalContext: context
|
|
3773
|
+
}
|
|
3774
|
+
})
|
|
3775
|
+
);
|
|
3776
|
+
process.exit(0);
|
|
3777
|
+
}
|
|
3778
|
+
var ENABLED7, API_URL5, API_KEY5, WORKSPACE_ID3, isDirectRun7;
|
|
3779
|
+
var init_post_compact = __esm({
|
|
3780
|
+
"src/hooks/post-compact.ts"() {
|
|
3781
|
+
"use strict";
|
|
3782
|
+
ENABLED7 = process.env.CONTEXTSTREAM_POSTCOMPACT_ENABLED !== "false";
|
|
3783
|
+
API_URL5 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3784
|
+
API_KEY5 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3785
|
+
WORKSPACE_ID3 = null;
|
|
3786
|
+
isDirectRun7 = process.argv[1]?.includes("post-compact") || process.argv[2] === "post-compact";
|
|
3787
|
+
if (isDirectRun7) {
|
|
3788
|
+
runPostCompactHook().catch(() => process.exit(0));
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
});
|
|
3792
|
+
|
|
3793
|
+
// src/hooks/on-bash.ts
|
|
3794
|
+
var on_bash_exports = {};
|
|
3795
|
+
__export(on_bash_exports, {
|
|
3796
|
+
runOnBashHook: () => runOnBashHook
|
|
3797
|
+
});
|
|
3798
|
+
import * as fs14 from "node:fs";
|
|
3799
|
+
import * as path15 from "node:path";
|
|
3800
|
+
import { homedir as homedir12 } from "node:os";
|
|
3801
|
+
function loadConfigFromMcpJson4(cwd) {
|
|
3802
|
+
let searchDir = path15.resolve(cwd);
|
|
3803
|
+
for (let i = 0; i < 5; i++) {
|
|
3804
|
+
if (!API_KEY6) {
|
|
3805
|
+
const mcpPath = path15.join(searchDir, ".mcp.json");
|
|
3806
|
+
if (fs14.existsSync(mcpPath)) {
|
|
3807
|
+
try {
|
|
3808
|
+
const content = fs14.readFileSync(mcpPath, "utf-8");
|
|
3809
|
+
const config = JSON.parse(content);
|
|
3810
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3811
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3812
|
+
API_KEY6 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3813
|
+
}
|
|
3814
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3815
|
+
API_URL6 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3816
|
+
}
|
|
3817
|
+
} catch {
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
if (!WORKSPACE_ID4) {
|
|
3822
|
+
const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
|
|
3823
|
+
if (fs14.existsSync(csConfigPath)) {
|
|
3824
|
+
try {
|
|
3825
|
+
const content = fs14.readFileSync(csConfigPath, "utf-8");
|
|
3826
|
+
const csConfig = JSON.parse(content);
|
|
3827
|
+
if (csConfig.workspace_id) {
|
|
3828
|
+
WORKSPACE_ID4 = csConfig.workspace_id;
|
|
3829
|
+
}
|
|
3830
|
+
} catch {
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
const parentDir = path15.dirname(searchDir);
|
|
3835
|
+
if (parentDir === searchDir) break;
|
|
3836
|
+
searchDir = parentDir;
|
|
3837
|
+
}
|
|
3838
|
+
if (!API_KEY6) {
|
|
3839
|
+
const homeMcpPath = path15.join(homedir12(), ".mcp.json");
|
|
3840
|
+
if (fs14.existsSync(homeMcpPath)) {
|
|
3841
|
+
try {
|
|
3842
|
+
const content = fs14.readFileSync(homeMcpPath, "utf-8");
|
|
3843
|
+
const config = JSON.parse(content);
|
|
3844
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3845
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3846
|
+
API_KEY6 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3847
|
+
}
|
|
3848
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3849
|
+
API_URL6 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3850
|
+
}
|
|
3851
|
+
} catch {
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
async function captureCommand(command, output, exitCode, isError, sessionId) {
|
|
3857
|
+
if (!API_KEY6) return;
|
|
3858
|
+
const payload = {
|
|
3859
|
+
event_type: isError ? "bash_error" : "bash_command",
|
|
3860
|
+
title: isError ? `Bash Error: ${command.slice(0, 50)}...` : `Command: ${command.slice(0, 50)}...`,
|
|
3861
|
+
content: JSON.stringify({
|
|
3862
|
+
command,
|
|
3863
|
+
output: output.slice(0, 2e3),
|
|
3864
|
+
exit_code: exitCode,
|
|
3865
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3866
|
+
}),
|
|
3867
|
+
importance: isError ? "high" : "low",
|
|
3868
|
+
tags: isError ? ["bash", "error", "command"] : ["bash", "command"],
|
|
3869
|
+
source_type: "hook",
|
|
3870
|
+
session_id: sessionId
|
|
3871
|
+
};
|
|
3872
|
+
if (WORKSPACE_ID4) {
|
|
3873
|
+
payload.workspace_id = WORKSPACE_ID4;
|
|
3874
|
+
}
|
|
3875
|
+
try {
|
|
3876
|
+
const controller = new AbortController();
|
|
3877
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
3878
|
+
await fetch(`${API_URL6}/api/v1/memory/events`, {
|
|
3879
|
+
method: "POST",
|
|
3880
|
+
headers: {
|
|
3881
|
+
"Content-Type": "application/json",
|
|
3882
|
+
"X-API-Key": API_KEY6
|
|
3883
|
+
},
|
|
3884
|
+
body: JSON.stringify(payload),
|
|
3885
|
+
signal: controller.signal
|
|
3886
|
+
});
|
|
3887
|
+
clearTimeout(timeoutId);
|
|
3888
|
+
} catch {
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
async function suggestLesson(command, error) {
|
|
3892
|
+
const errorPatterns = [
|
|
3893
|
+
{
|
|
3894
|
+
pattern: /command not found/i,
|
|
3895
|
+
lesson: `The command "${command.split(" ")[0]}" is not installed. Check if the package needs to be installed first.`
|
|
3896
|
+
},
|
|
3897
|
+
{
|
|
3898
|
+
pattern: /permission denied/i,
|
|
3899
|
+
lesson: "Permission denied. May need sudo or to check file permissions."
|
|
3900
|
+
},
|
|
3901
|
+
{
|
|
3902
|
+
pattern: /no such file or directory/i,
|
|
3903
|
+
lesson: "Path does not exist. Verify the file/directory path before running commands."
|
|
3904
|
+
},
|
|
3905
|
+
{
|
|
3906
|
+
pattern: /EADDRINUSE|address already in use/i,
|
|
3907
|
+
lesson: "Port is already in use. Kill the existing process or use a different port."
|
|
3908
|
+
},
|
|
3909
|
+
{
|
|
3910
|
+
pattern: /npm ERR!|ERESOLVE/i,
|
|
3911
|
+
lesson: "npm dependency conflict. Try `npm install --legacy-peer-deps` or check package versions."
|
|
3912
|
+
},
|
|
3913
|
+
{
|
|
3914
|
+
pattern: /ENOENT.*package\.json/i,
|
|
3915
|
+
lesson: "No package.json found. Make sure you're in the right directory or run `npm init`."
|
|
3916
|
+
},
|
|
3917
|
+
{
|
|
3918
|
+
pattern: /git.*not a git repository/i,
|
|
3919
|
+
lesson: "Not in a git repository. Run `git init` or navigate to a git repo."
|
|
3920
|
+
}
|
|
3921
|
+
];
|
|
3922
|
+
for (const { pattern, lesson } of errorPatterns) {
|
|
3923
|
+
if (pattern.test(error)) {
|
|
3924
|
+
return lesson;
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
return null;
|
|
3928
|
+
}
|
|
3929
|
+
async function runOnBashHook() {
|
|
3930
|
+
if (!ENABLED8) {
|
|
3931
|
+
process.exit(0);
|
|
3932
|
+
}
|
|
3933
|
+
let inputData = "";
|
|
3934
|
+
for await (const chunk of process.stdin) {
|
|
3935
|
+
inputData += chunk;
|
|
3936
|
+
}
|
|
3937
|
+
if (!inputData.trim()) {
|
|
3938
|
+
process.exit(0);
|
|
3939
|
+
}
|
|
3940
|
+
let input;
|
|
3941
|
+
try {
|
|
3942
|
+
input = JSON.parse(inputData);
|
|
3943
|
+
} catch {
|
|
3944
|
+
process.exit(0);
|
|
3945
|
+
}
|
|
3946
|
+
if (input.tool_name !== "Bash") {
|
|
3947
|
+
process.exit(0);
|
|
3948
|
+
}
|
|
3949
|
+
const cwd = input.cwd || process.cwd();
|
|
3950
|
+
loadConfigFromMcpJson4(cwd);
|
|
3951
|
+
const command = input.tool_input?.command || "";
|
|
3952
|
+
const output = input.tool_result?.output || input.tool_result?.error || "";
|
|
3953
|
+
const exitCode = input.tool_result?.exit_code ?? 0;
|
|
3954
|
+
const sessionId = input.session_id || "unknown";
|
|
3955
|
+
const isError = exitCode !== 0 || !!input.tool_result?.error;
|
|
3956
|
+
captureCommand(command, output, exitCode, isError, sessionId).catch(() => {
|
|
3957
|
+
});
|
|
3958
|
+
if (isError) {
|
|
3959
|
+
const lesson = await suggestLesson(command, output);
|
|
3960
|
+
if (lesson) {
|
|
3961
|
+
console.log(
|
|
3962
|
+
JSON.stringify({
|
|
3963
|
+
hookSpecificOutput: {
|
|
3964
|
+
hookEventName: "PostToolUse",
|
|
3965
|
+
additionalContext: `[ContextStream Insight] ${lesson}`
|
|
3966
|
+
}
|
|
3967
|
+
})
|
|
3968
|
+
);
|
|
3969
|
+
process.exit(0);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
process.exit(0);
|
|
3973
|
+
}
|
|
3974
|
+
var ENABLED8, API_URL6, API_KEY6, WORKSPACE_ID4, isDirectRun8;
|
|
3975
|
+
var init_on_bash = __esm({
|
|
3976
|
+
"src/hooks/on-bash.ts"() {
|
|
3977
|
+
"use strict";
|
|
3978
|
+
ENABLED8 = process.env.CONTEXTSTREAM_BASH_HOOK_ENABLED !== "false";
|
|
3979
|
+
API_URL6 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3980
|
+
API_KEY6 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3981
|
+
WORKSPACE_ID4 = null;
|
|
3982
|
+
isDirectRun8 = process.argv[1]?.includes("on-bash") || process.argv[2] === "on-bash";
|
|
3983
|
+
if (isDirectRun8) {
|
|
3984
|
+
runOnBashHook().catch(() => process.exit(0));
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
});
|
|
3988
|
+
|
|
3989
|
+
// src/hooks/on-task.ts
|
|
3990
|
+
var on_task_exports = {};
|
|
3991
|
+
__export(on_task_exports, {
|
|
3992
|
+
runOnTaskHook: () => runOnTaskHook
|
|
3993
|
+
});
|
|
3994
|
+
import * as fs15 from "node:fs";
|
|
3995
|
+
import * as path16 from "node:path";
|
|
3996
|
+
import { homedir as homedir13 } from "node:os";
|
|
3997
|
+
function loadConfigFromMcpJson5(cwd) {
|
|
3998
|
+
let searchDir = path16.resolve(cwd);
|
|
3999
|
+
for (let i = 0; i < 5; i++) {
|
|
4000
|
+
if (!API_KEY7) {
|
|
4001
|
+
const mcpPath = path16.join(searchDir, ".mcp.json");
|
|
4002
|
+
if (fs15.existsSync(mcpPath)) {
|
|
4003
|
+
try {
|
|
4004
|
+
const content = fs15.readFileSync(mcpPath, "utf-8");
|
|
4005
|
+
const config = JSON.parse(content);
|
|
4006
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4007
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4008
|
+
API_KEY7 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4009
|
+
}
|
|
4010
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4011
|
+
API_URL7 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4012
|
+
}
|
|
4013
|
+
} catch {
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
if (!WORKSPACE_ID5) {
|
|
4018
|
+
const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
|
|
4019
|
+
if (fs15.existsSync(csConfigPath)) {
|
|
4020
|
+
try {
|
|
4021
|
+
const content = fs15.readFileSync(csConfigPath, "utf-8");
|
|
4022
|
+
const csConfig = JSON.parse(content);
|
|
4023
|
+
if (csConfig.workspace_id) {
|
|
4024
|
+
WORKSPACE_ID5 = csConfig.workspace_id;
|
|
4025
|
+
}
|
|
4026
|
+
} catch {
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
const parentDir = path16.dirname(searchDir);
|
|
4031
|
+
if (parentDir === searchDir) break;
|
|
4032
|
+
searchDir = parentDir;
|
|
4033
|
+
}
|
|
4034
|
+
if (!API_KEY7) {
|
|
4035
|
+
const homeMcpPath = path16.join(homedir13(), ".mcp.json");
|
|
4036
|
+
if (fs15.existsSync(homeMcpPath)) {
|
|
4037
|
+
try {
|
|
4038
|
+
const content = fs15.readFileSync(homeMcpPath, "utf-8");
|
|
4039
|
+
const config = JSON.parse(content);
|
|
4040
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4041
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4042
|
+
API_KEY7 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4043
|
+
}
|
|
4044
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4045
|
+
API_URL7 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4046
|
+
}
|
|
4047
|
+
} catch {
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
async function captureTaskInvocation(description, prompt, agentType, result, sessionId) {
|
|
4053
|
+
if (!API_KEY7) return;
|
|
4054
|
+
const payload = {
|
|
4055
|
+
event_type: "task_agent",
|
|
4056
|
+
title: `Agent: ${agentType} - ${description}`,
|
|
4057
|
+
content: JSON.stringify({
|
|
4058
|
+
description,
|
|
4059
|
+
prompt: prompt.slice(0, 1e3),
|
|
4060
|
+
agent_type: agentType,
|
|
4061
|
+
result: result.slice(0, 2e3),
|
|
4062
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4063
|
+
}),
|
|
4064
|
+
importance: "medium",
|
|
4065
|
+
tags: ["task", "agent", agentType.toLowerCase()],
|
|
4066
|
+
source_type: "hook",
|
|
4067
|
+
session_id: sessionId
|
|
4068
|
+
};
|
|
4069
|
+
if (WORKSPACE_ID5) {
|
|
4070
|
+
payload.workspace_id = WORKSPACE_ID5;
|
|
4071
|
+
}
|
|
4072
|
+
try {
|
|
4073
|
+
const controller = new AbortController();
|
|
4074
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
4075
|
+
await fetch(`${API_URL7}/api/v1/memory/events`, {
|
|
4076
|
+
method: "POST",
|
|
4077
|
+
headers: {
|
|
4078
|
+
"Content-Type": "application/json",
|
|
4079
|
+
"X-API-Key": API_KEY7
|
|
4080
|
+
},
|
|
4081
|
+
body: JSON.stringify(payload),
|
|
4082
|
+
signal: controller.signal
|
|
4083
|
+
});
|
|
4084
|
+
clearTimeout(timeoutId);
|
|
4085
|
+
} catch {
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
async function runOnTaskHook() {
|
|
4089
|
+
if (!ENABLED9) {
|
|
4090
|
+
process.exit(0);
|
|
4091
|
+
}
|
|
4092
|
+
let inputData = "";
|
|
4093
|
+
for await (const chunk of process.stdin) {
|
|
4094
|
+
inputData += chunk;
|
|
4095
|
+
}
|
|
4096
|
+
if (!inputData.trim()) {
|
|
4097
|
+
process.exit(0);
|
|
4098
|
+
}
|
|
4099
|
+
let input;
|
|
4100
|
+
try {
|
|
4101
|
+
input = JSON.parse(inputData);
|
|
4102
|
+
} catch {
|
|
4103
|
+
process.exit(0);
|
|
4104
|
+
}
|
|
4105
|
+
if (input.tool_name !== "Task") {
|
|
4106
|
+
process.exit(0);
|
|
4107
|
+
}
|
|
4108
|
+
const cwd = input.cwd || process.cwd();
|
|
4109
|
+
loadConfigFromMcpJson5(cwd);
|
|
4110
|
+
const description = input.tool_input?.description || "Unknown task";
|
|
4111
|
+
const prompt = input.tool_input?.prompt || "";
|
|
4112
|
+
const agentType = input.tool_input?.subagent_type || "general-purpose";
|
|
4113
|
+
const result = input.tool_result?.output || "";
|
|
4114
|
+
const sessionId = input.session_id || "unknown";
|
|
4115
|
+
captureTaskInvocation(description, prompt, agentType, result, sessionId).catch(() => {
|
|
4116
|
+
});
|
|
4117
|
+
process.exit(0);
|
|
4118
|
+
}
|
|
4119
|
+
var ENABLED9, API_URL7, API_KEY7, WORKSPACE_ID5, isDirectRun9;
|
|
4120
|
+
var init_on_task = __esm({
|
|
4121
|
+
"src/hooks/on-task.ts"() {
|
|
4122
|
+
"use strict";
|
|
4123
|
+
ENABLED9 = process.env.CONTEXTSTREAM_TASK_HOOK_ENABLED !== "false";
|
|
4124
|
+
API_URL7 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4125
|
+
API_KEY7 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4126
|
+
WORKSPACE_ID5 = null;
|
|
4127
|
+
isDirectRun9 = process.argv[1]?.includes("on-task") || process.argv[2] === "on-task";
|
|
4128
|
+
if (isDirectRun9) {
|
|
4129
|
+
runOnTaskHook().catch(() => process.exit(0));
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
});
|
|
4133
|
+
|
|
4134
|
+
// src/hooks/on-read.ts
|
|
4135
|
+
var on_read_exports = {};
|
|
4136
|
+
__export(on_read_exports, {
|
|
4137
|
+
runOnReadHook: () => runOnReadHook
|
|
4138
|
+
});
|
|
4139
|
+
import * as fs16 from "node:fs";
|
|
4140
|
+
import * as path17 from "node:path";
|
|
4141
|
+
import { homedir as homedir14 } from "node:os";
|
|
4142
|
+
function loadConfigFromMcpJson6(cwd) {
|
|
4143
|
+
let searchDir = path17.resolve(cwd);
|
|
4144
|
+
for (let i = 0; i < 5; i++) {
|
|
4145
|
+
if (!API_KEY8) {
|
|
4146
|
+
const mcpPath = path17.join(searchDir, ".mcp.json");
|
|
4147
|
+
if (fs16.existsSync(mcpPath)) {
|
|
4148
|
+
try {
|
|
4149
|
+
const content = fs16.readFileSync(mcpPath, "utf-8");
|
|
4150
|
+
const config = JSON.parse(content);
|
|
4151
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4152
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4153
|
+
API_KEY8 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4154
|
+
}
|
|
4155
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4156
|
+
API_URL8 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4157
|
+
}
|
|
4158
|
+
} catch {
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
if (!WORKSPACE_ID6) {
|
|
4163
|
+
const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
|
|
4164
|
+
if (fs16.existsSync(csConfigPath)) {
|
|
4165
|
+
try {
|
|
4166
|
+
const content = fs16.readFileSync(csConfigPath, "utf-8");
|
|
4167
|
+
const csConfig = JSON.parse(content);
|
|
4168
|
+
if (csConfig.workspace_id) {
|
|
4169
|
+
WORKSPACE_ID6 = csConfig.workspace_id;
|
|
4170
|
+
}
|
|
4171
|
+
} catch {
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
const parentDir = path17.dirname(searchDir);
|
|
4176
|
+
if (parentDir === searchDir) break;
|
|
4177
|
+
searchDir = parentDir;
|
|
4178
|
+
}
|
|
4179
|
+
if (!API_KEY8) {
|
|
4180
|
+
const homeMcpPath = path17.join(homedir14(), ".mcp.json");
|
|
4181
|
+
if (fs16.existsSync(homeMcpPath)) {
|
|
4182
|
+
try {
|
|
4183
|
+
const content = fs16.readFileSync(homeMcpPath, "utf-8");
|
|
4184
|
+
const config = JSON.parse(content);
|
|
4185
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4186
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4187
|
+
API_KEY8 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4188
|
+
}
|
|
4189
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4190
|
+
API_URL8 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4191
|
+
}
|
|
4192
|
+
} catch {
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
async function captureExploration(toolName, target, resultSummary, sessionId) {
|
|
4198
|
+
if (!API_KEY8) return;
|
|
4199
|
+
const cacheKey = `${toolName}:${target}`;
|
|
4200
|
+
if (recentCaptures.has(cacheKey)) {
|
|
4201
|
+
return;
|
|
4202
|
+
}
|
|
4203
|
+
recentCaptures.add(cacheKey);
|
|
4204
|
+
setTimeout(() => recentCaptures.delete(cacheKey), CAPTURE_WINDOW_MS);
|
|
4205
|
+
const payload = {
|
|
4206
|
+
event_type: "file_exploration",
|
|
4207
|
+
title: `${toolName}: ${target.slice(0, 50)}`,
|
|
4208
|
+
content: JSON.stringify({
|
|
4209
|
+
tool: toolName,
|
|
4210
|
+
target,
|
|
4211
|
+
result_summary: resultSummary.slice(0, 500),
|
|
4212
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4213
|
+
}),
|
|
4214
|
+
importance: "low",
|
|
4215
|
+
tags: ["exploration", toolName.toLowerCase()],
|
|
4216
|
+
source_type: "hook",
|
|
4217
|
+
session_id: sessionId
|
|
4218
|
+
};
|
|
4219
|
+
if (WORKSPACE_ID6) {
|
|
4220
|
+
payload.workspace_id = WORKSPACE_ID6;
|
|
4221
|
+
}
|
|
4222
|
+
try {
|
|
4223
|
+
const controller = new AbortController();
|
|
4224
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
4225
|
+
await fetch(`${API_URL8}/api/v1/memory/events`, {
|
|
4226
|
+
method: "POST",
|
|
4227
|
+
headers: {
|
|
4228
|
+
"Content-Type": "application/json",
|
|
4229
|
+
"X-API-Key": API_KEY8
|
|
4230
|
+
},
|
|
4231
|
+
body: JSON.stringify(payload),
|
|
4232
|
+
signal: controller.signal
|
|
4233
|
+
});
|
|
4234
|
+
clearTimeout(timeoutId);
|
|
4235
|
+
} catch {
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
async function runOnReadHook() {
|
|
4239
|
+
if (!ENABLED10) {
|
|
4240
|
+
process.exit(0);
|
|
4241
|
+
}
|
|
4242
|
+
let inputData = "";
|
|
4243
|
+
for await (const chunk of process.stdin) {
|
|
4244
|
+
inputData += chunk;
|
|
4245
|
+
}
|
|
4246
|
+
if (!inputData.trim()) {
|
|
4247
|
+
process.exit(0);
|
|
4248
|
+
}
|
|
4249
|
+
let input;
|
|
4250
|
+
try {
|
|
4251
|
+
input = JSON.parse(inputData);
|
|
4252
|
+
} catch {
|
|
4253
|
+
process.exit(0);
|
|
4254
|
+
}
|
|
4255
|
+
const toolName = input.tool_name || "";
|
|
4256
|
+
if (!["Read", "Glob", "Grep"].includes(toolName)) {
|
|
4257
|
+
process.exit(0);
|
|
4258
|
+
}
|
|
4259
|
+
const cwd = input.cwd || process.cwd();
|
|
4260
|
+
loadConfigFromMcpJson6(cwd);
|
|
4261
|
+
const sessionId = input.session_id || "unknown";
|
|
4262
|
+
let target = "";
|
|
4263
|
+
let resultSummary = "";
|
|
4264
|
+
switch (toolName) {
|
|
4265
|
+
case "Read":
|
|
4266
|
+
target = input.tool_input?.file_path || "";
|
|
4267
|
+
resultSummary = `Read file: ${target}`;
|
|
4268
|
+
break;
|
|
4269
|
+
case "Glob":
|
|
4270
|
+
target = input.tool_input?.pattern || "";
|
|
4271
|
+
const globFiles = input.tool_result?.files || [];
|
|
4272
|
+
resultSummary = `Found ${globFiles.length} files matching ${target}`;
|
|
4273
|
+
break;
|
|
4274
|
+
case "Grep":
|
|
4275
|
+
target = input.tool_input?.pattern || "";
|
|
4276
|
+
const matches = input.tool_result?.matches || 0;
|
|
4277
|
+
resultSummary = `Found ${matches} matches for "${target}"`;
|
|
4278
|
+
break;
|
|
4279
|
+
}
|
|
4280
|
+
if (target) {
|
|
4281
|
+
captureExploration(toolName, target, resultSummary, sessionId).catch(() => {
|
|
4282
|
+
});
|
|
4283
|
+
}
|
|
4284
|
+
process.exit(0);
|
|
4285
|
+
}
|
|
4286
|
+
var ENABLED10, API_URL8, API_KEY8, WORKSPACE_ID6, recentCaptures, CAPTURE_WINDOW_MS, isDirectRun10;
|
|
4287
|
+
var init_on_read = __esm({
|
|
4288
|
+
"src/hooks/on-read.ts"() {
|
|
4289
|
+
"use strict";
|
|
4290
|
+
ENABLED10 = process.env.CONTEXTSTREAM_READ_HOOK_ENABLED !== "false";
|
|
4291
|
+
API_URL8 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4292
|
+
API_KEY8 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4293
|
+
WORKSPACE_ID6 = null;
|
|
4294
|
+
recentCaptures = /* @__PURE__ */ new Set();
|
|
4295
|
+
CAPTURE_WINDOW_MS = 6e4;
|
|
4296
|
+
isDirectRun10 = process.argv[1]?.includes("on-read") || process.argv[2] === "on-read";
|
|
4297
|
+
if (isDirectRun10) {
|
|
4298
|
+
runOnReadHook().catch(() => process.exit(0));
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
});
|
|
4302
|
+
|
|
4303
|
+
// src/hooks/on-web.ts
|
|
4304
|
+
var on_web_exports = {};
|
|
4305
|
+
__export(on_web_exports, {
|
|
4306
|
+
runOnWebHook: () => runOnWebHook
|
|
4307
|
+
});
|
|
4308
|
+
import * as fs17 from "node:fs";
|
|
4309
|
+
import * as path18 from "node:path";
|
|
4310
|
+
import { homedir as homedir15 } from "node:os";
|
|
4311
|
+
function loadConfigFromMcpJson7(cwd) {
|
|
4312
|
+
let searchDir = path18.resolve(cwd);
|
|
4313
|
+
for (let i = 0; i < 5; i++) {
|
|
4314
|
+
if (!API_KEY9) {
|
|
4315
|
+
const mcpPath = path18.join(searchDir, ".mcp.json");
|
|
4316
|
+
if (fs17.existsSync(mcpPath)) {
|
|
4317
|
+
try {
|
|
4318
|
+
const content = fs17.readFileSync(mcpPath, "utf-8");
|
|
4319
|
+
const config = JSON.parse(content);
|
|
4320
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4321
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4322
|
+
API_KEY9 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4323
|
+
}
|
|
4324
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4325
|
+
API_URL9 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4326
|
+
}
|
|
4327
|
+
} catch {
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
if (!WORKSPACE_ID7) {
|
|
4332
|
+
const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
|
|
4333
|
+
if (fs17.existsSync(csConfigPath)) {
|
|
4334
|
+
try {
|
|
4335
|
+
const content = fs17.readFileSync(csConfigPath, "utf-8");
|
|
4336
|
+
const csConfig = JSON.parse(content);
|
|
4337
|
+
if (csConfig.workspace_id) {
|
|
4338
|
+
WORKSPACE_ID7 = csConfig.workspace_id;
|
|
4339
|
+
}
|
|
4340
|
+
} catch {
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
const parentDir = path18.dirname(searchDir);
|
|
4345
|
+
if (parentDir === searchDir) break;
|
|
4346
|
+
searchDir = parentDir;
|
|
4347
|
+
}
|
|
4348
|
+
if (!API_KEY9) {
|
|
4349
|
+
const homeMcpPath = path18.join(homedir15(), ".mcp.json");
|
|
4350
|
+
if (fs17.existsSync(homeMcpPath)) {
|
|
4351
|
+
try {
|
|
4352
|
+
const content = fs17.readFileSync(homeMcpPath, "utf-8");
|
|
4353
|
+
const config = JSON.parse(content);
|
|
4354
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4355
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4356
|
+
API_KEY9 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4357
|
+
}
|
|
4358
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4359
|
+
API_URL9 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4360
|
+
}
|
|
4361
|
+
} catch {
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
async function captureWebResearch(toolName, target, summary, sessionId) {
|
|
4367
|
+
if (!API_KEY9) return;
|
|
4368
|
+
const payload = {
|
|
4369
|
+
event_type: "web_research",
|
|
4370
|
+
title: `${toolName}: ${target.slice(0, 60)}`,
|
|
4371
|
+
content: JSON.stringify({
|
|
4372
|
+
tool: toolName,
|
|
4373
|
+
target,
|
|
4374
|
+
summary: summary.slice(0, 1e3),
|
|
4375
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4376
|
+
}),
|
|
4377
|
+
importance: "medium",
|
|
4378
|
+
tags: ["research", "web", toolName.toLowerCase()],
|
|
4379
|
+
source_type: "hook",
|
|
4380
|
+
session_id: sessionId
|
|
4381
|
+
};
|
|
4382
|
+
if (WORKSPACE_ID7) {
|
|
4383
|
+
payload.workspace_id = WORKSPACE_ID7;
|
|
4384
|
+
}
|
|
4385
|
+
try {
|
|
4386
|
+
const controller = new AbortController();
|
|
4387
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
4388
|
+
await fetch(`${API_URL9}/api/v1/memory/events`, {
|
|
4389
|
+
method: "POST",
|
|
4390
|
+
headers: {
|
|
4391
|
+
"Content-Type": "application/json",
|
|
4392
|
+
"X-API-Key": API_KEY9
|
|
4393
|
+
},
|
|
4394
|
+
body: JSON.stringify(payload),
|
|
4395
|
+
signal: controller.signal
|
|
4396
|
+
});
|
|
4397
|
+
clearTimeout(timeoutId);
|
|
4398
|
+
} catch {
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
async function runOnWebHook() {
|
|
4402
|
+
if (!ENABLED11) {
|
|
4403
|
+
process.exit(0);
|
|
4404
|
+
}
|
|
4405
|
+
let inputData = "";
|
|
4406
|
+
for await (const chunk of process.stdin) {
|
|
4407
|
+
inputData += chunk;
|
|
4408
|
+
}
|
|
4409
|
+
if (!inputData.trim()) {
|
|
4410
|
+
process.exit(0);
|
|
4411
|
+
}
|
|
4412
|
+
let input;
|
|
4413
|
+
try {
|
|
4414
|
+
input = JSON.parse(inputData);
|
|
4415
|
+
} catch {
|
|
4416
|
+
process.exit(0);
|
|
4417
|
+
}
|
|
4418
|
+
const toolName = input.tool_name || "";
|
|
4419
|
+
if (!["WebFetch", "WebSearch"].includes(toolName)) {
|
|
4420
|
+
process.exit(0);
|
|
4421
|
+
}
|
|
4422
|
+
const cwd = input.cwd || process.cwd();
|
|
4423
|
+
loadConfigFromMcpJson7(cwd);
|
|
4424
|
+
const sessionId = input.session_id || "unknown";
|
|
4425
|
+
let target = "";
|
|
4426
|
+
let summary = "";
|
|
4427
|
+
switch (toolName) {
|
|
4428
|
+
case "WebFetch":
|
|
4429
|
+
target = input.tool_input?.url || "";
|
|
4430
|
+
const prompt = input.tool_input?.prompt || "fetched content";
|
|
4431
|
+
const content = input.tool_result?.output || input.tool_result?.content || "";
|
|
4432
|
+
summary = `Fetched ${target} (${prompt}): ${content.slice(0, 300)}`;
|
|
4433
|
+
break;
|
|
4434
|
+
case "WebSearch":
|
|
4435
|
+
target = input.tool_input?.query || "";
|
|
4436
|
+
const results = input.tool_result?.results || [];
|
|
4437
|
+
const topResults = results.slice(0, 3).map((r) => `- ${r.title}: ${r.url}`).join("\n");
|
|
4438
|
+
summary = `Search: "${target}"
|
|
4439
|
+
Top results:
|
|
4440
|
+
${topResults}`;
|
|
4441
|
+
break;
|
|
4442
|
+
}
|
|
4443
|
+
if (target) {
|
|
4444
|
+
captureWebResearch(toolName, target, summary, sessionId).catch(() => {
|
|
4445
|
+
});
|
|
4446
|
+
}
|
|
4447
|
+
process.exit(0);
|
|
4448
|
+
}
|
|
4449
|
+
var ENABLED11, API_URL9, API_KEY9, WORKSPACE_ID7, isDirectRun11;
|
|
4450
|
+
var init_on_web = __esm({
|
|
4451
|
+
"src/hooks/on-web.ts"() {
|
|
4452
|
+
"use strict";
|
|
4453
|
+
ENABLED11 = process.env.CONTEXTSTREAM_WEB_HOOK_ENABLED !== "false";
|
|
4454
|
+
API_URL9 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4455
|
+
API_KEY9 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4456
|
+
WORKSPACE_ID7 = null;
|
|
4457
|
+
isDirectRun11 = process.argv[1]?.includes("on-web") || process.argv[2] === "on-web";
|
|
4458
|
+
if (isDirectRun11) {
|
|
4459
|
+
runOnWebHook().catch(() => process.exit(0));
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
});
|
|
4463
|
+
|
|
4464
|
+
// src/hooks/session-init.ts
|
|
4465
|
+
var session_init_exports = {};
|
|
4466
|
+
__export(session_init_exports, {
|
|
4467
|
+
runSessionInitHook: () => runSessionInitHook
|
|
4468
|
+
});
|
|
4469
|
+
import * as fs18 from "node:fs";
|
|
4470
|
+
import * as path19 from "node:path";
|
|
4471
|
+
import { homedir as homedir16 } from "node:os";
|
|
4472
|
+
function loadConfigFromMcpJson8(cwd) {
|
|
4473
|
+
let searchDir = path19.resolve(cwd);
|
|
4474
|
+
for (let i = 0; i < 5; i++) {
|
|
4475
|
+
if (!API_KEY10) {
|
|
4476
|
+
const mcpPath = path19.join(searchDir, ".mcp.json");
|
|
4477
|
+
if (fs18.existsSync(mcpPath)) {
|
|
4478
|
+
try {
|
|
4479
|
+
const content = fs18.readFileSync(mcpPath, "utf-8");
|
|
4480
|
+
const config = JSON.parse(content);
|
|
4481
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4482
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4483
|
+
API_KEY10 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4484
|
+
}
|
|
4485
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4486
|
+
API_URL10 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4487
|
+
}
|
|
4488
|
+
if (csEnv?.CONTEXTSTREAM_WORKSPACE_ID) {
|
|
4489
|
+
WORKSPACE_ID8 = csEnv.CONTEXTSTREAM_WORKSPACE_ID;
|
|
4490
|
+
}
|
|
4491
|
+
} catch {
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
}
|
|
4495
|
+
if (!WORKSPACE_ID8 || !PROJECT_ID2) {
|
|
4496
|
+
const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
|
|
4497
|
+
if (fs18.existsSync(csConfigPath)) {
|
|
4498
|
+
try {
|
|
4499
|
+
const content = fs18.readFileSync(csConfigPath, "utf-8");
|
|
4500
|
+
const csConfig = JSON.parse(content);
|
|
4501
|
+
if (csConfig.workspace_id && !WORKSPACE_ID8) {
|
|
4502
|
+
WORKSPACE_ID8 = csConfig.workspace_id;
|
|
4503
|
+
}
|
|
4504
|
+
if (csConfig.project_id && !PROJECT_ID2) {
|
|
4505
|
+
PROJECT_ID2 = csConfig.project_id;
|
|
4506
|
+
}
|
|
4507
|
+
} catch {
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
const parentDir = path19.dirname(searchDir);
|
|
4512
|
+
if (parentDir === searchDir) break;
|
|
4513
|
+
searchDir = parentDir;
|
|
4514
|
+
}
|
|
4515
|
+
if (!API_KEY10) {
|
|
4516
|
+
const homeMcpPath = path19.join(homedir16(), ".mcp.json");
|
|
4517
|
+
if (fs18.existsSync(homeMcpPath)) {
|
|
4518
|
+
try {
|
|
4519
|
+
const content = fs18.readFileSync(homeMcpPath, "utf-8");
|
|
4520
|
+
const config = JSON.parse(content);
|
|
4521
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4522
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4523
|
+
API_KEY10 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4524
|
+
}
|
|
4525
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4526
|
+
API_URL10 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4527
|
+
}
|
|
4528
|
+
} catch {
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
async function fetchSessionContext2() {
|
|
4534
|
+
if (!API_KEY10) return null;
|
|
4535
|
+
try {
|
|
4536
|
+
const controller = new AbortController();
|
|
4537
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
4538
|
+
const url = new URL(`${API_URL10}/api/v1/context`);
|
|
4539
|
+
if (WORKSPACE_ID8) url.searchParams.set("workspace_id", WORKSPACE_ID8);
|
|
4540
|
+
if (PROJECT_ID2) url.searchParams.set("project_id", PROJECT_ID2);
|
|
4541
|
+
url.searchParams.set("include_rules", "true");
|
|
4542
|
+
url.searchParams.set("include_lessons", "true");
|
|
4543
|
+
url.searchParams.set("include_decisions", "true");
|
|
4544
|
+
url.searchParams.set("include_plans", "true");
|
|
4545
|
+
url.searchParams.set("limit", "5");
|
|
4546
|
+
const response = await fetch(url.toString(), {
|
|
4547
|
+
method: "GET",
|
|
4548
|
+
headers: {
|
|
4549
|
+
"X-API-Key": API_KEY10
|
|
4550
|
+
},
|
|
4551
|
+
signal: controller.signal
|
|
4552
|
+
});
|
|
4553
|
+
clearTimeout(timeoutId);
|
|
4554
|
+
if (response.ok) {
|
|
4555
|
+
return await response.json();
|
|
4556
|
+
}
|
|
4557
|
+
return null;
|
|
4558
|
+
} catch {
|
|
4559
|
+
return null;
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
function formatContext(ctx) {
|
|
4563
|
+
if (!ctx) {
|
|
4564
|
+
return `[ContextStream Session Start]
|
|
4565
|
+
|
|
4566
|
+
No stored context found. Call \`mcp__contextstream__context(user_message="starting new session")\` to initialize.`;
|
|
4567
|
+
}
|
|
4568
|
+
const parts = ["[ContextStream Session Start]"];
|
|
4569
|
+
if (ctx.lessons && ctx.lessons.length > 0) {
|
|
4570
|
+
parts.push("\n## \u26A0\uFE0F Lessons from Past Mistakes");
|
|
4571
|
+
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
4572
|
+
parts.push(`- **${lesson.title}**: ${lesson.prevention}`);
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
if (ctx.active_plans && ctx.active_plans.length > 0) {
|
|
4576
|
+
parts.push("\n## \u{1F4CB} Active Plans");
|
|
4577
|
+
for (const plan of ctx.active_plans.slice(0, 3)) {
|
|
4578
|
+
parts.push(`- ${plan.title} (${plan.status})`);
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
if (ctx.pending_tasks && ctx.pending_tasks.length > 0) {
|
|
4582
|
+
parts.push("\n## \u2705 Pending Tasks");
|
|
4583
|
+
for (const task of ctx.pending_tasks.slice(0, 5)) {
|
|
4584
|
+
parts.push(`- ${task.title}`);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
if (ctx.recent_decisions && ctx.recent_decisions.length > 0) {
|
|
4588
|
+
parts.push("\n## \u{1F4DD} Recent Decisions");
|
|
4589
|
+
for (const decision of ctx.recent_decisions.slice(0, 3)) {
|
|
4590
|
+
parts.push(`- **${decision.title}**`);
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
parts.push("\n---");
|
|
4594
|
+
parts.push('Call `mcp__contextstream__context(user_message="...")` for task-specific context.');
|
|
4595
|
+
return parts.join("\n");
|
|
4596
|
+
}
|
|
4597
|
+
async function runSessionInitHook() {
|
|
4598
|
+
if (!ENABLED12) {
|
|
4599
|
+
process.exit(0);
|
|
4600
|
+
}
|
|
4601
|
+
let inputData = "";
|
|
4602
|
+
for await (const chunk of process.stdin) {
|
|
4603
|
+
inputData += chunk;
|
|
4604
|
+
}
|
|
4605
|
+
if (!inputData.trim()) {
|
|
4606
|
+
process.exit(0);
|
|
4607
|
+
}
|
|
4608
|
+
let input;
|
|
4609
|
+
try {
|
|
4610
|
+
input = JSON.parse(inputData);
|
|
4611
|
+
} catch {
|
|
4612
|
+
process.exit(0);
|
|
4613
|
+
}
|
|
4614
|
+
const cwd = input.cwd || process.cwd();
|
|
4615
|
+
loadConfigFromMcpJson8(cwd);
|
|
4616
|
+
const context = await fetchSessionContext2();
|
|
4617
|
+
const formattedContext = formatContext(context);
|
|
4618
|
+
console.log(
|
|
4619
|
+
JSON.stringify({
|
|
4620
|
+
hookSpecificOutput: {
|
|
4621
|
+
hookEventName: "SessionStart",
|
|
4622
|
+
additionalContext: formattedContext
|
|
4623
|
+
}
|
|
4624
|
+
})
|
|
4625
|
+
);
|
|
4626
|
+
process.exit(0);
|
|
4627
|
+
}
|
|
4628
|
+
var ENABLED12, API_URL10, API_KEY10, WORKSPACE_ID8, PROJECT_ID2, isDirectRun12;
|
|
4629
|
+
var init_session_init = __esm({
|
|
4630
|
+
"src/hooks/session-init.ts"() {
|
|
4631
|
+
"use strict";
|
|
4632
|
+
ENABLED12 = process.env.CONTEXTSTREAM_SESSION_INIT_ENABLED !== "false";
|
|
4633
|
+
API_URL10 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4634
|
+
API_KEY10 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4635
|
+
WORKSPACE_ID8 = null;
|
|
4636
|
+
PROJECT_ID2 = null;
|
|
4637
|
+
isDirectRun12 = process.argv[1]?.includes("session-init") || process.argv[2] === "session-init";
|
|
4638
|
+
if (isDirectRun12) {
|
|
4639
|
+
runSessionInitHook().catch(() => process.exit(0));
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
});
|
|
4643
|
+
|
|
4644
|
+
// src/hooks/session-end.ts
|
|
4645
|
+
var session_end_exports = {};
|
|
4646
|
+
__export(session_end_exports, {
|
|
4647
|
+
runSessionEndHook: () => runSessionEndHook
|
|
4648
|
+
});
|
|
4649
|
+
import * as fs19 from "node:fs";
|
|
4650
|
+
import * as path20 from "node:path";
|
|
4651
|
+
import { homedir as homedir17 } from "node:os";
|
|
4652
|
+
function loadConfigFromMcpJson9(cwd) {
|
|
4653
|
+
let searchDir = path20.resolve(cwd);
|
|
4654
|
+
for (let i = 0; i < 5; i++) {
|
|
4655
|
+
if (!API_KEY11) {
|
|
4656
|
+
const mcpPath = path20.join(searchDir, ".mcp.json");
|
|
4657
|
+
if (fs19.existsSync(mcpPath)) {
|
|
4658
|
+
try {
|
|
4659
|
+
const content = fs19.readFileSync(mcpPath, "utf-8");
|
|
4660
|
+
const config = JSON.parse(content);
|
|
4661
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4662
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4663
|
+
API_KEY11 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4664
|
+
}
|
|
4665
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4666
|
+
API_URL11 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4667
|
+
}
|
|
4668
|
+
} catch {
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
if (!WORKSPACE_ID9) {
|
|
4673
|
+
const csConfigPath = path20.join(searchDir, ".contextstream", "config.json");
|
|
4674
|
+
if (fs19.existsSync(csConfigPath)) {
|
|
4675
|
+
try {
|
|
4676
|
+
const content = fs19.readFileSync(csConfigPath, "utf-8");
|
|
4677
|
+
const csConfig = JSON.parse(content);
|
|
4678
|
+
if (csConfig.workspace_id) {
|
|
4679
|
+
WORKSPACE_ID9 = csConfig.workspace_id;
|
|
4680
|
+
}
|
|
4681
|
+
} catch {
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
const parentDir = path20.dirname(searchDir);
|
|
4686
|
+
if (parentDir === searchDir) break;
|
|
4687
|
+
searchDir = parentDir;
|
|
4688
|
+
}
|
|
4689
|
+
if (!API_KEY11) {
|
|
4690
|
+
const homeMcpPath = path20.join(homedir17(), ".mcp.json");
|
|
4691
|
+
if (fs19.existsSync(homeMcpPath)) {
|
|
4692
|
+
try {
|
|
4693
|
+
const content = fs19.readFileSync(homeMcpPath, "utf-8");
|
|
4694
|
+
const config = JSON.parse(content);
|
|
4695
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4696
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4697
|
+
API_KEY11 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4698
|
+
}
|
|
4699
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4700
|
+
API_URL11 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4701
|
+
}
|
|
4702
|
+
} catch {
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
function parseTranscriptStats(transcriptPath) {
|
|
4708
|
+
const stats = {
|
|
4709
|
+
messageCount: 0,
|
|
4710
|
+
toolCallCount: 0,
|
|
4711
|
+
duration: 0,
|
|
4712
|
+
filesModified: []
|
|
4713
|
+
};
|
|
4714
|
+
if (!transcriptPath || !fs19.existsSync(transcriptPath)) {
|
|
4715
|
+
return stats;
|
|
4716
|
+
}
|
|
4717
|
+
try {
|
|
4718
|
+
const content = fs19.readFileSync(transcriptPath, "utf-8");
|
|
4719
|
+
const lines = content.split("\n");
|
|
4720
|
+
let firstTimestamp = null;
|
|
4721
|
+
let lastTimestamp = null;
|
|
4722
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
4723
|
+
for (const line of lines) {
|
|
4724
|
+
if (!line.trim()) continue;
|
|
4725
|
+
try {
|
|
4726
|
+
const entry = JSON.parse(line);
|
|
4727
|
+
if (entry.type === "user" || entry.type === "assistant") {
|
|
4728
|
+
stats.messageCount++;
|
|
4729
|
+
} else if (entry.type === "tool_use") {
|
|
4730
|
+
stats.toolCallCount++;
|
|
4731
|
+
if (["Write", "Edit", "NotebookEdit"].includes(entry.name || "")) {
|
|
4732
|
+
const filePath = entry.input?.file_path;
|
|
4733
|
+
if (filePath) {
|
|
4734
|
+
modifiedFiles.add(filePath);
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
if (entry.timestamp) {
|
|
4739
|
+
const ts = new Date(entry.timestamp);
|
|
4740
|
+
if (!firstTimestamp || ts < firstTimestamp) {
|
|
4741
|
+
firstTimestamp = ts;
|
|
4742
|
+
}
|
|
4743
|
+
if (!lastTimestamp || ts > lastTimestamp) {
|
|
4744
|
+
lastTimestamp = ts;
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
} catch {
|
|
4748
|
+
continue;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
if (firstTimestamp && lastTimestamp) {
|
|
4752
|
+
stats.duration = Math.round((lastTimestamp.getTime() - firstTimestamp.getTime()) / 1e3);
|
|
4753
|
+
}
|
|
4754
|
+
stats.filesModified = Array.from(modifiedFiles);
|
|
4755
|
+
} catch {
|
|
4756
|
+
}
|
|
4757
|
+
return stats;
|
|
4758
|
+
}
|
|
4759
|
+
async function finalizeSession(sessionId, stats, reason) {
|
|
4760
|
+
if (!API_KEY11) return;
|
|
4761
|
+
const payload = {
|
|
4762
|
+
event_type: "session_end",
|
|
4763
|
+
title: `Session Ended: ${reason}`,
|
|
4764
|
+
content: JSON.stringify({
|
|
4765
|
+
session_id: sessionId,
|
|
4766
|
+
reason,
|
|
4767
|
+
stats: {
|
|
4768
|
+
messages: stats.messageCount,
|
|
4769
|
+
tool_calls: stats.toolCallCount,
|
|
4770
|
+
duration_seconds: stats.duration,
|
|
4771
|
+
files_modified: stats.filesModified.length
|
|
4772
|
+
},
|
|
4773
|
+
files_modified: stats.filesModified.slice(0, 20),
|
|
4774
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4775
|
+
}),
|
|
4776
|
+
importance: "low",
|
|
4777
|
+
tags: ["session", "end", reason],
|
|
4778
|
+
source_type: "hook",
|
|
4779
|
+
session_id: sessionId
|
|
4780
|
+
};
|
|
4781
|
+
if (WORKSPACE_ID9) {
|
|
4782
|
+
payload.workspace_id = WORKSPACE_ID9;
|
|
4783
|
+
}
|
|
4784
|
+
try {
|
|
4785
|
+
const controller = new AbortController();
|
|
4786
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
4787
|
+
await fetch(`${API_URL11}/api/v1/memory/events`, {
|
|
4788
|
+
method: "POST",
|
|
4789
|
+
headers: {
|
|
4790
|
+
"Content-Type": "application/json",
|
|
4791
|
+
"X-API-Key": API_KEY11
|
|
4792
|
+
},
|
|
4793
|
+
body: JSON.stringify(payload),
|
|
4794
|
+
signal: controller.signal
|
|
4795
|
+
});
|
|
4796
|
+
clearTimeout(timeoutId);
|
|
4797
|
+
} catch {
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
async function runSessionEndHook() {
|
|
4801
|
+
if (!ENABLED13) {
|
|
4802
|
+
process.exit(0);
|
|
4803
|
+
}
|
|
4804
|
+
let inputData = "";
|
|
4805
|
+
for await (const chunk of process.stdin) {
|
|
4806
|
+
inputData += chunk;
|
|
4807
|
+
}
|
|
4808
|
+
if (!inputData.trim()) {
|
|
4809
|
+
process.exit(0);
|
|
4810
|
+
}
|
|
4811
|
+
let input;
|
|
4812
|
+
try {
|
|
4813
|
+
input = JSON.parse(inputData);
|
|
4814
|
+
} catch {
|
|
4815
|
+
process.exit(0);
|
|
4816
|
+
}
|
|
4817
|
+
const cwd = input.cwd || process.cwd();
|
|
4818
|
+
loadConfigFromMcpJson9(cwd);
|
|
4819
|
+
const sessionId = input.session_id || "unknown";
|
|
4820
|
+
const transcriptPath = input.transcript_path || "";
|
|
4821
|
+
const reason = input.reason || "user_exit";
|
|
4822
|
+
const stats = parseTranscriptStats(transcriptPath);
|
|
4823
|
+
await finalizeSession(sessionId, stats, reason);
|
|
4824
|
+
process.exit(0);
|
|
4825
|
+
}
|
|
4826
|
+
var ENABLED13, API_URL11, API_KEY11, WORKSPACE_ID9, isDirectRun13;
|
|
4827
|
+
var init_session_end = __esm({
|
|
4828
|
+
"src/hooks/session-end.ts"() {
|
|
4829
|
+
"use strict";
|
|
4830
|
+
ENABLED13 = process.env.CONTEXTSTREAM_SESSION_END_ENABLED !== "false";
|
|
4831
|
+
API_URL11 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4832
|
+
API_KEY11 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4833
|
+
WORKSPACE_ID9 = null;
|
|
4834
|
+
isDirectRun13 = process.argv[1]?.includes("session-end") || process.argv[2] === "session-end";
|
|
4835
|
+
if (isDirectRun13) {
|
|
4836
|
+
runSessionEndHook().catch(() => process.exit(0));
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
});
|
|
4840
|
+
|
|
4841
|
+
// src/hooks/on-save-intent.ts
|
|
4842
|
+
var on_save_intent_exports = {};
|
|
4843
|
+
__export(on_save_intent_exports, {
|
|
4844
|
+
runOnSaveIntentHook: () => runOnSaveIntentHook
|
|
4845
|
+
});
|
|
4846
|
+
function detectsSaveIntent(text) {
|
|
4847
|
+
const hasSaveIntent = SAVE_PATTERNS.some((p) => p.test(text));
|
|
4848
|
+
const isLocalFile = LOCAL_FILE_PATTERNS.some((p) => p.test(text));
|
|
4849
|
+
return { hasSaveIntent, isLocalFile };
|
|
4850
|
+
}
|
|
4851
|
+
async function runOnSaveIntentHook() {
|
|
4852
|
+
if (!ENABLED14) {
|
|
3186
4853
|
process.exit(0);
|
|
3187
4854
|
}
|
|
3188
4855
|
let inputData = "";
|
|
@@ -3198,40 +4865,393 @@ async function runAutoRulesHook() {
|
|
|
3198
4865
|
} catch {
|
|
3199
4866
|
process.exit(0);
|
|
3200
4867
|
}
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
4868
|
+
let prompt = input.prompt || "";
|
|
4869
|
+
if (!prompt && input.session?.messages) {
|
|
4870
|
+
for (const msg of [...input.session.messages].reverse()) {
|
|
4871
|
+
if (msg.role === "user") {
|
|
4872
|
+
if (typeof msg.content === "string") {
|
|
4873
|
+
prompt = msg.content;
|
|
4874
|
+
} else if (Array.isArray(msg.content)) {
|
|
4875
|
+
for (const block of msg.content) {
|
|
4876
|
+
if (block.type === "text" && block.text) {
|
|
4877
|
+
prompt = block.text;
|
|
4878
|
+
break;
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
break;
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
3205
4885
|
}
|
|
3206
|
-
|
|
3207
|
-
const pythonHooks = detectPythonHooks(cwd);
|
|
3208
|
-
const hasPythonHooksToUpgrade = pythonHooks.global || pythonHooks.project;
|
|
3209
|
-
const rulesNotice = extractRulesNotice(input);
|
|
3210
|
-
const rulesNeedUpdate = rulesNotice && rulesNotice.status !== "current";
|
|
3211
|
-
if (!hasPythonHooksToUpgrade && !rulesNeedUpdate) {
|
|
4886
|
+
if (!prompt) {
|
|
3212
4887
|
process.exit(0);
|
|
3213
4888
|
}
|
|
3214
|
-
const
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
4889
|
+
const { hasSaveIntent, isLocalFile } = detectsSaveIntent(prompt);
|
|
4890
|
+
if (hasSaveIntent || isLocalFile) {
|
|
4891
|
+
console.log(
|
|
4892
|
+
JSON.stringify({
|
|
4893
|
+
hookSpecificOutput: {
|
|
4894
|
+
hookEventName: "UserPromptSubmit",
|
|
4895
|
+
additionalContext: SAVE_GUIDANCE
|
|
4896
|
+
}
|
|
4897
|
+
})
|
|
4898
|
+
);
|
|
3219
4899
|
}
|
|
3220
4900
|
process.exit(0);
|
|
3221
4901
|
}
|
|
3222
|
-
var
|
|
3223
|
-
var
|
|
3224
|
-
"src/hooks/
|
|
4902
|
+
var ENABLED14, SAVE_PATTERNS, LOCAL_FILE_PATTERNS, SAVE_GUIDANCE, isDirectRun14;
|
|
4903
|
+
var init_on_save_intent = __esm({
|
|
4904
|
+
"src/hooks/on-save-intent.ts"() {
|
|
3225
4905
|
"use strict";
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
4906
|
+
ENABLED14 = process.env.CONTEXTSTREAM_SAVE_INTENT_ENABLED !== "false";
|
|
4907
|
+
SAVE_PATTERNS = [
|
|
4908
|
+
// Direct save requests
|
|
4909
|
+
/\b(save|store|record|capture|log|document|write down|note down|keep track)\b.*\b(this|that|it|the)\b/i,
|
|
4910
|
+
/\b(save|store|record|capture|log)\b.*\b(to|in|for)\b.*\b(contextstream|memory|later|reference|future)\b/i,
|
|
4911
|
+
// Document creation
|
|
4912
|
+
/\b(create|make|write|draft)\b.*\b(a|the)\b.*\b(document|doc|note|summary|report|spec|design)\b/i,
|
|
4913
|
+
/\b(document|summarize|write up)\b.*\b(this|that|the|our)\b.*\b(decision|discussion|conversation|meeting|finding)\b/i,
|
|
4914
|
+
// Memory/reference requests
|
|
4915
|
+
/\b(remember|don't forget|keep in mind|note that|important to remember)\b/i,
|
|
4916
|
+
/\bfor\s+(future|later)\s+reference\b/i,
|
|
4917
|
+
/\b(add|put)\s+(this|it|that)\s+(to|in)\s+(memory|notes|docs)\b/i,
|
|
4918
|
+
// Decision tracking
|
|
4919
|
+
/\b(we\s+)?(decided|agreed|concluded|determined)\b.*\b(to|that)\b/i,
|
|
4920
|
+
/\blet('s|s)\s+document\b/i,
|
|
4921
|
+
/\bsave\s+(this|the)\s+(decision|choice|approach)\b/i,
|
|
4922
|
+
// Implementation/design docs
|
|
4923
|
+
/\b(implementation|design|architecture|spec)\s+(doc|document|plan)\b/i,
|
|
4924
|
+
/\bwrite\s+(the|a|an)\s+.*(md|markdown|readme)\b/i
|
|
4925
|
+
];
|
|
4926
|
+
LOCAL_FILE_PATTERNS = [
|
|
4927
|
+
/\b(save|write|create)\s+(it|this|the\s+\w+)\s+(to|in|as)\s+[./~]/i,
|
|
4928
|
+
/\b(save|write)\s+to\s+.*(\.md|\.txt|\.json|docs\/|notes\/)/i,
|
|
4929
|
+
/\bcreate\s+(a|the)\s+file\b/i
|
|
4930
|
+
];
|
|
4931
|
+
SAVE_GUIDANCE = `[CONTEXTSTREAM DOCUMENT STORAGE]
|
|
4932
|
+
The user wants to save/store content. Use ContextStream instead of local files:
|
|
4933
|
+
|
|
4934
|
+
**For decisions/notes:**
|
|
4935
|
+
\`\`\`
|
|
4936
|
+
mcp__contextstream__session(
|
|
4937
|
+
action="capture",
|
|
4938
|
+
event_type="decision|note|insight",
|
|
4939
|
+
title="...",
|
|
4940
|
+
content="...",
|
|
4941
|
+
importance="high|medium|low"
|
|
4942
|
+
)
|
|
4943
|
+
\`\`\`
|
|
4944
|
+
|
|
4945
|
+
**For documents/specs:**
|
|
4946
|
+
\`\`\`
|
|
4947
|
+
mcp__contextstream__docs(
|
|
4948
|
+
action="create",
|
|
4949
|
+
title="...",
|
|
4950
|
+
content="...",
|
|
4951
|
+
doc_type="implementation|design|spec|guide"
|
|
4952
|
+
)
|
|
4953
|
+
\`\`\`
|
|
4954
|
+
|
|
4955
|
+
**For plans:**
|
|
4956
|
+
\`\`\`
|
|
4957
|
+
mcp__contextstream__session(
|
|
4958
|
+
action="capture_plan",
|
|
4959
|
+
title="...",
|
|
4960
|
+
steps=[...]
|
|
4961
|
+
)
|
|
4962
|
+
\`\`\`
|
|
4963
|
+
|
|
4964
|
+
**Why ContextStream?**
|
|
4965
|
+
- Persists across sessions (local files don't)
|
|
4966
|
+
- Searchable and retrievable
|
|
4967
|
+
- Shows up in context automatically
|
|
4968
|
+
- Can be shared with team
|
|
4969
|
+
|
|
4970
|
+
Only save to local files if user explicitly requests a specific file path.
|
|
4971
|
+
[END GUIDANCE]`;
|
|
4972
|
+
isDirectRun14 = process.argv[1]?.includes("on-save-intent") || process.argv[2] === "on-save-intent";
|
|
4973
|
+
if (isDirectRun14) {
|
|
4974
|
+
runOnSaveIntentHook().catch(() => process.exit(0));
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
});
|
|
4978
|
+
|
|
4979
|
+
// src/verify-key.ts
|
|
4980
|
+
var verify_key_exports = {};
|
|
4981
|
+
__export(verify_key_exports, {
|
|
4982
|
+
loadApiKey: () => loadApiKey,
|
|
4983
|
+
maskApiKey: () => maskApiKey2,
|
|
4984
|
+
runVerifyKey: () => runVerifyKey,
|
|
4985
|
+
validateApiKey: () => validateApiKey
|
|
4986
|
+
});
|
|
4987
|
+
import * as fs20 from "node:fs";
|
|
4988
|
+
import * as path21 from "node:path";
|
|
4989
|
+
import { homedir as homedir18 } from "node:os";
|
|
4990
|
+
function maskApiKey2(key) {
|
|
4991
|
+
if (!key || key.length < 10) return "***";
|
|
4992
|
+
const prefix = key.slice(0, 6);
|
|
4993
|
+
const suffix = key.slice(-4);
|
|
4994
|
+
return `${prefix}...${suffix}`;
|
|
4995
|
+
}
|
|
4996
|
+
function extractFromMcpConfig(config) {
|
|
4997
|
+
if (!config.mcpServers) return {};
|
|
4998
|
+
const priorityNames = ["contextstream", "ContextStream", "context-stream"];
|
|
4999
|
+
for (const name of priorityNames) {
|
|
5000
|
+
const server = config.mcpServers[name];
|
|
5001
|
+
if (server?.env?.CONTEXTSTREAM_API_KEY) {
|
|
5002
|
+
return {
|
|
5003
|
+
apiKey: server.env.CONTEXTSTREAM_API_KEY,
|
|
5004
|
+
apiUrl: server.env.CONTEXTSTREAM_API_URL
|
|
5005
|
+
};
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
for (const [, server] of Object.entries(config.mcpServers)) {
|
|
5009
|
+
if (server?.env?.CONTEXTSTREAM_API_KEY) {
|
|
5010
|
+
return {
|
|
5011
|
+
apiKey: server.env.CONTEXTSTREAM_API_KEY,
|
|
5012
|
+
apiUrl: server.env.CONTEXTSTREAM_API_URL
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
return {};
|
|
5017
|
+
}
|
|
5018
|
+
function getClaudeDesktopConfigPath() {
|
|
5019
|
+
const platform = process.platform;
|
|
5020
|
+
if (platform === "darwin") {
|
|
5021
|
+
return path21.join(homedir18(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5022
|
+
} else if (platform === "win32") {
|
|
5023
|
+
return path21.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
5024
|
+
} else {
|
|
5025
|
+
return path21.join(homedir18(), ".config", "Claude", "claude_desktop_config.json");
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
function loadApiKey() {
|
|
5029
|
+
let apiKey = null;
|
|
5030
|
+
let apiUrl = "https://api.contextstream.io";
|
|
5031
|
+
let source = "none";
|
|
5032
|
+
if (process.env.CONTEXTSTREAM_API_KEY) {
|
|
5033
|
+
apiKey = process.env.CONTEXTSTREAM_API_KEY;
|
|
5034
|
+
source = "environment";
|
|
5035
|
+
if (process.env.CONTEXTSTREAM_API_URL) {
|
|
5036
|
+
apiUrl = process.env.CONTEXTSTREAM_API_URL;
|
|
5037
|
+
}
|
|
5038
|
+
return { apiKey, apiUrl, source };
|
|
5039
|
+
}
|
|
5040
|
+
let searchDir = process.cwd();
|
|
5041
|
+
for (let i = 0; i < 5; i++) {
|
|
5042
|
+
const projectMcpPath = path21.join(searchDir, ".mcp.json");
|
|
5043
|
+
if (fs20.existsSync(projectMcpPath)) {
|
|
5044
|
+
try {
|
|
5045
|
+
const content = fs20.readFileSync(projectMcpPath, "utf-8");
|
|
5046
|
+
const config = JSON.parse(content);
|
|
5047
|
+
const extracted = extractFromMcpConfig(config);
|
|
5048
|
+
if (extracted.apiKey) {
|
|
5049
|
+
apiKey = extracted.apiKey;
|
|
5050
|
+
source = `${projectMcpPath}`;
|
|
5051
|
+
if (extracted.apiUrl) {
|
|
5052
|
+
apiUrl = extracted.apiUrl;
|
|
5053
|
+
}
|
|
5054
|
+
return { apiKey, apiUrl, source };
|
|
5055
|
+
}
|
|
5056
|
+
} catch {
|
|
5057
|
+
}
|
|
5058
|
+
}
|
|
5059
|
+
const parentDir = path21.dirname(searchDir);
|
|
5060
|
+
if (parentDir === searchDir) break;
|
|
5061
|
+
searchDir = parentDir;
|
|
5062
|
+
}
|
|
5063
|
+
const globalMcpPath = path21.join(homedir18(), ".mcp.json");
|
|
5064
|
+
if (fs20.existsSync(globalMcpPath)) {
|
|
5065
|
+
try {
|
|
5066
|
+
const content = fs20.readFileSync(globalMcpPath, "utf-8");
|
|
5067
|
+
const config = JSON.parse(content);
|
|
5068
|
+
const extracted = extractFromMcpConfig(config);
|
|
5069
|
+
if (extracted.apiKey) {
|
|
5070
|
+
apiKey = extracted.apiKey;
|
|
5071
|
+
source = "~/.mcp.json";
|
|
5072
|
+
if (extracted.apiUrl) {
|
|
5073
|
+
apiUrl = extracted.apiUrl;
|
|
5074
|
+
}
|
|
5075
|
+
return { apiKey, apiUrl, source };
|
|
5076
|
+
}
|
|
5077
|
+
} catch {
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
const cursorPaths = [
|
|
5081
|
+
path21.join(process.cwd(), ".cursor", "mcp.json"),
|
|
5082
|
+
path21.join(homedir18(), ".cursor", "mcp.json")
|
|
5083
|
+
];
|
|
5084
|
+
for (const cursorPath of cursorPaths) {
|
|
5085
|
+
if (fs20.existsSync(cursorPath)) {
|
|
5086
|
+
try {
|
|
5087
|
+
const content = fs20.readFileSync(cursorPath, "utf-8");
|
|
5088
|
+
const config = JSON.parse(content);
|
|
5089
|
+
const extracted = extractFromMcpConfig(config);
|
|
5090
|
+
if (extracted.apiKey) {
|
|
5091
|
+
apiKey = extracted.apiKey;
|
|
5092
|
+
source = cursorPath;
|
|
5093
|
+
if (extracted.apiUrl) {
|
|
5094
|
+
apiUrl = extracted.apiUrl;
|
|
5095
|
+
}
|
|
5096
|
+
return { apiKey, apiUrl, source };
|
|
5097
|
+
}
|
|
5098
|
+
} catch {
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
const claudeDesktopPath = getClaudeDesktopConfigPath();
|
|
5103
|
+
if (fs20.existsSync(claudeDesktopPath)) {
|
|
5104
|
+
try {
|
|
5105
|
+
const content = fs20.readFileSync(claudeDesktopPath, "utf-8");
|
|
5106
|
+
const config = JSON.parse(content);
|
|
5107
|
+
const extracted = extractFromMcpConfig(config);
|
|
5108
|
+
if (extracted.apiKey) {
|
|
5109
|
+
apiKey = extracted.apiKey;
|
|
5110
|
+
source = claudeDesktopPath;
|
|
5111
|
+
if (extracted.apiUrl) {
|
|
5112
|
+
apiUrl = extracted.apiUrl;
|
|
5113
|
+
}
|
|
5114
|
+
return { apiKey, apiUrl, source };
|
|
5115
|
+
}
|
|
5116
|
+
} catch {
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
const vscodePaths = [
|
|
5120
|
+
path21.join(homedir18(), ".vscode", "mcp.json"),
|
|
5121
|
+
path21.join(homedir18(), ".codeium", "windsurf", "mcp_config.json"),
|
|
5122
|
+
path21.join(homedir18(), ".continue", "config.json")
|
|
5123
|
+
];
|
|
5124
|
+
for (const vsPath of vscodePaths) {
|
|
5125
|
+
if (fs20.existsSync(vsPath)) {
|
|
5126
|
+
try {
|
|
5127
|
+
const content = fs20.readFileSync(vsPath, "utf-8");
|
|
5128
|
+
const config = JSON.parse(content);
|
|
5129
|
+
const extracted = extractFromMcpConfig(config);
|
|
5130
|
+
if (extracted.apiKey) {
|
|
5131
|
+
apiKey = extracted.apiKey;
|
|
5132
|
+
source = vsPath;
|
|
5133
|
+
if (extracted.apiUrl) {
|
|
5134
|
+
apiUrl = extracted.apiUrl;
|
|
5135
|
+
}
|
|
5136
|
+
return { apiKey, apiUrl, source };
|
|
5137
|
+
}
|
|
5138
|
+
} catch {
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
const credentialsPath = path21.join(homedir18(), ".contextstream", "credentials.json");
|
|
5143
|
+
if (fs20.existsSync(credentialsPath)) {
|
|
5144
|
+
try {
|
|
5145
|
+
const content = fs20.readFileSync(credentialsPath, "utf-8");
|
|
5146
|
+
const creds = JSON.parse(content);
|
|
5147
|
+
if (creds.api_key) {
|
|
5148
|
+
apiKey = creds.api_key;
|
|
5149
|
+
source = "~/.contextstream/credentials.json";
|
|
5150
|
+
if (creds.api_url) {
|
|
5151
|
+
apiUrl = creds.api_url;
|
|
5152
|
+
}
|
|
5153
|
+
return { apiKey, apiUrl, source };
|
|
5154
|
+
}
|
|
5155
|
+
} catch {
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
return { apiKey, apiUrl, source };
|
|
5159
|
+
}
|
|
5160
|
+
async function validateApiKey(apiKey, apiUrl) {
|
|
5161
|
+
try {
|
|
5162
|
+
const controller = new AbortController();
|
|
5163
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
5164
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/me`, {
|
|
5165
|
+
method: "GET",
|
|
5166
|
+
headers: {
|
|
5167
|
+
"X-API-Key": apiKey
|
|
5168
|
+
},
|
|
5169
|
+
signal: controller.signal
|
|
5170
|
+
});
|
|
5171
|
+
clearTimeout(timeoutId);
|
|
5172
|
+
if (response.ok) {
|
|
5173
|
+
const data = await response.json();
|
|
5174
|
+
return {
|
|
5175
|
+
valid: true,
|
|
5176
|
+
masked_key: maskApiKey2(apiKey),
|
|
5177
|
+
email: data.email,
|
|
5178
|
+
name: data.name || data.full_name,
|
|
5179
|
+
plan: data.plan_name || data.plan || "free",
|
|
5180
|
+
workspace_name: data.workspace?.name
|
|
5181
|
+
};
|
|
5182
|
+
} else if (response.status === 401) {
|
|
5183
|
+
return {
|
|
5184
|
+
valid: false,
|
|
5185
|
+
masked_key: maskApiKey2(apiKey),
|
|
5186
|
+
error: "Invalid or expired API key"
|
|
5187
|
+
};
|
|
5188
|
+
} else {
|
|
5189
|
+
return {
|
|
5190
|
+
valid: false,
|
|
5191
|
+
masked_key: maskApiKey2(apiKey),
|
|
5192
|
+
error: `API error: ${response.status}`
|
|
5193
|
+
};
|
|
5194
|
+
}
|
|
5195
|
+
} catch (error) {
|
|
5196
|
+
return {
|
|
5197
|
+
valid: false,
|
|
5198
|
+
masked_key: maskApiKey2(apiKey),
|
|
5199
|
+
error: `Connection error: ${error instanceof Error ? error.message : String(error)}`
|
|
5200
|
+
};
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
async function runVerifyKey(outputJson) {
|
|
5204
|
+
const { apiKey, apiUrl, source } = loadApiKey();
|
|
5205
|
+
if (!apiKey) {
|
|
5206
|
+
const result2 = {
|
|
5207
|
+
valid: false,
|
|
5208
|
+
masked_key: "",
|
|
5209
|
+
error: "No API key found. Run 'contextstream-mcp setup' to configure."
|
|
5210
|
+
};
|
|
5211
|
+
if (outputJson) {
|
|
5212
|
+
console.log(JSON.stringify(result2));
|
|
5213
|
+
} else {
|
|
5214
|
+
console.log("\u274C No API key found");
|
|
5215
|
+
console.log(" Run 'contextstream-mcp setup' to configure your API key.");
|
|
5216
|
+
}
|
|
5217
|
+
return result2;
|
|
5218
|
+
}
|
|
5219
|
+
const result = await validateApiKey(apiKey, apiUrl);
|
|
5220
|
+
if (outputJson) {
|
|
5221
|
+
console.log(JSON.stringify({ ...result, source }));
|
|
5222
|
+
} else {
|
|
5223
|
+
console.log("");
|
|
5224
|
+
console.log("\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");
|
|
5225
|
+
console.log(" ContextStream API Key");
|
|
5226
|
+
console.log("\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");
|
|
5227
|
+
console.log("");
|
|
5228
|
+
console.log(` Key: ${result.masked_key}`);
|
|
5229
|
+
console.log(` Source: ${source}`);
|
|
5230
|
+
if (result.valid) {
|
|
5231
|
+
console.log(` Status: \u2713 Valid`);
|
|
5232
|
+
if (result.email) {
|
|
5233
|
+
console.log(` Account: ${result.email}`);
|
|
5234
|
+
}
|
|
5235
|
+
if (result.name) {
|
|
5236
|
+
console.log(` Name: ${result.name}`);
|
|
5237
|
+
}
|
|
5238
|
+
if (result.plan) {
|
|
5239
|
+
console.log(` Plan: ${result.plan}`);
|
|
5240
|
+
}
|
|
5241
|
+
if (result.workspace_name) {
|
|
5242
|
+
console.log(` Workspace: ${result.workspace_name}`);
|
|
5243
|
+
}
|
|
5244
|
+
} else {
|
|
5245
|
+
console.log(` Status: \u2717 Invalid`);
|
|
5246
|
+
console.log(` Error: ${result.error}`);
|
|
3234
5247
|
}
|
|
5248
|
+
console.log("");
|
|
5249
|
+
}
|
|
5250
|
+
return result;
|
|
5251
|
+
}
|
|
5252
|
+
var init_verify_key = __esm({
|
|
5253
|
+
"src/verify-key.ts"() {
|
|
5254
|
+
"use strict";
|
|
3235
5255
|
}
|
|
3236
5256
|
});
|
|
3237
5257
|
|
|
@@ -3717,8 +5737,8 @@ function getErrorMap() {
|
|
|
3717
5737
|
|
|
3718
5738
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
3719
5739
|
var makeIssue = (params) => {
|
|
3720
|
-
const { data, path:
|
|
3721
|
-
const fullPath = [...
|
|
5740
|
+
const { data, path: path22, errorMaps, issueData } = params;
|
|
5741
|
+
const fullPath = [...path22, ...issueData.path || []];
|
|
3722
5742
|
const fullIssue = {
|
|
3723
5743
|
...issueData,
|
|
3724
5744
|
path: fullPath
|
|
@@ -3834,11 +5854,11 @@ var errorUtil;
|
|
|
3834
5854
|
|
|
3835
5855
|
// node_modules/zod/v3/types.js
|
|
3836
5856
|
var ParseInputLazyPath = class {
|
|
3837
|
-
constructor(parent, value,
|
|
5857
|
+
constructor(parent, value, path22, key) {
|
|
3838
5858
|
this._cachedPath = [];
|
|
3839
5859
|
this.parent = parent;
|
|
3840
5860
|
this.data = value;
|
|
3841
|
-
this._path =
|
|
5861
|
+
this._path = path22;
|
|
3842
5862
|
this._key = key;
|
|
3843
5863
|
}
|
|
3844
5864
|
get path() {
|
|
@@ -7288,6 +9308,9 @@ import { join } from "path";
|
|
|
7288
9308
|
var UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
|
|
7289
9309
|
var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
7290
9310
|
function getVersion() {
|
|
9311
|
+
if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
|
|
9312
|
+
return __CONTEXTSTREAM_VERSION__;
|
|
9313
|
+
}
|
|
7291
9314
|
try {
|
|
7292
9315
|
const require2 = createRequire(import.meta.url);
|
|
7293
9316
|
const pkg = require2("../package.json");
|
|
@@ -7571,14 +9594,14 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504])
|
|
|
7571
9594
|
var MAX_RETRIES = 3;
|
|
7572
9595
|
var BASE_DELAY = 1e3;
|
|
7573
9596
|
async function sleep(ms) {
|
|
7574
|
-
return new Promise((
|
|
9597
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
7575
9598
|
}
|
|
7576
|
-
async function request(config,
|
|
9599
|
+
async function request(config, path22, options = {}) {
|
|
7577
9600
|
const { apiUrl, userAgent } = config;
|
|
7578
9601
|
const authOverride = getAuthOverride();
|
|
7579
9602
|
const apiKey = authOverride?.apiKey ?? config.apiKey;
|
|
7580
9603
|
const jwt = authOverride?.jwt ?? config.jwt;
|
|
7581
|
-
const apiPath =
|
|
9604
|
+
const apiPath = path22.startsWith("/api/") ? path22 : `/api/v1${path22}`;
|
|
7582
9605
|
const url = `${apiUrl.replace(/\/$/, "")}${apiPath}`;
|
|
7583
9606
|
const maxRetries = options.retries ?? MAX_RETRIES;
|
|
7584
9607
|
const baseDelay = options.retryDelay ?? BASE_DELAY;
|
|
@@ -7724,9 +9747,9 @@ function extractErrorCode(payload) {
|
|
|
7724
9747
|
if (typeof payload.code === "string" && payload.code.trim()) return payload.code.trim();
|
|
7725
9748
|
return null;
|
|
7726
9749
|
}
|
|
7727
|
-
function detectIntegrationProvider(
|
|
7728
|
-
if (/\/github(\/|$)/i.test(
|
|
7729
|
-
if (/\/slack(\/|$)/i.test(
|
|
9750
|
+
function detectIntegrationProvider(path22) {
|
|
9751
|
+
if (/\/github(\/|$)/i.test(path22)) return "github";
|
|
9752
|
+
if (/\/slack(\/|$)/i.test(path22)) return "slack";
|
|
7730
9753
|
return null;
|
|
7731
9754
|
}
|
|
7732
9755
|
function rewriteNotFoundMessage(input) {
|
|
@@ -8379,10 +10402,10 @@ var PROJECT_MARKERS = [
|
|
|
8379
10402
|
];
|
|
8380
10403
|
function isMultiProjectFolder(folderPath) {
|
|
8381
10404
|
try {
|
|
8382
|
-
const
|
|
10405
|
+
const fs21 = __require("fs");
|
|
8383
10406
|
const pathModule = __require("path");
|
|
8384
|
-
const rootHasGit =
|
|
8385
|
-
const entries =
|
|
10407
|
+
const rootHasGit = fs21.existsSync(pathModule.join(folderPath, ".git"));
|
|
10408
|
+
const entries = fs21.readdirSync(folderPath, { withFileTypes: true });
|
|
8386
10409
|
const subdirs = entries.filter(
|
|
8387
10410
|
(e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules"
|
|
8388
10411
|
);
|
|
@@ -8390,7 +10413,7 @@ function isMultiProjectFolder(folderPath) {
|
|
|
8390
10413
|
for (const subdir of subdirs) {
|
|
8391
10414
|
const subdirPath = pathModule.join(folderPath, subdir.name);
|
|
8392
10415
|
for (const marker of PROJECT_MARKERS) {
|
|
8393
|
-
if (
|
|
10416
|
+
if (fs21.existsSync(pathModule.join(subdirPath, marker))) {
|
|
8394
10417
|
projectSubdirs.push(subdir.name);
|
|
8395
10418
|
break;
|
|
8396
10419
|
}
|
|
@@ -10457,9 +12480,9 @@ var ContextStreamClient = class {
|
|
|
10457
12480
|
candidateParts.push("## Relevant Code\n");
|
|
10458
12481
|
currentChars += 18;
|
|
10459
12482
|
const codeEntries = code.results.map((c) => {
|
|
10460
|
-
const
|
|
12483
|
+
const path22 = c.file_path || "file";
|
|
10461
12484
|
const content = c.content?.slice(0, 150) || "";
|
|
10462
|
-
return { path:
|
|
12485
|
+
return { path: path22, entry: `\u2022 ${path22}: ${content}...
|
|
10463
12486
|
` };
|
|
10464
12487
|
});
|
|
10465
12488
|
for (const c of codeEntries) {
|
|
@@ -12973,6 +14996,143 @@ Use for monorepos or folders with multiple independent projects.
|
|
|
12973
14996
|
|
|
12974
14997
|
Full docs: https://contextstream.io/docs/mcp/tools
|
|
12975
14998
|
`.trim();
|
|
14999
|
+
var NO_HOOKS_SUPPLEMENT = `
|
|
15000
|
+
|
|
15001
|
+
---
|
|
15002
|
+
## \u26A0\uFE0F IMPORTANT: No Hooks Available \u26A0\uFE0F
|
|
15003
|
+
|
|
15004
|
+
**This editor does NOT have hooks to enforce ContextStream behavior.**
|
|
15005
|
+
You MUST follow these rules manually - there is no automatic enforcement.
|
|
15006
|
+
|
|
15007
|
+
---
|
|
15008
|
+
|
|
15009
|
+
## \u{1F680} SESSION START PROTOCOL
|
|
15010
|
+
|
|
15011
|
+
**On EVERY new session, you MUST:**
|
|
15012
|
+
|
|
15013
|
+
1. **Call \`init(folder_path="<project_path>")\`** FIRST
|
|
15014
|
+
- This triggers project indexing
|
|
15015
|
+
- Check response for \`indexing_status\`
|
|
15016
|
+
- If \`"started"\` or \`"refreshing"\`: wait before searching
|
|
15017
|
+
|
|
15018
|
+
2. **Call \`context(user_message="<first_message>")\`** SECOND
|
|
15019
|
+
- Gets task-specific rules and lessons
|
|
15020
|
+
- Check for [LESSONS_WARNING] - past mistakes to avoid
|
|
15021
|
+
- Check for [RULES_NOTICE] - update rules if needed
|
|
15022
|
+
|
|
15023
|
+
3. **NEVER skip init/context** - you will miss critical context
|
|
15024
|
+
|
|
15025
|
+
---
|
|
15026
|
+
|
|
15027
|
+
## \u{1F4C1} FILE INDEXING (CRITICAL)
|
|
15028
|
+
|
|
15029
|
+
**There is NO automatic file indexing in this editor.**
|
|
15030
|
+
You MUST manage indexing manually:
|
|
15031
|
+
|
|
15032
|
+
### After Creating/Editing Files:
|
|
15033
|
+
\`\`\`
|
|
15034
|
+
project(action="index") # Re-index entire project
|
|
15035
|
+
\`\`\`
|
|
15036
|
+
|
|
15037
|
+
### For Single File Updates:
|
|
15038
|
+
\`\`\`
|
|
15039
|
+
project(action="ingest_local", path="<file_path>")
|
|
15040
|
+
\`\`\`
|
|
15041
|
+
|
|
15042
|
+
### Signs You Need to Re-index:
|
|
15043
|
+
- Search doesn't find code you just wrote
|
|
15044
|
+
- Search returns old versions of functions
|
|
15045
|
+
- New files don't appear in search results
|
|
15046
|
+
|
|
15047
|
+
### Best Practice:
|
|
15048
|
+
After completing a feature or making multiple file changes, ALWAYS run:
|
|
15049
|
+
\`\`\`
|
|
15050
|
+
project(action="index")
|
|
15051
|
+
\`\`\`
|
|
15052
|
+
|
|
15053
|
+
---
|
|
15054
|
+
|
|
15055
|
+
## \u{1F50D} SEARCH-FIRST (No PreToolUse Hook)
|
|
15056
|
+
|
|
15057
|
+
**There is NO hook to block local tools.** You MUST self-enforce:
|
|
15058
|
+
|
|
15059
|
+
### Before ANY Search, Check Index Status:
|
|
15060
|
+
\`\`\`
|
|
15061
|
+
project(action="index_status")
|
|
15062
|
+
\`\`\`
|
|
15063
|
+
|
|
15064
|
+
This tells you:
|
|
15065
|
+
- \`indexed\`: true/false - is project indexed?
|
|
15066
|
+
- \`last_indexed_at\`: timestamp - when was it last indexed?
|
|
15067
|
+
- \`file_count\`: number - how many files indexed?
|
|
15068
|
+
|
|
15069
|
+
### Search Protocol:
|
|
15070
|
+
|
|
15071
|
+
**IF project is indexed and fresh:**
|
|
15072
|
+
\`\`\`
|
|
15073
|
+
search(mode="hybrid", query="what you're looking for")
|
|
15074
|
+
\`\`\`
|
|
15075
|
+
|
|
15076
|
+
**IF project is NOT indexed or very stale (>7 days):**
|
|
15077
|
+
\u2192 Use local tools (Glob/Grep/Read) directly
|
|
15078
|
+
\u2192 OR run \`project(action="index")\` first, then search
|
|
15079
|
+
|
|
15080
|
+
**IF ContextStream search returns 0 results or errors:**
|
|
15081
|
+
\u2192 Use local tools (Glob/Grep/Read) as fallback
|
|
15082
|
+
|
|
15083
|
+
### When Local Tools Are OK:
|
|
15084
|
+
\u2705 Project is not indexed
|
|
15085
|
+
\u2705 Index is stale/outdated (>7 days old)
|
|
15086
|
+
\u2705 ContextStream search returns 0 results
|
|
15087
|
+
\u2705 ContextStream returns errors
|
|
15088
|
+
\u2705 User explicitly requests local tools
|
|
15089
|
+
|
|
15090
|
+
### When to Use ContextStream Search:
|
|
15091
|
+
\u2705 Project is indexed and fresh
|
|
15092
|
+
\u2705 Looking for code by meaning/concept
|
|
15093
|
+
\u2705 Need semantic understanding
|
|
15094
|
+
|
|
15095
|
+
---
|
|
15096
|
+
|
|
15097
|
+
## \u{1F4BE} CONTEXT COMPACTION (No PreCompact Hook)
|
|
15098
|
+
|
|
15099
|
+
**There is NO automatic state saving before compaction.**
|
|
15100
|
+
You MUST save state manually when the conversation gets long:
|
|
15101
|
+
|
|
15102
|
+
### When to Save State:
|
|
15103
|
+
- After completing a major task
|
|
15104
|
+
- Before the conversation might be compacted
|
|
15105
|
+
- If \`context()\` returns \`context_pressure.level: "high"\`
|
|
15106
|
+
|
|
15107
|
+
### How to Save State:
|
|
15108
|
+
\`\`\`
|
|
15109
|
+
session(action="capture", event_type="session_snapshot",
|
|
15110
|
+
title="Session checkpoint",
|
|
15111
|
+
content="{ \\"summary\\": \\"what we did\\", \\"active_files\\": [...], \\"next_steps\\": [...] }")
|
|
15112
|
+
\`\`\`
|
|
15113
|
+
|
|
15114
|
+
### After Compaction (if context seems lost):
|
|
15115
|
+
\`\`\`
|
|
15116
|
+
init(folder_path="...", is_post_compact=true)
|
|
15117
|
+
\`\`\`
|
|
15118
|
+
This restores the most recent snapshot.
|
|
15119
|
+
|
|
15120
|
+
---
|
|
15121
|
+
|
|
15122
|
+
## \u{1F4CB} PLANS & TASKS (No EnterPlanMode)
|
|
15123
|
+
|
|
15124
|
+
**Always use ContextStream for planning:**
|
|
15125
|
+
|
|
15126
|
+
\`\`\`
|
|
15127
|
+
session(action="capture_plan", title="...", steps=[...])
|
|
15128
|
+
memory(action="create_task", title="...", plan_id="...")
|
|
15129
|
+
\`\`\`
|
|
15130
|
+
|
|
15131
|
+
\u274C DO NOT use built-in plan mode or write plans to markdown files.
|
|
15132
|
+
|
|
15133
|
+
---
|
|
15134
|
+
`;
|
|
15135
|
+
var NO_HOOKS_EDITORS = ["codex", "aider", "antigravity"];
|
|
12976
15136
|
var TEMPLATES = {
|
|
12977
15137
|
codex: {
|
|
12978
15138
|
filename: "AGENTS.md",
|
|
@@ -13056,6 +15216,9 @@ ${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
|
|
|
13056
15216
|
`;
|
|
13057
15217
|
content = header + content;
|
|
13058
15218
|
}
|
|
15219
|
+
if (NO_HOOKS_EDITORS.includes(editor.toLowerCase())) {
|
|
15220
|
+
content += NO_HOOKS_SUPPLEMENT;
|
|
15221
|
+
}
|
|
13059
15222
|
if (options?.additionalRules) {
|
|
13060
15223
|
content += "\n\n## Project-Specific Rules\n\n" + options.additionalRules;
|
|
13061
15224
|
}
|
|
@@ -14169,9 +16332,9 @@ function humanizeKey(raw) {
|
|
|
14169
16332
|
const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
|
|
14170
16333
|
return withSpaces.toLowerCase();
|
|
14171
16334
|
}
|
|
14172
|
-
function buildParamDescription(key,
|
|
16335
|
+
function buildParamDescription(key, path22) {
|
|
14173
16336
|
const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
|
|
14174
|
-
const parent =
|
|
16337
|
+
const parent = path22[path22.length - 1];
|
|
14175
16338
|
if (parent === "target") {
|
|
14176
16339
|
if (key === "id") return "Target identifier (module path, function id, etc.).";
|
|
14177
16340
|
if (key === "type") return "Target type (module, file, function, type, variable).";
|
|
@@ -14202,7 +16365,7 @@ function getDescription(schema) {
|
|
|
14202
16365
|
if (def?.description && def.description.trim()) return def.description;
|
|
14203
16366
|
return void 0;
|
|
14204
16367
|
}
|
|
14205
|
-
function applyParamDescriptions(schema,
|
|
16368
|
+
function applyParamDescriptions(schema, path22 = []) {
|
|
14206
16369
|
if (!(schema instanceof external_exports.ZodObject)) {
|
|
14207
16370
|
return schema;
|
|
14208
16371
|
}
|
|
@@ -14213,7 +16376,7 @@ function applyParamDescriptions(schema, path13 = []) {
|
|
|
14213
16376
|
let nextField = field;
|
|
14214
16377
|
const existingDescription = getDescription(field);
|
|
14215
16378
|
if (field instanceof external_exports.ZodObject) {
|
|
14216
|
-
const nested = applyParamDescriptions(field, [...
|
|
16379
|
+
const nested = applyParamDescriptions(field, [...path22, key]);
|
|
14217
16380
|
if (nested !== field) {
|
|
14218
16381
|
nextField = nested;
|
|
14219
16382
|
changed = true;
|
|
@@ -14225,7 +16388,7 @@ function applyParamDescriptions(schema, path13 = []) {
|
|
|
14225
16388
|
changed = true;
|
|
14226
16389
|
}
|
|
14227
16390
|
} else {
|
|
14228
|
-
nextField = nextField.describe(buildParamDescription(key,
|
|
16391
|
+
nextField = nextField.describe(buildParamDescription(key, path22));
|
|
14229
16392
|
changed = true;
|
|
14230
16393
|
}
|
|
14231
16394
|
nextShape[key] = nextField;
|
|
@@ -15145,10 +17308,12 @@ function registerTools(server, client, sessionManager) {
|
|
|
15145
17308
|
function isTeamPlanCached() {
|
|
15146
17309
|
return teamStatus.checked ? teamStatus.isTeamPlan : false;
|
|
15147
17310
|
}
|
|
15148
|
-
|
|
17311
|
+
const INTEGRATION_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
17312
|
+
let integrationStatus = { checked: false, checkedAt: 0, slack: false, github: false, notion: false };
|
|
15149
17313
|
let toolsListChangedNotified = false;
|
|
15150
17314
|
async function checkIntegrationStatus(workspaceId) {
|
|
15151
|
-
|
|
17315
|
+
const cacheAge = Date.now() - integrationStatus.checkedAt;
|
|
17316
|
+
if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId && cacheAge < INTEGRATION_CACHE_TTL_MS) {
|
|
15152
17317
|
return { slack: integrationStatus.slack, github: integrationStatus.github, notion: integrationStatus.notion };
|
|
15153
17318
|
}
|
|
15154
17319
|
if (!workspaceId) {
|
|
@@ -15156,17 +17321,19 @@ function registerTools(server, client, sessionManager) {
|
|
|
15156
17321
|
}
|
|
15157
17322
|
try {
|
|
15158
17323
|
const status = await client.integrationsStatus({ workspace_id: workspaceId });
|
|
17324
|
+
const isConnectedStatus = (s) => s === "connected" || s === "syncing";
|
|
15159
17325
|
const slackConnected = status?.some(
|
|
15160
|
-
(s) => s.provider === "slack" && s.status
|
|
17326
|
+
(s) => s.provider === "slack" && isConnectedStatus(s.status)
|
|
15161
17327
|
) ?? false;
|
|
15162
17328
|
const githubConnected = status?.some(
|
|
15163
|
-
(s) => s.provider === "github" && s.status
|
|
17329
|
+
(s) => s.provider === "github" && isConnectedStatus(s.status)
|
|
15164
17330
|
) ?? false;
|
|
15165
17331
|
const notionConnected = status?.some(
|
|
15166
|
-
(s) => s.provider === "notion" && s.status
|
|
17332
|
+
(s) => s.provider === "notion" && isConnectedStatus(s.status)
|
|
15167
17333
|
) ?? false;
|
|
15168
17334
|
integrationStatus = {
|
|
15169
17335
|
checked: true,
|
|
17336
|
+
checkedAt: Date.now(),
|
|
15170
17337
|
slack: slackConnected,
|
|
15171
17338
|
github: githubConnected,
|
|
15172
17339
|
notion: notionConnected,
|
|
@@ -15185,6 +17352,7 @@ function registerTools(server, client, sessionManager) {
|
|
|
15185
17352
|
const hadNotion = integrationStatus.notion;
|
|
15186
17353
|
integrationStatus = {
|
|
15187
17354
|
checked: true,
|
|
17355
|
+
checkedAt: Date.now(),
|
|
15188
17356
|
slack: status.slack,
|
|
15189
17357
|
github: status.github,
|
|
15190
17358
|
notion: status.notion,
|
|
@@ -19798,14 +21966,15 @@ Use this to verify integrations are healthy and syncing properly.`,
|
|
|
19798
21966
|
}
|
|
19799
21967
|
const result = await client.integrationsStatus({ workspace_id: workspaceId });
|
|
19800
21968
|
if (AUTO_HIDE_INTEGRATIONS) {
|
|
21969
|
+
const isConnectedStatus = (s) => s === "connected" || s === "syncing";
|
|
19801
21970
|
const slackConnected = result?.some(
|
|
19802
|
-
(s) => s.provider === "slack" && s.status
|
|
21971
|
+
(s) => s.provider === "slack" && isConnectedStatus(s.status)
|
|
19803
21972
|
) ?? false;
|
|
19804
21973
|
const githubConnected = result?.some(
|
|
19805
|
-
(s) => s.provider === "github" && s.status
|
|
21974
|
+
(s) => s.provider === "github" && isConnectedStatus(s.status)
|
|
19806
21975
|
) ?? false;
|
|
19807
21976
|
const notionConnected = result?.some(
|
|
19808
|
-
(s) => s.provider === "notion" && s.status
|
|
21977
|
+
(s) => s.provider === "notion" && isConnectedStatus(s.status)
|
|
19809
21978
|
) ?? false;
|
|
19810
21979
|
updateIntegrationStatus({ slack: slackConnected, github: githubConnected, notion: notionConnected }, workspaceId);
|
|
19811
21980
|
}
|
|
@@ -23216,13 +25385,13 @@ Example workflow:
|
|
|
23216
25385
|
);
|
|
23217
25386
|
}
|
|
23218
25387
|
if (input.file_path) {
|
|
23219
|
-
const
|
|
25388
|
+
const fs21 = await import("fs/promises");
|
|
23220
25389
|
const pathModule = await import("path");
|
|
23221
25390
|
const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
|
|
23222
25391
|
const resolvedPath = pathModule.resolve(filePath);
|
|
23223
25392
|
let fileStats;
|
|
23224
25393
|
try {
|
|
23225
|
-
fileStats = await
|
|
25394
|
+
fileStats = await fs21.stat(resolvedPath);
|
|
23226
25395
|
} catch {
|
|
23227
25396
|
return errorResult(`File not found: ${resolvedPath}`);
|
|
23228
25397
|
}
|
|
@@ -23269,7 +25438,7 @@ Example workflow:
|
|
|
23269
25438
|
mime_type: mimeType,
|
|
23270
25439
|
tags: input.tags
|
|
23271
25440
|
});
|
|
23272
|
-
const fileBuffer = await
|
|
25441
|
+
const fileBuffer = await fs21.readFile(resolvedPath);
|
|
23273
25442
|
const uploadResponse = await fetch(uploadInit.upload_url, {
|
|
23274
25443
|
method: "PUT",
|
|
23275
25444
|
headers: uploadInit.headers,
|
|
@@ -24676,8 +26845,8 @@ var SessionManager = class _SessionManager {
|
|
|
24676
26845
|
/**
|
|
24677
26846
|
* Set the folder path hint (can be passed from tools that know the workspace path)
|
|
24678
26847
|
*/
|
|
24679
|
-
setFolderPath(
|
|
24680
|
-
this.folderPath =
|
|
26848
|
+
setFolderPath(path22) {
|
|
26849
|
+
this.folderPath = path22;
|
|
24681
26850
|
}
|
|
24682
26851
|
/**
|
|
24683
26852
|
* Mark that context_smart has been called in this session.
|
|
@@ -24867,7 +27036,7 @@ var SessionManager = class _SessionManager {
|
|
|
24867
27036
|
}
|
|
24868
27037
|
if (this.ideRoots.length === 0) {
|
|
24869
27038
|
const cwd = process.cwd();
|
|
24870
|
-
const
|
|
27039
|
+
const fs21 = await import("fs");
|
|
24871
27040
|
const projectIndicators = [
|
|
24872
27041
|
".git",
|
|
24873
27042
|
"package.json",
|
|
@@ -24877,7 +27046,7 @@ var SessionManager = class _SessionManager {
|
|
|
24877
27046
|
];
|
|
24878
27047
|
const hasProjectIndicator = projectIndicators.some((f) => {
|
|
24879
27048
|
try {
|
|
24880
|
-
return
|
|
27049
|
+
return fs21.existsSync(`${cwd}/${f}`);
|
|
24881
27050
|
} catch {
|
|
24882
27051
|
return false;
|
|
24883
27052
|
}
|
|
@@ -25454,9 +27623,9 @@ async function runHttpGateway() {
|
|
|
25454
27623
|
}
|
|
25455
27624
|
|
|
25456
27625
|
// src/index.ts
|
|
25457
|
-
import { existsSync as
|
|
25458
|
-
import { homedir as
|
|
25459
|
-
import { join as
|
|
27626
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
27627
|
+
import { homedir as homedir19 } from "os";
|
|
27628
|
+
import { join as join23 } from "path";
|
|
25460
27629
|
|
|
25461
27630
|
// src/setup.ts
|
|
25462
27631
|
import * as fs7 from "node:fs/promises";
|
|
@@ -26286,10 +28455,10 @@ Code: ${device.user_code}`);
|
|
|
26286
28455
|
if (poll && poll.status === "pending") {
|
|
26287
28456
|
const intervalSeconds = typeof poll.interval === "number" ? poll.interval : 5;
|
|
26288
28457
|
const waitMs = Math.max(1, intervalSeconds) * 1e3;
|
|
26289
|
-
await new Promise((
|
|
28458
|
+
await new Promise((resolve16) => setTimeout(resolve16, waitMs));
|
|
26290
28459
|
continue;
|
|
26291
28460
|
}
|
|
26292
|
-
await new Promise((
|
|
28461
|
+
await new Promise((resolve16) => setTimeout(resolve16, 1e3));
|
|
26293
28462
|
}
|
|
26294
28463
|
if (!accessToken) {
|
|
26295
28464
|
throw new Error(
|
|
@@ -26854,12 +29023,12 @@ Applying to ${projects.length} project(s)...`);
|
|
|
26854
29023
|
// src/index.ts
|
|
26855
29024
|
var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
|
|
26856
29025
|
function showFirstRunMessage() {
|
|
26857
|
-
const configDir =
|
|
26858
|
-
const starShownFile =
|
|
26859
|
-
if (
|
|
29026
|
+
const configDir = join23(homedir19(), ".contextstream");
|
|
29027
|
+
const starShownFile = join23(configDir, ".star-shown");
|
|
29028
|
+
if (existsSync17(starShownFile)) {
|
|
26860
29029
|
return;
|
|
26861
29030
|
}
|
|
26862
|
-
if (!
|
|
29031
|
+
if (!existsSync17(configDir)) {
|
|
26863
29032
|
try {
|
|
26864
29033
|
mkdirSync5(configDir, { recursive: true });
|
|
26865
29034
|
} catch {
|
|
@@ -26890,13 +29059,26 @@ Usage:
|
|
|
26890
29059
|
|
|
26891
29060
|
Commands:
|
|
26892
29061
|
setup Interactive onboarding wizard (rules + workspace mapping)
|
|
29062
|
+
verify-key [--json] Verify API key and show account info
|
|
29063
|
+
update-hooks [flags] Update hooks for all editors (Claude, Cursor, Cline, Roo, Kilo)
|
|
29064
|
+
--scope=global Install hooks globally (default)
|
|
29065
|
+
--scope=project, -p Install hooks for current project only
|
|
29066
|
+
--path=/path Specify project path (implies --scope=project)
|
|
26893
29067
|
http Run HTTP MCP gateway (streamable HTTP transport)
|
|
26894
29068
|
hook pre-tool-use PreToolUse hook - blocks discovery tools, redirects to ContextStream
|
|
26895
29069
|
hook user-prompt-submit UserPromptSubmit hook - injects ContextStream rules reminder
|
|
26896
29070
|
hook media-aware Media-aware hook - detects media prompts, injects media tool guidance
|
|
26897
29071
|
hook pre-compact PreCompact hook - saves conversation state before compaction
|
|
29072
|
+
hook post-compact PostCompact hook - restores context after compaction
|
|
26898
29073
|
hook post-write PostToolUse hook - real-time file indexing after Edit/Write
|
|
26899
29074
|
hook auto-rules PostToolUse hook - auto-updates rules when behind (silent)
|
|
29075
|
+
hook on-bash PostToolUse hook - captures bash commands, learns from errors
|
|
29076
|
+
hook on-task PostToolUse hook - tracks Task agent work
|
|
29077
|
+
hook on-read PostToolUse hook - tracks file exploration (Read/Glob/Grep)
|
|
29078
|
+
hook on-web PostToolUse hook - captures web research (WebFetch/WebSearch)
|
|
29079
|
+
hook session-init SessionStart hook - full context injection on session start
|
|
29080
|
+
hook session-end Stop hook - finalizes session, saves state
|
|
29081
|
+
hook on-save-intent UserPromptSubmit hook - redirects doc saves to ContextStream
|
|
26900
29082
|
|
|
26901
29083
|
Environment variables:
|
|
26902
29084
|
CONTEXTSTREAM_API_URL Base API URL (e.g. https://api.contextstream.io)
|
|
@@ -27005,12 +29187,90 @@ async function main() {
|
|
|
27005
29187
|
await runAutoRulesHook2();
|
|
27006
29188
|
return;
|
|
27007
29189
|
}
|
|
29190
|
+
case "post-compact": {
|
|
29191
|
+
const { runPostCompactHook: runPostCompactHook2 } = await Promise.resolve().then(() => (init_post_compact(), post_compact_exports));
|
|
29192
|
+
await runPostCompactHook2();
|
|
29193
|
+
return;
|
|
29194
|
+
}
|
|
29195
|
+
case "on-bash": {
|
|
29196
|
+
const { runOnBashHook: runOnBashHook2 } = await Promise.resolve().then(() => (init_on_bash(), on_bash_exports));
|
|
29197
|
+
await runOnBashHook2();
|
|
29198
|
+
return;
|
|
29199
|
+
}
|
|
29200
|
+
case "on-task": {
|
|
29201
|
+
const { runOnTaskHook: runOnTaskHook2 } = await Promise.resolve().then(() => (init_on_task(), on_task_exports));
|
|
29202
|
+
await runOnTaskHook2();
|
|
29203
|
+
return;
|
|
29204
|
+
}
|
|
29205
|
+
case "on-read": {
|
|
29206
|
+
const { runOnReadHook: runOnReadHook2 } = await Promise.resolve().then(() => (init_on_read(), on_read_exports));
|
|
29207
|
+
await runOnReadHook2();
|
|
29208
|
+
return;
|
|
29209
|
+
}
|
|
29210
|
+
case "on-web": {
|
|
29211
|
+
const { runOnWebHook: runOnWebHook2 } = await Promise.resolve().then(() => (init_on_web(), on_web_exports));
|
|
29212
|
+
await runOnWebHook2();
|
|
29213
|
+
return;
|
|
29214
|
+
}
|
|
29215
|
+
case "session-init": {
|
|
29216
|
+
const { runSessionInitHook: runSessionInitHook2 } = await Promise.resolve().then(() => (init_session_init(), session_init_exports));
|
|
29217
|
+
await runSessionInitHook2();
|
|
29218
|
+
return;
|
|
29219
|
+
}
|
|
29220
|
+
case "session-end": {
|
|
29221
|
+
const { runSessionEndHook: runSessionEndHook2 } = await Promise.resolve().then(() => (init_session_end(), session_end_exports));
|
|
29222
|
+
await runSessionEndHook2();
|
|
29223
|
+
return;
|
|
29224
|
+
}
|
|
29225
|
+
case "on-save-intent": {
|
|
29226
|
+
const { runOnSaveIntentHook: runOnSaveIntentHook2 } = await Promise.resolve().then(() => (init_on_save_intent(), on_save_intent_exports));
|
|
29227
|
+
await runOnSaveIntentHook2();
|
|
29228
|
+
return;
|
|
29229
|
+
}
|
|
27008
29230
|
default:
|
|
27009
29231
|
console.error(`Unknown hook: ${hookName}`);
|
|
27010
|
-
console.error("Available hooks: pre-tool-use, user-prompt-submit, media-aware, pre-compact, post-write, auto-rules");
|
|
29232
|
+
console.error("Available hooks: pre-tool-use, user-prompt-submit, media-aware, pre-compact, post-compact, post-write, auto-rules, on-bash, on-task, on-read, on-web, session-init, session-end, on-save-intent");
|
|
27011
29233
|
process.exit(1);
|
|
27012
29234
|
}
|
|
27013
29235
|
}
|
|
29236
|
+
if (args[0] === "verify-key") {
|
|
29237
|
+
const { runVerifyKey: runVerifyKey2 } = await Promise.resolve().then(() => (init_verify_key(), verify_key_exports));
|
|
29238
|
+
const outputJson = args.includes("--json");
|
|
29239
|
+
const result = await runVerifyKey2(outputJson);
|
|
29240
|
+
process.exit(result.valid ? 0 : 1);
|
|
29241
|
+
}
|
|
29242
|
+
if (args[0] === "update-hooks") {
|
|
29243
|
+
const { installAllEditorHooks: installAllEditorHooks2 } = await Promise.resolve().then(() => (init_hooks_config(), hooks_config_exports));
|
|
29244
|
+
let scope = "global";
|
|
29245
|
+
let projectPath;
|
|
29246
|
+
for (const arg of args.slice(1)) {
|
|
29247
|
+
if (arg === "--scope=project" || arg === "-p") {
|
|
29248
|
+
scope = "project";
|
|
29249
|
+
projectPath = projectPath || process.cwd();
|
|
29250
|
+
} else if (arg === "--scope=global" || arg === "-g") {
|
|
29251
|
+
scope = "global";
|
|
29252
|
+
} else if (arg.startsWith("--path=")) {
|
|
29253
|
+
projectPath = arg.replace("--path=", "");
|
|
29254
|
+
scope = "project";
|
|
29255
|
+
}
|
|
29256
|
+
}
|
|
29257
|
+
const scopeLabel = scope === "project" ? `project (${projectPath || process.cwd()})` : "global";
|
|
29258
|
+
console.error(`Updating hooks for all editors (${scopeLabel})...`);
|
|
29259
|
+
try {
|
|
29260
|
+
const results = await installAllEditorHooks2({
|
|
29261
|
+
scope,
|
|
29262
|
+
projectPath: scope === "project" ? projectPath || process.cwd() : void 0
|
|
29263
|
+
});
|
|
29264
|
+
for (const result of results) {
|
|
29265
|
+
console.error(`\u2713 ${result.editor}: ${result.installed.length} hooks installed`);
|
|
29266
|
+
}
|
|
29267
|
+
console.error("\u2713 Hooks updated successfully");
|
|
29268
|
+
} catch (error) {
|
|
29269
|
+
console.error("Failed to update hooks:", error);
|
|
29270
|
+
process.exit(1);
|
|
29271
|
+
}
|
|
29272
|
+
return;
|
|
29273
|
+
}
|
|
27014
29274
|
if (!process.env.CONTEXTSTREAM_API_KEY && !process.env.CONTEXTSTREAM_JWT) {
|
|
27015
29275
|
const saved = await readSavedCredentials();
|
|
27016
29276
|
if (saved) {
|