@zvec/zvec-grep 0.1.5 → 0.1.6

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 (317) hide show
  1. package/README.md +88 -249
  2. package/README_CN.md +82 -246
  3. package/dist/authorization/index.d.ts +7 -0
  4. package/dist/authorization/index.js +7 -0
  5. package/dist/authorization/index.js.map +1 -0
  6. package/dist/authorization/manager.d.ts +8 -0
  7. package/dist/authorization/manager.js +19 -0
  8. package/dist/authorization/manager.js.map +1 -0
  9. package/dist/authorization/operation.d.ts +7 -0
  10. package/dist/authorization/operation.js +50 -0
  11. package/dist/authorization/operation.js.map +1 -0
  12. package/dist/authorization/planner.d.ts +21 -0
  13. package/dist/authorization/planner.js +97 -0
  14. package/dist/authorization/planner.js.map +1 -0
  15. package/dist/authorization/prompt.d.ts +11 -0
  16. package/dist/authorization/prompt.js +62 -0
  17. package/dist/authorization/prompt.js.map +1 -0
  18. package/dist/authorization/store.d.ts +20 -0
  19. package/dist/authorization/store.js +251 -0
  20. package/dist/authorization/store.js.map +1 -0
  21. package/dist/authorization/target.d.ts +15 -0
  22. package/dist/authorization/target.js +49 -0
  23. package/dist/authorization/target.js.map +1 -0
  24. package/dist/authorization/types.d.ts +61 -0
  25. package/dist/authorization/types.js +2 -0
  26. package/dist/authorization/types.js.map +1 -0
  27. package/dist/cli/args.d.ts +1 -1
  28. package/dist/cli/args.js +304 -54
  29. package/dist/cli/args.js.map +1 -1
  30. package/dist/cli/auth.d.ts +21 -0
  31. package/dist/cli/auth.js +212 -0
  32. package/dist/cli/auth.js.map +1 -0
  33. package/dist/cli/commands.js +433 -618
  34. package/dist/cli/commands.js.map +1 -1
  35. package/dist/cli/errors.js +2 -2
  36. package/dist/cli/errors.js.map +1 -1
  37. package/dist/cli/format/context.d.ts +1 -0
  38. package/dist/cli/format/context.js +241 -45
  39. package/dist/cli/format/context.js.map +1 -1
  40. package/dist/cli/format/debug.js +2 -2
  41. package/dist/cli/format/debug.js.map +1 -1
  42. package/dist/cli/format/progress.d.ts +4 -0
  43. package/dist/cli/format/progress.js +219 -46
  44. package/dist/cli/format/progress.js.map +1 -1
  45. package/dist/cli/format/status.d.ts +77 -6
  46. package/dist/cli/format/status.js +302 -106
  47. package/dist/cli/format/status.js.map +1 -1
  48. package/dist/cli/help.js +258 -59
  49. package/dist/cli/help.js.map +1 -1
  50. package/dist/cli/index.js +0 -0
  51. package/dist/cli/install.d.ts +4 -0
  52. package/dist/cli/install.js +845 -0
  53. package/dist/cli/install.js.map +1 -0
  54. package/dist/cli/managed-rg.d.ts +10 -0
  55. package/dist/cli/managed-rg.js +329 -0
  56. package/dist/cli/managed-rg.js.map +1 -0
  57. package/dist/cli/types.d.ts +20 -6
  58. package/dist/cli/types.js.map +1 -1
  59. package/dist/client/daemon-client.d.ts +19 -0
  60. package/dist/client/daemon-client.js +180 -0
  61. package/dist/client/daemon-client.js.map +1 -0
  62. package/dist/client/mode-router.d.ts +9 -0
  63. package/dist/client/mode-router.js +28 -0
  64. package/dist/client/mode-router.js.map +1 -0
  65. package/dist/client/search-policy.d.ts +11 -0
  66. package/dist/client/search-policy.js +15 -0
  67. package/dist/client/search-policy.js.map +1 -0
  68. package/dist/daemon/backend.d.ts +76 -0
  69. package/dist/daemon/backend.js +1049 -0
  70. package/dist/daemon/backend.js.map +1 -0
  71. package/dist/daemon/change-set.d.ts +25 -0
  72. package/dist/daemon/change-set.js +101 -0
  73. package/dist/daemon/change-set.js.map +1 -0
  74. package/dist/daemon/config.d.ts +24 -0
  75. package/dist/daemon/config.js +88 -0
  76. package/dist/daemon/config.js.map +1 -0
  77. package/dist/daemon/errors.d.ts +5 -0
  78. package/dist/daemon/errors.js +11 -0
  79. package/dist/daemon/errors.js.map +1 -0
  80. package/dist/daemon/http-server.d.ts +30 -0
  81. package/dist/daemon/http-server.js +287 -0
  82. package/dist/daemon/http-server.js.map +1 -0
  83. package/dist/daemon/index-coordinator.d.ts +23 -0
  84. package/dist/daemon/index-coordinator.js +68 -0
  85. package/dist/daemon/index-coordinator.js.map +1 -0
  86. package/dist/daemon/job-scheduler.d.ts +70 -0
  87. package/dist/daemon/job-scheduler.js +366 -0
  88. package/dist/daemon/job-scheduler.js.map +1 -0
  89. package/dist/daemon/logger.d.ts +9 -0
  90. package/dist/daemon/logger.js +53 -0
  91. package/dist/daemon/logger.js.map +1 -0
  92. package/dist/daemon/model-pool.d.ts +45 -0
  93. package/dist/daemon/model-pool.js +187 -0
  94. package/dist/daemon/model-pool.js.map +1 -0
  95. package/dist/daemon/root-lease.d.ts +14 -0
  96. package/dist/daemon/root-lease.js +188 -0
  97. package/dist/daemon/root-lease.js.map +1 -0
  98. package/dist/daemon/root-runtime.d.ts +78 -0
  99. package/dist/daemon/root-runtime.js +333 -0
  100. package/dist/daemon/root-runtime.js.map +1 -0
  101. package/dist/daemon/runtime-manager.d.ts +45 -0
  102. package/dist/daemon/runtime-manager.js +243 -0
  103. package/dist/daemon/runtime-manager.js.map +1 -0
  104. package/dist/daemon/runtime.d.ts +12 -0
  105. package/dist/daemon/runtime.js +102 -0
  106. package/dist/daemon/runtime.js.map +1 -0
  107. package/dist/daemon/server-controller.d.ts +39 -0
  108. package/dist/daemon/server-controller.js +203 -0
  109. package/dist/daemon/server-controller.js.map +1 -0
  110. package/dist/daemon/watch-manager.d.ts +50 -0
  111. package/dist/daemon/watch-manager.js +293 -0
  112. package/dist/daemon/watch-manager.js.map +1 -0
  113. package/dist/daemon/workspace-read-session-cache.d.ts +31 -0
  114. package/dist/daemon/workspace-read-session-cache.js +115 -0
  115. package/dist/daemon/workspace-read-session-cache.js.map +1 -0
  116. package/dist/engine/config.d.ts +30 -8
  117. package/dist/engine/config.js +171 -45
  118. package/dist/engine/config.js.map +1 -1
  119. package/dist/engine/{errors/index.d.ts → errors.d.ts} +6 -1
  120. package/dist/engine/{errors/details.js → errors.js} +17 -4
  121. package/dist/engine/errors.js.map +1 -0
  122. package/dist/engine/extraction/code/adapter.d.ts +1 -1
  123. package/dist/engine/extraction/code/extractor.d.ts +5 -11
  124. package/dist/engine/extraction/code/extractor.js +105 -44
  125. package/dist/engine/extraction/code/extractor.js.map +1 -1
  126. package/dist/engine/extraction/code/families/c-family.js +1 -1
  127. package/dist/engine/extraction/code/families/c-family.js.map +1 -1
  128. package/dist/engine/extraction/code/families/js-ts.d.ts +1 -1
  129. package/dist/engine/extraction/code/families/metadata.d.ts +1 -1
  130. package/dist/engine/extraction/code/tree-sitter/grammar.js.map +1 -0
  131. package/dist/engine/extraction/code/tree-sitter/nodes.js.map +1 -0
  132. package/dist/engine/extraction/code/tree-sitter/parser.js.map +1 -0
  133. package/dist/engine/extraction/image/extractor.d.ts +5 -0
  134. package/dist/engine/extraction/image/extractor.js +35 -0
  135. package/dist/engine/extraction/image/extractor.js.map +1 -0
  136. package/dist/engine/extraction/index.d.ts +3 -44
  137. package/dist/engine/extraction/index.js +1 -280
  138. package/dist/engine/extraction/index.js.map +1 -1
  139. package/dist/engine/extraction/markdown/extractor.d.ts +5 -11
  140. package/dist/engine/extraction/markdown/extractor.js +35 -16
  141. package/dist/engine/extraction/markdown/extractor.js.map +1 -1
  142. package/dist/engine/extraction/runtime.d.ts +4 -0
  143. package/dist/engine/extraction/runtime.js +26 -0
  144. package/dist/engine/extraction/runtime.js.map +1 -0
  145. package/dist/engine/extraction/source.d.ts +18 -0
  146. package/dist/engine/extraction/source.js +22 -0
  147. package/dist/engine/extraction/source.js.map +1 -0
  148. package/dist/engine/extraction/text/extractor.d.ts +7 -0
  149. package/dist/engine/extraction/text/extractor.js +168 -0
  150. package/dist/engine/extraction/text/extractor.js.map +1 -0
  151. package/dist/engine/extraction/types.d.ts +4 -0
  152. package/dist/engine/{models → extraction}/types.js.map +1 -1
  153. package/dist/engine/{files/file-type.d.ts → file-type.d.ts} +1 -1
  154. package/dist/engine/file-type.js.map +1 -0
  155. package/dist/engine/index-status.d.ts +9 -0
  156. package/dist/engine/index-status.js +38 -0
  157. package/dist/engine/index-status.js.map +1 -0
  158. package/dist/engine/manifest.d.ts +12 -0
  159. package/dist/engine/manifest.js +120 -0
  160. package/dist/engine/manifest.js.map +1 -0
  161. package/dist/engine/models/{providers/llama-cpp/embedding.d.ts → backends/llama-cpp.d.ts} +25 -20
  162. package/dist/engine/models/{providers/llama-cpp/embedding.js → backends/llama-cpp.js} +127 -106
  163. package/dist/engine/models/backends/llama-cpp.js.map +1 -0
  164. package/dist/engine/models/backends/model2vec.d.ts +57 -0
  165. package/dist/engine/models/backends/model2vec.js +332 -0
  166. package/dist/engine/models/backends/model2vec.js.map +1 -0
  167. package/dist/engine/models/backends/qwen.d.ts +44 -0
  168. package/dist/engine/models/{providers/qwen/embedding.js → backends/qwen.js} +155 -94
  169. package/dist/engine/models/backends/qwen.js.map +1 -0
  170. package/dist/engine/models/backends/transformers-js.d.ts +87 -0
  171. package/dist/engine/models/backends/transformers-js.js +316 -0
  172. package/dist/engine/models/backends/transformers-js.js.map +1 -0
  173. package/dist/engine/models/catalog.d.ts +188 -23
  174. package/dist/engine/models/catalog.js +172 -25
  175. package/dist/engine/models/catalog.js.map +1 -1
  176. package/dist/engine/models/download-progress.d.ts +15 -0
  177. package/dist/engine/models/download-progress.js +48 -0
  178. package/dist/engine/models/download-progress.js.map +1 -0
  179. package/dist/engine/models/embeddings.d.ts +58 -18
  180. package/dist/engine/models/embeddings.js +52 -28
  181. package/dist/engine/models/embeddings.js.map +1 -1
  182. package/dist/engine/models/factory.d.ts +2 -5
  183. package/dist/engine/models/factory.js +41 -56
  184. package/dist/engine/models/factory.js.map +1 -1
  185. package/dist/engine/models/index.d.ts +4 -6
  186. package/dist/engine/models/index.js +4 -4
  187. package/dist/engine/models/index.js.map +1 -1
  188. package/dist/engine/models/ranking.d.ts +7 -3
  189. package/dist/engine/models/resolution.d.ts +8 -0
  190. package/dist/engine/models/resolution.js +22 -0
  191. package/dist/engine/models/resolution.js.map +1 -0
  192. package/dist/engine/pipeline/indexing/index.d.ts +9 -7
  193. package/dist/engine/pipeline/indexing/index.js +360 -121
  194. package/dist/engine/pipeline/indexing/index.js.map +1 -1
  195. package/dist/engine/pipeline/indexing/root-paths.js +6 -6
  196. package/dist/engine/pipeline/indexing/root-paths.js.map +1 -1
  197. package/dist/engine/pipeline/indexing/scanner/index.d.ts +6 -1
  198. package/dist/engine/pipeline/indexing/scanner/index.js +233 -29
  199. package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
  200. package/dist/engine/pipeline/search/index.d.ts +6 -6
  201. package/dist/engine/pipeline/search/index.js +8 -10
  202. package/dist/engine/pipeline/search/index.js.map +1 -1
  203. package/dist/engine/service/index.d.ts +3 -2
  204. package/dist/engine/service/index.js +1 -1
  205. package/dist/engine/service/index.js.map +1 -1
  206. package/dist/engine/service/root.d.ts +13 -12
  207. package/dist/engine/service/root.js +35 -24
  208. package/dist/engine/service/root.js.map +1 -1
  209. package/dist/engine/service/structure-enrichment.js +12 -14
  210. package/dist/engine/service/structure-enrichment.js.map +1 -1
  211. package/dist/engine/service/types.d.ts +32 -37
  212. package/dist/engine/service/workspace-index.d.ts +24 -0
  213. package/dist/engine/service/workspace-index.js +151 -0
  214. package/dist/engine/service/workspace-index.js.map +1 -0
  215. package/dist/engine/service/zvec-grep.d.ts +12 -1
  216. package/dist/engine/service/zvec-grep.js +677 -486
  217. package/dist/engine/service/zvec-grep.js.map +1 -1
  218. package/dist/engine/storage/index.d.ts +8 -4
  219. package/dist/engine/storage/index.js +1 -1
  220. package/dist/engine/storage/index.js.map +1 -1
  221. package/dist/engine/storage/zvec.d.ts +10 -33
  222. package/dist/engine/storage/zvec.js +95 -38
  223. package/dist/engine/storage/zvec.js.map +1 -1
  224. package/dist/engine/types.d.ts +15 -23
  225. package/dist/engine/types.js +1 -1
  226. package/dist/engine/types.js.map +1 -1
  227. package/dist/engine/utils/daemon-lease.d.ts +17 -0
  228. package/dist/engine/utils/daemon-lease.js +175 -0
  229. package/dist/engine/utils/daemon-lease.js.map +1 -0
  230. package/dist/engine/utils/file-selection.js +38 -1
  231. package/dist/engine/utils/file-selection.js.map +1 -1
  232. package/dist/engine/utils/lock.js +1 -1
  233. package/dist/engine/utils/lock.js.map +1 -1
  234. package/dist/index-progress.d.ts +10 -0
  235. package/dist/index-progress.js +159 -0
  236. package/dist/index-progress.js.map +1 -0
  237. package/dist/index.d.ts +4 -4
  238. package/dist/index.js +1 -1
  239. package/dist/index.js.map +1 -1
  240. package/dist/mcp/http-transport.d.ts +27 -0
  241. package/dist/mcp/http-transport.js +182 -0
  242. package/dist/mcp/http-transport.js.map +1 -0
  243. package/dist/mcp/input-normalization.d.ts +38 -0
  244. package/dist/mcp/input-normalization.js +95 -0
  245. package/dist/mcp/input-normalization.js.map +1 -0
  246. package/dist/mcp/progress-heartbeat.d.ts +17 -0
  247. package/dist/mcp/progress-heartbeat.js +46 -0
  248. package/dist/mcp/progress-heartbeat.js.map +1 -0
  249. package/dist/mcp/request-metadata.d.ts +2 -0
  250. package/dist/mcp/request-metadata.js +11 -0
  251. package/dist/mcp/request-metadata.js.map +1 -0
  252. package/dist/mcp/request-state.d.ts +40 -0
  253. package/dist/mcp/request-state.js +210 -0
  254. package/dist/mcp/request-state.js.map +1 -0
  255. package/dist/mcp/result-format.d.ts +13 -0
  256. package/dist/mcp/result-format.js +75 -0
  257. package/dist/mcp/result-format.js.map +1 -0
  258. package/dist/mcp/schemas.d.ts +430 -0
  259. package/dist/mcp/schemas.js +447 -0
  260. package/dist/mcp/schemas.js.map +1 -0
  261. package/dist/mcp/tools.d.ts +150 -0
  262. package/dist/mcp/tools.js +448 -0
  263. package/dist/mcp/tools.js.map +1 -0
  264. package/dist/mcp/toolset.d.ts +5 -0
  265. package/dist/mcp/toolset.js +12 -0
  266. package/dist/mcp/toolset.js.map +1 -0
  267. package/dist/observability/trace-context.d.ts +11 -0
  268. package/dist/observability/trace-context.js +138 -0
  269. package/dist/observability/trace-context.js.map +1 -0
  270. package/docs/01-agents.md +111 -0
  271. package/docs/02-cli.md +229 -0
  272. package/docs/03-mcp.md +167 -0
  273. package/docs/04-pipeline.md +218 -0
  274. package/docs/05-architecture.md +86 -0
  275. package/docs/06-server.md +210 -0
  276. package/docs/07-embedding.md +179 -0
  277. package/docs/08-roadmap.md +85 -0
  278. package/docs/README.md +53 -0
  279. package/package.json +17 -8
  280. package/skills/zvec-grep/SKILL.md +43 -133
  281. package/skills/zvec-grep/agents/openai.yaml +2 -2
  282. package/skills/zvec-grep/references/cli-fallback.md +85 -0
  283. package/dist/cli/mcp.d.ts +0 -2
  284. package/dist/cli/mcp.js +0 -354
  285. package/dist/cli/mcp.js.map +0 -1
  286. package/dist/engine/collection/index.d.ts +0 -55
  287. package/dist/engine/collection/index.js +0 -710
  288. package/dist/engine/collection/index.js.map +0 -1
  289. package/dist/engine/errors/details.d.ts +0 -6
  290. package/dist/engine/errors/details.js.map +0 -1
  291. package/dist/engine/errors/index.js +0 -16
  292. package/dist/engine/errors/index.js.map +0 -1
  293. package/dist/engine/extraction/tree-sitter/grammar.js.map +0 -1
  294. package/dist/engine/extraction/tree-sitter/nodes.js.map +0 -1
  295. package/dist/engine/extraction/tree-sitter/parser.js.map +0 -1
  296. package/dist/engine/files/file-type.js.map +0 -1
  297. package/dist/engine/models/providers/index.d.ts +0 -2
  298. package/dist/engine/models/providers/index.js +0 -3
  299. package/dist/engine/models/providers/index.js.map +0 -1
  300. package/dist/engine/models/providers/llama-cpp/embedding.js.map +0 -1
  301. package/dist/engine/models/providers/llama-cpp/index.d.ts +0 -1
  302. package/dist/engine/models/providers/llama-cpp/index.js +0 -2
  303. package/dist/engine/models/providers/llama-cpp/index.js.map +0 -1
  304. package/dist/engine/models/providers/qwen/embedding.d.ts +0 -42
  305. package/dist/engine/models/providers/qwen/embedding.js.map +0 -1
  306. package/dist/engine/models/providers/qwen/index.d.ts +0 -1
  307. package/dist/engine/models/providers/qwen/index.js +0 -2
  308. package/dist/engine/models/providers/qwen/index.js.map +0 -1
  309. package/dist/engine/models/types.d.ts +0 -30
  310. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.d.ts +0 -0
  311. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.js +0 -0
  312. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.d.ts +0 -0
  313. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.js +0 -0
  314. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.d.ts +0 -0
  315. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.js +0 -0
  316. /package/dist/engine/{models → extraction}/types.js +0 -0
  317. /package/dist/engine/{files/file-type.js → file-type.js} +0 -0
@@ -1,29 +1,24 @@
1
- import { randomUUID } from "node:crypto";
2
- import { chmod, lstat, mkdir, open, readFile, readlink, rename, stat, unlink, } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { dirname, resolve } from "node:path";
1
+ import { resolve } from "node:path";
5
2
  import { createInterface } from "node:readline/promises";
6
3
  import { createZvecGrep, } from "../index.js";
7
- import { globalConfigPath, updateGlobalConfigFromExplicitOptions, } from "../engine/config.js";
8
- import { findNearestAnonymousWorkspace } from "../engine/service/root.js";
4
+ import { globalConfigPath, updateGlobalConfig } from "../engine/config.js";
5
+ import { listEmbeddingModels } from "../engine/models/index.js";
6
+ import { DaemonClient } from "../client/daemon-client.js";
7
+ import { resolveDirectSearchPolicy, resolveServerSearchPolicy, } from "../client/search-policy.js";
8
+ import { resolveClientMode, resolveServerUrl, routeByMode, } from "../client/mode-router.js";
9
+ import { serverStatus } from "../daemon/server-controller.js";
10
+ import { findNearestWorkspace } from "../engine/service/root.js";
9
11
  import { contextWarningLines, printAgentContextResult, printHumanContextResult, } from "./format/context.js";
12
+ import { createHighlighter, shouldUseColor } from "./format/highlight.js";
10
13
  import { printDebug } from "./format/debug.js";
11
14
  import { createIndexProgressReporter } from "./format/progress.js";
12
- import { printAnonymousInfo, printCollectionInfo, printCollectionList, printCollectionRemoveResult, printIndexPathFilterTip, printIndexResult, } from "./format/status.js";
13
- const AGENT_INSTALLERS = [
14
- {
15
- id: "codex",
16
- label: "Codex",
17
- description: "configure zvec-grep MCP and Codex guidance",
18
- install: installCodexIntegration,
19
- uninstall: uninstallCodexIntegration,
20
- },
21
- ];
22
- const ZVEC_GREP_CONFIG_START = "# ZVEC_GREP_START";
23
- const ZVEC_GREP_CONFIG_END = "# ZVEC_GREP_END";
24
- const ZVEC_GREP_AGENTS_START = "<!-- ZVEC_GREP_START -->";
25
- const ZVEC_GREP_AGENTS_END = "<!-- ZVEC_GREP_END -->";
26
- const DEFAULT_MCP_TOOL_TIMEOUT_SECONDS = 600;
15
+ import { printWorkspaceInfo, printIndexPathFilterTip, printIndexResult, printServerIndexInfo, } from "./format/status.js";
16
+ import { planRemoteIndexAuthorization, planRemoteSearchAuthorization, } from "../authorization/index.js";
17
+ import { indexCompletionFromStatus, indexStatusNeedsRefresh, } from "../engine/index-status.js";
18
+ import { indexProgressFromMessage } from "../index-progress.js";
19
+ import { normalizeManagedRgInput } from "./managed-rg.js";
20
+ import { runInstall, runUninstall } from "./install.js";
21
+ import { runAuth, authorizationStore, authorizeCliPlan, resolveAuthorizationSchema, embeddingModelInfo, configuredEmbeddingReference, workspaceRuntimeFromInfo, assertEmbeddingModelCompatible, assertRequestedEndpointCompatible, requireEmbeddingModelCatalogEntry, unsupportedRemoteEmbeddingProvider, withRemoteEmbeddingOperationPermit, } from "./auth.js";
27
22
  export async function runParsedCommand(parsed) {
28
23
  switch (parsed.command) {
29
24
  case "query":
@@ -35,174 +30,107 @@ export async function runParsedCommand(parsed) {
35
30
  case "status":
36
31
  await runStatus(parsed);
37
32
  return;
38
- case "collections":
39
- await runCollections(parsed);
40
- return;
41
33
  case "install":
42
34
  await runInstall(parsed);
43
35
  return;
44
36
  case "uninstall":
45
37
  await runUninstall(parsed);
46
38
  return;
47
- case "serve":
48
- await runServe(parsed);
39
+ case "config":
40
+ await runConfig(parsed);
41
+ return;
42
+ case "auth":
43
+ await runAuth(parsed);
44
+ return;
45
+ case "server":
46
+ await runServer(parsed);
49
47
  return;
50
48
  case "help":
51
49
  case "version":
52
50
  throw new Error(`${parsed.command} must be handled before dispatch`);
53
51
  }
54
52
  }
55
- async function runInstall(parsed) {
56
- const installers = await resolveInstallers(parsed, "install");
57
- if (installers.length === 0) {
58
- console.log("No agents selected.");
59
- return;
53
+ async function runConfig(parsed) {
54
+ if (parsed.positionals.length !== 1) {
55
+ throw new Error(`zg config ${parsed.options.configAction === "provider-set" ? "provider" : "model"} set requires exactly one reference`);
60
56
  }
61
- console.log(`Installing zvec-grep for: ${installers.map((installer) => installer.label).join(", ")}`);
62
- for (const installer of installers) {
63
- const result = await installer.install({
64
- force: parsed.options.force === true,
65
- mcpToolTimeoutSeconds: parsed.options.installMcpToolTimeoutSeconds ??
66
- DEFAULT_MCP_TOOL_TIMEOUT_SECONDS,
67
- });
68
- console.log(`Installed ${installer.label}:`);
69
- for (const file of result.files) {
70
- console.log(` ${file}`);
57
+ const reference = parsed.positionals[0];
58
+ if (parsed.options.configAction === "provider-set") {
59
+ if (!/^[a-z][a-z0-9_-]*$/.test(reference)) {
60
+ throw unsupportedRemoteEmbeddingProvider(reference, "Invalid embedding provider");
71
61
  }
72
- }
73
- console.log("Restart the selected agent or start a new session to pick up the integration.");
74
- if (installers.some((installer) => installer.id === "codex")) {
75
- console.log("Codex MCP server: zg serve --mcp");
76
- }
77
- }
78
- async function runUninstall(parsed) {
79
- const installers = await resolveInstallers(parsed, "uninstall");
80
- if (installers.length === 0) {
81
- console.log("No agents selected.");
82
- return;
83
- }
84
- console.log(`Removing zvec-grep from: ${installers.map((installer) => installer.label).join(", ")}`);
85
- for (const installer of installers) {
86
- const result = await installer.uninstall();
87
- console.log(`Removed ${installer.label} integration:`);
88
- for (const file of result.files) {
89
- console.log(` ${file}`);
62
+ if (reference === "local" ||
63
+ !listEmbeddingModels().some((entry) => entry.provider === reference)) {
64
+ throw unsupportedRemoteEmbeddingProvider(reference);
65
+ }
66
+ if (parsed.options.apiKey === undefined) {
67
+ throw new Error("zg config provider set requires --api-key");
90
68
  }
69
+ updateGlobalConfig({
70
+ providers: {
71
+ [reference]: {
72
+ apiKey: parsed.options.apiKey,
73
+ },
74
+ },
75
+ });
76
+ console.log(`Provider config: ${reference}`);
77
+ console.log(`Global config: ${globalConfigPath()}`);
78
+ return;
91
79
  }
92
- console.log("Restart the selected agent or start a new session to apply the change.");
93
- }
94
- async function installCodexIntegration(options) {
95
- const codexHome = resolveCodexHome();
96
- const configPath = resolve(codexHome, "config.toml");
97
- const agentsPath = resolve(codexHome, "AGENTS.md");
98
- await writeMarkedFile({
99
- path: configPath,
100
- startMarker: ZVEC_GREP_CONFIG_START,
101
- endMarker: ZVEC_GREP_CONFIG_END,
102
- block: codexConfigBlock(options.mcpToolTimeoutSeconds),
103
- force: options.force,
104
- hasConflict: hasCodexMcpServerConfig,
105
- conflictMessage: `Existing [mcp_servers.zvec_grep] found in ${configPath}. Re-run with --force after removing or moving that table into the zvec-grep managed block.`,
106
- removeConflict: removeCodexMcpServerConfig,
107
- });
108
- await writeMarkedFile({
109
- path: agentsPath,
110
- startMarker: ZVEC_GREP_AGENTS_START,
111
- endMarker: ZVEC_GREP_AGENTS_END,
112
- block: codexAgentsBlock(),
113
- force: true,
114
- });
115
- return { files: [configPath, agentsPath] };
116
- }
117
- async function uninstallCodexIntegration() {
118
- const codexHome = resolveCodexHome();
119
- const configPath = resolve(codexHome, "config.toml");
120
- const agentsPath = resolve(codexHome, "AGENTS.md");
121
- await removeMarkedFile({
122
- path: configPath,
123
- startMarker: ZVEC_GREP_CONFIG_START,
124
- endMarker: ZVEC_GREP_CONFIG_END,
125
- });
126
- await removeMarkedFile({
127
- path: agentsPath,
128
- startMarker: ZVEC_GREP_AGENTS_START,
129
- endMarker: ZVEC_GREP_AGENTS_END,
80
+ if (parsed.options.configAction !== "model-set") {
81
+ throw new Error("zg config requires provider set or model set");
82
+ }
83
+ const catalogEntry = requireEmbeddingModelCatalogEntry(reference);
84
+ if (parsed.options.endpoint === undefined &&
85
+ parsed.options.device === undefined &&
86
+ !parsed.options.defaultModel) {
87
+ throw new Error("zg config model set requires --endpoint, --device, or --default");
88
+ }
89
+ if (catalogEntry.provider === "local" &&
90
+ parsed.options.endpoint !== undefined) {
91
+ throw new Error("--endpoint is only supported for remote embedding models");
92
+ }
93
+ if (catalogEntry.provider !== "local" &&
94
+ parsed.options.device !== undefined) {
95
+ throw new Error("--device is only supported for local embedding models");
96
+ }
97
+ if (parsed.options.endpoint !== undefined) {
98
+ assertHttpEndpoint(parsed.options.endpoint);
99
+ }
100
+ updateGlobalConfig({
101
+ ...(parsed.options.endpoint !== undefined ||
102
+ parsed.options.device !== undefined
103
+ ? {
104
+ models: {
105
+ [reference]: {
106
+ ...(parsed.options.endpoint !== undefined
107
+ ? { endpoint: parsed.options.endpoint }
108
+ : {}),
109
+ ...(parsed.options.device !== undefined
110
+ ? { device: parsed.options.device }
111
+ : {}),
112
+ },
113
+ },
114
+ }
115
+ : {}),
116
+ ...(parsed.options.defaultModel
117
+ ? { defaults: { embedding: reference } }
118
+ : {}),
130
119
  });
131
- return { files: [configPath, agentsPath] };
132
- }
133
- async function resolveInstallers(parsed, action) {
134
- const targetTokens = [
135
- ...(parsed.options.installTargets ?? []),
136
- ...parsed.positionals,
137
- ];
138
- if (targetTokens.length > 0) {
139
- return installersFromTokens(targetTokens);
140
- }
141
- if (parsed.options.yes === true ||
142
- !process.stdin.isTTY ||
143
- !process.stdout.isTTY) {
144
- return installersFromTokens(["auto"]);
145
- }
146
- return promptInstallers(action);
120
+ console.log(`Model config: ${reference}`);
121
+ console.log(`Global config: ${globalConfigPath()}`);
147
122
  }
148
- async function promptInstallers(action) {
149
- console.log(`Select agents whose zvec-grep integration should be ${action === "install" ? "installed" : "removed"}:`);
150
- AGENT_INSTALLERS.forEach((installer, index) => {
151
- console.log(` ${index + 1}. ${installer.label} - ${installer.description}`);
152
- });
153
- console.log(" all. All supported agents");
154
- console.log(" none. Cancel");
155
- const readline = createInterface({
156
- input: process.stdin,
157
- output: process.stdout,
158
- });
123
+ function assertHttpEndpoint(endpoint) {
124
+ let parsed;
159
125
  try {
160
- const answer = await readline.question("Agents [codex]: ");
161
- const normalized = answer.trim();
162
- return installersFromTokens(normalized.length > 0 ? splitTargetTokens(normalized) : ["codex"]);
126
+ parsed = new URL(endpoint);
163
127
  }
164
- finally {
165
- readline.close();
128
+ catch {
129
+ throw new Error("--endpoint must be a valid HTTP(S) URL");
166
130
  }
167
- }
168
- function installersFromTokens(tokens) {
169
- const normalized = tokens.flatMap(splitTargetTokens);
170
- if (normalized.length === 0) {
171
- return installersFromTokens(["auto"]);
172
- }
173
- const selected = new Map();
174
- for (const token of normalized) {
175
- const lower = token.toLowerCase();
176
- if (lower === "none") {
177
- return [];
178
- }
179
- if (lower === "auto" || lower === "all") {
180
- for (const installer of AGENT_INSTALLERS) {
181
- selected.set(installer.id, installer);
182
- }
183
- continue;
184
- }
185
- const numbered = /^\d+$/.test(lower) ? Number(lower) : Number.NaN;
186
- if (Number.isInteger(numbered) &&
187
- numbered >= 1 &&
188
- numbered <= AGENT_INSTALLERS.length) {
189
- const installer = AGENT_INSTALLERS[numbered - 1];
190
- selected.set(installer.id, installer);
191
- continue;
192
- }
193
- const installer = AGENT_INSTALLERS.find((candidate) => candidate.id === lower || candidate.label.toLowerCase() === lower);
194
- if (!installer) {
195
- throw new Error(`Unknown install target: ${token}`);
196
- }
197
- selected.set(installer.id, installer);
131
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
132
+ throw new Error("--endpoint must be a valid HTTP(S) URL");
198
133
  }
199
- return [...selected.values()];
200
- }
201
- function splitTargetTokens(value) {
202
- return value
203
- .split(/[,\s]+/)
204
- .map((target) => target.trim())
205
- .filter((target) => target.length > 0);
206
134
  }
207
135
  async function runIndex(parsed) {
208
136
  const explicitRoot = parsed.positionals.length > 0;
@@ -215,11 +143,110 @@ async function runIndex(parsed) {
215
143
  await runDropIndex(parsed, rootPath.absolutePath);
216
144
  return;
217
145
  }
218
- const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, rootPath.absolutePath));
219
- const progress = createIndexProgressReporter();
146
+ if (parsed.options.embedding) {
147
+ requireEmbeddingModelCatalogEntry(parsed.options.embedding);
148
+ }
149
+ const mode = resolveClientMode(parsed.options.mode);
150
+ await routeByMode({
151
+ mode,
152
+ serverAvailable: () => daemonIsReady(parsed.options.home),
153
+ server: async () => {
154
+ const progress = createIndexProgressReporter({
155
+ color: parsed.options.color,
156
+ });
157
+ let result;
158
+ try {
159
+ result = await daemonClient(parsed.options).callTool("zvec_grep_index", {
160
+ root: rootPath.absolutePath,
161
+ embedding: parsed.options.embedding,
162
+ apiKey: parsed.options.apiKey,
163
+ endpoint: parsed.options.endpoint,
164
+ device: parsed.options.device,
165
+ rebuild: parsed.options.rebuild,
166
+ resetPaths: parsed.options.resetPaths,
167
+ globs: parsed.options.globs,
168
+ insensitiveGlobs: parsed.options.insensitiveGlobs,
169
+ fileTypes: parsed.options.fileTypes,
170
+ excludedFileTypes: parsed.options.excludedFileTypes,
171
+ hidden: parsed.options.hidden,
172
+ noIgnore: parsed.options.noIgnore,
173
+ ignoreFiles: parsed.options.ignoreFiles,
174
+ maxDepth: parsed.options.maxDepth,
175
+ maxFileSizeBytes: parsed.options.maxFileSizeBytes,
176
+ follow: parsed.options.follow,
177
+ embeddingConcurrency: parsed.options.embeddingConcurrency,
178
+ wait: true,
179
+ }, {
180
+ onProgress: (event) => {
181
+ const update = indexProgressFromMessage(event.message);
182
+ if (update) {
183
+ progress.report(update.progress);
184
+ }
185
+ },
186
+ embeddingEnvironment: process.env.ZVEC_GREP_EMBEDDING?.trim(),
187
+ });
188
+ }
189
+ finally {
190
+ progress.finish();
191
+ }
192
+ console.log(`Workspace index: ${String(result.state ?? "submitted")}`);
193
+ console.log(`Root: ${String(result.root ?? rootPath.absolutePath)}`);
194
+ console.log(`Job: ${String(result.job_id ?? "unknown")}`);
195
+ if (result.state === "failed") {
196
+ throw new Error(serverIndexFailureMessage(result));
197
+ }
198
+ },
199
+ direct: () => runDirectIndex(parsed, rootPath, explicitRoot),
200
+ });
201
+ }
202
+ function serverIndexFailureMessage(result) {
203
+ const error = result.error;
204
+ if (error && typeof error === "object") {
205
+ const code = error.code;
206
+ const message = error.message;
207
+ if (typeof message === "string" && message.length > 0) {
208
+ if (typeof code !== "string" ||
209
+ code.length === 0 ||
210
+ message.includes(`[${code}]`)) {
211
+ return message;
212
+ }
213
+ return `[${code}] ${message}`;
214
+ }
215
+ if (typeof code === "string" && code.length > 0) {
216
+ return `[${code}] Index job failed.`;
217
+ }
218
+ }
219
+ return `Index job ${String(result.job_id ?? "unknown")} failed. Run zg status --mode server for details.`;
220
+ }
221
+ async function runDirectIndex(parsed, rootPath, explicitRoot) {
222
+ const serviceOptions = createServiceOptions(parsed.options, rootPath.absolutePath);
223
+ const zvecGrep = await createZvecGrep(serviceOptions);
224
+ const progress = createIndexProgressReporter({ color: parsed.options.color });
220
225
  try {
226
+ const infoBefore = await zvecGrep.info({
227
+ root: rootPath.absolutePath,
228
+ includeStatus: parsed.options.rebuild !== true,
229
+ });
230
+ const schema = resolveAuthorizationSchema(configuredEmbeddingReference(parsed.options, infoBefore.workspaceIndex?.embedding), infoBefore.workspaceIndex?.embedding);
231
+ assertEmbeddingModelCompatible(infoBefore.workspaceIndex?.embedding, schema, parsed.options.rebuild === true);
232
+ const workspaceRuntime = workspaceRuntimeFromInfo(infoBefore);
233
+ assertRequestedEndpointCompatible(infoBefore, workspaceRuntime, parsed.options.endpoint, parsed.options.rebuild === true);
234
+ const modelInfo = schema?.provider === "qwen"
235
+ ? await embeddingModelInfo(schema, serviceOptions, workspaceRuntime)
236
+ : undefined;
237
+ const plan = modelInfo
238
+ ? await planRemoteIndexAuthorization({
239
+ info: infoBefore,
240
+ model: modelInfo,
241
+ rebuild: parsed.options.rebuild,
242
+ store: authorizationStore(parsed.options),
243
+ })
244
+ : undefined;
245
+ const authorizationResolution = plan
246
+ ? await authorizeCliPlan(plan, parsed.options)
247
+ : {};
221
248
  printIndexPathFilterTip(parsed.options);
222
- const result = await zvecGrep.index({
249
+ const result = await withRemoteEmbeddingOperationPermit(authorizationResolution.authorization, () => zvecGrep.index({
223
250
  root: rootPath.absolutePath,
224
251
  rootPaths: explicitRoot ? [rootPath] : undefined,
225
252
  rebuild: parsed.options.rebuild,
@@ -236,11 +263,10 @@ async function runIndex(parsed) {
236
263
  follow: parsed.options.follow,
237
264
  embeddingConcurrency: parsed.options.embeddingConcurrency,
238
265
  onProgress: progress.report,
239
- });
266
+ }));
240
267
  progress.finish();
241
268
  const info = await zvecGrep.info({ root: rootPath.absolutePath });
242
- printIndexResult("Indexed anonymous workspace", result, parsed.options, info.collection?.rootPaths);
243
- persistExplicitGlobalConfig(parsed.options, info.collection?.embedding?.provider);
269
+ printIndexResult("Workspace index", result, parsed.options, info.workspaceIndex?.rootPaths);
244
270
  }
245
271
  catch (error) {
246
272
  progress.finish();
@@ -255,14 +281,26 @@ async function runDropIndex(parsed, root) {
255
281
  console.log("Index drop cancelled.");
256
282
  return;
257
283
  }
258
- const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, root));
259
- try {
260
- const removed = await zvecGrep.dropIndex({ root });
284
+ const printResult = (removed) => {
261
285
  console.log(removed ? `Dropped index for ${root}` : `No index found for ${root}`);
262
- }
263
- finally {
264
- await zvecGrep.close();
265
- }
286
+ };
287
+ await routeByMode({
288
+ mode: resolveClientMode(parsed.options.mode),
289
+ serverAvailable: () => daemonIsReady(parsed.options.home),
290
+ server: async () => {
291
+ const result = await daemonClient(parsed.options).callTool("zvec_grep_index_drop", { root });
292
+ printResult(result.removed === true);
293
+ },
294
+ direct: async () => {
295
+ const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, root));
296
+ try {
297
+ printResult(await zvecGrep.dropIndex({ root }));
298
+ }
299
+ finally {
300
+ await zvecGrep.close();
301
+ }
302
+ },
303
+ });
266
304
  }
267
305
  async function confirmIndexDrop(root, accepted) {
268
306
  if (accepted) {
@@ -283,163 +321,77 @@ async function confirmIndexDrop(root, accepted) {
283
321
  readline.close();
284
322
  }
285
323
  }
286
- async function runServe(parsed) {
324
+ async function runServer(parsed) {
287
325
  if (parsed.positionals.length > 0) {
288
- throw new Error("zg serve --mcp does not accept positional arguments");
326
+ throw new Error(`zg server ${parsed.options.serverAction} does not accept positional arguments`);
327
+ }
328
+ const { readPackageVersion } = await import("./version.js");
329
+ if (parsed.options.serverAction === "on") {
330
+ const { startServer } = await import("../daemon/server-controller.js");
331
+ const status = await startServer({
332
+ cliPath: process.argv[1],
333
+ listen: parsed.options.listen,
334
+ tokenFile: parsed.options.serverTokenFile,
335
+ home: parsed.options.home,
336
+ mcpToolset: parsed.options.mcpToolset,
337
+ });
338
+ printServerControlStatus(status);
339
+ return;
340
+ }
341
+ if (parsed.options.serverAction === "off") {
342
+ const { stopServer } = await import("../daemon/server-controller.js");
343
+ printServerControlStatus(await stopServer(parsed.options.home, 30_000, parsed.options.serverTokenFile));
344
+ return;
289
345
  }
290
- const { runMcpServer } = await import("./mcp.js");
291
- await runMcpServer(createServiceOptions(parsed.options, process.cwd()));
346
+ if (parsed.options.serverAction === "status") {
347
+ const status = await serverStatus(parsed.options.home);
348
+ printServerControlStatus(status);
349
+ if (parsed.options.checkReady && !status.ready) {
350
+ throw new Error("zvec-grep server is not ready");
351
+ }
352
+ return;
353
+ }
354
+ const { runDaemonForeground } = await import("../daemon/runtime.js");
355
+ await runDaemonForeground({
356
+ version: readPackageVersion(),
357
+ listen: parsed.options.listen,
358
+ tokenFile: parsed.options.serverTokenFile,
359
+ home: parsed.options.home,
360
+ mcpToolset: parsed.options.mcpToolset,
361
+ serviceOptions: createServiceOptions(parsed.options, process.cwd()),
362
+ });
292
363
  }
293
364
  async function runStatus(parsed) {
294
365
  const root = parsed.positionals[0] ?? process.cwd();
295
366
  if (parsed.positionals.length > 1) {
296
367
  throw new Error("zg status accepts at most one root path");
297
368
  }
298
- const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, root));
299
- try {
300
- const info = await zvecGrep.info({ root });
301
- printAnonymousInfo(info, parsed.options);
302
- }
303
- finally {
304
- await zvecGrep.close();
305
- }
306
- }
307
- async function runCollections(parsed) {
308
- const [action = "list", name, root] = parsed.positionals;
309
- validateCollectionArguments(action, parsed.positionals);
310
- const indexOption = collectionIndexOption(parsed.options);
311
- if (action !== "index" && indexOption) {
312
- throw new Error(`${indexOption} can only be used with zg collections index`);
313
- }
314
- const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, undefined));
315
- try {
316
- if (action === "list") {
317
- if (parsed.options.resetPaths) {
318
- throw new Error("--reset-paths can only be used with zg collections index");
319
- }
320
- printCollectionList(await zvecGrep.collections.list(), parsed.options);
321
- return;
322
- }
323
- if (action === "info") {
324
- if (parsed.options.resetPaths) {
325
- throw new Error("--reset-paths can only be used with zg collections index");
326
- }
327
- if (!name) {
328
- throw new Error("zg collections info requires <name>");
329
- }
330
- const [info, status] = await Promise.all([
331
- zvecGrep.collections.info(name),
332
- zvecGrep.collections.status(name),
333
- ]);
334
- if (!info) {
335
- throw new Error(`Collection not found: ${name}`);
336
- }
337
- printCollectionInfo(info, status, parsed.options);
338
- return;
339
- }
340
- if (action === "index") {
341
- if (!name) {
342
- throw new Error("zg collections index requires <name>");
343
- }
344
- const explicitRoot = root !== undefined;
345
- const rootPath = indexRootPath(root ?? process.cwd(), parsed.options);
346
- const rootPaths = explicitRoot ? rootPath : undefined;
347
- const progress = createIndexProgressReporter();
369
+ const mode = resolveClientMode(parsed.options.mode);
370
+ const state = await routeByMode({
371
+ mode,
372
+ serverAvailable: () => daemonIsReady(parsed.options.home),
373
+ server: async () => {
374
+ const result = await daemonClient(parsed.options).callTool("zvec_grep_index_status", { root: resolve(root) });
375
+ return printServerIndexInfo(result, parsed.options);
376
+ },
377
+ direct: async () => {
378
+ const zvecGrep = await createZvecGrep(createServiceOptions(parsed.options, root));
348
379
  try {
349
- const result = await zvecGrep.collections.index(name, rootPaths, {
350
- rebuild: parsed.options.rebuild,
351
- resetPaths: parsed.options.resetPaths,
352
- globs: parsed.options.globs,
353
- insensitiveGlobs: parsed.options.insensitiveGlobs,
354
- fileTypes: parsed.options.fileTypes,
355
- excludedFileTypes: parsed.options.excludedFileTypes,
356
- hidden: parsed.options.hidden,
357
- noIgnore: parsed.options.noIgnore,
358
- ignoreFiles: parsed.options.ignoreFiles,
359
- maxDepth: parsed.options.maxDepth,
360
- maxFileSizeBytes: parsed.options.maxFileSizeBytes,
361
- follow: parsed.options.follow,
362
- embeddingConcurrency: parsed.options.embeddingConcurrency,
363
- onProgress: progress.report,
364
- });
365
- progress.finish();
366
- const info = await zvecGrep.collections.info(name);
367
- printIndexResult(`Indexed collection ${name}`, result, parsed.options, info?.rootPaths);
368
- persistExplicitGlobalConfig(parsed.options, info?.embedding?.provider);
369
- }
370
- catch (error) {
371
- progress.finish();
372
- throw error;
373
- }
374
- return;
375
- }
376
- if (action === "remove") {
377
- if (parsed.options.resetPaths) {
378
- throw new Error("--reset-paths can only be used with zg collections index");
380
+ return printWorkspaceInfo(await zvecGrep.info({ root }), parsed.options);
379
381
  }
380
- if (!name) {
381
- throw new Error("zg collections remove requires <name>");
382
+ finally {
383
+ await zvecGrep.close();
382
384
  }
383
- const removed = await zvecGrep.collections.remove(name);
384
- printCollectionRemoveResult(name, removed, parsed.options);
385
- return;
386
- }
387
- if (parsed.options.resetPaths) {
388
- throw new Error("--reset-paths can only be used with zg collections index");
389
- }
390
- throw new Error(`Unknown collections action: ${action}`);
391
- }
392
- finally {
393
- await zvecGrep.close();
394
- }
395
- }
396
- function validateCollectionArguments(action, positionals) {
397
- if (action === "list") {
398
- if (positionals.length > 1) {
399
- throw new Error("zg collections list does not accept arguments");
400
- }
401
- return;
402
- }
403
- if (action === "info" || action === "remove") {
404
- if (positionals.length > 2) {
405
- throw new Error(`zg collections ${action} accepts only <name>`);
406
- }
407
- return;
408
- }
409
- if (action === "index") {
410
- if (positionals.length > 3) {
411
- throw new Error("zg collections index accepts only <name> and [root]");
412
- }
413
- return;
385
+ },
386
+ });
387
+ if (parsed.options.checkReady && state !== "ready") {
388
+ throw new Error(`Workspace index is not ready (state: ${state})`);
414
389
  }
415
- throw new Error(`Unknown collections action: ${action}`);
416
- }
417
- function collectionIndexOption(options) {
418
- const candidates = [
419
- [options.rebuild, "--rebuild"],
420
- [options.resetPaths, "--reset-paths"],
421
- [options.embedding, "--embedding"],
422
- [options.modelCacheDir, "--model-cache"],
423
- [options.llamaGpu, "--llama-gpu"],
424
- [options.embeddingParallelism, "--embedding-parallelism"],
425
- [options.apiKey, "--api-key"],
426
- [options.endpoint, "--endpoint"],
427
- [options.embeddingConcurrency, "--embedding-concurrency"],
428
- [options.globs?.length, "--glob"],
429
- [options.insensitiveGlobs?.length, "--iglob"],
430
- [options.fileTypes?.length, "--type"],
431
- [options.excludedFileTypes?.length, "--type-not"],
432
- [options.hidden, "--hidden"],
433
- [options.noIgnore, "--no-ignore"],
434
- [options.ignoreFiles?.length, "--ignore-file"],
435
- [options.maxDepth, "--max-depth"],
436
- [options.maxFileSizeBytes, "--max-filesize"],
437
- [options.follow, "--follow"],
438
- ];
439
- return candidates.find(([value]) => Array.isArray(value) ? value.length > 0 : value !== undefined)?.[1];
440
390
  }
441
391
  async function runQuery(parsed) {
442
- const rgInput = parsed.options.rg ? normalizeRgInput(parsed) : undefined;
392
+ const rgInput = parsed.options.rg
393
+ ? normalizeManagedRgInput(parsed)
394
+ : undefined;
443
395
  const commandOptions = rgInput?.options ?? parsed.options;
444
396
  const queries = (rgInput?.queries ?? [
445
397
  ...parsed.positionals,
@@ -455,14 +407,55 @@ async function runQuery(parsed) {
455
407
  ? "zg query --rg requires a pattern. Use zg help query for examples."
456
408
  : "zg query requires text or --hybrid/--fts/--vector routes. Use zg help query for examples.");
457
409
  }
458
- const zvecGrep = await createZvecGrep(createServiceOptions(commandOptions, undefined));
459
- const progress = createIndexProgressReporter();
410
+ if (!commandOptions.rg) {
411
+ const mode = resolveClientMode(commandOptions.mode);
412
+ if (mode !== "direct") {
413
+ await routeByMode({
414
+ mode,
415
+ serverAvailable: () => daemonIsReady(commandOptions.home),
416
+ server: () => runServerQuery(commandOptions, queries, routes),
417
+ direct: () => runDirectQuery(commandOptions, queries),
418
+ });
419
+ return;
420
+ }
421
+ }
422
+ await runDirectQuery(commandOptions, queries);
423
+ }
424
+ async function runDirectQuery(commandOptions, queries) {
425
+ if (commandOptions.refresh === "background") {
426
+ console.error("warning: --refresh background requires Server mode; Direct mode uses --refresh off");
427
+ }
428
+ const serviceOptions = createServiceOptions(commandOptions, undefined);
429
+ const zvecGrep = await createZvecGrep(serviceOptions);
430
+ const progress = createIndexProgressReporter({
431
+ color: commandOptions.color,
432
+ });
460
433
  try {
461
- const result = await zvecGrep.context(contextOptions(commandOptions, queries, (progressEvent) => {
462
- if (progressEvent.phase !== "done") {
434
+ const contextRequest = contextOptions(commandOptions, queries, (progressEvent) => {
435
+ if (progressEvent.phase !== "done")
463
436
  progress.report(progressEvent);
464
- }
465
- }));
437
+ });
438
+ const info = await directQueryInfo(zvecGrep);
439
+ const schema = info.workspaceIndex?.embedding;
440
+ const workspaceRuntime = workspaceRuntimeFromInfo(info);
441
+ const modelInfo = !commandOptions.rg && schema?.provider === "qwen"
442
+ ? await embeddingModelInfo(schema, createServiceOptions(commandOptions, info.root), workspaceRuntime)
443
+ : undefined;
444
+ const plan = modelInfo
445
+ ? await planRemoteSearchAuthorization({
446
+ info,
447
+ model: modelInfo,
448
+ search: normalizedDirectSearchInput(commandOptions, queries, info.root),
449
+ store: authorizationStore(commandOptions),
450
+ })
451
+ : undefined;
452
+ const authorizationResolution = plan
453
+ ? await authorizeCliPlan(plan, commandOptions, "local_search")
454
+ : {};
455
+ const effectiveContextRequest = authorizationResolution.alternative === "local_search"
456
+ ? ftsFallbackContextRequest(contextRequest)
457
+ : contextRequest;
458
+ const result = await withRemoteEmbeddingOperationPermit(authorizationResolution.authorization, () => zvecGrep.context(effectiveContextRequest));
466
459
  progress.finish();
467
460
  if (commandOptions.human) {
468
461
  printHumanContextResult(result, commandOptions);
@@ -473,6 +466,10 @@ async function runQuery(parsed) {
473
466
  for (const line of contextWarningLines(result)) {
474
467
  console.error(line);
475
468
  }
469
+ if (effectiveContextRequest.autoUpdate !== true &&
470
+ indexStatusNeedsRefresh(info.status)) {
471
+ printStaleIndexStatus("idle", indexCompletionFromStatus(info.status));
472
+ }
476
473
  if (commandOptions.debug) {
477
474
  printDebug(result, {
478
475
  trace: commandOptions.trace === true,
@@ -487,7 +484,106 @@ async function runQuery(parsed) {
487
484
  await zvecGrep.close();
488
485
  }
489
486
  }
487
+ async function runServerQuery(options, queries, routes) {
488
+ const searchPolicy = resolveServerSearchPolicy(options);
489
+ const fts = routes
490
+ .filter((route) => route.mode === "fts")
491
+ .map((route) => route.query);
492
+ const vector = routes
493
+ .filter((route) => route.mode === "vector")
494
+ .map((route) => route.query);
495
+ const response = await daemonClient(options).callTextTool("zvec_grep_search", {
496
+ root: resolve(process.cwd()),
497
+ apiKey: options.apiKey,
498
+ device: options.device,
499
+ queries: queries.length ? queries : undefined,
500
+ fts: fts.length ? fts : undefined,
501
+ vector: vector.length ? vector : undefined,
502
+ fuse: options.fuse,
503
+ limit: options.limit,
504
+ trace: options.trace,
505
+ preferSymbol: options.preferSymbol,
506
+ symbolTypes: options.symbolTypes,
507
+ globs: options.globs,
508
+ insensitiveGlobs: options.insensitiveGlobs,
509
+ fileTypes: options.fileTypes,
510
+ excludedFileTypes: options.excludedFileTypes,
511
+ hidden: options.hidden,
512
+ noIgnore: options.noIgnore,
513
+ ignoreFiles: options.ignoreFiles,
514
+ maxDepth: options.maxDepth,
515
+ maxFileSizeBytes: options.maxFileSizeBytes,
516
+ follow: options.follow,
517
+ embeddingConcurrency: options.embeddingConcurrency,
518
+ modifiedAfter: options.modifiedAfter,
519
+ modifiedBefore: options.modifiedBefore,
520
+ freshness: searchPolicy.freshness,
521
+ autoUpdate: searchPolicy.autoUpdate,
522
+ });
523
+ const highlighter = createHighlighter([...queries, ...fts, ...vector].join(" "), shouldUseColor(options));
524
+ for (const line of response.split("\n")) {
525
+ console.log(highlighter(line));
526
+ }
527
+ }
528
+ function printStaleIndexStatus(state, completion) {
529
+ console.error("status: possibly_stale");
530
+ if (!state)
531
+ return;
532
+ const progress = completion?.completed === undefined || completion.total === undefined
533
+ ? ""
534
+ : ` (${completion.completed}/${completion.total})`;
535
+ console.error(`indexing: ${state}${progress}`);
536
+ }
537
+ function daemonClient(options) {
538
+ return new DaemonClient({
539
+ serverUrl: resolveServerUrl(),
540
+ home: options.home,
541
+ tokenFile: options.serverTokenFile,
542
+ allowRemote: options.allowRemote,
543
+ });
544
+ }
545
+ function ftsFallbackContextRequest(request) {
546
+ const queries = [
547
+ ...(request.queries ?? []),
548
+ ...(request.query ? [request.query] : []),
549
+ ...(request.routes ?? []).map((route) => route.query),
550
+ ];
551
+ return {
552
+ ...request,
553
+ query: undefined,
554
+ queries: undefined,
555
+ routes: queries.map((query) => ({ mode: "fts", query })),
556
+ autoUpdate: false,
557
+ };
558
+ }
559
+ async function directQueryInfo(service) {
560
+ return await service.info({ root: process.cwd() });
561
+ }
562
+ function normalizedDirectSearchInput(options, queries, root) {
563
+ const policy = resolveDirectSearchPolicy(options);
564
+ return {
565
+ root,
566
+ apiKey: options.apiKey,
567
+ device: options.device,
568
+ queries: !options.rg && queries.length > 0 ? [...queries] : undefined,
569
+ routes: [...(options.routes ?? [])],
570
+ ...policy,
571
+ };
572
+ }
573
+ async function daemonIsReady(home) {
574
+ return (await serverStatus(home)).ready;
575
+ }
576
+ function printServerControlStatus(status) {
577
+ console.log(`Server: ${status.running ? (status.ready ? "ready" : "starting") : "stopped"}`);
578
+ if (status.pid)
579
+ console.log(`PID: ${status.pid}`);
580
+ if (status.serverUrl)
581
+ console.log(`URL: ${status.serverUrl}`);
582
+ if (status.mcpToolset)
583
+ console.log(`MCP toolset: ${status.mcpToolset}`);
584
+ }
490
585
  function contextOptions(options, queries, onAutoUpdateProgress) {
586
+ const policy = resolveDirectSearchPolicy(options);
491
587
  return {
492
588
  queries: queries.length > 0 ? queries : undefined,
493
589
  rg: options.rg,
@@ -495,9 +591,8 @@ function contextOptions(options, queries, onAutoUpdateProgress) {
495
591
  rgPaths: options.rgPaths,
496
592
  routes: options.routes,
497
593
  fuse: options.fuse,
498
- collection: options.collection,
499
594
  limit: options.limit,
500
- autoUpdate: !options.noAutoUpdate,
595
+ autoUpdate: policy.autoUpdate,
501
596
  onAutoUpdateProgress,
502
597
  trace: options.trace,
503
598
  preferSymbol: options.preferSymbol,
@@ -517,303 +612,23 @@ function contextOptions(options, queries, onAutoUpdateProgress) {
517
612
  embeddingConcurrency: options.embeddingConcurrency,
518
613
  };
519
614
  }
520
- function normalizeRgInput(parsed) {
521
- const explicitPatterns = parsed.options.rgOptions?.patterns ?? [];
522
- const hasPatternFiles = (parsed.options.rgOptions?.patternFiles?.length ?? 0) > 0;
523
- const queries = explicitPatterns.length > 0 || hasPatternFiles
524
- ? explicitPatterns
525
- : parsed.positionals.slice(0, 1);
526
- const paths = explicitPatterns.length > 0 || hasPatternFiles
527
- ? parsed.positionals
528
- : parsed.positionals.slice(1);
529
- return {
530
- queries,
531
- options: {
532
- ...parsed.options,
533
- rgPaths: paths.length > 0 ? paths : undefined,
534
- },
535
- };
536
- }
537
615
  export function createServiceOptions(options, root) {
538
- const embedding = options.embedding ?? process.env.ZVEC_GREP_EMBEDDING;
539
- const apiKey = options.apiKey ??
540
- process.env.ZVEC_GREP_API_KEY ??
541
- process.env.DASHSCOPE_API_KEY ??
542
- process.env.QWEN_API_KEY;
543
- const endpoint = options.endpoint ?? process.env.ZVEC_GREP_ENDPOINT;
544
616
  return {
545
617
  root,
546
618
  home: options.home ?? process.env.ZVEC_GREP_HOME,
547
- embedding,
548
- apiKey,
549
- endpoint,
550
- modelCacheDir: options.modelCacheDir ?? process.env.ZVEC_GREP_MODEL_CACHE,
551
- llamaGpu: options.llamaGpu ?? parseEnvLlamaGpu(process.env.ZVEC_GREP_LLAMA_GPU),
552
- embeddingParallelism: options.embeddingParallelism ??
553
- parseEnvPositiveInteger(process.env.ZVEC_GREP_EMBED_PARALLELISM),
619
+ embedding: options.embedding,
620
+ apiKey: options.apiKey,
621
+ endpoint: options.endpoint,
622
+ modelCacheDir: options.modelCacheDir,
623
+ device: options.device,
624
+ authorizationSigningKeyPath: process.env.ZVEC_GREP_AUTHORIZATION_KEY_FILE,
554
625
  };
555
626
  }
556
- function persistExplicitGlobalConfig(options, indexedProvider) {
557
- if (!updateGlobalConfigFromExplicitOptions(options, indexedProvider)) {
558
- return;
559
- }
560
- console.log(`Global config: ${globalConfigPath()}`);
561
- }
562
- function parseEnvLlamaGpu(value) {
563
- const normalized = value?.trim().toLowerCase() ?? "";
564
- if (!normalized) {
565
- return undefined;
566
- }
567
- if (normalized === "auto" ||
568
- normalized === "metal" ||
569
- normalized === "vulkan" ||
570
- normalized === "cuda") {
571
- return normalized;
572
- }
573
- if (["false", "off", "none", "disable", "disabled", "0"].includes(normalized)) {
574
- return false;
575
- }
576
- return undefined;
577
- }
578
- function parseEnvPositiveInteger(value) {
579
- const normalized = value?.trim() ?? "";
580
- if (!/^\d+$/.test(normalized)) {
581
- return undefined;
582
- }
583
- const parsed = Number(normalized);
584
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
585
- }
586
- function resolveCodexHome() {
587
- return resolve(process.env.CODEX_HOME ?? resolve(homedir(), ".codex"));
588
- }
589
- async function writeMarkedFile(options) {
590
- let existing = await readTextFileIfExists(options.path);
591
- const next = replaceMarkedBlock(existing, options.startMarker, options.endMarker, options.block);
592
- if (next === null) {
593
- existing = removeOrphanedMarkers(existing, options.startMarker, options.endMarker);
594
- }
595
- if (next === null && options.hasConflict?.(existing)) {
596
- if (!options.force) {
597
- throw new Error(options.conflictMessage ??
598
- `Existing unmanaged configuration found in ${options.path}`);
599
- }
600
- existing = options.removeConflict
601
- ? options.removeConflict(existing)
602
- : existing;
603
- }
604
- await writeTextFileAtomic(options.path, next ?? appendMarkedBlock(existing, options.block));
605
- }
606
- async function removeMarkedFile(options) {
607
- const existing = await readTextFileIfExists(options.path);
608
- if (!existing) {
609
- return;
610
- }
611
- const next = replaceMarkedBlock(existing, options.startMarker, options.endMarker, "") ??
612
- removeOrphanedMarkers(existing, options.startMarker, options.endMarker);
613
- if (next === existing) {
614
- return;
615
- }
616
- await writeTextFileAtomic(options.path, next);
617
- }
618
- async function writeTextFileAtomic(path, content) {
619
- const targetPath = await resolveAtomicWriteTarget(path);
620
- await mkdir(dirname(targetPath), { recursive: true });
621
- const mode = await fileModeIfExists(targetPath);
622
- const temporaryPath = `${targetPath}.${process.pid}.${randomUUID()}.tmp`;
623
- let temporaryFile;
624
- try {
625
- temporaryFile = await open(temporaryPath, "wx", mode);
626
- await temporaryFile.writeFile(content, "utf8");
627
- await temporaryFile.sync();
628
- await temporaryFile.close();
629
- temporaryFile = undefined;
630
- if (mode !== undefined) {
631
- await chmod(temporaryPath, mode);
632
- }
633
- await rename(temporaryPath, targetPath);
634
- }
635
- catch (error) {
636
- await temporaryFile?.close().catch(() => undefined);
637
- await unlink(temporaryPath).catch(() => undefined);
638
- throw error;
639
- }
640
- }
641
- async function resolveAtomicWriteTarget(path, visited = new Set()) {
642
- const absolutePath = resolve(path);
643
- if (visited.has(absolutePath)) {
644
- throw new Error(`Cannot atomically write through circular symbolic link: ${path}`);
645
- }
646
- try {
647
- const file = await lstat(absolutePath);
648
- if (!file.isSymbolicLink()) {
649
- return absolutePath;
650
- }
651
- visited.add(absolutePath);
652
- const link = await readlink(absolutePath);
653
- return resolveAtomicWriteTarget(resolve(dirname(absolutePath), link), visited);
654
- }
655
- catch (error) {
656
- if (isNodeError(error) && error.code === "ENOENT") {
657
- return absolutePath;
658
- }
659
- throw error;
660
- }
661
- }
662
- async function fileModeIfExists(path) {
663
- try {
664
- return (await stat(path)).mode & 0o777;
665
- }
666
- catch (error) {
667
- if (isNodeError(error) && error.code === "ENOENT") {
668
- return undefined;
669
- }
670
- throw error;
671
- }
672
- }
673
- async function readTextFileIfExists(path) {
674
- try {
675
- return await readFile(path, "utf8");
676
- }
677
- catch (error) {
678
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
679
- return "";
680
- }
681
- throw error;
682
- }
683
- }
684
- function isNodeError(error) {
685
- return typeof error === "object" && error !== null && "code" in error;
686
- }
687
- function replaceMarkedBlock(existing, startMarker, endMarker, block) {
688
- const lines = existing.split(/\r?\n/);
689
- const markerLines = new Set();
690
- const ranges = [];
691
- let pendingStart;
692
- for (const [index, line] of lines.entries()) {
693
- const trimmed = line.trim();
694
- if (trimmed === startMarker) {
695
- markerLines.add(index);
696
- pendingStart = index;
697
- continue;
698
- }
699
- if (trimmed === endMarker) {
700
- markerLines.add(index);
701
- if (pendingStart !== undefined) {
702
- ranges.push({ start: pendingStart, end: index });
703
- pendingStart = undefined;
704
- }
705
- }
706
- }
707
- const first = ranges[0];
708
- if (!first) {
709
- return null;
710
- }
711
- const before = retainedMarkedLines(lines, 0, first.start, markerLines, [])
712
- .join("\n")
713
- .trimEnd();
714
- const after = retainedMarkedLines(lines, first.end + 1, lines.length, markerLines, ranges.slice(1))
715
- .join("\n")
716
- .trim();
717
- return ([before, block.trim(), after.trim()]
718
- .filter((part) => part.length > 0)
719
- .join("\n\n") + "\n");
720
- }
721
- function retainedMarkedLines(lines, start, end, markerLines, removedRanges) {
722
- const retained = [];
723
- for (let index = start; index < end; index += 1) {
724
- if (markerLines.has(index)) {
725
- continue;
726
- }
727
- if (removedRanges.some((range) => index >= range.start && index <= range.end)) {
728
- continue;
729
- }
730
- retained.push(lines[index]);
731
- }
732
- return retained;
733
- }
734
- function removeOrphanedMarkers(existing, startMarker, endMarker) {
735
- return (existing
736
- .split(/\r?\n/)
737
- .filter((line) => {
738
- const trimmed = line.trim();
739
- return trimmed !== startMarker && trimmed !== endMarker;
740
- })
741
- .join("\n")
742
- .trimEnd() + "\n");
743
- }
744
- function appendMarkedBlock(existing, block) {
745
- const prefix = existing.trimEnd();
746
- return `${prefix.length > 0 ? `${prefix}\n\n` : ""}${block.trim()}\n`;
747
- }
748
- function hasCodexMcpServerConfig(existing) {
749
- return existing.split(/\r?\n/).some((line) => {
750
- const tableName = tomlTableName(line);
751
- return tableName !== undefined && isCodexMcpServerTableName(tableName);
752
- });
753
- }
754
- function removeCodexMcpServerConfig(existing) {
755
- const lines = existing.split(/\r?\n/);
756
- const kept = [];
757
- let skipping = false;
758
- for (const line of lines) {
759
- const tableName = tomlTableName(line);
760
- if (tableName !== undefined) {
761
- skipping = isCodexMcpServerTableName(tableName);
762
- }
763
- if (!skipping) {
764
- kept.push(line);
765
- }
766
- }
767
- return kept.join("\n").trimEnd() + "\n";
768
- }
769
- function tomlTableName(line) {
770
- return line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/)?.[1]?.trim();
771
- }
772
- function isCodexMcpServerTableName(tableName) {
773
- return /^(?:mcp_servers|"mcp_servers"|'mcp_servers')\s*\.\s*(?:zvec_grep|"zvec_grep"|'zvec_grep')(?:\s*\.|$)/.test(tableName);
774
- }
775
- function codexConfigBlock(mcpToolTimeoutSeconds) {
776
- return `${ZVEC_GREP_CONFIG_START}
777
- [mcp_servers.zvec_grep]
778
- command = "zg"
779
- args = ["serve", "--mcp"]
780
- env_vars = [
781
- "DASHSCOPE_API_KEY",
782
- "QWEN_API_KEY",
783
- "ZVEC_GREP_API_KEY",
784
- "ZVEC_GREP_EMBEDDING",
785
- "ZVEC_GREP_ENDPOINT",
786
- "ZVEC_GREP_HOME",
787
- "ZVEC_GREP_MODEL_CACHE",
788
- "ZVEC_GREP_LLAMA_GPU",
789
- "ZVEC_GREP_EMBED_PARALLELISM",
790
- "NODE_LLAMA_CPP_CMAKE_OPTION_GGML_OPENMP",
791
- "NODE_LLAMA_CPP_CMAKE_OPTION_GGML_NATIVE",
792
- "NODE_LLAMA_CPP_CMAKE_OPTION_GGML_CPU_ARM_ARCH"
793
- ]
794
- startup_timeout_sec = 20
795
- tool_timeout_sec = ${mcpToolTimeoutSeconds}
796
- default_tools_approval_mode = "auto"
797
- ${ZVEC_GREP_CONFIG_END}`;
798
- }
799
- function codexAgentsBlock() {
800
- return `${ZVEC_GREP_AGENTS_START}
801
- ## zvec-grep
802
-
803
- Use zvec-grep before grep, rg, or broad file reads when you need to understand or locate code.
804
-
805
- - **MCP tools**: Use \`zvec_grep_search\` for indexed semantic/lexical code search and \`zvec_grep_rg\` for explicit no-index lexical search.
806
- - **Indexing and status**: These are CLI-only operations. If an index is missing, use \`zvec_grep_rg\` and mention \`zg index\` as the opt-in setup path; use \`zg status\` when status inspection is needed.
807
- - **Shell fallback**: If the MCP server is unavailable, use \`zg status\`, \`zg query "<query>"\`, and \`zg query --rg "<pattern>"\`.
808
-
809
- Prefer focused -g/--glob and -t/--type filters, and exclude dependencies, generated output, caches, build artifacts, and logs unless the task is about those files.
810
- ${ZVEC_GREP_AGENTS_END}`;
811
- }
812
627
  function resolveIndexRoot(root) {
813
628
  if (root !== undefined) {
814
629
  return root;
815
630
  }
816
- return findNearestAnonymousWorkspace(process.cwd())?.root ?? process.cwd();
631
+ return findNearestWorkspace(process.cwd())?.root ?? process.cwd();
817
632
  }
818
633
  function indexRootPath(path, options) {
819
634
  return {