grepmax 0.12.9 → 0.12.12
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 +129 -299
- package/dist/commands/watch.js +0 -11
- package/dist/config.js +1 -0
- package/dist/lib/daemon/daemon.js +43 -29
- package/dist/lib/store/vector-db.js +6 -2
- package/dist/lib/utils/exit.js +9 -0
- package/package.json +3 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -20,59 +20,61 @@
|
|
|
20
20
|
Natural-language search that works like `grep`. Fast, local, and built for coding agents.
|
|
21
21
|
|
|
22
22
|
- **Semantic:** Finds concepts ("where do transactions get created?"), not just strings.
|
|
23
|
-
- **Call Graph Tracing:** Map dependencies with `trace`
|
|
23
|
+
- **Call Graph Tracing:** Map dependencies with `trace`, find tests with `test`, measure blast radius with `impact`.
|
|
24
24
|
- **Role Detection:** Distinguishes `ORCHESTRATION` (high-level logic) from `DEFINITION` (types/classes).
|
|
25
25
|
- **Local & Private:** 100% local embeddings via ONNX (CPU) or MLX (Apple Silicon GPU).
|
|
26
26
|
- **Centralized Index:** One database at `~/.gmax/` — index once, search from anywhere.
|
|
27
|
-
- **
|
|
28
|
-
- **Agent-Ready:** Pointer mode returns metadata (symbol, role, calls, summary) — no code snippets, ~80% fewer tokens.
|
|
27
|
+
- **Agent-Ready:** `--agent` flag returns compact one-line output — ~90% fewer tokens than default.
|
|
29
28
|
|
|
30
29
|
## Quick Start
|
|
31
30
|
|
|
32
31
|
```bash
|
|
33
32
|
npm install -g grepmax # 1. Install
|
|
34
|
-
cd my-repo && gmax
|
|
35
|
-
gmax "where do we handle auth?" # 3. Search
|
|
33
|
+
cd my-repo && gmax add # 2. Add + index
|
|
34
|
+
gmax "where do we handle auth?" --agent # 3. Search
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
No setup required — gmax auto-detects your platform (GPU on Apple Silicon, CPU elsewhere) and downloads models on first use.
|
|
39
38
|
|
|
40
|
-
###
|
|
39
|
+
### Setup & Config
|
|
41
40
|
|
|
42
41
|
```bash
|
|
43
|
-
gmax setup #
|
|
42
|
+
gmax setup # Interactive wizard (models, embedding mode, plugins)
|
|
43
|
+
gmax config # View current settings
|
|
44
|
+
gmax config --embed-mode gpu # Switch to GPU (Apple Silicon)
|
|
45
|
+
gmax doctor # Health check
|
|
46
|
+
gmax doctor --fix # Auto-repair (compact, prune, remove stale locks)
|
|
44
47
|
```
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
- Switch between **CPU** (ONNX, works everywhere) and **GPU** (MLX, Apple Silicon only, ~3x faster)
|
|
48
|
-
- Choose between **small** model (384d, 47M params, fast) and **standard** model (768d, 149M params, better quality)
|
|
49
|
-
|
|
50
|
-
### Quick Config (Non-Interactive)
|
|
49
|
+
### Core Commands
|
|
51
50
|
|
|
52
51
|
```bash
|
|
53
|
-
gmax
|
|
54
|
-
gmax
|
|
55
|
-
gmax
|
|
56
|
-
gmax
|
|
52
|
+
gmax "where do we handle auth?" --agent # Semantic search (compact output)
|
|
53
|
+
gmax extract handleAuth # Full function body with line numbers
|
|
54
|
+
gmax peek handleAuth # Signature + callers + callees
|
|
55
|
+
gmax trace handleAuth -d 2 # Call graph (2-hop)
|
|
56
|
+
gmax skeleton src/lib/search/ # File structure (bodies collapsed)
|
|
57
|
+
gmax symbols auth # List indexed symbols
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
###
|
|
60
|
+
### Analysis Commands
|
|
60
61
|
|
|
61
62
|
```bash
|
|
62
|
-
gmax
|
|
63
|
+
gmax diff main # Changed files vs main
|
|
64
|
+
gmax diff main --query "auth changes" # Semantic search within changes
|
|
65
|
+
gmax test handleAuth # Find tests via reverse call graph
|
|
66
|
+
gmax impact handleAuth # Dependents + affected tests
|
|
67
|
+
gmax similar handleAuth # Find similar code patterns
|
|
68
|
+
gmax context "auth system" --budget 4000 # Token-budgeted topic summary
|
|
63
69
|
```
|
|
64
70
|
|
|
65
|
-
###
|
|
71
|
+
### Project Commands
|
|
66
72
|
|
|
67
73
|
```bash
|
|
68
|
-
gmax
|
|
69
|
-
gmax
|
|
70
|
-
gmax
|
|
71
|
-
gmax
|
|
72
|
-
gmax project # Project overview
|
|
73
|
-
gmax related src/lib/syncer.ts # Dependencies + dependents
|
|
74
|
-
gmax recent # Recently modified files
|
|
75
|
-
gmax symbols auth # List indexed symbols
|
|
74
|
+
gmax project # Languages, structure, key symbols
|
|
75
|
+
gmax related src/lib/auth.ts # Dependencies + dependents
|
|
76
|
+
gmax recent # Recently modified files
|
|
77
|
+
gmax status # All indexed projects + chunk counts
|
|
76
78
|
```
|
|
77
79
|
|
|
78
80
|
In our public benchmarks, `grepmax` can save about 20% of your LLM tokens and deliver a 30% speedup.
|
|
@@ -83,334 +85,162 @@ In our public benchmarks, `grepmax` can save about 20% of your LLM tokens and de
|
|
|
83
85
|
|
|
84
86
|
## Agent Plugins
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
1. Run `gmax install-claude-code`
|
|
89
|
-
2. Open Claude Code — the plugin auto-starts the MLX GPU server and a background file watcher.
|
|
90
|
-
3. Claude uses `gmax` for semantic searches automatically via MCP tools.
|
|
91
|
-
|
|
92
|
-
Plugin files (skill instructions, hooks) auto-update when you run `npm update -g grepmax` — no need to re-run `install-claude-code`.
|
|
93
|
-
|
|
94
|
-
### Opencode
|
|
95
|
-
1. Run `gmax install-opencode`
|
|
96
|
-
2. OC uses `gmax` for semantic searches via MCP.
|
|
97
|
-
|
|
98
|
-
### Codex
|
|
99
|
-
1. Run `gmax install-codex`
|
|
100
|
-
2. Codex uses `gmax` for semantic searches.
|
|
101
|
-
|
|
102
|
-
### Factory Droid
|
|
103
|
-
1. Run `gmax install-droid`
|
|
104
|
-
2. To remove: `gmax uninstall-droid`
|
|
105
|
-
|
|
106
|
-
### MCP Server
|
|
107
|
-
|
|
108
|
-
`gmax mcp` starts a stdio-based MCP server that searches the centralized index directly — no HTTP daemon needed.
|
|
109
|
-
|
|
110
|
-
| Tool | Description |
|
|
111
|
-
| --- | --- |
|
|
112
|
-
| `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. |
|
|
113
|
-
| `search_all` | Search ALL indexed code. Same params + `projects`/`exclude_projects` to scope by project name. |
|
|
114
|
-
| `code_skeleton` | Collapsed file structure (~4x fewer tokens). Accepts files, directories, or comma-separated paths. `format: "json"` for structured output. |
|
|
115
|
-
| `trace_calls` | Call graph with importers, callers (multi-hop via `depth`), and callees with file:line locations. |
|
|
116
|
-
| `list_symbols` | List indexed symbols with role (ORCH/DEF/IMPL) and export status. |
|
|
117
|
-
| `summarize_project` | High-level project overview — languages, directory structure, roles, key symbols, entry points. |
|
|
118
|
-
| `related_files` | Find dependencies and dependents of a file by shared symbol references. |
|
|
119
|
-
| `recent_changes` | Recently modified indexed files with relative timestamps. |
|
|
120
|
-
| `index_status` | Check index health: per-project chunk counts, model info, watcher status. |
|
|
121
|
-
| `summarize_directory` | Generate LLM summaries for indexed chunks. Summaries appear in search results. |
|
|
122
|
-
|
|
123
|
-
## Commands
|
|
124
|
-
|
|
125
|
-
### `gmax search`
|
|
126
|
-
|
|
127
|
-
The default command. Searches indexed code using semantic meaning.
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
gmax "how is the database connection pooled?"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Options:**
|
|
134
|
-
|
|
135
|
-
| Flag | Description | Default |
|
|
136
|
-
| --- | --- | --- |
|
|
137
|
-
| `--agent` | Ultra-compact output for AI agents (one line per result). | `false` |
|
|
138
|
-
| `-m <n>` | Max total results to return. | `5` |
|
|
139
|
-
| `--per-file <n>` | Max matches to show per file. | `3` |
|
|
140
|
-
| `-c`, `--content` | Show full chunk content instead of snippets. | `false` |
|
|
141
|
-
| `-C <n>`, `--context <n>` | Include N lines before/after each result. | `0` |
|
|
142
|
-
| `--scores` | Show relevance scores (0-1) for each result. | `false` |
|
|
143
|
-
| `--min-score <n>` | Filter out results below this score threshold. | `0` |
|
|
144
|
-
| `--root <dir>` | Search a different project directory. | cwd |
|
|
145
|
-
| `--file <name>` | Filter to files matching this name (e.g. `syncer.ts`). | — |
|
|
146
|
-
| `--exclude <prefix>` | Exclude files under this path prefix (e.g. `tests/`). | — |
|
|
147
|
-
| `--lang <ext>` | Filter by file extension (e.g. `ts`, `py`). | — |
|
|
148
|
-
| `--role <role>` | Filter by role: `ORCHESTRATION`, `DEFINITION`, `IMPLEMENTATION`. | — |
|
|
149
|
-
| `--symbol` | Append call graph (importers, callers, callees) after results. | `false` |
|
|
150
|
-
| `--imports` | Prepend file imports to each result. | `false` |
|
|
151
|
-
| `--name <regex>` | Filter results by symbol name regex. | — |
|
|
152
|
-
| `--compact` | Compact hits view (paths + line ranges + role/preview). | `false` |
|
|
153
|
-
| `--skeleton` | Show code skeleton for matching files instead of snippets. | `false` |
|
|
154
|
-
| `--plain` | Disable ANSI colors and use simpler formatting. | `false` |
|
|
155
|
-
| `-s`, `--sync` | Force re-index changed files before searching. | `false` |
|
|
156
|
-
|
|
157
|
-
**Examples:**
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
gmax "API rate limiting logic"
|
|
161
|
-
gmax "auth handler" --role ORCHESTRATION --lang ts --agent
|
|
162
|
-
gmax "database" --file syncer.ts --agent
|
|
163
|
-
gmax "VectorDB" --symbol --agent
|
|
164
|
-
gmax "error handling" -C 5 --imports --plain
|
|
165
|
-
gmax "handler" --name "handle.*" --exclude tests/ --agent
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
> **For AI agents:** Use `--agent` for the most token-efficient output (~90% fewer tokens than default). Output format: `file:line symbol [role] — summary`
|
|
169
|
-
|
|
170
|
-
### `gmax index`
|
|
171
|
-
|
|
172
|
-
Index a directory into the centralized store.
|
|
173
|
-
|
|
174
|
-
- Respects `.gitignore` and `.gmaxignore`.
|
|
175
|
-
- Only embeds code and config files. Skips binaries, lockfiles, and minified assets.
|
|
176
|
-
- Uses TreeSitter for semantic chunking (TypeScript, JavaScript, Python, Go, Rust, C/C++, Java, C#, Ruby, PHP, Swift, Kotlin, JSON).
|
|
177
|
-
- Files already indexed with matching content are skipped automatically.
|
|
88
|
+
gmax integrates with Claude Code, OpenCode, Codex, and Factory Droid. Install all detected clients at once:
|
|
178
89
|
|
|
179
90
|
```bash
|
|
180
|
-
gmax
|
|
181
|
-
gmax
|
|
182
|
-
gmax
|
|
183
|
-
gmax index --verbose # Watch detailed progress
|
|
184
|
-
gmax index --reset # Full re-index from scratch
|
|
91
|
+
gmax plugin add # Install all detected clients
|
|
92
|
+
gmax plugin # Show plugin status
|
|
93
|
+
gmax plugin remove # Remove all plugins
|
|
185
94
|
```
|
|
186
95
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
Background file watcher for live reindexing. A single daemon process watches all registered projects through native OS file system events (`@parcel/watcher` — FSEvents on macOS, inotify on Linux). File changes are detected in sub-second and incrementally reindexed.
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
gmax watch --daemon -b # Start daemon (watches all projects)
|
|
193
|
-
gmax watch -b # Per-project mode (fallback)
|
|
194
|
-
gmax watch status # Show daemon + watcher status
|
|
195
|
-
gmax watch stop # Stop daemon
|
|
196
|
-
gmax watch stop --all # Stop everything
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
The daemon auto-starts when you run `gmax search` or use MCP tools. It shuts down after 30 minutes of inactivity. CLI commands communicate with the daemon over a Unix domain socket at `~/.gmax/daemon.sock`.
|
|
200
|
-
|
|
201
|
-
### `gmax summarize`
|
|
202
|
-
|
|
203
|
-
Generate one-line LLM summaries for indexed chunks. Requires the summarizer server (Qwen3-Coder via MLX on Apple Silicon). Summaries are stored in LanceDB and appear in search results.
|
|
96
|
+
Or manage individually:
|
|
204
97
|
|
|
205
98
|
```bash
|
|
206
|
-
gmax
|
|
207
|
-
gmax
|
|
99
|
+
gmax plugin add claude # Claude Code only
|
|
100
|
+
gmax plugin add opencode # OpenCode only
|
|
101
|
+
gmax plugin add codex # Codex only
|
|
102
|
+
gmax plugin add droid # Factory Droid only
|
|
103
|
+
gmax plugin remove claude # Remove specific plugin
|
|
208
104
|
```
|
|
209
105
|
|
|
210
|
-
|
|
106
|
+
Plugins auto-update when you run `npm install -g grepmax@latest` — no need to re-run `gmax plugin add`.
|
|
211
107
|
|
|
212
|
-
###
|
|
108
|
+
### How it works per client
|
|
213
109
|
|
|
214
|
-
|
|
110
|
+
- **Claude Code:** Plugin with hooks (SessionStart, CwdChanged, SubagentStart, PreToolUse). Model uses CLI via `Bash(gmax ... --agent)`.
|
|
111
|
+
- **OpenCode:** Tool shim with dynamic SKILL + session plugin for daemon startup. Model calls gmax tool directly.
|
|
112
|
+
- **Codex:** MCP server registration + AGENTS.md skill instructions.
|
|
113
|
+
- **Factory Droid:** Skills + SessionStart/SessionEnd hooks for daemon lifecycle.
|
|
215
114
|
|
|
216
|
-
|
|
217
|
-
gmax serve # Foreground, port 4444
|
|
218
|
-
gmax serve --background # Background mode
|
|
219
|
-
gmax serve --cpu # Force CPU-only embeddings
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### `gmax trace`
|
|
223
|
-
|
|
224
|
-
Call graph — who imports a symbol, who calls it, and what it calls.
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
gmax trace handleAuth # 1-hop trace
|
|
228
|
-
gmax trace handleAuth -d 2 # 2-hop: callers-of-callers
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### `gmax skeleton`
|
|
232
|
-
|
|
233
|
-
Compressed view of a file — signatures with bodies collapsed. Supports files, directories, and batch.
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
gmax skeleton src/lib/auth.ts # Single file
|
|
237
|
-
gmax skeleton src/lib/search/ # All files in directory
|
|
238
|
-
gmax skeleton src/a.ts,src/b.ts # Batch
|
|
239
|
-
gmax skeleton src/lib/auth.ts --json # Structured JSON output
|
|
240
|
-
gmax skeleton AuthService # Find symbol, skeletonize its file
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**Supported Languages:** TypeScript, JavaScript, Python, Go, Rust, Java, C#, C++, C, Ruby, PHP, Swift, Kotlin.
|
|
244
|
-
|
|
245
|
-
### `gmax project`
|
|
246
|
-
|
|
247
|
-
High-level project overview — languages, directory structure, role distribution, key symbols, entry points.
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
gmax project # Current project
|
|
251
|
-
gmax project --root ~/workspace # Different project
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### `gmax related`
|
|
115
|
+
### MCP Server
|
|
255
116
|
|
|
256
|
-
|
|
117
|
+
`gmax mcp` starts a stdio-based MCP server for clients that support MCP but can't run shell commands (Cursor, Windsurf, custom agents).
|
|
257
118
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
119
|
+
| Tool | Description |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `semantic_search` | Search by meaning. 16+ params: query, limit, role, language, scope (project/all), etc. |
|
|
122
|
+
| `search_all` | Cross-project search. Same params + project filtering. |
|
|
123
|
+
| `code_skeleton` | File structure with bodies collapsed (~4x fewer tokens). |
|
|
124
|
+
| `trace_calls` | Call graph: importers, callers (multi-hop), callees with file:line. |
|
|
125
|
+
| `extract_symbol` | Complete function/class body by symbol name. |
|
|
126
|
+
| `peek_symbol` | Compact overview: signature + callers + callees. |
|
|
127
|
+
| `list_symbols` | Indexed symbols with role and export status. |
|
|
128
|
+
| `index_status` | Index health: chunks, files, projects, watcher status. |
|
|
129
|
+
| `summarize_project` | Project overview: languages, structure, key symbols, entry points. |
|
|
130
|
+
| `summarize_directory` | Generate LLM summaries for indexed chunks. |
|
|
131
|
+
| `related_files` | Dependencies and dependents by shared symbols. |
|
|
132
|
+
| `recent_changes` | Recently modified indexed files. |
|
|
133
|
+
| `diff_changes` | Search scoped to git changes. |
|
|
134
|
+
| `find_tests` | Find tests via reverse call graph. |
|
|
135
|
+
| `impact_analysis` | Dependents + affected tests for a symbol or file. |
|
|
136
|
+
| `find_similar` | Vector similarity search. |
|
|
137
|
+
| `build_context` | Token-budgeted topic summary. |
|
|
138
|
+
|
|
139
|
+
## Search Options
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
gmax "query" [options]
|
|
261
143
|
```
|
|
262
144
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
145
|
+
| Flag | Description | Default |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
| `--agent` | Compact one-line output for AI agents. | `false` |
|
|
148
|
+
| `-m <n>` | Max results. | `5` |
|
|
149
|
+
| `--per-file <n>` | Max matches per file. | `3` |
|
|
150
|
+
| `--role <role>` | Filter: `ORCHESTRATION`, `DEFINITION`, `IMPLEMENTATION`. | — |
|
|
151
|
+
| `--lang <ext>` | Filter by extension (e.g. `ts`, `py`). | — |
|
|
152
|
+
| `--file <name>` | Filter by filename. | — |
|
|
153
|
+
| `--exclude <prefix>` | Exclude path prefix. | — |
|
|
154
|
+
| `--symbol` | Append call graph after results. | `false` |
|
|
155
|
+
| `--imports` | Prepend file imports per result. | `false` |
|
|
156
|
+
| `--name <regex>` | Filter by symbol name. | — |
|
|
157
|
+
| `--skeleton` | Show file skeletons for top matches. | `false` |
|
|
158
|
+
| `--context-for-llm` | Full function bodies + imports per result. | `false` |
|
|
159
|
+
| `--budget <tokens>` | Cap output tokens (for `--context-for-llm`). | `8000` |
|
|
160
|
+
| `--explain` | Show scoring breakdown per result. | `false` |
|
|
161
|
+
| `-C <n>` | Context lines before/after. | `0` |
|
|
162
|
+
| `--root <dir>` | Search a different project. | cwd |
|
|
163
|
+
| `--min-score <n>` | Minimum relevance score. | `0` |
|
|
272
164
|
|
|
273
|
-
|
|
165
|
+
## Background Daemon
|
|
274
166
|
|
|
275
|
-
|
|
167
|
+
A single daemon watches all registered projects via native OS file events (FSEvents/inotify). Changes are detected in sub-second and incrementally reindexed.
|
|
276
168
|
|
|
277
169
|
```bash
|
|
278
|
-
gmax
|
|
279
|
-
gmax
|
|
280
|
-
gmax
|
|
281
|
-
gmax config --model-tier standard # Switch to standard model (768d)
|
|
170
|
+
gmax watch --daemon -b # Start daemon
|
|
171
|
+
gmax watch stop # Stop daemon
|
|
172
|
+
gmax status # See all projects + watcher status
|
|
282
173
|
```
|
|
283
174
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
Checks installation health, model paths, and database integrity.
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
gmax doctor
|
|
290
|
-
```
|
|
175
|
+
The daemon auto-starts via agent plugins and shuts down after 30 minutes of inactivity.
|
|
291
176
|
|
|
292
177
|
## Architecture
|
|
293
178
|
|
|
294
|
-
### Centralized Index
|
|
295
|
-
|
|
296
179
|
All data lives in `~/.gmax/`:
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
-
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
|
|
307
|
-
All chunks store **absolute file paths**. Search scoping is done via path prefix filtering. There are no per-project index directories.
|
|
308
|
-
|
|
309
|
-
### Performance
|
|
310
|
-
|
|
311
|
-
- **Single Daemon:** One process watches all projects via native OS events — no polling, sub-second file change detection. Shared VectorDB, MetaCache, and worker pool across projects.
|
|
312
|
-
- **Native File Watching:** `@parcel/watcher` uses FSEvents (macOS), inotify (Linux), ReadDirectoryChangesW (Windows) — zero CPU overhead, no file descriptor exhaustion.
|
|
313
|
-
- **Automatic Compaction:** LanceDB table fragments from incremental inserts are compacted every 5 minutes, preventing performance degradation over time.
|
|
314
|
-
- **LMDB Caching:** File metadata reads use LRU/LFU caching for the watcher's hot path.
|
|
315
|
-
- **Bounded Concurrency:** Worker threads scale to 50% of CPU cores (min 4). Override with `GMAX_WORKER_THREADS`.
|
|
316
|
-
- **Smart Chunking:** `tree-sitter` splits code by function/class boundaries for complete logical blocks.
|
|
317
|
-
- **Deduplication:** Identical code blocks are embedded once and cached.
|
|
318
|
-
- **Multi-stage Search:** Vector search + FTS + RRF fusion + ColBERT reranking + structural boosting.
|
|
319
|
-
- **Role Classification:** Detects `ORCHESTRATION` (high complexity, many calls) vs `DEFINITION` (types/classes).
|
|
180
|
+
- `lancedb/` — LanceDB vector store (centralized, all projects)
|
|
181
|
+
- `cache/meta.lmdb` — file metadata cache (hashes, mtimes)
|
|
182
|
+
- `cache/watchers.lmdb` — watcher/daemon registry (LMDB, crash-safe)
|
|
183
|
+
- `daemon.sock` — Unix domain socket for daemon IPC
|
|
184
|
+
- `daemon.pid` — PID file for daemon dedup
|
|
185
|
+
- `logs/` — daemon and server logs (5MB rotation)
|
|
186
|
+
- `config.json` — global config (model tier, embed mode)
|
|
187
|
+
- `models/` — embedding models
|
|
188
|
+
- `grammars/` — Tree-sitter grammars
|
|
189
|
+
- `projects.json` — registry of indexed directories
|
|
320
190
|
|
|
321
|
-
|
|
191
|
+
**Pipeline:** Walk (gitignore-aware) → Chunk (Tree-sitter) → Embed (384-dim Granite via ONNX/MLX) → Store (LanceDB + LMDB) → Search (vector + FTS + RRF fusion + ColBERT rerank)
|
|
322
192
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
To force CPU mode: `GMAX_EMBED_MODE=cpu gmax index`
|
|
326
|
-
|
|
327
|
-
### LLM Summaries
|
|
328
|
-
|
|
329
|
-
gmax can generate one-line natural language descriptions for every code chunk using a local LLM (Qwen3-Coder-30B-A3B via MLX). Summaries are stored in LanceDB — zero latency at search time.
|
|
330
|
-
|
|
331
|
-
Summarization is **on-demand**, not automatic. Run `gmax summarize` or use the `summarize_directory` MCP tool after indexing. The summarizer server runs on port `8101` and must be started separately. If unavailable, `gmax summarize` will report the server is not running.
|
|
332
|
-
|
|
333
|
-
```bash
|
|
334
|
-
gmax summarize # Generate summaries for all unsummarized chunks
|
|
335
|
-
gmax summarize --path src/lib/ # Scope to a directory
|
|
336
|
-
gmax doctor # Check summarizer status + coverage
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
Example search output with summaries:
|
|
340
|
-
```
|
|
341
|
-
handleAuth [exported ORCH C:8] src/auth/handler.ts:45-90
|
|
342
|
-
Validates JWT from Authorization header, checks RBAC permissions, returns 401 on failure
|
|
343
|
-
parent:AuthController calls:validateToken,checkRole,respond
|
|
344
|
-
```
|
|
193
|
+
**Supported Languages:** TypeScript, JavaScript, Python, Go, Rust, Java, C#, C++, C, Ruby, PHP, Swift, Kotlin, JSON, YAML, Markdown, SQL, Shell.
|
|
345
194
|
|
|
346
195
|
## Configuration
|
|
347
196
|
|
|
348
|
-
### Config File
|
|
349
|
-
|
|
350
|
-
Settings are stored in `~/.gmax/config.json`:
|
|
351
|
-
|
|
352
197
|
```json
|
|
198
|
+
// ~/.gmax/config.json
|
|
353
199
|
{
|
|
354
200
|
"modelTier": "small",
|
|
355
201
|
"vectorDim": 384,
|
|
356
|
-
"embedMode": "gpu"
|
|
357
|
-
"mlxModel": "ibm-granite/granite-embedding-small-english-r2"
|
|
202
|
+
"embedMode": "gpu"
|
|
358
203
|
}
|
|
359
204
|
```
|
|
360
205
|
|
|
361
|
-
View and change settings with `gmax config` or run `gmax setup` for interactive configuration.
|
|
362
|
-
|
|
363
206
|
### Ignoring Files
|
|
364
207
|
|
|
365
|
-
gmax respects `.gitignore` and `.gmaxignore
|
|
208
|
+
gmax respects `.gitignore` and `.gmaxignore`:
|
|
366
209
|
|
|
367
210
|
```gitignore
|
|
368
|
-
# .gmaxignore
|
|
211
|
+
# .gmaxignore
|
|
369
212
|
docs/generated/
|
|
370
213
|
*.test.ts
|
|
371
214
|
fixtures/
|
|
372
215
|
```
|
|
373
216
|
|
|
374
|
-
### Index Management
|
|
375
|
-
|
|
376
|
-
- **View indexed directories:** `gmax list --all`
|
|
377
|
-
- **Index location:** `~/.gmax/lancedb/` (centralized)
|
|
378
|
-
- **Clean up:** `gmax index --reset` re-indexes the current directory from scratch
|
|
379
|
-
- **Full reset:** `rm -rf ~/.gmax/lancedb ~/.gmax/cache` to start completely fresh
|
|
380
|
-
|
|
381
217
|
### Environment Variables
|
|
382
218
|
|
|
383
219
|
| Variable | Description | Default |
|
|
384
220
|
| --- | --- | --- |
|
|
385
|
-
| `
|
|
386
|
-
| `
|
|
387
|
-
| `GMAX_DEBUG` |
|
|
388
|
-
| `
|
|
389
|
-
| `GMAX_WORKER_TASK_TIMEOUT_MS` | Worker task timeout in ms | `120000` |
|
|
390
|
-
| `GMAX_MAX_WORKER_MEMORY_MB` | Max worker memory in MB | 50% of system RAM |
|
|
391
|
-
| `GMAX_MAX_PER_FILE` | Default max results per file in search | `3` |
|
|
392
|
-
| `GMAX_WATCH_POLL` | Force polling mode for file watcher (`1` to enable) | Off (FSEvents on macOS) |
|
|
221
|
+
| `GMAX_EMBED_MODE` | Force `cpu` or `gpu` | Auto-detect |
|
|
222
|
+
| `GMAX_WORKER_THREADS` | Worker threads for embedding | 50% of cores |
|
|
223
|
+
| `GMAX_DEBUG` | Debug logging | Off |
|
|
224
|
+
| `GMAX_SUMMARIZER` | Enable summarizer auto-start (`1`) | Off |
|
|
393
225
|
|
|
394
|
-
##
|
|
226
|
+
## Troubleshooting
|
|
395
227
|
|
|
396
|
-
|
|
228
|
+
```bash
|
|
229
|
+
gmax doctor # Check health
|
|
230
|
+
gmax doctor --fix # Auto-repair (compact, prune, fix locks)
|
|
231
|
+
gmax doctor --agent # Machine-readable health output
|
|
232
|
+
gmax index --reset # Full reindex from scratch
|
|
233
|
+
gmax watch stop && gmax watch --daemon -b # Restart daemon
|
|
234
|
+
```
|
|
397
235
|
|
|
398
|
-
##
|
|
236
|
+
## Contributing
|
|
399
237
|
|
|
400
|
-
|
|
401
|
-
- **Weird results?** Run `gmax doctor` to verify models.
|
|
402
|
-
- **Index getting stuck?** Run `gmax index --verbose` to see which file is being processed.
|
|
403
|
-
- **Need a fresh start?** `rm -rf ~/.gmax/lancedb ~/.gmax/cache` then `gmax index`.
|
|
404
|
-
- **Daemon issues?** Check `~/.gmax/logs/daemon.log`. Run `gmax watch stop` then `gmax watch --daemon -b` to restart.
|
|
405
|
-
- **MLX server won't start?** Check `~/.gmax/logs/mlx-embed-server.log` for errors. Use `GMAX_EMBED_MODE=cpu` to fall back to CPU.
|
|
238
|
+
See [CLAUDE.md](CLAUDE.md) for development setup, commands, and architecture details.
|
|
406
239
|
|
|
407
240
|
## Attribution
|
|
408
241
|
|
|
409
|
-
grepmax is built upon the foundation of [mgrep](https://github.com/mixedbread-ai/mgrep) by MixedBread.
|
|
410
|
-
|
|
411
|
-
See the [NOTICE](NOTICE) file for detailed attribution information.
|
|
242
|
+
grepmax is built upon the foundation of [mgrep](https://github.com/mixedbread-ai/mgrep) by MixedBread. See the [NOTICE](NOTICE) file for details.
|
|
412
243
|
|
|
413
244
|
## License
|
|
414
245
|
|
|
415
|
-
Licensed under the Apache License, Version 2.0.
|
|
416
|
-
See [LICENSE](LICENSE) and [Apache-2.0](https://opensource.org/licenses/Apache-2.0) for details.
|
|
246
|
+
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
|
package/dist/commands/watch.js
CHANGED
|
@@ -44,7 +44,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.watch = void 0;
|
|
46
46
|
const node_child_process_1 = require("node:child_process");
|
|
47
|
-
const fs = __importStar(require("node:fs"));
|
|
48
47
|
const path = __importStar(require("node:path"));
|
|
49
48
|
const commander_1 = require("commander");
|
|
50
49
|
const config_1 = require("../config");
|
|
@@ -80,16 +79,6 @@ exports.watch = new commander_1.Command("watch")
|
|
|
80
79
|
if (options.background) {
|
|
81
80
|
// Skip spawn if daemon already running — prevents process accumulation
|
|
82
81
|
// when SessionStart hook fires on every session/clear/resume
|
|
83
|
-
const pidFile = config_1.PATHS.daemonPidFile;
|
|
84
|
-
try {
|
|
85
|
-
const existingPid = Number.parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
|
|
86
|
-
if (existingPid) {
|
|
87
|
-
process.kill(existingPid, 0); // throws if dead
|
|
88
|
-
process.exit(0); // alive — skip
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch (_c) { }
|
|
92
|
-
// Also check socket as fallback
|
|
93
82
|
const { isDaemonRunning } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
94
83
|
if (yield isDaemonRunning()) {
|
|
95
84
|
process.exit(0);
|
package/dist/config.js
CHANGED
|
@@ -96,6 +96,7 @@ exports.PATHS = {
|
|
|
96
96
|
logsDir: path.join(GLOBAL_ROOT, "logs"),
|
|
97
97
|
daemonSocket: path.join(GLOBAL_ROOT, "daemon.sock"),
|
|
98
98
|
daemonPidFile: path.join(GLOBAL_ROOT, "daemon.pid"),
|
|
99
|
+
daemonLockFile: path.join(GLOBAL_ROOT, "daemon.lock"),
|
|
99
100
|
// Centralized index storage — one database for all indexed directories
|
|
100
101
|
lancedbDir: path.join(GLOBAL_ROOT, "lancedb"),
|
|
101
102
|
cacheDir: path.join(GLOBAL_ROOT, "cache"),
|
|
@@ -41,12 +41,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
48
|
exports.Daemon = void 0;
|
|
46
49
|
const fs = __importStar(require("node:fs"));
|
|
47
50
|
const net = __importStar(require("node:net"));
|
|
48
51
|
const path = __importStar(require("node:path"));
|
|
49
52
|
const watcher = __importStar(require("@parcel/watcher"));
|
|
53
|
+
const proper_lockfile_1 = __importDefault(require("proper-lockfile"));
|
|
50
54
|
const config_1 = require("../../config");
|
|
51
55
|
const batch_processor_1 = require("../index/batch-processor");
|
|
52
56
|
const watcher_1 = require("../index/watcher");
|
|
@@ -65,6 +69,7 @@ class Daemon {
|
|
|
65
69
|
this.vectorDb = null;
|
|
66
70
|
this.metaCache = null;
|
|
67
71
|
this.server = null;
|
|
72
|
+
this.releaseLock = null;
|
|
68
73
|
this.lastActivity = Date.now();
|
|
69
74
|
this.startTime = Date.now();
|
|
70
75
|
this.heartbeatInterval = null;
|
|
@@ -75,35 +80,37 @@ class Daemon {
|
|
|
75
80
|
start() {
|
|
76
81
|
return __awaiter(this, void 0, void 0, function* () {
|
|
77
82
|
process.title = "gmax-daemon";
|
|
78
|
-
// 1.
|
|
83
|
+
// 1. Acquire exclusive lock — kernel-enforced, atomic, auto-released on death
|
|
84
|
+
fs.mkdirSync(path.dirname(config_1.PATHS.daemonLockFile), { recursive: true });
|
|
85
|
+
fs.writeFileSync(config_1.PATHS.daemonLockFile, "", { flag: "a" }); // ensure file exists
|
|
86
|
+
try {
|
|
87
|
+
this.releaseLock = yield proper_lockfile_1.default.lock(config_1.PATHS.daemonLockFile, {
|
|
88
|
+
retries: 0,
|
|
89
|
+
stale: 30000,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err.code === "ELOCKED") {
|
|
94
|
+
console.error("[daemon] Another daemon is already running");
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
// 2. Kill existing per-project watchers
|
|
79
100
|
const existing = (0, watcher_store_1.listWatchers)();
|
|
80
101
|
for (const w of existing) {
|
|
81
102
|
console.log(`[daemon] Taking over from per-project watcher (PID: ${w.pid}, ${path.basename(w.projectRoot)})`);
|
|
82
103
|
yield (0, process_1.killProcess)(w.pid);
|
|
83
104
|
(0, watcher_store_1.unregisterWatcher)(w.pid);
|
|
84
105
|
}
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// Check if another daemon is alive
|
|
89
|
-
const existingPid = Number.parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
|
|
90
|
-
if (existingPid && existingPid !== process.pid) {
|
|
91
|
-
try {
|
|
92
|
-
process.kill(existingPid, 0); // throws if dead
|
|
93
|
-
console.error("[daemon] Another daemon is already running (PID:", existingPid + ")");
|
|
94
|
-
process.exit(0);
|
|
95
|
-
}
|
|
96
|
-
catch (_a) { }
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch (_b) { }
|
|
100
|
-
fs.writeFileSync(pidFile, String(process.pid));
|
|
101
|
-
// 3. Stale socket cleanup
|
|
106
|
+
// 3. Write PID file (informational only — lock is the real guard)
|
|
107
|
+
fs.writeFileSync(config_1.PATHS.daemonPidFile, String(process.pid));
|
|
108
|
+
// 4. Stale socket cleanup
|
|
102
109
|
try {
|
|
103
110
|
fs.unlinkSync(config_1.PATHS.daemonSocket);
|
|
104
111
|
}
|
|
105
|
-
catch (
|
|
106
|
-
//
|
|
112
|
+
catch (_a) { }
|
|
113
|
+
// 5. Open shared resources
|
|
107
114
|
try {
|
|
108
115
|
fs.mkdirSync(config_1.PATHS.cacheDir, { recursive: true });
|
|
109
116
|
fs.mkdirSync(config_1.PATHS.lancedbDir, { recursive: true });
|
|
@@ -114,9 +121,9 @@ class Daemon {
|
|
|
114
121
|
console.error("[daemon] Failed to open shared resources:", err);
|
|
115
122
|
throw err;
|
|
116
123
|
}
|
|
117
|
-
//
|
|
124
|
+
// 6. Register daemon (only after resources are open)
|
|
118
125
|
(0, watcher_store_1.registerDaemon)(process.pid);
|
|
119
|
-
//
|
|
126
|
+
// 7. Subscribe to all registered projects (skip missing directories)
|
|
120
127
|
const projects = (0, project_registry_1.listProjects)().filter((p) => p.status === "indexed");
|
|
121
128
|
for (const p of projects) {
|
|
122
129
|
if (!fs.existsSync(p.root)) {
|
|
@@ -130,18 +137,18 @@ class Daemon {
|
|
|
130
137
|
console.error(`[daemon] Failed to watch ${path.basename(p.root)}:`, err);
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
|
-
//
|
|
140
|
+
// 8. Heartbeat
|
|
134
141
|
this.heartbeatInterval = setInterval(() => {
|
|
135
142
|
(0, watcher_store_1.heartbeat)(process.pid);
|
|
136
143
|
}, HEARTBEAT_INTERVAL_MS);
|
|
137
|
-
//
|
|
144
|
+
// 9. Idle timeout
|
|
138
145
|
this.idleInterval = setInterval(() => {
|
|
139
146
|
if (Date.now() - this.lastActivity > IDLE_TIMEOUT_MS) {
|
|
140
147
|
console.log("[daemon] Idle for 30 minutes, shutting down");
|
|
141
148
|
this.shutdown();
|
|
142
149
|
}
|
|
143
150
|
}, HEARTBEAT_INTERVAL_MS);
|
|
144
|
-
//
|
|
151
|
+
// 10. Socket server
|
|
145
152
|
this.server = net.createServer((conn) => {
|
|
146
153
|
let buf = "";
|
|
147
154
|
conn.on("data", (chunk) => {
|
|
@@ -171,7 +178,7 @@ class Daemon {
|
|
|
171
178
|
this.server.on("error", (err) => {
|
|
172
179
|
const code = err.code;
|
|
173
180
|
if (code === "EADDRINUSE") {
|
|
174
|
-
console.error("[daemon]
|
|
181
|
+
console.error("[daemon] Socket already in use");
|
|
175
182
|
reject(err);
|
|
176
183
|
}
|
|
177
184
|
else if (code === "EOPNOTSUPP") {
|
|
@@ -301,7 +308,7 @@ class Daemon {
|
|
|
301
308
|
catch (_d) { }
|
|
302
309
|
}
|
|
303
310
|
this.subscriptions.clear();
|
|
304
|
-
// Close server + socket + PID file
|
|
311
|
+
// Close server + socket + PID file + lock
|
|
305
312
|
(_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
|
|
306
313
|
try {
|
|
307
314
|
fs.unlinkSync(config_1.PATHS.daemonSocket);
|
|
@@ -311,6 +318,13 @@ class Daemon {
|
|
|
311
318
|
fs.unlinkSync(config_1.PATHS.daemonPidFile);
|
|
312
319
|
}
|
|
313
320
|
catch (_f) { }
|
|
321
|
+
if (this.releaseLock) {
|
|
322
|
+
try {
|
|
323
|
+
yield this.releaseLock();
|
|
324
|
+
}
|
|
325
|
+
catch (_g) { }
|
|
326
|
+
this.releaseLock = null;
|
|
327
|
+
}
|
|
314
328
|
// Unregister all
|
|
315
329
|
for (const root of this.processors.keys()) {
|
|
316
330
|
(0, watcher_store_1.unregisterWatcherByRoot)(root);
|
|
@@ -321,11 +335,11 @@ class Daemon {
|
|
|
321
335
|
try {
|
|
322
336
|
yield ((_b = this.metaCache) === null || _b === void 0 ? void 0 : _b.close());
|
|
323
337
|
}
|
|
324
|
-
catch (
|
|
338
|
+
catch (_h) { }
|
|
325
339
|
try {
|
|
326
340
|
yield ((_c = this.vectorDb) === null || _c === void 0 ? void 0 : _c.close());
|
|
327
341
|
}
|
|
328
|
-
catch (
|
|
342
|
+
catch (_j) { }
|
|
329
343
|
console.log("[daemon] Shutdown complete");
|
|
330
344
|
});
|
|
331
345
|
}
|
|
@@ -488,8 +488,12 @@ class VectorDB {
|
|
|
488
488
|
(_a = this.unregisterCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
489
489
|
this.unregisterCleanup = undefined;
|
|
490
490
|
if (this.db) {
|
|
491
|
-
if (this.db.close)
|
|
492
|
-
yield
|
|
491
|
+
if (this.db.close) {
|
|
492
|
+
yield Promise.race([
|
|
493
|
+
this.db.close(),
|
|
494
|
+
new Promise((resolve) => setTimeout(resolve, 5000)),
|
|
495
|
+
]);
|
|
496
|
+
}
|
|
493
497
|
}
|
|
494
498
|
this.db = null;
|
|
495
499
|
});
|
package/dist/lib/utils/exit.js
CHANGED
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.gracefulExit = gracefulExit;
|
|
13
13
|
const pool_1 = require("../workers/pool");
|
|
14
14
|
const cleanup_1 = require("./cleanup");
|
|
15
|
+
const EXIT_TIMEOUT_MS = 8000;
|
|
15
16
|
function gracefulExit(code) {
|
|
16
17
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
18
|
const finalCode = typeof code === "number"
|
|
@@ -19,6 +20,12 @@ function gracefulExit(code) {
|
|
|
19
20
|
: typeof process.exitCode === "number"
|
|
20
21
|
? process.exitCode
|
|
21
22
|
: 0;
|
|
23
|
+
// Safety net: force-exit if cleanup hangs
|
|
24
|
+
const forceTimer = !process.env.VITEST && process.env.NODE_ENV !== "test"
|
|
25
|
+
? setTimeout(() => process.exit(finalCode), EXIT_TIMEOUT_MS)
|
|
26
|
+
: undefined;
|
|
27
|
+
if (forceTimer)
|
|
28
|
+
forceTimer.unref();
|
|
22
29
|
try {
|
|
23
30
|
if ((0, pool_1.isWorkerPoolInitialized)()) {
|
|
24
31
|
yield (0, pool_1.destroyWorkerPool)();
|
|
@@ -28,6 +35,8 @@ function gracefulExit(code) {
|
|
|
28
35
|
console.error("[exit] Failed to destroy worker pool:", err);
|
|
29
36
|
}
|
|
30
37
|
yield (0, cleanup_1.runCleanup)();
|
|
38
|
+
if (forceTimer)
|
|
39
|
+
clearTimeout(forceTimer);
|
|
31
40
|
// Avoid exiting the process during test runs so Vitest can report results.
|
|
32
41
|
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
33
42
|
process.exitCode = finalCode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.12",
|
|
4
4
|
"author": "Robert Owens <robowens@me.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"onnxruntime-node": "1.24.3",
|
|
49
49
|
"ora": "^9.3.0",
|
|
50
50
|
"piscina": "^5.1.4",
|
|
51
|
+
"proper-lockfile": "^4.1.2",
|
|
51
52
|
"simsimd": "^6.5.5",
|
|
52
53
|
"uuid": "^13.0.0",
|
|
53
54
|
"web-tree-sitter": "^0.26.7",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"@anthropic-ai/claude-agent-sdk": "^0.2.87",
|
|
58
59
|
"@biomejs/biome": "2.4.10",
|
|
59
60
|
"@types/node": "^25.5.0",
|
|
61
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
60
62
|
"node-gyp": "^12.1.0",
|
|
61
63
|
"ts-node": "^10.9.2",
|
|
62
64
|
"typescript": "^6.0.2",
|