grepmax 0.7.21 → 0.7.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,12 +100,15 @@ In our public benchmarks, `grepmax` can save about 20% of your LLM tokens and de
100
100
 
101
101
  | Tool | Description |
102
102
  | --- | --- |
103
- | `semantic_search` | Code search by meaning. Returns pointers (symbol, file:line, role, calls, summary) by default. Use `root` for cross-directory search, `detail: "code"` for snippets. |
104
- | `search_all` | Search ALL indexed code across every directory. Same pointer format. |
105
- | `code_skeleton` | Collapsed file structure (~4x fewer tokens than reading the full file) |
106
- | `trace_calls` | Call graph who calls a symbol and what it calls (unscoped, crosses project boundaries) |
107
- | `list_symbols` | List indexed functions, classes, and types with definition locations |
108
- | `index_status` | Check index health: chunk counts, indexed directories, model info |
103
+ | `semantic_search` | Code search by meaning. 16 composable params: query, limit, root, path, detail (pointer/code/full), context_lines, min_score, max_per_file, file, exclude, language, role, mode (symbol), include_imports, name_pattern. |
104
+ | `search_all` | Search ALL indexed code. Same params + `projects`/`exclude_projects` to scope by project name. |
105
+ | `code_skeleton` | Collapsed file structure (~4x fewer tokens). Accepts files, directories, or comma-separated paths. `format: "json"` for structured output. |
106
+ | `trace_calls` | Call graph with importers, callers (multi-hop via `depth`), and callees with file:line locations. |
107
+ | `list_symbols` | List indexed symbols with role (ORCH/DEF/IMPL) and export status. |
108
+ | `summarize_project` | High-level project overview languages, directory structure, roles, key symbols, entry points. |
109
+ | `related_files` | Find dependencies and dependents of a file by shared symbol references. |
110
+ | `recent_changes` | Recently modified indexed files with relative timestamps. |
111
+ | `index_status` | Check index health: per-project chunk counts, model info, watcher status. |
109
112
  | `summarize_directory` | Generate LLM summaries for indexed chunks. Summaries appear in search results. |
110
113
 
111
114
  ## Commands
@@ -125,8 +128,17 @@ gmax "how is the database connection pooled?"
125
128
  | `-m <n>` | Max total results to return. | `5` |
126
129
  | `--per-file <n>` | Max matches to show per file. | `3` |
127
130
  | `-c`, `--content` | Show full chunk content instead of snippets. | `false` |
131
+ | `-C <n>`, `--context <n>` | Include N lines before/after each result. | `0` |
128
132
  | `--scores` | Show relevance scores (0-1) for each result. | `false` |
129
133
  | `--min-score <n>` | Filter out results below this score threshold. | `0` |
134
+ | `--root <dir>` | Search a different project directory. | cwd |
135
+ | `--file <name>` | Filter to files matching this name (e.g. `syncer.ts`). | — |
136
+ | `--exclude <prefix>` | Exclude files under this path prefix (e.g. `tests/`). | — |
137
+ | `--lang <ext>` | Filter by file extension (e.g. `ts`, `py`). | — |
138
+ | `--role <role>` | Filter by role: `ORCHESTRATION`, `DEFINITION`, `IMPLEMENTATION`. | — |
139
+ | `--symbol` | Append call graph (importers, callers, callees) after results. | `false` |
140
+ | `--imports` | Prepend file imports to each result. | `false` |
141
+ | `--name <regex>` | Filter results by symbol name regex. | — |
130
142
  | `--compact` | Compact hits view (paths + line ranges + role/preview). | `false` |
131
143
  | `--skeleton` | Show code skeleton for matching files instead of snippets. | `false` |
132
144
  | `--plain` | Disable ANSI colors and use simpler formatting. | `false` |
@@ -136,10 +148,11 @@ gmax "how is the database connection pooled?"
136
148
 
137
149
  ```bash
138
150
  gmax "API rate limiting logic"
139
- gmax "error handling" --per-file 5
140
- gmax "user validation" --compact
141
- gmax "authentication" --scores --min-score 0.5
142
- gmax "database connection" --skeleton
151
+ gmax "auth handler" --role ORCHESTRATION --lang ts --plain
152
+ gmax "database" --file syncer.ts --plain
153
+ gmax "VectorDB" --symbol --plain
154
+ gmax "error handling" -C 5 --imports
155
+ gmax "handler" --name "handle.*" --exclude tests/
143
156
  ```
144
157
 
145
158
  ### `gmax index`
@@ -182,18 +195,57 @@ gmax serve --background # Background mode
182
195
  gmax serve --cpu # Force CPU-only embeddings
183
196
  ```
184
197
 
198
+ ### `gmax trace`
199
+
200
+ Call graph — who imports a symbol, who calls it, and what it calls.
201
+
202
+ ```bash
203
+ gmax trace handleAuth # 1-hop trace
204
+ gmax trace handleAuth -d 2 # 2-hop: callers-of-callers
205
+ ```
206
+
185
207
  ### `gmax skeleton`
186
208
 
187
- Compressed view of a file — signatures with bodies collapsed.
209
+ Compressed view of a file — signatures with bodies collapsed. Supports files, directories, and batch.
188
210
 
189
211
  ```bash
190
- gmax skeleton src/lib/auth.ts
191
- gmax skeleton AuthService # Find symbol, skeletonize its file
192
- gmax skeleton "auth logic" # Search, skeletonize top matches
212
+ gmax skeleton src/lib/auth.ts # Single file
213
+ gmax skeleton src/lib/search/ # All files in directory
214
+ gmax skeleton src/a.ts,src/b.ts # Batch
215
+ gmax skeleton src/lib/auth.ts --json # Structured JSON output
216
+ gmax skeleton AuthService # Find symbol, skeletonize its file
193
217
  ```
194
218
 
195
219
  **Supported Languages:** TypeScript, JavaScript, Python, Go, Rust, Java, C#, C++, C, Ruby, PHP, Swift, Kotlin.
196
220
 
221
+ ### `gmax project`
222
+
223
+ High-level project overview — languages, directory structure, role distribution, key symbols, entry points.
224
+
225
+ ```bash
226
+ gmax project # Current project
227
+ gmax project --root ~/workspace # Different project
228
+ ```
229
+
230
+ ### `gmax related`
231
+
232
+ Find files related by shared symbol references — dependencies and dependents.
233
+
234
+ ```bash
235
+ gmax related src/lib/index/syncer.ts
236
+ gmax related src/commands/mcp.ts -l 5
237
+ ```
238
+
239
+ ### `gmax recent`
240
+
241
+ Show recently modified indexed files with relative timestamps.
242
+
243
+ ```bash
244
+ gmax recent # Last 20 modified files
245
+ gmax recent -l 10 # Last 10
246
+ gmax recent --root ~/workspace # Different project
247
+ ```
248
+
197
249
  ### `gmax config`
198
250
 
199
251
  View or update configuration without the full interactive setup.
@@ -0,0 +1,175 @@
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
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.project = void 0;
46
+ const path = __importStar(require("node:path"));
47
+ const commander_1 = require("commander");
48
+ const vector_db_1 = require("../lib/store/vector-db");
49
+ const filter_builder_1 = require("../lib/utils/filter-builder");
50
+ const exit_1 = require("../lib/utils/exit");
51
+ const project_registry_1 = require("../lib/utils/project-registry");
52
+ const project_root_1 = require("../lib/utils/project-root");
53
+ function toArr(val) {
54
+ if (val && typeof val.toArray === "function") {
55
+ return val.toArray();
56
+ }
57
+ return Array.isArray(val) ? val : [];
58
+ }
59
+ exports.project = new commander_1.Command("project")
60
+ .description("Show project overview — languages, structure, key symbols")
61
+ .option("--root <dir>", "Project root (defaults to current directory)")
62
+ .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
63
+ var _a, _b, _c;
64
+ let vectorDb = null;
65
+ try {
66
+ const root = opts.root
67
+ ? (_a = (0, project_root_1.findProjectRoot)(path.resolve(opts.root))) !== null && _a !== void 0 ? _a : path.resolve(opts.root)
68
+ : (_b = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _b !== void 0 ? _b : process.cwd();
69
+ const prefix = root.endsWith("/") ? root : `${root}/`;
70
+ const projectName = path.basename(root);
71
+ const paths = (0, project_root_1.ensureProjectPaths)(root);
72
+ vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
73
+ const table = yield vectorDb.ensureTable();
74
+ const rows = yield table
75
+ .query()
76
+ .select([
77
+ "path",
78
+ "role",
79
+ "is_exported",
80
+ "complexity",
81
+ "defined_symbols",
82
+ "referenced_symbols",
83
+ ])
84
+ .where(`path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
85
+ .limit(200000)
86
+ .toArray();
87
+ if (rows.length === 0) {
88
+ console.log(`No indexed data found for ${root}. Run: gmax index --path ${root}`);
89
+ return;
90
+ }
91
+ const files = new Set();
92
+ const extCounts = new Map();
93
+ const dirCounts = new Map();
94
+ const roleCounts = new Map();
95
+ const symbolRefs = new Map();
96
+ const entryPoints = [];
97
+ for (const row of rows) {
98
+ const p = String(row.path || "");
99
+ const role = String(row.role || "IMPLEMENTATION");
100
+ const exported = Boolean(row.is_exported);
101
+ const complexity = Number(row.complexity || 0);
102
+ const defs = toArr(row.defined_symbols);
103
+ const refs = toArr(row.referenced_symbols);
104
+ files.add(p);
105
+ const ext = path.extname(p).toLowerCase() || path.basename(p);
106
+ extCounts.set(ext, (extCounts.get(ext) || 0) + 1);
107
+ const rel = p.startsWith(prefix) ? p.slice(prefix.length) : p;
108
+ const parts = rel.split("/");
109
+ const dir = parts.length > 2
110
+ ? `${parts.slice(0, 2).join("/")}/`
111
+ : parts.length > 1
112
+ ? `${parts[0]}/`
113
+ : "(root)";
114
+ if (!dirCounts.has(dir))
115
+ dirCounts.set(dir, { files: new Set(), chunks: 0 });
116
+ const dc = dirCounts.get(dir);
117
+ dc.files.add(p);
118
+ dc.chunks++;
119
+ roleCounts.set(role, (roleCounts.get(role) || 0) + 1);
120
+ for (const ref of refs)
121
+ symbolRefs.set(ref, (symbolRefs.get(ref) || 0) + 1);
122
+ if (exported && role === "ORCHESTRATION" && complexity >= 5 && defs.length > 0) {
123
+ entryPoints.push({
124
+ symbol: defs[0],
125
+ path: p.startsWith(prefix) ? p.slice(prefix.length) : p,
126
+ });
127
+ }
128
+ }
129
+ const projects = (0, project_registry_1.listProjects)();
130
+ const proj = projects.find((p) => p.root === root);
131
+ console.log(`Project: ${projectName} (${root})`);
132
+ console.log(`Last indexed: ${(_c = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _c !== void 0 ? _c : "unknown"} • ${rows.length} chunks • ${files.size} files\n`);
133
+ const extEntries = Array.from(extCounts.entries())
134
+ .sort((a, b) => b[1] - a[1])
135
+ .slice(0, 8);
136
+ console.log(`Languages: ${extEntries.map(([ext, count]) => `${ext} (${Math.round((count / rows.length) * 100)}%)`).join(", ")}\n`);
137
+ console.log("Directory structure:");
138
+ for (const [dir, data] of Array.from(dirCounts.entries())
139
+ .sort((a, b) => b[1].chunks - a[1].chunks)
140
+ .slice(0, 12)) {
141
+ console.log(` ${dir.padEnd(25)} (${data.files.size} files, ${data.chunks} chunks)`);
142
+ }
143
+ const roleEntries = Array.from(roleCounts.entries()).sort((a, b) => b[1] - a[1]);
144
+ console.log(`\nRoles: ${roleEntries.map(([r, c]) => `${Math.round((c / rows.length) * 100)}% ${r}`).join(", ")}\n`);
145
+ const topSymbols = Array.from(symbolRefs.entries())
146
+ .sort((a, b) => b[1] - a[1])
147
+ .slice(0, 8);
148
+ if (topSymbols.length > 0) {
149
+ console.log("Key symbols (by reference count):");
150
+ for (const [sym, count] of topSymbols) {
151
+ console.log(` ${sym.padEnd(25)} (referenced ${count}x)`);
152
+ }
153
+ }
154
+ if (entryPoints.length > 0) {
155
+ console.log("\nEntry points (exported orchestration):");
156
+ for (const ep of entryPoints.slice(0, 10)) {
157
+ console.log(` ${ep.symbol.padEnd(25)} ${ep.path}`);
158
+ }
159
+ }
160
+ }
161
+ catch (error) {
162
+ const msg = error instanceof Error ? error.message : "Unknown error";
163
+ console.error("Project summary failed:", msg);
164
+ process.exitCode = 1;
165
+ }
166
+ finally {
167
+ if (vectorDb) {
168
+ try {
169
+ yield vectorDb.close();
170
+ }
171
+ catch (_d) { }
172
+ }
173
+ yield (0, exit_1.gracefulExit)();
174
+ }
175
+ }));
@@ -0,0 +1,131 @@
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
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
45
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
46
+ var m = o[Symbol.asyncIterator], i;
47
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
48
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
49
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
50
+ };
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.recent = void 0;
53
+ const path = __importStar(require("node:path"));
54
+ const commander_1 = require("commander");
55
+ const config_1 = require("../config");
56
+ const meta_cache_1 = require("../lib/store/meta-cache");
57
+ const exit_1 = require("../lib/utils/exit");
58
+ const project_root_1 = require("../lib/utils/project-root");
59
+ function formatTimeAgo(ms) {
60
+ const sec = Math.floor(ms / 1000);
61
+ if (sec < 60)
62
+ return `${sec}s ago`;
63
+ const min = Math.floor(sec / 60);
64
+ if (min < 60)
65
+ return `${min}m ago`;
66
+ const hr = Math.floor(min / 60);
67
+ if (hr < 24)
68
+ return `${hr}h ago`;
69
+ const days = Math.floor(hr / 24);
70
+ return `${days}d ago`;
71
+ }
72
+ exports.recent = new commander_1.Command("recent")
73
+ .description("Show recently modified indexed files")
74
+ .option("-l, --limit <n>", "Max files (default 20)", "20")
75
+ .option("--root <dir>", "Project root (defaults to current directory)")
76
+ .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
77
+ var _a, e_1, _b, _c;
78
+ var _d, _e;
79
+ const limit = Math.min(Math.max(Number.parseInt(opts.limit || "20", 10), 1), 50);
80
+ try {
81
+ const root = opts.root
82
+ ? (_d = (0, project_root_1.findProjectRoot)(path.resolve(opts.root))) !== null && _d !== void 0 ? _d : path.resolve(opts.root)
83
+ : (_e = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _e !== void 0 ? _e : process.cwd();
84
+ const prefix = root.endsWith("/") ? root : `${root}/`;
85
+ const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
86
+ try {
87
+ const files = [];
88
+ try {
89
+ for (var _f = true, _g = __asyncValues(metaCache.entries()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
90
+ _c = _h.value;
91
+ _f = false;
92
+ const { path: p, entry } = _c;
93
+ if (p.startsWith(prefix)) {
94
+ files.push({ path: p, mtimeMs: entry.mtimeMs });
95
+ }
96
+ }
97
+ }
98
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
99
+ finally {
100
+ try {
101
+ if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
102
+ }
103
+ finally { if (e_1) throw e_1.error; }
104
+ }
105
+ files.sort((a, b) => b.mtimeMs - a.mtimeMs);
106
+ const top = files.slice(0, limit);
107
+ if (top.length === 0) {
108
+ console.log(`No indexed files found for ${root}`);
109
+ return;
110
+ }
111
+ const now = Date.now();
112
+ console.log(`Recent changes in ${path.basename(root)} (${top.length} most recent):\n`);
113
+ for (const f of top) {
114
+ const rel = f.path.startsWith(prefix)
115
+ ? f.path.slice(prefix.length)
116
+ : f.path;
117
+ const ago = formatTimeAgo(now - f.mtimeMs);
118
+ console.log(` ${ago.padEnd(10)} ${rel}`);
119
+ }
120
+ }
121
+ finally {
122
+ yield metaCache.close();
123
+ }
124
+ }
125
+ catch (error) {
126
+ const msg = error instanceof Error ? error.message : "Unknown error";
127
+ console.error("Recent changes failed:", msg);
128
+ process.exitCode = 1;
129
+ }
130
+ yield (0, exit_1.gracefulExit)();
131
+ }));
@@ -0,0 +1,170 @@
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
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.related = void 0;
46
+ const path = __importStar(require("node:path"));
47
+ const commander_1 = require("commander");
48
+ const vector_db_1 = require("../lib/store/vector-db");
49
+ const filter_builder_1 = require("../lib/utils/filter-builder");
50
+ const exit_1 = require("../lib/utils/exit");
51
+ const project_root_1 = require("../lib/utils/project-root");
52
+ function toArr(val) {
53
+ if (val && typeof val.toArray === "function") {
54
+ return val.toArray();
55
+ }
56
+ return Array.isArray(val) ? val : [];
57
+ }
58
+ exports.related = new commander_1.Command("related")
59
+ .description("Find files related by shared symbol references")
60
+ .argument("<file>", "File path relative to project root")
61
+ .option("-l, --limit <n>", "Max results per direction (default 10)", "10")
62
+ .action((file, opts) => __awaiter(void 0, void 0, void 0, function* () {
63
+ var _a;
64
+ const limit = Math.min(Math.max(Number.parseInt(opts.limit || "10", 10), 1), 25);
65
+ let vectorDb = null;
66
+ try {
67
+ const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
68
+ const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
69
+ vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
70
+ const absPath = path.resolve(projectRoot, file);
71
+ const table = yield vectorDb.ensureTable();
72
+ const fileChunks = yield table
73
+ .query()
74
+ .select(["defined_symbols", "referenced_symbols"])
75
+ .where(`path = '${(0, filter_builder_1.escapeSqlString)(absPath)}'`)
76
+ .toArray();
77
+ if (fileChunks.length === 0) {
78
+ console.log(`File not found in index: ${file}`);
79
+ return;
80
+ }
81
+ const definedHere = new Set();
82
+ const referencedHere = new Set();
83
+ for (const chunk of fileChunks) {
84
+ for (const s of toArr(chunk.defined_symbols))
85
+ definedHere.add(s);
86
+ for (const s of toArr(chunk.referenced_symbols))
87
+ referencedHere.add(s);
88
+ }
89
+ // Dependencies
90
+ const depCounts = new Map();
91
+ for (const sym of referencedHere) {
92
+ if (definedHere.has(sym))
93
+ continue;
94
+ const rows = yield table
95
+ .query()
96
+ .select(["path"])
97
+ .where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
98
+ .limit(3)
99
+ .toArray();
100
+ for (const row of rows) {
101
+ const p = String(row.path || "");
102
+ if (p === absPath)
103
+ continue;
104
+ depCounts.set(p, (depCounts.get(p) || 0) + 1);
105
+ }
106
+ }
107
+ // Dependents
108
+ const revCounts = new Map();
109
+ for (const sym of definedHere) {
110
+ const rows = yield table
111
+ .query()
112
+ .select(["path"])
113
+ .where(`array_contains(referenced_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
114
+ .limit(20)
115
+ .toArray();
116
+ for (const row of rows) {
117
+ const p = String(row.path || "");
118
+ if (p === absPath)
119
+ continue;
120
+ revCounts.set(p, (revCounts.get(p) || 0) + 1);
121
+ }
122
+ }
123
+ console.log(`Related files for ${file}:\n`);
124
+ const topDeps = Array.from(depCounts.entries())
125
+ .sort((a, b) => b[1] - a[1])
126
+ .slice(0, limit);
127
+ if (topDeps.length > 0) {
128
+ console.log("Dependencies (files this imports/calls):");
129
+ for (const [p, count] of topDeps) {
130
+ const rel = p.startsWith(`${projectRoot}/`)
131
+ ? p.slice(projectRoot.length + 1)
132
+ : p;
133
+ console.log(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
134
+ }
135
+ }
136
+ else {
137
+ console.log("Dependencies: none found");
138
+ }
139
+ console.log("");
140
+ const topRevs = Array.from(revCounts.entries())
141
+ .sort((a, b) => b[1] - a[1])
142
+ .slice(0, limit);
143
+ if (topRevs.length > 0) {
144
+ console.log("Dependents (files that call this):");
145
+ for (const [p, count] of topRevs) {
146
+ const rel = p.startsWith(`${projectRoot}/`)
147
+ ? p.slice(projectRoot.length + 1)
148
+ : p;
149
+ console.log(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
150
+ }
151
+ }
152
+ else {
153
+ console.log("Dependents: none found");
154
+ }
155
+ }
156
+ catch (error) {
157
+ const msg = error instanceof Error ? error.message : "Unknown error";
158
+ console.error("Related files failed:", msg);
159
+ process.exitCode = 1;
160
+ }
161
+ finally {
162
+ if (vectorDb) {
163
+ try {
164
+ yield vectorDb.close();
165
+ }
166
+ catch (_b) { }
167
+ }
168
+ yield (0, exit_1.gracefulExit)();
169
+ }
170
+ }));
package/dist/index.js CHANGED
@@ -45,6 +45,9 @@ const droid_1 = require("./commands/droid");
45
45
  const index_1 = require("./commands/index");
46
46
  const list_1 = require("./commands/list");
47
47
  const mcp_1 = require("./commands/mcp");
48
+ const project_1 = require("./commands/project");
49
+ const recent_1 = require("./commands/recent");
50
+ const related_1 = require("./commands/related");
48
51
  const opencode_1 = require("./commands/opencode");
49
52
  const search_1 = require("./commands/search");
50
53
  const serve_1 = require("./commands/serve");
@@ -77,6 +80,9 @@ commander_1.program.addCommand(list_1.list);
77
80
  commander_1.program.addCommand(skeleton_1.skeleton);
78
81
  commander_1.program.addCommand(symbols_1.symbols);
79
82
  commander_1.program.addCommand(trace_1.trace);
83
+ commander_1.program.addCommand(project_1.project);
84
+ commander_1.program.addCommand(related_1.related);
85
+ commander_1.program.addCommand(recent_1.recent);
80
86
  // Services
81
87
  commander_1.program.addCommand(serve_1.serve);
82
88
  commander_1.program.addCommand(watch_1.watch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",