@openez-graph/cli 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -14
  3. package/dist/cli.cjs +206 -118
  4. package/package.json +33 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Asta Nguyen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # @openez-graph/cli
2
2
 
3
- Local-first code intelligence CLI. Zero-config. SQLite-only.
3
+ > Local-first code intelligence engine — index, query, and graph your codebase with zero config.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@openez-graph/cli.svg)](https://www.npmjs.com/package/@openez-graph/cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ OpenEZ Graph indexes your codebase into a local SQLite database, builds a code graph (symbols, files, chunks, edges), and exposes it through MCP tools for AI coding agents like Claude Code, Codex, and OpenCode.
9
+
10
+ **Zero config. No Docker. No Postgres. No Redis. Just install and go.**
11
+
12
+ ## Features
13
+
14
+ - **Zero-config** — auto-registers workspace, auto-indexes, auto-syncs on file changes
15
+ - **SQLite-first** — all data stored locally in `.openez/` per workspace
16
+ - **MCP-first** — exposes `memory_query`, `code_context`, `graph_neighbors`, `memory_write`, `index_workspace`, `list_workspaces` tools
17
+ - **Multi-workspace** — register and query across multiple codebases
18
+ - **Code graph** — symbols, files, chunks, and edges (calls, imports, contains)
19
+ - **Web dashboard** — built-in graph explorer and workspace management UI
20
+ - **Auto-sync** — file watcher re-indexes on changes (2s debounce)
4
21
 
5
22
  ## Install
6
23
 
@@ -9,24 +26,87 @@ npm install -g @openez-graph/cli
9
26
  openez setup claude # or: codex, opencode
10
27
  ```
11
28
 
29
+ Restart your agent. Done.
30
+
31
+ ## Quick start
32
+
33
+ ```bash
34
+ # 1. Install
35
+ npm install -g @openez-graph/cli
36
+
37
+ # 2. Wire up your agent
38
+ openez setup claude # Claude Code
39
+ openez setup codex # Codex
40
+ openez setup opencode # OpenCode
41
+
42
+ # 3. Restart your agent — it will auto-index and auto-sync
43
+ ```
44
+
12
45
  ## Commands
13
46
 
14
47
  ```bash
15
- openez init [path] # register + index a workspace
16
- openez index [path] # incremental index
17
- openez reindex [path] # full rebuild
18
- openez watch [path] # watch + auto-reindex on changes
19
- openez serve --mcp # start MCP server (auto-index + auto-sync)
20
- openez serve --web # start web dashboard API server (default port 11368)
21
- openez serve --web --port 8080 # start web dashboard API server on custom port
22
- openez status [path] # show workspace status
23
- openez list # list registered workspaces
24
- openez setup claude # wire up Claude Code
25
- openez setup codex # wire up Codex
26
- openez setup opencode # wire up OpenCode
48
+ openez init [path] # register + index a workspace
49
+ openez index [path] # incremental index
50
+ openez reindex [path] # full rebuild
51
+ openez watch [path] # watch + auto-reindex on changes
52
+ openez serve --mcp # start MCP server (auto-index + auto-sync)
53
+ openez serve --web # start web dashboard (default port 17881)
54
+ openez serve --web --port 8080 # start web dashboard on custom port
55
+ openez status [path] # show workspace status
56
+ openez list # list registered workspaces
57
+ openez setup claude # wire up Claude Code
58
+ openez setup codex # wire up Codex
59
+ openez setup opencode # wire up OpenCode
27
60
  ```
28
61
 
62
+ ## MCP Tools
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `list_workspaces` | List all registered workspaces |
67
+ | `memory_query` | Full-text search + graph expansion for retrieval context |
68
+ | `code_context` | Get symbol context with callers, callees, and related files |
69
+ | `graph_neighbors` | Traverse graph edges from a node or label |
70
+ | `memory_write` | Write a memory entry (notes, decisions, patterns) |
71
+ | `index_workspace` | Trigger indexing for a workspace |
72
+
73
+ ## How it works
74
+
75
+ 1. **`openez setup claude`** writes MCP server config to `~/.claude/settings.json`
76
+ 2. When Claude Code starts, it launches the MCP server via `openez serve --mcp`
77
+ 3. The MCP server auto-registers the current project as a workspace
78
+ 4. It auto-indexes if the workspace has no documents yet
79
+ 5. It watches for file changes and re-indexes automatically (2s debounce)
80
+ 6. All data is stored in `<project>/.openez/index.sqlite` — local, portable, gitignored
81
+
82
+ ## Supported languages
83
+
84
+ | Language | Indexing depth |
85
+ |----------|---------------|
86
+ | TypeScript / JavaScript | Richest — `ts-morph` symbol extraction, imports, calls |
87
+ | Python | Basic top-level symbol extraction |
88
+ | Go | Basic top-level symbol extraction |
89
+ | Rust | Basic top-level symbol extraction |
90
+ | YAML / JSON / TOML | Structure-aware chunking |
91
+ | Markdown | Section-oriented chunking |
92
+
93
+ ## Web dashboard
94
+
95
+ ```bash
96
+ openez serve --web
97
+ ```
98
+
99
+ Opens a full web dashboard at `http://localhost:17881` with:
100
+ - Workspace overview (documents, chunks, nodes, edges)
101
+ - Graph explorer with force-directed layout
102
+ - Query interface for memory retrieval
103
+ - Indexing control and run history
104
+
29
105
  ## Requirements
30
106
 
31
107
  - Node.js 20+
32
- - No Docker, Postgres, or Redis needed
108
+ - No external services needed
109
+
110
+ ## License
111
+
112
+ MIT © [Asta Nguyen](https://github.com/asta-nguyen)
package/dist/cli.cjs CHANGED
@@ -2912,7 +2912,7 @@ var require_command = __commonJS({
2912
2912
  var EventEmitter2 = require("events").EventEmitter;
2913
2913
  var childProcess = require("child_process");
2914
2914
  var path19 = require("path");
2915
- var fs16 = require("fs");
2915
+ var fs17 = require("fs");
2916
2916
  var process3 = require("process");
2917
2917
  var { Argument: Argument2, humanReadableArgName } = require_argument();
2918
2918
  var { CommanderError: CommanderError2 } = require_error();
@@ -3906,7 +3906,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3906
3906
  * @param {string} subcommandName
3907
3907
  */
3908
3908
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
3909
- if (fs16.existsSync(executableFile)) return;
3909
+ if (fs17.existsSync(executableFile)) return;
3910
3910
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
3911
3911
  const executableMissing = `'${executableFile}' does not exist
3912
3912
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -3925,10 +3925,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
3925
3925
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
3926
3926
  function findFile(baseDir, baseName) {
3927
3927
  const localBin = path19.resolve(baseDir, baseName);
3928
- if (fs16.existsSync(localBin)) return localBin;
3928
+ if (fs17.existsSync(localBin)) return localBin;
3929
3929
  if (sourceExt.includes(path19.extname(baseName))) return void 0;
3930
3930
  const foundExt = sourceExt.find(
3931
- (ext) => fs16.existsSync(`${localBin}${ext}`)
3931
+ (ext) => fs17.existsSync(`${localBin}${ext}`)
3932
3932
  );
3933
3933
  if (foundExt) return `${localBin}${foundExt}`;
3934
3934
  return void 0;
@@ -3940,7 +3940,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3940
3940
  if (this._scriptPath) {
3941
3941
  let resolvedScriptPath;
3942
3942
  try {
3943
- resolvedScriptPath = fs16.realpathSync(this._scriptPath);
3943
+ resolvedScriptPath = fs17.realpathSync(this._scriptPath);
3944
3944
  } catch {
3945
3945
  resolvedScriptPath = this._scriptPath;
3946
3946
  }
@@ -12514,7 +12514,7 @@ var require_package = __commonJS({
12514
12514
  var require_main = __commonJS({
12515
12515
  "../../node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/lib/main.js"(exports2, module3) {
12516
12516
  "use strict";
12517
- var fs16 = require("fs");
12517
+ var fs17 = require("fs");
12518
12518
  var path19 = require("path");
12519
12519
  var os6 = require("os");
12520
12520
  var crypto4 = require("crypto");
@@ -12623,7 +12623,7 @@ var require_main = __commonJS({
12623
12623
  if (options && options.path && options.path.length > 0) {
12624
12624
  if (Array.isArray(options.path)) {
12625
12625
  for (const filepath of options.path) {
12626
- if (fs16.existsSync(filepath)) {
12626
+ if (fs17.existsSync(filepath)) {
12627
12627
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
12628
12628
  }
12629
12629
  }
@@ -12633,7 +12633,7 @@ var require_main = __commonJS({
12633
12633
  } else {
12634
12634
  possibleVaultPath = path19.resolve(process.cwd(), ".env.vault");
12635
12635
  }
12636
- if (fs16.existsSync(possibleVaultPath)) {
12636
+ if (fs17.existsSync(possibleVaultPath)) {
12637
12637
  return possibleVaultPath;
12638
12638
  }
12639
12639
  return null;
@@ -12682,7 +12682,7 @@ var require_main = __commonJS({
12682
12682
  const parsedAll = {};
12683
12683
  for (const path20 of optionPaths) {
12684
12684
  try {
12685
- const parsed = DotenvModule.parse(fs16.readFileSync(path20, { encoding }));
12685
+ const parsed = DotenvModule.parse(fs17.readFileSync(path20, { encoding }));
12686
12686
  DotenvModule.populate(parsedAll, parsed, options);
12687
12687
  } catch (e) {
12688
12688
  if (debug) {
@@ -29609,11 +29609,11 @@ var require_source_map_support = __commonJS({
29609
29609
  "use strict";
29610
29610
  var SourceMapConsumer = require_source_map().SourceMapConsumer;
29611
29611
  var path19 = require("path");
29612
- var fs16;
29612
+ var fs17;
29613
29613
  try {
29614
- fs16 = require("fs");
29615
- if (!fs16.existsSync || !fs16.readFileSync) {
29616
- fs16 = null;
29614
+ fs17 = require("fs");
29615
+ if (!fs17.existsSync || !fs17.readFileSync) {
29616
+ fs17 = null;
29617
29617
  }
29618
29618
  } catch (err) {
29619
29619
  }
@@ -29684,7 +29684,7 @@ var require_source_map_support = __commonJS({
29684
29684
  }
29685
29685
  var contents = "";
29686
29686
  try {
29687
- if (!fs16) {
29687
+ if (!fs17) {
29688
29688
  var xhr = new XMLHttpRequest();
29689
29689
  xhr.open(
29690
29690
  "GET",
@@ -29696,8 +29696,8 @@ var require_source_map_support = __commonJS({
29696
29696
  if (xhr.readyState === 4 && xhr.status === 200) {
29697
29697
  contents = xhr.responseText;
29698
29698
  }
29699
- } else if (fs16.existsSync(path20)) {
29700
- contents = fs16.readFileSync(path20, "utf8");
29699
+ } else if (fs17.existsSync(path20)) {
29700
+ contents = fs17.readFileSync(path20, "utf8");
29701
29701
  }
29702
29702
  } catch (er) {
29703
29703
  }
@@ -29961,9 +29961,9 @@ var require_source_map_support = __commonJS({
29961
29961
  var line = +match[2];
29962
29962
  var column = +match[3];
29963
29963
  var contents = fileContentsCache[source];
29964
- if (!contents && fs16 && fs16.existsSync(source)) {
29964
+ if (!contents && fs17 && fs17.existsSync(source)) {
29965
29965
  try {
29966
- contents = fs16.readFileSync(source, "utf8");
29966
+ contents = fs17.readFileSync(source, "utf8");
29967
29967
  } catch (er) {
29968
29968
  contents = "";
29969
29969
  }
@@ -33724,10 +33724,10 @@ var require_typescript = __commonJS({
33724
33724
  function and2(f, g2) {
33725
33725
  return (arg) => f(arg) && g2(arg);
33726
33726
  }
33727
- function or2(...fs16) {
33727
+ function or2(...fs17) {
33728
33728
  return (...args) => {
33729
33729
  let lastResult;
33730
- for (const f of fs16) {
33730
+ for (const f of fs17) {
33731
33731
  lastResult = f(...args);
33732
33732
  if (lastResult) {
33733
33733
  return lastResult;
@@ -35302,7 +35302,7 @@ ${lanes.join("\n")}
35302
35302
  var tracing;
35303
35303
  var tracingEnabled;
35304
35304
  ((tracingEnabled2) => {
35305
- let fs16;
35305
+ let fs17;
35306
35306
  let traceCount = 0;
35307
35307
  let traceFd = 0;
35308
35308
  let mode;
@@ -35311,9 +35311,9 @@ ${lanes.join("\n")}
35311
35311
  const legend = [];
35312
35312
  function startTracing2(tracingMode, traceDir, configFilePath) {
35313
35313
  Debug.assert(!tracing, "Tracing already started");
35314
- if (fs16 === void 0) {
35314
+ if (fs17 === void 0) {
35315
35315
  try {
35316
- fs16 = require("fs");
35316
+ fs17 = require("fs");
35317
35317
  } catch (e) {
35318
35318
  throw new Error(`tracing requires having fs
35319
35319
  (original error: ${e.message || e})`);
@@ -35324,8 +35324,8 @@ ${lanes.join("\n")}
35324
35324
  if (legendPath === void 0) {
35325
35325
  legendPath = combinePaths(traceDir, "legend.json");
35326
35326
  }
35327
- if (!fs16.existsSync(traceDir)) {
35328
- fs16.mkdirSync(traceDir, { recursive: true });
35327
+ if (!fs17.existsSync(traceDir)) {
35328
+ fs17.mkdirSync(traceDir, { recursive: true });
35329
35329
  }
35330
35330
  const countPart = mode === "build" ? `.${process.pid}-${++traceCount}` : mode === "server" ? `.${process.pid}` : ``;
35331
35331
  const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
@@ -35335,10 +35335,10 @@ ${lanes.join("\n")}
35335
35335
  tracePath,
35336
35336
  typesPath
35337
35337
  });
35338
- traceFd = fs16.openSync(tracePath, "w");
35338
+ traceFd = fs17.openSync(tracePath, "w");
35339
35339
  tracing = tracingEnabled2;
35340
35340
  const meta = { cat: "__metadata", ph: "M", ts: 1e3 * timestamp(), pid: 1, tid: 1 };
35341
- fs16.writeSync(
35341
+ fs17.writeSync(
35342
35342
  traceFd,
35343
35343
  "[\n" + [{ name: "process_name", args: { name: "tsc" }, ...meta }, { name: "thread_name", args: { name: "Main" }, ...meta }, { name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }].map((v) => JSON.stringify(v)).join(",\n")
35344
35344
  );
@@ -35347,10 +35347,10 @@ ${lanes.join("\n")}
35347
35347
  function stopTracing() {
35348
35348
  Debug.assert(tracing, "Tracing is not in progress");
35349
35349
  Debug.assert(!!typeCatalog.length === (mode !== "server"));
35350
- fs16.writeSync(traceFd, `
35350
+ fs17.writeSync(traceFd, `
35351
35351
  ]
35352
35352
  `);
35353
- fs16.closeSync(traceFd);
35353
+ fs17.closeSync(traceFd);
35354
35354
  tracing = void 0;
35355
35355
  if (typeCatalog.length) {
35356
35356
  dumpTypes(typeCatalog);
@@ -35422,11 +35422,11 @@ ${lanes.join("\n")}
35422
35422
  function writeEvent(eventType, phase, name, args, extras, time3 = 1e3 * timestamp()) {
35423
35423
  if (mode === "server" && phase === "checkTypes") return;
35424
35424
  mark("beginTracing");
35425
- fs16.writeSync(traceFd, `,
35425
+ fs17.writeSync(traceFd, `,
35426
35426
  {"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time3},"name":"${name}"`);
35427
- if (extras) fs16.writeSync(traceFd, `,${extras}`);
35428
- if (args) fs16.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
35429
- fs16.writeSync(traceFd, `}`);
35427
+ if (extras) fs17.writeSync(traceFd, `,${extras}`);
35428
+ if (args) fs17.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
35429
+ fs17.writeSync(traceFd, `}`);
35430
35430
  mark("endTracing");
35431
35431
  measure("Tracing", "beginTracing", "endTracing");
35432
35432
  }
@@ -35448,9 +35448,9 @@ ${lanes.join("\n")}
35448
35448
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
35449
35449
  mark("beginDumpTypes");
35450
35450
  const typesPath = legend[legend.length - 1].typesPath;
35451
- const typesFd = fs16.openSync(typesPath, "w");
35451
+ const typesFd = fs17.openSync(typesPath, "w");
35452
35452
  const recursionIdentityMap = /* @__PURE__ */ new Map();
35453
- fs16.writeSync(typesFd, "[");
35453
+ fs17.writeSync(typesFd, "[");
35454
35454
  const numTypes = types.length;
35455
35455
  for (let i = 0; i < numTypes; i++) {
35456
35456
  const type = types[i];
@@ -35546,13 +35546,13 @@ ${lanes.join("\n")}
35546
35546
  flags: Debug.formatTypeFlags(type.flags).split("|"),
35547
35547
  display
35548
35548
  };
35549
- fs16.writeSync(typesFd, JSON.stringify(descriptor));
35549
+ fs17.writeSync(typesFd, JSON.stringify(descriptor));
35550
35550
  if (i < numTypes - 1) {
35551
- fs16.writeSync(typesFd, ",\n");
35551
+ fs17.writeSync(typesFd, ",\n");
35552
35552
  }
35553
35553
  }
35554
- fs16.writeSync(typesFd, "]\n");
35555
- fs16.closeSync(typesFd);
35554
+ fs17.writeSync(typesFd, "]\n");
35555
+ fs17.closeSync(typesFd);
35556
35556
  mark("endDumpTypes");
35557
35557
  measure("Dump types", "beginDumpTypes", "endDumpTypes");
35558
35558
  }
@@ -35560,7 +35560,7 @@ ${lanes.join("\n")}
35560
35560
  if (!legendPath) {
35561
35561
  return;
35562
35562
  }
35563
- fs16.writeFileSync(legendPath, JSON.stringify(legend));
35563
+ fs17.writeFileSync(legendPath, JSON.stringify(legend));
35564
35564
  }
35565
35565
  tracingEnabled2.dumpLegend = dumpLegend;
35566
35566
  })(tracingEnabled || (tracingEnabled = {}));
@@ -244613,8 +244613,8 @@ var require_utils3 = __commonJS({
244613
244613
  exports2.array = array2;
244614
244614
  var errno = require_errno();
244615
244615
  exports2.errno = errno;
244616
- var fs16 = require_fs();
244617
- exports2.fs = fs16;
244616
+ var fs17 = require_fs();
244617
+ exports2.fs = fs17;
244618
244618
  var path19 = require_path();
244619
244619
  exports2.path = path19;
244620
244620
  var pattern = require_pattern();
@@ -244798,12 +244798,12 @@ var require_fs2 = __commonJS({
244798
244798
  "use strict";
244799
244799
  Object.defineProperty(exports2, "__esModule", { value: true });
244800
244800
  exports2.createFileSystemAdapter = exports2.FILE_SYSTEM_ADAPTER = void 0;
244801
- var fs16 = require("fs");
244801
+ var fs17 = require("fs");
244802
244802
  exports2.FILE_SYSTEM_ADAPTER = {
244803
- lstat: fs16.lstat,
244804
- stat: fs16.stat,
244805
- lstatSync: fs16.lstatSync,
244806
- statSync: fs16.statSync
244803
+ lstat: fs17.lstat,
244804
+ stat: fs17.stat,
244805
+ lstatSync: fs17.lstatSync,
244806
+ statSync: fs17.statSync
244807
244807
  };
244808
244808
  function createFileSystemAdapter(fsMethods) {
244809
244809
  if (fsMethods === void 0) {
@@ -244820,12 +244820,12 @@ var require_settings = __commonJS({
244820
244820
  "../../node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/settings.js"(exports2) {
244821
244821
  "use strict";
244822
244822
  Object.defineProperty(exports2, "__esModule", { value: true });
244823
- var fs16 = require_fs2();
244823
+ var fs17 = require_fs2();
244824
244824
  var Settings = class {
244825
244825
  constructor(_options = {}) {
244826
244826
  this._options = _options;
244827
244827
  this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true);
244828
- this.fs = fs16.createFileSystemAdapter(this._options.fs);
244828
+ this.fs = fs17.createFileSystemAdapter(this._options.fs);
244829
244829
  this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false);
244830
244830
  this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
244831
244831
  }
@@ -244982,8 +244982,8 @@ var require_utils4 = __commonJS({
244982
244982
  "use strict";
244983
244983
  Object.defineProperty(exports2, "__esModule", { value: true });
244984
244984
  exports2.fs = void 0;
244985
- var fs16 = require_fs3();
244986
- exports2.fs = fs16;
244985
+ var fs17 = require_fs3();
244986
+ exports2.fs = fs17;
244987
244987
  }
244988
244988
  });
244989
244989
 
@@ -245178,14 +245178,14 @@ var require_fs4 = __commonJS({
245178
245178
  "use strict";
245179
245179
  Object.defineProperty(exports2, "__esModule", { value: true });
245180
245180
  exports2.createFileSystemAdapter = exports2.FILE_SYSTEM_ADAPTER = void 0;
245181
- var fs16 = require("fs");
245181
+ var fs17 = require("fs");
245182
245182
  exports2.FILE_SYSTEM_ADAPTER = {
245183
- lstat: fs16.lstat,
245184
- stat: fs16.stat,
245185
- lstatSync: fs16.lstatSync,
245186
- statSync: fs16.statSync,
245187
- readdir: fs16.readdir,
245188
- readdirSync: fs16.readdirSync
245183
+ lstat: fs17.lstat,
245184
+ stat: fs17.stat,
245185
+ lstatSync: fs17.lstatSync,
245186
+ statSync: fs17.statSync,
245187
+ readdir: fs17.readdir,
245188
+ readdirSync: fs17.readdirSync
245189
245189
  };
245190
245190
  function createFileSystemAdapter(fsMethods) {
245191
245191
  if (fsMethods === void 0) {
@@ -245204,12 +245204,12 @@ var require_settings2 = __commonJS({
245204
245204
  Object.defineProperty(exports2, "__esModule", { value: true });
245205
245205
  var path19 = require("path");
245206
245206
  var fsStat = require_out();
245207
- var fs16 = require_fs4();
245207
+ var fs17 = require_fs4();
245208
245208
  var Settings = class {
245209
245209
  constructor(_options = {}) {
245210
245210
  this._options = _options;
245211
245211
  this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
245212
- this.fs = fs16.createFileSystemAdapter(this._options.fs);
245212
+ this.fs = fs17.createFileSystemAdapter(this._options.fs);
245213
245213
  this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path19.sep);
245214
245214
  this.stats = this._getValue(this._options.stats, false);
245215
245215
  this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
@@ -246590,16 +246590,16 @@ var require_settings4 = __commonJS({
246590
246590
  "use strict";
246591
246591
  Object.defineProperty(exports2, "__esModule", { value: true });
246592
246592
  exports2.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
246593
- var fs16 = require("fs");
246593
+ var fs17 = require("fs");
246594
246594
  var os6 = require("os");
246595
246595
  var CPU_COUNT = Math.max(os6.cpus().length, 1);
246596
246596
  exports2.DEFAULT_FILE_SYSTEM_ADAPTER = {
246597
- lstat: fs16.lstat,
246598
- lstatSync: fs16.lstatSync,
246599
- stat: fs16.stat,
246600
- statSync: fs16.statSync,
246601
- readdir: fs16.readdir,
246602
- readdirSync: fs16.readdirSync
246597
+ lstat: fs17.lstat,
246598
+ lstatSync: fs17.lstatSync,
246599
+ stat: fs17.stat,
246600
+ statSync: fs17.statSync,
246601
+ readdir: fs17.readdir,
246602
+ readdirSync: fs17.readdirSync
246603
246603
  };
246604
246604
  var Settings = class {
246605
246605
  constructor(_options = {}) {
@@ -279303,30 +279303,30 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
279303
279303
  yield path20;
279304
279304
  }
279305
279305
  }
279306
- var fs16 = runtime.fs;
279306
+ var fs17 = runtime.fs;
279307
279307
  var RealFileSystemHost = class {
279308
279308
  async delete(path20) {
279309
279309
  try {
279310
- await fs16.delete(path20);
279310
+ await fs17.delete(path20);
279311
279311
  } catch (err) {
279312
279312
  throw this.#getFileNotFoundErrorIfNecessary(err, path20);
279313
279313
  }
279314
279314
  }
279315
279315
  deleteSync(path20) {
279316
279316
  try {
279317
- fs16.deleteSync(path20);
279317
+ fs17.deleteSync(path20);
279318
279318
  } catch (err) {
279319
279319
  throw this.#getFileNotFoundErrorIfNecessary(err, path20);
279320
279320
  }
279321
279321
  }
279322
279322
  readDirSync(dirPath) {
279323
279323
  try {
279324
- const entries = fs16.readDirSync(dirPath);
279324
+ const entries = fs17.readDirSync(dirPath);
279325
279325
  for (const entry of entries) {
279326
279326
  entry.name = FileUtils.pathJoin(dirPath, entry.name);
279327
279327
  if (entry.isSymlink) {
279328
279328
  try {
279329
- const info = fs16.statSync(entry.name);
279329
+ const info = fs17.statSync(entry.name);
279330
279330
  if (info != null) {
279331
279331
  entry.isDirectory = info.isDirectory();
279332
279332
  entry.isFile = info.isFile();
@@ -279342,84 +279342,84 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
279342
279342
  }
279343
279343
  async readFile(filePath, encoding = "utf-8") {
279344
279344
  try {
279345
- return await fs16.readFile(filePath, encoding);
279345
+ return await fs17.readFile(filePath, encoding);
279346
279346
  } catch (err) {
279347
279347
  throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
279348
279348
  }
279349
279349
  }
279350
279350
  readFileSync(filePath, encoding = "utf-8") {
279351
279351
  try {
279352
- return fs16.readFileSync(filePath, encoding);
279352
+ return fs17.readFileSync(filePath, encoding);
279353
279353
  } catch (err) {
279354
279354
  throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
279355
279355
  }
279356
279356
  }
279357
279357
  async writeFile(filePath, fileText) {
279358
- return fs16.writeFile(filePath, fileText);
279358
+ return fs17.writeFile(filePath, fileText);
279359
279359
  }
279360
279360
  writeFileSync(filePath, fileText) {
279361
- fs16.writeFileSync(filePath, fileText);
279361
+ fs17.writeFileSync(filePath, fileText);
279362
279362
  }
279363
279363
  mkdir(dirPath) {
279364
- return fs16.mkdir(dirPath);
279364
+ return fs17.mkdir(dirPath);
279365
279365
  }
279366
279366
  mkdirSync(dirPath) {
279367
- fs16.mkdirSync(dirPath);
279367
+ fs17.mkdirSync(dirPath);
279368
279368
  }
279369
279369
  move(srcPath, destPath) {
279370
- return fs16.move(srcPath, destPath);
279370
+ return fs17.move(srcPath, destPath);
279371
279371
  }
279372
279372
  moveSync(srcPath, destPath) {
279373
- fs16.moveSync(srcPath, destPath);
279373
+ fs17.moveSync(srcPath, destPath);
279374
279374
  }
279375
279375
  copy(srcPath, destPath) {
279376
- return fs16.copy(srcPath, destPath);
279376
+ return fs17.copy(srcPath, destPath);
279377
279377
  }
279378
279378
  copySync(srcPath, destPath) {
279379
- fs16.copySync(srcPath, destPath);
279379
+ fs17.copySync(srcPath, destPath);
279380
279380
  }
279381
279381
  async fileExists(filePath) {
279382
279382
  try {
279383
- return (await fs16.stat(filePath))?.isFile() ?? false;
279383
+ return (await fs17.stat(filePath))?.isFile() ?? false;
279384
279384
  } catch {
279385
279385
  return false;
279386
279386
  }
279387
279387
  }
279388
279388
  fileExistsSync(filePath) {
279389
279389
  try {
279390
- return fs16.statSync(filePath)?.isFile() ?? false;
279390
+ return fs17.statSync(filePath)?.isFile() ?? false;
279391
279391
  } catch {
279392
279392
  return false;
279393
279393
  }
279394
279394
  }
279395
279395
  async directoryExists(dirPath) {
279396
279396
  try {
279397
- return (await fs16.stat(dirPath))?.isDirectory() ?? false;
279397
+ return (await fs17.stat(dirPath))?.isDirectory() ?? false;
279398
279398
  } catch {
279399
279399
  return false;
279400
279400
  }
279401
279401
  }
279402
279402
  directoryExistsSync(dirPath) {
279403
279403
  try {
279404
- return fs16.statSync(dirPath)?.isDirectory() ?? false;
279404
+ return fs17.statSync(dirPath)?.isDirectory() ?? false;
279405
279405
  } catch {
279406
279406
  return false;
279407
279407
  }
279408
279408
  }
279409
279409
  realpathSync(path20) {
279410
- return fs16.realpathSync(path20);
279410
+ return fs17.realpathSync(path20);
279411
279411
  }
279412
279412
  getCurrentDirectory() {
279413
- return FileUtils.standardizeSlashes(fs16.getCurrentDirectory());
279413
+ return FileUtils.standardizeSlashes(fs17.getCurrentDirectory());
279414
279414
  }
279415
279415
  glob(patterns) {
279416
- return fs16.glob(backSlashesToForward(patterns));
279416
+ return fs17.glob(backSlashesToForward(patterns));
279417
279417
  }
279418
279418
  globSync(patterns) {
279419
- return fs16.globSync(backSlashesToForward(patterns));
279419
+ return fs17.globSync(backSlashesToForward(patterns));
279420
279420
  }
279421
279421
  isCaseSensitive() {
279422
- return fs16.isCaseSensitive();
279422
+ return fs17.isCaseSensitive();
279423
279423
  }
279424
279424
  #getDirectoryNotFoundErrorIfNecessary(err, path20) {
279425
279425
  return FileUtils.isNotExistsError(err) ? new exports2.errors.DirectoryNotFoundError(FileUtils.getStandardizedAbsolutePath(this, path20)) : err;
@@ -317461,12 +317461,12 @@ var require_dist = __commonJS({
317461
317461
  throw new Error(`Unknown format "${name}"`);
317462
317462
  return f;
317463
317463
  };
317464
- function addFormats(ajv, list, fs16, exportName) {
317464
+ function addFormats(ajv, list, fs17, exportName) {
317465
317465
  var _a3;
317466
317466
  var _b;
317467
317467
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
317468
317468
  for (const f of list)
317469
- ajv.addFormat(f, fs16[f]);
317469
+ ajv.addFormat(f, fs17[f]);
317470
317470
  }
317471
317471
  module3.exports = exports2 = formatsPlugin;
317472
317472
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -318403,6 +318403,27 @@ function jsonResponse(result) {
318403
318403
  ]
318404
318404
  };
318405
318405
  }
318406
+ async function catchUpWorkspaceIndex(workspaceId) {
318407
+ const now = Date.now();
318408
+ const current = catchupState.get(workspaceId);
318409
+ if (current?.inFlight) {
318410
+ await current.inFlight;
318411
+ return;
318412
+ }
318413
+ if (current && now - current.lastRunAt < MCP_CATCHUP_INTERVAL_MS) {
318414
+ return;
318415
+ }
318416
+ const inFlight = indexWorkspace({ workspaceId, mode: "incremental" }).then(() => void 0).catch((error2) => {
318417
+ console.error(`OpenEZ MCP catch-up indexing failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
318418
+ }).finally(() => {
318419
+ catchupState.set(workspaceId, { lastRunAt: Date.now() });
318420
+ });
318421
+ catchupState.set(workspaceId, { lastRunAt: current?.lastRunAt ?? 0, inFlight });
318422
+ await inFlight;
318423
+ }
318424
+ async function catchUpReadWorkspaces(workspaces2) {
318425
+ await Promise.all(workspaces2.map((workspace) => catchUpWorkspaceIndex(workspace.id)));
318426
+ }
318406
318427
  async function createAndStartMcpServer(options) {
318407
318428
  const resolver = createWorkspaceResolver(options);
318408
318429
  const server2 = new Server(
@@ -318514,6 +318535,7 @@ async function createAndStartMcpServer(options) {
318514
318535
  case "memory_query": {
318515
318536
  const input = memoryQuerySchema.parse(request.params.arguments ?? {});
318516
318537
  const workspaces2 = await resolver.resolveReadWorkspaces(input);
318538
+ await catchUpReadWorkspaces(workspaces2);
318517
318539
  const results = await Promise.all(
318518
318540
  workspaces2.map(async (workspace) => ({
318519
318541
  workspace,
@@ -318548,6 +318570,7 @@ ${result.answerContext}`).join("\n\n");
318548
318570
  case "code_context": {
318549
318571
  const input = codeContextSchema.parse(request.params.arguments ?? {});
318550
318572
  const workspaces2 = await resolver.resolveReadWorkspaces(input);
318573
+ await catchUpReadWorkspaces(workspaces2);
318551
318574
  const results = await Promise.all(
318552
318575
  workspaces2.map(async (workspace) => ({
318553
318576
  workspaceId: workspace.id,
@@ -318565,6 +318588,7 @@ ${result.answerContext}`).join("\n\n");
318565
318588
  case "graph_neighbors": {
318566
318589
  const input = graphNeighborsSchema.parse(request.params.arguments ?? {});
318567
318590
  const workspaces2 = await resolver.resolveReadWorkspaces(input);
318591
+ await catchUpReadWorkspaces(workspaces2);
318568
318592
  const results = await Promise.all(
318569
318593
  workspaces2.map(async (workspace) => ({
318570
318594
  workspaceId: workspace.id,
@@ -318615,8 +318639,7 @@ async function autoIndexAndSync(searchRoot) {
318615
318639
  }
318616
318640
  }
318617
318641
  if (!workspace) {
318618
- workspace = await registry2.ensureWorkspace({ rootPath: resolvedRoot });
318619
- await writeLocalWorkspaceConfig(workspace);
318642
+ return;
318620
318643
  }
318621
318644
  if (workspace.indexingStatus === "pending" || workspace.documentCount === 0) {
318622
318645
  try {
@@ -318624,6 +318647,9 @@ async function autoIndexAndSync(searchRoot) {
318624
318647
  } catch {
318625
318648
  }
318626
318649
  }
318650
+ if (!WATCH_ENABLED) {
318651
+ return;
318652
+ }
318627
318653
  let debounceTimer = null;
318628
318654
  const watcher = esm_default.watch(resolvedRoot, {
318629
318655
  ignored: WATCH_IGNORE_PATTERNS,
@@ -318642,8 +318668,12 @@ async function autoIndexAndSync(searchRoot) {
318642
318668
  watcher.on("add", triggerReindex);
318643
318669
  watcher.on("change", triggerReindex);
318644
318670
  watcher.on("unlink", triggerReindex);
318671
+ watcher.on("error", (error2) => {
318672
+ console.error(`OpenEZ MCP auto-sync watcher disabled: ${error2 instanceof Error ? error2.message : String(error2)}`);
318673
+ void watcher.close();
318674
+ });
318645
318675
  }
318646
- var import_node_fs8, import_node_path12, memoryQuerySchema, codeContextSchema, graphNeighborsSchema, memoryWriteSchema, indexWorkspaceSchema, WATCH_DEBOUNCE_MS, WATCH_IGNORE_PATTERNS;
318676
+ var import_node_fs8, import_node_path12, memoryQuerySchema, codeContextSchema, graphNeighborsSchema, memoryWriteSchema, indexWorkspaceSchema, MCP_CATCHUP_INTERVAL_MS, catchupState, WATCH_DEBOUNCE_MS, WATCH_ENABLED, WATCH_IGNORE_PATTERNS;
318647
318677
  var init_mcp_core = __esm({
318648
318678
  "../mcp/src/mcp-core.ts"() {
318649
318679
  "use strict";
@@ -318697,7 +318727,10 @@ var init_mcp_core = __esm({
318697
318727
  path: external_exports.string().optional(),
318698
318728
  mode: external_exports.enum(["incremental", "full"]).optional()
318699
318729
  });
318730
+ MCP_CATCHUP_INTERVAL_MS = Number(process.env.OPENEZ_MCP_CATCHUP_INTERVAL_MS ?? 5e3);
318731
+ catchupState = /* @__PURE__ */ new Map();
318700
318732
  WATCH_DEBOUNCE_MS = 2e3;
318733
+ WATCH_ENABLED = ["1", "true", "yes"].includes((process.env.OPENEZ_MCP_WATCH ?? "").toLowerCase());
318701
318734
  WATCH_IGNORE_PATTERNS = [
318702
318735
  "**/node_modules/**",
318703
318736
  "**/.git/**",
@@ -318914,6 +318947,9 @@ function initializeWorkspaceSchema2(db) {
318914
318947
  }
318915
318948
  function getWorkspaceDb2(rootPath) {
318916
318949
  const normalized = normalizeRootPath2(rootPath);
318950
+ if (normalized === "/" || normalized === "" || !import_node_fs9.default.existsSync(normalized)) {
318951
+ throw new Error(`Workspace root path does not exist: "${rootPath}"`);
318952
+ }
318917
318953
  const cached2 = workspaceDbs.get(normalized);
318918
318954
  if (cached2) {
318919
318955
  return cached2;
@@ -319175,9 +319211,9 @@ function mapWorkspace2(ws) {
319175
319211
  }
319176
319212
  function resolveWebDist() {
319177
319213
  const sourceDist = import_node_path14.default.resolve(__dirname, "..", "dist");
319178
- if ((0, import_node_fs11.existsSync)(import_node_path14.default.join(sourceDist, "index.html"))) return sourceDist;
319214
+ if ((0, import_node_fs10.existsSync)(import_node_path14.default.join(sourceDist, "index.html"))) return sourceDist;
319179
319215
  const cliDist = import_node_path14.default.resolve(__dirname, "web");
319180
- if ((0, import_node_fs11.existsSync)(import_node_path14.default.join(cliDist, "index.html"))) return cliDist;
319216
+ if ((0, import_node_fs10.existsSync)(import_node_path14.default.join(cliDist, "index.html"))) return cliDist;
319181
319217
  return null;
319182
319218
  }
319183
319219
  function createWebServer() {
@@ -319186,22 +319222,21 @@ function createWebServer() {
319186
319222
  app.use("/*", (0, import_serve_static.serveStatic)({ root: webDist, rewriteRequestPath: (p) => p }));
319187
319223
  app.get("*", (c) => {
319188
319224
  const indexPath = import_node_path14.default.join(webDist, "index.html");
319189
- const index2 = (0, import_node_fs11.readFileSync)(indexPath, "utf-8");
319225
+ const index2 = (0, import_node_fs10.readFileSync)(indexPath, "utf-8");
319190
319226
  return c.html(index2);
319191
319227
  });
319192
319228
  }
319193
319229
  return app;
319194
319230
  }
319195
- var import_hono, import_cors, import_serve_static, import_node_crypto3, import_node_fs10, import_node_fs11, import_node_path14, app, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS;
319231
+ var import_serve_static, import_hono, import_cors, import_node_crypto3, import_node_fs10, import_node_path14, app, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS;
319196
319232
  var init_server3 = __esm({
319197
319233
  "../web/src/server/index.ts"() {
319198
319234
  "use strict";
319235
+ import_serve_static = require("@hono/node-server/serve-static");
319199
319236
  import_hono = require("hono");
319200
319237
  import_cors = require("hono/cors");
319201
- import_serve_static = require("@hono/node-server/serve-static");
319202
319238
  import_node_crypto3 = __toESM(require("crypto"), 1);
319203
319239
  import_node_fs10 = require("fs");
319204
- import_node_fs11 = require("fs");
319205
319240
  import_node_path14 = __toESM(require("path"), 1);
319206
319241
  init_sqlite2();
319207
319242
  init_src3();
@@ -319231,7 +319266,13 @@ var init_server3 = __esm({
319231
319266
  app.get("/api/dashboard", (c) => {
319232
319267
  try {
319233
319268
  const all = listRegistryWorkspaces();
319234
- const target = all[0];
319269
+ const target = all.find((ws) => {
319270
+ try {
319271
+ return ws.rootPath && ws.rootPath !== "/" && (0, import_node_fs10.existsSync)(ws.rootPath);
319272
+ } catch {
319273
+ return false;
319274
+ }
319275
+ }) ?? all[0];
319235
319276
  if (!target) {
319236
319277
  return c.json({
319237
319278
  workspace: { id: "", name: "No workspace", root: "" },
@@ -319312,11 +319353,20 @@ var init_server3 = __esm({
319312
319353
  try {
319313
319354
  const dbPath = resolveRegistryDbPath2();
319314
319355
  const all = listRegistryWorkspaces();
319315
- const data = all.map((ws) => ({
319316
- ...mapWorkspace2(ws),
319317
- latestIndexRun: getLatestIndexRun(ws.rootPath),
319318
- latestGraphRun: getLatestGraphRun(ws.rootPath)
319319
- }));
319356
+ const data = all.map((ws) => {
319357
+ let latestIndexRun = null;
319358
+ let latestGraphRun = null;
319359
+ try {
319360
+ latestIndexRun = getLatestIndexRun(ws.rootPath);
319361
+ latestGraphRun = getLatestGraphRun(ws.rootPath);
319362
+ } catch {
319363
+ }
319364
+ return {
319365
+ ...mapWorkspace2(ws),
319366
+ latestIndexRun,
319367
+ latestGraphRun
319368
+ };
319369
+ });
319320
319370
  return c.json({ ok: true, data });
319321
319371
  } catch (err) {
319322
319372
  const message = err instanceof Error ? err.message : String(err);
@@ -319577,6 +319627,14 @@ function resolveCliInvocation() {
319577
319627
  };
319578
319628
  }
319579
319629
  const repoRoot = import_node_path15.default.resolve(thisDir, "../../..");
319630
+ const builtCliPath = import_node_path15.default.resolve(thisDir, "../dist/cli.cjs");
319631
+ if (import_node_fs11.default.existsSync(builtCliPath)) {
319632
+ return {
319633
+ command: process.execPath,
319634
+ args: [builtCliPath, "serve", "--mcp"],
319635
+ repoRoot
319636
+ };
319637
+ }
319580
319638
  const tsxPath = import_node_path15.default.join(repoRoot, "node_modules", ".bin", "tsx");
319581
319639
  const cliPath = import_node_path15.default.resolve(thisDir, "./cli.ts");
319582
319640
  return {
@@ -319585,10 +319643,11 @@ function resolveCliInvocation() {
319585
319643
  repoRoot
319586
319644
  };
319587
319645
  }
319588
- var import_node_path15, import_meta4;
319646
+ var import_node_fs11, import_node_path15, import_meta4;
319589
319647
  var init_resolve_cli = __esm({
319590
319648
  "src/resolve-cli.ts"() {
319591
319649
  "use strict";
319650
+ import_node_fs11 = __toESM(require("fs"), 1);
319592
319651
  import_node_path15 = __toESM(require("path"), 1);
319593
319652
  import_meta4 = {};
319594
319653
  }
@@ -320714,7 +320773,7 @@ var {
320714
320773
  init_src();
320715
320774
  init_src4();
320716
320775
  var program2 = new Command();
320717
- program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version("0.2.0");
320776
+ program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version("0.3.0");
320718
320777
  program2.command("init").description("Initialize a workspace at the given path and run initial index").argument("[path]", "path to the project directory", process.cwd()).option("--no-index", "skip initial indexing").action(async (targetPath, options) => {
320719
320778
  const resolvedPath = import_node_path19.default.resolve(targetPath);
320720
320779
  if (!import_node_fs15.default.existsSync(resolvedPath)) {
@@ -320758,6 +320817,7 @@ program2.command("index").description("Index files in a workspace").argument("[p
320758
320817
  });
320759
320818
  console.log(`Auto-registered workspace '${workspace.name}' (${workspace.id})`);
320760
320819
  }
320820
+ await writeLocalWorkspaceConfig(workspace);
320761
320821
  const summary = await indexWorkspace({ workspaceId: workspace.id });
320762
320822
  console.log(JSON.stringify(summary, null, 2));
320763
320823
  });
@@ -320769,6 +320829,7 @@ program2.command("reindex").description("Full rebuild of a workspace index").arg
320769
320829
  console.error(`Error: no workspace registered at ${resolvedPath}. Run 'openez init' first.`);
320770
320830
  process.exit(1);
320771
320831
  }
320832
+ await writeLocalWorkspaceConfig(workspace);
320772
320833
  const summary = await indexWorkspace({ workspaceId: workspace.id, mode: "full" });
320773
320834
  console.log(JSON.stringify(summary, null, 2));
320774
320835
  });
@@ -320782,8 +320843,10 @@ program2.command("watch").description("Watch files and re-index on changes").arg
320782
320843
  });
320783
320844
  console.log(`Auto-registered workspace '${workspace.name}' (${workspace.id})`);
320784
320845
  }
320846
+ await writeLocalWorkspaceConfig(workspace);
320847
+ const workspaceId = workspace.id;
320785
320848
  console.log(`Running initial index for ${resolvedPath}...`);
320786
- await indexWorkspace({ workspaceId: workspace.id });
320849
+ await indexWorkspace({ workspaceId });
320787
320850
  const watcher = esm_default.watch(resolvedPath, {
320788
320851
  ignored: [
320789
320852
  "**/node_modules/**",
@@ -320798,14 +320861,39 @@ program2.command("watch").description("Watch files and re-index on changes").arg
320798
320861
  ignoreInitial: true,
320799
320862
  persistent: true
320800
320863
  });
320801
- const reindex = async () => {
320802
- console.log("Change detected, re-indexing...");
320803
- const summary = await indexWorkspace({ workspaceId: workspace.id });
320804
- console.log(JSON.stringify(summary, null, 2));
320864
+ let debounceTimer;
320865
+ let isIndexing = false;
320866
+ let hasPendingChange = false;
320867
+ const runReindex = async () => {
320868
+ if (isIndexing) {
320869
+ hasPendingChange = true;
320870
+ return;
320871
+ }
320872
+ isIndexing = true;
320873
+ do {
320874
+ hasPendingChange = false;
320875
+ console.log("Change detected, re-indexing...");
320876
+ try {
320877
+ const summary = await indexWorkspace({ workspaceId });
320878
+ console.log(JSON.stringify(summary, null, 2));
320879
+ } catch (error2) {
320880
+ console.error("Re-index failed:");
320881
+ console.error(error2);
320882
+ }
320883
+ } while (hasPendingChange);
320884
+ isIndexing = false;
320885
+ };
320886
+ const scheduleReindex = () => {
320887
+ if (debounceTimer) {
320888
+ clearTimeout(debounceTimer);
320889
+ }
320890
+ debounceTimer = setTimeout(() => {
320891
+ void runReindex();
320892
+ }, 250);
320805
320893
  };
320806
- watcher.on("add", reindex);
320807
- watcher.on("change", reindex);
320808
- watcher.on("unlink", reindex);
320894
+ watcher.on("add", scheduleReindex);
320895
+ watcher.on("change", scheduleReindex);
320896
+ watcher.on("unlink", scheduleReindex);
320809
320897
  console.log(`Watching ${resolvedPath} for changes...`);
320810
320898
  });
320811
320899
  program2.command("serve").description("Start the web dashboard or MCP server").option("--mcp", "run as MCP server instead of web").option("--web", "start the web dashboard API server").option("-p, --path <path>", "workspace path").option("--port <port>", "API server port (default: 11368)").action(async (options) => {
package/package.json CHANGED
@@ -1,6 +1,36 @@
1
1
  {
2
2
  "name": "@openez-graph/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
+ "description": "Local-first code intelligence engine — index, query, and graph your codebase with zero config. SQLite-only, MCP-first.",
5
+ "license": "MIT",
6
+ "author": "Asta Nguyen",
7
+ "homepage": "https://github.com/asta-nguyen/openez-graph",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/asta-nguyen/openez-graph.git",
11
+ "directory": "apps/cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/asta-nguyen/openez-graph/issues"
15
+ },
16
+ "keywords": [
17
+ "code-intelligence",
18
+ "code-graph",
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "code-indexing",
22
+ "codebase",
23
+ "retrieval",
24
+ "sqlite",
25
+ "local-first",
26
+ "developer-tools",
27
+ "ai-coding",
28
+ "claude-code",
29
+ "codex"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
4
34
  "type": "module",
5
35
  "main": "./src/cli.ts",
6
36
  "types": "./src/cli.ts",
@@ -9,7 +39,8 @@
9
39
  },
10
40
  "files": [
11
41
  "dist",
12
- "README.md"
42
+ "README.md",
43
+ "LICENSE"
13
44
  ],
14
45
  "publishConfig": {
15
46
  "main": "./dist/cli.cjs",