eminiar 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hook.js CHANGED
@@ -174,14 +174,14 @@ var require_is_extglob = __commonJS({
174
174
  "node_modules/is-extglob/index.js"(exports, module) {
175
175
  "use strict";
176
176
  init_esm_shims();
177
- module.exports = function isExtglob(str2) {
178
- if (typeof str2 !== "string" || str2 === "") {
177
+ module.exports = function isExtglob(str3) {
178
+ if (typeof str3 !== "string" || str3 === "") {
179
179
  return false;
180
180
  }
181
181
  var match;
182
- while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str2)) {
182
+ while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str3)) {
183
183
  if (match[2]) return true;
184
- str2 = str2.slice(match.index + match[0].length);
184
+ str3 = str3.slice(match.index + match[0].length);
185
185
  }
186
186
  return false;
187
187
  };
@@ -195,8 +195,8 @@ var require_is_glob = __commonJS({
195
195
  init_esm_shims();
196
196
  var isExtglob = require_is_extglob();
197
197
  var chars = { "{": "}", "(": ")", "[": "]" };
198
- var strictCheck = function(str2) {
199
- if (str2[0] === "!") {
198
+ var strictCheck = function(str3) {
199
+ if (str3[0] === "!") {
200
200
  return true;
201
201
  }
202
202
  var index = 0;
@@ -205,70 +205,70 @@ var require_is_glob = __commonJS({
205
205
  var closeCurlyIndex = -2;
206
206
  var closeParenIndex = -2;
207
207
  var backSlashIndex = -2;
208
- while (index < str2.length) {
209
- if (str2[index] === "*") {
208
+ while (index < str3.length) {
209
+ if (str3[index] === "*") {
210
210
  return true;
211
211
  }
212
- if (str2[index + 1] === "?" && /[\].+)]/.test(str2[index])) {
212
+ if (str3[index + 1] === "?" && /[\].+)]/.test(str3[index])) {
213
213
  return true;
214
214
  }
215
- if (closeSquareIndex !== -1 && str2[index] === "[" && str2[index + 1] !== "]") {
215
+ if (closeSquareIndex !== -1 && str3[index] === "[" && str3[index + 1] !== "]") {
216
216
  if (closeSquareIndex < index) {
217
- closeSquareIndex = str2.indexOf("]", index);
217
+ closeSquareIndex = str3.indexOf("]", index);
218
218
  }
219
219
  if (closeSquareIndex > index) {
220
220
  if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
221
221
  return true;
222
222
  }
223
- backSlashIndex = str2.indexOf("\\", index);
223
+ backSlashIndex = str3.indexOf("\\", index);
224
224
  if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
225
225
  return true;
226
226
  }
227
227
  }
228
228
  }
229
- if (closeCurlyIndex !== -1 && str2[index] === "{" && str2[index + 1] !== "}") {
230
- closeCurlyIndex = str2.indexOf("}", index);
229
+ if (closeCurlyIndex !== -1 && str3[index] === "{" && str3[index + 1] !== "}") {
230
+ closeCurlyIndex = str3.indexOf("}", index);
231
231
  if (closeCurlyIndex > index) {
232
- backSlashIndex = str2.indexOf("\\", index);
232
+ backSlashIndex = str3.indexOf("\\", index);
233
233
  if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) {
234
234
  return true;
235
235
  }
236
236
  }
237
237
  }
238
- if (closeParenIndex !== -1 && str2[index] === "(" && str2[index + 1] === "?" && /[:!=]/.test(str2[index + 2]) && str2[index + 3] !== ")") {
239
- closeParenIndex = str2.indexOf(")", index);
238
+ if (closeParenIndex !== -1 && str3[index] === "(" && str3[index + 1] === "?" && /[:!=]/.test(str3[index + 2]) && str3[index + 3] !== ")") {
239
+ closeParenIndex = str3.indexOf(")", index);
240
240
  if (closeParenIndex > index) {
241
- backSlashIndex = str2.indexOf("\\", index);
241
+ backSlashIndex = str3.indexOf("\\", index);
242
242
  if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
243
243
  return true;
244
244
  }
245
245
  }
246
246
  }
247
- if (pipeIndex !== -1 && str2[index] === "(" && str2[index + 1] !== "|") {
247
+ if (pipeIndex !== -1 && str3[index] === "(" && str3[index + 1] !== "|") {
248
248
  if (pipeIndex < index) {
249
- pipeIndex = str2.indexOf("|", index);
249
+ pipeIndex = str3.indexOf("|", index);
250
250
  }
251
- if (pipeIndex !== -1 && str2[pipeIndex + 1] !== ")") {
252
- closeParenIndex = str2.indexOf(")", pipeIndex);
251
+ if (pipeIndex !== -1 && str3[pipeIndex + 1] !== ")") {
252
+ closeParenIndex = str3.indexOf(")", pipeIndex);
253
253
  if (closeParenIndex > pipeIndex) {
254
- backSlashIndex = str2.indexOf("\\", pipeIndex);
254
+ backSlashIndex = str3.indexOf("\\", pipeIndex);
255
255
  if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
256
256
  return true;
257
257
  }
258
258
  }
259
259
  }
260
260
  }
261
- if (str2[index] === "\\") {
262
- var open4 = str2[index + 1];
261
+ if (str3[index] === "\\") {
262
+ var open4 = str3[index + 1];
263
263
  index += 2;
264
264
  var close = chars[open4];
265
265
  if (close) {
266
- var n = str2.indexOf(close, index);
266
+ var n = str3.indexOf(close, index);
267
267
  if (n !== -1) {
268
268
  index = n + 1;
269
269
  }
270
270
  }
271
- if (str2[index] === "!") {
271
+ if (str3[index] === "!") {
272
272
  return true;
273
273
  }
274
274
  } else {
@@ -277,26 +277,26 @@ var require_is_glob = __commonJS({
277
277
  }
278
278
  return false;
279
279
  };
280
- var relaxedCheck = function(str2) {
281
- if (str2[0] === "!") {
280
+ var relaxedCheck = function(str3) {
281
+ if (str3[0] === "!") {
282
282
  return true;
283
283
  }
284
284
  var index = 0;
285
- while (index < str2.length) {
286
- if (/[*?{}()[\]]/.test(str2[index])) {
285
+ while (index < str3.length) {
286
+ if (/[*?{}()[\]]/.test(str3[index])) {
287
287
  return true;
288
288
  }
289
- if (str2[index] === "\\") {
290
- var open4 = str2[index + 1];
289
+ if (str3[index] === "\\") {
290
+ var open4 = str3[index + 1];
291
291
  index += 2;
292
292
  var close = chars[open4];
293
293
  if (close) {
294
- var n = str2.indexOf(close, index);
294
+ var n = str3.indexOf(close, index);
295
295
  if (n !== -1) {
296
296
  index = n + 1;
297
297
  }
298
298
  }
299
- if (str2[index] === "!") {
299
+ if (str3[index] === "!") {
300
300
  return true;
301
301
  }
302
302
  } else {
@@ -305,18 +305,18 @@ var require_is_glob = __commonJS({
305
305
  }
306
306
  return false;
307
307
  };
308
- module.exports = function isGlob(str2, options) {
309
- if (typeof str2 !== "string" || str2 === "") {
308
+ module.exports = function isGlob(str3, options) {
309
+ if (typeof str3 !== "string" || str3 === "") {
310
310
  return false;
311
311
  }
312
- if (isExtglob(str2)) {
312
+ if (isExtglob(str3)) {
313
313
  return true;
314
314
  }
315
315
  var check2 = strictCheck;
316
316
  if (options && options.strict === false) {
317
317
  check2 = relaxedCheck;
318
318
  }
319
- return check2(str2);
319
+ return check2(str3);
320
320
  };
321
321
  }
322
322
  });
@@ -334,19 +334,19 @@ var require_glob_parent = __commonJS({
334
334
  var enclosure = /[\{\[].*[\}\]]$/;
335
335
  var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
336
336
  var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
337
- module.exports = function globParent(str2, opts) {
337
+ module.exports = function globParent(str3, opts) {
338
338
  var options = Object.assign({ flipBackslashes: true }, opts);
339
- if (options.flipBackslashes && isWin32 && str2.indexOf(slash) < 0) {
340
- str2 = str2.replace(backslash, slash);
339
+ if (options.flipBackslashes && isWin32 && str3.indexOf(slash) < 0) {
340
+ str3 = str3.replace(backslash, slash);
341
341
  }
342
- if (enclosure.test(str2)) {
343
- str2 += slash;
342
+ if (enclosure.test(str3)) {
343
+ str3 += slash;
344
344
  }
345
- str2 += "a";
345
+ str3 += "a";
346
346
  do {
347
- str2 = pathPosixDirname(str2);
348
- } while (isGlob(str2) || globby.test(str2));
349
- return str2.replace(escaped, "$1");
347
+ str3 = pathPosixDirname(str3);
348
+ } while (isGlob(str3) || globby.test(str3));
349
+ return str3.replace(escaped, "$1");
350
350
  };
351
351
  }
352
352
  });
@@ -668,8 +668,8 @@ var require_to_regex_range = __commonJS({
668
668
  function toCharacterClass(a, b, options) {
669
669
  return `[${a}${b - a === 1 ? "" : "-"}${b}]`;
670
670
  }
671
- function hasPadding(str2) {
672
- return /^-?(0+)\d/.test(str2);
671
+ function hasPadding(str3) {
672
+ return /^-?(0+)\d/.test(str3);
673
673
  }
674
674
  function padZeros(value, tok2, options) {
675
675
  if (!tok2.isPadded) {
@@ -1659,12 +1659,12 @@ var require_utils2 = __commonJS({
1659
1659
  REGEX_SPECIAL_CHARS_GLOBAL
1660
1660
  } = require_constants2();
1661
1661
  exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
1662
- exports.hasRegexChars = (str2) => REGEX_SPECIAL_CHARS.test(str2);
1663
- exports.isRegexChar = (str2) => str2.length === 1 && exports.hasRegexChars(str2);
1664
- exports.escapeRegex = (str2) => str2.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
1665
- exports.toPosixSlashes = (str2) => str2.replace(REGEX_BACKSLASH, "/");
1666
- exports.removeBackslashes = (str2) => {
1667
- return str2.replace(REGEX_REMOVE_BACKSLASH, (match) => {
1662
+ exports.hasRegexChars = (str3) => REGEX_SPECIAL_CHARS.test(str3);
1663
+ exports.isRegexChar = (str3) => str3.length === 1 && exports.hasRegexChars(str3);
1664
+ exports.escapeRegex = (str3) => str3.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
1665
+ exports.toPosixSlashes = (str3) => str3.replace(REGEX_BACKSLASH, "/");
1666
+ exports.removeBackslashes = (str3) => {
1667
+ return str3.replace(REGEX_REMOVE_BACKSLASH, (match) => {
1668
1668
  return match === "\\" ? "" : match;
1669
1669
  });
1670
1670
  };
@@ -1760,7 +1760,7 @@ var require_scan = __commonJS({
1760
1760
  const slashes = [];
1761
1761
  const tokens = [];
1762
1762
  const parts = [];
1763
- let str2 = input2;
1763
+ let str3 = input2;
1764
1764
  let index = -1;
1765
1765
  let start = 0;
1766
1766
  let lastIndex = 0;
@@ -1779,10 +1779,10 @@ var require_scan = __commonJS({
1779
1779
  let code;
1780
1780
  let token = { value: "", depth: 0, isGlob: false };
1781
1781
  const eos = () => index >= length;
1782
- const peek = () => str2.charCodeAt(index + 1);
1782
+ const peek = () => str3.charCodeAt(index + 1);
1783
1783
  const advance = () => {
1784
1784
  prev = code;
1785
- return str2.charCodeAt(++index);
1785
+ return str3.charCodeAt(++index);
1786
1786
  };
1787
1787
  while (index < length) {
1788
1788
  code = advance();
@@ -1950,24 +1950,24 @@ var require_scan = __commonJS({
1950
1950
  isExtglob = false;
1951
1951
  isGlob = false;
1952
1952
  }
1953
- let base = str2;
1953
+ let base = str3;
1954
1954
  let prefix = "";
1955
1955
  let glob = "";
1956
1956
  if (start > 0) {
1957
- prefix = str2.slice(0, start);
1958
- str2 = str2.slice(start);
1957
+ prefix = str3.slice(0, start);
1958
+ str3 = str3.slice(start);
1959
1959
  lastIndex -= start;
1960
1960
  }
1961
1961
  if (base && isGlob === true && lastIndex > 0) {
1962
- base = str2.slice(0, lastIndex);
1963
- glob = str2.slice(lastIndex);
1962
+ base = str3.slice(0, lastIndex);
1963
+ glob = str3.slice(lastIndex);
1964
1964
  } else if (isGlob === true) {
1965
1965
  base = "";
1966
- glob = str2;
1966
+ glob = str3;
1967
1967
  } else {
1968
- base = str2;
1968
+ base = str3;
1969
1969
  }
1970
- if (base && base !== "" && base !== "/" && base !== str2) {
1970
+ if (base && base !== "" && base !== "/" && base !== str3) {
1971
1971
  if (isPathSeparator(base.charCodeAt(base.length - 1))) {
1972
1972
  base = base.slice(0, -1);
1973
1973
  }
@@ -3003,8 +3003,8 @@ var require_parse2 = __commonJS({
3003
3003
  if (opts2.noglobstar === true) return star;
3004
3004
  return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
3005
3005
  };
3006
- const create = (str2) => {
3007
- switch (str2) {
3006
+ const create = (str3) => {
3007
+ switch (str3) {
3008
3008
  case "*":
3009
3009
  return `${nodot}${ONE_CHAR}${star}`;
3010
3010
  case ".*":
@@ -3022,7 +3022,7 @@ var require_parse2 = __commonJS({
3022
3022
  case "**/.*":
3023
3023
  return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
3024
3024
  default: {
3025
- const match = /^(.*?)\.(\w+)$/.exec(str2);
3025
+ const match = /^(.*?)\.(\w+)$/.exec(str3);
3026
3026
  if (!match) return;
3027
3027
  const source2 = create(match[1]);
3028
3028
  if (!source2) return;
@@ -3055,9 +3055,9 @@ var require_picomatch = __commonJS({
3055
3055
  var picomatch = (glob, options, returnState = false) => {
3056
3056
  if (Array.isArray(glob)) {
3057
3057
  const fns = glob.map((input2) => picomatch(input2, options, returnState));
3058
- const arrayMatcher = (str2) => {
3058
+ const arrayMatcher = (str3) => {
3059
3059
  for (const isMatch of fns) {
3060
- const state2 = isMatch(str2);
3060
+ const state2 = isMatch(str3);
3061
3061
  if (state2) return state2;
3062
3062
  }
3063
3063
  return false;
@@ -3133,7 +3133,7 @@ var require_picomatch = __commonJS({
3133
3133
  const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
3134
3134
  return regex.test(path2.basename(input2));
3135
3135
  };
3136
- picomatch.isMatch = (str2, patterns, options) => picomatch(patterns, options)(str2);
3136
+ picomatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
3137
3137
  picomatch.parse = (pattern, options) => {
3138
3138
  if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options));
3139
3139
  return parse4(pattern, { ...options, fastpaths: false });
@@ -3249,7 +3249,7 @@ var require_micromatch = __commonJS({
3249
3249
  };
3250
3250
  micromatch.match = micromatch;
3251
3251
  micromatch.matcher = (pattern, options) => picomatch(pattern, options);
3252
- micromatch.isMatch = (str2, patterns, options) => picomatch(patterns, options)(str2);
3252
+ micromatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
3253
3253
  micromatch.any = micromatch.isMatch;
3254
3254
  micromatch.not = (list, patterns, options = {}) => {
3255
3255
  patterns = [].concat(patterns).map(String);
@@ -3267,22 +3267,22 @@ var require_micromatch = __commonJS({
3267
3267
  }
3268
3268
  return [...result];
3269
3269
  };
3270
- micromatch.contains = (str2, pattern, options) => {
3271
- if (typeof str2 !== "string") {
3272
- throw new TypeError(`Expected a string: "${util.inspect(str2)}"`);
3270
+ micromatch.contains = (str3, pattern, options) => {
3271
+ if (typeof str3 !== "string") {
3272
+ throw new TypeError(`Expected a string: "${util.inspect(str3)}"`);
3273
3273
  }
3274
3274
  if (Array.isArray(pattern)) {
3275
- return pattern.some((p) => micromatch.contains(str2, p, options));
3275
+ return pattern.some((p) => micromatch.contains(str3, p, options));
3276
3276
  }
3277
3277
  if (typeof pattern === "string") {
3278
- if (isEmptyString(str2) || isEmptyString(pattern)) {
3278
+ if (isEmptyString(str3) || isEmptyString(pattern)) {
3279
3279
  return false;
3280
3280
  }
3281
- if (str2.includes(pattern) || str2.startsWith("./") && str2.slice(2).includes(pattern)) {
3281
+ if (str3.includes(pattern) || str3.startsWith("./") && str3.slice(2).includes(pattern)) {
3282
3282
  return true;
3283
3283
  }
3284
3284
  }
3285
- return micromatch.isMatch(str2, pattern, { ...options, contains: true });
3285
+ return micromatch.isMatch(str3, pattern, { ...options, contains: true });
3286
3286
  };
3287
3287
  micromatch.matchKeys = (obj, patterns, options) => {
3288
3288
  if (!utils.isObject(obj)) {
@@ -3313,11 +3313,11 @@ var require_micromatch = __commonJS({
3313
3313
  }
3314
3314
  return true;
3315
3315
  };
3316
- micromatch.all = (str2, patterns, options) => {
3317
- if (typeof str2 !== "string") {
3318
- throw new TypeError(`Expected a string: "${util.inspect(str2)}"`);
3316
+ micromatch.all = (str3, patterns, options) => {
3317
+ if (typeof str3 !== "string") {
3318
+ throw new TypeError(`Expected a string: "${util.inspect(str3)}"`);
3319
3319
  }
3320
- return [].concat(patterns).every((p) => picomatch(p, options)(str2));
3320
+ return [].concat(patterns).every((p) => picomatch(p, options)(str3));
3321
3321
  };
3322
3322
  micromatch.capture = (glob, input2, options) => {
3323
3323
  let posix = utils.isWindows(options);
@@ -3332,8 +3332,8 @@ var require_micromatch = __commonJS({
3332
3332
  micromatch.parse = (patterns, options) => {
3333
3333
  let res = [];
3334
3334
  for (let pattern of [].concat(patterns || [])) {
3335
- for (let str2 of braces(String(pattern), options)) {
3336
- res.push(picomatch.parse(str2, options));
3335
+ for (let str3 of braces(String(pattern), options)) {
3336
+ res.push(picomatch.parse(str3, options));
3337
3337
  }
3338
3338
  }
3339
3339
  return res;
@@ -5873,246 +5873,8 @@ var require_out4 = __commonJS({
5873
5873
  // src/hook.ts
5874
5874
  init_esm_shims();
5875
5875
 
5876
- // src/hooks/index.ts
5877
- init_esm_shims();
5878
-
5879
- // src/hooks/capture.ts
5880
- init_esm_shims();
5881
-
5882
- // src/capture/classify.ts
5883
- init_esm_shims();
5884
- import { createHash } from "crypto";
5885
- import { realpathSync } from "fs";
5886
- import { isAbsolute, relative } from "path";
5887
-
5888
- // src/capture/categories.ts
5876
+ // src/hooks/dispatch.ts
5889
5877
  init_esm_shims();
5890
- var CATEGORIES = {
5891
- // P1
5892
- files: 1,
5893
- tasks: 1,
5894
- plans: 1,
5895
- rules: 1,
5896
- user_prompt: 1,
5897
- // P2
5898
- decision: 2,
5899
- git: 2,
5900
- error: 2,
5901
- error_resolution: 2,
5902
- constraint: 2,
5903
- blocker: 2,
5904
- rejected: 2,
5905
- environment: 2,
5906
- agent_findings: 2,
5907
- iteration_loop: 2,
5908
- assistant_summary: 2,
5909
- // P3
5910
- latency: 3,
5911
- mcp_tool: 3,
5912
- subagent: 3,
5913
- skill: 3,
5914
- external_ref: 3,
5915
- role: 3,
5916
- // P4
5917
- intent: 4,
5918
- data: 4,
5919
- tool_call: 4,
5920
- session_start: 4,
5921
- /** A proactive recall decision (fired or quiet) and what it showed; never in the guide. */
5922
- recall: 4,
5923
- /** A routing decision (allow / annotate / deny) for one native call; never in the guide. */
5924
- routing: 4
5925
- };
5926
- var GUIDE_ORDER = [
5927
- { title: "Last Request", categories: ["user_prompt"] },
5928
- { title: "Tasks", categories: ["tasks"] },
5929
- { title: "Plans", categories: ["plans"] },
5930
- { title: "Key Decisions", categories: ["decision"] },
5931
- { title: "Files Modified", categories: ["files"] },
5932
- { title: "Unresolved Errors", categories: ["error"] },
5933
- { title: "Resolved", categories: ["error_resolution"] },
5934
- { title: "Constraints", categories: ["constraint"] },
5935
- { title: "Blockers", categories: ["blocker"] },
5936
- { title: "Git", categories: ["git"] },
5937
- { title: "Project Rules", categories: ["rules"] },
5938
- { title: "Loops", categories: ["iteration_loop"] },
5939
- { title: "Last Reply", categories: ["assistant_summary"] },
5940
- { title: "MCP Tools Used", categories: ["mcp_tool"] },
5941
- { title: "Subagent Tasks", categories: ["subagent", "agent_findings"] },
5942
- { title: "Skills", categories: ["skill"] },
5943
- { title: "Rejected Approaches", categories: ["rejected"] },
5944
- { title: "External Refs", categories: ["external_ref"] },
5945
- { title: "Environment", categories: ["environment"] },
5946
- { title: "Slow Calls", categories: ["latency"] },
5947
- { title: "Data refs", categories: ["data"] },
5948
- { title: "Intent", categories: ["intent"] }
5949
- ];
5950
-
5951
- // src/capture/classify.ts
5952
- function relPath(root, fp) {
5953
- const tryRel = (base, p) => {
5954
- const r = relative(base, p);
5955
- return r && !r.startsWith("..") && !isAbsolute(r) ? r : void 0;
5956
- };
5957
- const direct = tryRel(root, fp);
5958
- if (direct) return direct;
5959
- try {
5960
- const real = tryRel(realpathSync(root), realpathSync(fp));
5961
- if (real) return real;
5962
- } catch {
5963
- }
5964
- return fp;
5965
- }
5966
- function ev(category, payload, tool) {
5967
- return { category, payload, tool };
5968
- }
5969
- function withPriority(sessionId, drafts) {
5970
- return drafts.map((d) => ({ ...d, sessionId, priority: CATEGORIES[d.category] }));
5971
- }
5972
- var clip = (s, n) => {
5973
- const str2 = typeof s === "string" ? s : JSON.stringify(s ?? "");
5974
- return str2.length > n ? `${str2.slice(0, n - 1)}\u2026` : str2;
5975
- };
5976
- var DECISION = /\b(let'?s go with|we'?ll use|decided|decision:|go with|use (?:the )?\w+ instead|stick with|switch(?:ing)? to|chose|prefer(?:red)?)\b/i;
5977
- var CONSTRAINT = /\b(must(?: not)?|never|always|only|do not|don'?t|should not|required?|hard requirement|no\s+\w+ allowed)\b/i;
5978
- var REJECTED = /\b(instead of|rather than|not \w+ing|don'?t use|drop(?:ped)?|revert(?:ed)?|abandon(?:ed)?|scrap(?:ped)?)\b/i;
5979
- var BLOCKER = /\b(blocked|blocker|can'?t (?:proceed|continue)|waiting (?:on|for)|need(?:s)? (?:access|permission|approval))\b/i;
5980
- var RESOLVED = /\b(fixed|resolved|now passes|passing now|works now|solved|green)\b/i;
5981
- var INTENT = /^\s*(?:(?:please|can you|could you|let'?s|i want to|i'?d like to|help me)\s+)?(\w+(?:\s+\w+){0,5})/i;
5982
- function classifyPrompt(prompt) {
5983
- const out = [ev("user_prompt", { text: clip(prompt, 600) })];
5984
- const first = prompt.split("\n").find((l) => l.trim()) ?? "";
5985
- if (DECISION.test(prompt))
5986
- out.push(ev("decision", { text: clip(firstMatchLine(prompt, DECISION), 300), by: "user" }));
5987
- if (CONSTRAINT.test(prompt) && prompt.length < 2e3)
5988
- out.push(ev("constraint", { text: clip(firstMatchLine(prompt, CONSTRAINT), 300) }));
5989
- if (REJECTED.test(prompt))
5990
- out.push(ev("rejected", { text: clip(firstMatchLine(prompt, REJECTED), 300) }));
5991
- if (BLOCKER.test(prompt))
5992
- out.push(ev("blocker", { text: clip(firstMatchLine(prompt, BLOCKER), 300) }));
5993
- const m = INTENT.exec(first);
5994
- if (m?.[1]) out.push(ev("intent", { text: clip(m[1], 80) }));
5995
- return out;
5996
- }
5997
- function firstMatchLine(text, re) {
5998
- return text.split("\n").find((l) => re.test(l)) ?? text;
5999
- }
6000
- var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
6001
- var GIT_OP = /^\s*git\s+(commit|checkout|switch|merge|rebase|push|pull|stash|reset|revert|cherry-pick|tag|branch\s+-[dD])/;
6002
- function callKey(toolName, toolInput) {
6003
- return `${toolName}:${createHash("sha1").update(JSON.stringify(toolInput)).digest("hex").slice(0, 12)}`;
6004
- }
6005
- function responseText(r) {
6006
- if (typeof r === "string") return r;
6007
- if (r && typeof r === "object") {
6008
- const o = r;
6009
- if (typeof o.stdout === "string")
6010
- return `${o.stdout}
6011
- ${typeof o.stderr === "string" ? o.stderr : ""}`;
6012
- if (Array.isArray(o.content)) {
6013
- return o.content.map(
6014
- (c) => c && typeof c === "object" && typeof c.text === "string" ? c.text : ""
6015
- ).join("\n");
6016
- }
6017
- if (typeof o.error === "string") return o.error;
6018
- }
6019
- return "";
6020
- }
6021
- function responseFailed(r) {
6022
- if (!r || typeof r !== "object") return void 0;
6023
- const o = r;
6024
- if (o.is_error === true || o.isError === true) return clip(responseText(r), 400);
6025
- if (typeof o.exit_code === "number" && o.exit_code !== 0) return clip(responseText(r), 400);
6026
- if (typeof o.interrupted === "boolean" && o.interrupted) return "interrupted";
6027
- return void 0;
6028
- }
6029
- function classifyToolCall(info) {
6030
- const { toolName, toolInput, root } = info;
6031
- const out = [];
6032
- const key = callKey(toolName, toolInput);
6033
- out.push(ev("tool_call", { key, summary: summarizeInput(toolName, toolInput) }, toolName));
6034
- if (EDIT_TOOLS.has(toolName)) {
6035
- const fp = String(toolInput.file_path ?? toolInput.notebook_path ?? "");
6036
- if (fp) {
6037
- const rel = relPath(root, fp);
6038
- if (/\.claude\/plans\/|\/plans\/.*\.md$/.test(fp))
6039
- out.push(ev("plans", { path: rel, action: toolName }, toolName));
6040
- else out.push(ev("files", { path: rel, action: toolName }, toolName));
6041
- if (/(^|\/)(CLAUDE|AGENTS)\.md$|\.claude\/settings/.test(fp))
6042
- out.push(ev("rules", { path: rel, action: toolName }, toolName));
6043
- }
6044
- }
6045
- if (toolName === "Bash") {
6046
- const cmd = String(toolInput.command ?? "");
6047
- if (GIT_OP.test(cmd)) out.push(ev("git", { command: clip(cmd, 200) }, toolName));
6048
- }
6049
- if (toolName === "TodoWrite" && Array.isArray(toolInput.todos)) {
6050
- const todos = toolInput.todos;
6051
- out.push(
6052
- ev(
6053
- "tasks",
6054
- {
6055
- todos: todos.slice(0, 12).map(
6056
- (t) => `${t.status === "completed" ? "x" : t.status === "in_progress" ? ">" : " "} ${clip(t.content, 80)}`
6057
- )
6058
- },
6059
- toolName
6060
- )
6061
- );
6062
- }
6063
- if (toolName === "ExitPlanMode") out.push(ev("plans", { action: "plan approved" }, toolName));
6064
- if (toolName === "Agent" || toolName === "Task") {
6065
- out.push(
6066
- ev(
6067
- "subagent",
6068
- { description: clip(toolInput.description, 120), type: toolInput.subagent_type },
6069
- toolName
6070
- )
6071
- );
6072
- }
6073
- if (toolName === "Skill") out.push(ev("skill", { skill: clip(toolInput.skill, 80) }, toolName));
6074
- if (toolName === "WebFetch" || toolName === "WebSearch") {
6075
- out.push(ev("external_ref", { url: clip(toolInput.url ?? toolInput.query, 200) }, toolName));
6076
- }
6077
- if (toolName.startsWith("mcp__")) out.push(ev("mcp_tool", { tool: toolName }, toolName));
6078
- if (info.durationMs && info.durationMs > 5e3) {
6079
- out.push(
6080
- ev(
6081
- "latency",
6082
- { ms: info.durationMs, summary: summarizeInput(toolName, toolInput) },
6083
- toolName
6084
- )
6085
- );
6086
- }
6087
- const failure2 = info.error ?? responseFailed(info.toolResponse);
6088
- if (failure2)
6089
- out.push(
6090
- ev(
6091
- "error",
6092
- { tool: toolName, summary: summarizeInput(toolName, toolInput), error: clip(failure2, 400) },
6093
- toolName
6094
- )
6095
- );
6096
- return out;
6097
- }
6098
- function summarizeInput(toolName, input2) {
6099
- const pick2 = input2.command ?? input2.file_path ?? input2.pattern ?? input2.url ?? input2.prompt ?? input2.query ?? input2.description ?? input2.skill;
6100
- return clip(`${toolName} ${pick2 ?? ""}`.trim(), 160);
6101
- }
6102
- function classifyAssistant(message) {
6103
- const out = [];
6104
- if (!message.trim()) return out;
6105
- out.push(ev("assistant_summary", { text: clip(message.replace(/\s+/g, " "), 500) }));
6106
- if (DECISION.test(message))
6107
- out.push(
6108
- ev("decision", { text: clip(firstMatchLine(message, DECISION), 300), by: "assistant" })
6109
- );
6110
- if (RESOLVED.test(message))
6111
- out.push(ev("error_resolution", { text: clip(firstMatchLine(message, RESOLVED), 300) }));
6112
- if (BLOCKER.test(message))
6113
- out.push(ev("blocker", { text: clip(firstMatchLine(message, BLOCKER), 300), by: "assistant" }));
6114
- return out;
6115
- }
6116
5878
 
6117
5879
  // src/context.ts
6118
5880
  init_esm_shims();
@@ -6966,14 +6728,14 @@ function promiseAllObject(promisesObj) {
6966
6728
  }
6967
6729
  function randomString(length = 10) {
6968
6730
  const chars = "abcdefghijklmnopqrstuvwxyz";
6969
- let str2 = "";
6731
+ let str3 = "";
6970
6732
  for (let i = 0; i < length; i++) {
6971
- str2 += chars[Math.floor(Math.random() * chars.length)];
6733
+ str3 += chars[Math.floor(Math.random() * chars.length)];
6972
6734
  }
6973
- return str2;
6735
+ return str3;
6974
6736
  }
6975
- function esc(str2) {
6976
- return JSON.stringify(str2);
6737
+ function esc(str3) {
6738
+ return JSON.stringify(str3);
6977
6739
  }
6978
6740
  function slugify(input2) {
6979
6741
  return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -7087,8 +6849,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
7087
6849
  "symbol",
7088
6850
  "undefined"
7089
6851
  ]);
7090
- function escapeRegex(str2) {
7091
- return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6852
+ function escapeRegex(str3) {
6853
+ return str3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7092
6854
  }
7093
6855
  function clone(inst, def, params) {
7094
6856
  const cl = new inst._zod.constr(def ?? inst._zod.def);
@@ -7354,13 +7116,13 @@ function getSizableOrigin(input2) {
7354
7116
  return "unknown";
7355
7117
  }
7356
7118
  var highSurrogate = /[\uD800-\uDBFF]/;
7357
- function codePointLength(str2) {
7358
- const units = str2.length;
7359
- if (!highSurrogate.test(str2))
7119
+ function codePointLength(str3) {
7120
+ const units = str3.length;
7121
+ if (!highSurrogate.test(str3))
7360
7122
  return units;
7361
7123
  let count = units;
7362
7124
  for (let i = 0; i < units - 1; i++) {
7363
- if ((str2.charCodeAt(i) & 64512) === 55296 && (str2.charCodeAt(i + 1) & 64512) === 56320) {
7125
+ if ((str3.charCodeAt(i) & 64512) === 55296 && (str3.charCodeAt(i + 1) & 64512) === 56320) {
7364
7126
  count--;
7365
7127
  i++;
7366
7128
  }
@@ -26219,8 +25981,8 @@ function loadConfig(projectRoot) {
26219
25981
 
26220
25982
  // src/project.ts
26221
25983
  init_esm_shims();
26222
- import { createHash as createHash2 } from "crypto";
26223
- import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
25984
+ import { createHash } from "crypto";
25985
+ import { existsSync as existsSync2, realpathSync } from "fs";
26224
25986
  import { dirname, join as join3, resolve } from "path";
26225
25987
  var ROOT_MARKERS = [
26226
25988
  ".git",
@@ -26245,13 +26007,13 @@ function resolveProjectRoot(hint) {
26245
26007
  const candidate = hint || process.env.EMINIAR_PROJECT_ROOT || process.env.CLAUDE_PROJECT_DIR || process.cwd();
26246
26008
  const root = findProjectRoot(candidate);
26247
26009
  try {
26248
- return realpathSync2(root);
26010
+ return realpathSync(root);
26249
26011
  } catch {
26250
26012
  return root;
26251
26013
  }
26252
26014
  }
26253
26015
  function projectId(root) {
26254
- return createHash2("sha256").update(root).digest("hex").slice(0, 16);
26016
+ return createHash("sha256").update(root).digest("hex").slice(0, 16);
26255
26017
  }
26256
26018
 
26257
26019
  // src/context.ts
@@ -26267,25 +26029,459 @@ function getContext(rootHint) {
26267
26029
  return ctx;
26268
26030
  }
26269
26031
 
26270
- // src/index/indexer.ts
26271
- init_esm_shims();
26272
- var import_fast_glob = __toESM(require_out4(), 1);
26273
- import { readFileSync as readFileSync2, statSync } from "fs";
26274
- import { relative as relative2, resolve as resolve2 } from "path";
26275
-
26276
- // src/chunker/index.ts
26277
- init_esm_shims();
26278
- import { extname } from "path";
26279
-
26280
- // src/chunker/code.ts
26281
- init_esm_shims();
26282
-
26283
- // src/chunker/types.ts
26032
+ // src/hooks/cursor.ts
26284
26033
  init_esm_shims();
26285
- import { createHash as createHash3 } from "crypto";
26286
- var DEFAULT_CHUNKER = { maxChars: 1800, overlapLines: 5 };
26287
- function hashText(s) {
26288
- return createHash3("sha1").update(s).digest("hex");
26034
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
26035
+ import { homedir as homedir2 } from "os";
26036
+ import { join as join4 } from "path";
26037
+ var CURSOR_EVENTS = {
26038
+ sessionStart: "SessionStart",
26039
+ beforeSubmitPrompt: "UserPromptSubmit",
26040
+ preToolUse: "PreToolUse",
26041
+ postToolUse: "PostToolUse",
26042
+ postToolUseFailure: "PostToolUseFailure",
26043
+ subagentStop: "SubagentStop",
26044
+ preCompact: "PreCompact",
26045
+ // afterAgentResponse carries the assistant text that Claude Code's Stop event carries.
26046
+ afterAgentResponse: "Stop",
26047
+ stop: "Stop",
26048
+ afterFileEdit: "PostToolUse",
26049
+ sessionEnd: "SessionEnd"
26050
+ };
26051
+ var TOOL_NAMES = { Shell: "Bash" };
26052
+ var PENDING_TTL_MS = 10 * 6e4;
26053
+ var PENDING_MAX = 8e3;
26054
+ function cursorDir() {
26055
+ return process.env.CURSOR_CONFIG_DIR || join4(homedir2(), ".cursor");
26056
+ }
26057
+ function detectClient(input2, argv) {
26058
+ const i = argv.indexOf("--client");
26059
+ if (i !== -1 && argv[i + 1]) return argv[i + 1] === "cursor" ? "cursor" : "claude";
26060
+ const ev3 = typeof input2.hook_event_name === "string" ? input2.hook_event_name : "";
26061
+ if (typeof input2.cursor_version === "string" || Array.isArray(input2.workspace_roots) || typeof input2.conversation_id === "string" && !input2.session_id || ev3 in CURSOR_EVENTS)
26062
+ return "cursor";
26063
+ return "claude";
26064
+ }
26065
+ var str = (v) => typeof v === "string" && v.length ? v : void 0;
26066
+ function mapToolName(name) {
26067
+ if (name in TOOL_NAMES) return TOOL_NAMES[name];
26068
+ const mcp = /^MCP:(.+)$/.exec(name);
26069
+ if (mcp) return `mcp__cursor__${mcp[1]}`;
26070
+ return name;
26071
+ }
26072
+ function toolInputOf(v) {
26073
+ let obj = v;
26074
+ if (typeof obj === "string") {
26075
+ try {
26076
+ obj = JSON.parse(obj);
26077
+ } catch {
26078
+ return void 0;
26079
+ }
26080
+ }
26081
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return void 0;
26082
+ const input2 = { ...obj };
26083
+ if (typeof input2.path === "string" && input2.file_path === void 0) input2.file_path = input2.path;
26084
+ return input2;
26085
+ }
26086
+ function toolResponseOf(v) {
26087
+ if (typeof v !== "string") return v;
26088
+ try {
26089
+ return JSON.parse(v);
26090
+ } catch {
26091
+ return v;
26092
+ }
26093
+ }
26094
+ function fromCursor(raw) {
26095
+ const event = str(raw.hook_event_name) ?? "";
26096
+ const roots = Array.isArray(raw.workspace_roots) ? raw.workspace_roots.filter((r) => typeof r === "string") : [];
26097
+ const cwd = str(raw.cwd) ?? roots[0] ?? process.env.CURSOR_PROJECT_DIR ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
26098
+ const input2 = {
26099
+ session_id: str(raw.session_id) ?? str(raw.conversation_id) ?? process.env.EMINIAR_SESSION_ID ?? "cursor",
26100
+ cwd,
26101
+ hook_event_name: CURSOR_EVENTS[event] ?? event
26102
+ };
26103
+ const transcript = str(raw.transcript_path);
26104
+ if (transcript) input2.transcript_path = transcript;
26105
+ if (input2.hook_event_name === "SessionStart") input2.source = "startup";
26106
+ const prompt = str(raw.prompt);
26107
+ if (prompt) input2.prompt = prompt;
26108
+ if (event === "afterFileEdit") {
26109
+ input2.tool_name = "Edit";
26110
+ const ti = {};
26111
+ if (str(raw.file_path)) ti.file_path = raw.file_path;
26112
+ if (Array.isArray(raw.edits)) ti.edits = raw.edits;
26113
+ input2.tool_input = ti;
26114
+ } else {
26115
+ const tool = str(raw.tool_name);
26116
+ if (tool) input2.tool_name = mapToolName(tool);
26117
+ const ti = toolInputOf(raw.tool_input);
26118
+ if (ti) input2.tool_input = ti;
26119
+ }
26120
+ const useId = str(raw.tool_use_id);
26121
+ if (useId) input2.tool_use_id = useId;
26122
+ if (raw.tool_output !== void 0) input2.tool_response = toolResponseOf(raw.tool_output);
26123
+ const dur = typeof raw.duration === "number" ? raw.duration : raw.duration_ms;
26124
+ if (typeof dur === "number") input2.duration_ms = dur;
26125
+ const err = str(raw.error_message) ?? str(raw.error);
26126
+ if (err) input2.error = err;
26127
+ const text = str(raw.text) ?? str(raw.summary) ?? str(raw.last_assistant_message);
26128
+ if (text) input2.last_assistant_message = text;
26129
+ const agent = str(raw.subagent_type) ?? str(raw.agent_type);
26130
+ if (agent) input2.agent_type = agent;
26131
+ const agentTranscript = str(raw.agent_transcript_path);
26132
+ if (agentTranscript) input2.agent_transcript_path = agentTranscript;
26133
+ if (raw.trigger === "manual" || raw.trigger === "auto") input2.trigger = raw.trigger;
26134
+ const reason = str(raw.reason);
26135
+ if (reason) input2.reason = reason;
26136
+ return input2;
26137
+ }
26138
+ function pendingFile(root, sessionId) {
26139
+ const id = getContext(root).id;
26140
+ return join4(projectDir(id), "pending-context", `${sessionId.replace(/[^\w.-]/g, "_")}.json`);
26141
+ }
26142
+ function readPending(file3) {
26143
+ try {
26144
+ const list = JSON.parse(readFileSync2(file3, "utf8"));
26145
+ return Array.isArray(list) ? list : [];
26146
+ } catch {
26147
+ return [];
26148
+ }
26149
+ }
26150
+ function stashContext(root, sessionId, text) {
26151
+ if (!text.trim()) return;
26152
+ const file3 = pendingFile(root, sessionId);
26153
+ mkdirSync2(join4(file3, ".."), { recursive: true });
26154
+ const now = Date.now();
26155
+ const list = readPending(file3).filter((p) => now - p.t < PENDING_TTL_MS);
26156
+ list.push({ t: now, text });
26157
+ writeFileSync(file3, JSON.stringify(list));
26158
+ }
26159
+ function takeContext(root, sessionId) {
26160
+ const file3 = pendingFile(root, sessionId);
26161
+ if (!existsSync3(file3)) return void 0;
26162
+ const now = Date.now();
26163
+ const list = readPending(file3).filter((p) => now - p.t < PENDING_TTL_MS);
26164
+ rmSync(file3, { force: true });
26165
+ const seen = /* @__PURE__ */ new Set();
26166
+ const texts = list.map((p) => p.text).filter((t) => !seen.has(t) && seen.add(t));
26167
+ const joined = texts.join("\n\n").slice(0, PENDING_MAX);
26168
+ return joined || void 0;
26169
+ }
26170
+ function cursorHasServer() {
26171
+ try {
26172
+ const raw = readFileSync2(join4(cursorDir(), "mcp.json"), "utf8");
26173
+ return raw.includes("em-server");
26174
+ } catch {
26175
+ return false;
26176
+ }
26177
+ }
26178
+ var joinText = (...parts) => {
26179
+ const kept = parts.filter((p) => !!p?.trim());
26180
+ return kept.length ? kept.join("\n\n") : void 0;
26181
+ };
26182
+ function toCursor(event, out, scope) {
26183
+ const specific2 = out?.hookSpecificOutput;
26184
+ const context = specific2?.additionalContext;
26185
+ switch (event) {
26186
+ case "PreToolUse": {
26187
+ if (specific2?.permissionDecision === "deny") {
26188
+ if (!cursorHasServer()) {
26189
+ stashContext(
26190
+ scope.root,
26191
+ scope.sessionId,
26192
+ `${specific2.permissionDecisionReason ?? ""} (eminiar MCP server is not registered with Cursor, so the call was allowed; run \`npx eminiar install --cursor\`)`.trim()
26193
+ );
26194
+ return { permission: "allow" };
26195
+ }
26196
+ const reason = joinText(specific2.permissionDecisionReason, context) ?? "eminiar: denied";
26197
+ return { permission: "deny", user_message: reason, agent_message: reason };
26198
+ }
26199
+ const res = { permission: "allow" };
26200
+ if (specific2?.updatedInput) res.updated_input = specific2.updatedInput;
26201
+ if (context) stashContext(scope.root, scope.sessionId, context);
26202
+ return res;
26203
+ }
26204
+ case "PostToolUse":
26205
+ case "PostToolUseFailure": {
26206
+ const text = joinText(takeContext(scope.root, scope.sessionId), context);
26207
+ return text ? { additional_context: text } : {};
26208
+ }
26209
+ case "SessionStart": {
26210
+ const res = {
26211
+ env: { EMINIAR_SESSION_ID: scope.sessionId, EMINIAR_PROJECT_ROOT: scope.root }
26212
+ };
26213
+ if (context) res.additional_context = context;
26214
+ return res;
26215
+ }
26216
+ case "UserPromptSubmit": {
26217
+ if (context) stashContext(scope.root, scope.sessionId, context);
26218
+ return { continue: true };
26219
+ }
26220
+ default:
26221
+ return {};
26222
+ }
26223
+ }
26224
+
26225
+ // src/hooks/index.ts
26226
+ init_esm_shims();
26227
+
26228
+ // src/hooks/capture.ts
26229
+ init_esm_shims();
26230
+
26231
+ // src/capture/classify.ts
26232
+ init_esm_shims();
26233
+ import { createHash as createHash2 } from "crypto";
26234
+ import { realpathSync as realpathSync2 } from "fs";
26235
+ import { isAbsolute, relative } from "path";
26236
+
26237
+ // src/capture/categories.ts
26238
+ init_esm_shims();
26239
+ var CATEGORIES = {
26240
+ // P1
26241
+ files: 1,
26242
+ tasks: 1,
26243
+ plans: 1,
26244
+ rules: 1,
26245
+ user_prompt: 1,
26246
+ // P2
26247
+ decision: 2,
26248
+ git: 2,
26249
+ error: 2,
26250
+ error_resolution: 2,
26251
+ constraint: 2,
26252
+ blocker: 2,
26253
+ rejected: 2,
26254
+ environment: 2,
26255
+ agent_findings: 2,
26256
+ iteration_loop: 2,
26257
+ assistant_summary: 2,
26258
+ // P3
26259
+ latency: 3,
26260
+ mcp_tool: 3,
26261
+ subagent: 3,
26262
+ skill: 3,
26263
+ external_ref: 3,
26264
+ role: 3,
26265
+ // P4
26266
+ intent: 4,
26267
+ data: 4,
26268
+ tool_call: 4,
26269
+ session_start: 4,
26270
+ /** A proactive recall decision (fired or quiet) and what it showed; never in the guide. */
26271
+ recall: 4,
26272
+ /** A routing decision (allow / annotate / deny) for one native call; never in the guide. */
26273
+ routing: 4
26274
+ };
26275
+ var GUIDE_ORDER = [
26276
+ { title: "Last Request", categories: ["user_prompt"] },
26277
+ { title: "Tasks", categories: ["tasks"] },
26278
+ { title: "Plans", categories: ["plans"] },
26279
+ { title: "Key Decisions", categories: ["decision"] },
26280
+ { title: "Files Modified", categories: ["files"] },
26281
+ { title: "Unresolved Errors", categories: ["error"] },
26282
+ { title: "Resolved", categories: ["error_resolution"] },
26283
+ { title: "Constraints", categories: ["constraint"] },
26284
+ { title: "Blockers", categories: ["blocker"] },
26285
+ { title: "Git", categories: ["git"] },
26286
+ { title: "Project Rules", categories: ["rules"] },
26287
+ { title: "Loops", categories: ["iteration_loop"] },
26288
+ { title: "Last Reply", categories: ["assistant_summary"] },
26289
+ { title: "MCP Tools Used", categories: ["mcp_tool"] },
26290
+ { title: "Subagent Tasks", categories: ["subagent", "agent_findings"] },
26291
+ { title: "Skills", categories: ["skill"] },
26292
+ { title: "Rejected Approaches", categories: ["rejected"] },
26293
+ { title: "External Refs", categories: ["external_ref"] },
26294
+ { title: "Environment", categories: ["environment"] },
26295
+ { title: "Slow Calls", categories: ["latency"] },
26296
+ { title: "Data refs", categories: ["data"] },
26297
+ { title: "Intent", categories: ["intent"] }
26298
+ ];
26299
+
26300
+ // src/capture/classify.ts
26301
+ function relPath(root, fp) {
26302
+ const tryRel = (base, p) => {
26303
+ const r = relative(base, p);
26304
+ return r && !r.startsWith("..") && !isAbsolute(r) ? r : void 0;
26305
+ };
26306
+ const direct = tryRel(root, fp);
26307
+ if (direct) return direct;
26308
+ try {
26309
+ const real = tryRel(realpathSync2(root), realpathSync2(fp));
26310
+ if (real) return real;
26311
+ } catch {
26312
+ }
26313
+ return fp;
26314
+ }
26315
+ function ev(category, payload, tool) {
26316
+ return { category, payload, tool };
26317
+ }
26318
+ function withPriority(sessionId, drafts) {
26319
+ return drafts.map((d) => ({ ...d, sessionId, priority: CATEGORIES[d.category] }));
26320
+ }
26321
+ var clip = (s, n) => {
26322
+ const str3 = typeof s === "string" ? s : JSON.stringify(s ?? "");
26323
+ return str3.length > n ? `${str3.slice(0, n - 1)}\u2026` : str3;
26324
+ };
26325
+ var DECISION = /\b(let'?s go with|we'?ll use|decided|decision:|go with|use (?:the )?\w+ instead|stick with|switch(?:ing)? to|chose|prefer(?:red)?)\b/i;
26326
+ var CONSTRAINT = /\b(must(?: not)?|never|always|only|do not|don'?t|should not|required?|hard requirement|no\s+\w+ allowed)\b/i;
26327
+ var REJECTED = /\b(instead of|rather than|not \w+ing|don'?t use|drop(?:ped)?|revert(?:ed)?|abandon(?:ed)?|scrap(?:ped)?)\b/i;
26328
+ var BLOCKER = /\b(blocked|blocker|can'?t (?:proceed|continue)|waiting (?:on|for)|need(?:s)? (?:access|permission|approval))\b/i;
26329
+ var RESOLVED = /\b(fixed|resolved|now passes|passing now|works now|solved|green)\b/i;
26330
+ var INTENT = /^\s*(?:(?:please|can you|could you|let'?s|i want to|i'?d like to|help me)\s+)?(\w+(?:\s+\w+){0,5})/i;
26331
+ function classifyPrompt(prompt) {
26332
+ const out = [ev("user_prompt", { text: clip(prompt, 600) })];
26333
+ const first = prompt.split("\n").find((l) => l.trim()) ?? "";
26334
+ if (DECISION.test(prompt))
26335
+ out.push(ev("decision", { text: clip(firstMatchLine(prompt, DECISION), 300), by: "user" }));
26336
+ if (CONSTRAINT.test(prompt) && prompt.length < 2e3)
26337
+ out.push(ev("constraint", { text: clip(firstMatchLine(prompt, CONSTRAINT), 300) }));
26338
+ if (REJECTED.test(prompt))
26339
+ out.push(ev("rejected", { text: clip(firstMatchLine(prompt, REJECTED), 300) }));
26340
+ if (BLOCKER.test(prompt))
26341
+ out.push(ev("blocker", { text: clip(firstMatchLine(prompt, BLOCKER), 300) }));
26342
+ const m = INTENT.exec(first);
26343
+ if (m?.[1]) out.push(ev("intent", { text: clip(m[1], 80) }));
26344
+ return out;
26345
+ }
26346
+ function firstMatchLine(text, re) {
26347
+ return text.split("\n").find((l) => re.test(l)) ?? text;
26348
+ }
26349
+ var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
26350
+ var GIT_OP = /^\s*git\s+(commit|checkout|switch|merge|rebase|push|pull|stash|reset|revert|cherry-pick|tag|branch\s+-[dD])/;
26351
+ function callKey(toolName, toolInput) {
26352
+ return `${toolName}:${createHash2("sha1").update(JSON.stringify(toolInput)).digest("hex").slice(0, 12)}`;
26353
+ }
26354
+ function responseText(r) {
26355
+ if (typeof r === "string") return r;
26356
+ if (r && typeof r === "object") {
26357
+ const o = r;
26358
+ if (typeof o.stdout === "string")
26359
+ return `${o.stdout}
26360
+ ${typeof o.stderr === "string" ? o.stderr : ""}`;
26361
+ if (Array.isArray(o.content)) {
26362
+ return o.content.map(
26363
+ (c) => c && typeof c === "object" && typeof c.text === "string" ? c.text : ""
26364
+ ).join("\n");
26365
+ }
26366
+ if (typeof o.error === "string") return o.error;
26367
+ }
26368
+ return "";
26369
+ }
26370
+ function responseFailed(r) {
26371
+ if (!r || typeof r !== "object") return void 0;
26372
+ const o = r;
26373
+ if (o.is_error === true || o.isError === true) return clip(responseText(r), 400);
26374
+ if (typeof o.exit_code === "number" && o.exit_code !== 0) return clip(responseText(r), 400);
26375
+ if (typeof o.interrupted === "boolean" && o.interrupted) return "interrupted";
26376
+ return void 0;
26377
+ }
26378
+ function classifyToolCall(info) {
26379
+ const { toolName, toolInput, root } = info;
26380
+ const out = [];
26381
+ const key = callKey(toolName, toolInput);
26382
+ out.push(ev("tool_call", { key, summary: summarizeInput(toolName, toolInput) }, toolName));
26383
+ if (EDIT_TOOLS.has(toolName)) {
26384
+ const fp = String(toolInput.file_path ?? toolInput.notebook_path ?? "");
26385
+ if (fp) {
26386
+ const rel = relPath(root, fp);
26387
+ if (/\.claude\/plans\/|\/plans\/.*\.md$/.test(fp))
26388
+ out.push(ev("plans", { path: rel, action: toolName }, toolName));
26389
+ else out.push(ev("files", { path: rel, action: toolName }, toolName));
26390
+ if (/(^|\/)(CLAUDE|AGENTS)\.md$|\.claude\/settings/.test(fp))
26391
+ out.push(ev("rules", { path: rel, action: toolName }, toolName));
26392
+ }
26393
+ }
26394
+ if (toolName === "Bash") {
26395
+ const cmd = String(toolInput.command ?? "");
26396
+ if (GIT_OP.test(cmd)) out.push(ev("git", { command: clip(cmd, 200) }, toolName));
26397
+ }
26398
+ if (toolName === "TodoWrite" && Array.isArray(toolInput.todos)) {
26399
+ const todos = toolInput.todos;
26400
+ out.push(
26401
+ ev(
26402
+ "tasks",
26403
+ {
26404
+ todos: todos.slice(0, 12).map(
26405
+ (t) => `${t.status === "completed" ? "x" : t.status === "in_progress" ? ">" : " "} ${clip(t.content, 80)}`
26406
+ )
26407
+ },
26408
+ toolName
26409
+ )
26410
+ );
26411
+ }
26412
+ if (toolName === "ExitPlanMode") out.push(ev("plans", { action: "plan approved" }, toolName));
26413
+ if (toolName === "Agent" || toolName === "Task") {
26414
+ out.push(
26415
+ ev(
26416
+ "subagent",
26417
+ { description: clip(toolInput.description, 120), type: toolInput.subagent_type },
26418
+ toolName
26419
+ )
26420
+ );
26421
+ }
26422
+ if (toolName === "Skill") out.push(ev("skill", { skill: clip(toolInput.skill, 80) }, toolName));
26423
+ if (toolName === "WebFetch" || toolName === "WebSearch") {
26424
+ out.push(ev("external_ref", { url: clip(toolInput.url ?? toolInput.query, 200) }, toolName));
26425
+ }
26426
+ if (toolName.startsWith("mcp__")) out.push(ev("mcp_tool", { tool: toolName }, toolName));
26427
+ if (info.durationMs && info.durationMs > 5e3) {
26428
+ out.push(
26429
+ ev(
26430
+ "latency",
26431
+ { ms: info.durationMs, summary: summarizeInput(toolName, toolInput) },
26432
+ toolName
26433
+ )
26434
+ );
26435
+ }
26436
+ const failure2 = info.error ?? responseFailed(info.toolResponse);
26437
+ if (failure2)
26438
+ out.push(
26439
+ ev(
26440
+ "error",
26441
+ { tool: toolName, summary: summarizeInput(toolName, toolInput), error: clip(failure2, 400) },
26442
+ toolName
26443
+ )
26444
+ );
26445
+ return out;
26446
+ }
26447
+ function summarizeInput(toolName, input2) {
26448
+ const pick2 = input2.command ?? input2.file_path ?? input2.pattern ?? input2.url ?? input2.prompt ?? input2.query ?? input2.description ?? input2.skill;
26449
+ return clip(`${toolName} ${pick2 ?? ""}`.trim(), 160);
26450
+ }
26451
+ function classifyAssistant(message) {
26452
+ const out = [];
26453
+ if (!message.trim()) return out;
26454
+ out.push(ev("assistant_summary", { text: clip(message.replace(/\s+/g, " "), 500) }));
26455
+ if (DECISION.test(message))
26456
+ out.push(
26457
+ ev("decision", { text: clip(firstMatchLine(message, DECISION), 300), by: "assistant" })
26458
+ );
26459
+ if (RESOLVED.test(message))
26460
+ out.push(ev("error_resolution", { text: clip(firstMatchLine(message, RESOLVED), 300) }));
26461
+ if (BLOCKER.test(message))
26462
+ out.push(ev("blocker", { text: clip(firstMatchLine(message, BLOCKER), 300), by: "assistant" }));
26463
+ return out;
26464
+ }
26465
+
26466
+ // src/index/indexer.ts
26467
+ init_esm_shims();
26468
+ var import_fast_glob = __toESM(require_out4(), 1);
26469
+ import { readFileSync as readFileSync3, statSync } from "fs";
26470
+ import { relative as relative2, resolve as resolve2 } from "path";
26471
+
26472
+ // src/chunker/index.ts
26473
+ init_esm_shims();
26474
+ import { extname } from "path";
26475
+
26476
+ // src/chunker/code.ts
26477
+ init_esm_shims();
26478
+
26479
+ // src/chunker/types.ts
26480
+ init_esm_shims();
26481
+ import { createHash as createHash3 } from "crypto";
26482
+ var DEFAULT_CHUNKER = { maxChars: 1800, overlapLines: 5 };
26483
+ function hashText(s) {
26484
+ return createHash3("sha1").update(s).digest("hex");
26289
26485
  }
26290
26486
  function finalize2(raw, contentType) {
26291
26487
  return raw.filter((r) => r.body.trim().length > 0).map((r, ord) => ({
@@ -26434,7 +26630,7 @@ function chunkMarkdown(text, baseTitle, opts = DEFAULT_CHUNKER) {
26434
26630
  if (/^\s*(```|~~~)/.test(line2)) inFence = !inFence;
26435
26631
  const m = !inFence && /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line2);
26436
26632
  if (m) {
26437
- if (current.lines.length) sections.push(current);
26633
+ sections.push(current);
26438
26634
  const level = m[1].length;
26439
26635
  while (stack.length && stack[stack.length - 1].level >= level) stack.pop();
26440
26636
  stack.push({ level, text: m[2] });
@@ -26443,14 +26639,23 @@ function chunkMarkdown(text, baseTitle, opts = DEFAULT_CHUNKER) {
26443
26639
  }
26444
26640
  current.lines.push(line2);
26445
26641
  });
26446
- if (current.lines.length) sections.push(current);
26642
+ sections.push(current);
26447
26643
  const out = [];
26448
- for (const s of sections) {
26644
+ sections.forEach((s, k) => {
26449
26645
  const title = s.path.length ? `${baseTitle} > ${s.path.join(" > ")}` : baseTitle;
26450
26646
  const body = s.lines.join("\n");
26647
+ if (!body.trim()) {
26648
+ const next = sections[k + 1];
26649
+ const isParent = next !== void 0 && next.path.length > s.path.length && s.path.every((p, j) => next.path[j] === p);
26650
+ if (s.path.length && !isParent) {
26651
+ const heading = s.path[s.path.length - 1];
26652
+ out.push({ title, body: heading, startLine: s.startLine, endLine: s.startLine });
26653
+ }
26654
+ return;
26655
+ }
26451
26656
  if (body.length <= opts.maxChars) {
26452
26657
  out.push({ title, body, startLine: s.startLine, endLine: s.startLine + s.lines.length - 1 });
26453
- continue;
26658
+ return;
26454
26659
  }
26455
26660
  let fence = false;
26456
26661
  const fenceState = s.lines.map((l) => {
@@ -26466,7 +26671,7 @@ function chunkMarkdown(text, baseTitle, opts = DEFAULT_CHUNKER) {
26466
26671
  s.startLine - 1
26467
26672
  )
26468
26673
  );
26469
- }
26674
+ });
26470
26675
  return out;
26471
26676
  }
26472
26677
 
@@ -26549,7 +26754,20 @@ function chunk(content, baseTitle, kind, uri, opts = DEFAULT_CHUNKER) {
26549
26754
  // src/index/indexer.ts
26550
26755
  function indexContent(db, input2) {
26551
26756
  const kind = input2.kind ?? (input2.contentType === "code" ? "code" : input2.contentType === "json" ? "json" : void 0);
26552
- const { chunks, kind: k } = input2.chunks ? { chunks: finalize2(input2.chunks, input2.contentType ?? "code"), kind: kind ?? "code" } : chunk(input2.content, input2.title, kind, input2.uri);
26757
+ const cut = input2.chunks ? { chunks: finalize2(input2.chunks, input2.contentType ?? "code"), kind: kind ?? "code" } : chunk(input2.content, input2.title, kind, input2.uri);
26758
+ const k = cut.kind;
26759
+ const fallback = cut.chunks.length === 0 && input2.content.trim().length > 0;
26760
+ const chunks = fallback ? finalize2(
26761
+ [
26762
+ {
26763
+ title: input2.title,
26764
+ body: input2.content,
26765
+ startLine: 1,
26766
+ endLine: input2.content.split("\n").length
26767
+ }
26768
+ ],
26769
+ kindToContentType(k)
26770
+ ) : cut.chunks;
26553
26771
  const contentHash = hashText(input2.content);
26554
26772
  const existing = input2.uri ? db.findDocumentByUri(input2.uri) : void 0;
26555
26773
  if (existing && existing.content_hash === contentHash) {
@@ -26559,7 +26777,8 @@ function indexContent(db, input2) {
26559
26777
  created: false,
26560
26778
  chunks: chunks.length,
26561
26779
  chars: input2.content.length,
26562
- kind: k
26780
+ kind: k,
26781
+ fallback
26563
26782
  };
26564
26783
  }
26565
26784
  db.deleteDocument(existing.id);
@@ -26576,7 +26795,14 @@ function indexContent(db, input2) {
26576
26795
  },
26577
26796
  chunks
26578
26797
  );
26579
- return { documentId: id, created, chunks: chunks.length, chars: input2.content.length, kind: k };
26798
+ return {
26799
+ documentId: id,
26800
+ created,
26801
+ chunks: chunks.length,
26802
+ chars: input2.content.length,
26803
+ kind: k,
26804
+ fallback
26805
+ };
26580
26806
  }
26581
26807
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
26582
26808
 
@@ -26597,6 +26823,23 @@ function fileOf(id) {
26597
26823
  return void 0;
26598
26824
  }
26599
26825
 
26826
+ // src/graph/metrics.ts
26827
+ init_esm_shims();
26828
+
26829
+ // src/graph/clusters.ts
26830
+ init_esm_shims();
26831
+
26832
+ // src/graph/metrics.ts
26833
+ var DEPENDENCY_KINDS = [
26834
+ "calls",
26835
+ "references",
26836
+ "extends",
26837
+ "implements",
26838
+ "imports",
26839
+ "reexports",
26840
+ "handles"
26841
+ ];
26842
+
26600
26843
  // src/history/model.ts
26601
26844
  init_esm_shims();
26602
26845
 
@@ -27298,6 +27541,9 @@ function memoriesForFile(db, rel, opts = {}) {
27298
27541
  }
27299
27542
  var RISKY = 0.3;
27300
27543
  var FAN_IN = 5;
27544
+ function dependentFiles(db, paths2) {
27545
+ return db.graphDependentFileCounts(paths2, DEPENDENCY_KINDS);
27546
+ }
27301
27547
  function riskLine(path2, f) {
27302
27548
  if (f.risk < RISKY && !f.bugOrigins) return void 0;
27303
27549
  const parts = [`${f.fixes}/${f.commits} commits were fixes`];
@@ -27325,10 +27571,10 @@ function fileContext(db, rel, opts = {}) {
27325
27571
  }
27326
27572
  const fanKey = `fanin:${rel}`;
27327
27573
  if (opts.edit && !seen.has(fanKey)) {
27328
- const fanIn = db.metricsGet([fileId(rel)])[0]?.fan_in ?? 0;
27329
- if (fanIn >= FAN_IN) {
27574
+ const dependents = dependentFiles(db, [rel]).get(rel) ?? 0;
27575
+ if (dependents >= FAN_IN) {
27330
27576
  lines.push(
27331
- `eminiar graph: ${rel} is imported by ${fanIn} files; em_graph_impact {node:"${fileId(rel)}"} lists them before you change an exported signature.`
27577
+ `eminiar graph: ${rel} is imported by ${dependents} files; em_graph_impact {node:"${fileId(rel)}"} lists them before you change an exported signature.`
27332
27578
  );
27333
27579
  keys.push(fanKey);
27334
27580
  }
@@ -27340,7 +27586,7 @@ function commitContext(db, files) {
27340
27586
  if (!files.length) return "";
27341
27587
  const table = readFileRisk(db);
27342
27588
  const risky = files.map((p) => table[p] ? riskLine(p, table[p]) : void 0).filter((l) => !!l).slice(0, 4);
27343
- const wide = db.metricsGet(files.map(fileId)).filter((m) => m.fan_in >= FAN_IN).sort((a, b) => b.fan_in - a.fan_in).slice(0, 4).map((m) => `${m.path} (${m.fan_in} importers)`);
27589
+ const wide = [...dependentFiles(db, files)].filter(([, n]) => n >= FAN_IN).sort((a, b) => b[1] - a[1]).slice(0, 4).map(([p, n]) => `${p} (${n} importers)`);
27344
27590
  if (!risky.length && !wide.length) return "";
27345
27591
  const lines = ["eminiar history: this commit includes files that deserve a second look:"];
27346
27592
  for (const r of risky) lines.push(`- ${r}`);
@@ -27624,6 +27870,10 @@ function topTermsOf(bodies, limit) {
27624
27870
 
27625
27871
  // src/storage/sqlite/schema.ts
27626
27872
  init_esm_shims();
27873
+ function hasColumn(db, table, column) {
27874
+ const cols = db.pragma(`table_info(${table})`);
27875
+ return cols.some((c) => c.name === column);
27876
+ }
27627
27877
  var migrations = [
27628
27878
  {
27629
27879
  version: 1,
@@ -27958,7 +28208,6 @@ CREATE TABLE IF NOT EXISTS transcript_files (
27958
28208
  offset INTEGER NOT NULL DEFAULT 0,
27959
28209
  size INTEGER NOT NULL DEFAULT 0,
27960
28210
  mtime INTEGER NOT NULL DEFAULT 0,
27961
- head_hash TEXT,
27962
28211
  ingested_at INTEGER NOT NULL
27963
28212
  );
27964
28213
 
@@ -27986,6 +28235,16 @@ CREATE TABLE IF NOT EXISTS statusline_snapshots (
27986
28235
  CREATE INDEX IF NOT EXISTS statusline_snapshots_session_ts ON statusline_snapshots(session_id, ts);
27987
28236
  CREATE INDEX IF NOT EXISTS statusline_snapshots_ts ON statusline_snapshots(ts);
27988
28237
  `
28238
+ },
28239
+ {
28240
+ // transcript_files.head_hash (fingerprint of the file head, so a rewrite with the same
28241
+ // size is noticed) was first added to v5 after v5 had already run on real databases, so
28242
+ // version-5 databases exist both with and without the column. Add it only when missing.
28243
+ version: 6,
28244
+ up(db) {
28245
+ if (!hasColumn(db, "transcript_files", "head_hash"))
28246
+ db.exec("ALTER TABLE transcript_files ADD COLUMN head_hash TEXT");
28247
+ }
27989
28248
  }
27990
28249
  ];
27991
28250
  function vecTableSql(dims) {
@@ -28097,7 +28356,8 @@ var SqliteAdapter = class _SqliteAdapter {
28097
28356
  if (pending.length) {
28098
28357
  this.transaction(() => {
28099
28358
  for (const m of pending) {
28100
- this.db.exec(m.sql);
28359
+ if (m.sql) this.db.exec(m.sql);
28360
+ m.up?.(this.db);
28101
28361
  this.metaSet("schema_version", String(m.version));
28102
28362
  }
28103
28363
  });
@@ -28316,18 +28576,42 @@ var SqliteAdapter = class _SqliteAdapter {
28316
28576
  }
28317
28577
  vecSearch(embedding, k, filters) {
28318
28578
  if (!this.vectors) return [];
28319
- const raw = this.db.prepare(
28320
- "SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ORDER BY distance"
28321
- ).all(embedding, BigInt(filters ? k * 4 : k));
28322
- if (!filters || !raw.length) return raw.map((r) => ({ chunkId: r.id, distance: r.distance }));
28323
28579
  const { where, params } = filterSql(filters);
28324
- const allowed = new Set(
28325
- this.db.prepare(
28326
- `SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id
28327
- WHERE c.id IN (${raw.map(() => "?").join(",")})${where}`
28328
- ).all(...raw.map((r) => r.id), ...params).map((r) => r.id)
28329
- );
28330
- return raw.filter((r) => allowed.has(r.id)).slice(0, k).map((r) => ({ chunkId: r.id, distance: r.distance }));
28580
+ if (!where) return this.knn(embedding, k);
28581
+ try {
28582
+ return this.knn(
28583
+ embedding,
28584
+ k,
28585
+ `AND chunk_id IN (SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id WHERE 1=1${where})`,
28586
+ params
28587
+ );
28588
+ } catch {
28589
+ return this.vecSearchWidening(embedding, k, where, params);
28590
+ }
28591
+ }
28592
+ knn(embedding, k, extra = "", params = []) {
28593
+ return this.db.prepare(
28594
+ `SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ${extra} ORDER BY distance`
28595
+ ).all(embedding, BigInt(k), ...params).map((r) => ({ chunkId: r.id, distance: r.distance }));
28596
+ }
28597
+ /** Fallback filtered KNN: fetch k*4, k*16, ... nearest until k pass the filter or all were seen. */
28598
+ vecSearchWidening(embedding, k, where, params) {
28599
+ const total = this.vecCount();
28600
+ for (let fetch = k * 4; ; fetch *= 4) {
28601
+ const raw = this.knn(embedding, Math.min(fetch, total));
28602
+ if (!raw.length) return [];
28603
+ const allowed = /* @__PURE__ */ new Set();
28604
+ for (let i = 0; i < raw.length; i += 400) {
28605
+ const ids = raw.slice(i, i + 400).map((r) => r.chunkId);
28606
+ for (const r of this.db.prepare(
28607
+ `SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id
28608
+ WHERE c.id IN (${ids.map(() => "?").join(",")})${where}`
28609
+ ).all(...ids, ...params))
28610
+ allowed.add(r.id);
28611
+ }
28612
+ const hits = raw.filter((r) => allowed.has(r.chunkId)).slice(0, k);
28613
+ if (hits.length >= k || fetch >= total) return hits;
28614
+ }
28331
28615
  }
28332
28616
  unembeddedChunkIds(limit) {
28333
28617
  return this.db.prepare("SELECT id FROM chunks WHERE embedded = 0 ORDER BY id LIMIT ?").all(limit).map((r) => r.id);
@@ -28606,6 +28890,26 @@ var SqliteAdapter = class _SqliteAdapter {
28606
28890
  }
28607
28891
  return out;
28608
28892
  }
28893
+ graphDependentFileCounts(paths2, kinds) {
28894
+ const out = /* @__PURE__ */ new Map();
28895
+ if (!paths2.length || !kinds.length) return out;
28896
+ const kph = kinds.map(() => "?").join(",");
28897
+ for (let i = 0; i < paths2.length; i += 400) {
28898
+ const batch = paths2.slice(i, i + 400);
28899
+ const pph = batch.map(() => "?").join(",");
28900
+ for (const r of this.db.prepare(
28901
+ `SELECT d.path p, COUNT(DISTINCT s.path) n
28902
+ FROM graph_nodes d
28903
+ JOIN graph_edges e ON e.dst = d.id AND e.kind IN (${kph})
28904
+ JOIN graph_nodes s ON s.id = e.src
28905
+ WHERE d.path IN (${pph}) AND s.path IS NOT NULL AND s.path <> d.path
28906
+ GROUP BY d.path`
28907
+ ).all(...kinds, ...batch)) {
28908
+ out.set(r.p, r.n);
28909
+ }
28910
+ }
28911
+ return out;
28912
+ }
28609
28913
  edgesWhere(col, ids, kinds) {
28610
28914
  if (!ids.length) return [];
28611
28915
  const out = [];
@@ -30274,9 +30578,9 @@ function budgetWarnings(db, ctx, sessionId, now = Date.now()) {
30274
30578
  // src/usage/ingest.ts
30275
30579
  init_esm_shims();
30276
30580
  import { createHash as createHash4 } from "crypto";
30277
- import { closeSync, existsSync as existsSync3, openSync, readdirSync, readSync, statSync as statSync2 } from "fs";
30278
- import { homedir as homedir2 } from "os";
30279
- import { basename, dirname as dirname2, join as join4, resolve as resolve3, sep } from "path";
30581
+ import { closeSync, existsSync as existsSync4, openSync, readdirSync, readSync, statSync as statSync2 } from "fs";
30582
+ import { homedir as homedir3 } from "os";
30583
+ import { basename, dirname as dirname2, join as join5, resolve as resolve3, sep } from "path";
30280
30584
 
30281
30585
  // src/usage/transcript.ts
30282
30586
  init_esm_shims();
@@ -30285,7 +30589,7 @@ var DENIAL_RE = /^\s*PreToolUse:|hook error|\bdenied\b/i;
30285
30589
  function isObj(v) {
30286
30590
  return typeof v === "object" && v !== null && !Array.isArray(v);
30287
30591
  }
30288
- function str(v) {
30592
+ function str2(v) {
30289
30593
  return typeof v === "string" && v.length ? v : null;
30290
30594
  }
30291
30595
  function num3(v) {
@@ -30313,8 +30617,8 @@ function resultChars(content) {
30313
30617
  n += safeJsonLength(part);
30314
30618
  continue;
30315
30619
  }
30316
- const kind = str(part.type);
30317
- if (kind === "text" || typeof part.text === "string") n += str(part.text)?.length ?? 0;
30620
+ const kind = str2(part.type);
30621
+ if (kind === "text" || typeof part.text === "string") n += str2(part.text)?.length ?? 0;
30318
30622
  else if (kind === "image" || kind === "document" || kind === "tool_reference")
30319
30623
  n += NON_TEXT_PART_CHARS;
30320
30624
  else n += safeJsonLength(part);
@@ -30359,10 +30663,10 @@ function parseTranscriptLines(lines, ctx) {
30359
30663
  continue;
30360
30664
  }
30361
30665
  if (!isObj(e)) continue;
30362
- const type = str(e.type);
30666
+ const type = str2(e.type);
30363
30667
  if (!type) continue;
30364
- const sessionId = str(e.sessionId) ?? ctx.sessionId;
30365
- const agentId = str(e.agentId);
30668
+ const sessionId = str2(e.sessionId) ?? ctx.sessionId;
30669
+ const agentId = str2(e.agentId);
30366
30670
  const sidechain = e.isSidechain === true ? 1 : 0;
30367
30671
  const real = tsOf(e);
30368
30672
  const ts = real ?? prevTs ?? fallbackTs;
@@ -30371,12 +30675,12 @@ function parseTranscriptLines(lines, ctx) {
30371
30675
  if (type === "assistant") {
30372
30676
  const msg = isObj(e.message) ? e.message : void 0;
30373
30677
  if (!msg) continue;
30374
- const messageId = str(msg.id);
30375
- const requestId = str(e.requestId) ?? messageId;
30678
+ const messageId = str2(msg.id);
30679
+ const requestId = str2(e.requestId) ?? messageId;
30376
30680
  if (Array.isArray(msg.content)) {
30377
30681
  for (const block of msg.content) {
30378
30682
  if (!isObj(block) || block.type !== "tool_use") continue;
30379
- const id = str(block.id);
30683
+ const id = str2(block.id);
30380
30684
  if (!id) continue;
30381
30685
  const prev2 = toolUses.get(id);
30382
30686
  toolUses.set(id, {
@@ -30384,7 +30688,7 @@ function parseTranscriptLines(lines, ctx) {
30384
30688
  session_id: prev2?.session_id ?? sessionId,
30385
30689
  agent_id: prev2?.agent_id ?? agentId,
30386
30690
  ts: prev2?.ts ?? ts,
30387
- tool: str(block.name) ?? prev2?.tool ?? "",
30691
+ tool: str2(block.name) ?? prev2?.tool ?? "",
30388
30692
  input_chars: block.input === void 0 ? 0 : safeJsonLength(block.input),
30389
30693
  result_chars: prev2?.result_chars ?? null,
30390
30694
  is_error: prev2?.is_error ?? 0,
@@ -30394,7 +30698,7 @@ function parseTranscriptLines(lines, ctx) {
30394
30698
  }
30395
30699
  const usage = isObj(msg.usage) ? msg.usage : void 0;
30396
30700
  if (!usage || !messageId || !requestId) continue;
30397
- const model = str(msg.model) ?? "unknown";
30701
+ const model = str2(msg.model) ?? "unknown";
30398
30702
  const cc = isObj(usage.cache_creation) ? usage.cache_creation : void 0;
30399
30703
  const create5m = cc ? num3(cc.ephemeral_5m_input_tokens) : num3(usage.cache_creation_input_tokens);
30400
30704
  const create1h = cc ? num3(cc.ephemeral_1h_input_tokens) : 0;
@@ -30418,15 +30722,15 @@ function parseTranscriptLines(lines, ctx) {
30418
30722
  thinking_tokens: details ? num3(details.thinking_tokens) : 0,
30419
30723
  web_searches: server ? num3(server.web_search_requests) : 0,
30420
30724
  web_fetches: server ? num3(server.web_fetch_requests) : 0,
30421
- service_tier: str(usage.service_tier),
30422
- speed: str(usage.speed),
30423
- stop_reason: str(msg.stop_reason) ?? prev?.stop_reason ?? null,
30424
- prompt_id: str(e.promptId) ?? prev?.prompt_id ?? null,
30425
- git_branch: str(e.gitBranch) ?? prev?.git_branch ?? null,
30426
- cwd: str(e.cwd) ?? prev?.cwd ?? null,
30427
- effort: str(e.effort) ?? prev?.effort ?? null,
30428
- version: str(e.version) ?? prev?.version ?? null,
30429
- entrypoint: str(e.entrypoint) ?? prev?.entrypoint ?? null
30725
+ service_tier: str2(usage.service_tier),
30726
+ speed: str2(usage.speed),
30727
+ stop_reason: str2(msg.stop_reason) ?? prev?.stop_reason ?? null,
30728
+ prompt_id: str2(e.promptId) ?? prev?.prompt_id ?? null,
30729
+ git_branch: str2(e.gitBranch) ?? prev?.git_branch ?? null,
30730
+ cwd: str2(e.cwd) ?? prev?.cwd ?? null,
30731
+ effort: str2(e.effort) ?? prev?.effort ?? null,
30732
+ version: str2(e.version) ?? prev?.version ?? null,
30733
+ entrypoint: str2(e.entrypoint) ?? prev?.entrypoint ?? null
30430
30734
  });
30431
30735
  continue;
30432
30736
  }
@@ -30435,7 +30739,7 @@ function parseTranscriptLines(lines, ctx) {
30435
30739
  if (!msg || !Array.isArray(msg.content)) continue;
30436
30740
  for (const block of msg.content) {
30437
30741
  if (!isObj(block) || block.type !== "tool_result") continue;
30438
- const id = str(block.tool_use_id);
30742
+ const id = str2(block.tool_use_id);
30439
30743
  if (!id) continue;
30440
30744
  const prev = toolUses.get(id);
30441
30745
  let isError = prev?.is_error ?? 0;
@@ -30458,20 +30762,20 @@ function parseTranscriptLines(lines, ctx) {
30458
30762
  continue;
30459
30763
  }
30460
30764
  if (type === "system") {
30461
- const subtype = str(e.subtype);
30765
+ const subtype = str2(e.subtype);
30462
30766
  if (subtype === "compact_boundary") {
30463
30767
  const meta3 = isObj(e.compactMetadata) ? e.compactMetadata : void 0;
30464
30768
  contextEvents.push({
30465
30769
  session_id: sessionId,
30466
30770
  ts,
30467
30771
  kind: "compact",
30468
- trigger: meta3 ? str(meta3.trigger) : null,
30772
+ trigger: meta3 ? str2(meta3.trigger) : null,
30469
30773
  pre_tokens: meta3 ? numOrNull(meta3.preTokens) : null,
30470
30774
  post_tokens: meta3 ? numOrNull(meta3.postTokens) : null
30471
30775
  });
30472
30776
  } else if (subtype === "local_command") {
30473
30777
  const run = isObj(e.commandRun) ? e.commandRun : void 0;
30474
- if (run && str(run.command) === "clear") {
30778
+ if (run && str2(run.command) === "clear") {
30475
30779
  contextEvents.push({
30476
30780
  session_id: sessionId,
30477
30781
  ts,
@@ -30504,7 +30808,7 @@ function isValidSessionId(id) {
30504
30808
  return SESSION_ID_RE.test(id);
30505
30809
  }
30506
30810
  function claudeDir() {
30507
- return process.env.EMINIAR_CLAUDE_DIR || join4(homedir2(), ".claude");
30811
+ return process.env.EMINIAR_CLAUDE_DIR || join5(homedir3(), ".claude");
30508
30812
  }
30509
30813
  function insideDir(path2, dir) {
30510
30814
  const d = resolve3(dir);
@@ -30523,7 +30827,7 @@ function transcriptSlug(root) {
30523
30827
  return root.replace(/[^A-Za-z0-9]/g, "-");
30524
30828
  }
30525
30829
  function transcriptDirFor(root) {
30526
- return join4(claudeDir(), "projects", transcriptSlug(root));
30830
+ return join5(claudeDir(), "projects", transcriptSlug(root));
30527
30831
  }
30528
30832
  function usageEnabled(ctx) {
30529
30833
  return ctx.config.usage.enabled !== false;
@@ -30693,7 +30997,7 @@ function add(a, b) {
30693
30997
  }
30694
30998
  function listJsonl(dir) {
30695
30999
  try {
30696
- return readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => join4(dir, f));
31000
+ return readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => join5(dir, f));
30697
31001
  } catch {
30698
31002
  return [];
30699
31003
  }
@@ -30713,7 +31017,7 @@ function firstEntrySessionId(path2) {
30713
31017
  function sidechainFilesFor(db, dir, sessionId) {
30714
31018
  if (!isValidSessionId(sessionId)) return [];
30715
31019
  const inside = (p) => insideDir(p, dir) && p.endsWith(".jsonl");
30716
- const out = listJsonl(join4(dir, sessionId, "subagents")).filter(inside);
31020
+ const out = listJsonl(join5(dir, sessionId, "subagents")).filter(inside);
30717
31021
  for (const path2 of listJsonl(dir)) {
30718
31022
  if (!inside(path2) || !basename(path2).startsWith("agent-")) continue;
30719
31023
  const known = db.transcriptFileGet(path2)?.session_id;
@@ -30755,6 +31059,18 @@ function ingestSession(db, root, sessionId, transcriptPath, opts = {}) {
30755
31059
  return total;
30756
31060
  }
30757
31061
 
31062
+ // src/hooks/log.ts
31063
+ init_esm_shims();
31064
+ import { appendFileSync } from "fs";
31065
+ function hookLog(level, msg) {
31066
+ try {
31067
+ const p = ensureDirs();
31068
+ appendFileSync(p.hookLog, `${(/* @__PURE__ */ new Date()).toISOString()} ${level} ${msg}
31069
+ `);
31070
+ } catch {
31071
+ }
31072
+ }
31073
+
30758
31074
  // src/hooks/capture.ts
30759
31075
  var INDEXED = /* @__PURE__ */ new Set([
30760
31076
  "decision",
@@ -30797,6 +31113,7 @@ function open3(input2) {
30797
31113
  });
30798
31114
  return { db, root: ctx.root, sessionId };
30799
31115
  }
31116
+ var usageFailureLogged = false;
30800
31117
  function ingestUsage(db, input2, sessionId) {
30801
31118
  try {
30802
31119
  const ctx = getContext(input2.cwd);
@@ -30805,7 +31122,13 @@ function ingestUsage(db, input2, sessionId) {
30805
31122
  ingestSession(db, ctx.root, sessionId, input2.transcript_path, { overrides });
30806
31123
  if (input2.agent_transcript_path)
30807
31124
  ingestTranscript(db, input2.agent_transcript_path, { sessionId, overrides });
30808
- } catch {
31125
+ } catch (err) {
31126
+ if (usageFailureLogged) return;
31127
+ usageFailureLogged = true;
31128
+ hookLog(
31129
+ "error",
31130
+ `usage ingest failed (session ${sessionId}): ${err.message}; run \`eminiar usage ingest\` to see the error`
31131
+ );
30809
31132
  }
30810
31133
  }
30811
31134
  var userPromptSubmit = async (input2) => {
@@ -31065,19 +31388,20 @@ var preCompact = (input2) => {
31065
31388
  // src/hooks/routing.ts
31066
31389
  init_esm_shims();
31067
31390
  import { realpathSync as realpathSync4, statSync as statSync5 } from "fs";
31068
- import { homedir as homedir5 } from "os";
31069
- import { extname as extname2, join as join12, relative as relative4, resolve as resolve4 } from "path";
31391
+ import { homedir as homedir6 } from "os";
31392
+ import { extname as extname2, join as join13, relative as relative4, resolve as resolve4 } from "path";
31070
31393
 
31071
31394
  // src/docs/manifest.ts
31072
31395
  init_esm_shims();
31073
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
31074
- import { dirname as dirname3, join as join5 } from "path";
31396
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
31397
+ import { dirname as dirname3, join as join6 } from "path";
31398
+ var DOCS_PREFIX = "docs:";
31075
31399
  var NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
31076
31400
  function isValidName(name) {
31077
31401
  return NAME_RE.test(name);
31078
31402
  }
31079
31403
  function manifestPathAt(root, rel = ".eminiar/docs.json") {
31080
- return join5(root, rel);
31404
+ return join6(root, rel);
31081
31405
  }
31082
31406
  function manifestPath(ctx) {
31083
31407
  return manifestPathAt(ctx.root, ctx.config.docs.manifest);
@@ -31105,9 +31429,9 @@ function normalize(raw) {
31105
31429
  return { version: 1, sources: out };
31106
31430
  }
31107
31431
  function readManifestAt(file3) {
31108
- if (!existsSync4(file3)) return empty();
31432
+ if (!existsSync5(file3)) return empty();
31109
31433
  try {
31110
- return normalize(JSON.parse(readFileSync3(file3, "utf8")));
31434
+ return normalize(JSON.parse(readFileSync4(file3, "utf8")));
31111
31435
  } catch {
31112
31436
  return empty();
31113
31437
  }
@@ -31118,9 +31442,9 @@ function readManifest(ctx) {
31118
31442
 
31119
31443
  // src/permissions/settings.ts
31120
31444
  init_esm_shims();
31121
- import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
31122
- import { homedir as homedir3 } from "os";
31123
- import { join as join6 } from "path";
31445
+ import { existsSync as existsSync6, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
31446
+ import { homedir as homedir4 } from "os";
31447
+ import { join as join7 } from "path";
31124
31448
 
31125
31449
  // src/permissions/rules.ts
31126
31450
  init_esm_shims();
@@ -31157,9 +31481,9 @@ init_esm_shims();
31157
31481
 
31158
31482
  // src/permissions/settings.ts
31159
31483
  function readRules(file3) {
31160
- if (!existsSync5(file3)) return {};
31484
+ if (!existsSync6(file3)) return {};
31161
31485
  try {
31162
- const s = JSON.parse(readFileSync4(file3, "utf8"));
31486
+ const s = JSON.parse(readFileSync5(file3, "utf8"));
31163
31487
  return s.permissions ?? {};
31164
31488
  } catch {
31165
31489
  return {};
@@ -31175,9 +31499,9 @@ function stamp(file3) {
31175
31499
  var cache2 = /* @__PURE__ */ new Map();
31176
31500
  function loadPermissionRules(root, extraDeny = []) {
31177
31501
  const files = [
31178
- join6(homedir3(), ".claude", "settings.json"),
31179
- join6(root, ".claude", "settings.json"),
31180
- join6(root, ".claude", "settings.local.json")
31502
+ join7(homedir4(), ".claude", "settings.json"),
31503
+ join7(root, ".claude", "settings.json"),
31504
+ join7(root, ".claude", "settings.local.json")
31181
31505
  ];
31182
31506
  const key = `${root}\0${extraDeny.join("\0")}`;
31183
31507
  const stamps = files.map(stamp);
@@ -31215,8 +31539,8 @@ function checkReadPath(rules, abs) {
31215
31539
 
31216
31540
  // src/session/brief.ts
31217
31541
  init_esm_shims();
31218
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
31219
- import { join as join8 } from "path";
31542
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
31543
+ import { join as join9 } from "path";
31220
31544
 
31221
31545
  // src/memory/frontmatter.ts
31222
31546
  init_esm_shims();
@@ -31250,15 +31574,15 @@ function parseMemoryFile(text) {
31250
31574
  init_esm_shims();
31251
31575
  var import_fast_glob2 = __toESM(require_out4(), 1);
31252
31576
  import {
31253
- existsSync as existsSync6,
31254
- mkdirSync as mkdirSync3,
31255
- readFileSync as readFileSync5,
31577
+ existsSync as existsSync7,
31578
+ mkdirSync as mkdirSync4,
31579
+ readFileSync as readFileSync6,
31256
31580
  renameSync,
31257
31581
  statSync as statSync4,
31258
31582
  unlinkSync,
31259
- writeFileSync as writeFileSync2
31583
+ writeFileSync as writeFileSync3
31260
31584
  } from "fs";
31261
- import { dirname as dirname4, join as join7, relative as relative3, sep as sep2 } from "path";
31585
+ import { dirname as dirname4, join as join8, relative as relative3, sep as sep2 } from "path";
31262
31586
 
31263
31587
  // src/storage/types.ts
31264
31588
  init_esm_shims();
@@ -31270,7 +31594,7 @@ function memoriesRel(ctx) {
31270
31594
  return ctx.config.memories.dir.replace(/^\.\//, "").replace(/\/+$/, "");
31271
31595
  }
31272
31596
  function memoriesDir(ctx) {
31273
- return join7(ctx.root, memoriesRel(ctx));
31597
+ return join8(ctx.root, memoriesRel(ctx));
31274
31598
  }
31275
31599
 
31276
31600
  // src/session/brief.ts
@@ -31313,10 +31637,10 @@ function historyLine(db) {
31313
31637
  return `${parts.join("; ")}. em_history {action:"file", path} before editing, {action:"risk"} before committing.`;
31314
31638
  }
31315
31639
  function overviewParagraph(ctx) {
31316
- const file3 = join8(memoriesDir(ctx), "onboarding", "overview.md");
31317
- if (!existsSync7(file3)) return void 0;
31640
+ const file3 = join9(memoriesDir(ctx), "onboarding", "overview.md");
31641
+ if (!existsSync8(file3)) return void 0;
31318
31642
  try {
31319
- const body = parseMemoryFile(readFileSync6(file3, "utf8")).body.trim();
31643
+ const body = parseMemoryFile(readFileSync7(file3, "utf8")).body.trim();
31320
31644
  const para = body.split(/\n\s*\n/).map(
31321
31645
  (p) => p.replace(/^#+\s.*$/m, "").replace(/\s+/g, " ").trim()
31322
31646
  ).find((p) => p.length > 20);
@@ -31385,10 +31709,10 @@ function recordRouting(db, sessionId, e) {
31385
31709
 
31386
31710
  // src/session/server-alive.ts
31387
31711
  init_esm_shims();
31388
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
31389
- import { join as join9 } from "path";
31712
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
31713
+ import { join as join10 } from "path";
31390
31714
  function markersDir(ctx) {
31391
- return join9(projectDir(ctx.id), "servers");
31715
+ return join10(projectDir(ctx.id), "servers");
31392
31716
  }
31393
31717
  function processAlive(pid) {
31394
31718
  try {
@@ -31400,13 +31724,13 @@ function processAlive(pid) {
31400
31724
  }
31401
31725
  function liveServers(ctx) {
31402
31726
  const dir = markersDir(ctx);
31403
- if (!existsSync8(dir)) return [];
31727
+ if (!existsSync9(dir)) return [];
31404
31728
  const live2 = [];
31405
31729
  for (const name of readdirSync2(dir)) {
31406
31730
  const pid = Number(name);
31407
31731
  if (!Number.isInteger(pid) || pid <= 0) continue;
31408
31732
  if (processAlive(pid)) live2.push(pid);
31409
- else rmSync(join9(dir, name), { force: true });
31733
+ else rmSync2(join10(dir, name), { force: true });
31410
31734
  }
31411
31735
  return live2;
31412
31736
  }
@@ -31416,30 +31740,30 @@ function serverAlive(ctx) {
31416
31740
 
31417
31741
  // src/hooks/coexist.ts
31418
31742
  init_esm_shims();
31419
- import { readFileSync as readFileSync8 } from "fs";
31420
- import { join as join11 } from "path";
31743
+ import { readFileSync as readFileSync9 } from "fs";
31744
+ import { join as join12 } from "path";
31421
31745
 
31422
31746
  // src/install/claude-config.ts
31423
31747
  init_esm_shims();
31424
31748
  import {
31425
31749
  copyFileSync,
31426
- existsSync as existsSync9,
31427
- mkdirSync as mkdirSync5,
31750
+ existsSync as existsSync10,
31751
+ mkdirSync as mkdirSync6,
31428
31752
  readdirSync as readdirSync3,
31429
- readFileSync as readFileSync7,
31430
- writeFileSync as writeFileSync4
31753
+ readFileSync as readFileSync8,
31754
+ writeFileSync as writeFileSync5
31431
31755
  } from "fs";
31432
- import { homedir as homedir4 } from "os";
31433
- import { dirname as dirname5, join as join10 } from "path";
31434
- var claudeDir2 = () => process.env.CLAUDE_CONFIG_DIR || join10(homedir4(), ".claude");
31435
- var pluginsDir = () => join10(claudeDir2(), "plugins");
31756
+ import { homedir as homedir5 } from "os";
31757
+ import { dirname as dirname5, join as join11 } from "path";
31758
+ var claudeDir2 = () => process.env.CLAUDE_CONFIG_DIR || join11(homedir5(), ".claude");
31759
+ var pluginsDir = () => join11(claudeDir2(), "plugins");
31436
31760
 
31437
31761
  // src/hooks/coexist.ts
31438
31762
  var cached2;
31439
31763
  function contextModeInstalled() {
31440
31764
  if (cached2 !== void 0) return cached2;
31441
31765
  try {
31442
- const reg = JSON.parse(readFileSync8(join11(pluginsDir(), "installed_plugins.json"), "utf8"));
31766
+ const reg = JSON.parse(readFileSync9(join12(pluginsDir(), "installed_plugins.json"), "utf8"));
31443
31767
  cached2 = Object.keys(reg.plugins ?? {}).some((id) => id.startsWith("context-mode@"));
31444
31768
  } catch {
31445
31769
  cached2 = false;
@@ -31451,18 +31775,6 @@ function effectiveRoutingMode(config2) {
31451
31775
  return contextModeInstalled() ? "annotate" : "enforce";
31452
31776
  }
31453
31777
 
31454
- // src/hooks/log.ts
31455
- init_esm_shims();
31456
- import { appendFileSync } from "fs";
31457
- function hookLog(level, msg) {
31458
- try {
31459
- const p = ensureDirs();
31460
- appendFileSync(p.hookLog, `${(/* @__PURE__ */ new Date()).toISOString()} ${level} ${msg}
31461
- `);
31462
- } catch {
31463
- }
31464
- }
31465
-
31466
31778
  // src/hooks/routing.ts
31467
31779
  var SYMBOL_KINDS2 = [
31468
31780
  "function",
@@ -31535,7 +31847,7 @@ function historyCall(cmd) {
31535
31847
  }
31536
31848
  function docsSources(root) {
31537
31849
  try {
31538
- return readManifestAt(join12(root, ".eminiar", "docs.json")).sources;
31850
+ return readManifestAt(join13(root, ".eminiar", "docs.json")).sources;
31539
31851
  } catch {
31540
31852
  return [];
31541
31853
  }
@@ -31573,6 +31885,7 @@ var BIG_TEXT_EXT = /* @__PURE__ */ new Set([
31573
31885
  ".sql",
31574
31886
  ".har"
31575
31887
  ]);
31888
+ var VISUAL_EXT = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".pdf"]);
31576
31889
  function withoutHeredocs(cmd) {
31577
31890
  return cmd.replace(/<<-?\s*(['"]?)(\w+)\1[^\n]*\n[\s\S]*?\n\s*\2\s*(?=\n|$)/g, "<<$2");
31578
31891
  }
@@ -31682,7 +31995,7 @@ function fileOperands(stage, cwd) {
31682
31995
  for (let f of files) {
31683
31996
  if (/[*?[\]{}$`\\]/.test(f)) return void 0;
31684
31997
  f = f.replace(/^['"]|['"]$/g, "");
31685
- if (f.startsWith("~/")) f = join12(homedir5(), f.slice(2));
31998
+ if (f.startsWith("~/")) f = join13(homedir6(), f.slice(2));
31686
31999
  out.push(resolve4(cwd, f));
31687
32000
  }
31688
32001
  return out;
@@ -31774,10 +32087,11 @@ function suggest(toolName, input2, root, extra = {}) {
31774
32087
  case "Read": {
31775
32088
  const fp = String(input2.file_path ?? "");
31776
32089
  if (!fp) return void 0;
32090
+ const ext = extname2(fp).toLowerCase();
32091
+ if (VISUAL_EXT.has(ext)) return void 0;
31777
32092
  try {
31778
32093
  const abs = resolve4(root, fp);
31779
32094
  const st = statSync5(abs);
31780
- const ext = extname2(fp).toLowerCase();
31781
32095
  if (st.size > 200 * 1024 || BIG_TEXT_EXT.has(ext) && st.size > 20 * 1024) {
31782
32096
  if (!checkReadPath(loadPermissionRules(root), abs).allowed) return void 0;
31783
32097
  return {
@@ -32087,18 +32401,18 @@ function annotate(s, note = "") {
32087
32401
 
32088
32402
  // src/hooks/session-start.ts
32089
32403
  init_esm_shims();
32090
- import { appendFileSync as appendFileSync2, existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
32091
- import { join as join14 } from "path";
32404
+ import { appendFileSync as appendFileSync2, existsSync as existsSync11, readdirSync as readdirSync4 } from "fs";
32405
+ import { join as join15 } from "path";
32092
32406
 
32093
32407
  // src/session/current.ts
32094
32408
  init_esm_shims();
32095
- import { readFileSync as readFileSync9, statSync as statSync6, writeFileSync as writeFileSync5 } from "fs";
32096
- import { join as join13 } from "path";
32409
+ import { readFileSync as readFileSync10, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
32410
+ import { join as join14 } from "path";
32097
32411
  function file2(ctx) {
32098
- return join13(projectDir(ctx.id), "current-session");
32412
+ return join14(projectDir(ctx.id), "current-session");
32099
32413
  }
32100
32414
  function setCurrentSession(ctx, sessionId) {
32101
- writeFileSync5(file2(ctx), sessionId);
32415
+ writeFileSync6(file2(ctx), sessionId);
32102
32416
  }
32103
32417
 
32104
32418
  // src/hooks/session-start.ts
@@ -32187,20 +32501,22 @@ ${guide}`);
32187
32501
  } catch {
32188
32502
  }
32189
32503
  try {
32190
- const onboarding = join14(memoriesDir(ctx), "onboarding");
32191
- const onboarded = existsSync10(onboarding) && readdirSync4(onboarding).some((f) => f.endsWith(".md"));
32504
+ const onboarding = join15(memoriesDir(ctx), "onboarding");
32505
+ const onboarded = existsSync11(onboarding) && readdirSync4(onboarding).some((f) => f.endsWith(".md"));
32192
32506
  if (!onboarded)
32193
32507
  parts.push(
32194
32508
  "No onboarding memories yet: run /eminiar:em-onboard once so future sessions start with the project's structure, build and test commands."
32195
32509
  );
32196
- else if (!readManifest(ctx).sources.length)
32510
+ else if (!readManifest(ctx).sources.length) {
32511
+ const orphaned = db.documentSourceStats(DOCS_PREFIX).length;
32197
32512
  parts.push(
32198
- `No documentation sources registered yet: em_docs {action:"discover"} maps this project's libraries, services and APIs to their documentation, and {action:"add", pick:"recommended"} indexes it for em_docs lookup.`
32513
+ orphaned ? `No documentation sources registered (${orphaned} orphaned docs:* source(s) still in the index; em_docs {action:"list"} shows them, {action:"remove"} drops them, {action:"add", pick:"recommended"} re-registers).` : `No documentation sources registered yet: em_docs {action:"discover"} maps this project's libraries, services and APIs to their documentation, and {action:"add", pick:"recommended"} indexes it for em_docs lookup.`
32199
32514
  );
32515
+ }
32200
32516
  } catch {
32201
32517
  }
32202
32518
  try {
32203
- if (existsSync10(join14(ctx.root, ".git")) && !db.metaGet("history_files"))
32519
+ if (existsSync11(join15(ctx.root, ".git")) && !db.metaGet("history_files"))
32204
32520
  parts.push(
32205
32521
  'Git history is not classified yet: em_history {action:"overview"} builds it (the server also does this in the background shortly after start); bug-prone files are flagged before you read or edit them once it exists.'
32206
32522
  );
@@ -32236,37 +32552,65 @@ var handlers = {
32236
32552
  SessionEnd: sessionEnd
32237
32553
  };
32238
32554
 
32239
- // src/hook.ts
32240
- async function readStdin() {
32241
- const chunks = [];
32242
- for await (const chunk2 of process.stdin) chunks.push(chunk2);
32243
- return Buffer.concat(chunks).toString("utf8");
32244
- }
32245
- function emit(out) {
32246
- process.stdout.write(JSON.stringify(out));
32247
- }
32248
- async function main() {
32249
- const forced = process.argv.indexOf("--event");
32250
- const raw = await readStdin();
32251
- let input2;
32555
+ // src/hooks/dispatch.ts
32556
+ async function dispatch(raw, argv) {
32557
+ let parsed;
32252
32558
  try {
32253
- input2 = raw.trim() ? JSON.parse(raw) : {};
32559
+ parsed = raw.trim() ? JSON.parse(raw) : {};
32254
32560
  } catch (err) {
32255
32561
  hookLog("error", `bad stdin JSON: ${err.message}`);
32256
- return emit({});
32562
+ return "{}";
32257
32563
  }
32258
- if (forced !== -1 && process.argv[forced + 1]) input2.hook_event_name = process.argv[forced + 1];
32259
- if (process.argv.includes("--selftest")) return emit({ systemMessage: "eminiar hook ok" });
32564
+ const forced = argv.indexOf("--event");
32565
+ if (forced !== -1 && argv[forced + 1]) parsed.hook_event_name = argv[forced + 1];
32566
+ if (argv.includes("--selftest")) return JSON.stringify({ systemMessage: "eminiar hook ok" });
32567
+ if (detectClient(parsed, argv) === "cursor") return runCursor(parsed);
32568
+ const input2 = parsed;
32260
32569
  const handler = handlers[input2.hook_event_name];
32261
- if (!handler) return emit({});
32570
+ if (!handler) return "{}";
32262
32571
  try {
32263
32572
  const out = await handler(input2);
32264
- emit(out ?? {});
32573
+ return JSON.stringify(out ?? {});
32265
32574
  } catch (err) {
32266
32575
  hookLog("error", `${input2.hook_event_name}: ${err.stack ?? String(err)}`);
32267
- emit({});
32576
+ return "{}";
32577
+ }
32578
+ }
32579
+ async function runCursor(parsed) {
32580
+ const input2 = fromCursor(parsed);
32581
+ const event = input2.hook_event_name;
32582
+ let out;
32583
+ const handler = handlers[event];
32584
+ if (handler) {
32585
+ try {
32586
+ out = await handler(input2);
32587
+ } catch (err) {
32588
+ hookLog("error", `cursor ${event}: ${err.stack ?? String(err)}`);
32589
+ }
32590
+ }
32591
+ let root = input2.cwd;
32592
+ try {
32593
+ root = getContext(input2.cwd).root;
32594
+ } catch {
32595
+ }
32596
+ try {
32597
+ return JSON.stringify(toCursor(event, out, { root, sessionId: input2.session_id }));
32598
+ } catch (err) {
32599
+ hookLog("error", `cursor ${event} output: ${err.stack ?? String(err)}`);
32600
+ return event === "PreToolUse" ? '{"permission":"allow"}' : "{}";
32268
32601
  }
32269
32602
  }
32603
+
32604
+ // src/hook.ts
32605
+ async function readStdin() {
32606
+ const chunks = [];
32607
+ for await (const chunk2 of process.stdin) chunks.push(chunk2);
32608
+ return Buffer.concat(chunks).toString("utf8");
32609
+ }
32610
+ async function main() {
32611
+ const raw = await readStdin();
32612
+ process.stdout.write(await dispatch(raw, process.argv.slice(2)));
32613
+ }
32270
32614
  main().catch((err) => {
32271
32615
  hookLog("error", `fatal: ${err.stack ?? String(err)}`);
32272
32616
  process.stdout.write("{}");