@contextstream/mcp-server 0.4.60 → 0.4.61

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,13 +1,1157 @@
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 __esm = (fn, res) => function __init() {
5
9
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
10
  };
11
+ var __commonJS = (cb, mod) => function __require() {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ };
7
14
  var __export = (target, all) => {
8
15
  for (var name in all)
9
16
  __defProp(target, name, { get: all[name], enumerable: true });
10
17
  };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
27
+ // If the importer is in node compatibility mode or this is not an ESM
28
+ // file that has been converted to a CommonJS file using a Babel-
29
+ // compatible transform (i.e. "__esModule" has not been set), then set
30
+ // "default" to the CommonJS "module.exports" for node compatibility.
31
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
32
+ mod
33
+ ));
34
+
35
+ // node_modules/ignore/index.js
36
+ var require_ignore = __commonJS({
37
+ "node_modules/ignore/index.js"(exports, module) {
38
+ function makeArray(subject) {
39
+ return Array.isArray(subject) ? subject : [subject];
40
+ }
41
+ var UNDEFINED = void 0;
42
+ var EMPTY = "";
43
+ var SPACE = " ";
44
+ var ESCAPE = "\\";
45
+ var REGEX_TEST_BLANK_LINE = /^\s+$/;
46
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
47
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
48
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
49
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
50
+ var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
51
+ var REGEX_TEST_TRAILING_SLASH = /\/$/;
52
+ var SLASH = "/";
53
+ var TMP_KEY_IGNORE = "node-ignore";
54
+ if (typeof Symbol !== "undefined") {
55
+ TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
56
+ }
57
+ var KEY_IGNORE = TMP_KEY_IGNORE;
58
+ var define = (object, key, value) => {
59
+ Object.defineProperty(object, key, { value });
60
+ return value;
61
+ };
62
+ var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
63
+ var RETURN_FALSE = () => false;
64
+ var sanitizeRange = (range) => range.replace(
65
+ REGEX_REGEXP_RANGE,
66
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
67
+ );
68
+ var cleanRangeBackSlash = (slashes) => {
69
+ const { length } = slashes;
70
+ return slashes.slice(0, length - length % 2);
71
+ };
72
+ var REPLACERS = [
73
+ [
74
+ // Remove BOM
75
+ // TODO:
76
+ // Other similar zero-width characters?
77
+ /^\uFEFF/,
78
+ () => EMPTY
79
+ ],
80
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
81
+ [
82
+ // (a\ ) -> (a )
83
+ // (a ) -> (a)
84
+ // (a ) -> (a)
85
+ // (a \ ) -> (a )
86
+ /((?:\\\\)*?)(\\?\s+)$/,
87
+ (_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
88
+ ],
89
+ // Replace (\ ) with ' '
90
+ // (\ ) -> ' '
91
+ // (\\ ) -> '\\ '
92
+ // (\\\ ) -> '\\ '
93
+ [
94
+ /(\\+?)\s/g,
95
+ (_, m1) => {
96
+ const { length } = m1;
97
+ return m1.slice(0, length - length % 2) + SPACE;
98
+ }
99
+ ],
100
+ // Escape metacharacters
101
+ // which is written down by users but means special for regular expressions.
102
+ // > There are 12 characters with special meanings:
103
+ // > - the backslash \,
104
+ // > - the caret ^,
105
+ // > - the dollar sign $,
106
+ // > - the period or dot .,
107
+ // > - the vertical bar or pipe symbol |,
108
+ // > - the question mark ?,
109
+ // > - the asterisk or star *,
110
+ // > - the plus sign +,
111
+ // > - the opening parenthesis (,
112
+ // > - the closing parenthesis ),
113
+ // > - and the opening square bracket [,
114
+ // > - the opening curly brace {,
115
+ // > These special characters are often called "metacharacters".
116
+ [
117
+ /[\\$.|*+(){^]/g,
118
+ (match) => `\\${match}`
119
+ ],
120
+ [
121
+ // > a question mark (?) matches a single character
122
+ /(?!\\)\?/g,
123
+ () => "[^/]"
124
+ ],
125
+ // leading slash
126
+ [
127
+ // > A leading slash matches the beginning of the pathname.
128
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
129
+ // A leading slash matches the beginning of the pathname
130
+ /^\//,
131
+ () => "^"
132
+ ],
133
+ // replace special metacharacter slash after the leading slash
134
+ [
135
+ /\//g,
136
+ () => "\\/"
137
+ ],
138
+ [
139
+ // > A leading "**" followed by a slash means match in all directories.
140
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
141
+ // > the same as pattern "foo".
142
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
143
+ // > under directory "foo".
144
+ // Notice that the '*'s have been replaced as '\\*'
145
+ /^\^*\\\*\\\*\\\//,
146
+ // '**/foo' <-> 'foo'
147
+ () => "^(?:.*\\/)?"
148
+ ],
149
+ // starting
150
+ [
151
+ // there will be no leading '/'
152
+ // (which has been replaced by section "leading slash")
153
+ // If starts with '**', adding a '^' to the regular expression also works
154
+ /^(?=[^^])/,
155
+ function startingReplacer() {
156
+ return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
157
+ }
158
+ ],
159
+ // two globstars
160
+ [
161
+ // Use lookahead assertions so that we could match more than one `'/**'`
162
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
163
+ // Zero, one or several directories
164
+ // should not use '*', or it will be replaced by the next replacer
165
+ // Check if it is not the last `'/**'`
166
+ (_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
167
+ ],
168
+ // normal intermediate wildcards
169
+ [
170
+ // Never replace escaped '*'
171
+ // ignore rule '\*' will match the path '*'
172
+ // 'abc.*/' -> go
173
+ // 'abc.*' -> skip this rule,
174
+ // coz trailing single wildcard will be handed by [trailing wildcard]
175
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
176
+ // '*.js' matches '.js'
177
+ // '*.js' doesn't match 'abc'
178
+ (_, p1, p2) => {
179
+ const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
180
+ return p1 + unescaped;
181
+ }
182
+ ],
183
+ [
184
+ // unescape, revert step 3 except for back slash
185
+ // For example, if a user escape a '\\*',
186
+ // after step 3, the result will be '\\\\\\*'
187
+ /\\\\\\(?=[$.|*+(){^])/g,
188
+ () => ESCAPE
189
+ ],
190
+ [
191
+ // '\\\\' -> '\\'
192
+ /\\\\/g,
193
+ () => ESCAPE
194
+ ],
195
+ [
196
+ // > The range notation, e.g. [a-zA-Z],
197
+ // > can be used to match one of the characters in a range.
198
+ // `\` is escaped by step 3
199
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
200
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
201
+ ],
202
+ // ending
203
+ [
204
+ // 'js' will not match 'js.'
205
+ // 'ab' will not match 'abc'
206
+ /(?:[^*])$/,
207
+ // WTF!
208
+ // https://git-scm.com/docs/gitignore
209
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
210
+ // which re-fixes #24, #38
211
+ // > If there is a separator at the end of the pattern then the pattern
212
+ // > will only match directories, otherwise the pattern can match both
213
+ // > files and directories.
214
+ // 'js*' will not match 'a.js'
215
+ // 'js/' will not match 'a.js'
216
+ // 'js' will match 'a.js' and 'a.js/'
217
+ (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
218
+ ]
219
+ ];
220
+ var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
221
+ var MODE_IGNORE = "regex";
222
+ var MODE_CHECK_IGNORE = "checkRegex";
223
+ var UNDERSCORE = "_";
224
+ var TRAILING_WILD_CARD_REPLACERS = {
225
+ [MODE_IGNORE](_, p1) {
226
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
227
+ return `${prefix}(?=$|\\/$)`;
228
+ },
229
+ [MODE_CHECK_IGNORE](_, p1) {
230
+ const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
231
+ return `${prefix}(?=$|\\/$)`;
232
+ }
233
+ };
234
+ var makeRegexPrefix = (pattern) => REPLACERS.reduce(
235
+ (prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
236
+ pattern
237
+ );
238
+ var isString = (subject) => typeof subject === "string";
239
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
240
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
241
+ var IgnoreRule = class {
242
+ constructor(pattern, mark, body, ignoreCase, negative, prefix) {
243
+ this.pattern = pattern;
244
+ this.mark = mark;
245
+ this.negative = negative;
246
+ define(this, "body", body);
247
+ define(this, "ignoreCase", ignoreCase);
248
+ define(this, "regexPrefix", prefix);
249
+ }
250
+ get regex() {
251
+ const key = UNDERSCORE + MODE_IGNORE;
252
+ if (this[key]) {
253
+ return this[key];
254
+ }
255
+ return this._make(MODE_IGNORE, key);
256
+ }
257
+ get checkRegex() {
258
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
259
+ if (this[key]) {
260
+ return this[key];
261
+ }
262
+ return this._make(MODE_CHECK_IGNORE, key);
263
+ }
264
+ _make(mode, key) {
265
+ const str = this.regexPrefix.replace(
266
+ REGEX_REPLACE_TRAILING_WILDCARD,
267
+ // It does not need to bind pattern
268
+ TRAILING_WILD_CARD_REPLACERS[mode]
269
+ );
270
+ const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
271
+ return define(this, key, regex);
272
+ }
273
+ };
274
+ var createRule = ({
275
+ pattern,
276
+ mark
277
+ }, ignoreCase) => {
278
+ let negative = false;
279
+ let body = pattern;
280
+ if (body.indexOf("!") === 0) {
281
+ negative = true;
282
+ body = body.substr(1);
283
+ }
284
+ body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
285
+ const regexPrefix = makeRegexPrefix(body);
286
+ return new IgnoreRule(
287
+ pattern,
288
+ mark,
289
+ body,
290
+ ignoreCase,
291
+ negative,
292
+ regexPrefix
293
+ );
294
+ };
295
+ var RuleManager = class {
296
+ constructor(ignoreCase) {
297
+ this._ignoreCase = ignoreCase;
298
+ this._rules = [];
299
+ }
300
+ _add(pattern) {
301
+ if (pattern && pattern[KEY_IGNORE]) {
302
+ this._rules = this._rules.concat(pattern._rules._rules);
303
+ this._added = true;
304
+ return;
305
+ }
306
+ if (isString(pattern)) {
307
+ pattern = {
308
+ pattern
309
+ };
310
+ }
311
+ if (checkPattern(pattern.pattern)) {
312
+ const rule = createRule(pattern, this._ignoreCase);
313
+ this._added = true;
314
+ this._rules.push(rule);
315
+ }
316
+ }
317
+ // @param {Array<string> | string | Ignore} pattern
318
+ add(pattern) {
319
+ this._added = false;
320
+ makeArray(
321
+ isString(pattern) ? splitPattern(pattern) : pattern
322
+ ).forEach(this._add, this);
323
+ return this._added;
324
+ }
325
+ // Test one single path without recursively checking parent directories
326
+ //
327
+ // - checkUnignored `boolean` whether should check if the path is unignored,
328
+ // setting `checkUnignored` to `false` could reduce additional
329
+ // path matching.
330
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
331
+ // @returns {TestResult} true if a file is ignored
332
+ test(path5, checkUnignored, mode) {
333
+ let ignored = false;
334
+ let unignored = false;
335
+ let matchedRule;
336
+ this._rules.forEach((rule) => {
337
+ const { negative } = rule;
338
+ if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
339
+ return;
340
+ }
341
+ const matched = rule[mode].test(path5);
342
+ if (!matched) {
343
+ return;
344
+ }
345
+ ignored = !negative;
346
+ unignored = negative;
347
+ matchedRule = negative ? UNDEFINED : rule;
348
+ });
349
+ const ret = {
350
+ ignored,
351
+ unignored
352
+ };
353
+ if (matchedRule) {
354
+ ret.rule = matchedRule;
355
+ }
356
+ return ret;
357
+ }
358
+ };
359
+ var throwError = (message, Ctor) => {
360
+ throw new Ctor(message);
361
+ };
362
+ var checkPath = (path5, originalPath, doThrow) => {
363
+ if (!isString(path5)) {
364
+ return doThrow(
365
+ `path must be a string, but got \`${originalPath}\``,
366
+ TypeError
367
+ );
368
+ }
369
+ if (!path5) {
370
+ return doThrow(`path must not be empty`, TypeError);
371
+ }
372
+ if (checkPath.isNotRelative(path5)) {
373
+ const r = "`path.relative()`d";
374
+ return doThrow(
375
+ `path should be a ${r} string, but got "${originalPath}"`,
376
+ RangeError
377
+ );
378
+ }
379
+ return true;
380
+ };
381
+ var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5);
382
+ checkPath.isNotRelative = isNotRelative;
383
+ checkPath.convert = (p) => p;
384
+ var Ignore2 = class {
385
+ constructor({
386
+ ignorecase = true,
387
+ ignoreCase = ignorecase,
388
+ allowRelativePaths = false
389
+ } = {}) {
390
+ define(this, KEY_IGNORE, true);
391
+ this._rules = new RuleManager(ignoreCase);
392
+ this._strictPathCheck = !allowRelativePaths;
393
+ this._initCache();
394
+ }
395
+ _initCache() {
396
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
397
+ this._testCache = /* @__PURE__ */ Object.create(null);
398
+ }
399
+ add(pattern) {
400
+ if (this._rules.add(pattern)) {
401
+ this._initCache();
402
+ }
403
+ return this;
404
+ }
405
+ // legacy
406
+ addPattern(pattern) {
407
+ return this.add(pattern);
408
+ }
409
+ // @returns {TestResult}
410
+ _test(originalPath, cache, checkUnignored, slices) {
411
+ const path5 = originalPath && checkPath.convert(originalPath);
412
+ checkPath(
413
+ path5,
414
+ originalPath,
415
+ this._strictPathCheck ? throwError : RETURN_FALSE
416
+ );
417
+ return this._t(path5, cache, checkUnignored, slices);
418
+ }
419
+ checkIgnore(path5) {
420
+ if (!REGEX_TEST_TRAILING_SLASH.test(path5)) {
421
+ return this.test(path5);
422
+ }
423
+ const slices = path5.split(SLASH).filter(Boolean);
424
+ slices.pop();
425
+ if (slices.length) {
426
+ const parent = this._t(
427
+ slices.join(SLASH) + SLASH,
428
+ this._testCache,
429
+ true,
430
+ slices
431
+ );
432
+ if (parent.ignored) {
433
+ return parent;
434
+ }
435
+ }
436
+ return this._rules.test(path5, false, MODE_CHECK_IGNORE);
437
+ }
438
+ _t(path5, cache, checkUnignored, slices) {
439
+ if (path5 in cache) {
440
+ return cache[path5];
441
+ }
442
+ if (!slices) {
443
+ slices = path5.split(SLASH).filter(Boolean);
444
+ }
445
+ slices.pop();
446
+ if (!slices.length) {
447
+ return cache[path5] = this._rules.test(path5, checkUnignored, MODE_IGNORE);
448
+ }
449
+ const parent = this._t(
450
+ slices.join(SLASH) + SLASH,
451
+ cache,
452
+ checkUnignored,
453
+ slices
454
+ );
455
+ return cache[path5] = parent.ignored ? parent : this._rules.test(path5, checkUnignored, MODE_IGNORE);
456
+ }
457
+ ignores(path5) {
458
+ return this._test(path5, this._ignoreCache, false).ignored;
459
+ }
460
+ createFilter() {
461
+ return (path5) => !this.ignores(path5);
462
+ }
463
+ filter(paths) {
464
+ return makeArray(paths).filter(this.createFilter());
465
+ }
466
+ // @returns {TestResult}
467
+ test(path5) {
468
+ return this._test(path5, this._testCache, true);
469
+ }
470
+ };
471
+ var factory = (options) => new Ignore2(options);
472
+ var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE);
473
+ var setupWindows = () => {
474
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
475
+ checkPath.convert = makePosix;
476
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
477
+ checkPath.isNotRelative = (path5) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5);
478
+ };
479
+ if (
480
+ // Detect `process` so that it can run in browsers.
481
+ typeof process !== "undefined" && process.platform === "win32"
482
+ ) {
483
+ setupWindows();
484
+ }
485
+ module.exports = factory;
486
+ factory.default = factory;
487
+ module.exports.isPathValid = isPathValid;
488
+ define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
489
+ }
490
+ });
491
+
492
+ // src/ignore.ts
493
+ import * as fs from "fs";
494
+ import * as path from "path";
495
+ async function loadIgnorePatterns(projectRoot) {
496
+ const ig = (0, import_ignore.default)();
497
+ const patterns = [...DEFAULT_IGNORE_PATTERNS];
498
+ ig.add(DEFAULT_IGNORE_PATTERNS);
499
+ const ignoreFilePath = path.join(projectRoot, IGNORE_FILENAME);
500
+ let hasUserPatterns = false;
501
+ try {
502
+ const content = await fs.promises.readFile(ignoreFilePath, "utf-8");
503
+ const userPatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
504
+ if (userPatterns.length > 0) {
505
+ ig.add(userPatterns);
506
+ patterns.push(...userPatterns);
507
+ hasUserPatterns = true;
508
+ }
509
+ } catch {
510
+ }
511
+ return {
512
+ ignores: (pathname) => ig.ignores(pathname),
513
+ patterns,
514
+ hasUserPatterns
515
+ };
516
+ }
517
+ var import_ignore, IGNORE_FILENAME, DEFAULT_IGNORE_PATTERNS;
518
+ var init_ignore = __esm({
519
+ "src/ignore.ts"() {
520
+ "use strict";
521
+ import_ignore = __toESM(require_ignore(), 1);
522
+ IGNORE_FILENAME = ".contextstream/ignore";
523
+ DEFAULT_IGNORE_PATTERNS = [
524
+ // Version control
525
+ ".git/",
526
+ ".svn/",
527
+ ".hg/",
528
+ // Package managers / dependencies
529
+ "node_modules/",
530
+ "vendor/",
531
+ ".pnpm/",
532
+ // Build outputs
533
+ "target/",
534
+ "dist/",
535
+ "build/",
536
+ "out/",
537
+ ".next/",
538
+ ".nuxt/",
539
+ // Python
540
+ "__pycache__/",
541
+ ".pytest_cache/",
542
+ ".mypy_cache/",
543
+ "venv/",
544
+ ".venv/",
545
+ "env/",
546
+ ".env/",
547
+ // IDE
548
+ ".idea/",
549
+ ".vscode/",
550
+ ".vs/",
551
+ // Coverage
552
+ "coverage/",
553
+ ".coverage/",
554
+ // Lock files
555
+ "package-lock.json",
556
+ "yarn.lock",
557
+ "pnpm-lock.yaml",
558
+ "Cargo.lock",
559
+ "poetry.lock",
560
+ "Gemfile.lock",
561
+ "composer.lock",
562
+ // OS files
563
+ ".DS_Store",
564
+ "Thumbs.db"
565
+ ];
566
+ }
567
+ });
568
+
569
+ // src/files.ts
570
+ var files_exports = {};
571
+ __export(files_exports, {
572
+ clearGitContextCache: () => clearGitContextCache,
573
+ countIndexableFiles: () => countIndexableFiles,
574
+ deleteHashManifest: () => deleteHashManifest,
575
+ detectLanguage: () => detectLanguage,
576
+ readAllFilesInBatches: () => readAllFilesInBatches,
577
+ readChangedFilesInBatches: () => readChangedFilesInBatches,
578
+ readFilesFromDirectory: () => readFilesFromDirectory,
579
+ readHashManifest: () => readHashManifest,
580
+ sha256Hex: () => sha256Hex,
581
+ writeHashManifest: () => writeHashManifest
582
+ });
583
+ import * as fs2 from "fs";
584
+ import * as path2 from "path";
585
+ import { exec } from "child_process";
586
+ import { promisify } from "util";
587
+ import * as os from "os";
588
+ import * as crypto from "crypto";
589
+ function getMachineId() {
590
+ const hostname2 = os.hostname();
591
+ const hash = crypto.createHash("sha256").update(hostname2).digest("hex");
592
+ return hash.substring(0, 12);
593
+ }
594
+ async function isGitRepo(rootPath) {
595
+ try {
596
+ await execAsync("git rev-parse --is-inside-work-tree", {
597
+ cwd: rootPath,
598
+ timeout: 5e3
599
+ });
600
+ return true;
601
+ } catch {
602
+ return false;
603
+ }
604
+ }
605
+ async function getGitBranch(rootPath) {
606
+ try {
607
+ const { stdout } = await execAsync("git branch --show-current", {
608
+ cwd: rootPath,
609
+ timeout: 5e3
610
+ });
611
+ const branch = stdout.trim();
612
+ return branch || void 0;
613
+ } catch {
614
+ return void 0;
615
+ }
616
+ }
617
+ async function getGitDefaultBranch(rootPath) {
618
+ try {
619
+ const { stdout } = await execAsync(
620
+ "git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null",
621
+ { cwd: rootPath, timeout: 5e3 }
622
+ );
623
+ const match = stdout.trim().match(/refs\/remotes\/origin\/(.+)/);
624
+ if (match) return match[1];
625
+ } catch {
626
+ }
627
+ try {
628
+ const { stdout } = await execAsync(
629
+ "git config --get init.defaultBranch 2>/dev/null",
630
+ { cwd: rootPath, timeout: 5e3 }
631
+ );
632
+ const branch = stdout.trim();
633
+ if (branch) return branch;
634
+ } catch {
635
+ }
636
+ try {
637
+ const { stdout } = await execAsync("git branch --list main master", {
638
+ cwd: rootPath,
639
+ timeout: 5e3
640
+ });
641
+ const branches = stdout.trim().split("\n").map((b) => b.replace(/^\*?\s*/, "").trim()).filter(Boolean);
642
+ if (branches.includes("main")) return "main";
643
+ if (branches.includes("master")) return "master";
644
+ } catch {
645
+ }
646
+ return void 0;
647
+ }
648
+ async function getFileGitInfo(rootPath, relativePath) {
649
+ try {
650
+ const { stdout } = await execAsync(
651
+ `git log -1 --format="%H %ct" -- "${relativePath}"`,
652
+ { cwd: rootPath, timeout: 5e3 }
653
+ );
654
+ const parts = stdout.trim().split(" ");
655
+ if (parts.length >= 2) {
656
+ const sha = parts[0];
657
+ const unixTimestamp = parseInt(parts[1], 10);
658
+ if (sha && !isNaN(unixTimestamp)) {
659
+ const timestamp = new Date(unixTimestamp * 1e3).toISOString();
660
+ return { sha, timestamp };
661
+ }
662
+ }
663
+ } catch {
664
+ }
665
+ return void 0;
666
+ }
667
+ async function getGitContext(rootPath) {
668
+ const cached = gitContextCache.get(rootPath);
669
+ if (cached) return cached;
670
+ const machineId = getMachineId();
671
+ const isRepo = await isGitRepo(rootPath);
672
+ if (!isRepo) {
673
+ const context2 = { isGitRepo: false, machineId };
674
+ gitContextCache.set(rootPath, context2);
675
+ return context2;
676
+ }
677
+ const [branch, defaultBranch] = await Promise.all([
678
+ getGitBranch(rootPath),
679
+ getGitDefaultBranch(rootPath)
680
+ ]);
681
+ const context = {
682
+ isGitRepo: true,
683
+ branch,
684
+ defaultBranch,
685
+ isDefaultBranch: branch !== void 0 && branch === defaultBranch,
686
+ machineId
687
+ };
688
+ gitContextCache.set(rootPath, context);
689
+ return context;
690
+ }
691
+ function clearGitContextCache() {
692
+ gitContextCache.clear();
693
+ }
694
+ async function readFilesFromDirectory(rootPath, options = {}) {
695
+ const maxFiles = options.maxFiles ?? MAX_FILES_PER_BATCH;
696
+ const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
697
+ const files = [];
698
+ const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
699
+ const gitCtx = await getGitContext(rootPath);
700
+ async function walkDir(dir, relativePath = "") {
701
+ if (files.length >= maxFiles) return;
702
+ let entries;
703
+ try {
704
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
705
+ } catch {
706
+ return;
707
+ }
708
+ for (const entry of entries) {
709
+ if (files.length >= maxFiles) break;
710
+ const fullPath = path2.join(dir, entry.name);
711
+ const relPath = path2.join(relativePath, entry.name);
712
+ if (entry.isDirectory()) {
713
+ if (IGNORE_DIRS.has(entry.name)) continue;
714
+ if (ig.ignores(relPath + "/")) continue;
715
+ await walkDir(fullPath, relPath);
716
+ } else if (entry.isFile()) {
717
+ if (IGNORE_FILES.has(entry.name)) continue;
718
+ if (ig.ignores(relPath)) continue;
719
+ const ext = entry.name.split(".").pop()?.toLowerCase() ?? "";
720
+ if (!CODE_EXTENSIONS.has(ext)) continue;
721
+ try {
722
+ const stat = await fs2.promises.stat(fullPath);
723
+ if (stat.size > maxFileSize) continue;
724
+ const content = await fs2.promises.readFile(fullPath, "utf-8");
725
+ const file = {
726
+ path: relPath,
727
+ content,
728
+ // Always include machine_id and source_modified_at
729
+ machine_id: gitCtx.machineId,
730
+ source_modified_at: stat.mtime.toISOString()
731
+ };
732
+ if (gitCtx.isGitRepo) {
733
+ file.git_branch = gitCtx.branch;
734
+ file.git_default_branch = gitCtx.defaultBranch;
735
+ file.is_default_branch = gitCtx.isDefaultBranch;
736
+ const gitInfo = await getFileGitInfo(rootPath, relPath);
737
+ if (gitInfo) {
738
+ file.git_commit_sha = gitInfo.sha;
739
+ file.git_commit_timestamp = gitInfo.timestamp;
740
+ }
741
+ }
742
+ files.push(file);
743
+ } catch {
744
+ }
745
+ }
746
+ }
747
+ }
748
+ await walkDir(rootPath);
749
+ return files;
750
+ }
751
+ async function* readAllFilesInBatches(rootPath, options = {}) {
752
+ const maxBatchBytes = options.maxBatchBytes ?? MAX_BATCH_BYTES;
753
+ const largeFileThreshold = options.largeFileThreshold ?? LARGE_FILE_THRESHOLD;
754
+ const maxFilesPerBatch = options.maxFilesPerBatch ?? options.batchSize ?? MAX_FILES_PER_BATCH;
755
+ const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
756
+ const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
757
+ const gitCtx = await getGitContext(rootPath);
758
+ let batch = [];
759
+ let currentBatchBytes = 0;
760
+ async function* walkDir(dir, relativePath = "") {
761
+ let entries;
762
+ try {
763
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
764
+ } catch {
765
+ return;
766
+ }
767
+ for (const entry of entries) {
768
+ const fullPath = path2.join(dir, entry.name);
769
+ const relPath = path2.join(relativePath, entry.name);
770
+ if (entry.isDirectory()) {
771
+ if (IGNORE_DIRS.has(entry.name)) continue;
772
+ if (ig.ignores(relPath + "/")) continue;
773
+ yield* walkDir(fullPath, relPath);
774
+ } else if (entry.isFile()) {
775
+ if (IGNORE_FILES.has(entry.name)) continue;
776
+ if (ig.ignores(relPath)) continue;
777
+ const ext = entry.name.split(".").pop()?.toLowerCase() ?? "";
778
+ if (!CODE_EXTENSIONS.has(ext)) continue;
779
+ try {
780
+ const stat = await fs2.promises.stat(fullPath);
781
+ if (stat.size > maxFileSize) continue;
782
+ const content = await fs2.promises.readFile(fullPath, "utf-8");
783
+ const file = {
784
+ path: relPath,
785
+ content,
786
+ sizeBytes: stat.size,
787
+ // Always include machine_id and source_modified_at
788
+ machine_id: gitCtx.machineId,
789
+ source_modified_at: stat.mtime.toISOString()
790
+ };
791
+ if (gitCtx.isGitRepo) {
792
+ file.git_branch = gitCtx.branch;
793
+ file.git_default_branch = gitCtx.defaultBranch;
794
+ file.is_default_branch = gitCtx.isDefaultBranch;
795
+ const gitInfo = await getFileGitInfo(rootPath, relPath);
796
+ if (gitInfo) {
797
+ file.git_commit_sha = gitInfo.sha;
798
+ file.git_commit_timestamp = gitInfo.timestamp;
799
+ }
800
+ }
801
+ yield file;
802
+ } catch {
803
+ }
804
+ }
805
+ }
806
+ }
807
+ for await (const file of walkDir(rootPath)) {
808
+ if (file.sizeBytes > largeFileThreshold) {
809
+ if (batch.length > 0) {
810
+ yield batch.map(({ sizeBytes: sizeBytes2, ...rest }) => rest);
811
+ batch = [];
812
+ currentBatchBytes = 0;
813
+ }
814
+ const { sizeBytes, ...fileData } = file;
815
+ yield [fileData];
816
+ continue;
817
+ }
818
+ const wouldExceedBytes = currentBatchBytes + file.sizeBytes > maxBatchBytes;
819
+ const wouldExceedFiles = batch.length >= maxFilesPerBatch;
820
+ if (wouldExceedBytes || wouldExceedFiles) {
821
+ if (batch.length > 0) {
822
+ yield batch.map(({ sizeBytes, ...rest }) => rest);
823
+ batch = [];
824
+ currentBatchBytes = 0;
825
+ }
826
+ }
827
+ batch.push(file);
828
+ currentBatchBytes += file.sizeBytes;
829
+ }
830
+ if (batch.length > 0) {
831
+ yield batch.map(({ sizeBytes, ...rest }) => rest);
832
+ }
833
+ }
834
+ async function* readChangedFilesInBatches(rootPath, sinceTimestamp, options = {}) {
835
+ const maxBatchBytes = options.maxBatchBytes ?? MAX_BATCH_BYTES;
836
+ const largeFileThreshold = options.largeFileThreshold ?? LARGE_FILE_THRESHOLD;
837
+ const maxFilesPerBatch = options.maxFilesPerBatch ?? options.batchSize ?? MAX_FILES_PER_BATCH;
838
+ const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
839
+ const sinceMs = sinceTimestamp.getTime();
840
+ const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
841
+ const gitCtx = await getGitContext(rootPath);
842
+ let batch = [];
843
+ let currentBatchBytes = 0;
844
+ let filesScanned = 0;
845
+ let filesChanged = 0;
846
+ async function* walkDir(dir, relativePath = "") {
847
+ let entries;
848
+ try {
849
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
850
+ } catch {
851
+ return;
852
+ }
853
+ for (const entry of entries) {
854
+ const fullPath = path2.join(dir, entry.name);
855
+ const relPath = path2.join(relativePath, entry.name);
856
+ if (entry.isDirectory()) {
857
+ if (IGNORE_DIRS.has(entry.name)) continue;
858
+ if (ig.ignores(relPath + "/")) continue;
859
+ yield* walkDir(fullPath, relPath);
860
+ } else if (entry.isFile()) {
861
+ if (IGNORE_FILES.has(entry.name)) continue;
862
+ if (ig.ignores(relPath)) continue;
863
+ const ext = entry.name.split(".").pop()?.toLowerCase() ?? "";
864
+ if (!CODE_EXTENSIONS.has(ext)) continue;
865
+ try {
866
+ const stat = await fs2.promises.stat(fullPath);
867
+ filesScanned++;
868
+ if (stat.mtimeMs <= sinceMs) continue;
869
+ if (stat.size > maxFileSize) continue;
870
+ const content = await fs2.promises.readFile(fullPath, "utf-8");
871
+ filesChanged++;
872
+ const file = {
873
+ path: relPath,
874
+ content,
875
+ sizeBytes: stat.size,
876
+ // Always include machine_id and source_modified_at
877
+ machine_id: gitCtx.machineId,
878
+ source_modified_at: stat.mtime.toISOString()
879
+ };
880
+ if (gitCtx.isGitRepo) {
881
+ file.git_branch = gitCtx.branch;
882
+ file.git_default_branch = gitCtx.defaultBranch;
883
+ file.is_default_branch = gitCtx.isDefaultBranch;
884
+ const gitInfo = await getFileGitInfo(rootPath, relPath);
885
+ if (gitInfo) {
886
+ file.git_commit_sha = gitInfo.sha;
887
+ file.git_commit_timestamp = gitInfo.timestamp;
888
+ }
889
+ }
890
+ yield file;
891
+ } catch {
892
+ }
893
+ }
894
+ }
895
+ }
896
+ for await (const file of walkDir(rootPath)) {
897
+ if (file.sizeBytes > largeFileThreshold) {
898
+ if (batch.length > 0) {
899
+ yield batch.map(({ sizeBytes: sizeBytes2, ...rest }) => rest);
900
+ batch = [];
901
+ currentBatchBytes = 0;
902
+ }
903
+ const { sizeBytes, ...fileData } = file;
904
+ yield [fileData];
905
+ continue;
906
+ }
907
+ const wouldExceedBytes = currentBatchBytes + file.sizeBytes > maxBatchBytes;
908
+ const wouldExceedFiles = batch.length >= maxFilesPerBatch;
909
+ if (wouldExceedBytes || wouldExceedFiles) {
910
+ if (batch.length > 0) {
911
+ yield batch.map(({ sizeBytes, ...rest }) => rest);
912
+ batch = [];
913
+ currentBatchBytes = 0;
914
+ }
915
+ }
916
+ batch.push(file);
917
+ currentBatchBytes += file.sizeBytes;
918
+ }
919
+ if (batch.length > 0) {
920
+ yield batch.map(({ sizeBytes, ...rest }) => rest);
921
+ }
922
+ console.error(
923
+ `[ContextStream] Incremental scan: ${filesChanged} changed files out of ${filesScanned} scanned (since ${sinceTimestamp.toISOString()})`
924
+ );
925
+ }
926
+ async function countIndexableFiles(rootPath, options = {}) {
927
+ const maxFiles = options.maxFiles ?? 1;
928
+ const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
929
+ const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
930
+ let count = 0;
931
+ let stopped = false;
932
+ async function walkDir(dir, relativePath = "") {
933
+ if (count >= maxFiles) {
934
+ stopped = true;
935
+ return;
936
+ }
937
+ let entries;
938
+ try {
939
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
940
+ } catch {
941
+ return;
942
+ }
943
+ for (const entry of entries) {
944
+ if (count >= maxFiles) {
945
+ stopped = true;
946
+ return;
947
+ }
948
+ const fullPath = path2.join(dir, entry.name);
949
+ const relPath = path2.join(relativePath, entry.name);
950
+ if (entry.isDirectory()) {
951
+ if (IGNORE_DIRS.has(entry.name)) continue;
952
+ if (ig.ignores(relPath + "/")) continue;
953
+ await walkDir(fullPath, relPath);
954
+ } else if (entry.isFile()) {
955
+ if (IGNORE_FILES.has(entry.name)) continue;
956
+ if (ig.ignores(relPath)) continue;
957
+ const ext = entry.name.split(".").pop()?.toLowerCase() ?? "";
958
+ if (!CODE_EXTENSIONS.has(ext)) continue;
959
+ try {
960
+ const stat = await fs2.promises.stat(fullPath);
961
+ if (stat.size > maxFileSize) continue;
962
+ count++;
963
+ if (count >= maxFiles) {
964
+ stopped = true;
965
+ return;
966
+ }
967
+ } catch {
968
+ }
969
+ }
970
+ }
971
+ }
972
+ await walkDir(rootPath);
973
+ return { count, stopped };
974
+ }
975
+ function detectLanguage(filePath) {
976
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
977
+ const langMap = {
978
+ rs: "rust",
979
+ ts: "typescript",
980
+ tsx: "typescript",
981
+ js: "javascript",
982
+ jsx: "javascript",
983
+ py: "python",
984
+ go: "go",
985
+ java: "java",
986
+ kt: "kotlin",
987
+ c: "c",
988
+ h: "c",
989
+ cpp: "cpp",
990
+ hpp: "cpp",
991
+ cs: "csharp",
992
+ rb: "ruby",
993
+ php: "php",
994
+ swift: "swift",
995
+ scala: "scala",
996
+ sql: "sql",
997
+ md: "markdown",
998
+ json: "json",
999
+ yaml: "yaml",
1000
+ yml: "yaml",
1001
+ toml: "toml",
1002
+ html: "html",
1003
+ css: "css",
1004
+ sh: "shell"
1005
+ };
1006
+ return langMap[ext] ?? "unknown";
1007
+ }
1008
+ function sha256Hex(content) {
1009
+ return crypto.createHash("sha256").update(content).digest("hex");
1010
+ }
1011
+ function hashManifestPath(projectId) {
1012
+ return path2.join(os.homedir(), ".contextstream", "file-hashes", `${projectId}.json`);
1013
+ }
1014
+ function readHashManifest(projectId) {
1015
+ try {
1016
+ const content = fs2.readFileSync(hashManifestPath(projectId), "utf-8");
1017
+ const parsed = JSON.parse(content);
1018
+ return new Map(Object.entries(parsed));
1019
+ } catch {
1020
+ return /* @__PURE__ */ new Map();
1021
+ }
1022
+ }
1023
+ function writeHashManifest(projectId, hashes) {
1024
+ const filePath = hashManifestPath(projectId);
1025
+ try {
1026
+ const dir = path2.dirname(filePath);
1027
+ fs2.mkdirSync(dir, { recursive: true });
1028
+ const obj = Object.fromEntries(hashes);
1029
+ fs2.writeFileSync(filePath, JSON.stringify(obj, null, 2));
1030
+ } catch {
1031
+ }
1032
+ }
1033
+ function deleteHashManifest(projectId) {
1034
+ try {
1035
+ fs2.unlinkSync(hashManifestPath(projectId));
1036
+ } catch {
1037
+ }
1038
+ }
1039
+ var execAsync, CODE_EXTENSIONS, IGNORE_DIRS, IGNORE_FILES, MAX_FILE_SIZE, MAX_BATCH_BYTES, LARGE_FILE_THRESHOLD, MAX_FILES_PER_BATCH, gitContextCache;
1040
+ var init_files = __esm({
1041
+ "src/files.ts"() {
1042
+ "use strict";
1043
+ init_ignore();
1044
+ execAsync = promisify(exec);
1045
+ CODE_EXTENSIONS = /* @__PURE__ */ new Set([
1046
+ // Rust
1047
+ "rs",
1048
+ // TypeScript/JavaScript
1049
+ "ts",
1050
+ "tsx",
1051
+ "js",
1052
+ "jsx",
1053
+ "mjs",
1054
+ "cjs",
1055
+ // Python
1056
+ "py",
1057
+ "pyi",
1058
+ // Go
1059
+ "go",
1060
+ // Java/Kotlin
1061
+ "java",
1062
+ "kt",
1063
+ "kts",
1064
+ // C/C++
1065
+ "c",
1066
+ "h",
1067
+ "cpp",
1068
+ "hpp",
1069
+ "cc",
1070
+ "cxx",
1071
+ // C#
1072
+ "cs",
1073
+ // Ruby
1074
+ "rb",
1075
+ // PHP
1076
+ "php",
1077
+ // Swift
1078
+ "swift",
1079
+ // Scala
1080
+ "scala",
1081
+ // Shell
1082
+ "sh",
1083
+ "bash",
1084
+ "zsh",
1085
+ // Config/Data
1086
+ "json",
1087
+ "yaml",
1088
+ "yml",
1089
+ "toml",
1090
+ "xml",
1091
+ // SQL
1092
+ "sql",
1093
+ // Markdown/Docs
1094
+ "md",
1095
+ "markdown",
1096
+ "rst",
1097
+ "txt",
1098
+ // HTML/CSS
1099
+ "html",
1100
+ "htm",
1101
+ "css",
1102
+ "scss",
1103
+ "sass",
1104
+ "less",
1105
+ // Other
1106
+ "graphql",
1107
+ "proto",
1108
+ "dockerfile"
1109
+ ]);
1110
+ IGNORE_DIRS = /* @__PURE__ */ new Set([
1111
+ "node_modules",
1112
+ ".git",
1113
+ ".svn",
1114
+ ".hg",
1115
+ "target",
1116
+ "dist",
1117
+ "build",
1118
+ "out",
1119
+ ".next",
1120
+ ".nuxt",
1121
+ "__pycache__",
1122
+ ".pytest_cache",
1123
+ ".mypy_cache",
1124
+ "venv",
1125
+ ".venv",
1126
+ "env",
1127
+ ".env",
1128
+ "vendor",
1129
+ "coverage",
1130
+ ".coverage",
1131
+ ".idea",
1132
+ ".vscode",
1133
+ ".vs"
1134
+ ]);
1135
+ IGNORE_FILES = /* @__PURE__ */ new Set([
1136
+ ".DS_Store",
1137
+ "Thumbs.db",
1138
+ ".gitignore",
1139
+ ".gitattributes",
1140
+ "package-lock.json",
1141
+ "yarn.lock",
1142
+ "pnpm-lock.yaml",
1143
+ "Cargo.lock",
1144
+ "poetry.lock",
1145
+ "Gemfile.lock",
1146
+ "composer.lock"
1147
+ ]);
1148
+ MAX_FILE_SIZE = 1024 * 1024;
1149
+ MAX_BATCH_BYTES = 10 * 1024 * 1024;
1150
+ LARGE_FILE_THRESHOLD = 2 * 1024 * 1024;
1151
+ MAX_FILES_PER_BATCH = 200;
1152
+ gitContextCache = /* @__PURE__ */ new Map();
1153
+ }
1154
+ });
11
1155
 
12
1156
  // src/hooks-config.ts
13
1157
  var hooks_config_exports = {};
@@ -22,6 +1166,7 @@ __export(hooks_config_exports, {
22
1166
  PRETOOLUSE_HOOK_SCRIPT: () => PRETOOLUSE_HOOK_SCRIPT,
23
1167
  USER_PROMPT_HOOK_SCRIPT: () => USER_PROMPT_HOOK_SCRIPT,
24
1168
  buildHooksConfig: () => buildHooksConfig,
1169
+ clearProjectIndex: () => clearProjectIndex,
25
1170
  generateAllHooksDocumentation: () => generateAllHooksDocumentation,
26
1171
  generateHooksDocumentation: () => generateHooksDocumentation,
27
1172
  getClaudeSettingsPath: () => getClaudeSettingsPath,
@@ -51,17 +1196,17 @@ __export(hooks_config_exports, {
51
1196
  writeCursorHooksConfig: () => writeCursorHooksConfig,
52
1197
  writeIndexStatus: () => writeIndexStatus
53
1198
  });
54
- import * as fs from "node:fs/promises";
1199
+ import * as fs3 from "node:fs/promises";
55
1200
  import * as fsSync from "node:fs";
56
- import * as path from "node:path";
57
- import { homedir } from "node:os";
1201
+ import * as path3 from "node:path";
1202
+ import { homedir as homedir2 } from "node:os";
58
1203
  import { fileURLToPath } from "node:url";
59
1204
  function getHookCommand(hookName) {
60
1205
  const isWindows = process.platform === "win32";
61
1206
  if (isWindows) {
62
1207
  const localAppData = process.env.LOCALAPPDATA;
63
1208
  if (localAppData) {
64
- const windowsBinaryPath = path.join(localAppData, "ContextStream", "contextstream-mcp.exe");
1209
+ const windowsBinaryPath = path3.join(localAppData, "ContextStream", "contextstream-mcp.exe");
65
1210
  if (fsSync.existsSync(windowsBinaryPath)) {
66
1211
  return `"${windowsBinaryPath}" hook ${hookName}`;
67
1212
  }
@@ -73,8 +1218,8 @@ function getHookCommand(hookName) {
73
1218
  }
74
1219
  }
75
1220
  try {
76
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
77
- const indexPath = path.join(__dirname, "index.js");
1221
+ const __dirname = path3.dirname(fileURLToPath(import.meta.url));
1222
+ const indexPath = path3.join(__dirname, "index.js");
78
1223
  if (fsSync.existsSync(indexPath)) {
79
1224
  return `node "${indexPath}" hook ${hookName}`;
80
1225
  }
@@ -84,15 +1229,15 @@ function getHookCommand(hookName) {
84
1229
  }
85
1230
  function getClaudeSettingsPath(scope, projectPath) {
86
1231
  if (scope === "user") {
87
- return path.join(homedir(), ".claude", "settings.json");
1232
+ return path3.join(homedir2(), ".claude", "settings.json");
88
1233
  }
89
1234
  if (!projectPath) {
90
1235
  throw new Error("projectPath required for project scope");
91
1236
  }
92
- return path.join(projectPath, ".claude", "settings.json");
1237
+ return path3.join(projectPath, ".claude", "settings.json");
93
1238
  }
94
1239
  function getHooksDir() {
95
- return path.join(homedir(), ".claude", "hooks");
1240
+ return path3.join(homedir2(), ".claude", "hooks");
96
1241
  }
97
1242
  function buildHooksConfig(options) {
98
1243
  const userPromptHooks = [
@@ -268,7 +1413,7 @@ function buildHooksConfig(options) {
268
1413
  }
269
1414
  async function installHookScripts(options) {
270
1415
  const hooksDir = getHooksDir();
271
- await fs.mkdir(hooksDir, { recursive: true });
1416
+ await fs3.mkdir(hooksDir, { recursive: true });
272
1417
  const result = {
273
1418
  preToolUse: getHookCommand("pre-tool-use"),
274
1419
  userPrompt: getHookCommand("user-prompt-submit")
@@ -287,7 +1432,7 @@ async function installHookScripts(options) {
287
1432
  async function readClaudeSettings(scope, projectPath) {
288
1433
  const settingsPath = getClaudeSettingsPath(scope, projectPath);
289
1434
  try {
290
- const content = await fs.readFile(settingsPath, "utf-8");
1435
+ const content = await fs3.readFile(settingsPath, "utf-8");
291
1436
  return JSON.parse(content);
292
1437
  } catch {
293
1438
  return {};
@@ -295,9 +1440,9 @@ async function readClaudeSettings(scope, projectPath) {
295
1440
  }
296
1441
  async function writeClaudeSettings(settings, scope, projectPath) {
297
1442
  const settingsPath = getClaudeSettingsPath(scope, projectPath);
298
- const dir = path.dirname(settingsPath);
299
- await fs.mkdir(dir, { recursive: true });
300
- await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
1443
+ const dir = path3.dirname(settingsPath);
1444
+ await fs3.mkdir(dir, { recursive: true });
1445
+ await fs3.writeFile(settingsPath, JSON.stringify(settings, null, 2));
301
1446
  }
302
1447
  function mergeHooksIntoSettings(existingSettings, newHooks) {
303
1448
  const settings = { ...existingSettings };
@@ -447,12 +1592,12 @@ If you prefer to configure manually, add to \`~/.claude/settings.json\`:
447
1592
  `.trim();
448
1593
  }
449
1594
  function getIndexStatusPath() {
450
- return path.join(homedir(), ".contextstream", "indexed-projects.json");
1595
+ return path3.join(homedir2(), ".contextstream", "indexed-projects.json");
451
1596
  }
452
1597
  async function readIndexStatus() {
453
1598
  const statusPath = getIndexStatusPath();
454
1599
  try {
455
- const content = await fs.readFile(statusPath, "utf-8");
1600
+ const content = await fs3.readFile(statusPath, "utf-8");
456
1601
  return JSON.parse(content);
457
1602
  } catch {
458
1603
  return { version: 1, projects: {} };
@@ -460,13 +1605,13 @@ async function readIndexStatus() {
460
1605
  }
461
1606
  async function writeIndexStatus(status) {
462
1607
  const statusPath = getIndexStatusPath();
463
- const dir = path.dirname(statusPath);
464
- await fs.mkdir(dir, { recursive: true });
465
- await fs.writeFile(statusPath, JSON.stringify(status, null, 2));
1608
+ const dir = path3.dirname(statusPath);
1609
+ await fs3.mkdir(dir, { recursive: true });
1610
+ await fs3.writeFile(statusPath, JSON.stringify(status, null, 2));
466
1611
  }
467
1612
  async function markProjectIndexed(projectPath, options) {
468
1613
  const status = await readIndexStatus();
469
- const resolvedPath = path.resolve(projectPath);
1614
+ const resolvedPath = path3.resolve(projectPath);
470
1615
  status.projects[resolvedPath] = {
471
1616
  indexed_at: (/* @__PURE__ */ new Date()).toISOString(),
472
1617
  project_id: options?.project_id,
@@ -476,18 +1621,25 @@ async function markProjectIndexed(projectPath, options) {
476
1621
  }
477
1622
  async function unmarkProjectIndexed(projectPath) {
478
1623
  const status = await readIndexStatus();
479
- const resolvedPath = path.resolve(projectPath);
1624
+ const resolvedPath = path3.resolve(projectPath);
480
1625
  delete status.projects[resolvedPath];
481
1626
  await writeIndexStatus(status);
482
1627
  }
1628
+ async function clearProjectIndex(projectPath, projectId) {
1629
+ await unmarkProjectIndexed(projectPath);
1630
+ if (projectId) {
1631
+ const { deleteHashManifest: deleteHashManifest2 } = await Promise.resolve().then(() => (init_files(), files_exports));
1632
+ deleteHashManifest2(projectId);
1633
+ }
1634
+ }
483
1635
  function getClineHooksDir(scope, projectPath) {
484
1636
  if (scope === "global") {
485
- return path.join(homedir(), "Documents", "Cline", "Rules", "Hooks");
1637
+ return path3.join(homedir2(), "Documents", "Cline", "Rules", "Hooks");
486
1638
  }
487
1639
  if (!projectPath) {
488
1640
  throw new Error("projectPath required for project scope");
489
1641
  }
490
- return path.join(projectPath, ".clinerules", "hooks");
1642
+ return path3.join(projectPath, ".clinerules", "hooks");
491
1643
  }
492
1644
  function getHookWrapperScript(hookName) {
493
1645
  const isWindows = process.platform === "win32";
@@ -511,107 +1663,107 @@ exec ${command}
511
1663
  }
512
1664
  async function installClineHookScripts(options) {
513
1665
  const hooksDir = getClineHooksDir(options.scope, options.projectPath);
514
- await fs.mkdir(hooksDir, { recursive: true });
1666
+ await fs3.mkdir(hooksDir, { recursive: true });
515
1667
  const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
516
1668
  const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
517
1669
  const postWriteWrapper = getHookWrapperScript("post-write");
518
- const preToolUsePath = path.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
519
- const userPromptPath = path.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
520
- const postToolUsePath = path.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
521
- await fs.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
522
- await fs.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
1670
+ const preToolUsePath = path3.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
1671
+ const userPromptPath = path3.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
1672
+ const postToolUsePath = path3.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
1673
+ await fs3.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
1674
+ await fs3.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
523
1675
  const result = {
524
1676
  preToolUse: preToolUsePath,
525
1677
  userPromptSubmit: userPromptPath
526
1678
  };
527
1679
  if (options.includePostWrite !== false) {
528
- await fs.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
1680
+ await fs3.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
529
1681
  result.postToolUse = postToolUsePath;
530
1682
  }
531
1683
  return result;
532
1684
  }
533
1685
  function getRooCodeHooksDir(scope, projectPath) {
534
1686
  if (scope === "global") {
535
- return path.join(homedir(), ".roo", "hooks");
1687
+ return path3.join(homedir2(), ".roo", "hooks");
536
1688
  }
537
1689
  if (!projectPath) {
538
1690
  throw new Error("projectPath required for project scope");
539
1691
  }
540
- return path.join(projectPath, ".roo", "hooks");
1692
+ return path3.join(projectPath, ".roo", "hooks");
541
1693
  }
542
1694
  async function installRooCodeHookScripts(options) {
543
1695
  const hooksDir = getRooCodeHooksDir(options.scope, options.projectPath);
544
- await fs.mkdir(hooksDir, { recursive: true });
1696
+ await fs3.mkdir(hooksDir, { recursive: true });
545
1697
  const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
546
1698
  const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
547
1699
  const postWriteWrapper = getHookWrapperScript("post-write");
548
- const preToolUsePath = path.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
549
- const userPromptPath = path.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
550
- const postToolUsePath = path.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
551
- await fs.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
552
- await fs.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
1700
+ const preToolUsePath = path3.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
1701
+ const userPromptPath = path3.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
1702
+ const postToolUsePath = path3.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
1703
+ await fs3.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
1704
+ await fs3.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
553
1705
  const result = {
554
1706
  preToolUse: preToolUsePath,
555
1707
  userPromptSubmit: userPromptPath
556
1708
  };
557
1709
  if (options.includePostWrite !== false) {
558
- await fs.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
1710
+ await fs3.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
559
1711
  result.postToolUse = postToolUsePath;
560
1712
  }
561
1713
  return result;
562
1714
  }
563
1715
  function getKiloCodeHooksDir(scope, projectPath) {
564
1716
  if (scope === "global") {
565
- return path.join(homedir(), ".kilocode", "hooks");
1717
+ return path3.join(homedir2(), ".kilocode", "hooks");
566
1718
  }
567
1719
  if (!projectPath) {
568
1720
  throw new Error("projectPath required for project scope");
569
1721
  }
570
- return path.join(projectPath, ".kilocode", "hooks");
1722
+ return path3.join(projectPath, ".kilocode", "hooks");
571
1723
  }
572
1724
  async function installKiloCodeHookScripts(options) {
573
1725
  const hooksDir = getKiloCodeHooksDir(options.scope, options.projectPath);
574
- await fs.mkdir(hooksDir, { recursive: true });
1726
+ await fs3.mkdir(hooksDir, { recursive: true });
575
1727
  const preToolUseWrapper = getHookWrapperScript("pre-tool-use");
576
1728
  const userPromptWrapper = getHookWrapperScript("user-prompt-submit");
577
1729
  const postWriteWrapper = getHookWrapperScript("post-write");
578
- const preToolUsePath = path.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
579
- const userPromptPath = path.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
580
- const postToolUsePath = path.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
581
- await fs.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
582
- await fs.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
1730
+ const preToolUsePath = path3.join(hooksDir, `PreToolUse${preToolUseWrapper.extension}`);
1731
+ const userPromptPath = path3.join(hooksDir, `UserPromptSubmit${userPromptWrapper.extension}`);
1732
+ const postToolUsePath = path3.join(hooksDir, `PostToolUse${postWriteWrapper.extension}`);
1733
+ await fs3.writeFile(preToolUsePath, preToolUseWrapper.content, { mode: 493 });
1734
+ await fs3.writeFile(userPromptPath, userPromptWrapper.content, { mode: 493 });
583
1735
  const result = {
584
1736
  preToolUse: preToolUsePath,
585
1737
  userPromptSubmit: userPromptPath
586
1738
  };
587
1739
  if (options.includePostWrite !== false) {
588
- await fs.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
1740
+ await fs3.writeFile(postToolUsePath, postWriteWrapper.content, { mode: 493 });
589
1741
  result.postToolUse = postToolUsePath;
590
1742
  }
591
1743
  return result;
592
1744
  }
593
1745
  function getCursorHooksConfigPath(scope, projectPath) {
594
1746
  if (scope === "global") {
595
- return path.join(homedir(), ".cursor", "hooks.json");
1747
+ return path3.join(homedir2(), ".cursor", "hooks.json");
596
1748
  }
597
1749
  if (!projectPath) {
598
1750
  throw new Error("projectPath required for project scope");
599
1751
  }
600
- return path.join(projectPath, ".cursor", "hooks.json");
1752
+ return path3.join(projectPath, ".cursor", "hooks.json");
601
1753
  }
602
1754
  function getCursorHooksDir(scope, projectPath) {
603
1755
  if (scope === "global") {
604
- return path.join(homedir(), ".cursor", "hooks");
1756
+ return path3.join(homedir2(), ".cursor", "hooks");
605
1757
  }
606
1758
  if (!projectPath) {
607
1759
  throw new Error("projectPath required for project scope");
608
1760
  }
609
- return path.join(projectPath, ".cursor", "hooks");
1761
+ return path3.join(projectPath, ".cursor", "hooks");
610
1762
  }
611
1763
  async function readCursorHooksConfig(scope, projectPath) {
612
1764
  const configPath = getCursorHooksConfigPath(scope, projectPath);
613
1765
  try {
614
- const content = await fs.readFile(configPath, "utf-8");
1766
+ const content = await fs3.readFile(configPath, "utf-8");
615
1767
  return JSON.parse(content);
616
1768
  } catch {
617
1769
  return { version: 1, hooks: {} };
@@ -619,13 +1771,13 @@ async function readCursorHooksConfig(scope, projectPath) {
619
1771
  }
620
1772
  async function writeCursorHooksConfig(config, scope, projectPath) {
621
1773
  const configPath = getCursorHooksConfigPath(scope, projectPath);
622
- const dir = path.dirname(configPath);
623
- await fs.mkdir(dir, { recursive: true });
624
- await fs.writeFile(configPath, JSON.stringify(config, null, 2));
1774
+ const dir = path3.dirname(configPath);
1775
+ await fs3.mkdir(dir, { recursive: true });
1776
+ await fs3.writeFile(configPath, JSON.stringify(config, null, 2));
625
1777
  }
626
1778
  async function installCursorHookScripts(options) {
627
1779
  const hooksDir = getCursorHooksDir(options.scope, options.projectPath);
628
- await fs.mkdir(hooksDir, { recursive: true });
1780
+ await fs3.mkdir(hooksDir, { recursive: true });
629
1781
  const existingConfig = await readCursorHooksConfig(options.scope, options.projectPath);
630
1782
  const filterContextStreamHooks = (hooks) => {
631
1783
  if (!hooks) return [];
@@ -1637,18 +2789,18 @@ if __name__ == "__main__":
1637
2789
  });
1638
2790
 
1639
2791
  // src/hooks/auto-rules.ts
1640
- import * as fs2 from "node:fs";
1641
- import * as path2 from "node:path";
1642
- import { homedir as homedir2 } from "node:os";
2792
+ import * as fs4 from "node:fs";
2793
+ import * as path4 from "node:path";
2794
+ import { homedir as homedir3 } from "node:os";
1643
2795
  var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
1644
2796
  var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
1645
2797
  var ENABLED = process.env.CONTEXTSTREAM_AUTO_RULES !== "false";
1646
- var MARKER_FILE = path2.join(homedir2(), ".contextstream", ".auto-rules-ran");
2798
+ var MARKER_FILE = path4.join(homedir3(), ".contextstream", ".auto-rules-ran");
1647
2799
  var COOLDOWN_MS = 4 * 60 * 60 * 1e3;
1648
2800
  function hasRunRecently() {
1649
2801
  try {
1650
- if (!fs2.existsSync(MARKER_FILE)) return false;
1651
- const stat = fs2.statSync(MARKER_FILE);
2802
+ if (!fs4.existsSync(MARKER_FILE)) return false;
2803
+ const stat = fs4.statSync(MARKER_FILE);
1652
2804
  const age = Date.now() - stat.mtimeMs;
1653
2805
  return age < COOLDOWN_MS;
1654
2806
  } catch {
@@ -1657,11 +2809,11 @@ function hasRunRecently() {
1657
2809
  }
1658
2810
  function markAsRan() {
1659
2811
  try {
1660
- const dir = path2.dirname(MARKER_FILE);
1661
- if (!fs2.existsSync(dir)) {
1662
- fs2.mkdirSync(dir, { recursive: true });
2812
+ const dir = path4.dirname(MARKER_FILE);
2813
+ if (!fs4.existsSync(dir)) {
2814
+ fs4.mkdirSync(dir, { recursive: true });
1663
2815
  }
1664
- fs2.writeFileSync(MARKER_FILE, (/* @__PURE__ */ new Date()).toISOString());
2816
+ fs4.writeFileSync(MARKER_FILE, (/* @__PURE__ */ new Date()).toISOString());
1665
2817
  } catch {
1666
2818
  }
1667
2819
  }
@@ -1690,8 +2842,8 @@ function extractCwd(input) {
1690
2842
  }
1691
2843
  function hasPythonHooks(settingsPath) {
1692
2844
  try {
1693
- if (!fs2.existsSync(settingsPath)) return false;
1694
- const content = fs2.readFileSync(settingsPath, "utf-8");
2845
+ if (!fs4.existsSync(settingsPath)) return false;
2846
+ const content = fs4.readFileSync(settingsPath, "utf-8");
1695
2847
  const settings = JSON.parse(content);
1696
2848
  const hooks = settings.hooks;
1697
2849
  if (!hooks) return false;
@@ -1715,8 +2867,8 @@ function hasPythonHooks(settingsPath) {
1715
2867
  }
1716
2868
  }
1717
2869
  function detectPythonHooks(cwd) {
1718
- const globalSettingsPath = path2.join(homedir2(), ".claude", "settings.json");
1719
- const projectSettingsPath = path2.join(cwd, ".claude", "settings.json");
2870
+ const globalSettingsPath = path4.join(homedir3(), ".claude", "settings.json");
2871
+ const projectSettingsPath = path4.join(cwd, ".claude", "settings.json");
1720
2872
  return {
1721
2873
  global: hasPythonHooks(globalSettingsPath),
1722
2874
  project: hasPythonHooks(projectSettingsPath)