@contextstream/mcp-server 0.4.49 → 0.4.51
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/README.md +27 -0
- package/dist/hooks/auto-rules.js +136 -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/post-write.js +0 -0
- package/dist/hooks/pre-compact.js +100 -11
- package/dist/hooks/runner.js +2889 -0
- package/dist/hooks/session-end.js +191 -0
- package/dist/hooks/session-init.js +174 -0
- package/dist/index.js +2458 -252
- 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(path21, 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(path21);
|
|
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 = (path21, originalPath, doThrow) => {
|
|
369
|
+
if (!isString(path21)) {
|
|
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 (!path21) {
|
|
376
376
|
return doThrow(`path must not be empty`, TypeError);
|
|
377
377
|
}
|
|
378
|
-
if (checkPath.isNotRelative(
|
|
378
|
+
if (checkPath.isNotRelative(path21)) {
|
|
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 = (path21) => REGEX_TEST_INVALID_PATH.test(path21);
|
|
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 path21 = originalPath && checkPath.convert(originalPath);
|
|
418
418
|
checkPath(
|
|
419
|
-
|
|
419
|
+
path21,
|
|
420
420
|
originalPath,
|
|
421
421
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
422
422
|
);
|
|
423
|
-
return this._t(
|
|
423
|
+
return this._t(path21, cache, checkUnignored, slices);
|
|
424
424
|
}
|
|
425
|
-
checkIgnore(
|
|
426
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
427
|
-
return this.test(
|
|
425
|
+
checkIgnore(path21) {
|
|
426
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
|
|
427
|
+
return this.test(path21);
|
|
428
428
|
}
|
|
429
|
-
const slices =
|
|
429
|
+
const slices = path21.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(path21, false, MODE_CHECK_IGNORE);
|
|
443
443
|
}
|
|
444
|
-
_t(
|
|
445
|
-
if (
|
|
446
|
-
return cache[
|
|
444
|
+
_t(path21, cache, checkUnignored, slices) {
|
|
445
|
+
if (path21 in cache) {
|
|
446
|
+
return cache[path21];
|
|
447
447
|
}
|
|
448
448
|
if (!slices) {
|
|
449
|
-
slices =
|
|
449
|
+
slices = path21.split(SLASH).filter(Boolean);
|
|
450
450
|
}
|
|
451
451
|
slices.pop();
|
|
452
452
|
if (!slices.length) {
|
|
453
|
-
return cache[
|
|
453
|
+
return cache[path21] = this._rules.test(path21, 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[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
462
462
|
}
|
|
463
|
-
ignores(
|
|
464
|
-
return this._test(
|
|
463
|
+
ignores(path21) {
|
|
464
|
+
return this._test(path21, this._ignoreCache, false).ignored;
|
|
465
465
|
}
|
|
466
466
|
createFilter() {
|
|
467
|
-
return (
|
|
467
|
+
return (path21) => !this.ignores(path21);
|
|
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(path21) {
|
|
474
|
+
return this._test(path21, this._testCache, true);
|
|
475
475
|
}
|
|
476
476
|
};
|
|
477
477
|
var factory = (options) => new Ignore2(options);
|
|
478
|
-
var isPathValid = (
|
|
478
|
+
var isPathValid = (path21) => checkPath(path21 && checkPath.convert(path21), path21, 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 = (path21) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path21) || isNotRelative(path21);
|
|
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,19 @@ __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
|
+
try {
|
|
546
|
+
const __dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
547
|
+
const indexPath = path5.join(__dirname, "index.js");
|
|
548
|
+
const fs20 = __require("node:fs");
|
|
549
|
+
if (fs20.existsSync(indexPath)) {
|
|
550
|
+
return `node ${indexPath} hook ${hookName}`;
|
|
551
|
+
}
|
|
552
|
+
} catch {
|
|
553
|
+
}
|
|
554
|
+
return `npx @contextstream/mcp-server hook ${hookName}`;
|
|
555
|
+
}
|
|
542
556
|
function getClaudeSettingsPath(scope, projectPath) {
|
|
543
557
|
if (scope === "user") {
|
|
544
558
|
return path5.join(homedir2(), ".claude", "settings.json");
|
|
@@ -558,19 +572,31 @@ function buildHooksConfig(options) {
|
|
|
558
572
|
hooks: [
|
|
559
573
|
{
|
|
560
574
|
type: "command",
|
|
561
|
-
command: "
|
|
575
|
+
command: getHookCommand("user-prompt-submit"),
|
|
562
576
|
timeout: 5
|
|
563
577
|
}
|
|
564
578
|
]
|
|
565
579
|
}
|
|
566
580
|
];
|
|
581
|
+
if (options?.includeOnSaveIntent !== false) {
|
|
582
|
+
userPromptHooks.push({
|
|
583
|
+
matcher: "*",
|
|
584
|
+
hooks: [
|
|
585
|
+
{
|
|
586
|
+
type: "command",
|
|
587
|
+
command: getHookCommand("on-save-intent"),
|
|
588
|
+
timeout: 5
|
|
589
|
+
}
|
|
590
|
+
]
|
|
591
|
+
});
|
|
592
|
+
}
|
|
567
593
|
if (options?.includeMediaAware !== false) {
|
|
568
594
|
userPromptHooks.push({
|
|
569
595
|
matcher: "*",
|
|
570
596
|
hooks: [
|
|
571
597
|
{
|
|
572
598
|
type: "command",
|
|
573
|
-
command: "
|
|
599
|
+
command: getHookCommand("media-aware"),
|
|
574
600
|
timeout: 5
|
|
575
601
|
}
|
|
576
602
|
]
|
|
@@ -583,7 +609,7 @@ function buildHooksConfig(options) {
|
|
|
583
609
|
hooks: [
|
|
584
610
|
{
|
|
585
611
|
type: "command",
|
|
586
|
-
command: "
|
|
612
|
+
command: getHookCommand("pre-tool-use"),
|
|
587
613
|
timeout: 5
|
|
588
614
|
}
|
|
589
615
|
]
|
|
@@ -594,12 +620,39 @@ function buildHooksConfig(options) {
|
|
|
594
620
|
if (options?.includePreCompact !== false) {
|
|
595
621
|
config.PreCompact = [
|
|
596
622
|
{
|
|
597
|
-
// Match both manual (/compact) and automatic compaction
|
|
598
623
|
matcher: "*",
|
|
599
624
|
hooks: [
|
|
600
625
|
{
|
|
601
626
|
type: "command",
|
|
602
|
-
command: "
|
|
627
|
+
command: getHookCommand("pre-compact"),
|
|
628
|
+
timeout: 10
|
|
629
|
+
}
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
];
|
|
633
|
+
}
|
|
634
|
+
if (options?.includeSessionInit !== false) {
|
|
635
|
+
config.SessionStart = [
|
|
636
|
+
{
|
|
637
|
+
matcher: "*",
|
|
638
|
+
hooks: [
|
|
639
|
+
{
|
|
640
|
+
type: "command",
|
|
641
|
+
command: getHookCommand("session-init"),
|
|
642
|
+
timeout: 10
|
|
643
|
+
}
|
|
644
|
+
]
|
|
645
|
+
}
|
|
646
|
+
];
|
|
647
|
+
}
|
|
648
|
+
if (options?.includeSessionEnd !== false) {
|
|
649
|
+
config.Stop = [
|
|
650
|
+
{
|
|
651
|
+
matcher: "*",
|
|
652
|
+
hooks: [
|
|
653
|
+
{
|
|
654
|
+
type: "command",
|
|
655
|
+
command: getHookCommand("session-end"),
|
|
603
656
|
timeout: 10
|
|
604
657
|
}
|
|
605
658
|
]
|
|
@@ -613,7 +666,7 @@ function buildHooksConfig(options) {
|
|
|
613
666
|
hooks: [
|
|
614
667
|
{
|
|
615
668
|
type: "command",
|
|
616
|
-
command: "
|
|
669
|
+
command: getHookCommand("post-write"),
|
|
617
670
|
timeout: 10
|
|
618
671
|
}
|
|
619
672
|
]
|
|
@@ -625,12 +678,60 @@ function buildHooksConfig(options) {
|
|
|
625
678
|
hooks: [
|
|
626
679
|
{
|
|
627
680
|
type: "command",
|
|
628
|
-
command: "
|
|
681
|
+
command: getHookCommand("auto-rules"),
|
|
629
682
|
timeout: 15
|
|
630
683
|
}
|
|
631
684
|
]
|
|
632
685
|
});
|
|
633
686
|
}
|
|
687
|
+
if (options?.includeOnBash !== false) {
|
|
688
|
+
postToolUseHooks.push({
|
|
689
|
+
matcher: "Bash",
|
|
690
|
+
hooks: [
|
|
691
|
+
{
|
|
692
|
+
type: "command",
|
|
693
|
+
command: getHookCommand("on-bash"),
|
|
694
|
+
timeout: 5
|
|
695
|
+
}
|
|
696
|
+
]
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
if (options?.includeOnTask !== false) {
|
|
700
|
+
postToolUseHooks.push({
|
|
701
|
+
matcher: "Task",
|
|
702
|
+
hooks: [
|
|
703
|
+
{
|
|
704
|
+
type: "command",
|
|
705
|
+
command: getHookCommand("on-task"),
|
|
706
|
+
timeout: 5
|
|
707
|
+
}
|
|
708
|
+
]
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (options?.includeOnRead !== false) {
|
|
712
|
+
postToolUseHooks.push({
|
|
713
|
+
matcher: "Read|Glob|Grep",
|
|
714
|
+
hooks: [
|
|
715
|
+
{
|
|
716
|
+
type: "command",
|
|
717
|
+
command: getHookCommand("on-read"),
|
|
718
|
+
timeout: 5
|
|
719
|
+
}
|
|
720
|
+
]
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
if (options?.includeOnWeb !== false) {
|
|
724
|
+
postToolUseHooks.push({
|
|
725
|
+
matcher: "WebFetch|WebSearch",
|
|
726
|
+
hooks: [
|
|
727
|
+
{
|
|
728
|
+
type: "command",
|
|
729
|
+
command: getHookCommand("on-web"),
|
|
730
|
+
timeout: 5
|
|
731
|
+
}
|
|
732
|
+
]
|
|
733
|
+
});
|
|
734
|
+
}
|
|
634
735
|
if (postToolUseHooks.length > 0) {
|
|
635
736
|
config.PostToolUse = postToolUseHooks;
|
|
636
737
|
}
|
|
@@ -640,17 +741,17 @@ async function installHookScripts(options) {
|
|
|
640
741
|
const hooksDir = getHooksDir();
|
|
641
742
|
await fs4.mkdir(hooksDir, { recursive: true });
|
|
642
743
|
const result = {
|
|
643
|
-
preToolUse: "
|
|
644
|
-
userPrompt: "
|
|
744
|
+
preToolUse: getHookCommand("pre-tool-use"),
|
|
745
|
+
userPrompt: getHookCommand("user-prompt-submit")
|
|
645
746
|
};
|
|
646
747
|
if (options?.includePreCompact !== false) {
|
|
647
|
-
result.preCompact = "
|
|
748
|
+
result.preCompact = getHookCommand("pre-compact");
|
|
648
749
|
}
|
|
649
750
|
if (options?.includeMediaAware !== false) {
|
|
650
|
-
result.mediaAware = "
|
|
751
|
+
result.mediaAware = getHookCommand("media-aware");
|
|
651
752
|
}
|
|
652
753
|
if (options?.includeAutoRules !== false) {
|
|
653
|
-
result.autoRules = "
|
|
754
|
+
result.autoRules = getHookCommand("auto-rules");
|
|
654
755
|
}
|
|
655
756
|
return result;
|
|
656
757
|
}
|
|
@@ -686,20 +787,20 @@ function mergeHooksIntoSettings(existingSettings, newHooks) {
|
|
|
686
787
|
async function installClaudeCodeHooks(options) {
|
|
687
788
|
const result = { scripts: [], settings: [] };
|
|
688
789
|
result.scripts.push(
|
|
689
|
-
"
|
|
690
|
-
"
|
|
790
|
+
getHookCommand("pre-tool-use"),
|
|
791
|
+
getHookCommand("user-prompt-submit")
|
|
691
792
|
);
|
|
692
793
|
if (options.includePreCompact !== false) {
|
|
693
|
-
result.scripts.push("
|
|
794
|
+
result.scripts.push(getHookCommand("pre-compact"));
|
|
694
795
|
}
|
|
695
796
|
if (options.includeMediaAware !== false) {
|
|
696
|
-
result.scripts.push("
|
|
797
|
+
result.scripts.push(getHookCommand("media-aware"));
|
|
697
798
|
}
|
|
698
799
|
if (options.includePostWrite !== false) {
|
|
699
|
-
result.scripts.push("
|
|
800
|
+
result.scripts.push(getHookCommand("post-write"));
|
|
700
801
|
}
|
|
701
802
|
if (options.includeAutoRules !== false) {
|
|
702
|
-
result.scripts.push("
|
|
803
|
+
result.scripts.push(getHookCommand("auto-rules"));
|
|
703
804
|
}
|
|
704
805
|
const hooksConfig = buildHooksConfig({
|
|
705
806
|
includePreCompact: options.includePreCompact,
|
|
@@ -977,6 +1078,8 @@ async function installCursorHookScripts(options) {
|
|
|
977
1078
|
};
|
|
978
1079
|
const filteredPreToolUse = filterContextStreamHooks(existingConfig.hooks.preToolUse);
|
|
979
1080
|
const filteredBeforeSubmit = filterContextStreamHooks(existingConfig.hooks.beforeSubmitPrompt);
|
|
1081
|
+
const preToolUseCommand = getHookCommand("pre-tool-use");
|
|
1082
|
+
const userPromptCommand = getHookCommand("user-prompt-submit");
|
|
980
1083
|
const config = {
|
|
981
1084
|
version: 1,
|
|
982
1085
|
hooks: {
|
|
@@ -984,7 +1087,7 @@ async function installCursorHookScripts(options) {
|
|
|
984
1087
|
preToolUse: [
|
|
985
1088
|
...filteredPreToolUse,
|
|
986
1089
|
{
|
|
987
|
-
command:
|
|
1090
|
+
command: preToolUseCommand,
|
|
988
1091
|
type: "command",
|
|
989
1092
|
timeout: 5,
|
|
990
1093
|
matcher: { tool_name: "Glob|Grep|search_files|list_files|ripgrep" }
|
|
@@ -993,7 +1096,7 @@ async function installCursorHookScripts(options) {
|
|
|
993
1096
|
beforeSubmitPrompt: [
|
|
994
1097
|
...filteredBeforeSubmit,
|
|
995
1098
|
{
|
|
996
|
-
command:
|
|
1099
|
+
command: userPromptCommand,
|
|
997
1100
|
type: "command",
|
|
998
1101
|
timeout: 5
|
|
999
1102
|
}
|
|
@@ -1003,8 +1106,8 @@ async function installCursorHookScripts(options) {
|
|
|
1003
1106
|
await writeCursorHooksConfig(config, options.scope, options.projectPath);
|
|
1004
1107
|
const configPath = getCursorHooksConfigPath(options.scope, options.projectPath);
|
|
1005
1108
|
return {
|
|
1006
|
-
preToolUse:
|
|
1007
|
-
beforeSubmitPrompt:
|
|
1109
|
+
preToolUse: preToolUseCommand,
|
|
1110
|
+
beforeSubmitPrompt: userPromptCommand,
|
|
1008
1111
|
config: configPath
|
|
1009
1112
|
};
|
|
1010
1113
|
}
|
|
@@ -1813,11 +1916,13 @@ esac
|
|
|
1813
1916
|
|
|
1814
1917
|
exit 0
|
|
1815
1918
|
`;
|
|
1816
|
-
CLINE_HOOK_WRAPPER = (hookName) =>
|
|
1919
|
+
CLINE_HOOK_WRAPPER = (hookName) => {
|
|
1920
|
+
const command = getHookCommand(hookName);
|
|
1921
|
+
return `#!/bin/bash
|
|
1817
1922
|
# ContextStream ${hookName} Hook Wrapper for Cline/Roo/Kilo Code
|
|
1818
|
-
|
|
1819
|
-
exec npx @contextstream/mcp-server hook ${hookName}
|
|
1923
|
+
exec ${command}
|
|
1820
1924
|
`;
|
|
1925
|
+
};
|
|
1821
1926
|
CURSOR_PRETOOLUSE_HOOK_SCRIPT = `#!/usr/bin/env python3
|
|
1822
1927
|
"""
|
|
1823
1928
|
ContextStream PreToolUse Hook for Cursor
|
|
@@ -2825,6 +2930,9 @@ function parseTranscript(transcriptPath) {
|
|
|
2825
2930
|
const activeFiles = /* @__PURE__ */ new Set();
|
|
2826
2931
|
const recentMessages = [];
|
|
2827
2932
|
const toolCalls = [];
|
|
2933
|
+
const messages = [];
|
|
2934
|
+
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2935
|
+
let firstTimestamp = true;
|
|
2828
2936
|
try {
|
|
2829
2937
|
const content = fs10.readFileSync(transcriptPath, "utf-8");
|
|
2830
2938
|
const lines = content.split("\n");
|
|
@@ -2833,6 +2941,11 @@ function parseTranscript(transcriptPath) {
|
|
|
2833
2941
|
try {
|
|
2834
2942
|
const entry = JSON.parse(line);
|
|
2835
2943
|
const msgType = entry.type || "";
|
|
2944
|
+
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
2945
|
+
if (firstTimestamp && entry.timestamp) {
|
|
2946
|
+
startedAt = entry.timestamp;
|
|
2947
|
+
firstTimestamp = false;
|
|
2948
|
+
}
|
|
2836
2949
|
if (msgType === "tool_use") {
|
|
2837
2950
|
const toolName = entry.name || "";
|
|
2838
2951
|
const toolInput = entry.input || {};
|
|
@@ -2848,11 +2961,40 @@ function parseTranscript(transcriptPath) {
|
|
|
2848
2961
|
activeFiles.add(`[glob:${pattern}]`);
|
|
2849
2962
|
}
|
|
2850
2963
|
}
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2964
|
+
messages.push({
|
|
2965
|
+
role: "assistant",
|
|
2966
|
+
content: `[Tool: ${toolName}]`,
|
|
2967
|
+
timestamp,
|
|
2968
|
+
tool_calls: { name: toolName, input: toolInput }
|
|
2969
|
+
});
|
|
2970
|
+
} else if (msgType === "tool_result") {
|
|
2971
|
+
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
2972
|
+
messages.push({
|
|
2973
|
+
role: "tool",
|
|
2974
|
+
content: resultContent,
|
|
2975
|
+
timestamp,
|
|
2976
|
+
tool_results: { name: entry.name }
|
|
2977
|
+
});
|
|
2978
|
+
} else if (msgType === "user" || entry.role === "user") {
|
|
2979
|
+
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
2980
|
+
if (userContent) {
|
|
2981
|
+
messages.push({
|
|
2982
|
+
role: "user",
|
|
2983
|
+
content: userContent,
|
|
2984
|
+
timestamp
|
|
2985
|
+
});
|
|
2986
|
+
}
|
|
2987
|
+
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
2988
|
+
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
2989
|
+
if (assistantContent) {
|
|
2990
|
+
messages.push({
|
|
2991
|
+
role: "assistant",
|
|
2992
|
+
content: assistantContent,
|
|
2993
|
+
timestamp
|
|
2994
|
+
});
|
|
2995
|
+
if (assistantContent.length > 50) {
|
|
2996
|
+
recentMessages.push(assistantContent.slice(0, 500));
|
|
2997
|
+
}
|
|
2856
2998
|
}
|
|
2857
2999
|
}
|
|
2858
3000
|
} catch {
|
|
@@ -2865,10 +3007,56 @@ function parseTranscript(transcriptPath) {
|
|
|
2865
3007
|
activeFiles: Array.from(activeFiles).slice(-20),
|
|
2866
3008
|
// Last 20 files
|
|
2867
3009
|
toolCallCount: toolCalls.length,
|
|
2868
|
-
messageCount:
|
|
2869
|
-
lastTools: toolCalls.slice(-10).map((t) => t.name)
|
|
3010
|
+
messageCount: messages.length,
|
|
3011
|
+
lastTools: toolCalls.slice(-10).map((t) => t.name),
|
|
2870
3012
|
// Last 10 tool names
|
|
3013
|
+
messages,
|
|
3014
|
+
startedAt
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
3018
|
+
if (!API_KEY2) {
|
|
3019
|
+
return { success: false, message: "No API key configured" };
|
|
3020
|
+
}
|
|
3021
|
+
if (transcriptData.messages.length === 0) {
|
|
3022
|
+
return { success: false, message: "No messages to save" };
|
|
3023
|
+
}
|
|
3024
|
+
const payload = {
|
|
3025
|
+
session_id: sessionId,
|
|
3026
|
+
messages: transcriptData.messages,
|
|
3027
|
+
started_at: transcriptData.startedAt,
|
|
3028
|
+
source_type: "pre_compact",
|
|
3029
|
+
title: `Pre-compaction save (${trigger})`,
|
|
3030
|
+
metadata: {
|
|
3031
|
+
trigger,
|
|
3032
|
+
active_files: transcriptData.activeFiles,
|
|
3033
|
+
tool_call_count: transcriptData.toolCallCount
|
|
3034
|
+
},
|
|
3035
|
+
tags: ["pre_compaction", trigger]
|
|
2871
3036
|
};
|
|
3037
|
+
if (WORKSPACE_ID) {
|
|
3038
|
+
payload.workspace_id = WORKSPACE_ID;
|
|
3039
|
+
}
|
|
3040
|
+
try {
|
|
3041
|
+
const controller = new AbortController();
|
|
3042
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
3043
|
+
const response = await fetch(`${API_URL2}/api/v1/transcripts`, {
|
|
3044
|
+
method: "POST",
|
|
3045
|
+
headers: {
|
|
3046
|
+
"Content-Type": "application/json",
|
|
3047
|
+
"X-API-Key": API_KEY2
|
|
3048
|
+
},
|
|
3049
|
+
body: JSON.stringify(payload),
|
|
3050
|
+
signal: controller.signal
|
|
3051
|
+
});
|
|
3052
|
+
clearTimeout(timeoutId);
|
|
3053
|
+
if (response.ok) {
|
|
3054
|
+
return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
|
|
3055
|
+
}
|
|
3056
|
+
return { success: false, message: `API error: ${response.status}` };
|
|
3057
|
+
} catch (error) {
|
|
3058
|
+
return { success: false, message: String(error) };
|
|
3059
|
+
}
|
|
2872
3060
|
}
|
|
2873
3061
|
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
2874
3062
|
if (!API_KEY2) {
|
|
@@ -2950,13 +3138,19 @@ async function runPreCompactHook() {
|
|
|
2950
3138
|
}
|
|
2951
3139
|
let autoSaveStatus = "";
|
|
2952
3140
|
if (AUTO_SAVE && API_KEY2) {
|
|
2953
|
-
const
|
|
2954
|
-
if (success) {
|
|
3141
|
+
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
3142
|
+
if (transcriptResult.success) {
|
|
2955
3143
|
autoSaveStatus = `
|
|
2956
|
-
[ContextStream:
|
|
3144
|
+
[ContextStream: ${transcriptResult.message}]`;
|
|
2957
3145
|
} else {
|
|
2958
|
-
|
|
3146
|
+
const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
|
|
3147
|
+
if (success) {
|
|
3148
|
+
autoSaveStatus = `
|
|
3149
|
+
[ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
|
|
3150
|
+
} else {
|
|
3151
|
+
autoSaveStatus = `
|
|
2959
3152
|
[ContextStream: Auto-save failed - ${message}]`;
|
|
3153
|
+
}
|
|
2960
3154
|
}
|
|
2961
3155
|
}
|
|
2962
3156
|
const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
|
|
@@ -3103,46 +3297,1689 @@ async function runAutoRulesHook() {
|
|
|
3103
3297
|
if (!inputData.trim()) {
|
|
3104
3298
|
process.exit(0);
|
|
3105
3299
|
}
|
|
3106
|
-
let input;
|
|
3300
|
+
let input;
|
|
3301
|
+
try {
|
|
3302
|
+
input = JSON.parse(inputData);
|
|
3303
|
+
} catch {
|
|
3304
|
+
process.exit(0);
|
|
3305
|
+
}
|
|
3306
|
+
const toolName = input.tool_name || input.toolName || "";
|
|
3307
|
+
const isContextTool = toolName.includes("init") || toolName.includes("context") || toolName.includes("session_init") || toolName.includes("context_smart");
|
|
3308
|
+
if (!isContextTool) {
|
|
3309
|
+
process.exit(0);
|
|
3310
|
+
}
|
|
3311
|
+
const cwd = extractCwd3(input);
|
|
3312
|
+
const pythonHooks = detectPythonHooks(cwd);
|
|
3313
|
+
const hasPythonHooksToUpgrade = pythonHooks.global || pythonHooks.project;
|
|
3314
|
+
const rulesNotice = extractRulesNotice(input);
|
|
3315
|
+
const rulesNeedUpdate = rulesNotice && rulesNotice.status !== "current";
|
|
3316
|
+
if (!hasPythonHooksToUpgrade && !rulesNeedUpdate) {
|
|
3317
|
+
process.exit(0);
|
|
3318
|
+
}
|
|
3319
|
+
const folderPath = rulesNotice?.update_args?.folder_path || cwd;
|
|
3320
|
+
try {
|
|
3321
|
+
await upgradeHooksForFolder(folderPath);
|
|
3322
|
+
markAsRan();
|
|
3323
|
+
} catch {
|
|
3324
|
+
}
|
|
3325
|
+
process.exit(0);
|
|
3326
|
+
}
|
|
3327
|
+
var API_URL3, API_KEY3, ENABLED6, MARKER_FILE, COOLDOWN_MS, isDirectRun6;
|
|
3328
|
+
var init_auto_rules = __esm({
|
|
3329
|
+
"src/hooks/auto-rules.ts"() {
|
|
3330
|
+
"use strict";
|
|
3331
|
+
API_URL3 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3332
|
+
API_KEY3 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3333
|
+
ENABLED6 = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
|
|
3334
|
+
MARKER_FILE = path12.join(homedir9(), ".contextstream", ".auto-rules-ran");
|
|
3335
|
+
COOLDOWN_MS = 4 * 60 * 60 * 1e3;
|
|
3336
|
+
isDirectRun6 = process.argv[1]?.includes("auto-rules") || process.argv[2] === "auto-rules";
|
|
3337
|
+
if (isDirectRun6) {
|
|
3338
|
+
runAutoRulesHook().catch(() => process.exit(0));
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
});
|
|
3342
|
+
|
|
3343
|
+
// src/hooks/post-compact.ts
|
|
3344
|
+
var post_compact_exports = {};
|
|
3345
|
+
__export(post_compact_exports, {
|
|
3346
|
+
runPostCompactHook: () => runPostCompactHook
|
|
3347
|
+
});
|
|
3348
|
+
import * as fs12 from "node:fs";
|
|
3349
|
+
import * as path13 from "node:path";
|
|
3350
|
+
import { homedir as homedir10 } from "node:os";
|
|
3351
|
+
function loadConfigFromMcpJson2(cwd) {
|
|
3352
|
+
let searchDir = path13.resolve(cwd);
|
|
3353
|
+
for (let i = 0; i < 5; i++) {
|
|
3354
|
+
if (!API_KEY4) {
|
|
3355
|
+
const mcpPath = path13.join(searchDir, ".mcp.json");
|
|
3356
|
+
if (fs12.existsSync(mcpPath)) {
|
|
3357
|
+
try {
|
|
3358
|
+
const content = fs12.readFileSync(mcpPath, "utf-8");
|
|
3359
|
+
const config = JSON.parse(content);
|
|
3360
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3361
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3362
|
+
API_KEY4 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3363
|
+
}
|
|
3364
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3365
|
+
API_URL4 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3366
|
+
}
|
|
3367
|
+
} catch {
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
if (!WORKSPACE_ID2) {
|
|
3372
|
+
const csConfigPath = path13.join(searchDir, ".contextstream", "config.json");
|
|
3373
|
+
if (fs12.existsSync(csConfigPath)) {
|
|
3374
|
+
try {
|
|
3375
|
+
const content = fs12.readFileSync(csConfigPath, "utf-8");
|
|
3376
|
+
const csConfig = JSON.parse(content);
|
|
3377
|
+
if (csConfig.workspace_id) {
|
|
3378
|
+
WORKSPACE_ID2 = csConfig.workspace_id;
|
|
3379
|
+
}
|
|
3380
|
+
} catch {
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
const parentDir = path13.dirname(searchDir);
|
|
3385
|
+
if (parentDir === searchDir) break;
|
|
3386
|
+
searchDir = parentDir;
|
|
3387
|
+
}
|
|
3388
|
+
if (!API_KEY4) {
|
|
3389
|
+
const homeMcpPath = path13.join(homedir10(), ".mcp.json");
|
|
3390
|
+
if (fs12.existsSync(homeMcpPath)) {
|
|
3391
|
+
try {
|
|
3392
|
+
const content = fs12.readFileSync(homeMcpPath, "utf-8");
|
|
3393
|
+
const config = JSON.parse(content);
|
|
3394
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3395
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3396
|
+
API_KEY4 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3397
|
+
}
|
|
3398
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3399
|
+
API_URL4 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3400
|
+
}
|
|
3401
|
+
} catch {
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
async function fetchLastTranscript(sessionId) {
|
|
3407
|
+
if (!API_KEY4) {
|
|
3408
|
+
return null;
|
|
3409
|
+
}
|
|
3410
|
+
try {
|
|
3411
|
+
const controller = new AbortController();
|
|
3412
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
3413
|
+
const url = new URL(`${API_URL4}/api/v1/transcripts`);
|
|
3414
|
+
url.searchParams.set("session_id", sessionId);
|
|
3415
|
+
url.searchParams.set("limit", "1");
|
|
3416
|
+
url.searchParams.set("sort", "created_at:desc");
|
|
3417
|
+
if (WORKSPACE_ID2) {
|
|
3418
|
+
url.searchParams.set("workspace_id", WORKSPACE_ID2);
|
|
3419
|
+
}
|
|
3420
|
+
const response = await fetch(url.toString(), {
|
|
3421
|
+
method: "GET",
|
|
3422
|
+
headers: {
|
|
3423
|
+
"X-API-Key": API_KEY4
|
|
3424
|
+
},
|
|
3425
|
+
signal: controller.signal
|
|
3426
|
+
});
|
|
3427
|
+
clearTimeout(timeoutId);
|
|
3428
|
+
if (response.ok) {
|
|
3429
|
+
const data = await response.json();
|
|
3430
|
+
if (data.transcripts && data.transcripts.length > 0) {
|
|
3431
|
+
return data.transcripts[0];
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
return null;
|
|
3435
|
+
} catch {
|
|
3436
|
+
return null;
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
function formatTranscriptSummary(transcript) {
|
|
3440
|
+
const messages = transcript.messages || [];
|
|
3441
|
+
const activeFiles = transcript.metadata?.active_files || [];
|
|
3442
|
+
const toolCallCount = transcript.metadata?.tool_call_count || 0;
|
|
3443
|
+
const userMessages = messages.filter((m) => m.role === "user").slice(-3).map((m) => `- "${m.content.slice(0, 100)}${m.content.length > 100 ? "..." : ""}"`).join("\n");
|
|
3444
|
+
const lastAssistant = messages.filter((m) => m.role === "assistant" && !m.content.startsWith("[Tool:")).slice(-1)[0];
|
|
3445
|
+
const lastWork = lastAssistant ? lastAssistant.content.slice(0, 300) + (lastAssistant.content.length > 300 ? "..." : "") : "None recorded";
|
|
3446
|
+
return `## Pre-Compaction State Restored
|
|
3447
|
+
|
|
3448
|
+
### Active Files (${activeFiles.length})
|
|
3449
|
+
${activeFiles.slice(0, 10).map((f) => `- ${f}`).join("\n") || "None tracked"}
|
|
3450
|
+
|
|
3451
|
+
### Recent User Requests
|
|
3452
|
+
${userMessages || "None recorded"}
|
|
3453
|
+
|
|
3454
|
+
### Last Work in Progress
|
|
3455
|
+
${lastWork}
|
|
3456
|
+
|
|
3457
|
+
### Session Stats
|
|
3458
|
+
- Tool calls: ${toolCallCount}
|
|
3459
|
+
- Messages: ${messages.length}
|
|
3460
|
+
- Saved at: ${transcript.created_at}`;
|
|
3461
|
+
}
|
|
3462
|
+
async function runPostCompactHook() {
|
|
3463
|
+
if (!ENABLED7) {
|
|
3464
|
+
process.exit(0);
|
|
3465
|
+
}
|
|
3466
|
+
let inputData = "";
|
|
3467
|
+
for await (const chunk of process.stdin) {
|
|
3468
|
+
inputData += chunk;
|
|
3469
|
+
}
|
|
3470
|
+
if (!inputData.trim()) {
|
|
3471
|
+
process.exit(0);
|
|
3472
|
+
}
|
|
3473
|
+
let input;
|
|
3474
|
+
try {
|
|
3475
|
+
input = JSON.parse(inputData);
|
|
3476
|
+
} catch {
|
|
3477
|
+
process.exit(0);
|
|
3478
|
+
}
|
|
3479
|
+
const cwd = input.cwd || process.cwd();
|
|
3480
|
+
loadConfigFromMcpJson2(cwd);
|
|
3481
|
+
const sessionId = input.session_id || "";
|
|
3482
|
+
let restoredContext = "";
|
|
3483
|
+
if (sessionId && API_KEY4) {
|
|
3484
|
+
const transcript = await fetchLastTranscript(sessionId);
|
|
3485
|
+
if (transcript) {
|
|
3486
|
+
restoredContext = formatTranscriptSummary(transcript);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
const context = `[POST-COMPACTION - Context Restored]
|
|
3490
|
+
|
|
3491
|
+
${restoredContext || "No saved state found. Starting fresh."}
|
|
3492
|
+
|
|
3493
|
+
**IMPORTANT:** Call \`mcp__contextstream__context(user_message="resuming after compaction")\` to get full context and any pending tasks.
|
|
3494
|
+
|
|
3495
|
+
The conversation was compacted to save memory. The above summary was automatically restored from ContextStream.`;
|
|
3496
|
+
console.log(
|
|
3497
|
+
JSON.stringify({
|
|
3498
|
+
hookSpecificOutput: {
|
|
3499
|
+
hookEventName: "PostCompact",
|
|
3500
|
+
additionalContext: context
|
|
3501
|
+
}
|
|
3502
|
+
})
|
|
3503
|
+
);
|
|
3504
|
+
process.exit(0);
|
|
3505
|
+
}
|
|
3506
|
+
var ENABLED7, API_URL4, API_KEY4, WORKSPACE_ID2, isDirectRun7;
|
|
3507
|
+
var init_post_compact = __esm({
|
|
3508
|
+
"src/hooks/post-compact.ts"() {
|
|
3509
|
+
"use strict";
|
|
3510
|
+
ENABLED7 = process.env.CONTEXTSTREAM_POSTCOMPACT_ENABLED !== "false";
|
|
3511
|
+
API_URL4 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3512
|
+
API_KEY4 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3513
|
+
WORKSPACE_ID2 = null;
|
|
3514
|
+
isDirectRun7 = process.argv[1]?.includes("post-compact") || process.argv[2] === "post-compact";
|
|
3515
|
+
if (isDirectRun7) {
|
|
3516
|
+
runPostCompactHook().catch(() => process.exit(0));
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
});
|
|
3520
|
+
|
|
3521
|
+
// src/hooks/on-bash.ts
|
|
3522
|
+
var on_bash_exports = {};
|
|
3523
|
+
__export(on_bash_exports, {
|
|
3524
|
+
runOnBashHook: () => runOnBashHook
|
|
3525
|
+
});
|
|
3526
|
+
import * as fs13 from "node:fs";
|
|
3527
|
+
import * as path14 from "node:path";
|
|
3528
|
+
import { homedir as homedir11 } from "node:os";
|
|
3529
|
+
function loadConfigFromMcpJson3(cwd) {
|
|
3530
|
+
let searchDir = path14.resolve(cwd);
|
|
3531
|
+
for (let i = 0; i < 5; i++) {
|
|
3532
|
+
if (!API_KEY5) {
|
|
3533
|
+
const mcpPath = path14.join(searchDir, ".mcp.json");
|
|
3534
|
+
if (fs13.existsSync(mcpPath)) {
|
|
3535
|
+
try {
|
|
3536
|
+
const content = fs13.readFileSync(mcpPath, "utf-8");
|
|
3537
|
+
const config = JSON.parse(content);
|
|
3538
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3539
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3540
|
+
API_KEY5 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3541
|
+
}
|
|
3542
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3543
|
+
API_URL5 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3544
|
+
}
|
|
3545
|
+
} catch {
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
if (!WORKSPACE_ID3) {
|
|
3550
|
+
const csConfigPath = path14.join(searchDir, ".contextstream", "config.json");
|
|
3551
|
+
if (fs13.existsSync(csConfigPath)) {
|
|
3552
|
+
try {
|
|
3553
|
+
const content = fs13.readFileSync(csConfigPath, "utf-8");
|
|
3554
|
+
const csConfig = JSON.parse(content);
|
|
3555
|
+
if (csConfig.workspace_id) {
|
|
3556
|
+
WORKSPACE_ID3 = csConfig.workspace_id;
|
|
3557
|
+
}
|
|
3558
|
+
} catch {
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
const parentDir = path14.dirname(searchDir);
|
|
3563
|
+
if (parentDir === searchDir) break;
|
|
3564
|
+
searchDir = parentDir;
|
|
3565
|
+
}
|
|
3566
|
+
if (!API_KEY5) {
|
|
3567
|
+
const homeMcpPath = path14.join(homedir11(), ".mcp.json");
|
|
3568
|
+
if (fs13.existsSync(homeMcpPath)) {
|
|
3569
|
+
try {
|
|
3570
|
+
const content = fs13.readFileSync(homeMcpPath, "utf-8");
|
|
3571
|
+
const config = JSON.parse(content);
|
|
3572
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3573
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3574
|
+
API_KEY5 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3575
|
+
}
|
|
3576
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3577
|
+
API_URL5 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3578
|
+
}
|
|
3579
|
+
} catch {
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
async function captureCommand(command, output, exitCode, isError, sessionId) {
|
|
3585
|
+
if (!API_KEY5) return;
|
|
3586
|
+
const payload = {
|
|
3587
|
+
event_type: isError ? "bash_error" : "bash_command",
|
|
3588
|
+
title: isError ? `Bash Error: ${command.slice(0, 50)}...` : `Command: ${command.slice(0, 50)}...`,
|
|
3589
|
+
content: JSON.stringify({
|
|
3590
|
+
command,
|
|
3591
|
+
output: output.slice(0, 2e3),
|
|
3592
|
+
exit_code: exitCode,
|
|
3593
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3594
|
+
}),
|
|
3595
|
+
importance: isError ? "high" : "low",
|
|
3596
|
+
tags: isError ? ["bash", "error", "command"] : ["bash", "command"],
|
|
3597
|
+
source_type: "hook",
|
|
3598
|
+
session_id: sessionId
|
|
3599
|
+
};
|
|
3600
|
+
if (WORKSPACE_ID3) {
|
|
3601
|
+
payload.workspace_id = WORKSPACE_ID3;
|
|
3602
|
+
}
|
|
3603
|
+
try {
|
|
3604
|
+
const controller = new AbortController();
|
|
3605
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
3606
|
+
await fetch(`${API_URL5}/api/v1/memory/events`, {
|
|
3607
|
+
method: "POST",
|
|
3608
|
+
headers: {
|
|
3609
|
+
"Content-Type": "application/json",
|
|
3610
|
+
"X-API-Key": API_KEY5
|
|
3611
|
+
},
|
|
3612
|
+
body: JSON.stringify(payload),
|
|
3613
|
+
signal: controller.signal
|
|
3614
|
+
});
|
|
3615
|
+
clearTimeout(timeoutId);
|
|
3616
|
+
} catch {
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
async function suggestLesson(command, error) {
|
|
3620
|
+
const errorPatterns = [
|
|
3621
|
+
{
|
|
3622
|
+
pattern: /command not found/i,
|
|
3623
|
+
lesson: `The command "${command.split(" ")[0]}" is not installed. Check if the package needs to be installed first.`
|
|
3624
|
+
},
|
|
3625
|
+
{
|
|
3626
|
+
pattern: /permission denied/i,
|
|
3627
|
+
lesson: "Permission denied. May need sudo or to check file permissions."
|
|
3628
|
+
},
|
|
3629
|
+
{
|
|
3630
|
+
pattern: /no such file or directory/i,
|
|
3631
|
+
lesson: "Path does not exist. Verify the file/directory path before running commands."
|
|
3632
|
+
},
|
|
3633
|
+
{
|
|
3634
|
+
pattern: /EADDRINUSE|address already in use/i,
|
|
3635
|
+
lesson: "Port is already in use. Kill the existing process or use a different port."
|
|
3636
|
+
},
|
|
3637
|
+
{
|
|
3638
|
+
pattern: /npm ERR!|ERESOLVE/i,
|
|
3639
|
+
lesson: "npm dependency conflict. Try `npm install --legacy-peer-deps` or check package versions."
|
|
3640
|
+
},
|
|
3641
|
+
{
|
|
3642
|
+
pattern: /ENOENT.*package\.json/i,
|
|
3643
|
+
lesson: "No package.json found. Make sure you're in the right directory or run `npm init`."
|
|
3644
|
+
},
|
|
3645
|
+
{
|
|
3646
|
+
pattern: /git.*not a git repository/i,
|
|
3647
|
+
lesson: "Not in a git repository. Run `git init` or navigate to a git repo."
|
|
3648
|
+
}
|
|
3649
|
+
];
|
|
3650
|
+
for (const { pattern, lesson } of errorPatterns) {
|
|
3651
|
+
if (pattern.test(error)) {
|
|
3652
|
+
return lesson;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
return null;
|
|
3656
|
+
}
|
|
3657
|
+
async function runOnBashHook() {
|
|
3658
|
+
if (!ENABLED8) {
|
|
3659
|
+
process.exit(0);
|
|
3660
|
+
}
|
|
3661
|
+
let inputData = "";
|
|
3662
|
+
for await (const chunk of process.stdin) {
|
|
3663
|
+
inputData += chunk;
|
|
3664
|
+
}
|
|
3665
|
+
if (!inputData.trim()) {
|
|
3666
|
+
process.exit(0);
|
|
3667
|
+
}
|
|
3668
|
+
let input;
|
|
3669
|
+
try {
|
|
3670
|
+
input = JSON.parse(inputData);
|
|
3671
|
+
} catch {
|
|
3672
|
+
process.exit(0);
|
|
3673
|
+
}
|
|
3674
|
+
if (input.tool_name !== "Bash") {
|
|
3675
|
+
process.exit(0);
|
|
3676
|
+
}
|
|
3677
|
+
const cwd = input.cwd || process.cwd();
|
|
3678
|
+
loadConfigFromMcpJson3(cwd);
|
|
3679
|
+
const command = input.tool_input?.command || "";
|
|
3680
|
+
const output = input.tool_result?.output || input.tool_result?.error || "";
|
|
3681
|
+
const exitCode = input.tool_result?.exit_code ?? 0;
|
|
3682
|
+
const sessionId = input.session_id || "unknown";
|
|
3683
|
+
const isError = exitCode !== 0 || !!input.tool_result?.error;
|
|
3684
|
+
captureCommand(command, output, exitCode, isError, sessionId).catch(() => {
|
|
3685
|
+
});
|
|
3686
|
+
if (isError) {
|
|
3687
|
+
const lesson = await suggestLesson(command, output);
|
|
3688
|
+
if (lesson) {
|
|
3689
|
+
console.log(
|
|
3690
|
+
JSON.stringify({
|
|
3691
|
+
hookSpecificOutput: {
|
|
3692
|
+
hookEventName: "PostToolUse",
|
|
3693
|
+
additionalContext: `[ContextStream Insight] ${lesson}`
|
|
3694
|
+
}
|
|
3695
|
+
})
|
|
3696
|
+
);
|
|
3697
|
+
process.exit(0);
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
process.exit(0);
|
|
3701
|
+
}
|
|
3702
|
+
var ENABLED8, API_URL5, API_KEY5, WORKSPACE_ID3, isDirectRun8;
|
|
3703
|
+
var init_on_bash = __esm({
|
|
3704
|
+
"src/hooks/on-bash.ts"() {
|
|
3705
|
+
"use strict";
|
|
3706
|
+
ENABLED8 = process.env.CONTEXTSTREAM_BASH_HOOK_ENABLED !== "false";
|
|
3707
|
+
API_URL5 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3708
|
+
API_KEY5 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3709
|
+
WORKSPACE_ID3 = null;
|
|
3710
|
+
isDirectRun8 = process.argv[1]?.includes("on-bash") || process.argv[2] === "on-bash";
|
|
3711
|
+
if (isDirectRun8) {
|
|
3712
|
+
runOnBashHook().catch(() => process.exit(0));
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
});
|
|
3716
|
+
|
|
3717
|
+
// src/hooks/on-task.ts
|
|
3718
|
+
var on_task_exports = {};
|
|
3719
|
+
__export(on_task_exports, {
|
|
3720
|
+
runOnTaskHook: () => runOnTaskHook
|
|
3721
|
+
});
|
|
3722
|
+
import * as fs14 from "node:fs";
|
|
3723
|
+
import * as path15 from "node:path";
|
|
3724
|
+
import { homedir as homedir12 } from "node:os";
|
|
3725
|
+
function loadConfigFromMcpJson4(cwd) {
|
|
3726
|
+
let searchDir = path15.resolve(cwd);
|
|
3727
|
+
for (let i = 0; i < 5; i++) {
|
|
3728
|
+
if (!API_KEY6) {
|
|
3729
|
+
const mcpPath = path15.join(searchDir, ".mcp.json");
|
|
3730
|
+
if (fs14.existsSync(mcpPath)) {
|
|
3731
|
+
try {
|
|
3732
|
+
const content = fs14.readFileSync(mcpPath, "utf-8");
|
|
3733
|
+
const config = JSON.parse(content);
|
|
3734
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3735
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3736
|
+
API_KEY6 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3737
|
+
}
|
|
3738
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3739
|
+
API_URL6 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3740
|
+
}
|
|
3741
|
+
} catch {
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
if (!WORKSPACE_ID4) {
|
|
3746
|
+
const csConfigPath = path15.join(searchDir, ".contextstream", "config.json");
|
|
3747
|
+
if (fs14.existsSync(csConfigPath)) {
|
|
3748
|
+
try {
|
|
3749
|
+
const content = fs14.readFileSync(csConfigPath, "utf-8");
|
|
3750
|
+
const csConfig = JSON.parse(content);
|
|
3751
|
+
if (csConfig.workspace_id) {
|
|
3752
|
+
WORKSPACE_ID4 = csConfig.workspace_id;
|
|
3753
|
+
}
|
|
3754
|
+
} catch {
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
const parentDir = path15.dirname(searchDir);
|
|
3759
|
+
if (parentDir === searchDir) break;
|
|
3760
|
+
searchDir = parentDir;
|
|
3761
|
+
}
|
|
3762
|
+
if (!API_KEY6) {
|
|
3763
|
+
const homeMcpPath = path15.join(homedir12(), ".mcp.json");
|
|
3764
|
+
if (fs14.existsSync(homeMcpPath)) {
|
|
3765
|
+
try {
|
|
3766
|
+
const content = fs14.readFileSync(homeMcpPath, "utf-8");
|
|
3767
|
+
const config = JSON.parse(content);
|
|
3768
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3769
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3770
|
+
API_KEY6 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3771
|
+
}
|
|
3772
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3773
|
+
API_URL6 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3774
|
+
}
|
|
3775
|
+
} catch {
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
async function captureTaskInvocation(description, prompt, agentType, result, sessionId) {
|
|
3781
|
+
if (!API_KEY6) return;
|
|
3782
|
+
const payload = {
|
|
3783
|
+
event_type: "task_agent",
|
|
3784
|
+
title: `Agent: ${agentType} - ${description}`,
|
|
3785
|
+
content: JSON.stringify({
|
|
3786
|
+
description,
|
|
3787
|
+
prompt: prompt.slice(0, 1e3),
|
|
3788
|
+
agent_type: agentType,
|
|
3789
|
+
result: result.slice(0, 2e3),
|
|
3790
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3791
|
+
}),
|
|
3792
|
+
importance: "medium",
|
|
3793
|
+
tags: ["task", "agent", agentType.toLowerCase()],
|
|
3794
|
+
source_type: "hook",
|
|
3795
|
+
session_id: sessionId
|
|
3796
|
+
};
|
|
3797
|
+
if (WORKSPACE_ID4) {
|
|
3798
|
+
payload.workspace_id = WORKSPACE_ID4;
|
|
3799
|
+
}
|
|
3800
|
+
try {
|
|
3801
|
+
const controller = new AbortController();
|
|
3802
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
3803
|
+
await fetch(`${API_URL6}/api/v1/memory/events`, {
|
|
3804
|
+
method: "POST",
|
|
3805
|
+
headers: {
|
|
3806
|
+
"Content-Type": "application/json",
|
|
3807
|
+
"X-API-Key": API_KEY6
|
|
3808
|
+
},
|
|
3809
|
+
body: JSON.stringify(payload),
|
|
3810
|
+
signal: controller.signal
|
|
3811
|
+
});
|
|
3812
|
+
clearTimeout(timeoutId);
|
|
3813
|
+
} catch {
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
async function runOnTaskHook() {
|
|
3817
|
+
if (!ENABLED9) {
|
|
3818
|
+
process.exit(0);
|
|
3819
|
+
}
|
|
3820
|
+
let inputData = "";
|
|
3821
|
+
for await (const chunk of process.stdin) {
|
|
3822
|
+
inputData += chunk;
|
|
3823
|
+
}
|
|
3824
|
+
if (!inputData.trim()) {
|
|
3825
|
+
process.exit(0);
|
|
3826
|
+
}
|
|
3827
|
+
let input;
|
|
3828
|
+
try {
|
|
3829
|
+
input = JSON.parse(inputData);
|
|
3830
|
+
} catch {
|
|
3831
|
+
process.exit(0);
|
|
3832
|
+
}
|
|
3833
|
+
if (input.tool_name !== "Task") {
|
|
3834
|
+
process.exit(0);
|
|
3835
|
+
}
|
|
3836
|
+
const cwd = input.cwd || process.cwd();
|
|
3837
|
+
loadConfigFromMcpJson4(cwd);
|
|
3838
|
+
const description = input.tool_input?.description || "Unknown task";
|
|
3839
|
+
const prompt = input.tool_input?.prompt || "";
|
|
3840
|
+
const agentType = input.tool_input?.subagent_type || "general-purpose";
|
|
3841
|
+
const result = input.tool_result?.output || "";
|
|
3842
|
+
const sessionId = input.session_id || "unknown";
|
|
3843
|
+
captureTaskInvocation(description, prompt, agentType, result, sessionId).catch(() => {
|
|
3844
|
+
});
|
|
3845
|
+
process.exit(0);
|
|
3846
|
+
}
|
|
3847
|
+
var ENABLED9, API_URL6, API_KEY6, WORKSPACE_ID4, isDirectRun9;
|
|
3848
|
+
var init_on_task = __esm({
|
|
3849
|
+
"src/hooks/on-task.ts"() {
|
|
3850
|
+
"use strict";
|
|
3851
|
+
ENABLED9 = process.env.CONTEXTSTREAM_TASK_HOOK_ENABLED !== "false";
|
|
3852
|
+
API_URL6 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3853
|
+
API_KEY6 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3854
|
+
WORKSPACE_ID4 = null;
|
|
3855
|
+
isDirectRun9 = process.argv[1]?.includes("on-task") || process.argv[2] === "on-task";
|
|
3856
|
+
if (isDirectRun9) {
|
|
3857
|
+
runOnTaskHook().catch(() => process.exit(0));
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
});
|
|
3861
|
+
|
|
3862
|
+
// src/hooks/on-read.ts
|
|
3863
|
+
var on_read_exports = {};
|
|
3864
|
+
__export(on_read_exports, {
|
|
3865
|
+
runOnReadHook: () => runOnReadHook
|
|
3866
|
+
});
|
|
3867
|
+
import * as fs15 from "node:fs";
|
|
3868
|
+
import * as path16 from "node:path";
|
|
3869
|
+
import { homedir as homedir13 } from "node:os";
|
|
3870
|
+
function loadConfigFromMcpJson5(cwd) {
|
|
3871
|
+
let searchDir = path16.resolve(cwd);
|
|
3872
|
+
for (let i = 0; i < 5; i++) {
|
|
3873
|
+
if (!API_KEY7) {
|
|
3874
|
+
const mcpPath = path16.join(searchDir, ".mcp.json");
|
|
3875
|
+
if (fs15.existsSync(mcpPath)) {
|
|
3876
|
+
try {
|
|
3877
|
+
const content = fs15.readFileSync(mcpPath, "utf-8");
|
|
3878
|
+
const config = JSON.parse(content);
|
|
3879
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3880
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3881
|
+
API_KEY7 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3882
|
+
}
|
|
3883
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3884
|
+
API_URL7 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3885
|
+
}
|
|
3886
|
+
} catch {
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
if (!WORKSPACE_ID5) {
|
|
3891
|
+
const csConfigPath = path16.join(searchDir, ".contextstream", "config.json");
|
|
3892
|
+
if (fs15.existsSync(csConfigPath)) {
|
|
3893
|
+
try {
|
|
3894
|
+
const content = fs15.readFileSync(csConfigPath, "utf-8");
|
|
3895
|
+
const csConfig = JSON.parse(content);
|
|
3896
|
+
if (csConfig.workspace_id) {
|
|
3897
|
+
WORKSPACE_ID5 = csConfig.workspace_id;
|
|
3898
|
+
}
|
|
3899
|
+
} catch {
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const parentDir = path16.dirname(searchDir);
|
|
3904
|
+
if (parentDir === searchDir) break;
|
|
3905
|
+
searchDir = parentDir;
|
|
3906
|
+
}
|
|
3907
|
+
if (!API_KEY7) {
|
|
3908
|
+
const homeMcpPath = path16.join(homedir13(), ".mcp.json");
|
|
3909
|
+
if (fs15.existsSync(homeMcpPath)) {
|
|
3910
|
+
try {
|
|
3911
|
+
const content = fs15.readFileSync(homeMcpPath, "utf-8");
|
|
3912
|
+
const config = JSON.parse(content);
|
|
3913
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
3914
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
3915
|
+
API_KEY7 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
3916
|
+
}
|
|
3917
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
3918
|
+
API_URL7 = csEnv.CONTEXTSTREAM_API_URL;
|
|
3919
|
+
}
|
|
3920
|
+
} catch {
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
async function captureExploration(toolName, target, resultSummary, sessionId) {
|
|
3926
|
+
if (!API_KEY7) return;
|
|
3927
|
+
const cacheKey = `${toolName}:${target}`;
|
|
3928
|
+
if (recentCaptures.has(cacheKey)) {
|
|
3929
|
+
return;
|
|
3930
|
+
}
|
|
3931
|
+
recentCaptures.add(cacheKey);
|
|
3932
|
+
setTimeout(() => recentCaptures.delete(cacheKey), CAPTURE_WINDOW_MS);
|
|
3933
|
+
const payload = {
|
|
3934
|
+
event_type: "file_exploration",
|
|
3935
|
+
title: `${toolName}: ${target.slice(0, 50)}`,
|
|
3936
|
+
content: JSON.stringify({
|
|
3937
|
+
tool: toolName,
|
|
3938
|
+
target,
|
|
3939
|
+
result_summary: resultSummary.slice(0, 500),
|
|
3940
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3941
|
+
}),
|
|
3942
|
+
importance: "low",
|
|
3943
|
+
tags: ["exploration", toolName.toLowerCase()],
|
|
3944
|
+
source_type: "hook",
|
|
3945
|
+
session_id: sessionId
|
|
3946
|
+
};
|
|
3947
|
+
if (WORKSPACE_ID5) {
|
|
3948
|
+
payload.workspace_id = WORKSPACE_ID5;
|
|
3949
|
+
}
|
|
3950
|
+
try {
|
|
3951
|
+
const controller = new AbortController();
|
|
3952
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
3953
|
+
await fetch(`${API_URL7}/api/v1/memory/events`, {
|
|
3954
|
+
method: "POST",
|
|
3955
|
+
headers: {
|
|
3956
|
+
"Content-Type": "application/json",
|
|
3957
|
+
"X-API-Key": API_KEY7
|
|
3958
|
+
},
|
|
3959
|
+
body: JSON.stringify(payload),
|
|
3960
|
+
signal: controller.signal
|
|
3961
|
+
});
|
|
3962
|
+
clearTimeout(timeoutId);
|
|
3963
|
+
} catch {
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
async function runOnReadHook() {
|
|
3967
|
+
if (!ENABLED10) {
|
|
3968
|
+
process.exit(0);
|
|
3969
|
+
}
|
|
3970
|
+
let inputData = "";
|
|
3971
|
+
for await (const chunk of process.stdin) {
|
|
3972
|
+
inputData += chunk;
|
|
3973
|
+
}
|
|
3974
|
+
if (!inputData.trim()) {
|
|
3975
|
+
process.exit(0);
|
|
3976
|
+
}
|
|
3977
|
+
let input;
|
|
3978
|
+
try {
|
|
3979
|
+
input = JSON.parse(inputData);
|
|
3980
|
+
} catch {
|
|
3981
|
+
process.exit(0);
|
|
3982
|
+
}
|
|
3983
|
+
const toolName = input.tool_name || "";
|
|
3984
|
+
if (!["Read", "Glob", "Grep"].includes(toolName)) {
|
|
3985
|
+
process.exit(0);
|
|
3986
|
+
}
|
|
3987
|
+
const cwd = input.cwd || process.cwd();
|
|
3988
|
+
loadConfigFromMcpJson5(cwd);
|
|
3989
|
+
const sessionId = input.session_id || "unknown";
|
|
3990
|
+
let target = "";
|
|
3991
|
+
let resultSummary = "";
|
|
3992
|
+
switch (toolName) {
|
|
3993
|
+
case "Read":
|
|
3994
|
+
target = input.tool_input?.file_path || "";
|
|
3995
|
+
resultSummary = `Read file: ${target}`;
|
|
3996
|
+
break;
|
|
3997
|
+
case "Glob":
|
|
3998
|
+
target = input.tool_input?.pattern || "";
|
|
3999
|
+
const globFiles = input.tool_result?.files || [];
|
|
4000
|
+
resultSummary = `Found ${globFiles.length} files matching ${target}`;
|
|
4001
|
+
break;
|
|
4002
|
+
case "Grep":
|
|
4003
|
+
target = input.tool_input?.pattern || "";
|
|
4004
|
+
const matches = input.tool_result?.matches || 0;
|
|
4005
|
+
resultSummary = `Found ${matches} matches for "${target}"`;
|
|
4006
|
+
break;
|
|
4007
|
+
}
|
|
4008
|
+
if (target) {
|
|
4009
|
+
captureExploration(toolName, target, resultSummary, sessionId).catch(() => {
|
|
4010
|
+
});
|
|
4011
|
+
}
|
|
4012
|
+
process.exit(0);
|
|
4013
|
+
}
|
|
4014
|
+
var ENABLED10, API_URL7, API_KEY7, WORKSPACE_ID5, recentCaptures, CAPTURE_WINDOW_MS, isDirectRun10;
|
|
4015
|
+
var init_on_read = __esm({
|
|
4016
|
+
"src/hooks/on-read.ts"() {
|
|
4017
|
+
"use strict";
|
|
4018
|
+
ENABLED10 = process.env.CONTEXTSTREAM_READ_HOOK_ENABLED !== "false";
|
|
4019
|
+
API_URL7 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4020
|
+
API_KEY7 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4021
|
+
WORKSPACE_ID5 = null;
|
|
4022
|
+
recentCaptures = /* @__PURE__ */ new Set();
|
|
4023
|
+
CAPTURE_WINDOW_MS = 6e4;
|
|
4024
|
+
isDirectRun10 = process.argv[1]?.includes("on-read") || process.argv[2] === "on-read";
|
|
4025
|
+
if (isDirectRun10) {
|
|
4026
|
+
runOnReadHook().catch(() => process.exit(0));
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
});
|
|
4030
|
+
|
|
4031
|
+
// src/hooks/on-web.ts
|
|
4032
|
+
var on_web_exports = {};
|
|
4033
|
+
__export(on_web_exports, {
|
|
4034
|
+
runOnWebHook: () => runOnWebHook
|
|
4035
|
+
});
|
|
4036
|
+
import * as fs16 from "node:fs";
|
|
4037
|
+
import * as path17 from "node:path";
|
|
4038
|
+
import { homedir as homedir14 } from "node:os";
|
|
4039
|
+
function loadConfigFromMcpJson6(cwd) {
|
|
4040
|
+
let searchDir = path17.resolve(cwd);
|
|
4041
|
+
for (let i = 0; i < 5; i++) {
|
|
4042
|
+
if (!API_KEY8) {
|
|
4043
|
+
const mcpPath = path17.join(searchDir, ".mcp.json");
|
|
4044
|
+
if (fs16.existsSync(mcpPath)) {
|
|
4045
|
+
try {
|
|
4046
|
+
const content = fs16.readFileSync(mcpPath, "utf-8");
|
|
4047
|
+
const config = JSON.parse(content);
|
|
4048
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4049
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4050
|
+
API_KEY8 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4051
|
+
}
|
|
4052
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4053
|
+
API_URL8 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4054
|
+
}
|
|
4055
|
+
} catch {
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
if (!WORKSPACE_ID6) {
|
|
4060
|
+
const csConfigPath = path17.join(searchDir, ".contextstream", "config.json");
|
|
4061
|
+
if (fs16.existsSync(csConfigPath)) {
|
|
4062
|
+
try {
|
|
4063
|
+
const content = fs16.readFileSync(csConfigPath, "utf-8");
|
|
4064
|
+
const csConfig = JSON.parse(content);
|
|
4065
|
+
if (csConfig.workspace_id) {
|
|
4066
|
+
WORKSPACE_ID6 = csConfig.workspace_id;
|
|
4067
|
+
}
|
|
4068
|
+
} catch {
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
const parentDir = path17.dirname(searchDir);
|
|
4073
|
+
if (parentDir === searchDir) break;
|
|
4074
|
+
searchDir = parentDir;
|
|
4075
|
+
}
|
|
4076
|
+
if (!API_KEY8) {
|
|
4077
|
+
const homeMcpPath = path17.join(homedir14(), ".mcp.json");
|
|
4078
|
+
if (fs16.existsSync(homeMcpPath)) {
|
|
4079
|
+
try {
|
|
4080
|
+
const content = fs16.readFileSync(homeMcpPath, "utf-8");
|
|
4081
|
+
const config = JSON.parse(content);
|
|
4082
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4083
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4084
|
+
API_KEY8 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4085
|
+
}
|
|
4086
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4087
|
+
API_URL8 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4088
|
+
}
|
|
4089
|
+
} catch {
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
async function captureWebResearch(toolName, target, summary, sessionId) {
|
|
4095
|
+
if (!API_KEY8) return;
|
|
4096
|
+
const payload = {
|
|
4097
|
+
event_type: "web_research",
|
|
4098
|
+
title: `${toolName}: ${target.slice(0, 60)}`,
|
|
4099
|
+
content: JSON.stringify({
|
|
4100
|
+
tool: toolName,
|
|
4101
|
+
target,
|
|
4102
|
+
summary: summary.slice(0, 1e3),
|
|
4103
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4104
|
+
}),
|
|
4105
|
+
importance: "medium",
|
|
4106
|
+
tags: ["research", "web", toolName.toLowerCase()],
|
|
4107
|
+
source_type: "hook",
|
|
4108
|
+
session_id: sessionId
|
|
4109
|
+
};
|
|
4110
|
+
if (WORKSPACE_ID6) {
|
|
4111
|
+
payload.workspace_id = WORKSPACE_ID6;
|
|
4112
|
+
}
|
|
4113
|
+
try {
|
|
4114
|
+
const controller = new AbortController();
|
|
4115
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
4116
|
+
await fetch(`${API_URL8}/api/v1/memory/events`, {
|
|
4117
|
+
method: "POST",
|
|
4118
|
+
headers: {
|
|
4119
|
+
"Content-Type": "application/json",
|
|
4120
|
+
"X-API-Key": API_KEY8
|
|
4121
|
+
},
|
|
4122
|
+
body: JSON.stringify(payload),
|
|
4123
|
+
signal: controller.signal
|
|
4124
|
+
});
|
|
4125
|
+
clearTimeout(timeoutId);
|
|
4126
|
+
} catch {
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
async function runOnWebHook() {
|
|
4130
|
+
if (!ENABLED11) {
|
|
4131
|
+
process.exit(0);
|
|
4132
|
+
}
|
|
4133
|
+
let inputData = "";
|
|
4134
|
+
for await (const chunk of process.stdin) {
|
|
4135
|
+
inputData += chunk;
|
|
4136
|
+
}
|
|
4137
|
+
if (!inputData.trim()) {
|
|
4138
|
+
process.exit(0);
|
|
4139
|
+
}
|
|
4140
|
+
let input;
|
|
4141
|
+
try {
|
|
4142
|
+
input = JSON.parse(inputData);
|
|
4143
|
+
} catch {
|
|
4144
|
+
process.exit(0);
|
|
4145
|
+
}
|
|
4146
|
+
const toolName = input.tool_name || "";
|
|
4147
|
+
if (!["WebFetch", "WebSearch"].includes(toolName)) {
|
|
4148
|
+
process.exit(0);
|
|
4149
|
+
}
|
|
4150
|
+
const cwd = input.cwd || process.cwd();
|
|
4151
|
+
loadConfigFromMcpJson6(cwd);
|
|
4152
|
+
const sessionId = input.session_id || "unknown";
|
|
4153
|
+
let target = "";
|
|
4154
|
+
let summary = "";
|
|
4155
|
+
switch (toolName) {
|
|
4156
|
+
case "WebFetch":
|
|
4157
|
+
target = input.tool_input?.url || "";
|
|
4158
|
+
const prompt = input.tool_input?.prompt || "fetched content";
|
|
4159
|
+
const content = input.tool_result?.output || input.tool_result?.content || "";
|
|
4160
|
+
summary = `Fetched ${target} (${prompt}): ${content.slice(0, 300)}`;
|
|
4161
|
+
break;
|
|
4162
|
+
case "WebSearch":
|
|
4163
|
+
target = input.tool_input?.query || "";
|
|
4164
|
+
const results = input.tool_result?.results || [];
|
|
4165
|
+
const topResults = results.slice(0, 3).map((r) => `- ${r.title}: ${r.url}`).join("\n");
|
|
4166
|
+
summary = `Search: "${target}"
|
|
4167
|
+
Top results:
|
|
4168
|
+
${topResults}`;
|
|
4169
|
+
break;
|
|
4170
|
+
}
|
|
4171
|
+
if (target) {
|
|
4172
|
+
captureWebResearch(toolName, target, summary, sessionId).catch(() => {
|
|
4173
|
+
});
|
|
4174
|
+
}
|
|
4175
|
+
process.exit(0);
|
|
4176
|
+
}
|
|
4177
|
+
var ENABLED11, API_URL8, API_KEY8, WORKSPACE_ID6, isDirectRun11;
|
|
4178
|
+
var init_on_web = __esm({
|
|
4179
|
+
"src/hooks/on-web.ts"() {
|
|
4180
|
+
"use strict";
|
|
4181
|
+
ENABLED11 = process.env.CONTEXTSTREAM_WEB_HOOK_ENABLED !== "false";
|
|
4182
|
+
API_URL8 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4183
|
+
API_KEY8 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4184
|
+
WORKSPACE_ID6 = null;
|
|
4185
|
+
isDirectRun11 = process.argv[1]?.includes("on-web") || process.argv[2] === "on-web";
|
|
4186
|
+
if (isDirectRun11) {
|
|
4187
|
+
runOnWebHook().catch(() => process.exit(0));
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
});
|
|
4191
|
+
|
|
4192
|
+
// src/hooks/session-init.ts
|
|
4193
|
+
var session_init_exports = {};
|
|
4194
|
+
__export(session_init_exports, {
|
|
4195
|
+
runSessionInitHook: () => runSessionInitHook
|
|
4196
|
+
});
|
|
4197
|
+
import * as fs17 from "node:fs";
|
|
4198
|
+
import * as path18 from "node:path";
|
|
4199
|
+
import { homedir as homedir15 } from "node:os";
|
|
4200
|
+
function loadConfigFromMcpJson7(cwd) {
|
|
4201
|
+
let searchDir = path18.resolve(cwd);
|
|
4202
|
+
for (let i = 0; i < 5; i++) {
|
|
4203
|
+
if (!API_KEY9) {
|
|
4204
|
+
const mcpPath = path18.join(searchDir, ".mcp.json");
|
|
4205
|
+
if (fs17.existsSync(mcpPath)) {
|
|
4206
|
+
try {
|
|
4207
|
+
const content = fs17.readFileSync(mcpPath, "utf-8");
|
|
4208
|
+
const config = JSON.parse(content);
|
|
4209
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4210
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4211
|
+
API_KEY9 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4212
|
+
}
|
|
4213
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4214
|
+
API_URL9 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4215
|
+
}
|
|
4216
|
+
if (csEnv?.CONTEXTSTREAM_WORKSPACE_ID) {
|
|
4217
|
+
WORKSPACE_ID7 = csEnv.CONTEXTSTREAM_WORKSPACE_ID;
|
|
4218
|
+
}
|
|
4219
|
+
} catch {
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
if (!WORKSPACE_ID7 || !PROJECT_ID) {
|
|
4224
|
+
const csConfigPath = path18.join(searchDir, ".contextstream", "config.json");
|
|
4225
|
+
if (fs17.existsSync(csConfigPath)) {
|
|
4226
|
+
try {
|
|
4227
|
+
const content = fs17.readFileSync(csConfigPath, "utf-8");
|
|
4228
|
+
const csConfig = JSON.parse(content);
|
|
4229
|
+
if (csConfig.workspace_id && !WORKSPACE_ID7) {
|
|
4230
|
+
WORKSPACE_ID7 = csConfig.workspace_id;
|
|
4231
|
+
}
|
|
4232
|
+
if (csConfig.project_id && !PROJECT_ID) {
|
|
4233
|
+
PROJECT_ID = csConfig.project_id;
|
|
4234
|
+
}
|
|
4235
|
+
} catch {
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
const parentDir = path18.dirname(searchDir);
|
|
4240
|
+
if (parentDir === searchDir) break;
|
|
4241
|
+
searchDir = parentDir;
|
|
4242
|
+
}
|
|
4243
|
+
if (!API_KEY9) {
|
|
4244
|
+
const homeMcpPath = path18.join(homedir15(), ".mcp.json");
|
|
4245
|
+
if (fs17.existsSync(homeMcpPath)) {
|
|
4246
|
+
try {
|
|
4247
|
+
const content = fs17.readFileSync(homeMcpPath, "utf-8");
|
|
4248
|
+
const config = JSON.parse(content);
|
|
4249
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4250
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4251
|
+
API_KEY9 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4252
|
+
}
|
|
4253
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4254
|
+
API_URL9 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4255
|
+
}
|
|
4256
|
+
} catch {
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
async function fetchSessionContext() {
|
|
4262
|
+
if (!API_KEY9) return null;
|
|
4263
|
+
try {
|
|
4264
|
+
const controller = new AbortController();
|
|
4265
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
4266
|
+
const url = new URL(`${API_URL9}/api/v1/context`);
|
|
4267
|
+
if (WORKSPACE_ID7) url.searchParams.set("workspace_id", WORKSPACE_ID7);
|
|
4268
|
+
if (PROJECT_ID) url.searchParams.set("project_id", PROJECT_ID);
|
|
4269
|
+
url.searchParams.set("include_rules", "true");
|
|
4270
|
+
url.searchParams.set("include_lessons", "true");
|
|
4271
|
+
url.searchParams.set("include_decisions", "true");
|
|
4272
|
+
url.searchParams.set("include_plans", "true");
|
|
4273
|
+
url.searchParams.set("limit", "5");
|
|
4274
|
+
const response = await fetch(url.toString(), {
|
|
4275
|
+
method: "GET",
|
|
4276
|
+
headers: {
|
|
4277
|
+
"X-API-Key": API_KEY9
|
|
4278
|
+
},
|
|
4279
|
+
signal: controller.signal
|
|
4280
|
+
});
|
|
4281
|
+
clearTimeout(timeoutId);
|
|
4282
|
+
if (response.ok) {
|
|
4283
|
+
return await response.json();
|
|
4284
|
+
}
|
|
4285
|
+
return null;
|
|
4286
|
+
} catch {
|
|
4287
|
+
return null;
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
function formatContext(ctx) {
|
|
4291
|
+
if (!ctx) {
|
|
4292
|
+
return `[ContextStream Session Start]
|
|
4293
|
+
|
|
4294
|
+
No stored context found. Call \`mcp__contextstream__context(user_message="starting new session")\` to initialize.`;
|
|
4295
|
+
}
|
|
4296
|
+
const parts = ["[ContextStream Session Start]"];
|
|
4297
|
+
if (ctx.lessons && ctx.lessons.length > 0) {
|
|
4298
|
+
parts.push("\n## \u26A0\uFE0F Lessons from Past Mistakes");
|
|
4299
|
+
for (const lesson of ctx.lessons.slice(0, 3)) {
|
|
4300
|
+
parts.push(`- **${lesson.title}**: ${lesson.prevention}`);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
if (ctx.active_plans && ctx.active_plans.length > 0) {
|
|
4304
|
+
parts.push("\n## \u{1F4CB} Active Plans");
|
|
4305
|
+
for (const plan of ctx.active_plans.slice(0, 3)) {
|
|
4306
|
+
parts.push(`- ${plan.title} (${plan.status})`);
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
if (ctx.pending_tasks && ctx.pending_tasks.length > 0) {
|
|
4310
|
+
parts.push("\n## \u2705 Pending Tasks");
|
|
4311
|
+
for (const task of ctx.pending_tasks.slice(0, 5)) {
|
|
4312
|
+
parts.push(`- ${task.title}`);
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
if (ctx.recent_decisions && ctx.recent_decisions.length > 0) {
|
|
4316
|
+
parts.push("\n## \u{1F4DD} Recent Decisions");
|
|
4317
|
+
for (const decision of ctx.recent_decisions.slice(0, 3)) {
|
|
4318
|
+
parts.push(`- **${decision.title}**`);
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
parts.push("\n---");
|
|
4322
|
+
parts.push('Call `mcp__contextstream__context(user_message="...")` for task-specific context.');
|
|
4323
|
+
return parts.join("\n");
|
|
4324
|
+
}
|
|
4325
|
+
async function runSessionInitHook() {
|
|
4326
|
+
if (!ENABLED12) {
|
|
4327
|
+
process.exit(0);
|
|
4328
|
+
}
|
|
4329
|
+
let inputData = "";
|
|
4330
|
+
for await (const chunk of process.stdin) {
|
|
4331
|
+
inputData += chunk;
|
|
4332
|
+
}
|
|
4333
|
+
if (!inputData.trim()) {
|
|
4334
|
+
process.exit(0);
|
|
4335
|
+
}
|
|
4336
|
+
let input;
|
|
4337
|
+
try {
|
|
4338
|
+
input = JSON.parse(inputData);
|
|
4339
|
+
} catch {
|
|
4340
|
+
process.exit(0);
|
|
4341
|
+
}
|
|
4342
|
+
const cwd = input.cwd || process.cwd();
|
|
4343
|
+
loadConfigFromMcpJson7(cwd);
|
|
4344
|
+
const context = await fetchSessionContext();
|
|
4345
|
+
const formattedContext = formatContext(context);
|
|
4346
|
+
console.log(
|
|
4347
|
+
JSON.stringify({
|
|
4348
|
+
hookSpecificOutput: {
|
|
4349
|
+
hookEventName: "SessionStart",
|
|
4350
|
+
additionalContext: formattedContext
|
|
4351
|
+
}
|
|
4352
|
+
})
|
|
4353
|
+
);
|
|
4354
|
+
process.exit(0);
|
|
4355
|
+
}
|
|
4356
|
+
var ENABLED12, API_URL9, API_KEY9, WORKSPACE_ID7, PROJECT_ID, isDirectRun12;
|
|
4357
|
+
var init_session_init = __esm({
|
|
4358
|
+
"src/hooks/session-init.ts"() {
|
|
4359
|
+
"use strict";
|
|
4360
|
+
ENABLED12 = process.env.CONTEXTSTREAM_SESSION_INIT_ENABLED !== "false";
|
|
4361
|
+
API_URL9 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4362
|
+
API_KEY9 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4363
|
+
WORKSPACE_ID7 = null;
|
|
4364
|
+
PROJECT_ID = null;
|
|
4365
|
+
isDirectRun12 = process.argv[1]?.includes("session-init") || process.argv[2] === "session-init";
|
|
4366
|
+
if (isDirectRun12) {
|
|
4367
|
+
runSessionInitHook().catch(() => process.exit(0));
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
});
|
|
4371
|
+
|
|
4372
|
+
// src/hooks/session-end.ts
|
|
4373
|
+
var session_end_exports = {};
|
|
4374
|
+
__export(session_end_exports, {
|
|
4375
|
+
runSessionEndHook: () => runSessionEndHook
|
|
4376
|
+
});
|
|
4377
|
+
import * as fs18 from "node:fs";
|
|
4378
|
+
import * as path19 from "node:path";
|
|
4379
|
+
import { homedir as homedir16 } from "node:os";
|
|
4380
|
+
function loadConfigFromMcpJson8(cwd) {
|
|
4381
|
+
let searchDir = path19.resolve(cwd);
|
|
4382
|
+
for (let i = 0; i < 5; i++) {
|
|
4383
|
+
if (!API_KEY10) {
|
|
4384
|
+
const mcpPath = path19.join(searchDir, ".mcp.json");
|
|
4385
|
+
if (fs18.existsSync(mcpPath)) {
|
|
4386
|
+
try {
|
|
4387
|
+
const content = fs18.readFileSync(mcpPath, "utf-8");
|
|
4388
|
+
const config = JSON.parse(content);
|
|
4389
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4390
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4391
|
+
API_KEY10 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4392
|
+
}
|
|
4393
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4394
|
+
API_URL10 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4395
|
+
}
|
|
4396
|
+
} catch {
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
if (!WORKSPACE_ID8) {
|
|
4401
|
+
const csConfigPath = path19.join(searchDir, ".contextstream", "config.json");
|
|
4402
|
+
if (fs18.existsSync(csConfigPath)) {
|
|
4403
|
+
try {
|
|
4404
|
+
const content = fs18.readFileSync(csConfigPath, "utf-8");
|
|
4405
|
+
const csConfig = JSON.parse(content);
|
|
4406
|
+
if (csConfig.workspace_id) {
|
|
4407
|
+
WORKSPACE_ID8 = csConfig.workspace_id;
|
|
4408
|
+
}
|
|
4409
|
+
} catch {
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
const parentDir = path19.dirname(searchDir);
|
|
4414
|
+
if (parentDir === searchDir) break;
|
|
4415
|
+
searchDir = parentDir;
|
|
4416
|
+
}
|
|
4417
|
+
if (!API_KEY10) {
|
|
4418
|
+
const homeMcpPath = path19.join(homedir16(), ".mcp.json");
|
|
4419
|
+
if (fs18.existsSync(homeMcpPath)) {
|
|
4420
|
+
try {
|
|
4421
|
+
const content = fs18.readFileSync(homeMcpPath, "utf-8");
|
|
4422
|
+
const config = JSON.parse(content);
|
|
4423
|
+
const csEnv = config.mcpServers?.contextstream?.env;
|
|
4424
|
+
if (csEnv?.CONTEXTSTREAM_API_KEY) {
|
|
4425
|
+
API_KEY10 = csEnv.CONTEXTSTREAM_API_KEY;
|
|
4426
|
+
}
|
|
4427
|
+
if (csEnv?.CONTEXTSTREAM_API_URL) {
|
|
4428
|
+
API_URL10 = csEnv.CONTEXTSTREAM_API_URL;
|
|
4429
|
+
}
|
|
4430
|
+
} catch {
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
function parseTranscriptStats(transcriptPath) {
|
|
4436
|
+
const stats = {
|
|
4437
|
+
messageCount: 0,
|
|
4438
|
+
toolCallCount: 0,
|
|
4439
|
+
duration: 0,
|
|
4440
|
+
filesModified: []
|
|
4441
|
+
};
|
|
4442
|
+
if (!transcriptPath || !fs18.existsSync(transcriptPath)) {
|
|
4443
|
+
return stats;
|
|
4444
|
+
}
|
|
4445
|
+
try {
|
|
4446
|
+
const content = fs18.readFileSync(transcriptPath, "utf-8");
|
|
4447
|
+
const lines = content.split("\n");
|
|
4448
|
+
let firstTimestamp = null;
|
|
4449
|
+
let lastTimestamp = null;
|
|
4450
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
4451
|
+
for (const line of lines) {
|
|
4452
|
+
if (!line.trim()) continue;
|
|
4453
|
+
try {
|
|
4454
|
+
const entry = JSON.parse(line);
|
|
4455
|
+
if (entry.type === "user" || entry.type === "assistant") {
|
|
4456
|
+
stats.messageCount++;
|
|
4457
|
+
} else if (entry.type === "tool_use") {
|
|
4458
|
+
stats.toolCallCount++;
|
|
4459
|
+
if (["Write", "Edit", "NotebookEdit"].includes(entry.name || "")) {
|
|
4460
|
+
const filePath = entry.input?.file_path;
|
|
4461
|
+
if (filePath) {
|
|
4462
|
+
modifiedFiles.add(filePath);
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
if (entry.timestamp) {
|
|
4467
|
+
const ts = new Date(entry.timestamp);
|
|
4468
|
+
if (!firstTimestamp || ts < firstTimestamp) {
|
|
4469
|
+
firstTimestamp = ts;
|
|
4470
|
+
}
|
|
4471
|
+
if (!lastTimestamp || ts > lastTimestamp) {
|
|
4472
|
+
lastTimestamp = ts;
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
} catch {
|
|
4476
|
+
continue;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
if (firstTimestamp && lastTimestamp) {
|
|
4480
|
+
stats.duration = Math.round((lastTimestamp.getTime() - firstTimestamp.getTime()) / 1e3);
|
|
4481
|
+
}
|
|
4482
|
+
stats.filesModified = Array.from(modifiedFiles);
|
|
4483
|
+
} catch {
|
|
4484
|
+
}
|
|
4485
|
+
return stats;
|
|
4486
|
+
}
|
|
4487
|
+
async function finalizeSession(sessionId, stats, reason) {
|
|
4488
|
+
if (!API_KEY10) return;
|
|
4489
|
+
const payload = {
|
|
4490
|
+
event_type: "session_end",
|
|
4491
|
+
title: `Session Ended: ${reason}`,
|
|
4492
|
+
content: JSON.stringify({
|
|
4493
|
+
session_id: sessionId,
|
|
4494
|
+
reason,
|
|
4495
|
+
stats: {
|
|
4496
|
+
messages: stats.messageCount,
|
|
4497
|
+
tool_calls: stats.toolCallCount,
|
|
4498
|
+
duration_seconds: stats.duration,
|
|
4499
|
+
files_modified: stats.filesModified.length
|
|
4500
|
+
},
|
|
4501
|
+
files_modified: stats.filesModified.slice(0, 20),
|
|
4502
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4503
|
+
}),
|
|
4504
|
+
importance: "low",
|
|
4505
|
+
tags: ["session", "end", reason],
|
|
4506
|
+
source_type: "hook",
|
|
4507
|
+
session_id: sessionId
|
|
4508
|
+
};
|
|
4509
|
+
if (WORKSPACE_ID8) {
|
|
4510
|
+
payload.workspace_id = WORKSPACE_ID8;
|
|
4511
|
+
}
|
|
4512
|
+
try {
|
|
4513
|
+
const controller = new AbortController();
|
|
4514
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
4515
|
+
await fetch(`${API_URL10}/api/v1/memory/events`, {
|
|
4516
|
+
method: "POST",
|
|
4517
|
+
headers: {
|
|
4518
|
+
"Content-Type": "application/json",
|
|
4519
|
+
"X-API-Key": API_KEY10
|
|
4520
|
+
},
|
|
4521
|
+
body: JSON.stringify(payload),
|
|
4522
|
+
signal: controller.signal
|
|
4523
|
+
});
|
|
4524
|
+
clearTimeout(timeoutId);
|
|
4525
|
+
} catch {
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
async function runSessionEndHook() {
|
|
4529
|
+
if (!ENABLED13) {
|
|
4530
|
+
process.exit(0);
|
|
4531
|
+
}
|
|
4532
|
+
let inputData = "";
|
|
4533
|
+
for await (const chunk of process.stdin) {
|
|
4534
|
+
inputData += chunk;
|
|
4535
|
+
}
|
|
4536
|
+
if (!inputData.trim()) {
|
|
4537
|
+
process.exit(0);
|
|
4538
|
+
}
|
|
4539
|
+
let input;
|
|
4540
|
+
try {
|
|
4541
|
+
input = JSON.parse(inputData);
|
|
4542
|
+
} catch {
|
|
4543
|
+
process.exit(0);
|
|
4544
|
+
}
|
|
4545
|
+
const cwd = input.cwd || process.cwd();
|
|
4546
|
+
loadConfigFromMcpJson8(cwd);
|
|
4547
|
+
const sessionId = input.session_id || "unknown";
|
|
4548
|
+
const transcriptPath = input.transcript_path || "";
|
|
4549
|
+
const reason = input.reason || "user_exit";
|
|
4550
|
+
const stats = parseTranscriptStats(transcriptPath);
|
|
4551
|
+
await finalizeSession(sessionId, stats, reason);
|
|
4552
|
+
process.exit(0);
|
|
4553
|
+
}
|
|
4554
|
+
var ENABLED13, API_URL10, API_KEY10, WORKSPACE_ID8, isDirectRun13;
|
|
4555
|
+
var init_session_end = __esm({
|
|
4556
|
+
"src/hooks/session-end.ts"() {
|
|
4557
|
+
"use strict";
|
|
4558
|
+
ENABLED13 = process.env.CONTEXTSTREAM_SESSION_END_ENABLED !== "false";
|
|
4559
|
+
API_URL10 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
4560
|
+
API_KEY10 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
4561
|
+
WORKSPACE_ID8 = null;
|
|
4562
|
+
isDirectRun13 = process.argv[1]?.includes("session-end") || process.argv[2] === "session-end";
|
|
4563
|
+
if (isDirectRun13) {
|
|
4564
|
+
runSessionEndHook().catch(() => process.exit(0));
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
});
|
|
4568
|
+
|
|
4569
|
+
// src/hooks/on-save-intent.ts
|
|
4570
|
+
var on_save_intent_exports = {};
|
|
4571
|
+
__export(on_save_intent_exports, {
|
|
4572
|
+
runOnSaveIntentHook: () => runOnSaveIntentHook
|
|
4573
|
+
});
|
|
4574
|
+
function detectsSaveIntent(text) {
|
|
4575
|
+
const hasSaveIntent = SAVE_PATTERNS.some((p) => p.test(text));
|
|
4576
|
+
const isLocalFile = LOCAL_FILE_PATTERNS.some((p) => p.test(text));
|
|
4577
|
+
return { hasSaveIntent, isLocalFile };
|
|
4578
|
+
}
|
|
4579
|
+
async function runOnSaveIntentHook() {
|
|
4580
|
+
if (!ENABLED14) {
|
|
4581
|
+
process.exit(0);
|
|
4582
|
+
}
|
|
4583
|
+
let inputData = "";
|
|
4584
|
+
for await (const chunk of process.stdin) {
|
|
4585
|
+
inputData += chunk;
|
|
4586
|
+
}
|
|
4587
|
+
if (!inputData.trim()) {
|
|
4588
|
+
process.exit(0);
|
|
4589
|
+
}
|
|
4590
|
+
let input;
|
|
4591
|
+
try {
|
|
4592
|
+
input = JSON.parse(inputData);
|
|
4593
|
+
} catch {
|
|
4594
|
+
process.exit(0);
|
|
4595
|
+
}
|
|
4596
|
+
let prompt = input.prompt || "";
|
|
4597
|
+
if (!prompt && input.session?.messages) {
|
|
4598
|
+
for (const msg of [...input.session.messages].reverse()) {
|
|
4599
|
+
if (msg.role === "user") {
|
|
4600
|
+
if (typeof msg.content === "string") {
|
|
4601
|
+
prompt = msg.content;
|
|
4602
|
+
} else if (Array.isArray(msg.content)) {
|
|
4603
|
+
for (const block of msg.content) {
|
|
4604
|
+
if (block.type === "text" && block.text) {
|
|
4605
|
+
prompt = block.text;
|
|
4606
|
+
break;
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
break;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
if (!prompt) {
|
|
4615
|
+
process.exit(0);
|
|
4616
|
+
}
|
|
4617
|
+
const { hasSaveIntent, isLocalFile } = detectsSaveIntent(prompt);
|
|
4618
|
+
if (hasSaveIntent || isLocalFile) {
|
|
4619
|
+
console.log(
|
|
4620
|
+
JSON.stringify({
|
|
4621
|
+
hookSpecificOutput: {
|
|
4622
|
+
hookEventName: "UserPromptSubmit",
|
|
4623
|
+
additionalContext: SAVE_GUIDANCE
|
|
4624
|
+
}
|
|
4625
|
+
})
|
|
4626
|
+
);
|
|
4627
|
+
}
|
|
4628
|
+
process.exit(0);
|
|
4629
|
+
}
|
|
4630
|
+
var ENABLED14, SAVE_PATTERNS, LOCAL_FILE_PATTERNS, SAVE_GUIDANCE, isDirectRun14;
|
|
4631
|
+
var init_on_save_intent = __esm({
|
|
4632
|
+
"src/hooks/on-save-intent.ts"() {
|
|
4633
|
+
"use strict";
|
|
4634
|
+
ENABLED14 = process.env.CONTEXTSTREAM_SAVE_INTENT_ENABLED !== "false";
|
|
4635
|
+
SAVE_PATTERNS = [
|
|
4636
|
+
// Direct save requests
|
|
4637
|
+
/\b(save|store|record|capture|log|document|write down|note down|keep track)\b.*\b(this|that|it|the)\b/i,
|
|
4638
|
+
/\b(save|store|record|capture|log)\b.*\b(to|in|for)\b.*\b(contextstream|memory|later|reference|future)\b/i,
|
|
4639
|
+
// Document creation
|
|
4640
|
+
/\b(create|make|write|draft)\b.*\b(a|the)\b.*\b(document|doc|note|summary|report|spec|design)\b/i,
|
|
4641
|
+
/\b(document|summarize|write up)\b.*\b(this|that|the|our)\b.*\b(decision|discussion|conversation|meeting|finding)\b/i,
|
|
4642
|
+
// Memory/reference requests
|
|
4643
|
+
/\b(remember|don't forget|keep in mind|note that|important to remember)\b/i,
|
|
4644
|
+
/\bfor\s+(future|later)\s+reference\b/i,
|
|
4645
|
+
/\b(add|put)\s+(this|it|that)\s+(to|in)\s+(memory|notes|docs)\b/i,
|
|
4646
|
+
// Decision tracking
|
|
4647
|
+
/\b(we\s+)?(decided|agreed|concluded|determined)\b.*\b(to|that)\b/i,
|
|
4648
|
+
/\blet('s|s)\s+document\b/i,
|
|
4649
|
+
/\bsave\s+(this|the)\s+(decision|choice|approach)\b/i,
|
|
4650
|
+
// Implementation/design docs
|
|
4651
|
+
/\b(implementation|design|architecture|spec)\s+(doc|document|plan)\b/i,
|
|
4652
|
+
/\bwrite\s+(the|a|an)\s+.*(md|markdown|readme)\b/i
|
|
4653
|
+
];
|
|
4654
|
+
LOCAL_FILE_PATTERNS = [
|
|
4655
|
+
/\b(save|write|create)\s+(it|this|the\s+\w+)\s+(to|in|as)\s+[./~]/i,
|
|
4656
|
+
/\b(save|write)\s+to\s+.*(\.md|\.txt|\.json|docs\/|notes\/)/i,
|
|
4657
|
+
/\bcreate\s+(a|the)\s+file\b/i
|
|
4658
|
+
];
|
|
4659
|
+
SAVE_GUIDANCE = `[CONTEXTSTREAM DOCUMENT STORAGE]
|
|
4660
|
+
The user wants to save/store content. Use ContextStream instead of local files:
|
|
4661
|
+
|
|
4662
|
+
**For decisions/notes:**
|
|
4663
|
+
\`\`\`
|
|
4664
|
+
mcp__contextstream__session(
|
|
4665
|
+
action="capture",
|
|
4666
|
+
event_type="decision|note|insight",
|
|
4667
|
+
title="...",
|
|
4668
|
+
content="...",
|
|
4669
|
+
importance="high|medium|low"
|
|
4670
|
+
)
|
|
4671
|
+
\`\`\`
|
|
4672
|
+
|
|
4673
|
+
**For documents/specs:**
|
|
4674
|
+
\`\`\`
|
|
4675
|
+
mcp__contextstream__docs(
|
|
4676
|
+
action="create",
|
|
4677
|
+
title="...",
|
|
4678
|
+
content="...",
|
|
4679
|
+
doc_type="implementation|design|spec|guide"
|
|
4680
|
+
)
|
|
4681
|
+
\`\`\`
|
|
4682
|
+
|
|
4683
|
+
**For plans:**
|
|
4684
|
+
\`\`\`
|
|
4685
|
+
mcp__contextstream__session(
|
|
4686
|
+
action="capture_plan",
|
|
4687
|
+
title="...",
|
|
4688
|
+
steps=[...]
|
|
4689
|
+
)
|
|
4690
|
+
\`\`\`
|
|
4691
|
+
|
|
4692
|
+
**Why ContextStream?**
|
|
4693
|
+
- Persists across sessions (local files don't)
|
|
4694
|
+
- Searchable and retrievable
|
|
4695
|
+
- Shows up in context automatically
|
|
4696
|
+
- Can be shared with team
|
|
4697
|
+
|
|
4698
|
+
Only save to local files if user explicitly requests a specific file path.
|
|
4699
|
+
[END GUIDANCE]`;
|
|
4700
|
+
isDirectRun14 = process.argv[1]?.includes("on-save-intent") || process.argv[2] === "on-save-intent";
|
|
4701
|
+
if (isDirectRun14) {
|
|
4702
|
+
runOnSaveIntentHook().catch(() => process.exit(0));
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
});
|
|
4706
|
+
|
|
4707
|
+
// src/verify-key.ts
|
|
4708
|
+
var verify_key_exports = {};
|
|
4709
|
+
__export(verify_key_exports, {
|
|
4710
|
+
loadApiKey: () => loadApiKey,
|
|
4711
|
+
maskApiKey: () => maskApiKey2,
|
|
4712
|
+
runVerifyKey: () => runVerifyKey,
|
|
4713
|
+
validateApiKey: () => validateApiKey
|
|
4714
|
+
});
|
|
4715
|
+
import * as fs19 from "node:fs";
|
|
4716
|
+
import * as path20 from "node:path";
|
|
4717
|
+
import { homedir as homedir17 } from "node:os";
|
|
4718
|
+
function maskApiKey2(key) {
|
|
4719
|
+
if (!key || key.length < 10) return "***";
|
|
4720
|
+
const prefix = key.slice(0, 6);
|
|
4721
|
+
const suffix = key.slice(-4);
|
|
4722
|
+
return `${prefix}...${suffix}`;
|
|
4723
|
+
}
|
|
4724
|
+
function extractFromMcpConfig(config) {
|
|
4725
|
+
if (!config.mcpServers) return {};
|
|
4726
|
+
const priorityNames = ["contextstream", "ContextStream", "context-stream"];
|
|
4727
|
+
for (const name of priorityNames) {
|
|
4728
|
+
const server = config.mcpServers[name];
|
|
4729
|
+
if (server?.env?.CONTEXTSTREAM_API_KEY) {
|
|
4730
|
+
return {
|
|
4731
|
+
apiKey: server.env.CONTEXTSTREAM_API_KEY,
|
|
4732
|
+
apiUrl: server.env.CONTEXTSTREAM_API_URL
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
for (const [, server] of Object.entries(config.mcpServers)) {
|
|
4737
|
+
if (server?.env?.CONTEXTSTREAM_API_KEY) {
|
|
4738
|
+
return {
|
|
4739
|
+
apiKey: server.env.CONTEXTSTREAM_API_KEY,
|
|
4740
|
+
apiUrl: server.env.CONTEXTSTREAM_API_URL
|
|
4741
|
+
};
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
return {};
|
|
4745
|
+
}
|
|
4746
|
+
function getClaudeDesktopConfigPath() {
|
|
4747
|
+
const platform = process.platform;
|
|
4748
|
+
if (platform === "darwin") {
|
|
4749
|
+
return path20.join(homedir17(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4750
|
+
} else if (platform === "win32") {
|
|
4751
|
+
return path20.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
4752
|
+
} else {
|
|
4753
|
+
return path20.join(homedir17(), ".config", "Claude", "claude_desktop_config.json");
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
function loadApiKey() {
|
|
4757
|
+
let apiKey = null;
|
|
4758
|
+
let apiUrl = "https://api.contextstream.io";
|
|
4759
|
+
let source = "none";
|
|
4760
|
+
if (process.env.CONTEXTSTREAM_API_KEY) {
|
|
4761
|
+
apiKey = process.env.CONTEXTSTREAM_API_KEY;
|
|
4762
|
+
source = "environment";
|
|
4763
|
+
if (process.env.CONTEXTSTREAM_API_URL) {
|
|
4764
|
+
apiUrl = process.env.CONTEXTSTREAM_API_URL;
|
|
4765
|
+
}
|
|
4766
|
+
return { apiKey, apiUrl, source };
|
|
4767
|
+
}
|
|
4768
|
+
let searchDir = process.cwd();
|
|
4769
|
+
for (let i = 0; i < 5; i++) {
|
|
4770
|
+
const projectMcpPath = path20.join(searchDir, ".mcp.json");
|
|
4771
|
+
if (fs19.existsSync(projectMcpPath)) {
|
|
4772
|
+
try {
|
|
4773
|
+
const content = fs19.readFileSync(projectMcpPath, "utf-8");
|
|
4774
|
+
const config = JSON.parse(content);
|
|
4775
|
+
const extracted = extractFromMcpConfig(config);
|
|
4776
|
+
if (extracted.apiKey) {
|
|
4777
|
+
apiKey = extracted.apiKey;
|
|
4778
|
+
source = `${projectMcpPath}`;
|
|
4779
|
+
if (extracted.apiUrl) {
|
|
4780
|
+
apiUrl = extracted.apiUrl;
|
|
4781
|
+
}
|
|
4782
|
+
return { apiKey, apiUrl, source };
|
|
4783
|
+
}
|
|
4784
|
+
} catch {
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
const parentDir = path20.dirname(searchDir);
|
|
4788
|
+
if (parentDir === searchDir) break;
|
|
4789
|
+
searchDir = parentDir;
|
|
4790
|
+
}
|
|
4791
|
+
const globalMcpPath = path20.join(homedir17(), ".mcp.json");
|
|
4792
|
+
if (fs19.existsSync(globalMcpPath)) {
|
|
4793
|
+
try {
|
|
4794
|
+
const content = fs19.readFileSync(globalMcpPath, "utf-8");
|
|
4795
|
+
const config = JSON.parse(content);
|
|
4796
|
+
const extracted = extractFromMcpConfig(config);
|
|
4797
|
+
if (extracted.apiKey) {
|
|
4798
|
+
apiKey = extracted.apiKey;
|
|
4799
|
+
source = "~/.mcp.json";
|
|
4800
|
+
if (extracted.apiUrl) {
|
|
4801
|
+
apiUrl = extracted.apiUrl;
|
|
4802
|
+
}
|
|
4803
|
+
return { apiKey, apiUrl, source };
|
|
4804
|
+
}
|
|
4805
|
+
} catch {
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
const cursorPaths = [
|
|
4809
|
+
path20.join(process.cwd(), ".cursor", "mcp.json"),
|
|
4810
|
+
path20.join(homedir17(), ".cursor", "mcp.json")
|
|
4811
|
+
];
|
|
4812
|
+
for (const cursorPath of cursorPaths) {
|
|
4813
|
+
if (fs19.existsSync(cursorPath)) {
|
|
4814
|
+
try {
|
|
4815
|
+
const content = fs19.readFileSync(cursorPath, "utf-8");
|
|
4816
|
+
const config = JSON.parse(content);
|
|
4817
|
+
const extracted = extractFromMcpConfig(config);
|
|
4818
|
+
if (extracted.apiKey) {
|
|
4819
|
+
apiKey = extracted.apiKey;
|
|
4820
|
+
source = cursorPath;
|
|
4821
|
+
if (extracted.apiUrl) {
|
|
4822
|
+
apiUrl = extracted.apiUrl;
|
|
4823
|
+
}
|
|
4824
|
+
return { apiKey, apiUrl, source };
|
|
4825
|
+
}
|
|
4826
|
+
} catch {
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
const claudeDesktopPath = getClaudeDesktopConfigPath();
|
|
4831
|
+
if (fs19.existsSync(claudeDesktopPath)) {
|
|
4832
|
+
try {
|
|
4833
|
+
const content = fs19.readFileSync(claudeDesktopPath, "utf-8");
|
|
4834
|
+
const config = JSON.parse(content);
|
|
4835
|
+
const extracted = extractFromMcpConfig(config);
|
|
4836
|
+
if (extracted.apiKey) {
|
|
4837
|
+
apiKey = extracted.apiKey;
|
|
4838
|
+
source = claudeDesktopPath;
|
|
4839
|
+
if (extracted.apiUrl) {
|
|
4840
|
+
apiUrl = extracted.apiUrl;
|
|
4841
|
+
}
|
|
4842
|
+
return { apiKey, apiUrl, source };
|
|
4843
|
+
}
|
|
4844
|
+
} catch {
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
const vscodePaths = [
|
|
4848
|
+
path20.join(homedir17(), ".vscode", "mcp.json"),
|
|
4849
|
+
path20.join(homedir17(), ".codeium", "windsurf", "mcp_config.json"),
|
|
4850
|
+
path20.join(homedir17(), ".continue", "config.json")
|
|
4851
|
+
];
|
|
4852
|
+
for (const vsPath of vscodePaths) {
|
|
4853
|
+
if (fs19.existsSync(vsPath)) {
|
|
4854
|
+
try {
|
|
4855
|
+
const content = fs19.readFileSync(vsPath, "utf-8");
|
|
4856
|
+
const config = JSON.parse(content);
|
|
4857
|
+
const extracted = extractFromMcpConfig(config);
|
|
4858
|
+
if (extracted.apiKey) {
|
|
4859
|
+
apiKey = extracted.apiKey;
|
|
4860
|
+
source = vsPath;
|
|
4861
|
+
if (extracted.apiUrl) {
|
|
4862
|
+
apiUrl = extracted.apiUrl;
|
|
4863
|
+
}
|
|
4864
|
+
return { apiKey, apiUrl, source };
|
|
4865
|
+
}
|
|
4866
|
+
} catch {
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
const credentialsPath = path20.join(homedir17(), ".contextstream", "credentials.json");
|
|
4871
|
+
if (fs19.existsSync(credentialsPath)) {
|
|
4872
|
+
try {
|
|
4873
|
+
const content = fs19.readFileSync(credentialsPath, "utf-8");
|
|
4874
|
+
const creds = JSON.parse(content);
|
|
4875
|
+
if (creds.api_key) {
|
|
4876
|
+
apiKey = creds.api_key;
|
|
4877
|
+
source = "~/.contextstream/credentials.json";
|
|
4878
|
+
if (creds.api_url) {
|
|
4879
|
+
apiUrl = creds.api_url;
|
|
4880
|
+
}
|
|
4881
|
+
return { apiKey, apiUrl, source };
|
|
4882
|
+
}
|
|
4883
|
+
} catch {
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
return { apiKey, apiUrl, source };
|
|
4887
|
+
}
|
|
4888
|
+
async function validateApiKey(apiKey, apiUrl) {
|
|
3107
4889
|
try {
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
4890
|
+
const controller = new AbortController();
|
|
4891
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
4892
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/me`, {
|
|
4893
|
+
method: "GET",
|
|
4894
|
+
headers: {
|
|
4895
|
+
"X-API-Key": apiKey
|
|
4896
|
+
},
|
|
4897
|
+
signal: controller.signal
|
|
4898
|
+
});
|
|
4899
|
+
clearTimeout(timeoutId);
|
|
4900
|
+
if (response.ok) {
|
|
4901
|
+
const data = await response.json();
|
|
4902
|
+
return {
|
|
4903
|
+
valid: true,
|
|
4904
|
+
masked_key: maskApiKey2(apiKey),
|
|
4905
|
+
email: data.email,
|
|
4906
|
+
name: data.name || data.full_name,
|
|
4907
|
+
plan: data.plan_name || data.plan || "free",
|
|
4908
|
+
workspace_name: data.workspace?.name
|
|
4909
|
+
};
|
|
4910
|
+
} else if (response.status === 401) {
|
|
4911
|
+
return {
|
|
4912
|
+
valid: false,
|
|
4913
|
+
masked_key: maskApiKey2(apiKey),
|
|
4914
|
+
error: "Invalid or expired API key"
|
|
4915
|
+
};
|
|
4916
|
+
} else {
|
|
4917
|
+
return {
|
|
4918
|
+
valid: false,
|
|
4919
|
+
masked_key: maskApiKey2(apiKey),
|
|
4920
|
+
error: `API error: ${response.status}`
|
|
4921
|
+
};
|
|
4922
|
+
}
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
return {
|
|
4925
|
+
valid: false,
|
|
4926
|
+
masked_key: maskApiKey2(apiKey),
|
|
4927
|
+
error: `Connection error: ${error instanceof Error ? error.message : String(error)}`
|
|
4928
|
+
};
|
|
3116
4929
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
const
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
4930
|
+
}
|
|
4931
|
+
async function runVerifyKey(outputJson) {
|
|
4932
|
+
const { apiKey, apiUrl, source } = loadApiKey();
|
|
4933
|
+
if (!apiKey) {
|
|
4934
|
+
const result2 = {
|
|
4935
|
+
valid: false,
|
|
4936
|
+
masked_key: "",
|
|
4937
|
+
error: "No API key found. Run 'contextstream-mcp setup' to configure."
|
|
4938
|
+
};
|
|
4939
|
+
if (outputJson) {
|
|
4940
|
+
console.log(JSON.stringify(result2));
|
|
4941
|
+
} else {
|
|
4942
|
+
console.log("\u274C No API key found");
|
|
4943
|
+
console.log(" Run 'contextstream-mcp setup' to configure your API key.");
|
|
4944
|
+
}
|
|
4945
|
+
return result2;
|
|
3124
4946
|
}
|
|
3125
|
-
const
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
4947
|
+
const result = await validateApiKey(apiKey, apiUrl);
|
|
4948
|
+
if (outputJson) {
|
|
4949
|
+
console.log(JSON.stringify({ ...result, source }));
|
|
4950
|
+
} else {
|
|
4951
|
+
console.log("");
|
|
4952
|
+
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");
|
|
4953
|
+
console.log(" ContextStream API Key");
|
|
4954
|
+
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");
|
|
4955
|
+
console.log("");
|
|
4956
|
+
console.log(` Key: ${result.masked_key}`);
|
|
4957
|
+
console.log(` Source: ${source}`);
|
|
4958
|
+
if (result.valid) {
|
|
4959
|
+
console.log(` Status: \u2713 Valid`);
|
|
4960
|
+
if (result.email) {
|
|
4961
|
+
console.log(` Account: ${result.email}`);
|
|
4962
|
+
}
|
|
4963
|
+
if (result.name) {
|
|
4964
|
+
console.log(` Name: ${result.name}`);
|
|
4965
|
+
}
|
|
4966
|
+
if (result.plan) {
|
|
4967
|
+
console.log(` Plan: ${result.plan}`);
|
|
4968
|
+
}
|
|
4969
|
+
if (result.workspace_name) {
|
|
4970
|
+
console.log(` Workspace: ${result.workspace_name}`);
|
|
4971
|
+
}
|
|
4972
|
+
} else {
|
|
4973
|
+
console.log(` Status: \u2717 Invalid`);
|
|
4974
|
+
console.log(` Error: ${result.error}`);
|
|
4975
|
+
}
|
|
4976
|
+
console.log("");
|
|
3130
4977
|
}
|
|
3131
|
-
|
|
4978
|
+
return result;
|
|
3132
4979
|
}
|
|
3133
|
-
var
|
|
3134
|
-
|
|
3135
|
-
"src/hooks/auto-rules.ts"() {
|
|
4980
|
+
var init_verify_key = __esm({
|
|
4981
|
+
"src/verify-key.ts"() {
|
|
3136
4982
|
"use strict";
|
|
3137
|
-
API_URL3 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
|
|
3138
|
-
API_KEY3 = process.env.CONTEXTSTREAM_API_KEY || "";
|
|
3139
|
-
ENABLED6 = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
|
|
3140
|
-
MARKER_FILE = path12.join(homedir9(), ".contextstream", ".auto-rules-ran");
|
|
3141
|
-
COOLDOWN_MS = 4 * 60 * 60 * 1e3;
|
|
3142
|
-
isDirectRun6 = process.argv[1]?.includes("auto-rules") || process.argv[2] === "auto-rules";
|
|
3143
|
-
if (isDirectRun6) {
|
|
3144
|
-
runAutoRulesHook().catch(() => process.exit(0));
|
|
3145
|
-
}
|
|
3146
4983
|
}
|
|
3147
4984
|
});
|
|
3148
4985
|
|
|
@@ -3628,8 +5465,8 @@ function getErrorMap() {
|
|
|
3628
5465
|
|
|
3629
5466
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
3630
5467
|
var makeIssue = (params) => {
|
|
3631
|
-
const { data, path:
|
|
3632
|
-
const fullPath = [...
|
|
5468
|
+
const { data, path: path21, errorMaps, issueData } = params;
|
|
5469
|
+
const fullPath = [...path21, ...issueData.path || []];
|
|
3633
5470
|
const fullIssue = {
|
|
3634
5471
|
...issueData,
|
|
3635
5472
|
path: fullPath
|
|
@@ -3745,11 +5582,11 @@ var errorUtil;
|
|
|
3745
5582
|
|
|
3746
5583
|
// node_modules/zod/v3/types.js
|
|
3747
5584
|
var ParseInputLazyPath = class {
|
|
3748
|
-
constructor(parent, value,
|
|
5585
|
+
constructor(parent, value, path21, key) {
|
|
3749
5586
|
this._cachedPath = [];
|
|
3750
5587
|
this.parent = parent;
|
|
3751
5588
|
this.data = value;
|
|
3752
|
-
this._path =
|
|
5589
|
+
this._path = path21;
|
|
3753
5590
|
this._key = key;
|
|
3754
5591
|
}
|
|
3755
5592
|
get path() {
|
|
@@ -7199,6 +9036,9 @@ import { join } from "path";
|
|
|
7199
9036
|
var UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
|
|
7200
9037
|
var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
7201
9038
|
function getVersion() {
|
|
9039
|
+
if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
|
|
9040
|
+
return __CONTEXTSTREAM_VERSION__;
|
|
9041
|
+
}
|
|
7202
9042
|
try {
|
|
7203
9043
|
const require2 = createRequire(import.meta.url);
|
|
7204
9044
|
const pkg = require2("../package.json");
|
|
@@ -7482,14 +9322,14 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504])
|
|
|
7482
9322
|
var MAX_RETRIES = 3;
|
|
7483
9323
|
var BASE_DELAY = 1e3;
|
|
7484
9324
|
async function sleep(ms) {
|
|
7485
|
-
return new Promise((
|
|
9325
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
7486
9326
|
}
|
|
7487
|
-
async function request(config,
|
|
9327
|
+
async function request(config, path21, options = {}) {
|
|
7488
9328
|
const { apiUrl, userAgent } = config;
|
|
7489
9329
|
const authOverride = getAuthOverride();
|
|
7490
9330
|
const apiKey = authOverride?.apiKey ?? config.apiKey;
|
|
7491
9331
|
const jwt = authOverride?.jwt ?? config.jwt;
|
|
7492
|
-
const apiPath =
|
|
9332
|
+
const apiPath = path21.startsWith("/api/") ? path21 : `/api/v1${path21}`;
|
|
7493
9333
|
const url = `${apiUrl.replace(/\/$/, "")}${apiPath}`;
|
|
7494
9334
|
const maxRetries = options.retries ?? MAX_RETRIES;
|
|
7495
9335
|
const baseDelay = options.retryDelay ?? BASE_DELAY;
|
|
@@ -7635,9 +9475,9 @@ function extractErrorCode(payload) {
|
|
|
7635
9475
|
if (typeof payload.code === "string" && payload.code.trim()) return payload.code.trim();
|
|
7636
9476
|
return null;
|
|
7637
9477
|
}
|
|
7638
|
-
function detectIntegrationProvider(
|
|
7639
|
-
if (/\/github(\/|$)/i.test(
|
|
7640
|
-
if (/\/slack(\/|$)/i.test(
|
|
9478
|
+
function detectIntegrationProvider(path21) {
|
|
9479
|
+
if (/\/github(\/|$)/i.test(path21)) return "github";
|
|
9480
|
+
if (/\/slack(\/|$)/i.test(path21)) return "slack";
|
|
7641
9481
|
return null;
|
|
7642
9482
|
}
|
|
7643
9483
|
function rewriteNotFoundMessage(input) {
|
|
@@ -8290,10 +10130,10 @@ var PROJECT_MARKERS = [
|
|
|
8290
10130
|
];
|
|
8291
10131
|
function isMultiProjectFolder(folderPath) {
|
|
8292
10132
|
try {
|
|
8293
|
-
const
|
|
10133
|
+
const fs20 = __require("fs");
|
|
8294
10134
|
const pathModule = __require("path");
|
|
8295
|
-
const rootHasGit =
|
|
8296
|
-
const entries =
|
|
10135
|
+
const rootHasGit = fs20.existsSync(pathModule.join(folderPath, ".git"));
|
|
10136
|
+
const entries = fs20.readdirSync(folderPath, { withFileTypes: true });
|
|
8297
10137
|
const subdirs = entries.filter(
|
|
8298
10138
|
(e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules"
|
|
8299
10139
|
);
|
|
@@ -8301,7 +10141,7 @@ function isMultiProjectFolder(folderPath) {
|
|
|
8301
10141
|
for (const subdir of subdirs) {
|
|
8302
10142
|
const subdirPath = pathModule.join(folderPath, subdir.name);
|
|
8303
10143
|
for (const marker of PROJECT_MARKERS) {
|
|
8304
|
-
if (
|
|
10144
|
+
if (fs20.existsSync(pathModule.join(subdirPath, marker))) {
|
|
8305
10145
|
projectSubdirs.push(subdir.name);
|
|
8306
10146
|
break;
|
|
8307
10147
|
}
|
|
@@ -9935,6 +11775,35 @@ var ContextStreamClient = class {
|
|
|
9935
11775
|
}
|
|
9936
11776
|
});
|
|
9937
11777
|
}
|
|
11778
|
+
/**
|
|
11779
|
+
* Capture a memory event with a direct event_type.
|
|
11780
|
+
* Used for auto-save session snapshots and other system events.
|
|
11781
|
+
*/
|
|
11782
|
+
async captureMemoryEvent(params) {
|
|
11783
|
+
const withDefaults = this.withDefaults(params);
|
|
11784
|
+
const metadata = {
|
|
11785
|
+
...params.metadata || {}
|
|
11786
|
+
};
|
|
11787
|
+
if (params.tags && params.tags.length > 0) {
|
|
11788
|
+
metadata.tags = params.tags;
|
|
11789
|
+
}
|
|
11790
|
+
if (!metadata.captured_at) {
|
|
11791
|
+
metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
11792
|
+
}
|
|
11793
|
+
if (!metadata.source) {
|
|
11794
|
+
metadata.source = "mcp_auto_capture";
|
|
11795
|
+
}
|
|
11796
|
+
return this.createMemoryEvent({
|
|
11797
|
+
workspace_id: withDefaults.workspace_id,
|
|
11798
|
+
project_id: withDefaults.project_id,
|
|
11799
|
+
event_type: params.event_type,
|
|
11800
|
+
title: params.title,
|
|
11801
|
+
content: params.content,
|
|
11802
|
+
provenance: params.provenance,
|
|
11803
|
+
code_refs: params.code_refs,
|
|
11804
|
+
metadata
|
|
11805
|
+
});
|
|
11806
|
+
}
|
|
9938
11807
|
submitContextFeedback(body) {
|
|
9939
11808
|
return request(this.config, "/context/smart/feedback", { body: this.withDefaults(body) });
|
|
9940
11809
|
}
|
|
@@ -10339,9 +12208,9 @@ var ContextStreamClient = class {
|
|
|
10339
12208
|
candidateParts.push("## Relevant Code\n");
|
|
10340
12209
|
currentChars += 18;
|
|
10341
12210
|
const codeEntries = code.results.map((c) => {
|
|
10342
|
-
const
|
|
12211
|
+
const path21 = c.file_path || "file";
|
|
10343
12212
|
const content = c.content?.slice(0, 150) || "";
|
|
10344
|
-
return { path:
|
|
12213
|
+
return { path: path21, entry: `\u2022 ${path21}: ${content}...
|
|
10345
12214
|
` };
|
|
10346
12215
|
});
|
|
10347
12216
|
for (const c of codeEntries) {
|
|
@@ -10502,7 +12371,11 @@ var ContextStreamClient = class {
|
|
|
10502
12371
|
notice_inline: false,
|
|
10503
12372
|
// Session token tracking for context pressure
|
|
10504
12373
|
...params.session_tokens !== void 0 && { session_tokens: params.session_tokens },
|
|
10505
|
-
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold }
|
|
12374
|
+
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold },
|
|
12375
|
+
// Transcript save parameters
|
|
12376
|
+
...params.save_exchange !== void 0 && { save_exchange: params.save_exchange },
|
|
12377
|
+
...params.session_id !== void 0 && { session_id: params.session_id },
|
|
12378
|
+
...params.client_name !== void 0 && { client_name: params.client_name }
|
|
10506
12379
|
}
|
|
10507
12380
|
});
|
|
10508
12381
|
const data = unwrapApiResponse(apiResult);
|
|
@@ -12064,6 +13937,95 @@ ${context}`;
|
|
|
12064
13937
|
uuidSchema.parse(params.doc_id);
|
|
12065
13938
|
return request(this.config, `/docs/${params.doc_id}`, { method: "DELETE" });
|
|
12066
13939
|
}
|
|
13940
|
+
// -------------------------------------------------------------------------
|
|
13941
|
+
// Transcript methods (conversation session storage)
|
|
13942
|
+
// -------------------------------------------------------------------------
|
|
13943
|
+
/**
|
|
13944
|
+
* List transcripts for a workspace/project
|
|
13945
|
+
*/
|
|
13946
|
+
async listTranscripts(params) {
|
|
13947
|
+
const withDefaults = this.withDefaults(params || {});
|
|
13948
|
+
const query = new URLSearchParams();
|
|
13949
|
+
if (withDefaults.workspace_id) query.set("workspace_id", withDefaults.workspace_id);
|
|
13950
|
+
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
13951
|
+
if (params?.session_id) query.set("session_id", params.session_id);
|
|
13952
|
+
if (params?.client_name) query.set("client_name", params.client_name);
|
|
13953
|
+
if (params?.started_after) query.set("started_after", params.started_after);
|
|
13954
|
+
if (params?.started_before) query.set("started_before", params.started_before);
|
|
13955
|
+
if (params?.limit) query.set("per_page", String(params.limit));
|
|
13956
|
+
if (params?.page) query.set("page", String(params.page));
|
|
13957
|
+
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
13958
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
13959
|
+
return request(this.config, `/transcripts${suffix}`, { method: "GET" });
|
|
13960
|
+
}
|
|
13961
|
+
/**
|
|
13962
|
+
* Get a specific transcript by ID
|
|
13963
|
+
*/
|
|
13964
|
+
async getTranscript(transcript_id) {
|
|
13965
|
+
uuidSchema.parse(transcript_id);
|
|
13966
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "GET" });
|
|
13967
|
+
}
|
|
13968
|
+
/**
|
|
13969
|
+
* Search transcripts by content
|
|
13970
|
+
*/
|
|
13971
|
+
async searchTranscripts(params) {
|
|
13972
|
+
const withDefaults = this.withDefaults(params);
|
|
13973
|
+
const queryParams = new URLSearchParams();
|
|
13974
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
13975
|
+
queryParams.set("query", params.query);
|
|
13976
|
+
if (params.limit) queryParams.set("limit", String(params.limit));
|
|
13977
|
+
return request(this.config, `/transcripts/search?${queryParams.toString()}`, { method: "GET" });
|
|
13978
|
+
}
|
|
13979
|
+
/**
|
|
13980
|
+
* Delete a transcript
|
|
13981
|
+
*/
|
|
13982
|
+
async deleteTranscript(transcript_id) {
|
|
13983
|
+
uuidSchema.parse(transcript_id);
|
|
13984
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "DELETE" });
|
|
13985
|
+
}
|
|
13986
|
+
// -------------------------------------------------------------------------
|
|
13987
|
+
// Suggested Rules methods (ML-generated rule suggestions)
|
|
13988
|
+
// -------------------------------------------------------------------------
|
|
13989
|
+
/**
|
|
13990
|
+
* List suggested rules
|
|
13991
|
+
*/
|
|
13992
|
+
async listSuggestedRules(params) {
|
|
13993
|
+
const withDefaults = this.withDefaults(params || {});
|
|
13994
|
+
const queryParams = new URLSearchParams();
|
|
13995
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
13996
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
13997
|
+
if (params?.source_type) queryParams.set("source_type", params.source_type);
|
|
13998
|
+
if (params?.min_confidence) queryParams.set("min_confidence", String(params.min_confidence));
|
|
13999
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
14000
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
14001
|
+
return request(this.config, `/suggested-rules?${queryParams.toString()}`, { method: "GET" });
|
|
14002
|
+
}
|
|
14003
|
+
/**
|
|
14004
|
+
* Get pending suggested rules count
|
|
14005
|
+
*/
|
|
14006
|
+
async getSuggestedRulesPendingCount(params) {
|
|
14007
|
+
const withDefaults = this.withDefaults(params || {});
|
|
14008
|
+
const queryParams = new URLSearchParams();
|
|
14009
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
14010
|
+
return request(this.config, `/suggested-rules/pending-count?${queryParams.toString()}`, { method: "GET" });
|
|
14011
|
+
}
|
|
14012
|
+
/**
|
|
14013
|
+
* Get suggested rules feedback stats
|
|
14014
|
+
*/
|
|
14015
|
+
async getSuggestedRulesStats(params) {
|
|
14016
|
+
const withDefaults = this.withDefaults(params || {});
|
|
14017
|
+
const queryParams = new URLSearchParams();
|
|
14018
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
14019
|
+
return request(this.config, `/suggested-rules/stats?${queryParams.toString()}`, { method: "GET" });
|
|
14020
|
+
}
|
|
14021
|
+
/**
|
|
14022
|
+
* Perform action on suggested rule (accept/reject/modify)
|
|
14023
|
+
*/
|
|
14024
|
+
async suggestedRuleAction(params) {
|
|
14025
|
+
uuidSchema.parse(params.rule_id);
|
|
14026
|
+
const { rule_id, ...body } = params;
|
|
14027
|
+
return request(this.config, `/suggested-rules/${rule_id}/action`, { method: "POST", body });
|
|
14028
|
+
}
|
|
12067
14029
|
};
|
|
12068
14030
|
|
|
12069
14031
|
// src/tools.ts
|
|
@@ -13110,111 +15072,84 @@ function trackToolTokenSavings(client, tool, contextText, params, extraMetadata)
|
|
|
13110
15072
|
}
|
|
13111
15073
|
}
|
|
13112
15074
|
|
|
13113
|
-
// src/
|
|
13114
|
-
var SESSION_INIT_TIPS = [
|
|
13115
|
-
"AI work that doesn't disappear.",
|
|
13116
|
-
"Every conversation builds context. Every artifact persists.",
|
|
13117
|
-
"Your AI remembers. Now you can see what it knows.",
|
|
13118
|
-
"Context that survives sessions.",
|
|
13119
|
-
"The bridge between AI conversations and human artifacts."
|
|
13120
|
-
];
|
|
15075
|
+
// src/microcopy.ts
|
|
13121
15076
|
function getSessionInitTip(sessionId) {
|
|
13122
|
-
|
|
13123
|
-
|
|
13124
|
-
}
|
|
13125
|
-
var CAPTURE_HINTS = {
|
|
13126
|
-
// Core event types
|
|
13127
|
-
decision: "Future you will thank present you.",
|
|
13128
|
-
preference: "Noted. This will inform future suggestions.",
|
|
13129
|
-
insight: "Captured for future reference.",
|
|
13130
|
-
note: "Saved. Won't disappear when the chat does.",
|
|
13131
|
-
implementation: "Implementation recorded.",
|
|
13132
|
-
task: "Task tracked.",
|
|
13133
|
-
bug: "Bug logged for tracking.",
|
|
13134
|
-
feature: "Feature request captured.",
|
|
13135
|
-
plan: "This plan will be here when you come back.",
|
|
13136
|
-
correction: "Correction noted. Learning from this.",
|
|
13137
|
-
lesson: "Learn once, remember forever.",
|
|
13138
|
-
warning: "Warning logged.",
|
|
13139
|
-
frustration: "Feedback captured. We're listening.",
|
|
13140
|
-
conversation: "Conversation preserved.",
|
|
13141
|
-
session_snapshot: "Session state saved. Ready to resume anytime."
|
|
13142
|
-
};
|
|
15077
|
+
return `Session ${sessionId.slice(0, 8)}... initialized. Use context(user_message="...") to get relevant context.`;
|
|
15078
|
+
}
|
|
13143
15079
|
function getCaptureHint(eventType) {
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
15080
|
+
switch (eventType) {
|
|
15081
|
+
case "decision":
|
|
15082
|
+
return "Decision captured. It will surface in future context() calls when relevant.";
|
|
15083
|
+
case "preference":
|
|
15084
|
+
return "Preference saved. Future sessions will respect this preference.";
|
|
15085
|
+
case "insight":
|
|
15086
|
+
return "Insight captured. Use recall() to retrieve it later.";
|
|
15087
|
+
case "task":
|
|
15088
|
+
return "Task captured. Use list_tasks() to view all tasks.";
|
|
15089
|
+
case "lesson":
|
|
15090
|
+
return "Lesson saved. It will warn you before similar mistakes.";
|
|
15091
|
+
case "session_snapshot":
|
|
15092
|
+
return "Session state saved. Use restore_context() after compaction.";
|
|
15093
|
+
default:
|
|
15094
|
+
return "Event captured. Use recall() to retrieve related events.";
|
|
15095
|
+
}
|
|
15096
|
+
}
|
|
15097
|
+
function getEmptyStateHint(operation) {
|
|
15098
|
+
switch (operation) {
|
|
15099
|
+
case "get_lessons":
|
|
15100
|
+
return "No lessons found. Lessons are captured when mistakes occur.";
|
|
15101
|
+
case "recall":
|
|
15102
|
+
return "No memories found. Use capture() or remember() to save context.";
|
|
15103
|
+
case "list_plans":
|
|
15104
|
+
return "No plans found. Use capture_plan() to create an implementation plan.";
|
|
15105
|
+
case "list_events":
|
|
15106
|
+
return "No events found. Events are captured as you work.";
|
|
15107
|
+
case "list_tasks":
|
|
15108
|
+
return "No tasks found. Use create_task() to add tasks.";
|
|
15109
|
+
case "list_todos":
|
|
15110
|
+
return "No todos found. Use create_todo() to add quick todos.";
|
|
15111
|
+
case "list_diagrams":
|
|
15112
|
+
return "No diagrams found. Use create_diagram() to save a Mermaid diagram.";
|
|
15113
|
+
case "list_docs":
|
|
15114
|
+
return "No docs found. Use create_doc() to save documentation.";
|
|
15115
|
+
default:
|
|
15116
|
+
return "No results found.";
|
|
15117
|
+
}
|
|
13173
15118
|
}
|
|
13174
|
-
var POST_COMPACT_HINTS = {
|
|
13175
|
-
restored: "Picked up where you left off. Session ended, memory didn't.",
|
|
13176
|
-
restored_with_session: (sessionId) => `Restored from session ${sessionId}. Session ended, memory didn't.`,
|
|
13177
|
-
no_snapshot: "No prior session found. Fresh start\u2014context will build as you work.",
|
|
13178
|
-
failed: "Couldn't restore previous session. Use context to retrieve what you need."
|
|
13179
|
-
};
|
|
13180
|
-
var PLAN_HINTS = {
|
|
13181
|
-
created: "Plan saved. It will be here when you come back.",
|
|
13182
|
-
activated: "Plan is now active. Track progress across sessions.",
|
|
13183
|
-
completed: "Plan completed. The journey is preserved for future reference.",
|
|
13184
|
-
abandoned: "Plan archived. Abandoned plans still teach.",
|
|
13185
|
-
updated: "Plan updated. Changes are preserved."
|
|
13186
|
-
};
|
|
13187
15119
|
function getPlanStatusHint(status) {
|
|
13188
|
-
|
|
13189
|
-
draft:
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
15120
|
+
switch (status) {
|
|
15121
|
+
case "draft":
|
|
15122
|
+
return "Plan saved as draft. Update status to 'active' when ready.";
|
|
15123
|
+
case "active":
|
|
15124
|
+
return "Plan is now active. Create tasks to track implementation.";
|
|
15125
|
+
case "completed":
|
|
15126
|
+
return "Plan completed. Great work!";
|
|
15127
|
+
case "archived":
|
|
15128
|
+
return "Plan archived. It will still appear in searches.";
|
|
15129
|
+
case "abandoned":
|
|
15130
|
+
return "Plan abandoned. Consider capturing lessons learned.";
|
|
15131
|
+
default:
|
|
15132
|
+
return "Plan updated. Changes are preserved.";
|
|
15133
|
+
}
|
|
13196
15134
|
}
|
|
13197
|
-
var
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
15135
|
+
var POST_COMPACT_HINTS = {
|
|
15136
|
+
restored: "Context restored from pre-compaction snapshot.",
|
|
15137
|
+
restored_with_session: (sessionId) => `Context restored from session ${sessionId.slice(0, 8)}... snapshot.`,
|
|
15138
|
+
no_snapshot: "No snapshot found. Session state may be incomplete.",
|
|
15139
|
+
failed: "Failed to restore context. Try recall() to find relevant memories."
|
|
13202
15140
|
};
|
|
13203
15141
|
var INTEGRATION_HINTS = {
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
15142
|
+
connected: "Integration connected and syncing.",
|
|
15143
|
+
not_connected: "Integration not connected. Connect at:",
|
|
15144
|
+
sync_in_progress: "Sync in progress. Results may be incomplete.",
|
|
15145
|
+
sync_complete: "Sync complete. All data is current."
|
|
15146
|
+
};
|
|
15147
|
+
var TASK_HINTS = {
|
|
15148
|
+
created: "Task created. Use update_task() to change status.",
|
|
15149
|
+
completed: "Task completed. Well done!",
|
|
15150
|
+
blocked: "Task blocked. Add blocked_reason for context.",
|
|
15151
|
+
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
13208
15152
|
};
|
|
13209
|
-
function hashString(str) {
|
|
13210
|
-
let hash = 0;
|
|
13211
|
-
for (let i = 0; i < str.length; i++) {
|
|
13212
|
-
const char = str.charCodeAt(i);
|
|
13213
|
-
hash = (hash << 5) - hash + char;
|
|
13214
|
-
hash = hash & hash;
|
|
13215
|
-
}
|
|
13216
|
-
return hash;
|
|
13217
|
-
}
|
|
13218
15153
|
|
|
13219
15154
|
// src/tools.ts
|
|
13220
15155
|
var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
|
|
@@ -13985,9 +15920,9 @@ function humanizeKey(raw) {
|
|
|
13985
15920
|
const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
|
|
13986
15921
|
return withSpaces.toLowerCase();
|
|
13987
15922
|
}
|
|
13988
|
-
function buildParamDescription(key,
|
|
15923
|
+
function buildParamDescription(key, path21) {
|
|
13989
15924
|
const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
|
|
13990
|
-
const parent =
|
|
15925
|
+
const parent = path21[path21.length - 1];
|
|
13991
15926
|
if (parent === "target") {
|
|
13992
15927
|
if (key === "id") return "Target identifier (module path, function id, etc.).";
|
|
13993
15928
|
if (key === "type") return "Target type (module, file, function, type, variable).";
|
|
@@ -14018,7 +15953,7 @@ function getDescription(schema) {
|
|
|
14018
15953
|
if (def?.description && def.description.trim()) return def.description;
|
|
14019
15954
|
return void 0;
|
|
14020
15955
|
}
|
|
14021
|
-
function applyParamDescriptions(schema,
|
|
15956
|
+
function applyParamDescriptions(schema, path21 = []) {
|
|
14022
15957
|
if (!(schema instanceof external_exports.ZodObject)) {
|
|
14023
15958
|
return schema;
|
|
14024
15959
|
}
|
|
@@ -14029,7 +15964,7 @@ function applyParamDescriptions(schema, path13 = []) {
|
|
|
14029
15964
|
let nextField = field;
|
|
14030
15965
|
const existingDescription = getDescription(field);
|
|
14031
15966
|
if (field instanceof external_exports.ZodObject) {
|
|
14032
|
-
const nested = applyParamDescriptions(field, [...
|
|
15967
|
+
const nested = applyParamDescriptions(field, [...path21, key]);
|
|
14033
15968
|
if (nested !== field) {
|
|
14034
15969
|
nextField = nested;
|
|
14035
15970
|
changed = true;
|
|
@@ -14041,7 +15976,7 @@ function applyParamDescriptions(schema, path13 = []) {
|
|
|
14041
15976
|
changed = true;
|
|
14042
15977
|
}
|
|
14043
15978
|
} else {
|
|
14044
|
-
nextField = nextField.describe(buildParamDescription(key,
|
|
15979
|
+
nextField = nextField.describe(buildParamDescription(key, path21));
|
|
14045
15980
|
changed = true;
|
|
14046
15981
|
}
|
|
14047
15982
|
nextShape[key] = nextField;
|
|
@@ -14880,7 +16815,10 @@ function registerTools(server, client, sessionManager) {
|
|
|
14880
16815
|
"notion_stats",
|
|
14881
16816
|
"notion_activity",
|
|
14882
16817
|
"notion_knowledge",
|
|
14883
|
-
"notion_summary"
|
|
16818
|
+
"notion_summary",
|
|
16819
|
+
// Media operations (credit-metered)
|
|
16820
|
+
"media_index",
|
|
16821
|
+
"media_search"
|
|
14884
16822
|
]);
|
|
14885
16823
|
const proTools = (() => {
|
|
14886
16824
|
const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
|
|
@@ -14958,10 +16896,12 @@ function registerTools(server, client, sessionManager) {
|
|
|
14958
16896
|
function isTeamPlanCached() {
|
|
14959
16897
|
return teamStatus.checked ? teamStatus.isTeamPlan : false;
|
|
14960
16898
|
}
|
|
14961
|
-
|
|
16899
|
+
const INTEGRATION_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
16900
|
+
let integrationStatus = { checked: false, checkedAt: 0, slack: false, github: false, notion: false };
|
|
14962
16901
|
let toolsListChangedNotified = false;
|
|
14963
16902
|
async function checkIntegrationStatus(workspaceId) {
|
|
14964
|
-
|
|
16903
|
+
const cacheAge = Date.now() - integrationStatus.checkedAt;
|
|
16904
|
+
if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId && cacheAge < INTEGRATION_CACHE_TTL_MS) {
|
|
14965
16905
|
return { slack: integrationStatus.slack, github: integrationStatus.github, notion: integrationStatus.notion };
|
|
14966
16906
|
}
|
|
14967
16907
|
if (!workspaceId) {
|
|
@@ -14969,17 +16909,19 @@ function registerTools(server, client, sessionManager) {
|
|
|
14969
16909
|
}
|
|
14970
16910
|
try {
|
|
14971
16911
|
const status = await client.integrationsStatus({ workspace_id: workspaceId });
|
|
16912
|
+
const isConnectedStatus = (s) => s === "connected" || s === "syncing";
|
|
14972
16913
|
const slackConnected = status?.some(
|
|
14973
|
-
(s) => s.provider === "slack" && s.status
|
|
16914
|
+
(s) => s.provider === "slack" && isConnectedStatus(s.status)
|
|
14974
16915
|
) ?? false;
|
|
14975
16916
|
const githubConnected = status?.some(
|
|
14976
|
-
(s) => s.provider === "github" && s.status
|
|
16917
|
+
(s) => s.provider === "github" && isConnectedStatus(s.status)
|
|
14977
16918
|
) ?? false;
|
|
14978
16919
|
const notionConnected = status?.some(
|
|
14979
|
-
(s) => s.provider === "notion" && s.status
|
|
16920
|
+
(s) => s.provider === "notion" && isConnectedStatus(s.status)
|
|
14980
16921
|
) ?? false;
|
|
14981
16922
|
integrationStatus = {
|
|
14982
16923
|
checked: true,
|
|
16924
|
+
checkedAt: Date.now(),
|
|
14983
16925
|
slack: slackConnected,
|
|
14984
16926
|
github: githubConnected,
|
|
14985
16927
|
notion: notionConnected,
|
|
@@ -14998,6 +16940,7 @@ function registerTools(server, client, sessionManager) {
|
|
|
14998
16940
|
const hadNotion = integrationStatus.notion;
|
|
14999
16941
|
integrationStatus = {
|
|
15000
16942
|
checked: true,
|
|
16943
|
+
checkedAt: Date.now(),
|
|
15001
16944
|
slack: status.slack,
|
|
15002
16945
|
github: status.github,
|
|
15003
16946
|
notion: status.notion,
|
|
@@ -18577,7 +20520,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18577
20520
|
mode: external_exports.enum(["standard", "pack"]).optional().describe("Context pack mode (default: pack when enabled)"),
|
|
18578
20521
|
distill: external_exports.boolean().optional().describe("Use distillation for context pack (default: true)"),
|
|
18579
20522
|
session_tokens: external_exports.number().optional().describe("Cumulative session token count for context pressure calculation"),
|
|
18580
|
-
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)")
|
|
20523
|
+
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)"),
|
|
20524
|
+
save_exchange: external_exports.boolean().optional().describe("Save this exchange to the transcript for later search (background task)"),
|
|
20525
|
+
session_id: external_exports.string().optional().describe("Session ID for transcript association (required if save_exchange is true)"),
|
|
20526
|
+
client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')")
|
|
18581
20527
|
})
|
|
18582
20528
|
},
|
|
18583
20529
|
async (input) => {
|
|
@@ -18653,6 +20599,14 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18653
20599
|
logDebug(`Failed to restore post-compact context: ${err}`);
|
|
18654
20600
|
}
|
|
18655
20601
|
}
|
|
20602
|
+
let sessionId = input.session_id;
|
|
20603
|
+
if (!sessionId && sessionManager && input.save_exchange) {
|
|
20604
|
+
sessionId = sessionManager.getSessionId();
|
|
20605
|
+
}
|
|
20606
|
+
let clientName = input.client_name;
|
|
20607
|
+
if (!clientName && detectedClientInfo) {
|
|
20608
|
+
clientName = detectedClientInfo.name;
|
|
20609
|
+
}
|
|
18656
20610
|
const result = await client.getSmartContext({
|
|
18657
20611
|
user_message: input.user_message,
|
|
18658
20612
|
workspace_id: workspaceId,
|
|
@@ -18662,7 +20616,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18662
20616
|
mode: input.mode,
|
|
18663
20617
|
distill: input.distill,
|
|
18664
20618
|
session_tokens: sessionTokens,
|
|
18665
|
-
context_threshold: contextThreshold
|
|
20619
|
+
context_threshold: contextThreshold,
|
|
20620
|
+
save_exchange: input.save_exchange,
|
|
20621
|
+
session_id: sessionId,
|
|
20622
|
+
client_name: clientName
|
|
18666
20623
|
});
|
|
18667
20624
|
if (sessionManager && result.token_estimate) {
|
|
18668
20625
|
sessionManager.addTokens(result.token_estimate);
|
|
@@ -19597,14 +21554,15 @@ Use this to verify integrations are healthy and syncing properly.`,
|
|
|
19597
21554
|
}
|
|
19598
21555
|
const result = await client.integrationsStatus({ workspace_id: workspaceId });
|
|
19599
21556
|
if (AUTO_HIDE_INTEGRATIONS) {
|
|
21557
|
+
const isConnectedStatus = (s) => s === "connected" || s === "syncing";
|
|
19600
21558
|
const slackConnected = result?.some(
|
|
19601
|
-
(s) => s.provider === "slack" && s.status
|
|
21559
|
+
(s) => s.provider === "slack" && isConnectedStatus(s.status)
|
|
19602
21560
|
) ?? false;
|
|
19603
21561
|
const githubConnected = result?.some(
|
|
19604
|
-
(s) => s.provider === "github" && s.status
|
|
21562
|
+
(s) => s.provider === "github" && isConnectedStatus(s.status)
|
|
19605
21563
|
) ?? false;
|
|
19606
21564
|
const notionConnected = result?.some(
|
|
19607
|
-
(s) => s.provider === "notion" && s.status
|
|
21565
|
+
(s) => s.provider === "notion" && isConnectedStatus(s.status)
|
|
19608
21566
|
) ?? false;
|
|
19609
21567
|
updateIntegrationStatus({ slack: slackConnected, github: githubConnected, notion: notionConnected }, workspaceId);
|
|
19610
21568
|
}
|
|
@@ -19959,7 +21917,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19959
21917
|
"session",
|
|
19960
21918
|
{
|
|
19961
21919
|
title: "Session",
|
|
19962
|
-
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
21920
|
+
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Suggested rules actions: list_suggested_rules (view ML-generated rule suggestions), suggested_rule_action (accept/reject/modify a suggestion), suggested_rules_stats (view ML accuracy stats). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
19963
21921
|
inputSchema: external_exports.object({
|
|
19964
21922
|
action: external_exports.enum([
|
|
19965
21923
|
"capture",
|
|
@@ -19983,7 +21941,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19983
21941
|
// Team actions (team plans only)
|
|
19984
21942
|
"team_decisions",
|
|
19985
21943
|
"team_lessons",
|
|
19986
|
-
"team_plans"
|
|
21944
|
+
"team_plans",
|
|
21945
|
+
// Suggested rules actions (ML-generated)
|
|
21946
|
+
"list_suggested_rules",
|
|
21947
|
+
"suggested_rule_action",
|
|
21948
|
+
"suggested_rules_stats"
|
|
19987
21949
|
]).describe("Action to perform"),
|
|
19988
21950
|
workspace_id: external_exports.string().uuid().optional(),
|
|
19989
21951
|
project_id: external_exports.string().uuid().optional(),
|
|
@@ -20060,7 +22022,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20060
22022
|
is_personal: external_exports.boolean().optional().describe("Mark plan as personal (only visible to creator). For capture_plan/list_plans."),
|
|
20061
22023
|
// Restore context params
|
|
20062
22024
|
snapshot_id: external_exports.string().uuid().optional().describe("Specific snapshot ID to restore (defaults to most recent)"),
|
|
20063
|
-
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)")
|
|
22025
|
+
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)"),
|
|
22026
|
+
// Suggested rules params
|
|
22027
|
+
rule_id: external_exports.string().uuid().optional().describe("Suggested rule ID for actions"),
|
|
22028
|
+
rule_action: external_exports.enum(["accept", "reject", "modify"]).optional().describe("Action to perform on suggested rule"),
|
|
22029
|
+
modified_keywords: external_exports.array(external_exports.string()).optional().describe("Modified keywords when action is modify"),
|
|
22030
|
+
modified_instruction: external_exports.string().optional().describe("Modified instruction when action is modify"),
|
|
22031
|
+
min_confidence: external_exports.number().optional().describe("Minimum confidence threshold for listing rules")
|
|
20064
22032
|
})
|
|
20065
22033
|
},
|
|
20066
22034
|
async (input) => {
|
|
@@ -20636,6 +22604,81 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20636
22604
|
]
|
|
20637
22605
|
};
|
|
20638
22606
|
}
|
|
22607
|
+
case "list_suggested_rules": {
|
|
22608
|
+
const result = await client.listSuggestedRules({
|
|
22609
|
+
workspace_id: workspaceId,
|
|
22610
|
+
status: input.status,
|
|
22611
|
+
min_confidence: input.min_confidence,
|
|
22612
|
+
limit: input.limit
|
|
22613
|
+
});
|
|
22614
|
+
const rules = result?.data?.items || result?.items || [];
|
|
22615
|
+
if (rules.length === 0) {
|
|
22616
|
+
return {
|
|
22617
|
+
content: [
|
|
22618
|
+
{
|
|
22619
|
+
type: "text",
|
|
22620
|
+
text: formatContent({
|
|
22621
|
+
suggested_rules: [],
|
|
22622
|
+
hint: "No pending rule suggestions. The ML system learns from your lessons and will suggest rules when patterns are detected."
|
|
22623
|
+
})
|
|
22624
|
+
}
|
|
22625
|
+
]
|
|
22626
|
+
};
|
|
22627
|
+
}
|
|
22628
|
+
return {
|
|
22629
|
+
content: [
|
|
22630
|
+
{
|
|
22631
|
+
type: "text",
|
|
22632
|
+
text: formatContent({
|
|
22633
|
+
suggested_rules: rules,
|
|
22634
|
+
total: result?.data?.total || rules.length,
|
|
22635
|
+
hint: "Use suggested_rule_action to accept, reject, or modify these suggestions."
|
|
22636
|
+
})
|
|
22637
|
+
}
|
|
22638
|
+
]
|
|
22639
|
+
};
|
|
22640
|
+
}
|
|
22641
|
+
case "suggested_rule_action": {
|
|
22642
|
+
if (!input.rule_id || !input.rule_action) {
|
|
22643
|
+
return errorResult("suggested_rule_action requires: rule_id, rule_action (accept/reject/modify)");
|
|
22644
|
+
}
|
|
22645
|
+
const result = await client.suggestedRuleAction({
|
|
22646
|
+
rule_id: input.rule_id,
|
|
22647
|
+
action: input.rule_action,
|
|
22648
|
+
modified_keywords: input.modified_keywords,
|
|
22649
|
+
modified_instruction: input.modified_instruction
|
|
22650
|
+
});
|
|
22651
|
+
const actionVerb = input.rule_action === "accept" ? "accepted" : input.rule_action === "reject" ? "rejected" : "modified";
|
|
22652
|
+
return {
|
|
22653
|
+
content: [
|
|
22654
|
+
{
|
|
22655
|
+
type: "text",
|
|
22656
|
+
text: formatContent({
|
|
22657
|
+
success: true,
|
|
22658
|
+
message: `Rule ${actionVerb} successfully`,
|
|
22659
|
+
rule: result?.data || result,
|
|
22660
|
+
hint: input.rule_action === "accept" ? "This rule will now be applied to future context() calls." : input.rule_action === "reject" ? "This pattern will have reduced confidence for future suggestions." : "The modified rule will be applied to future context() calls."
|
|
22661
|
+
})
|
|
22662
|
+
}
|
|
22663
|
+
]
|
|
22664
|
+
};
|
|
22665
|
+
}
|
|
22666
|
+
case "suggested_rules_stats": {
|
|
22667
|
+
const result = await client.getSuggestedRulesStats({
|
|
22668
|
+
workspace_id: workspaceId
|
|
22669
|
+
});
|
|
22670
|
+
return {
|
|
22671
|
+
content: [
|
|
22672
|
+
{
|
|
22673
|
+
type: "text",
|
|
22674
|
+
text: formatContent({
|
|
22675
|
+
stats: result?.data || result,
|
|
22676
|
+
hint: "These stats show ML vs Grok accuracy. The blend weight auto-adjusts based on these metrics."
|
|
22677
|
+
})
|
|
22678
|
+
}
|
|
22679
|
+
]
|
|
22680
|
+
};
|
|
22681
|
+
}
|
|
20639
22682
|
default:
|
|
20640
22683
|
return errorResult(`Unknown action: ${input.action}`);
|
|
20641
22684
|
}
|
|
@@ -20645,7 +22688,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20645
22688
|
"memory",
|
|
20646
22689
|
{
|
|
20647
22690
|
title: "Memory",
|
|
20648
|
-
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
22691
|
+
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Transcript actions: list_transcripts (list saved conversations), get_transcript (get full transcript by ID), search_transcripts (semantic search across conversations), delete_transcript. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
20649
22692
|
inputSchema: external_exports.object({
|
|
20650
22693
|
action: external_exports.enum([
|
|
20651
22694
|
"create_event",
|
|
@@ -20693,6 +22736,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20693
22736
|
"update_doc",
|
|
20694
22737
|
"delete_doc",
|
|
20695
22738
|
"create_roadmap",
|
|
22739
|
+
// Transcript actions
|
|
22740
|
+
"list_transcripts",
|
|
22741
|
+
"get_transcript",
|
|
22742
|
+
"search_transcripts",
|
|
22743
|
+
"delete_transcript",
|
|
20696
22744
|
// Team actions
|
|
20697
22745
|
"team_tasks",
|
|
20698
22746
|
"team_todos",
|
|
@@ -20798,7 +22846,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20798
22846
|
})
|
|
20799
22847
|
).optional().describe("Milestones for create_roadmap action"),
|
|
20800
22848
|
// Personal items param
|
|
20801
|
-
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs.")
|
|
22849
|
+
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs."),
|
|
22850
|
+
// Transcript params
|
|
22851
|
+
transcript_id: external_exports.string().uuid().optional().describe("Transcript ID for get_transcript/delete_transcript"),
|
|
22852
|
+
session_id: external_exports.string().optional().describe("Session ID filter for list_transcripts"),
|
|
22853
|
+
client_name: external_exports.string().optional().describe("Client name filter for list_transcripts (e.g., 'claude', 'cursor')"),
|
|
22854
|
+
started_after: external_exports.string().optional().describe("ISO timestamp - filter transcripts started after this time"),
|
|
22855
|
+
started_before: external_exports.string().optional().describe("ISO timestamp - filter transcripts started before this time")
|
|
20802
22856
|
})
|
|
20803
22857
|
},
|
|
20804
22858
|
async (input) => {
|
|
@@ -21572,6 +23626,54 @@ ${formatContent(result)}`
|
|
|
21572
23626
|
]
|
|
21573
23627
|
};
|
|
21574
23628
|
}
|
|
23629
|
+
// Transcript actions
|
|
23630
|
+
case "list_transcripts": {
|
|
23631
|
+
const result = await client.listTranscripts({
|
|
23632
|
+
workspace_id: workspaceId,
|
|
23633
|
+
project_id: projectId,
|
|
23634
|
+
session_id: input.session_id,
|
|
23635
|
+
client_name: input.client_name,
|
|
23636
|
+
started_after: input.started_after,
|
|
23637
|
+
started_before: input.started_before,
|
|
23638
|
+
limit: input.limit
|
|
23639
|
+
});
|
|
23640
|
+
const transcripts = result?.data?.items || result?.items || result?.data || [];
|
|
23641
|
+
const resultWithHint = Array.isArray(transcripts) && transcripts.length === 0 ? { ...result, hint: "No transcripts found. Enable save_exchange in context() calls to save conversations." } : result;
|
|
23642
|
+
return {
|
|
23643
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
23644
|
+
};
|
|
23645
|
+
}
|
|
23646
|
+
case "get_transcript": {
|
|
23647
|
+
if (!input.transcript_id) {
|
|
23648
|
+
return errorResult("get_transcript requires: transcript_id");
|
|
23649
|
+
}
|
|
23650
|
+
const result = await client.getTranscript(input.transcript_id);
|
|
23651
|
+
return {
|
|
23652
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
23653
|
+
};
|
|
23654
|
+
}
|
|
23655
|
+
case "search_transcripts": {
|
|
23656
|
+
if (!input.query) {
|
|
23657
|
+
return errorResult("search_transcripts requires: query");
|
|
23658
|
+
}
|
|
23659
|
+
const result = await client.searchTranscripts({
|
|
23660
|
+
workspace_id: workspaceId,
|
|
23661
|
+
query: input.query,
|
|
23662
|
+
limit: input.limit
|
|
23663
|
+
});
|
|
23664
|
+
return {
|
|
23665
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
23666
|
+
};
|
|
23667
|
+
}
|
|
23668
|
+
case "delete_transcript": {
|
|
23669
|
+
if (!input.transcript_id) {
|
|
23670
|
+
return errorResult("delete_transcript requires: transcript_id");
|
|
23671
|
+
}
|
|
23672
|
+
const result = await client.deleteTranscript(input.transcript_id);
|
|
23673
|
+
return {
|
|
23674
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
23675
|
+
};
|
|
23676
|
+
}
|
|
21575
23677
|
default:
|
|
21576
23678
|
return errorResult(`Unknown action: ${input.action}`);
|
|
21577
23679
|
}
|
|
@@ -22860,6 +24962,8 @@ Example workflow:
|
|
|
22860
24962
|
const projectId = resolveProjectId(input.project_id);
|
|
22861
24963
|
switch (input.action) {
|
|
22862
24964
|
case "index": {
|
|
24965
|
+
const indexGate = await gateIfProTool("media_index");
|
|
24966
|
+
if (indexGate) return indexGate;
|
|
22863
24967
|
if (!input.file_path && !input.external_url) {
|
|
22864
24968
|
return errorResult("index requires: file_path or external_url");
|
|
22865
24969
|
}
|
|
@@ -22869,13 +24973,13 @@ Example workflow:
|
|
|
22869
24973
|
);
|
|
22870
24974
|
}
|
|
22871
24975
|
if (input.file_path) {
|
|
22872
|
-
const
|
|
24976
|
+
const fs20 = await import("fs/promises");
|
|
22873
24977
|
const pathModule = await import("path");
|
|
22874
24978
|
const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
|
|
22875
24979
|
const resolvedPath = pathModule.resolve(filePath);
|
|
22876
24980
|
let fileStats;
|
|
22877
24981
|
try {
|
|
22878
|
-
fileStats = await
|
|
24982
|
+
fileStats = await fs20.stat(resolvedPath);
|
|
22879
24983
|
} catch {
|
|
22880
24984
|
return errorResult(`File not found: ${resolvedPath}`);
|
|
22881
24985
|
}
|
|
@@ -22922,7 +25026,7 @@ Example workflow:
|
|
|
22922
25026
|
mime_type: mimeType,
|
|
22923
25027
|
tags: input.tags
|
|
22924
25028
|
});
|
|
22925
|
-
const fileBuffer = await
|
|
25029
|
+
const fileBuffer = await fs20.readFile(resolvedPath);
|
|
22926
25030
|
const uploadResponse = await fetch(uploadInit.upload_url, {
|
|
22927
25031
|
method: "PUT",
|
|
22928
25032
|
headers: uploadInit.headers,
|
|
@@ -23024,6 +25128,8 @@ Created: ${content.created_at}`
|
|
|
23024
25128
|
}
|
|
23025
25129
|
}
|
|
23026
25130
|
case "search": {
|
|
25131
|
+
const searchGate = await gateIfProTool("media_search");
|
|
25132
|
+
if (searchGate) return searchGate;
|
|
23027
25133
|
if (!input.query) {
|
|
23028
25134
|
return errorResult("search requires: query");
|
|
23029
25135
|
}
|
|
@@ -24244,6 +26350,7 @@ function registerPrompts(server) {
|
|
|
24244
26350
|
}
|
|
24245
26351
|
|
|
24246
26352
|
// src/session-manager.ts
|
|
26353
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
24247
26354
|
var SessionManager = class _SessionManager {
|
|
24248
26355
|
constructor(server, client) {
|
|
24249
26356
|
this.server = server;
|
|
@@ -24275,12 +26382,20 @@ var SessionManager = class _SessionManager {
|
|
|
24275
26382
|
this.lastHighPressureAt = null;
|
|
24276
26383
|
this.lastHighPressureTokens = 0;
|
|
24277
26384
|
this.postCompactRestoreCompleted = false;
|
|
26385
|
+
this.sessionId = `mcp-${randomUUID2()}`;
|
|
24278
26386
|
}
|
|
24279
26387
|
static {
|
|
24280
26388
|
// Each conversation turn typically includes: user message (~500), AI response (~1500),
|
|
24281
26389
|
// system prompt overhead (~500), and reasoning (~1500). Conservative estimate: 3000/turn
|
|
24282
26390
|
this.TOKENS_PER_TURN_ESTIMATE = 3e3;
|
|
24283
26391
|
}
|
|
26392
|
+
/**
|
|
26393
|
+
* Get the unique session ID for this MCP connection.
|
|
26394
|
+
* Used for transcript saving and session association.
|
|
26395
|
+
*/
|
|
26396
|
+
getSessionId() {
|
|
26397
|
+
return this.sessionId;
|
|
26398
|
+
}
|
|
24284
26399
|
/**
|
|
24285
26400
|
* Check if session has been auto-initialized
|
|
24286
26401
|
*/
|
|
@@ -24318,8 +26433,8 @@ var SessionManager = class _SessionManager {
|
|
|
24318
26433
|
/**
|
|
24319
26434
|
* Set the folder path hint (can be passed from tools that know the workspace path)
|
|
24320
26435
|
*/
|
|
24321
|
-
setFolderPath(
|
|
24322
|
-
this.folderPath =
|
|
26436
|
+
setFolderPath(path21) {
|
|
26437
|
+
this.folderPath = path21;
|
|
24323
26438
|
}
|
|
24324
26439
|
/**
|
|
24325
26440
|
* Mark that context_smart has been called in this session.
|
|
@@ -24509,7 +26624,7 @@ var SessionManager = class _SessionManager {
|
|
|
24509
26624
|
}
|
|
24510
26625
|
if (this.ideRoots.length === 0) {
|
|
24511
26626
|
const cwd = process.cwd();
|
|
24512
|
-
const
|
|
26627
|
+
const fs20 = await import("fs");
|
|
24513
26628
|
const projectIndicators = [
|
|
24514
26629
|
".git",
|
|
24515
26630
|
"package.json",
|
|
@@ -24519,7 +26634,7 @@ var SessionManager = class _SessionManager {
|
|
|
24519
26634
|
];
|
|
24520
26635
|
const hasProjectIndicator = projectIndicators.some((f) => {
|
|
24521
26636
|
try {
|
|
24522
|
-
return
|
|
26637
|
+
return fs20.existsSync(`${cwd}/${f}`);
|
|
24523
26638
|
} catch {
|
|
24524
26639
|
return false;
|
|
24525
26640
|
}
|
|
@@ -24815,7 +26930,7 @@ var SessionManager = class _SessionManager {
|
|
|
24815
26930
|
|
|
24816
26931
|
// src/http-gateway.ts
|
|
24817
26932
|
import { createServer } from "node:http";
|
|
24818
|
-
import { randomUUID as
|
|
26933
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24819
26934
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24820
26935
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
24821
26936
|
var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
|
|
@@ -24992,7 +27107,7 @@ async function createSession() {
|
|
|
24992
27107
|
registerPrompts(server);
|
|
24993
27108
|
}
|
|
24994
27109
|
const transport = new StreamableHTTPServerTransport({
|
|
24995
|
-
sessionIdGenerator: () =>
|
|
27110
|
+
sessionIdGenerator: () => randomUUID3(),
|
|
24996
27111
|
enableJsonResponse: ENABLE_JSON_RESPONSE,
|
|
24997
27112
|
onsessionclosed: (sessionId) => {
|
|
24998
27113
|
sessions.delete(sessionId);
|
|
@@ -25096,9 +27211,9 @@ async function runHttpGateway() {
|
|
|
25096
27211
|
}
|
|
25097
27212
|
|
|
25098
27213
|
// src/index.ts
|
|
25099
|
-
import { existsSync as
|
|
25100
|
-
import { homedir as
|
|
25101
|
-
import { join as
|
|
27214
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
27215
|
+
import { homedir as homedir18 } from "os";
|
|
27216
|
+
import { join as join22 } from "path";
|
|
25102
27217
|
|
|
25103
27218
|
// src/setup.ts
|
|
25104
27219
|
import * as fs7 from "node:fs/promises";
|
|
@@ -25928,10 +28043,10 @@ Code: ${device.user_code}`);
|
|
|
25928
28043
|
if (poll && poll.status === "pending") {
|
|
25929
28044
|
const intervalSeconds = typeof poll.interval === "number" ? poll.interval : 5;
|
|
25930
28045
|
const waitMs = Math.max(1, intervalSeconds) * 1e3;
|
|
25931
|
-
await new Promise((
|
|
28046
|
+
await new Promise((resolve15) => setTimeout(resolve15, waitMs));
|
|
25932
28047
|
continue;
|
|
25933
28048
|
}
|
|
25934
|
-
await new Promise((
|
|
28049
|
+
await new Promise((resolve15) => setTimeout(resolve15, 1e3));
|
|
25935
28050
|
}
|
|
25936
28051
|
if (!accessToken) {
|
|
25937
28052
|
throw new Error(
|
|
@@ -26496,12 +28611,12 @@ Applying to ${projects.length} project(s)...`);
|
|
|
26496
28611
|
// src/index.ts
|
|
26497
28612
|
var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
|
|
26498
28613
|
function showFirstRunMessage() {
|
|
26499
|
-
const configDir =
|
|
26500
|
-
const starShownFile =
|
|
26501
|
-
if (
|
|
28614
|
+
const configDir = join22(homedir18(), ".contextstream");
|
|
28615
|
+
const starShownFile = join22(configDir, ".star-shown");
|
|
28616
|
+
if (existsSync16(starShownFile)) {
|
|
26502
28617
|
return;
|
|
26503
28618
|
}
|
|
26504
|
-
if (!
|
|
28619
|
+
if (!existsSync16(configDir)) {
|
|
26505
28620
|
try {
|
|
26506
28621
|
mkdirSync5(configDir, { recursive: true });
|
|
26507
28622
|
} catch {
|
|
@@ -26532,13 +28647,26 @@ Usage:
|
|
|
26532
28647
|
|
|
26533
28648
|
Commands:
|
|
26534
28649
|
setup Interactive onboarding wizard (rules + workspace mapping)
|
|
28650
|
+
verify-key [--json] Verify API key and show account info
|
|
28651
|
+
update-hooks [flags] Update hooks for all editors (Claude, Cursor, Cline, Roo, Kilo)
|
|
28652
|
+
--scope=global Install hooks globally (default)
|
|
28653
|
+
--scope=project, -p Install hooks for current project only
|
|
28654
|
+
--path=/path Specify project path (implies --scope=project)
|
|
26535
28655
|
http Run HTTP MCP gateway (streamable HTTP transport)
|
|
26536
28656
|
hook pre-tool-use PreToolUse hook - blocks discovery tools, redirects to ContextStream
|
|
26537
28657
|
hook user-prompt-submit UserPromptSubmit hook - injects ContextStream rules reminder
|
|
26538
28658
|
hook media-aware Media-aware hook - detects media prompts, injects media tool guidance
|
|
26539
28659
|
hook pre-compact PreCompact hook - saves conversation state before compaction
|
|
28660
|
+
hook post-compact PostCompact hook - restores context after compaction
|
|
26540
28661
|
hook post-write PostToolUse hook - real-time file indexing after Edit/Write
|
|
26541
28662
|
hook auto-rules PostToolUse hook - auto-updates rules when behind (silent)
|
|
28663
|
+
hook on-bash PostToolUse hook - captures bash commands, learns from errors
|
|
28664
|
+
hook on-task PostToolUse hook - tracks Task agent work
|
|
28665
|
+
hook on-read PostToolUse hook - tracks file exploration (Read/Glob/Grep)
|
|
28666
|
+
hook on-web PostToolUse hook - captures web research (WebFetch/WebSearch)
|
|
28667
|
+
hook session-init SessionStart hook - full context injection on session start
|
|
28668
|
+
hook session-end Stop hook - finalizes session, saves state
|
|
28669
|
+
hook on-save-intent UserPromptSubmit hook - redirects doc saves to ContextStream
|
|
26542
28670
|
|
|
26543
28671
|
Environment variables:
|
|
26544
28672
|
CONTEXTSTREAM_API_URL Base API URL (e.g. https://api.contextstream.io)
|
|
@@ -26647,12 +28775,90 @@ async function main() {
|
|
|
26647
28775
|
await runAutoRulesHook2();
|
|
26648
28776
|
return;
|
|
26649
28777
|
}
|
|
28778
|
+
case "post-compact": {
|
|
28779
|
+
const { runPostCompactHook: runPostCompactHook2 } = await Promise.resolve().then(() => (init_post_compact(), post_compact_exports));
|
|
28780
|
+
await runPostCompactHook2();
|
|
28781
|
+
return;
|
|
28782
|
+
}
|
|
28783
|
+
case "on-bash": {
|
|
28784
|
+
const { runOnBashHook: runOnBashHook2 } = await Promise.resolve().then(() => (init_on_bash(), on_bash_exports));
|
|
28785
|
+
await runOnBashHook2();
|
|
28786
|
+
return;
|
|
28787
|
+
}
|
|
28788
|
+
case "on-task": {
|
|
28789
|
+
const { runOnTaskHook: runOnTaskHook2 } = await Promise.resolve().then(() => (init_on_task(), on_task_exports));
|
|
28790
|
+
await runOnTaskHook2();
|
|
28791
|
+
return;
|
|
28792
|
+
}
|
|
28793
|
+
case "on-read": {
|
|
28794
|
+
const { runOnReadHook: runOnReadHook2 } = await Promise.resolve().then(() => (init_on_read(), on_read_exports));
|
|
28795
|
+
await runOnReadHook2();
|
|
28796
|
+
return;
|
|
28797
|
+
}
|
|
28798
|
+
case "on-web": {
|
|
28799
|
+
const { runOnWebHook: runOnWebHook2 } = await Promise.resolve().then(() => (init_on_web(), on_web_exports));
|
|
28800
|
+
await runOnWebHook2();
|
|
28801
|
+
return;
|
|
28802
|
+
}
|
|
28803
|
+
case "session-init": {
|
|
28804
|
+
const { runSessionInitHook: runSessionInitHook2 } = await Promise.resolve().then(() => (init_session_init(), session_init_exports));
|
|
28805
|
+
await runSessionInitHook2();
|
|
28806
|
+
return;
|
|
28807
|
+
}
|
|
28808
|
+
case "session-end": {
|
|
28809
|
+
const { runSessionEndHook: runSessionEndHook2 } = await Promise.resolve().then(() => (init_session_end(), session_end_exports));
|
|
28810
|
+
await runSessionEndHook2();
|
|
28811
|
+
return;
|
|
28812
|
+
}
|
|
28813
|
+
case "on-save-intent": {
|
|
28814
|
+
const { runOnSaveIntentHook: runOnSaveIntentHook2 } = await Promise.resolve().then(() => (init_on_save_intent(), on_save_intent_exports));
|
|
28815
|
+
await runOnSaveIntentHook2();
|
|
28816
|
+
return;
|
|
28817
|
+
}
|
|
26650
28818
|
default:
|
|
26651
28819
|
console.error(`Unknown hook: ${hookName}`);
|
|
26652
|
-
console.error("Available hooks: pre-tool-use, user-prompt-submit, media-aware, pre-compact, post-write, auto-rules");
|
|
28820
|
+
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");
|
|
26653
28821
|
process.exit(1);
|
|
26654
28822
|
}
|
|
26655
28823
|
}
|
|
28824
|
+
if (args[0] === "verify-key") {
|
|
28825
|
+
const { runVerifyKey: runVerifyKey2 } = await Promise.resolve().then(() => (init_verify_key(), verify_key_exports));
|
|
28826
|
+
const outputJson = args.includes("--json");
|
|
28827
|
+
const result = await runVerifyKey2(outputJson);
|
|
28828
|
+
process.exit(result.valid ? 0 : 1);
|
|
28829
|
+
}
|
|
28830
|
+
if (args[0] === "update-hooks") {
|
|
28831
|
+
const { installAllEditorHooks: installAllEditorHooks2 } = await Promise.resolve().then(() => (init_hooks_config(), hooks_config_exports));
|
|
28832
|
+
let scope = "global";
|
|
28833
|
+
let projectPath;
|
|
28834
|
+
for (const arg of args.slice(1)) {
|
|
28835
|
+
if (arg === "--scope=project" || arg === "-p") {
|
|
28836
|
+
scope = "project";
|
|
28837
|
+
projectPath = projectPath || process.cwd();
|
|
28838
|
+
} else if (arg === "--scope=global" || arg === "-g") {
|
|
28839
|
+
scope = "global";
|
|
28840
|
+
} else if (arg.startsWith("--path=")) {
|
|
28841
|
+
projectPath = arg.replace("--path=", "");
|
|
28842
|
+
scope = "project";
|
|
28843
|
+
}
|
|
28844
|
+
}
|
|
28845
|
+
const scopeLabel = scope === "project" ? `project (${projectPath || process.cwd()})` : "global";
|
|
28846
|
+
console.error(`Updating hooks for all editors (${scopeLabel})...`);
|
|
28847
|
+
try {
|
|
28848
|
+
const results = await installAllEditorHooks2({
|
|
28849
|
+
scope,
|
|
28850
|
+
projectPath: scope === "project" ? projectPath || process.cwd() : void 0
|
|
28851
|
+
});
|
|
28852
|
+
for (const result of results) {
|
|
28853
|
+
console.error(`\u2713 ${result.editor}: ${result.installed.length} hooks installed`);
|
|
28854
|
+
}
|
|
28855
|
+
console.error("\u2713 Hooks updated successfully");
|
|
28856
|
+
} catch (error) {
|
|
28857
|
+
console.error("Failed to update hooks:", error);
|
|
28858
|
+
process.exit(1);
|
|
28859
|
+
}
|
|
28860
|
+
return;
|
|
28861
|
+
}
|
|
26656
28862
|
if (!process.env.CONTEXTSTREAM_API_KEY && !process.env.CONTEXTSTREAM_JWT) {
|
|
26657
28863
|
const saved = await readSavedCredentials();
|
|
26658
28864
|
if (saved) {
|