grepmax 0.26.27 → 0.26.30

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.
Files changed (50) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +40 -0
  3. package/dist/commands/audit.js +28 -260
  4. package/dist/commands/context.js +42 -26
  5. package/dist/commands/dead.js +37 -35
  6. package/dist/commands/doctor.js +280 -10
  7. package/dist/commands/extract.js +79 -49
  8. package/dist/commands/impact.js +74 -20
  9. package/dist/commands/log.js +25 -25
  10. package/dist/commands/mcp.js +639 -475
  11. package/dist/commands/peek.js +49 -47
  12. package/dist/commands/project.js +29 -107
  13. package/dist/commands/related.js +43 -52
  14. package/dist/commands/search-run.js +23 -8
  15. package/dist/commands/search.js +7 -2
  16. package/dist/commands/similar.js +50 -76
  17. package/dist/commands/skeleton.js +137 -83
  18. package/dist/commands/status.js +90 -25
  19. package/dist/commands/surprises.js +73 -16
  20. package/dist/commands/symbols.js +58 -77
  21. package/dist/commands/test-find.js +51 -17
  22. package/dist/commands/trace.js +26 -11
  23. package/dist/commands/watch.js +1 -1
  24. package/dist/config.js +8 -2
  25. package/dist/lib/analysis/surprising-connections.js +135 -15
  26. package/dist/lib/daemon/daemon.js +157 -13
  27. package/dist/lib/daemon/graph-handler.js +859 -0
  28. package/dist/lib/daemon/ipc-handler.js +36 -4
  29. package/dist/lib/daemon/read-verbs.js +156 -0
  30. package/dist/lib/daemon/rows-handler.js +665 -0
  31. package/dist/lib/daemon/search-handler.js +7 -4
  32. package/dist/lib/daemon/vector-handler.js +199 -0
  33. package/dist/lib/daemon/watch-leases.js +179 -0
  34. package/dist/lib/index/batch-processor.js +7 -1
  35. package/dist/lib/index/watcher.js +4 -0
  36. package/dist/lib/search/pagerank.js +80 -19
  37. package/dist/lib/store/vector-db.js +35 -1
  38. package/dist/lib/utils/async-semaphore.js +141 -0
  39. package/dist/lib/utils/daemon-client.js +11 -0
  40. package/dist/lib/utils/process-footprint.js +51 -0
  41. package/dist/lib/utils/store-access.js +381 -0
  42. package/dist/lib/utils/tests-footer.js +19 -2
  43. package/dist/lib/utils/watcher-launcher.js +46 -19
  44. package/dist/lib/workers/pool.js +72 -2
  45. package/package.json +1 -1
  46. package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
  47. package/plugins/grepmax/hooks/cwd-changed.js +19 -36
  48. package/plugins/grepmax/hooks/start.js +15 -19
  49. package/plugins/grepmax/hooks/stop.js +13 -4
  50. package/plugins/grepmax/hooks/watch-lease.js +163 -0
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
36
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
37
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -12,11 +45,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
45
  exports.surprises = void 0;
13
46
  const commander_1 = require("commander");
14
47
  const surprising_connections_1 = require("../lib/analysis/surprising-connections");
15
- const vector_db_1 = require("../lib/store/vector-db");
48
+ const rows_handler_1 = require("../lib/daemon/rows-handler");
49
+ const vector_handler_1 = require("../lib/daemon/vector-handler");
50
+ const daemon_client_1 = require("../lib/utils/daemon-client");
16
51
  const exit_1 = require("../lib/utils/exit");
17
52
  const project_registry_1 = require("../lib/utils/project-registry");
18
53
  const project_root_1 = require("../lib/utils/project-root");
19
54
  const stale_hint_1 = require("../lib/utils/stale-hint");
55
+ const store_access_1 = require("../lib/utils/store-access");
20
56
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
21
57
  const style = {
22
58
  bold: (s) => (useColors ? `\x1b[1m${s}\x1b[22m` : s),
@@ -108,7 +144,6 @@ exports.surprises = new commander_1.Command("surprises")
108
144
  process.exitCode = 1;
109
145
  return;
110
146
  }
111
- let vectorDb = null;
112
147
  try {
113
148
  const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
114
149
  if (root === null)
@@ -117,10 +152,27 @@ exports.surprises = new commander_1.Command("surprises")
117
152
  (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
118
153
  (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
119
154
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
120
- vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
121
- const table = yield vectorDb.ensureTable();
122
155
  const top = parseIntOption(opts.top, 20, 100);
123
- const result = yield (0, surprising_connections_1.analyzeSurprisingConnections)(table, projectRoot, {
156
+ // --in/--exclude resolve against this process's cwd and filesystem, so
157
+ // they are resolved here and shipped as absolute prefixes.
158
+ // analyzeSurprisingConnections re-resolves them daemon-side; absolute
159
+ // input makes that a no-op, so both paths scan under the same scope.
160
+ const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
161
+ const scope = resolveScope({
162
+ projectRoot,
163
+ in: opts.in,
164
+ exclude: opts.exclude,
165
+ });
166
+ const projectPrefix = projectRoot.endsWith("/")
167
+ ? projectRoot
168
+ : `${projectRoot}/`;
169
+ const analysisIn = scope.inPrefixes.length > 0
170
+ ? scope.inPrefixes
171
+ : scope.pathPrefix !== projectPrefix
172
+ ? [scope.pathPrefix]
173
+ : undefined;
174
+ const analysisExclude = scope.excludePrefixes.length > 0 ? scope.excludePrefixes : undefined;
175
+ const options = {
124
176
  sample: parseIntOption(opts.sample, surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.sample),
125
177
  neighbors: parseIntOption(opts.neighbors, surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.neighbors, 200),
126
178
  dirDepth: parseIntOption(opts.dirDepth, surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.dirDepth, 20),
@@ -128,23 +180,28 @@ exports.surprises = new commander_1.Command("surprises")
128
180
  maxRows: parseIntOption(opts.maxRows, surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.maxRows, surprising_connections_1.MAX_SURPRISE_ROWS),
129
181
  includeTests: Boolean(opts.includeTests),
130
182
  includeEval: Boolean(opts.includeEval),
131
- in: opts.in,
132
- exclude: opts.exclude,
183
+ in: analysisIn,
184
+ exclude: analysisExclude,
185
+ };
186
+ // The scan reads up to MAX_SURPRISE_ROWS rows with their vectors; the
187
+ // daemon runs it against its warm store and returns the summary plus the
188
+ // findings that will actually be printed.
189
+ const result = yield (0, store_access_1.withStoreRead)("surprises", {
190
+ daemon: () => (0, daemon_client_1.sendDaemonCommand)({ cmd: "vector.surprises", projectRoot, options, top }, { timeoutMs: 300000 }),
191
+ render: (resp) => resp,
192
+ inProcess: () => (0, rows_handler_1.withLocalStore)(paths.lancedbDir, (deps) => (0, vector_handler_1.runSurprises)(deps, { projectRoot, options, top })),
193
+ fallbackOnUnknownVerb: true,
133
194
  });
134
195
  console.log(opts.agent ? formatAgent(result, top) : formatHuman(result, top));
135
196
  }
136
197
  catch (error) {
137
- const msg = error instanceof Error ? error.message : "Unknown error";
138
- console.error("Surprises failed:", msg);
139
- process.exitCode = 1;
198
+ if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
199
+ const msg = error instanceof Error ? error.message : "Unknown error";
200
+ console.error("Surprises failed:", msg);
201
+ process.exitCode = 1;
202
+ }
140
203
  }
141
204
  finally {
142
- if (vectorDb) {
143
- try {
144
- yield vectorDb.close();
145
- }
146
- catch (_b) { }
147
- }
148
205
  yield (0, exit_1.gracefulExit)();
149
206
  }
150
207
  }));
@@ -45,81 +45,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.symbols = void 0;
46
46
  const path = __importStar(require("node:path"));
47
47
  const commander_1 = require("commander");
48
- const vector_db_1 = require("../lib/store/vector-db");
48
+ const rows_handler_1 = require("../lib/daemon/rows-handler");
49
+ const daemon_client_1 = require("../lib/utils/daemon-client");
49
50
  const exit_1 = require("../lib/utils/exit");
50
- const filter_builder_1 = require("../lib/utils/filter-builder");
51
51
  const project_registry_1 = require("../lib/utils/project-registry");
52
52
  const project_root_1 = require("../lib/utils/project-root");
53
+ const store_access_1 = require("../lib/utils/store-access");
53
54
  const style = {
54
55
  bold: (s) => `\x1b[1m${s}\x1b[22m`,
55
56
  dim: (s) => `\x1b[2m${s}\x1b[22m`,
56
57
  green: (s) => `\x1b[32m${s}\x1b[39m`,
57
58
  };
58
- function toStringArray(val) {
59
- if (Array.isArray(val))
60
- return val.filter((v) => typeof v === "string");
61
- if (val && typeof val.toArray === "function") {
62
- try {
63
- const arr = val.toArray();
64
- return Array.isArray(arr) ? arr.filter((v) => typeof v === "string") : [];
65
- }
66
- catch (_a) {
67
- return [];
68
- }
69
- }
70
- return [];
71
- }
59
+ /**
60
+ * Ask the daemon for the symbol table; open the store only when no daemon is
61
+ * listening. Both branches run the same `runSymbols`, so the two paths cannot
62
+ * disagree — only the process the query runs in changes.
63
+ */
72
64
  function collectSymbols(options) {
73
65
  return __awaiter(this, void 0, void 0, function* () {
74
66
  const paths = (0, project_root_1.ensureProjectPaths)(options.projectRoot);
75
- const db = new vector_db_1.VectorDB(paths.lancedbDir);
76
- try {
77
- const table = yield db.ensureTable();
78
- let query = table
79
- .query()
80
- .select(["defined_symbols", "path", "start_line"])
81
- .where("array_length(defined_symbols) > 0")
82
- // Fetch more rows to ensure we have enough after filtering/aggregation
83
- .limit(options.pattern ? 10000 : Math.max(options.limit * 50, 2000));
84
- if (options.pathPrefix) {
85
- // Resolve to absolute path for centralized index
86
- const absPrefix = path.isAbsolute(options.pathPrefix)
87
- ? options.pathPrefix
88
- : path.resolve(options.projectRoot, options.pathPrefix);
89
- query = query.where((0, filter_builder_1.pathStartsWith)((0, filter_builder_1.normalizePath)(absPrefix)));
90
- }
91
- const rows = yield query.toArray();
92
- const map = new Map();
93
- for (const row of rows) {
94
- const defs = toStringArray(row.defined_symbols);
95
- const path = String(row.path || "");
96
- const line = Number(row.start_line || 0);
97
- for (const sym of defs) {
98
- if (options.pattern &&
99
- !sym.toLowerCase().includes(options.pattern.toLowerCase())) {
100
- continue;
101
- }
102
- const existing = map.get(sym);
103
- if (existing) {
104
- existing.count += 1;
105
- }
106
- else {
107
- map.set(sym, { symbol: sym, count: 1, path, line });
108
- }
109
- }
110
- }
111
- return Array.from(map.values())
112
- .sort((a, b) => {
113
- // Sort by count desc, then symbol asc
114
- if (b.count !== a.count)
115
- return b.count - a.count;
116
- return a.symbol.localeCompare(b.symbol);
117
- })
118
- .slice(0, options.limit);
119
- }
120
- finally {
121
- yield db.close();
122
- }
67
+ // Resolve to absolute for the centralized index. No trailing slash: the
68
+ // query has always matched starts_with on the bare prefix.
69
+ const absPrefix = options.pathPrefix
70
+ ? path.isAbsolute(options.pathPrefix)
71
+ ? options.pathPrefix
72
+ : path.resolve(options.projectRoot, options.pathPrefix)
73
+ : undefined;
74
+ return (0, store_access_1.withStoreRead)("symbols", {
75
+ daemon: () => (0, daemon_client_1.sendDaemonCommand)({
76
+ cmd: "rows.symbols",
77
+ projectRoot: options.projectRoot,
78
+ pathPrefix: absPrefix,
79
+ pattern: options.pattern,
80
+ limit: options.limit,
81
+ }, { timeoutMs: 60000 }),
82
+ render: (resp) => { var _a; return ((_a = resp.entries) !== null && _a !== void 0 ? _a : []); },
83
+ inProcess: () => (0, rows_handler_1.withLocalStore)(paths.lancedbDir, (deps) => (0, rows_handler_1.runSymbols)(deps, {
84
+ projectRoot: options.projectRoot,
85
+ pathPrefix: absPrefix,
86
+ pattern: options.pattern,
87
+ limit: options.limit,
88
+ })),
89
+ fallbackOnUnknownVerb: true,
90
+ });
123
91
  });
124
92
  }
125
93
  function formatTable(entries) {
@@ -182,20 +150,33 @@ exports.symbols = new commander_1.Command("symbols")
182
150
  const limit = Number.parseInt(cmd.limit, 10);
183
151
  // Auto-scope to project root; --path narrows further within it
184
152
  const pathPrefix = (_b = cmd.path) !== null && _b !== void 0 ? _b : projectRoot;
185
- const entries = yield collectSymbols({
186
- projectRoot,
187
- limit: Number.isFinite(limit) && limit > 0 ? limit : 20,
188
- pathPrefix,
189
- pattern: pattern,
190
- });
191
- if (cmd.agent) {
192
- console.log(formatAgent(entries, projectRoot));
153
+ try {
154
+ const entries = yield collectSymbols({
155
+ projectRoot,
156
+ limit: Number.isFinite(limit) && limit > 0 ? limit : 20,
157
+ pathPrefix,
158
+ pattern: pattern,
159
+ });
160
+ if (cmd.agent) {
161
+ console.log(formatAgent(entries, projectRoot));
162
+ }
163
+ else {
164
+ console.log(`${style.bold("Project")}: ${style.green(projectRoot)}\n${formatTable(entries)}`);
165
+ }
166
+ if (entries.length === 0) {
167
+ process.exitCode = 1;
168
+ }
193
169
  }
194
- else {
195
- console.log(`${style.bold("Project")}: ${style.green(projectRoot)}\n${formatTable(entries)}`);
170
+ catch (error) {
171
+ // `symbols` was the one read command with no catch at all, so a denied
172
+ // lease escaped as a raw node:fs stack trace instead of the one-line hint.
173
+ if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
174
+ const msg = error instanceof Error ? error.message : "Unknown error";
175
+ console.error("Symbols failed:", msg);
176
+ process.exitCode = 1;
177
+ }
196
178
  }
197
- if (entries.length === 0) {
198
- process.exitCode = 1;
179
+ finally {
180
+ yield (0, exit_1.gracefulExit)();
199
181
  }
200
- yield (0, exit_1.gracefulExit)();
201
182
  }));
@@ -44,13 +44,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.testFind = void 0;
46
46
  const commander_1 = require("commander");
47
- const impact_1 = require("../lib/graph/impact");
47
+ const graph_handler_1 = require("../lib/daemon/graph-handler");
48
48
  const test_hits_1 = require("../lib/graph/test-hits");
49
49
  const vector_db_1 = require("../lib/store/vector-db");
50
50
  const exit_1 = require("../lib/utils/exit");
51
51
  const project_registry_1 = require("../lib/utils/project-registry");
52
52
  const project_root_1 = require("../lib/utils/project-root");
53
53
  const stale_hint_1 = require("../lib/utils/stale-hint");
54
+ const store_access_1 = require("../lib/utils/store-access");
54
55
  exports.testFind = new commander_1.Command("test")
55
56
  .description("Find tests that exercise a symbol or file")
56
57
  .argument("<target>", "Symbol name or file path")
@@ -62,7 +63,8 @@ exports.testFind = new commander_1.Command("test")
62
63
  .action((target, opts) => __awaiter(void 0, void 0, void 0, function* () {
63
64
  var _a;
64
65
  const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
65
- let vectorDb = null;
66
+ // Opened only if the daemon is unreachable; withStoreRead decides.
67
+ const local = { db: null };
66
68
  try {
67
69
  const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
68
70
  if (root === null)
@@ -70,9 +72,31 @@ exports.testFind = new commander_1.Command("test")
70
72
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
71
73
  (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
72
74
  (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
73
- const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
74
- vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
75
- const { symbols, resolvedAsFile, symbolFamilies } = yield (0, impact_1.resolveTargetSymbols)(target, vectorDb, projectRoot);
75
+ const localDb = () => {
76
+ var _a;
77
+ (_a = local.db) !== null && _a !== void 0 ? _a : (local.db = new vector_db_1.VectorDB((0, project_root_1.ensureProjectPaths)(projectRoot).lancedbDir));
78
+ return local.db;
79
+ };
80
+ const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
81
+ const scope = resolveScope({
82
+ projectRoot,
83
+ in: opts.in,
84
+ exclude: opts.exclude,
85
+ });
86
+ const { symbols, resolvedAsFile, symbolFamilies } = yield (0, graph_handler_1.callGraphVerb)("graph.resolve", {
87
+ projectRoot,
88
+ scope,
89
+ payload: { target },
90
+ render: (resp) => {
91
+ var _a, _b;
92
+ return ({
93
+ symbols: (_a = resp.symbols) !== null && _a !== void 0 ? _a : [],
94
+ resolvedAsFile: resp.resolvedAsFile === true,
95
+ symbolFamilies: (_b = resp.symbolFamilies) !== null && _b !== void 0 ? _b : null,
96
+ });
97
+ },
98
+ inProcess: () => (0, graph_handler_1.runGraphResolve)(localDb(), { target, projectRoot }),
99
+ });
76
100
  if (symbols.length === 0) {
77
101
  console.log(resolvedAsFile
78
102
  ? `No symbols found in file: ${target}`
@@ -80,16 +104,22 @@ exports.testFind = new commander_1.Command("test")
80
104
  process.exitCode = 1;
81
105
  return;
82
106
  }
83
- const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
84
- const scope = resolveScope({
85
- projectRoot,
86
- in: opts.in,
87
- exclude: opts.exclude,
88
- });
89
107
  const queryRoot = opts.in && opts.in.length > 0
90
108
  ? scope.pathPrefix.replace(/\/$/, "")
91
109
  : projectRoot;
92
- const tests = yield (0, impact_1.findTests)(symbols, vectorDb, queryRoot, depth, scope.excludePrefixes, symbolFamilies);
110
+ const tests = yield (0, graph_handler_1.callGraphVerb)("graph.tests", {
111
+ projectRoot,
112
+ scope,
113
+ payload: { symbols, depth, families: symbolFamilies },
114
+ render: (resp) => { var _a; return (_a = resp.hits) !== null && _a !== void 0 ? _a : []; },
115
+ inProcess: () => (0, graph_handler_1.runGraphTests)(localDb(), {
116
+ symbols,
117
+ queryRoot,
118
+ depth,
119
+ excludePrefixes: scope.excludePrefixes,
120
+ families: (0, graph_handler_1.decodeSymbolFamilies)(symbolFamilies),
121
+ }),
122
+ });
93
123
  if (tests.length === 0) {
94
124
  console.log(`No tests found for ${target}.`);
95
125
  return;
@@ -111,14 +141,18 @@ exports.testFind = new commander_1.Command("test")
111
141
  }
112
142
  }
113
143
  catch (error) {
114
- const msg = error instanceof Error ? error.message : "Unknown error";
115
- console.error("Test find failed:", msg);
116
- process.exitCode = 1;
144
+ // A sandbox refusal is already one actionable line; a "Test find failed:"
145
+ // prefix would bury the settings key that fixes it.
146
+ if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
147
+ const msg = error instanceof Error ? error.message : "Unknown error";
148
+ console.error("Test find failed:", msg);
149
+ process.exitCode = 1;
150
+ }
117
151
  }
118
152
  finally {
119
- if (vectorDb) {
153
+ if (local.db) {
120
154
  try {
121
- yield vectorDb.close();
155
+ yield local.db.close();
122
156
  }
123
157
  catch (_b) { }
124
158
  }
@@ -44,8 +44,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.trace = void 0;
46
46
  const commander_1 = require("commander");
47
+ const graph_handler_1 = require("../lib/daemon/graph-handler");
47
48
  const callsites_1 = require("../lib/graph/callsites");
48
- const graph_builder_1 = require("../lib/graph/graph-builder");
49
49
  const formatter_1 = require("../lib/output/formatter");
50
50
  const vector_db_1 = require("../lib/store/vector-db");
51
51
  const agent_errors_1 = require("../lib/utils/agent-errors");
@@ -53,6 +53,7 @@ const exit_1 = require("../lib/utils/exit");
53
53
  const project_registry_1 = require("../lib/utils/project-registry");
54
54
  const project_root_1 = require("../lib/utils/project-root");
55
55
  const stale_hint_1 = require("../lib/utils/stale-hint");
56
+ const store_access_1 = require("../lib/utils/store-access");
56
57
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
57
58
  const dim = (s) => (useColors ? `\x1b[2m${s}\x1b[22m` : s);
58
59
  const bold = (s) => (useColors ? `\x1b[1m${s}\x1b[22m` : s);
@@ -231,21 +232,31 @@ exports.trace = new commander_1.Command("trace")
231
232
  const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
232
233
  if (root === null)
233
234
  return;
234
- let vectorDb = null;
235
+ // Opened only if the daemon is unreachable; withStoreRead decides.
236
+ const local = { db: null };
235
237
  try {
236
238
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
237
239
  (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
238
240
  (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
239
- const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
240
- vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
241
241
  const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
242
242
  const scope = resolveScope({
243
243
  projectRoot,
244
244
  in: opts.in,
245
245
  exclude: opts.exclude,
246
246
  });
247
- const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, scope.pathPrefix, scope.excludePrefixes);
248
- const graph = yield graphBuilder.buildGraphMultiHop(symbol, depth);
247
+ // The daemon returns the graph; call-site snippets below are read from
248
+ // the working tree by this process, which can already see it.
249
+ const graph = yield (0, graph_handler_1.callGraphVerb)("graph.trace", {
250
+ projectRoot,
251
+ scope,
252
+ payload: { target: symbol, hops: depth },
253
+ render: (resp) => resp.graph,
254
+ inProcess: () => {
255
+ var _a;
256
+ (_a = local.db) !== null && _a !== void 0 ? _a : (local.db = new vector_db_1.VectorDB((0, project_root_1.ensureProjectPaths)(projectRoot).lancedbDir));
257
+ return (0, graph_handler_1.runGraphTrace)(local.db, { symbol, hops: depth, scope });
258
+ },
259
+ });
249
260
  if (opts.inbound) {
250
261
  if (!graph.center) {
251
262
  console.log((0, agent_errors_1.symbolNotFoundLines)(symbol, { agent: opts.agent }).join("\n"));
@@ -275,14 +286,18 @@ exports.trace = new commander_1.Command("trace")
275
286
  }
276
287
  }
277
288
  catch (error) {
278
- const message = error instanceof Error ? error.message : "Unknown error";
279
- console.error("Trace failed:", message);
280
- process.exitCode = 1;
289
+ // A sandbox refusal is already one actionable line; a "Trace failed:"
290
+ // prefix would bury the settings key that fixes it.
291
+ if (!(0, store_access_1.reportStoreAccessRefusal)(error)) {
292
+ const message = error instanceof Error ? error.message : "Unknown error";
293
+ console.error("Trace failed:", message);
294
+ process.exitCode = 1;
295
+ }
281
296
  }
282
297
  finally {
283
- if (vectorDb) {
298
+ if (local.db) {
284
299
  try {
285
- yield vectorDb.close();
300
+ yield local.db.close();
286
301
  }
287
302
  catch (err) {
288
303
  console.error("Failed to close VectorDB:", err);
@@ -67,7 +67,7 @@ const IDLE_CHECK_INTERVAL_MS = 60 * 1000; // check every minute
67
67
  exports.watch = new commander_1.Command("watch")
68
68
  .description("Start background file watcher for live reindexing")
69
69
  .option("-b, --background", "Run watcher in background and exit")
70
- .option("-d, --daemon", "Run as centralized daemon watching all projects")
70
+ .option("-d, --daemon", "Run as centralized daemon watching projects that active sessions hold")
71
71
  .option("-p, --path <dir>", "Directory to watch (defaults to project root)")
72
72
  .option("--no-idle-timeout", "Disable the 30-minute idle shutdown")
73
73
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
package/dist/config.js CHANGED
@@ -68,9 +68,14 @@ const DEFAULT_WORKER_THREADS = (() => {
68
68
  const fromEnv = Number.parseInt((_a = process.env.GMAX_WORKER_THREADS) !== null && _a !== void 0 ? _a : "", 10);
69
69
  if (Number.isFinite(fromEnv) && fromEnv > 0)
70
70
  return fromEnv;
71
+ // Each worker is a separate process that loads the ONNX models, peaking at
72
+ // ~800 MB-1 GB. The old max(4, cores/2) let a 14-core host run 7 of them;
73
+ // together with the daemon that froze a 48 GB machine. Four is the ceiling;
74
+ // two is the floor where the cores exist, so one worker can stay free for
75
+ // search. GMAX_WORKER_THREADS still overrides.
71
76
  const cores = os.cpus().length || 1;
72
- const HARD_CAP = Math.max(4, Math.floor(cores * 0.5));
73
- return Math.max(1, Math.min(HARD_CAP, cores));
77
+ const target = Math.min(4, Math.max(2, Math.floor(cores * 0.5)));
78
+ return Math.max(1, Math.min(target, cores));
74
79
  })();
75
80
  exports.CONFIG = {
76
81
  VECTOR_DIM: 384,
@@ -224,6 +229,7 @@ exports.PATHS = {
224
229
  daemonSocket: path.join(GLOBAL_ROOT, "daemon.sock"),
225
230
  daemonPidFile: path.join(GLOBAL_ROOT, "daemon.pid"),
226
231
  daemonLockFile: path.join(GLOBAL_ROOT, "daemon.lock"),
232
+ watchLeasesFile: path.join(GLOBAL_ROOT, "watch-leases.json"),
227
233
  // Written by a daemon while it is gracefully shutting down (draining workers,
228
234
  // closing LanceDB). A successor's killStaleProcesses() respects this so it
229
235
  // never SIGKILLs a peer mid-cleanup once the peer has already dropped its