@contextstream/mcp-server 0.4.80 → 0.4.82

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.
@@ -1,19 +1,51 @@
1
1
  #!/usr/bin/env node
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
8
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
9
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
10
  }) : x)(function(x) {
7
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
8
12
  throw Error('Dynamic require of "' + x + '" is not supported');
9
13
  });
10
- var __esm = (fn, res) => function __init() {
11
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
14
+ var __esm = (fn, res, err) => function __init() {
15
+ if (err) throw err[0];
16
+ try {
17
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
+ } catch (e) {
19
+ throw err = [e], e;
20
+ }
21
+ };
22
+ var __commonJS = (cb, mod) => function __require2() {
23
+ try {
24
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
25
+ } catch (e) {
26
+ throw mod = 0, e;
27
+ }
12
28
  };
13
29
  var __export = (target, all) => {
14
30
  for (var name in all)
15
31
  __defProp(target, name, { get: all[name], enumerable: true });
16
32
  };
33
+ var __copyProps = (to, from, except, desc) => {
34
+ if (from && typeof from === "object" || typeof from === "function") {
35
+ for (let key of __getOwnPropNames(from))
36
+ if (!__hasOwnProp.call(to, key) && key !== except)
37
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
38
+ }
39
+ return to;
40
+ };
41
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
42
+ // If the importer is in node compatibility mode or this is not an ESM
43
+ // file that has been converted to a CommonJS file using a Babel-
44
+ // compatible transform (i.e. "__esModule" has not been set), then set
45
+ // "default" to the CommonJS "module.exports" for node compatibility.
46
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
47
+ mod
48
+ ));
17
49
 
18
50
  // src/hooks/prompt-state.ts
19
51
  import * as fs from "node:fs";
@@ -597,14 +629,638 @@ var init_pre_tool_use = __esm({
597
629
  }
598
630
  });
599
631
 
632
+ // node_modules/ignore/index.js
633
+ var require_ignore = __commonJS({
634
+ "node_modules/ignore/index.js"(exports, module) {
635
+ function makeArray(subject) {
636
+ return Array.isArray(subject) ? subject : [subject];
637
+ }
638
+ var UNDEFINED = void 0;
639
+ var EMPTY = "";
640
+ var SPACE = " ";
641
+ var ESCAPE = "\\";
642
+ var REGEX_TEST_BLANK_LINE = /^\s+$/;
643
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
644
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
645
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
646
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
647
+ var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
648
+ var REGEX_TEST_TRAILING_SLASH = /\/$/;
649
+ var SLASH = "/";
650
+ var TMP_KEY_IGNORE = "node-ignore";
651
+ if (typeof Symbol !== "undefined") {
652
+ TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
653
+ }
654
+ var KEY_IGNORE = TMP_KEY_IGNORE;
655
+ var define = (object, key, value) => {
656
+ Object.defineProperty(object, key, { value });
657
+ return value;
658
+ };
659
+ var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
660
+ var RETURN_FALSE = () => false;
661
+ var sanitizeRange = (range) => range.replace(
662
+ REGEX_REGEXP_RANGE,
663
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
664
+ );
665
+ var cleanRangeBackSlash = (slashes) => {
666
+ const { length } = slashes;
667
+ return slashes.slice(0, length - length % 2);
668
+ };
669
+ var REPLACERS = [
670
+ [
671
+ // Remove BOM
672
+ // TODO:
673
+ // Other similar zero-width characters?
674
+ /^\uFEFF/,
675
+ () => EMPTY
676
+ ],
677
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
678
+ [
679
+ // (a\ ) -> (a )
680
+ // (a ) -> (a)
681
+ // (a ) -> (a)
682
+ // (a \ ) -> (a )
683
+ /((?:\\\\)*?)(\\?\s+)$/,
684
+ (_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
685
+ ],
686
+ // Replace (\ ) with ' '
687
+ // (\ ) -> ' '
688
+ // (\\ ) -> '\\ '
689
+ // (\\\ ) -> '\\ '
690
+ [
691
+ /(\\+?)\s/g,
692
+ (_, m1) => {
693
+ const { length } = m1;
694
+ return m1.slice(0, length - length % 2) + SPACE;
695
+ }
696
+ ],
697
+ // Escape metacharacters
698
+ // which is written down by users but means special for regular expressions.
699
+ // > There are 12 characters with special meanings:
700
+ // > - the backslash \,
701
+ // > - the caret ^,
702
+ // > - the dollar sign $,
703
+ // > - the period or dot .,
704
+ // > - the vertical bar or pipe symbol |,
705
+ // > - the question mark ?,
706
+ // > - the asterisk or star *,
707
+ // > - the plus sign +,
708
+ // > - the opening parenthesis (,
709
+ // > - the closing parenthesis ),
710
+ // > - and the opening square bracket [,
711
+ // > - the opening curly brace {,
712
+ // > These special characters are often called "metacharacters".
713
+ [
714
+ /[\\$.|*+(){^]/g,
715
+ (match) => `\\${match}`
716
+ ],
717
+ [
718
+ // > a question mark (?) matches a single character
719
+ /(?!\\)\?/g,
720
+ () => "[^/]"
721
+ ],
722
+ // leading slash
723
+ [
724
+ // > A leading slash matches the beginning of the pathname.
725
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
726
+ // A leading slash matches the beginning of the pathname
727
+ /^\//,
728
+ () => "^"
729
+ ],
730
+ // replace special metacharacter slash after the leading slash
731
+ [
732
+ /\//g,
733
+ () => "\\/"
734
+ ],
735
+ [
736
+ // > A leading "**" followed by a slash means match in all directories.
737
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
738
+ // > the same as pattern "foo".
739
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
740
+ // > under directory "foo".
741
+ // Notice that the '*'s have been replaced as '\\*'
742
+ /^\^*\\\*\\\*\\\//,
743
+ // '**/foo' <-> 'foo'
744
+ () => "^(?:.*\\/)?"
745
+ ],
746
+ // starting
747
+ [
748
+ // there will be no leading '/'
749
+ // (which has been replaced by section "leading slash")
750
+ // If starts with '**', adding a '^' to the regular expression also works
751
+ /^(?=[^^])/,
752
+ function startingReplacer() {
753
+ return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
754
+ }
755
+ ],
756
+ // two globstars
757
+ [
758
+ // Use lookahead assertions so that we could match more than one `'/**'`
759
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
760
+ // Zero, one or several directories
761
+ // should not use '*', or it will be replaced by the next replacer
762
+ // Check if it is not the last `'/**'`
763
+ (_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
764
+ ],
765
+ // normal intermediate wildcards
766
+ [
767
+ // Never replace escaped '*'
768
+ // ignore rule '\*' will match the path '*'
769
+ // 'abc.*/' -> go
770
+ // 'abc.*' -> skip this rule,
771
+ // coz trailing single wildcard will be handed by [trailing wildcard]
772
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
773
+ // '*.js' matches '.js'
774
+ // '*.js' doesn't match 'abc'
775
+ (_, p1, p2) => {
776
+ const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
777
+ return p1 + unescaped;
778
+ }
779
+ ],
780
+ [
781
+ // unescape, revert step 3 except for back slash
782
+ // For example, if a user escape a '\\*',
783
+ // after step 3, the result will be '\\\\\\*'
784
+ /\\\\\\(?=[$.|*+(){^])/g,
785
+ () => ESCAPE
786
+ ],
787
+ [
788
+ // '\\\\' -> '\\'
789
+ /\\\\/g,
790
+ () => ESCAPE
791
+ ],
792
+ [
793
+ // > The range notation, e.g. [a-zA-Z],
794
+ // > can be used to match one of the characters in a range.
795
+ // `\` is escaped by step 3
796
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
797
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
798
+ ],
799
+ // ending
800
+ [
801
+ // 'js' will not match 'js.'
802
+ // 'ab' will not match 'abc'
803
+ /(?:[^*])$/,
804
+ // WTF!
805
+ // https://git-scm.com/docs/gitignore
806
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
807
+ // which re-fixes #24, #38
808
+ // > If there is a separator at the end of the pattern then the pattern
809
+ // > will only match directories, otherwise the pattern can match both
810
+ // > files and directories.
811
+ // 'js*' will not match 'a.js'
812
+ // 'js/' will not match 'a.js'
813
+ // 'js' will match 'a.js' and 'a.js/'
814
+ (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
815
+ ]
816
+ ];
817
+ var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
818
+ var MODE_IGNORE = "regex";
819
+ var MODE_CHECK_IGNORE = "checkRegex";
820
+ var UNDERSCORE = "_";
821
+ var TRAILING_WILD_CARD_REPLACERS = {
822
+ [MODE_IGNORE](_, p1) {
823
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
824
+ return `${prefix}(?=$|\\/$)`;
825
+ },
826
+ [MODE_CHECK_IGNORE](_, p1) {
827
+ const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
828
+ return `${prefix}(?=$|\\/$)`;
829
+ }
830
+ };
831
+ var makeRegexPrefix = (pattern) => REPLACERS.reduce(
832
+ (prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
833
+ pattern
834
+ );
835
+ var isString = (subject) => typeof subject === "string";
836
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
837
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
838
+ var IgnoreRule = class {
839
+ constructor(pattern, mark, body, ignoreCase, negative, prefix) {
840
+ this.pattern = pattern;
841
+ this.mark = mark;
842
+ this.negative = negative;
843
+ define(this, "body", body);
844
+ define(this, "ignoreCase", ignoreCase);
845
+ define(this, "regexPrefix", prefix);
846
+ }
847
+ get regex() {
848
+ const key = UNDERSCORE + MODE_IGNORE;
849
+ if (this[key]) {
850
+ return this[key];
851
+ }
852
+ return this._make(MODE_IGNORE, key);
853
+ }
854
+ get checkRegex() {
855
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
856
+ if (this[key]) {
857
+ return this[key];
858
+ }
859
+ return this._make(MODE_CHECK_IGNORE, key);
860
+ }
861
+ _make(mode, key) {
862
+ const str = this.regexPrefix.replace(
863
+ REGEX_REPLACE_TRAILING_WILDCARD,
864
+ // It does not need to bind pattern
865
+ TRAILING_WILD_CARD_REPLACERS[mode]
866
+ );
867
+ const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
868
+ return define(this, key, regex);
869
+ }
870
+ };
871
+ var createRule = ({
872
+ pattern,
873
+ mark
874
+ }, ignoreCase) => {
875
+ let negative = false;
876
+ let body = pattern;
877
+ if (body.indexOf("!") === 0) {
878
+ negative = true;
879
+ body = body.substr(1);
880
+ }
881
+ body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
882
+ const regexPrefix = makeRegexPrefix(body);
883
+ return new IgnoreRule(
884
+ pattern,
885
+ mark,
886
+ body,
887
+ ignoreCase,
888
+ negative,
889
+ regexPrefix
890
+ );
891
+ };
892
+ var RuleManager = class {
893
+ constructor(ignoreCase) {
894
+ this._ignoreCase = ignoreCase;
895
+ this._rules = [];
896
+ }
897
+ _add(pattern) {
898
+ if (pattern && pattern[KEY_IGNORE]) {
899
+ this._rules = this._rules.concat(pattern._rules._rules);
900
+ this._added = true;
901
+ return;
902
+ }
903
+ if (isString(pattern)) {
904
+ pattern = {
905
+ pattern
906
+ };
907
+ }
908
+ if (checkPattern(pattern.pattern)) {
909
+ const rule = createRule(pattern, this._ignoreCase);
910
+ this._added = true;
911
+ this._rules.push(rule);
912
+ }
913
+ }
914
+ // @param {Array<string> | string | Ignore} pattern
915
+ add(pattern) {
916
+ this._added = false;
917
+ makeArray(
918
+ isString(pattern) ? splitPattern(pattern) : pattern
919
+ ).forEach(this._add, this);
920
+ return this._added;
921
+ }
922
+ // Test one single path without recursively checking parent directories
923
+ //
924
+ // - checkUnignored `boolean` whether should check if the path is unignored,
925
+ // setting `checkUnignored` to `false` could reduce additional
926
+ // path matching.
927
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
928
+ // @returns {TestResult} true if a file is ignored
929
+ test(path12, checkUnignored, mode) {
930
+ let ignored = false;
931
+ let unignored = false;
932
+ let matchedRule;
933
+ this._rules.forEach((rule) => {
934
+ const { negative } = rule;
935
+ if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
936
+ return;
937
+ }
938
+ const matched = rule[mode].test(path12);
939
+ if (!matched) {
940
+ return;
941
+ }
942
+ ignored = !negative;
943
+ unignored = negative;
944
+ matchedRule = negative ? UNDEFINED : rule;
945
+ });
946
+ const ret = {
947
+ ignored,
948
+ unignored
949
+ };
950
+ if (matchedRule) {
951
+ ret.rule = matchedRule;
952
+ }
953
+ return ret;
954
+ }
955
+ };
956
+ var throwError = (message, Ctor) => {
957
+ throw new Ctor(message);
958
+ };
959
+ var checkPath = (path12, originalPath, doThrow) => {
960
+ if (!isString(path12)) {
961
+ return doThrow(
962
+ `path must be a string, but got \`${originalPath}\``,
963
+ TypeError
964
+ );
965
+ }
966
+ if (!path12) {
967
+ return doThrow(`path must not be empty`, TypeError);
968
+ }
969
+ if (checkPath.isNotRelative(path12)) {
970
+ const r = "`path.relative()`d";
971
+ return doThrow(
972
+ `path should be a ${r} string, but got "${originalPath}"`,
973
+ RangeError
974
+ );
975
+ }
976
+ return true;
977
+ };
978
+ var isNotRelative = (path12) => REGEX_TEST_INVALID_PATH.test(path12);
979
+ checkPath.isNotRelative = isNotRelative;
980
+ checkPath.convert = (p) => p;
981
+ var Ignore2 = class {
982
+ constructor({
983
+ ignorecase = true,
984
+ ignoreCase = ignorecase,
985
+ allowRelativePaths = false
986
+ } = {}) {
987
+ define(this, KEY_IGNORE, true);
988
+ this._rules = new RuleManager(ignoreCase);
989
+ this._strictPathCheck = !allowRelativePaths;
990
+ this._initCache();
991
+ }
992
+ _initCache() {
993
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
994
+ this._testCache = /* @__PURE__ */ Object.create(null);
995
+ }
996
+ add(pattern) {
997
+ if (this._rules.add(pattern)) {
998
+ this._initCache();
999
+ }
1000
+ return this;
1001
+ }
1002
+ // legacy
1003
+ addPattern(pattern) {
1004
+ return this.add(pattern);
1005
+ }
1006
+ // @returns {TestResult}
1007
+ _test(originalPath, cache, checkUnignored, slices) {
1008
+ const path12 = originalPath && checkPath.convert(originalPath);
1009
+ checkPath(
1010
+ path12,
1011
+ originalPath,
1012
+ this._strictPathCheck ? throwError : RETURN_FALSE
1013
+ );
1014
+ return this._t(path12, cache, checkUnignored, slices);
1015
+ }
1016
+ checkIgnore(path12) {
1017
+ if (!REGEX_TEST_TRAILING_SLASH.test(path12)) {
1018
+ return this.test(path12);
1019
+ }
1020
+ const slices = path12.split(SLASH).filter(Boolean);
1021
+ slices.pop();
1022
+ if (slices.length) {
1023
+ const parent = this._t(
1024
+ slices.join(SLASH) + SLASH,
1025
+ this._testCache,
1026
+ true,
1027
+ slices
1028
+ );
1029
+ if (parent.ignored) {
1030
+ return parent;
1031
+ }
1032
+ }
1033
+ return this._rules.test(path12, false, MODE_CHECK_IGNORE);
1034
+ }
1035
+ _t(path12, cache, checkUnignored, slices) {
1036
+ if (path12 in cache) {
1037
+ return cache[path12];
1038
+ }
1039
+ if (!slices) {
1040
+ slices = path12.split(SLASH).filter(Boolean);
1041
+ }
1042
+ slices.pop();
1043
+ if (!slices.length) {
1044
+ return cache[path12] = this._rules.test(path12, checkUnignored, MODE_IGNORE);
1045
+ }
1046
+ const parent = this._t(
1047
+ slices.join(SLASH) + SLASH,
1048
+ cache,
1049
+ checkUnignored,
1050
+ slices
1051
+ );
1052
+ return cache[path12] = parent.ignored ? parent : this._rules.test(path12, checkUnignored, MODE_IGNORE);
1053
+ }
1054
+ ignores(path12) {
1055
+ return this._test(path12, this._ignoreCache, false).ignored;
1056
+ }
1057
+ createFilter() {
1058
+ return (path12) => !this.ignores(path12);
1059
+ }
1060
+ filter(paths) {
1061
+ return makeArray(paths).filter(this.createFilter());
1062
+ }
1063
+ // @returns {TestResult}
1064
+ test(path12) {
1065
+ return this._test(path12, this._testCache, true);
1066
+ }
1067
+ };
1068
+ var factory = (options) => new Ignore2(options);
1069
+ var isPathValid = (path12) => checkPath(path12 && checkPath.convert(path12), path12, RETURN_FALSE);
1070
+ var setupWindows = () => {
1071
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
1072
+ checkPath.convert = makePosix;
1073
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
1074
+ checkPath.isNotRelative = (path12) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path12) || isNotRelative(path12);
1075
+ };
1076
+ if (
1077
+ // Detect `process` so that it can run in browsers.
1078
+ typeof process !== "undefined" && process.platform === "win32"
1079
+ ) {
1080
+ setupWindows();
1081
+ }
1082
+ module.exports = factory;
1083
+ factory.default = factory;
1084
+ module.exports.isPathValid = isPathValid;
1085
+ define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
1086
+ }
1087
+ });
1088
+
1089
+ // src/ignore.ts
1090
+ import * as fs3 from "fs";
1091
+ import * as path3 from "path";
1092
+ function isInvariantIgnoredPath(pathname) {
1093
+ const normalized = pathname.replace(/\\/g, "/").replace(/^\.\//, "");
1094
+ const segments = normalized.split("/").filter(Boolean);
1095
+ if (segments.some((segment) => INVARIANT_IGNORED_DIRECTORIES.has(segment.toLowerCase()))) {
1096
+ return true;
1097
+ }
1098
+ const basename2 = (segments.at(-1) || "").toLowerCase();
1099
+ if (INVARIANT_SECRET_FILENAMES.has(basename2)) return true;
1100
+ if (INVARIANT_SECRET_SUFFIXES.some((suffix) => basename2.endsWith(suffix))) return true;
1101
+ return (basename2 === ".env" || basename2.startsWith(".env.")) && !ENV_TEMPLATE_ALLOWLIST.has(basename2);
1102
+ }
1103
+ function loadIgnorePatternsSync(projectRoot) {
1104
+ const ig = (0, import_ignore.default)();
1105
+ const patterns = [...DEFAULT_IGNORE_PATTERNS];
1106
+ ig.add(DEFAULT_IGNORE_PATTERNS);
1107
+ let hasUserPatterns = false;
1108
+ for (const filename of IGNORE_FILENAMES) {
1109
+ try {
1110
+ const content = fs3.readFileSync(path3.join(projectRoot, filename), "utf-8");
1111
+ const userPatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
1112
+ if (userPatterns.length > 0) {
1113
+ ig.add(userPatterns);
1114
+ patterns.push(...userPatterns);
1115
+ hasUserPatterns = true;
1116
+ }
1117
+ } catch {
1118
+ }
1119
+ }
1120
+ return {
1121
+ ignores: (pathname) => isInvariantIgnoredPath(pathname) || ig.ignores(pathname),
1122
+ patterns,
1123
+ hasUserPatterns
1124
+ };
1125
+ }
1126
+ var import_ignore, IGNORE_FILENAMES, INVARIANT_IGNORED_DIRECTORIES, INVARIANT_SECRET_FILENAMES, INVARIANT_SECRET_SUFFIXES, ENV_TEMPLATE_ALLOWLIST, DEFAULT_IGNORE_PATTERNS;
1127
+ var init_ignore = __esm({
1128
+ "src/ignore.ts"() {
1129
+ "use strict";
1130
+ import_ignore = __toESM(require_ignore(), 1);
1131
+ IGNORE_FILENAMES = [".gitignore", ".contextignore", ".contextstream/ignore"];
1132
+ INVARIANT_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
1133
+ ".contextstream",
1134
+ ".claude",
1135
+ ".codex",
1136
+ ".cursor",
1137
+ ".windsurf",
1138
+ ".roo",
1139
+ ".kilocode",
1140
+ ".ssh",
1141
+ ".aws",
1142
+ ".gnupg",
1143
+ ".kube",
1144
+ ".docker"
1145
+ ]);
1146
+ INVARIANT_SECRET_FILENAMES = /* @__PURE__ */ new Set([
1147
+ ".npmrc",
1148
+ ".pypirc",
1149
+ ".netrc",
1150
+ ".pgpass",
1151
+ ".git-credentials",
1152
+ "id_rsa",
1153
+ "id_ed25519",
1154
+ "id_ecdsa",
1155
+ "known_hosts",
1156
+ "credentials.json",
1157
+ "serviceaccountkey.json",
1158
+ "auth.json"
1159
+ ]);
1160
+ INVARIANT_SECRET_SUFFIXES = [".pem", ".key", ".p12", ".pfx", ".jks"];
1161
+ ENV_TEMPLATE_ALLOWLIST = /* @__PURE__ */ new Set([
1162
+ ".env.example",
1163
+ ".env.sample",
1164
+ ".env.template",
1165
+ ".env.dist",
1166
+ ".env.defaults"
1167
+ ]);
1168
+ DEFAULT_IGNORE_PATTERNS = [
1169
+ // Version control
1170
+ ".git/",
1171
+ ".svn/",
1172
+ ".hg/",
1173
+ // ContextStream and AI editor/agent state. These directories can contain
1174
+ // credentials, generated rules, caches, and nested worktrees.
1175
+ ".contextstream/",
1176
+ ".claude/",
1177
+ ".codex/",
1178
+ ".cursor/",
1179
+ ".windsurf/",
1180
+ ".roo/",
1181
+ ".kilocode/",
1182
+ // Package managers / dependencies
1183
+ "node_modules/",
1184
+ "vendor/",
1185
+ ".pnpm/",
1186
+ // Build outputs
1187
+ "target/",
1188
+ "dist/",
1189
+ "build/",
1190
+ "out/",
1191
+ ".next/",
1192
+ ".nuxt/",
1193
+ ".svelte-kit/",
1194
+ ".parcel-cache/",
1195
+ ".turbo/",
1196
+ ".gradle/",
1197
+ ".cache/",
1198
+ "bin/",
1199
+ "obj/",
1200
+ // Python
1201
+ "__pycache__/",
1202
+ ".pytest_cache/",
1203
+ ".mypy_cache/",
1204
+ "venv/",
1205
+ ".venv/",
1206
+ "env/",
1207
+ ".env/",
1208
+ // IDE
1209
+ ".idea/",
1210
+ ".vscode/",
1211
+ ".vs/",
1212
+ // Credential stores
1213
+ ".ssh/",
1214
+ ".aws/",
1215
+ ".gnupg/",
1216
+ ".kube/",
1217
+ ".docker/",
1218
+ // Coverage
1219
+ "coverage/",
1220
+ ".coverage/",
1221
+ // Lock files
1222
+ "package-lock.json",
1223
+ "yarn.lock",
1224
+ "pnpm-lock.yaml",
1225
+ "Cargo.lock",
1226
+ "poetry.lock",
1227
+ "Gemfile.lock",
1228
+ "composer.lock",
1229
+ "*.min.js",
1230
+ "*.min.css",
1231
+ "*.pem",
1232
+ "*.key",
1233
+ "*.p12",
1234
+ "*.pfx",
1235
+ "*.jks",
1236
+ ".npmrc",
1237
+ ".pypirc",
1238
+ ".netrc",
1239
+ ".pgpass",
1240
+ ".git-credentials",
1241
+ "id_rsa",
1242
+ "id_ed25519",
1243
+ "id_ecdsa",
1244
+ "credentials.json",
1245
+ "serviceAccountKey.json",
1246
+ "auth.json",
1247
+ // OS files
1248
+ ".DS_Store",
1249
+ "Thumbs.db"
1250
+ ];
1251
+ }
1252
+ });
1253
+
600
1254
  // src/hooks/post-write.ts
601
1255
  var post_write_exports = {};
602
1256
  __export(post_write_exports, {
603
- runPostWriteHook: () => runPostWriteHook
1257
+ runPostWriteHook: () => runPostWriteHook,
1258
+ shouldIgnoreHookPath: () => shouldIgnoreHookPath
604
1259
  });
605
- import * as fs3 from "node:fs";
606
- import * as path3 from "node:path";
1260
+ import * as fs4 from "node:fs";
1261
+ import * as path4 from "node:path";
607
1262
  import { homedir as homedir3 } from "node:os";
1263
+ import { execFileSync } from "node:child_process";
608
1264
  function extractFilePath(input) {
609
1265
  if (input.tool_input) {
610
1266
  const filePath = input.tool_input.file_path || input.tool_input.notebook_path || input.tool_input.path;
@@ -629,17 +1285,17 @@ function extractCwd2(input) {
629
1285
  return process.cwd();
630
1286
  }
631
1287
  function findLocalConfig(startDir) {
632
- let currentDir = path3.resolve(startDir);
1288
+ let currentDir = path4.resolve(startDir);
633
1289
  for (let i = 0; i < 10; i++) {
634
- const configPath = path3.join(currentDir, ".contextstream", "config.json");
635
- if (fs3.existsSync(configPath)) {
1290
+ const configPath = path4.join(currentDir, ".contextstream", "config.json");
1291
+ if (fs4.existsSync(configPath)) {
636
1292
  try {
637
- const content = fs3.readFileSync(configPath, "utf-8");
1293
+ const content = fs4.readFileSync(configPath, "utf-8");
638
1294
  return JSON.parse(content);
639
1295
  } catch {
640
1296
  }
641
1297
  }
642
- const parentDir = path3.dirname(currentDir);
1298
+ const parentDir = path4.dirname(currentDir);
643
1299
  if (parentDir === currentDir) break;
644
1300
  currentDir = parentDir;
645
1301
  }
@@ -651,12 +1307,12 @@ function loadApiConfig(startDir) {
651
1307
  if (apiKey) {
652
1308
  return { apiUrl, apiKey };
653
1309
  }
654
- let currentDir = path3.resolve(startDir);
1310
+ let currentDir = path4.resolve(startDir);
655
1311
  for (let i = 0; i < 10; i++) {
656
- const mcpPath = path3.join(currentDir, ".mcp.json");
657
- if (fs3.existsSync(mcpPath)) {
1312
+ const mcpPath = path4.join(currentDir, ".mcp.json");
1313
+ if (fs4.existsSync(mcpPath)) {
658
1314
  try {
659
- const content = fs3.readFileSync(mcpPath, "utf-8");
1315
+ const content = fs4.readFileSync(mcpPath, "utf-8");
660
1316
  const config = JSON.parse(content);
661
1317
  const csEnv = config.mcpServers?.contextstream?.env;
662
1318
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -669,15 +1325,15 @@ function loadApiConfig(startDir) {
669
1325
  } catch {
670
1326
  }
671
1327
  }
672
- const parentDir = path3.dirname(currentDir);
1328
+ const parentDir = path4.dirname(currentDir);
673
1329
  if (parentDir === currentDir) break;
674
1330
  currentDir = parentDir;
675
1331
  }
676
1332
  if (!apiKey) {
677
- const homeMcpPath = path3.join(homedir3(), ".mcp.json");
678
- if (fs3.existsSync(homeMcpPath)) {
1333
+ const homeMcpPath = path4.join(homedir3(), ".mcp.json");
1334
+ if (fs4.existsSync(homeMcpPath)) {
679
1335
  try {
680
- const content = fs3.readFileSync(homeMcpPath, "utf-8");
1336
+ const content = fs4.readFileSync(homeMcpPath, "utf-8");
681
1337
  const config = JSON.parse(content);
682
1338
  const csEnv = config.mcpServers?.contextstream?.env;
683
1339
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -693,15 +1349,15 @@ function loadApiConfig(startDir) {
693
1349
  return { apiUrl, apiKey };
694
1350
  }
695
1351
  function shouldIndexFile(filePath) {
696
- const ext = path3.extname(filePath).toLowerCase();
1352
+ const ext = path4.extname(filePath).toLowerCase();
697
1353
  if (!INDEXABLE_EXTENSIONS.has(ext)) {
698
- const basename2 = path3.basename(filePath).toLowerCase();
1354
+ const basename2 = path4.basename(filePath).toLowerCase();
699
1355
  if (!["dockerfile", "makefile", "rakefile", "gemfile", "procfile"].includes(basename2)) {
700
1356
  return false;
701
1357
  }
702
1358
  }
703
1359
  try {
704
- const stats = fs3.statSync(filePath);
1360
+ const stats = fs4.statSync(filePath);
705
1361
  if (stats.size > MAX_FILE_SIZE) {
706
1362
  return false;
707
1363
  }
@@ -710,8 +1366,31 @@ function shouldIndexFile(filePath) {
710
1366
  }
711
1367
  return true;
712
1368
  }
1369
+ function shouldIgnoreHookPath(projectRoot, filePath) {
1370
+ const root = path4.resolve(projectRoot);
1371
+ const absolutePath = path4.resolve(filePath);
1372
+ if (isInvariantIgnoredPath(absolutePath)) {
1373
+ return true;
1374
+ }
1375
+ const relativePath = path4.relative(root, absolutePath).replace(/\\/g, "/");
1376
+ if (!relativePath || relativePath === ".." || relativePath.startsWith("../")) {
1377
+ return true;
1378
+ }
1379
+ if (loadIgnorePatternsSync(root).ignores(relativePath)) {
1380
+ return true;
1381
+ }
1382
+ try {
1383
+ execFileSync("git", ["-C", root, "check-ignore", "-q", "--", relativePath], {
1384
+ stdio: "ignore",
1385
+ timeout: 2e3
1386
+ });
1387
+ return true;
1388
+ } catch {
1389
+ return false;
1390
+ }
1391
+ }
713
1392
  function detectLanguage(filePath) {
714
- const ext = path3.extname(filePath).toLowerCase();
1393
+ const ext = path4.extname(filePath).toLowerCase();
715
1394
  const langMap = {
716
1395
  ".ts": "typescript",
717
1396
  ".tsx": "typescript",
@@ -780,8 +1459,8 @@ function detectLanguage(filePath) {
780
1459
  return langMap[ext] || "text";
781
1460
  }
782
1461
  async function indexFile(filePath, projectId, apiUrl, apiKey, projectRoot) {
783
- const content = fs3.readFileSync(filePath, "utf-8");
784
- const relativePath = path3.relative(projectRoot, filePath);
1462
+ const content = fs4.readFileSync(filePath, "utf-8");
1463
+ const relativePath = path4.relative(projectRoot, filePath);
785
1464
  const payload = {
786
1465
  files: [
787
1466
  {
@@ -813,13 +1492,13 @@ async function indexFile(filePath, projectId, apiUrl, apiKey, projectRoot) {
813
1492
  }
814
1493
  }
815
1494
  function findProjectRoot(filePath) {
816
- let currentDir = path3.dirname(path3.resolve(filePath));
1495
+ let currentDir = path4.dirname(path4.resolve(filePath));
817
1496
  for (let i = 0; i < 10; i++) {
818
- const configPath = path3.join(currentDir, ".contextstream", "config.json");
819
- if (fs3.existsSync(configPath)) {
1497
+ const configPath = path4.join(currentDir, ".contextstream", "config.json");
1498
+ if (fs4.existsSync(configPath)) {
820
1499
  return currentDir;
821
1500
  }
822
- const parentDir = path3.dirname(currentDir);
1501
+ const parentDir = path4.dirname(currentDir);
823
1502
  if (parentDir === currentDir) break;
824
1503
  currentDir = parentDir;
825
1504
  }
@@ -847,14 +1526,17 @@ async function runPostWriteHook() {
847
1526
  process.exit(0);
848
1527
  }
849
1528
  const cwd = extractCwd2(input);
850
- const absolutePath = path3.isAbsolute(filePath) ? filePath : path3.resolve(cwd, filePath);
851
- if (!fs3.existsSync(absolutePath) || !shouldIndexFile(absolutePath)) {
1529
+ const absolutePath = path4.isAbsolute(filePath) ? filePath : path4.resolve(cwd, filePath);
1530
+ if (!fs4.existsSync(absolutePath) || !shouldIndexFile(absolutePath)) {
852
1531
  process.exit(0);
853
1532
  }
854
1533
  const projectRoot = findProjectRoot(absolutePath);
855
1534
  if (!projectRoot) {
856
1535
  process.exit(0);
857
1536
  }
1537
+ if (shouldIgnoreHookPath(projectRoot, absolutePath)) {
1538
+ process.exit(0);
1539
+ }
858
1540
  const localConfig = findLocalConfig(projectRoot);
859
1541
  if (!localConfig?.project_id) {
860
1542
  process.exit(0);
@@ -873,6 +1555,7 @@ var API_URL, API_KEY, ENABLED2, INDEXABLE_EXTENSIONS, MAX_FILE_SIZE, isDirectRun
873
1555
  var init_post_write = __esm({
874
1556
  "src/hooks/post-write.ts"() {
875
1557
  "use strict";
1558
+ init_ignore();
876
1559
  API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
877
1560
  API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
878
1561
  ENABLED2 = process.env.CONTEXTSTREAM_POSTWRITE_ENABLED !== "false";
@@ -953,9 +1636,9 @@ var init_post_write = __esm({
953
1636
 
954
1637
  // src/version.ts
955
1638
  import { createRequire } from "module";
956
- import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1639
+ import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
957
1640
  import { homedir as homedir4, platform } from "os";
958
- import { join as join4 } from "path";
1641
+ import { join as join5 } from "path";
959
1642
  import { spawn } from "child_process";
960
1643
  function getVersion() {
961
1644
  if (typeof __CONTEXTSTREAM_VERSION__ !== "undefined" && __CONTEXTSTREAM_VERSION__) {
@@ -982,13 +1665,13 @@ function compareVersions(v1, v2) {
982
1665
  return 0;
983
1666
  }
984
1667
  function getCacheFilePath() {
985
- return join4(homedir4(), ".contextstream", "version-cache.json");
1668
+ return join5(homedir4(), ".contextstream", "version-cache.json");
986
1669
  }
987
1670
  function readCache() {
988
1671
  try {
989
1672
  const cacheFile = getCacheFilePath();
990
1673
  if (!existsSync3(cacheFile)) return null;
991
- const data = JSON.parse(readFileSync4(cacheFile, "utf-8"));
1674
+ const data = JSON.parse(readFileSync5(cacheFile, "utf-8"));
992
1675
  if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
993
1676
  return data;
994
1677
  } catch {
@@ -997,7 +1680,7 @@ function readCache() {
997
1680
  }
998
1681
  function writeCache(latestVersion) {
999
1682
  try {
1000
- const configDir = join4(homedir4(), ".contextstream");
1683
+ const configDir = join5(homedir4(), ".contextstream");
1001
1684
  if (!existsSync3(configDir)) {
1002
1685
  mkdirSync2(configDir, { recursive: true });
1003
1686
  }
@@ -1113,9 +1796,9 @@ function isAutoUpdateEnabled() {
1113
1796
  return false;
1114
1797
  }
1115
1798
  try {
1116
- const configPath = join4(homedir4(), ".contextstream", "config.json");
1799
+ const configPath = join5(homedir4(), ".contextstream", "config.json");
1117
1800
  if (existsSync3(configPath)) {
1118
- const config = JSON.parse(readFileSync4(configPath, "utf-8"));
1801
+ const config = JSON.parse(readFileSync5(configPath, "utf-8"));
1119
1802
  if (config.auto_update === false) {
1120
1803
  return false;
1121
1804
  }
@@ -1218,8 +1901,8 @@ async function runUpdate(method) {
1218
1901
  }
1219
1902
  function writeUpdateMarker(previousVersion, newVersion) {
1220
1903
  try {
1221
- const markerPath = join4(homedir4(), ".contextstream", "update-pending.json");
1222
- const configDir = join4(homedir4(), ".contextstream");
1904
+ const markerPath = join5(homedir4(), ".contextstream", "update-pending.json");
1905
+ const configDir = join5(homedir4(), ".contextstream");
1223
1906
  if (!existsSync3(configDir)) {
1224
1907
  mkdirSync2(configDir, { recursive: true });
1225
1908
  }
@@ -1233,9 +1916,9 @@ function writeUpdateMarker(previousVersion, newVersion) {
1233
1916
  }
1234
1917
  function checkUpdateMarker() {
1235
1918
  try {
1236
- const markerPath = join4(homedir4(), ".contextstream", "update-pending.json");
1919
+ const markerPath = join5(homedir4(), ".contextstream", "update-pending.json");
1237
1920
  if (!existsSync3(markerPath)) return null;
1238
- const marker = JSON.parse(readFileSync4(markerPath, "utf-8"));
1921
+ const marker = JSON.parse(readFileSync5(markerPath, "utf-8"));
1239
1922
  const updatedAt = new Date(marker.updatedAt);
1240
1923
  const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
1241
1924
  if (updatedAt < hourAgo) {
@@ -1252,7 +1935,7 @@ function checkUpdateMarker() {
1252
1935
  }
1253
1936
  function clearUpdateMarker() {
1254
1937
  try {
1255
- const markerPath = join4(homedir4(), ".contextstream", "update-pending.json");
1938
+ const markerPath = join5(homedir4(), ".contextstream", "update-pending.json");
1256
1939
  if (existsSync3(markerPath)) {
1257
1940
  __require("fs").unlinkSync(markerPath);
1258
1941
  }
@@ -1282,17 +1965,17 @@ var user_prompt_submit_exports = {};
1282
1965
  __export(user_prompt_submit_exports, {
1283
1966
  runUserPromptSubmitHook: () => runUserPromptSubmitHook
1284
1967
  });
1285
- import * as fs4 from "node:fs";
1286
- import * as path4 from "node:path";
1968
+ import * as fs5 from "node:fs";
1969
+ import * as path5 from "node:path";
1287
1970
  import { homedir as homedir5 } from "node:os";
1288
1971
  function loadConfigFromMcpJson(cwd) {
1289
- let searchDir = path4.resolve(cwd);
1972
+ let searchDir = path5.resolve(cwd);
1290
1973
  for (let i = 0; i < 5; i++) {
1291
1974
  if (!API_KEY2) {
1292
- const mcpPath = path4.join(searchDir, ".mcp.json");
1293
- if (fs4.existsSync(mcpPath)) {
1975
+ const mcpPath = path5.join(searchDir, ".mcp.json");
1976
+ if (fs5.existsSync(mcpPath)) {
1294
1977
  try {
1295
- const content = fs4.readFileSync(mcpPath, "utf-8");
1978
+ const content = fs5.readFileSync(mcpPath, "utf-8");
1296
1979
  const config = JSON.parse(content);
1297
1980
  const csEnv = config.mcpServers?.contextstream?.env;
1298
1981
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -1309,10 +1992,10 @@ function loadConfigFromMcpJson(cwd) {
1309
1992
  }
1310
1993
  }
1311
1994
  if (!WORKSPACE_ID || !PROJECT_ID) {
1312
- const csConfigPath = path4.join(searchDir, ".contextstream", "config.json");
1313
- if (fs4.existsSync(csConfigPath)) {
1995
+ const csConfigPath = path5.join(searchDir, ".contextstream", "config.json");
1996
+ if (fs5.existsSync(csConfigPath)) {
1314
1997
  try {
1315
- const content = fs4.readFileSync(csConfigPath, "utf-8");
1998
+ const content = fs5.readFileSync(csConfigPath, "utf-8");
1316
1999
  const csConfig = JSON.parse(content);
1317
2000
  if (csConfig.workspace_id && !WORKSPACE_ID) {
1318
2001
  WORKSPACE_ID = csConfig.workspace_id;
@@ -1324,15 +2007,15 @@ function loadConfigFromMcpJson(cwd) {
1324
2007
  }
1325
2008
  }
1326
2009
  }
1327
- const parentDir = path4.dirname(searchDir);
2010
+ const parentDir = path5.dirname(searchDir);
1328
2011
  if (parentDir === searchDir) break;
1329
2012
  searchDir = parentDir;
1330
2013
  }
1331
2014
  if (!API_KEY2) {
1332
- const homeMcpPath = path4.join(homedir5(), ".mcp.json");
1333
- if (fs4.existsSync(homeMcpPath)) {
2015
+ const homeMcpPath = path5.join(homedir5(), ".mcp.json");
2016
+ if (fs5.existsSync(homeMcpPath)) {
1334
2017
  try {
1335
- const content = fs4.readFileSync(homeMcpPath, "utf-8");
2018
+ const content = fs5.readFileSync(homeMcpPath, "utf-8");
1336
2019
  const config = JSON.parse(content);
1337
2020
  const csEnv = config.mcpServers?.contextstream?.env;
1338
2021
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -1348,7 +2031,7 @@ function loadConfigFromMcpJson(cwd) {
1348
2031
  }
1349
2032
  function readTranscriptFile(transcriptPath) {
1350
2033
  try {
1351
- const content = fs4.readFileSync(transcriptPath, "utf-8");
2034
+ const content = fs5.readFileSync(transcriptPath, "utf-8");
1352
2035
  const lines = content.trim().split("\n");
1353
2036
  const messages = [];
1354
2037
  for (const line of lines) {
@@ -1822,9 +2505,10 @@ var init_user_prompt_submit = __esm({
1822
2505
  API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
1823
2506
  WORKSPACE_ID = null;
1824
2507
  PROJECT_ID = null;
1825
- REMINDER = `[CONTEXTSTREAM] On the first message in every session call mcp__contextstream__init(...), then call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. On subsequent messages, default to context first. Narrow bypass is allowed only for immediate read-only ContextStream calls when prior context is fresh and no state-changing tool has run. Response contains dynamic rules, lessons, preferences.
2508
+ REMINDER = `[CONTEXTSTREAM] On the first message in every session call mcp__contextstream__init(...), then call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. On subsequent messages, default to context first. The fresh-context direct-read lane is limited to workspace list/get; memory list_*; help version/tools/auth; project list/get/index_status; and reminder list/active. Recall, decisions, searches, specific reads, writes, and indexing require context first. Fresh [GROUNDING] is the first continuation retrieval; do not immediately duplicate it with recall. Response contains dynamic rules, lessons, preferences.
1826
2509
  SEARCH-FIRST: Use mcp__contextstream__search(mode="auto") before Glob/Grep/Read/Explore/Task/EnterPlanMode. In planning, never use EnterPlanMode or Task(Explore) for file-by-file discovery.
1827
2510
  COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via session(action="get_lessons"), list plans via session(action="list_plans"), list tasks/todos via memory(action="list_tasks"|"list_todos").
2511
+ HANDOFFS: A generic handoff must create entity(kind="handoff", action="create", body={title,summary,scope,next_steps}); add capsule only for a requested portable bundle/share link. HANDOFF.md or prose alone is not the canonical handoff.
1828
2512
  [END]`;
1829
2513
  FULL_REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
1830
2514
 
@@ -1833,7 +2517,8 @@ COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via
1833
2517
  - Check response for: [LESSONS_WARNING], [RULES_NOTICE], preferences
1834
2518
  - save_exchange=true saves each conversation turn for later retrieval
1835
2519
  - Use a consistent session_id for the entire conversation (generate once on first message)
1836
- - On subsequent messages, default to context() first. Narrow bypass: immediate read-only ContextStream calls when context is fresh and no state-changing tool has run.
2520
+ - On subsequent messages, default to context() first. Direct-read bypass is limited to workspace list/get; memory list_*; help version/tools/auth; project list/get/index_status; and reminder list/active when context is fresh and no state-changing tool has run.
2521
+ - Fresh [GROUNDING] is the first continuation retrieval. Do not immediately duplicate sufficient grounding with session(action="recall").
1837
2522
 
1838
2523
  2. FOR CODE SEARCH: Check index status, then search appropriately
1839
2524
  \u2705 Use mcp__contextstream__search(mode="auto", query="...") BEFORE Glob/Grep/Read/Explore/Task/EnterPlanMode
@@ -1851,13 +2536,18 @@ COMMON MEMORY CALLS: list docs via memory(action="list_docs"), list lessons via
1851
2536
  \u274C DO NOT use EnterPlanMode or Task(subagent_type="Explore") for file-by-file discovery
1852
2537
  \u2705 For planning discovery: mcp__contextstream__search(mode="auto", query="...", output_format="paths")
1853
2538
 
1854
- 5. CHECK THESE from context() response:
2539
+ 5. FOR HANDOFFS: Create the canonical durable handoff entity
2540
+ \u2705 mcp__contextstream__entity(kind="handoff", action="create", body={title,summary,scope,next_steps})
2541
+ \u2705 Add a capsule only when a portable bundle or share link is requested
2542
+ \u274C HANDOFF.md, a scratch prompt, or prose alone is not the canonical handoff
2543
+
2544
+ 6. CHECK THESE from context() response:
1855
2545
  - Lessons: Past mistakes to avoid (shown as warnings)
1856
2546
  - Reminders: Active reminders for this project
1857
2547
  - Preferences: User's coding style and preferences
1858
2548
  - Rules: Dynamic rules matched to current task
1859
2549
 
1860
- 6. SKIP CONTEXTSTREAM: If user preference says "skip contextstream", use local tools instead
2550
+ 7. SKIP CONTEXTSTREAM: If user preference says "skip contextstream", use local tools instead
1861
2551
  [END]`;
1862
2552
  ENHANCED_REMINDER_HEADER = `\u2B21 ContextStream \u2014 Smart Context & Memory
1863
2553
 
@@ -1888,17 +2578,17 @@ var pre_compact_exports = {};
1888
2578
  __export(pre_compact_exports, {
1889
2579
  runPreCompactHook: () => runPreCompactHook
1890
2580
  });
1891
- import * as fs5 from "node:fs";
1892
- import * as path5 from "node:path";
2581
+ import * as fs6 from "node:fs";
2582
+ import * as path6 from "node:path";
1893
2583
  import { homedir as homedir6 } from "node:os";
1894
2584
  function loadConfigFromMcpJson2(cwd) {
1895
- let searchDir = path5.resolve(cwd);
2585
+ let searchDir = path6.resolve(cwd);
1896
2586
  for (let i = 0; i < 5; i++) {
1897
2587
  if (!API_KEY3) {
1898
- const mcpPath = path5.join(searchDir, ".mcp.json");
1899
- if (fs5.existsSync(mcpPath)) {
2588
+ const mcpPath = path6.join(searchDir, ".mcp.json");
2589
+ if (fs6.existsSync(mcpPath)) {
1900
2590
  try {
1901
- const content = fs5.readFileSync(mcpPath, "utf-8");
2591
+ const content = fs6.readFileSync(mcpPath, "utf-8");
1902
2592
  const config = JSON.parse(content);
1903
2593
  const csEnv = config.mcpServers?.contextstream?.env;
1904
2594
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -1912,10 +2602,10 @@ function loadConfigFromMcpJson2(cwd) {
1912
2602
  }
1913
2603
  }
1914
2604
  if (!WORKSPACE_ID2) {
1915
- const csConfigPath = path5.join(searchDir, ".contextstream", "config.json");
1916
- if (fs5.existsSync(csConfigPath)) {
2605
+ const csConfigPath = path6.join(searchDir, ".contextstream", "config.json");
2606
+ if (fs6.existsSync(csConfigPath)) {
1917
2607
  try {
1918
- const content = fs5.readFileSync(csConfigPath, "utf-8");
2608
+ const content = fs6.readFileSync(csConfigPath, "utf-8");
1919
2609
  const csConfig = JSON.parse(content);
1920
2610
  if (csConfig.workspace_id) {
1921
2611
  WORKSPACE_ID2 = csConfig.workspace_id;
@@ -1924,15 +2614,15 @@ function loadConfigFromMcpJson2(cwd) {
1924
2614
  }
1925
2615
  }
1926
2616
  }
1927
- const parentDir = path5.dirname(searchDir);
2617
+ const parentDir = path6.dirname(searchDir);
1928
2618
  if (parentDir === searchDir) break;
1929
2619
  searchDir = parentDir;
1930
2620
  }
1931
2621
  if (!API_KEY3) {
1932
- const homeMcpPath = path5.join(homedir6(), ".mcp.json");
1933
- if (fs5.existsSync(homeMcpPath)) {
2622
+ const homeMcpPath = path6.join(homedir6(), ".mcp.json");
2623
+ if (fs6.existsSync(homeMcpPath)) {
1934
2624
  try {
1935
- const content = fs5.readFileSync(homeMcpPath, "utf-8");
2625
+ const content = fs6.readFileSync(homeMcpPath, "utf-8");
1936
2626
  const config = JSON.parse(content);
1937
2627
  const csEnv = config.mcpServers?.contextstream?.env;
1938
2628
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -1954,7 +2644,7 @@ function parseTranscript(transcriptPath) {
1954
2644
  let startedAt = (/* @__PURE__ */ new Date()).toISOString();
1955
2645
  let firstTimestamp = true;
1956
2646
  try {
1957
- const content = fs5.readFileSync(transcriptPath, "utf-8");
2647
+ const content = fs6.readFileSync(transcriptPath, "utf-8");
1958
2648
  const lines = content.split("\n");
1959
2649
  for (const line of lines) {
1960
2650
  if (!line.trim()) continue;
@@ -2155,7 +2845,7 @@ async function runPreCompactHook() {
2155
2845
  messages: [],
2156
2846
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
2157
2847
  };
2158
- if (transcriptPath && fs5.existsSync(transcriptPath)) {
2848
+ if (transcriptPath && fs6.existsSync(transcriptPath)) {
2159
2849
  transcriptData = parseTranscript(transcriptPath);
2160
2850
  }
2161
2851
  let autoSaveStatus = "";
@@ -2211,17 +2901,17 @@ var post_compact_exports = {};
2211
2901
  __export(post_compact_exports, {
2212
2902
  runPostCompactHook: () => runPostCompactHook
2213
2903
  });
2214
- import * as fs6 from "node:fs";
2215
- import * as path6 from "node:path";
2904
+ import * as fs7 from "node:fs";
2905
+ import * as path7 from "node:path";
2216
2906
  import { homedir as homedir7 } from "node:os";
2217
2907
  function loadConfigFromMcpJson3(cwd) {
2218
- let searchDir = path6.resolve(cwd);
2908
+ let searchDir = path7.resolve(cwd);
2219
2909
  for (let i = 0; i < 5; i++) {
2220
2910
  if (!API_KEY4) {
2221
- const mcpPath = path6.join(searchDir, ".mcp.json");
2222
- if (fs6.existsSync(mcpPath)) {
2911
+ const mcpPath = path7.join(searchDir, ".mcp.json");
2912
+ if (fs7.existsSync(mcpPath)) {
2223
2913
  try {
2224
- const content = fs6.readFileSync(mcpPath, "utf-8");
2914
+ const content = fs7.readFileSync(mcpPath, "utf-8");
2225
2915
  const config = JSON.parse(content);
2226
2916
  const csEnv = config.mcpServers?.contextstream?.env;
2227
2917
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -2235,10 +2925,10 @@ function loadConfigFromMcpJson3(cwd) {
2235
2925
  }
2236
2926
  }
2237
2927
  if (!WORKSPACE_ID3) {
2238
- const csConfigPath = path6.join(searchDir, ".contextstream", "config.json");
2239
- if (fs6.existsSync(csConfigPath)) {
2928
+ const csConfigPath = path7.join(searchDir, ".contextstream", "config.json");
2929
+ if (fs7.existsSync(csConfigPath)) {
2240
2930
  try {
2241
- const content = fs6.readFileSync(csConfigPath, "utf-8");
2931
+ const content = fs7.readFileSync(csConfigPath, "utf-8");
2242
2932
  const csConfig = JSON.parse(content);
2243
2933
  if (csConfig.workspace_id) {
2244
2934
  WORKSPACE_ID3 = csConfig.workspace_id;
@@ -2247,15 +2937,15 @@ function loadConfigFromMcpJson3(cwd) {
2247
2937
  }
2248
2938
  }
2249
2939
  }
2250
- const parentDir = path6.dirname(searchDir);
2940
+ const parentDir = path7.dirname(searchDir);
2251
2941
  if (parentDir === searchDir) break;
2252
2942
  searchDir = parentDir;
2253
2943
  }
2254
2944
  if (!API_KEY4) {
2255
- const homeMcpPath = path6.join(homedir7(), ".mcp.json");
2256
- if (fs6.existsSync(homeMcpPath)) {
2945
+ const homeMcpPath = path7.join(homedir7(), ".mcp.json");
2946
+ if (fs7.existsSync(homeMcpPath)) {
2257
2947
  try {
2258
- const content = fs6.readFileSync(homeMcpPath, "utf-8");
2948
+ const content = fs7.readFileSync(homeMcpPath, "utf-8");
2259
2949
  const config = JSON.parse(content);
2260
2950
  const csEnv = config.mcpServers?.contextstream?.env;
2261
2951
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -2428,7 +3118,7 @@ ${options.workspaceId ? `# Workspace ID: ${options.workspaceId}` : ""}
2428
3118
  content: content.trim() + "\n"
2429
3119
  };
2430
3120
  }
2431
- var DEFAULT_CLAUDE_MCP_SERVER_NAME, RULES_VERSION, CONTEXTSTREAM_TOOL_NAMES, CONTEXTSTREAM_RULES_BOOTSTRAP, CONTEXTSTREAM_RULES_DYNAMIC, CONTEXTSTREAM_RULES_FULL, CONTEXTSTREAM_RULES_MINIMAL, NO_HOOKS_SUPPLEMENT, ANTIGRAVITY_SUPPLEMENT, NO_HOOKS_EDITORS, TEMPLATES;
3121
+ var DEFAULT_CLAUDE_MCP_SERVER_NAME, RULES_VERSION, CONTEXTSTREAM_TOOL_NAMES, FAST_DIRECT_READ_LANE, CANONICAL_HANDOFF_RULES, CONTEXTSTREAM_RULES_BOOTSTRAP, CONTEXTSTREAM_RULES_DYNAMIC, CONTEXTSTREAM_RULES_FULL, CONTEXTSTREAM_RULES_MINIMAL, NO_HOOKS_SUPPLEMENT, ANTIGRAVITY_SUPPLEMENT, NO_HOOKS_EDITORS, TEMPLATES;
2432
3122
  var init_rules_templates = __esm({
2433
3123
  "src/rules-templates.ts"() {
2434
3124
  "use strict";
@@ -2475,6 +3165,28 @@ var init_rules_templates = __esm({
2475
3165
  "help"
2476
3166
  // Actions: tools, auth, version, editor_rules, enable_bundle
2477
3167
  ];
3168
+ FAST_DIRECT_READ_LANE = `
3169
+ ## Fast Direct-Read Lane
3170
+
3171
+ After the required first-session \`init(...)\` + \`context(...)\`, these exact read-only calls may run without another grounding preamble only when no state-changing tool has run since the last grounding:
3172
+ - \`workspace(action="list"|"get")\`
3173
+ - \`memory(action="list_docs"|"list_events"|"list_todos"|"list_tasks"|"list_transcripts"|"list_nodes"|"list_diagrams")\`
3174
+ - \`help(action="version"|"tools"|"auth")\`
3175
+ - \`project(action="list"|"get"|"index_status")\`
3176
+ - \`reminder(action="list"|"active")\`
3177
+
3178
+ Do not use this lane for recall, decisions, searches, reading a specific record, media queries, or any create/update/delete/index operation; ground those with \`context(...)\` first.
3179
+ `.trim();
3180
+ CANONICAL_HANDOFF_RULES = `
3181
+ ## Canonical Agent Handoffs
3182
+
3183
+ When the user asks to create/prepare a handoff, hand work over, or continue in another agent/session, create the durable record immediately with:
3184
+ \`entity(kind="handoff", action="create", body={title, summary, scope, next_steps})\`
3185
+
3186
+ - Add \`to_user_id\` only when the recipient is known; never invent it.
3187
+ - If a portable bundle, capsule, or share link is requested, also create a session capsule and return both artifacts.
3188
+ - \`HANDOFF.md\`, a scratch prompt, a generic doc/event, or prose alone is not the canonical handoff. If a local file is explicitly requested, create the entity first and treat the file as an additional artifact.
3189
+ `.trim();
2478
3190
  CONTEXTSTREAM_RULES_BOOTSTRAP = `
2479
3191
  <contextstream_rules>
2480
3192
  | Message | Required |
@@ -2498,6 +3210,10 @@ var init_rules_templates = __esm({
2498
3210
  - "find media" / "search photos/videos/audio/docs" / "what's in this PDF/video/audio?" \u2192 \`media(action="search", query="...", content_types=["document"])\`
2499
3211
  - "index media" / "upload asset" / "read this photo/video/audio/PDF" \u2192 \`media(action="index", file_path="...", content_type="image")\` or \`media(action="index", external_url="...", content_type="document")\`
2500
3212
 
3213
+ ${FAST_DIRECT_READ_LANE}
3214
+
3215
+ ${CANONICAL_HANDOFF_RULES}
3216
+
2501
3217
  ## Skills, Docs & Lessons First
2502
3218
 
2503
3219
  Before guessing, improvising, or struggling through a workflow you do not fully know, check whether ContextStream already has guidance for it.
@@ -2527,12 +3243,12 @@ Before guessing, improvising, or struggling through a workflow you do not fully
2527
3243
 
2528
3244
  ## Past Sessions Are Queryable \u2014 USE THEM
2529
3245
 
2530
- Transcripts of every prior session are captured + indexed. Before asking the user what you did last time, or re-deriving context you built together previously, check the transcript + snapshot layer.
3246
+ When \`context()\` returns fresh, relevant \`[GROUNDING]\`, read those ranked prior-work hits first. If they are sufficient, stop there\u2014do not immediately call \`session(action="recall")\` for the same request. Escalate to recall only when grounding is absent, thin, stale, off-topic, or the user explicitly requests broader history.
2531
3247
 
2532
3248
  Triggers: user says "last time"/"previous"/"yesterday"/"pick up where we left off", the task is clearly a continuation, or you're about to ask a clarifying question whose answer is likely in a prior session.
2533
3249
 
2534
3250
  Exact calls:
2535
- - Ranked past-session context: \`session(action="recall", query="<what you're continuing>")\`
3251
+ - First explicit escalation after insufficient grounding: \`session(action="recall", query="<what you're continuing>")\`
2536
3252
  - Chronological list of recent sessions: \`memory(action="list_transcripts", limit=10)\`
2537
3253
  - Full-text search across ALL past transcripts: \`memory(action="search_transcripts", query="<keyword>")\`
2538
3254
  - Save a snapshot of the current session so the NEXT session can pick up: \`session(action="capture", event_type="session_snapshot", title="...", content="...")\`
@@ -2558,6 +3274,10 @@ v${RULES_VERSION}
2558
3274
 
2559
3275
  Use \`context()\` by default to get task-specific rules, lessons from past mistakes, and relevant decisions.
2560
3276
 
3277
+ ${FAST_DIRECT_READ_LANE}
3278
+
3279
+ ${CANONICAL_HANDOFF_RULES}
3280
+
2561
3281
  ---
2562
3282
 
2563
3283
  ## Why Default Context-First
@@ -3091,6 +3811,10 @@ See full documentation: https://contextstream.io/docs/mcp/tools
3091
3811
  **HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
3092
3812
  </contextstream_protocol>
3093
3813
 
3814
+ ${FAST_DIRECT_READ_LANE}
3815
+
3816
+ ${CANONICAL_HANDOFF_RULES}
3817
+
3094
3818
  Rules Version: ${RULES_VERSION}
3095
3819
 
3096
3820
  ---
@@ -3413,7 +4137,7 @@ curl -fsSL https://contextstream.io/scripts/setup.sh | bash
3413
4137
  irm https://contextstream.io/scripts/setup.ps1 | iex
3414
4138
  \`\`\`
3415
4139
 
3416
- **npm (requires Node.js 18+):**
4140
+ **npm (requires Node.js 20+):**
3417
4141
  \`\`\`bash
3418
4142
  npm install -g @contextstream/mcp-server@latest
3419
4143
  \`\`\`
@@ -3549,17 +4273,17 @@ var session_init_exports = {};
3549
4273
  __export(session_init_exports, {
3550
4274
  runSessionInitHook: () => runSessionInitHook
3551
4275
  });
3552
- import * as fs7 from "node:fs";
3553
- import * as path7 from "node:path";
4276
+ import * as fs8 from "node:fs";
4277
+ import * as path8 from "node:path";
3554
4278
  import { homedir as homedir8 } from "node:os";
3555
4279
  function loadConfigFromMcpJson4(cwd) {
3556
- let searchDir = path7.resolve(cwd);
4280
+ let searchDir = path8.resolve(cwd);
3557
4281
  for (let i = 0; i < 5; i++) {
3558
4282
  if (!API_KEY5) {
3559
- const mcpPath = path7.join(searchDir, ".mcp.json");
3560
- if (fs7.existsSync(mcpPath)) {
4283
+ const mcpPath = path8.join(searchDir, ".mcp.json");
4284
+ if (fs8.existsSync(mcpPath)) {
3561
4285
  try {
3562
- const content = fs7.readFileSync(mcpPath, "utf-8");
4286
+ const content = fs8.readFileSync(mcpPath, "utf-8");
3563
4287
  const config = JSON.parse(content);
3564
4288
  const csEnv = config.mcpServers?.contextstream?.env;
3565
4289
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -3576,10 +4300,10 @@ function loadConfigFromMcpJson4(cwd) {
3576
4300
  }
3577
4301
  }
3578
4302
  if (!WORKSPACE_ID4 || !PROJECT_ID2) {
3579
- const csConfigPath = path7.join(searchDir, ".contextstream", "config.json");
3580
- if (fs7.existsSync(csConfigPath)) {
4303
+ const csConfigPath = path8.join(searchDir, ".contextstream", "config.json");
4304
+ if (fs8.existsSync(csConfigPath)) {
3581
4305
  try {
3582
- const content = fs7.readFileSync(csConfigPath, "utf-8");
4306
+ const content = fs8.readFileSync(csConfigPath, "utf-8");
3583
4307
  const csConfig = JSON.parse(content);
3584
4308
  if (csConfig.workspace_id && !WORKSPACE_ID4) {
3585
4309
  WORKSPACE_ID4 = csConfig.workspace_id;
@@ -3591,15 +4315,15 @@ function loadConfigFromMcpJson4(cwd) {
3591
4315
  }
3592
4316
  }
3593
4317
  }
3594
- const parentDir = path7.dirname(searchDir);
4318
+ const parentDir = path8.dirname(searchDir);
3595
4319
  if (parentDir === searchDir) break;
3596
4320
  searchDir = parentDir;
3597
4321
  }
3598
4322
  if (!API_KEY5) {
3599
- const homeMcpPath = path7.join(homedir8(), ".mcp.json");
3600
- if (fs7.existsSync(homeMcpPath)) {
4323
+ const homeMcpPath = path8.join(homedir8(), ".mcp.json");
4324
+ if (fs8.existsSync(homeMcpPath)) {
3601
4325
  try {
3602
- const content = fs7.readFileSync(homeMcpPath, "utf-8");
4326
+ const content = fs8.readFileSync(homeMcpPath, "utf-8");
3603
4327
  const config = JSON.parse(content);
3604
4328
  const csEnv = config.mcpServers?.contextstream?.env;
3605
4329
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -3737,10 +4461,10 @@ function regenerateRuleFiles(folderPath) {
3737
4461
  for (const editor of editors) {
3738
4462
  const rule = generateRuleContent(editor, { mode: "bootstrap" });
3739
4463
  if (!rule) continue;
3740
- const filePath = path7.join(folderPath, rule.filename);
3741
- if (!fs7.existsSync(filePath)) continue;
4464
+ const filePath = path8.join(folderPath, rule.filename);
4465
+ if (!fs8.existsSync(filePath)) continue;
3742
4466
  try {
3743
- const existing = fs7.readFileSync(filePath, "utf8");
4467
+ const existing = fs8.readFileSync(filePath, "utf8");
3744
4468
  const startIdx = existing.indexOf(CONTEXTSTREAM_START_MARKER);
3745
4469
  const endIdx = existing.indexOf(CONTEXTSTREAM_END_MARKER);
3746
4470
  if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) continue;
@@ -3750,7 +4474,7 @@ function regenerateRuleFiles(folderPath) {
3750
4474
  ${rule.content.trim()}
3751
4475
  ${CONTEXTSTREAM_END_MARKER}`;
3752
4476
  const merged = [before, newBlock, after].filter((p) => p.length > 0).join("\n\n");
3753
- fs7.writeFileSync(filePath, merged.trim() + "\n", "utf8");
4477
+ fs8.writeFileSync(filePath, merged.trim() + "\n", "utf8");
3754
4478
  updated++;
3755
4479
  } catch {
3756
4480
  }
@@ -3823,12 +4547,12 @@ var init_session_init = __esm({
3823
4547
  });
3824
4548
 
3825
4549
  // src/hooks/common.ts
3826
- import * as fs8 from "node:fs";
3827
- import * as path8 from "node:path";
4550
+ import * as fs9 from "node:fs";
4551
+ import * as path9 from "node:path";
3828
4552
  import { homedir as homedir9 } from "node:os";
3829
4553
  function readHookInput() {
3830
4554
  try {
3831
- return JSON.parse(fs8.readFileSync(0, "utf8"));
4555
+ return JSON.parse(fs9.readFileSync(0, "utf8"));
3832
4556
  } catch {
3833
4557
  return {};
3834
4558
  }
@@ -3857,13 +4581,13 @@ function loadHookConfig(cwd) {
3857
4581
  let jwt = process.env.CONTEXTSTREAM_JWT || "";
3858
4582
  let workspaceId = process.env.CONTEXTSTREAM_WORKSPACE_ID || null;
3859
4583
  let projectId = process.env.CONTEXTSTREAM_PROJECT_ID || null;
3860
- let searchDir = path8.resolve(cwd);
4584
+ let searchDir = path9.resolve(cwd);
3861
4585
  for (let i = 0; i < 6; i++) {
3862
4586
  if (!apiKey && !jwt) {
3863
- const mcpPath = path8.join(searchDir, ".mcp.json");
3864
- if (fs8.existsSync(mcpPath)) {
4587
+ const mcpPath = path9.join(searchDir, ".mcp.json");
4588
+ if (fs9.existsSync(mcpPath)) {
3865
4589
  try {
3866
- const config = JSON.parse(fs8.readFileSync(mcpPath, "utf8"));
4590
+ const config = JSON.parse(fs9.readFileSync(mcpPath, "utf8"));
3867
4591
  const env = config.mcpServers?.contextstream?.env;
3868
4592
  if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
3869
4593
  if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
@@ -3875,25 +4599,25 @@ function loadHookConfig(cwd) {
3875
4599
  }
3876
4600
  }
3877
4601
  if (!workspaceId || !projectId) {
3878
- const localConfigPath = path8.join(searchDir, ".contextstream", "config.json");
3879
- if (fs8.existsSync(localConfigPath)) {
4602
+ const localConfigPath = path9.join(searchDir, ".contextstream", "config.json");
4603
+ if (fs9.existsSync(localConfigPath)) {
3880
4604
  try {
3881
- const localConfig = JSON.parse(fs8.readFileSync(localConfigPath, "utf8"));
4605
+ const localConfig = JSON.parse(fs9.readFileSync(localConfigPath, "utf8"));
3882
4606
  if (localConfig.workspace_id && !workspaceId) workspaceId = localConfig.workspace_id;
3883
4607
  if (localConfig.project_id && !projectId) projectId = localConfig.project_id;
3884
4608
  } catch {
3885
4609
  }
3886
4610
  }
3887
4611
  }
3888
- const parentDir = path8.dirname(searchDir);
4612
+ const parentDir = path9.dirname(searchDir);
3889
4613
  if (parentDir === searchDir) break;
3890
4614
  searchDir = parentDir;
3891
4615
  }
3892
4616
  if (!apiKey && !jwt) {
3893
- const homeMcpPath = path8.join(homedir9(), ".mcp.json");
3894
- if (fs8.existsSync(homeMcpPath)) {
4617
+ const homeMcpPath = path9.join(homedir9(), ".mcp.json");
4618
+ if (fs9.existsSync(homeMcpPath)) {
3895
4619
  try {
3896
- const config = JSON.parse(fs8.readFileSync(homeMcpPath, "utf8"));
4620
+ const config = JSON.parse(fs9.readFileSync(homeMcpPath, "utf8"));
3897
4621
  const env = config.mcpServers?.contextstream?.env;
3898
4622
  if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
3899
4623
  if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
@@ -4098,17 +4822,17 @@ var session_end_exports = {};
4098
4822
  __export(session_end_exports, {
4099
4823
  runSessionEndHook: () => runSessionEndHook
4100
4824
  });
4101
- import * as fs9 from "node:fs";
4102
- import * as path9 from "node:path";
4825
+ import * as fs10 from "node:fs";
4826
+ import * as path10 from "node:path";
4103
4827
  import { homedir as homedir10 } from "node:os";
4104
4828
  function loadConfigFromMcpJson5(cwd) {
4105
- let searchDir = path9.resolve(cwd);
4829
+ let searchDir = path10.resolve(cwd);
4106
4830
  for (let i = 0; i < 5; i++) {
4107
4831
  if (!API_KEY6) {
4108
- const mcpPath = path9.join(searchDir, ".mcp.json");
4109
- if (fs9.existsSync(mcpPath)) {
4832
+ const mcpPath = path10.join(searchDir, ".mcp.json");
4833
+ if (fs10.existsSync(mcpPath)) {
4110
4834
  try {
4111
- const content = fs9.readFileSync(mcpPath, "utf-8");
4835
+ const content = fs10.readFileSync(mcpPath, "utf-8");
4112
4836
  const config = JSON.parse(content);
4113
4837
  const csEnv = config.mcpServers?.contextstream?.env;
4114
4838
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -4122,10 +4846,10 @@ function loadConfigFromMcpJson5(cwd) {
4122
4846
  }
4123
4847
  }
4124
4848
  if (!WORKSPACE_ID5 || !PROJECT_ID3) {
4125
- const csConfigPath = path9.join(searchDir, ".contextstream", "config.json");
4126
- if (fs9.existsSync(csConfigPath)) {
4849
+ const csConfigPath = path10.join(searchDir, ".contextstream", "config.json");
4850
+ if (fs10.existsSync(csConfigPath)) {
4127
4851
  try {
4128
- const content = fs9.readFileSync(csConfigPath, "utf-8");
4852
+ const content = fs10.readFileSync(csConfigPath, "utf-8");
4129
4853
  const csConfig = JSON.parse(content);
4130
4854
  if (csConfig.workspace_id && !WORKSPACE_ID5) {
4131
4855
  WORKSPACE_ID5 = csConfig.workspace_id;
@@ -4137,15 +4861,15 @@ function loadConfigFromMcpJson5(cwd) {
4137
4861
  }
4138
4862
  }
4139
4863
  }
4140
- const parentDir = path9.dirname(searchDir);
4864
+ const parentDir = path10.dirname(searchDir);
4141
4865
  if (parentDir === searchDir) break;
4142
4866
  searchDir = parentDir;
4143
4867
  }
4144
4868
  if (!API_KEY6) {
4145
- const homeMcpPath = path9.join(homedir10(), ".mcp.json");
4146
- if (fs9.existsSync(homeMcpPath)) {
4869
+ const homeMcpPath = path10.join(homedir10(), ".mcp.json");
4870
+ if (fs10.existsSync(homeMcpPath)) {
4147
4871
  try {
4148
- const content = fs9.readFileSync(homeMcpPath, "utf-8");
4872
+ const content = fs10.readFileSync(homeMcpPath, "utf-8");
4149
4873
  const config = JSON.parse(content);
4150
4874
  const csEnv = config.mcpServers?.contextstream?.env;
4151
4875
  if (csEnv?.CONTEXTSTREAM_API_KEY) {
@@ -4168,11 +4892,11 @@ function parseTranscriptStats(transcriptPath) {
4168
4892
  messages: [],
4169
4893
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
4170
4894
  };
4171
- if (!transcriptPath || !fs9.existsSync(transcriptPath)) {
4895
+ if (!transcriptPath || !fs10.existsSync(transcriptPath)) {
4172
4896
  return stats;
4173
4897
  }
4174
4898
  try {
4175
- const content = fs9.readFileSync(transcriptPath, "utf-8");
4899
+ const content = fs10.readFileSync(transcriptPath, "utf-8");
4176
4900
  const lines = content.split("\n");
4177
4901
  let firstTimestamp = null;
4178
4902
  let lastTimestamp = null;
@@ -4473,8 +5197,8 @@ var post_tool_use_failure_exports = {};
4473
5197
  __export(post_tool_use_failure_exports, {
4474
5198
  runPostToolUseFailureHook: () => runPostToolUseFailureHook
4475
5199
  });
4476
- import * as fs10 from "node:fs";
4477
- import * as path10 from "node:path";
5200
+ import * as fs11 from "node:fs";
5201
+ import * as path11 from "node:path";
4478
5202
  import { homedir as homedir11 } from "node:os";
4479
5203
  function extractErrorText(input) {
4480
5204
  return typeof input.error === "string" && input.error || typeof input.tool_error === "string" && input.tool_error || typeof input.stderr === "string" && input.stderr || "Tool execution failed";
@@ -4486,13 +5210,13 @@ function failureFingerprint(toolName, errorText) {
4486
5210
  function incrementFailureCounter(fingerprint) {
4487
5211
  let counters = {};
4488
5212
  try {
4489
- counters = JSON.parse(fs10.readFileSync(FAILURE_COUNTERS_FILE, "utf8"));
5213
+ counters = JSON.parse(fs11.readFileSync(FAILURE_COUNTERS_FILE, "utf8"));
4490
5214
  } catch {
4491
5215
  counters = {};
4492
5216
  }
4493
5217
  counters[fingerprint] = (counters[fingerprint] || 0) + 1;
4494
- fs10.mkdirSync(path10.dirname(FAILURE_COUNTERS_FILE), { recursive: true });
4495
- fs10.writeFileSync(FAILURE_COUNTERS_FILE, JSON.stringify(counters, null, 2), "utf8");
5218
+ fs11.mkdirSync(path11.dirname(FAILURE_COUNTERS_FILE), { recursive: true });
5219
+ fs11.writeFileSync(FAILURE_COUNTERS_FILE, JSON.stringify(counters, null, 2), "utf8");
4496
5220
  return counters[fingerprint];
4497
5221
  }
4498
5222
  async function runPostToolUseFailureHook() {
@@ -4545,7 +5269,7 @@ var init_post_tool_use_failure = __esm({
4545
5269
  "src/hooks/post-tool-use-failure.ts"() {
4546
5270
  "use strict";
4547
5271
  init_common();
4548
- FAILURE_COUNTERS_FILE = path10.join(homedir11(), ".contextstream", "hook-failure-counts.json");
5272
+ FAILURE_COUNTERS_FILE = path11.join(homedir11(), ".contextstream", "hook-failure-counts.json");
4549
5273
  isDirectRun11 = process.argv[1]?.includes("post-tool-use-failure") || process.argv[2] === "post-tool-use-failure";
4550
5274
  if (isDirectRun11) {
4551
5275
  runPostToolUseFailureHook().catch(() => process.exit(0));
@@ -4627,10 +5351,10 @@ var subagent_stop_exports = {};
4627
5351
  __export(subagent_stop_exports, {
4628
5352
  runSubagentStopHook: () => runSubagentStopHook
4629
5353
  });
4630
- import * as fs11 from "node:fs";
5354
+ import * as fs12 from "node:fs";
4631
5355
  function parseTranscript2(transcriptPath) {
4632
5356
  try {
4633
- const content = fs11.readFileSync(transcriptPath, "utf8");
5357
+ const content = fs12.readFileSync(transcriptPath, "utf8");
4634
5358
  const assistantMessages = [];
4635
5359
  let toolCallCount = 0;
4636
5360
  for (const rawLine of content.split("\n")) {
@@ -4692,7 +5416,7 @@ async function runSubagentStopHook() {
4692
5416
  const agentType = typeof input.agent_type === "string" && input.agent_type || typeof input.subagent_type === "string" && input.subagent_type || "unknown";
4693
5417
  const agentId = typeof input.agent_id === "string" && input.agent_id || "unknown";
4694
5418
  const transcriptPath = typeof input.agent_transcript_path === "string" && input.agent_transcript_path || typeof input.transcript_path === "string" && input.transcript_path || "";
4695
- const transcript = transcriptPath && fs11.existsSync(transcriptPath) ? parseTranscript2(transcriptPath) : { assistantMessages: [], toolCallCount: 0 };
5419
+ const transcript = transcriptPath && fs12.existsSync(transcriptPath) ? parseTranscript2(transcriptPath) : { assistantMessages: [], toolCallCount: 0 };
4696
5420
  const summaryText = transcript.assistantMessages.join("\n\n") || typeof input.summary === "string" && input.summary || "(No assistant output found in subagent transcript.)";
4697
5421
  if (agentType.toLowerCase() === "plan") {
4698
5422
  const suppliedPlanId = typeof input.plan_id === "string" && input.plan_id || typeof input.planId === "string" && input.planId || null;