@contextstream/mcp-server 0.4.50 → 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/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(path13, checkUnignored, mode) {
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(path13);
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 = (path13, originalPath, doThrow) => {
369
- if (!isString(path13)) {
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 (!path13) {
375
+ if (!path21) {
376
376
  return doThrow(`path must not be empty`, TypeError);
377
377
  }
378
- if (checkPath.isNotRelative(path13)) {
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 = (path13) => REGEX_TEST_INVALID_PATH.test(path13);
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 path13 = originalPath && checkPath.convert(originalPath);
417
+ const path21 = originalPath && checkPath.convert(originalPath);
418
418
  checkPath(
419
- path13,
419
+ path21,
420
420
  originalPath,
421
421
  this._strictPathCheck ? throwError : RETURN_FALSE
422
422
  );
423
- return this._t(path13, cache, checkUnignored, slices);
423
+ return this._t(path21, cache, checkUnignored, slices);
424
424
  }
425
- checkIgnore(path13) {
426
- if (!REGEX_TEST_TRAILING_SLASH.test(path13)) {
427
- return this.test(path13);
425
+ checkIgnore(path21) {
426
+ if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
427
+ return this.test(path21);
428
428
  }
429
- const slices = path13.split(SLASH).filter(Boolean);
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(path13, false, MODE_CHECK_IGNORE);
442
+ return this._rules.test(path21, false, MODE_CHECK_IGNORE);
443
443
  }
444
- _t(path13, cache, checkUnignored, slices) {
445
- if (path13 in cache) {
446
- return cache[path13];
444
+ _t(path21, cache, checkUnignored, slices) {
445
+ if (path21 in cache) {
446
+ return cache[path21];
447
447
  }
448
448
  if (!slices) {
449
- slices = path13.split(SLASH).filter(Boolean);
449
+ slices = path21.split(SLASH).filter(Boolean);
450
450
  }
451
451
  slices.pop();
452
452
  if (!slices.length) {
453
- return cache[path13] = this._rules.test(path13, checkUnignored, MODE_IGNORE);
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[path13] = parent.ignored ? parent : this._rules.test(path13, checkUnignored, MODE_IGNORE);
461
+ return cache[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
462
462
  }
463
- ignores(path13) {
464
- return this._test(path13, this._ignoreCache, false).ignored;
463
+ ignores(path21) {
464
+ return this._test(path21, this._ignoreCache, false).ignored;
465
465
  }
466
466
  createFilter() {
467
- return (path13) => !this.ignores(path13);
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(path13) {
474
- return this._test(path13, this._testCache, true);
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 = (path13) => checkPath(path13 && checkPath.convert(path13), path13, RETURN_FALSE);
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 = (path13) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path13) || isNotRelative(path13);
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: "npx @contextstream/mcp-server hook user-prompt-submit",
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: "npx @contextstream/mcp-server hook media-aware",
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: "npx @contextstream/mcp-server hook pre-tool-use",
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: "npx @contextstream/mcp-server hook pre-compact",
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: "npx @contextstream/mcp-server hook post-write",
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: "npx @contextstream/mcp-server hook auto-rules",
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: "npx @contextstream/mcp-server hook pre-tool-use",
644
- userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
744
+ preToolUse: getHookCommand("pre-tool-use"),
745
+ userPrompt: getHookCommand("user-prompt-submit")
645
746
  };
646
747
  if (options?.includePreCompact !== false) {
647
- result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
748
+ result.preCompact = getHookCommand("pre-compact");
648
749
  }
649
750
  if (options?.includeMediaAware !== false) {
650
- result.mediaAware = "npx @contextstream/mcp-server hook media-aware";
751
+ result.mediaAware = getHookCommand("media-aware");
651
752
  }
652
753
  if (options?.includeAutoRules !== false) {
653
- result.autoRules = "npx @contextstream/mcp-server hook auto-rules";
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
- "npx @contextstream/mcp-server hook pre-tool-use",
690
- "npx @contextstream/mcp-server hook user-prompt-submit"
790
+ getHookCommand("pre-tool-use"),
791
+ getHookCommand("user-prompt-submit")
691
792
  );
692
793
  if (options.includePreCompact !== false) {
693
- result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
794
+ result.scripts.push(getHookCommand("pre-compact"));
694
795
  }
695
796
  if (options.includeMediaAware !== false) {
696
- result.scripts.push("npx @contextstream/mcp-server hook media-aware");
797
+ result.scripts.push(getHookCommand("media-aware"));
697
798
  }
698
799
  if (options.includePostWrite !== false) {
699
- result.scripts.push("npx @contextstream/mcp-server hook post-write");
800
+ result.scripts.push(getHookCommand("post-write"));
700
801
  }
701
802
  if (options.includeAutoRules !== false) {
702
- result.scripts.push("npx @contextstream/mcp-server hook auto-rules");
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: "npx @contextstream/mcp-server hook pre-tool-use",
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: "npx @contextstream/mcp-server hook user-prompt-submit",
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: "npx @contextstream/mcp-server hook pre-tool-use",
1007
- beforeSubmitPrompt: "npx @contextstream/mcp-server hook user-prompt-submit",
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) => `#!/bin/bash
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
- # Calls the Node.js hook via npx
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
@@ -3154,35 +3259,1274 @@ function hasPythonHooks(settingsPath) {
3154
3259
  }
3155
3260
  }
3156
3261
  }
3157
- return false;
3262
+ return false;
3263
+ } catch {
3264
+ return false;
3265
+ }
3266
+ }
3267
+ function detectPythonHooks(cwd) {
3268
+ const globalSettingsPath = path12.join(homedir9(), ".claude", "settings.json");
3269
+ const projectSettingsPath = path12.join(cwd, ".claude", "settings.json");
3270
+ return {
3271
+ global: hasPythonHooks(globalSettingsPath),
3272
+ project: hasPythonHooks(projectSettingsPath)
3273
+ };
3274
+ }
3275
+ async function upgradeHooksForFolder(folderPath) {
3276
+ const { installClaudeCodeHooks: installClaudeCodeHooks3 } = await Promise.resolve().then(() => (init_hooks_config(), hooks_config_exports));
3277
+ await installClaudeCodeHooks3({
3278
+ scope: "both",
3279
+ projectPath: folderPath,
3280
+ includePreCompact: true,
3281
+ includeMediaAware: true,
3282
+ includePostWrite: true,
3283
+ includeAutoRules: true
3284
+ });
3285
+ }
3286
+ async function runAutoRulesHook() {
3287
+ if (!ENABLED6) {
3288
+ process.exit(0);
3289
+ }
3290
+ if (hasRunRecently()) {
3291
+ process.exit(0);
3292
+ }
3293
+ let inputData = "";
3294
+ for await (const chunk of process.stdin) {
3295
+ inputData += chunk;
3296
+ }
3297
+ if (!inputData.trim()) {
3298
+ process.exit(0);
3299
+ }
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);
3158
4483
  } catch {
3159
- return false;
3160
4484
  }
4485
+ return stats;
3161
4486
  }
3162
- function detectPythonHooks(cwd) {
3163
- const globalSettingsPath = path12.join(homedir9(), ".claude", "settings.json");
3164
- const projectSettingsPath = path12.join(cwd, ".claude", "settings.json");
3165
- return {
3166
- global: hasPythonHooks(globalSettingsPath),
3167
- project: hasPythonHooks(projectSettingsPath)
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
3168
4508
  };
3169
- }
3170
- async function upgradeHooksForFolder(folderPath) {
3171
- const { installClaudeCodeHooks: installClaudeCodeHooks3 } = await Promise.resolve().then(() => (init_hooks_config(), hooks_config_exports));
3172
- await installClaudeCodeHooks3({
3173
- scope: "both",
3174
- projectPath: folderPath,
3175
- includePreCompact: true,
3176
- includeMediaAware: true,
3177
- includePostWrite: true,
3178
- includeAutoRules: true
3179
- });
3180
- }
3181
- async function runAutoRulesHook() {
3182
- if (!ENABLED6) {
3183
- process.exit(0);
4509
+ if (WORKSPACE_ID8) {
4510
+ payload.workspace_id = WORKSPACE_ID8;
3184
4511
  }
3185
- if (hasRunRecently()) {
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) {
3186
4530
  process.exit(0);
3187
4531
  }
3188
4532
  let inputData = "";
@@ -3198,40 +4542,444 @@ async function runAutoRulesHook() {
3198
4542
  } catch {
3199
4543
  process.exit(0);
3200
4544
  }
3201
- const toolName = input.tool_name || input.toolName || "";
3202
- const isContextTool = toolName.includes("init") || toolName.includes("context") || toolName.includes("session_init") || toolName.includes("context_smart");
3203
- if (!isContextTool) {
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) {
3204
4581
  process.exit(0);
3205
4582
  }
3206
- const cwd = extractCwd3(input);
3207
- const pythonHooks = detectPythonHooks(cwd);
3208
- const hasPythonHooksToUpgrade = pythonHooks.global || pythonHooks.project;
3209
- const rulesNotice = extractRulesNotice(input);
3210
- const rulesNeedUpdate = rulesNotice && rulesNotice.status !== "current";
3211
- if (!hasPythonHooksToUpgrade && !rulesNeedUpdate) {
4583
+ let inputData = "";
4584
+ for await (const chunk of process.stdin) {
4585
+ inputData += chunk;
4586
+ }
4587
+ if (!inputData.trim()) {
3212
4588
  process.exit(0);
3213
4589
  }
3214
- const folderPath = rulesNotice?.update_args?.folder_path || cwd;
4590
+ let input;
3215
4591
  try {
3216
- await upgradeHooksForFolder(folderPath);
3217
- markAsRan();
4592
+ input = JSON.parse(inputData);
3218
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
+ );
3219
4627
  }
3220
4628
  process.exit(0);
3221
4629
  }
3222
- var API_URL3, API_KEY3, ENABLED6, MARKER_FILE, COOLDOWN_MS, isDirectRun6;
3223
- var init_auto_rules = __esm({
3224
- "src/hooks/auto-rules.ts"() {
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"() {
3225
4633
  "use strict";
3226
- API_URL3 = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
3227
- API_KEY3 = process.env.CONTEXTSTREAM_API_KEY || "";
3228
- ENABLED6 = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
3229
- MARKER_FILE = path12.join(homedir9(), ".contextstream", ".auto-rules-ran");
3230
- COOLDOWN_MS = 4 * 60 * 60 * 1e3;
3231
- isDirectRun6 = process.argv[1]?.includes("auto-rules") || process.argv[2] === "auto-rules";
3232
- if (isDirectRun6) {
3233
- runAutoRulesHook().catch(() => process.exit(0));
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) {
4889
+ try {
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
+ };
4929
+ }
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;
4946
+ }
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}`);
3234
4975
  }
4976
+ console.log("");
4977
+ }
4978
+ return result;
4979
+ }
4980
+ var init_verify_key = __esm({
4981
+ "src/verify-key.ts"() {
4982
+ "use strict";
3235
4983
  }
3236
4984
  });
3237
4985
 
@@ -3717,8 +5465,8 @@ function getErrorMap() {
3717
5465
 
3718
5466
  // node_modules/zod/v3/helpers/parseUtil.js
3719
5467
  var makeIssue = (params) => {
3720
- const { data, path: path13, errorMaps, issueData } = params;
3721
- const fullPath = [...path13, ...issueData.path || []];
5468
+ const { data, path: path21, errorMaps, issueData } = params;
5469
+ const fullPath = [...path21, ...issueData.path || []];
3722
5470
  const fullIssue = {
3723
5471
  ...issueData,
3724
5472
  path: fullPath
@@ -3834,11 +5582,11 @@ var errorUtil;
3834
5582
 
3835
5583
  // node_modules/zod/v3/types.js
3836
5584
  var ParseInputLazyPath = class {
3837
- constructor(parent, value, path13, key) {
5585
+ constructor(parent, value, path21, key) {
3838
5586
  this._cachedPath = [];
3839
5587
  this.parent = parent;
3840
5588
  this.data = value;
3841
- this._path = path13;
5589
+ this._path = path21;
3842
5590
  this._key = key;
3843
5591
  }
3844
5592
  get path() {
@@ -7288,6 +9036,9 @@ import { join } from "path";
7288
9036
  var UPGRADE_COMMAND = "npm install -g @contextstream/mcp-server@latest";
7289
9037
  var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
7290
9038
  function getVersion() {
9039
+ if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
9040
+ return __CONTEXTSTREAM_VERSION__;
9041
+ }
7291
9042
  try {
7292
9043
  const require2 = createRequire(import.meta.url);
7293
9044
  const pkg = require2("../package.json");
@@ -7571,14 +9322,14 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504])
7571
9322
  var MAX_RETRIES = 3;
7572
9323
  var BASE_DELAY = 1e3;
7573
9324
  async function sleep(ms) {
7574
- return new Promise((resolve8) => setTimeout(resolve8, ms));
9325
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
7575
9326
  }
7576
- async function request(config, path13, options = {}) {
9327
+ async function request(config, path21, options = {}) {
7577
9328
  const { apiUrl, userAgent } = config;
7578
9329
  const authOverride = getAuthOverride();
7579
9330
  const apiKey = authOverride?.apiKey ?? config.apiKey;
7580
9331
  const jwt = authOverride?.jwt ?? config.jwt;
7581
- const apiPath = path13.startsWith("/api/") ? path13 : `/api/v1${path13}`;
9332
+ const apiPath = path21.startsWith("/api/") ? path21 : `/api/v1${path21}`;
7582
9333
  const url = `${apiUrl.replace(/\/$/, "")}${apiPath}`;
7583
9334
  const maxRetries = options.retries ?? MAX_RETRIES;
7584
9335
  const baseDelay = options.retryDelay ?? BASE_DELAY;
@@ -7724,9 +9475,9 @@ function extractErrorCode(payload) {
7724
9475
  if (typeof payload.code === "string" && payload.code.trim()) return payload.code.trim();
7725
9476
  return null;
7726
9477
  }
7727
- function detectIntegrationProvider(path13) {
7728
- if (/\/github(\/|$)/i.test(path13)) return "github";
7729
- if (/\/slack(\/|$)/i.test(path13)) return "slack";
9478
+ function detectIntegrationProvider(path21) {
9479
+ if (/\/github(\/|$)/i.test(path21)) return "github";
9480
+ if (/\/slack(\/|$)/i.test(path21)) return "slack";
7730
9481
  return null;
7731
9482
  }
7732
9483
  function rewriteNotFoundMessage(input) {
@@ -8379,10 +10130,10 @@ var PROJECT_MARKERS = [
8379
10130
  ];
8380
10131
  function isMultiProjectFolder(folderPath) {
8381
10132
  try {
8382
- const fs12 = __require("fs");
10133
+ const fs20 = __require("fs");
8383
10134
  const pathModule = __require("path");
8384
- const rootHasGit = fs12.existsSync(pathModule.join(folderPath, ".git"));
8385
- const entries = fs12.readdirSync(folderPath, { withFileTypes: true });
10135
+ const rootHasGit = fs20.existsSync(pathModule.join(folderPath, ".git"));
10136
+ const entries = fs20.readdirSync(folderPath, { withFileTypes: true });
8386
10137
  const subdirs = entries.filter(
8387
10138
  (e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules"
8388
10139
  );
@@ -8390,7 +10141,7 @@ function isMultiProjectFolder(folderPath) {
8390
10141
  for (const subdir of subdirs) {
8391
10142
  const subdirPath = pathModule.join(folderPath, subdir.name);
8392
10143
  for (const marker of PROJECT_MARKERS) {
8393
- if (fs12.existsSync(pathModule.join(subdirPath, marker))) {
10144
+ if (fs20.existsSync(pathModule.join(subdirPath, marker))) {
8394
10145
  projectSubdirs.push(subdir.name);
8395
10146
  break;
8396
10147
  }
@@ -10457,9 +12208,9 @@ var ContextStreamClient = class {
10457
12208
  candidateParts.push("## Relevant Code\n");
10458
12209
  currentChars += 18;
10459
12210
  const codeEntries = code.results.map((c) => {
10460
- const path13 = c.file_path || "file";
12211
+ const path21 = c.file_path || "file";
10461
12212
  const content = c.content?.slice(0, 150) || "";
10462
- return { path: path13, entry: `\u2022 ${path13}: ${content}...
12213
+ return { path: path21, entry: `\u2022 ${path21}: ${content}...
10463
12214
  ` };
10464
12215
  });
10465
12216
  for (const c of codeEntries) {
@@ -14169,9 +15920,9 @@ function humanizeKey(raw) {
14169
15920
  const withSpaces = raw.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ");
14170
15921
  return withSpaces.toLowerCase();
14171
15922
  }
14172
- function buildParamDescription(key, path13) {
15923
+ function buildParamDescription(key, path21) {
14173
15924
  const normalized = key in DEFAULT_PARAM_DESCRIPTIONS ? key : key.toLowerCase();
14174
- const parent = path13[path13.length - 1];
15925
+ const parent = path21[path21.length - 1];
14175
15926
  if (parent === "target") {
14176
15927
  if (key === "id") return "Target identifier (module path, function id, etc.).";
14177
15928
  if (key === "type") return "Target type (module, file, function, type, variable).";
@@ -14202,7 +15953,7 @@ function getDescription(schema) {
14202
15953
  if (def?.description && def.description.trim()) return def.description;
14203
15954
  return void 0;
14204
15955
  }
14205
- function applyParamDescriptions(schema, path13 = []) {
15956
+ function applyParamDescriptions(schema, path21 = []) {
14206
15957
  if (!(schema instanceof external_exports.ZodObject)) {
14207
15958
  return schema;
14208
15959
  }
@@ -14213,7 +15964,7 @@ function applyParamDescriptions(schema, path13 = []) {
14213
15964
  let nextField = field;
14214
15965
  const existingDescription = getDescription(field);
14215
15966
  if (field instanceof external_exports.ZodObject) {
14216
- const nested = applyParamDescriptions(field, [...path13, key]);
15967
+ const nested = applyParamDescriptions(field, [...path21, key]);
14217
15968
  if (nested !== field) {
14218
15969
  nextField = nested;
14219
15970
  changed = true;
@@ -14225,7 +15976,7 @@ function applyParamDescriptions(schema, path13 = []) {
14225
15976
  changed = true;
14226
15977
  }
14227
15978
  } else {
14228
- nextField = nextField.describe(buildParamDescription(key, path13));
15979
+ nextField = nextField.describe(buildParamDescription(key, path21));
14229
15980
  changed = true;
14230
15981
  }
14231
15982
  nextShape[key] = nextField;
@@ -15145,10 +16896,12 @@ function registerTools(server, client, sessionManager) {
15145
16896
  function isTeamPlanCached() {
15146
16897
  return teamStatus.checked ? teamStatus.isTeamPlan : false;
15147
16898
  }
15148
- let integrationStatus = { checked: false, slack: false, github: false, notion: false };
16899
+ const INTEGRATION_CACHE_TTL_MS = 5 * 60 * 1e3;
16900
+ let integrationStatus = { checked: false, checkedAt: 0, slack: false, github: false, notion: false };
15149
16901
  let toolsListChangedNotified = false;
15150
16902
  async function checkIntegrationStatus(workspaceId) {
15151
- if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId) {
16903
+ const cacheAge = Date.now() - integrationStatus.checkedAt;
16904
+ if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId && cacheAge < INTEGRATION_CACHE_TTL_MS) {
15152
16905
  return { slack: integrationStatus.slack, github: integrationStatus.github, notion: integrationStatus.notion };
15153
16906
  }
15154
16907
  if (!workspaceId) {
@@ -15156,17 +16909,19 @@ function registerTools(server, client, sessionManager) {
15156
16909
  }
15157
16910
  try {
15158
16911
  const status = await client.integrationsStatus({ workspace_id: workspaceId });
16912
+ const isConnectedStatus = (s) => s === "connected" || s === "syncing";
15159
16913
  const slackConnected = status?.some(
15160
- (s) => s.provider === "slack" && s.status === "connected"
16914
+ (s) => s.provider === "slack" && isConnectedStatus(s.status)
15161
16915
  ) ?? false;
15162
16916
  const githubConnected = status?.some(
15163
- (s) => s.provider === "github" && s.status === "connected"
16917
+ (s) => s.provider === "github" && isConnectedStatus(s.status)
15164
16918
  ) ?? false;
15165
16919
  const notionConnected = status?.some(
15166
- (s) => s.provider === "notion" && s.status === "connected"
16920
+ (s) => s.provider === "notion" && isConnectedStatus(s.status)
15167
16921
  ) ?? false;
15168
16922
  integrationStatus = {
15169
16923
  checked: true,
16924
+ checkedAt: Date.now(),
15170
16925
  slack: slackConnected,
15171
16926
  github: githubConnected,
15172
16927
  notion: notionConnected,
@@ -15185,6 +16940,7 @@ function registerTools(server, client, sessionManager) {
15185
16940
  const hadNotion = integrationStatus.notion;
15186
16941
  integrationStatus = {
15187
16942
  checked: true,
16943
+ checkedAt: Date.now(),
15188
16944
  slack: status.slack,
15189
16945
  github: status.github,
15190
16946
  notion: status.notion,
@@ -19798,14 +21554,15 @@ Use this to verify integrations are healthy and syncing properly.`,
19798
21554
  }
19799
21555
  const result = await client.integrationsStatus({ workspace_id: workspaceId });
19800
21556
  if (AUTO_HIDE_INTEGRATIONS) {
21557
+ const isConnectedStatus = (s) => s === "connected" || s === "syncing";
19801
21558
  const slackConnected = result?.some(
19802
- (s) => s.provider === "slack" && s.status === "connected"
21559
+ (s) => s.provider === "slack" && isConnectedStatus(s.status)
19803
21560
  ) ?? false;
19804
21561
  const githubConnected = result?.some(
19805
- (s) => s.provider === "github" && s.status === "connected"
21562
+ (s) => s.provider === "github" && isConnectedStatus(s.status)
19806
21563
  ) ?? false;
19807
21564
  const notionConnected = result?.some(
19808
- (s) => s.provider === "notion" && s.status === "connected"
21565
+ (s) => s.provider === "notion" && isConnectedStatus(s.status)
19809
21566
  ) ?? false;
19810
21567
  updateIntegrationStatus({ slack: slackConnected, github: githubConnected, notion: notionConnected }, workspaceId);
19811
21568
  }
@@ -23216,13 +24973,13 @@ Example workflow:
23216
24973
  );
23217
24974
  }
23218
24975
  if (input.file_path) {
23219
- const fs12 = await import("fs/promises");
24976
+ const fs20 = await import("fs/promises");
23220
24977
  const pathModule = await import("path");
23221
24978
  const filePath = input.file_path.startsWith("~") ? input.file_path.replace("~", process.env.HOME || "") : input.file_path;
23222
24979
  const resolvedPath = pathModule.resolve(filePath);
23223
24980
  let fileStats;
23224
24981
  try {
23225
- fileStats = await fs12.stat(resolvedPath);
24982
+ fileStats = await fs20.stat(resolvedPath);
23226
24983
  } catch {
23227
24984
  return errorResult(`File not found: ${resolvedPath}`);
23228
24985
  }
@@ -23269,7 +25026,7 @@ Example workflow:
23269
25026
  mime_type: mimeType,
23270
25027
  tags: input.tags
23271
25028
  });
23272
- const fileBuffer = await fs12.readFile(resolvedPath);
25029
+ const fileBuffer = await fs20.readFile(resolvedPath);
23273
25030
  const uploadResponse = await fetch(uploadInit.upload_url, {
23274
25031
  method: "PUT",
23275
25032
  headers: uploadInit.headers,
@@ -24676,8 +26433,8 @@ var SessionManager = class _SessionManager {
24676
26433
  /**
24677
26434
  * Set the folder path hint (can be passed from tools that know the workspace path)
24678
26435
  */
24679
- setFolderPath(path13) {
24680
- this.folderPath = path13;
26436
+ setFolderPath(path21) {
26437
+ this.folderPath = path21;
24681
26438
  }
24682
26439
  /**
24683
26440
  * Mark that context_smart has been called in this session.
@@ -24867,7 +26624,7 @@ var SessionManager = class _SessionManager {
24867
26624
  }
24868
26625
  if (this.ideRoots.length === 0) {
24869
26626
  const cwd = process.cwd();
24870
- const fs12 = await import("fs");
26627
+ const fs20 = await import("fs");
24871
26628
  const projectIndicators = [
24872
26629
  ".git",
24873
26630
  "package.json",
@@ -24877,7 +26634,7 @@ var SessionManager = class _SessionManager {
24877
26634
  ];
24878
26635
  const hasProjectIndicator = projectIndicators.some((f) => {
24879
26636
  try {
24880
- return fs12.existsSync(`${cwd}/${f}`);
26637
+ return fs20.existsSync(`${cwd}/${f}`);
24881
26638
  } catch {
24882
26639
  return false;
24883
26640
  }
@@ -25454,9 +27211,9 @@ async function runHttpGateway() {
25454
27211
  }
25455
27212
 
25456
27213
  // src/index.ts
25457
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
25458
- import { homedir as homedir10 } from "os";
25459
- import { join as join14 } from "path";
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";
25460
27217
 
25461
27218
  // src/setup.ts
25462
27219
  import * as fs7 from "node:fs/promises";
@@ -26286,10 +28043,10 @@ Code: ${device.user_code}`);
26286
28043
  if (poll && poll.status === "pending") {
26287
28044
  const intervalSeconds = typeof poll.interval === "number" ? poll.interval : 5;
26288
28045
  const waitMs = Math.max(1, intervalSeconds) * 1e3;
26289
- await new Promise((resolve8) => setTimeout(resolve8, waitMs));
28046
+ await new Promise((resolve15) => setTimeout(resolve15, waitMs));
26290
28047
  continue;
26291
28048
  }
26292
- await new Promise((resolve8) => setTimeout(resolve8, 1e3));
28049
+ await new Promise((resolve15) => setTimeout(resolve15, 1e3));
26293
28050
  }
26294
28051
  if (!accessToken) {
26295
28052
  throw new Error(
@@ -26854,12 +28611,12 @@ Applying to ${projects.length} project(s)...`);
26854
28611
  // src/index.ts
26855
28612
  var ENABLE_PROMPTS2 = (process.env.CONTEXTSTREAM_ENABLE_PROMPTS || "true").toLowerCase() !== "false";
26856
28613
  function showFirstRunMessage() {
26857
- const configDir = join14(homedir10(), ".contextstream");
26858
- const starShownFile = join14(configDir, ".star-shown");
26859
- if (existsSync8(starShownFile)) {
28614
+ const configDir = join22(homedir18(), ".contextstream");
28615
+ const starShownFile = join22(configDir, ".star-shown");
28616
+ if (existsSync16(starShownFile)) {
26860
28617
  return;
26861
28618
  }
26862
- if (!existsSync8(configDir)) {
28619
+ if (!existsSync16(configDir)) {
26863
28620
  try {
26864
28621
  mkdirSync5(configDir, { recursive: true });
26865
28622
  } catch {
@@ -26890,13 +28647,26 @@ Usage:
26890
28647
 
26891
28648
  Commands:
26892
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)
26893
28655
  http Run HTTP MCP gateway (streamable HTTP transport)
26894
28656
  hook pre-tool-use PreToolUse hook - blocks discovery tools, redirects to ContextStream
26895
28657
  hook user-prompt-submit UserPromptSubmit hook - injects ContextStream rules reminder
26896
28658
  hook media-aware Media-aware hook - detects media prompts, injects media tool guidance
26897
28659
  hook pre-compact PreCompact hook - saves conversation state before compaction
28660
+ hook post-compact PostCompact hook - restores context after compaction
26898
28661
  hook post-write PostToolUse hook - real-time file indexing after Edit/Write
26899
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
26900
28670
 
26901
28671
  Environment variables:
26902
28672
  CONTEXTSTREAM_API_URL Base API URL (e.g. https://api.contextstream.io)
@@ -27005,12 +28775,90 @@ async function main() {
27005
28775
  await runAutoRulesHook2();
27006
28776
  return;
27007
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
+ }
27008
28818
  default:
27009
28819
  console.error(`Unknown hook: ${hookName}`);
27010
- 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");
27011
28821
  process.exit(1);
27012
28822
  }
27013
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
+ }
27014
28862
  if (!process.env.CONTEXTSTREAM_API_KEY && !process.env.CONTEXTSTREAM_JWT) {
27015
28863
  const saved = await readSavedCredentials();
27016
28864
  if (saved) {