@contextstream/mcp-server 0.4.50 → 0.4.53

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