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 +66 -14
- package/dist/commands/project.js +175 -0
- package/dist/commands/recent.js +131 -0
- package/dist/commands/related.js +170 -0
- package/dist/index.js +6 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
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.
|
|
104
|
-
| `search_all` | Search ALL indexed code
|
|
105
|
-
| `code_skeleton` | Collapsed file structure (~4x fewer tokens
|
|
106
|
-
| `trace_calls` | Call graph
|
|
107
|
-
| `list_symbols` | List indexed
|
|
108
|
-
| `
|
|
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 "
|
|
140
|
-
gmax "
|
|
141
|
-
gmax "
|
|
142
|
-
gmax "
|
|
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
|
|
192
|
-
gmax skeleton
|
|
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