@contextstream/mcp-server 0.4.81 → 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.
- package/README.md +40 -15
- package/dist/hooks/auto-rules.js +162 -17
- package/dist/hooks/post-write.js +703 -28
- package/dist/hooks/runner.js +871 -147
- package/dist/hooks/session-init.js +37 -3
- package/dist/hooks/user-prompt-submit.js +11 -4
- package/dist/index.js +1038 -428
- package/package.json +6 -5
package/dist/hooks/runner.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
606
|
-
import * as
|
|
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 =
|
|
1288
|
+
let currentDir = path4.resolve(startDir);
|
|
633
1289
|
for (let i = 0; i < 10; i++) {
|
|
634
|
-
const configPath =
|
|
635
|
-
if (
|
|
1290
|
+
const configPath = path4.join(currentDir, ".contextstream", "config.json");
|
|
1291
|
+
if (fs4.existsSync(configPath)) {
|
|
636
1292
|
try {
|
|
637
|
-
const content =
|
|
1293
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
638
1294
|
return JSON.parse(content);
|
|
639
1295
|
} catch {
|
|
640
1296
|
}
|
|
641
1297
|
}
|
|
642
|
-
const parentDir =
|
|
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 =
|
|
1310
|
+
let currentDir = path4.resolve(startDir);
|
|
655
1311
|
for (let i = 0; i < 10; i++) {
|
|
656
|
-
const mcpPath =
|
|
657
|
-
if (
|
|
1312
|
+
const mcpPath = path4.join(currentDir, ".mcp.json");
|
|
1313
|
+
if (fs4.existsSync(mcpPath)) {
|
|
658
1314
|
try {
|
|
659
|
-
const content =
|
|
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 =
|
|
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 =
|
|
678
|
-
if (
|
|
1333
|
+
const homeMcpPath = path4.join(homedir3(), ".mcp.json");
|
|
1334
|
+
if (fs4.existsSync(homeMcpPath)) {
|
|
679
1335
|
try {
|
|
680
|
-
const content =
|
|
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 =
|
|
1352
|
+
const ext = path4.extname(filePath).toLowerCase();
|
|
697
1353
|
if (!INDEXABLE_EXTENSIONS.has(ext)) {
|
|
698
|
-
const basename2 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
784
|
-
const relativePath =
|
|
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 =
|
|
1495
|
+
let currentDir = path4.dirname(path4.resolve(filePath));
|
|
817
1496
|
for (let i = 0; i < 10; i++) {
|
|
818
|
-
const configPath =
|
|
819
|
-
if (
|
|
1497
|
+
const configPath = path4.join(currentDir, ".contextstream", "config.json");
|
|
1498
|
+
if (fs4.existsSync(configPath)) {
|
|
820
1499
|
return currentDir;
|
|
821
1500
|
}
|
|
822
|
-
const parentDir =
|
|
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 =
|
|
851
|
-
if (!
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
1799
|
+
const configPath = join5(homedir4(), ".contextstream", "config.json");
|
|
1117
1800
|
if (existsSync3(configPath)) {
|
|
1118
|
-
const config = JSON.parse(
|
|
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 =
|
|
1222
|
-
const configDir =
|
|
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 =
|
|
1919
|
+
const markerPath = join5(homedir4(), ".contextstream", "update-pending.json");
|
|
1237
1920
|
if (!existsSync3(markerPath)) return null;
|
|
1238
|
-
const marker = JSON.parse(
|
|
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 =
|
|
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
|
|
1286
|
-
import * as
|
|
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 =
|
|
1972
|
+
let searchDir = path5.resolve(cwd);
|
|
1290
1973
|
for (let i = 0; i < 5; i++) {
|
|
1291
1974
|
if (!API_KEY2) {
|
|
1292
|
-
const mcpPath =
|
|
1293
|
-
if (
|
|
1975
|
+
const mcpPath = path5.join(searchDir, ".mcp.json");
|
|
1976
|
+
if (fs5.existsSync(mcpPath)) {
|
|
1294
1977
|
try {
|
|
1295
|
-
const content =
|
|
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 =
|
|
1313
|
-
if (
|
|
1995
|
+
const csConfigPath = path5.join(searchDir, ".contextstream", "config.json");
|
|
1996
|
+
if (fs5.existsSync(csConfigPath)) {
|
|
1314
1997
|
try {
|
|
1315
|
-
const content =
|
|
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 =
|
|
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 =
|
|
1333
|
-
if (
|
|
2015
|
+
const homeMcpPath = path5.join(homedir5(), ".mcp.json");
|
|
2016
|
+
if (fs5.existsSync(homeMcpPath)) {
|
|
1334
2017
|
try {
|
|
1335
|
-
const content =
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
1892
|
-
import * as
|
|
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 =
|
|
2585
|
+
let searchDir = path6.resolve(cwd);
|
|
1896
2586
|
for (let i = 0; i < 5; i++) {
|
|
1897
2587
|
if (!API_KEY3) {
|
|
1898
|
-
const mcpPath =
|
|
1899
|
-
if (
|
|
2588
|
+
const mcpPath = path6.join(searchDir, ".mcp.json");
|
|
2589
|
+
if (fs6.existsSync(mcpPath)) {
|
|
1900
2590
|
try {
|
|
1901
|
-
const content =
|
|
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 =
|
|
1916
|
-
if (
|
|
2605
|
+
const csConfigPath = path6.join(searchDir, ".contextstream", "config.json");
|
|
2606
|
+
if (fs6.existsSync(csConfigPath)) {
|
|
1917
2607
|
try {
|
|
1918
|
-
const content =
|
|
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 =
|
|
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 =
|
|
1933
|
-
if (
|
|
2622
|
+
const homeMcpPath = path6.join(homedir6(), ".mcp.json");
|
|
2623
|
+
if (fs6.existsSync(homeMcpPath)) {
|
|
1934
2624
|
try {
|
|
1935
|
-
const content =
|
|
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 =
|
|
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 &&
|
|
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
|
|
2215
|
-
import * as
|
|
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 =
|
|
2908
|
+
let searchDir = path7.resolve(cwd);
|
|
2219
2909
|
for (let i = 0; i < 5; i++) {
|
|
2220
2910
|
if (!API_KEY4) {
|
|
2221
|
-
const mcpPath =
|
|
2222
|
-
if (
|
|
2911
|
+
const mcpPath = path7.join(searchDir, ".mcp.json");
|
|
2912
|
+
if (fs7.existsSync(mcpPath)) {
|
|
2223
2913
|
try {
|
|
2224
|
-
const content =
|
|
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 =
|
|
2239
|
-
if (
|
|
2928
|
+
const csConfigPath = path7.join(searchDir, ".contextstream", "config.json");
|
|
2929
|
+
if (fs7.existsSync(csConfigPath)) {
|
|
2240
2930
|
try {
|
|
2241
|
-
const content =
|
|
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 =
|
|
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 =
|
|
2256
|
-
if (
|
|
2945
|
+
const homeMcpPath = path7.join(homedir7(), ".mcp.json");
|
|
2946
|
+
if (fs7.existsSync(homeMcpPath)) {
|
|
2257
2947
|
try {
|
|
2258
|
-
const content =
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
|
3553
|
-
import * as
|
|
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 =
|
|
4280
|
+
let searchDir = path8.resolve(cwd);
|
|
3557
4281
|
for (let i = 0; i < 5; i++) {
|
|
3558
4282
|
if (!API_KEY5) {
|
|
3559
|
-
const mcpPath =
|
|
3560
|
-
if (
|
|
4283
|
+
const mcpPath = path8.join(searchDir, ".mcp.json");
|
|
4284
|
+
if (fs8.existsSync(mcpPath)) {
|
|
3561
4285
|
try {
|
|
3562
|
-
const content =
|
|
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 =
|
|
3580
|
-
if (
|
|
4303
|
+
const csConfigPath = path8.join(searchDir, ".contextstream", "config.json");
|
|
4304
|
+
if (fs8.existsSync(csConfigPath)) {
|
|
3581
4305
|
try {
|
|
3582
|
-
const content =
|
|
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 =
|
|
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 =
|
|
3600
|
-
if (
|
|
4323
|
+
const homeMcpPath = path8.join(homedir8(), ".mcp.json");
|
|
4324
|
+
if (fs8.existsSync(homeMcpPath)) {
|
|
3601
4325
|
try {
|
|
3602
|
-
const content =
|
|
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 =
|
|
3741
|
-
if (!
|
|
4464
|
+
const filePath = path8.join(folderPath, rule.filename);
|
|
4465
|
+
if (!fs8.existsSync(filePath)) continue;
|
|
3742
4466
|
try {
|
|
3743
|
-
const existing =
|
|
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
|
-
|
|
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
|
|
3827
|
-
import * as
|
|
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(
|
|
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 =
|
|
4584
|
+
let searchDir = path9.resolve(cwd);
|
|
3861
4585
|
for (let i = 0; i < 6; i++) {
|
|
3862
4586
|
if (!apiKey && !jwt) {
|
|
3863
|
-
const mcpPath =
|
|
3864
|
-
if (
|
|
4587
|
+
const mcpPath = path9.join(searchDir, ".mcp.json");
|
|
4588
|
+
if (fs9.existsSync(mcpPath)) {
|
|
3865
4589
|
try {
|
|
3866
|
-
const config = JSON.parse(
|
|
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 =
|
|
3879
|
-
if (
|
|
4602
|
+
const localConfigPath = path9.join(searchDir, ".contextstream", "config.json");
|
|
4603
|
+
if (fs9.existsSync(localConfigPath)) {
|
|
3880
4604
|
try {
|
|
3881
|
-
const localConfig = JSON.parse(
|
|
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 =
|
|
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 =
|
|
3894
|
-
if (
|
|
4617
|
+
const homeMcpPath = path9.join(homedir9(), ".mcp.json");
|
|
4618
|
+
if (fs9.existsSync(homeMcpPath)) {
|
|
3895
4619
|
try {
|
|
3896
|
-
const config = JSON.parse(
|
|
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
|
|
4102
|
-
import * as
|
|
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 =
|
|
4829
|
+
let searchDir = path10.resolve(cwd);
|
|
4106
4830
|
for (let i = 0; i < 5; i++) {
|
|
4107
4831
|
if (!API_KEY6) {
|
|
4108
|
-
const mcpPath =
|
|
4109
|
-
if (
|
|
4832
|
+
const mcpPath = path10.join(searchDir, ".mcp.json");
|
|
4833
|
+
if (fs10.existsSync(mcpPath)) {
|
|
4110
4834
|
try {
|
|
4111
|
-
const content =
|
|
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 =
|
|
4126
|
-
if (
|
|
4849
|
+
const csConfigPath = path10.join(searchDir, ".contextstream", "config.json");
|
|
4850
|
+
if (fs10.existsSync(csConfigPath)) {
|
|
4127
4851
|
try {
|
|
4128
|
-
const content =
|
|
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 =
|
|
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 =
|
|
4146
|
-
if (
|
|
4869
|
+
const homeMcpPath = path10.join(homedir10(), ".mcp.json");
|
|
4870
|
+
if (fs10.existsSync(homeMcpPath)) {
|
|
4147
4871
|
try {
|
|
4148
|
-
const content =
|
|
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 || !
|
|
4895
|
+
if (!transcriptPath || !fs10.existsSync(transcriptPath)) {
|
|
4172
4896
|
return stats;
|
|
4173
4897
|
}
|
|
4174
4898
|
try {
|
|
4175
|
-
const content =
|
|
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
|
|
4477
|
-
import * as
|
|
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(
|
|
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
|
-
|
|
4495
|
-
|
|
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 =
|
|
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
|
|
5354
|
+
import * as fs12 from "node:fs";
|
|
4631
5355
|
function parseTranscript2(transcriptPath) {
|
|
4632
5356
|
try {
|
|
4633
|
-
const content =
|
|
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 &&
|
|
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;
|