@opencodehub/cli 0.1.0

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 (191) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +85 -0
  3. package/dist/agent-context.d.ts +54 -0
  4. package/dist/agent-context.d.ts.map +1 -0
  5. package/dist/agent-context.js +122 -0
  6. package/dist/agent-context.js.map +1 -0
  7. package/dist/cobol-proleap-setup.d.ts +77 -0
  8. package/dist/cobol-proleap-setup.d.ts.map +1 -0
  9. package/dist/cobol-proleap-setup.js +289 -0
  10. package/dist/cobol-proleap-setup.js.map +1 -0
  11. package/dist/commands/analyze.d.ts +234 -0
  12. package/dist/commands/analyze.d.ts.map +1 -0
  13. package/dist/commands/analyze.js +1096 -0
  14. package/dist/commands/analyze.js.map +1 -0
  15. package/dist/commands/augment.d.ts +48 -0
  16. package/dist/commands/augment.d.ts.map +1 -0
  17. package/dist/commands/augment.js +249 -0
  18. package/dist/commands/augment.js.map +1 -0
  19. package/dist/commands/baseline.d.ts +68 -0
  20. package/dist/commands/baseline.d.ts.map +1 -0
  21. package/dist/commands/baseline.js +110 -0
  22. package/dist/commands/baseline.js.map +1 -0
  23. package/dist/commands/bench.d.ts +54 -0
  24. package/dist/commands/bench.d.ts.map +1 -0
  25. package/dist/commands/bench.js +283 -0
  26. package/dist/commands/bench.js.map +1 -0
  27. package/dist/commands/ci-init.d.ts +37 -0
  28. package/dist/commands/ci-init.d.ts.map +1 -0
  29. package/dist/commands/ci-init.js +115 -0
  30. package/dist/commands/ci-init.js.map +1 -0
  31. package/dist/commands/clean.d.ts +13 -0
  32. package/dist/commands/clean.d.ts.map +1 -0
  33. package/dist/commands/clean.js +38 -0
  34. package/dist/commands/clean.js.map +1 -0
  35. package/dist/commands/code-pack.d.ts +105 -0
  36. package/dist/commands/code-pack.d.ts.map +1 -0
  37. package/dist/commands/code-pack.js +187 -0
  38. package/dist/commands/code-pack.js.map +1 -0
  39. package/dist/commands/context.d.ts +30 -0
  40. package/dist/commands/context.d.ts.map +1 -0
  41. package/dist/commands/context.js +237 -0
  42. package/dist/commands/context.js.map +1 -0
  43. package/dist/commands/detect-changes.d.ts +26 -0
  44. package/dist/commands/detect-changes.d.ts.map +1 -0
  45. package/dist/commands/detect-changes.js +73 -0
  46. package/dist/commands/detect-changes.js.map +1 -0
  47. package/dist/commands/doctor.d.ts +52 -0
  48. package/dist/commands/doctor.d.ts.map +1 -0
  49. package/dist/commands/doctor.js +472 -0
  50. package/dist/commands/doctor.js.map +1 -0
  51. package/dist/commands/find-enclosing-symbol.d.ts +67 -0
  52. package/dist/commands/find-enclosing-symbol.d.ts.map +1 -0
  53. package/dist/commands/find-enclosing-symbol.js +106 -0
  54. package/dist/commands/find-enclosing-symbol.js.map +1 -0
  55. package/dist/commands/group.d.ts +123 -0
  56. package/dist/commands/group.d.ts.map +1 -0
  57. package/dist/commands/group.js +448 -0
  58. package/dist/commands/group.js.map +1 -0
  59. package/dist/commands/impact.d.ts +23 -0
  60. package/dist/commands/impact.d.ts.map +1 -0
  61. package/dist/commands/impact.js +91 -0
  62. package/dist/commands/impact.js.map +1 -0
  63. package/dist/commands/index-repo.d.ts +39 -0
  64. package/dist/commands/index-repo.d.ts.map +1 -0
  65. package/dist/commands/index-repo.js +148 -0
  66. package/dist/commands/index-repo.js.map +1 -0
  67. package/dist/commands/ingest-sarif.d.ts +64 -0
  68. package/dist/commands/ingest-sarif.d.ts.map +1 -0
  69. package/dist/commands/ingest-sarif.js +381 -0
  70. package/dist/commands/ingest-sarif.js.map +1 -0
  71. package/dist/commands/init.d.ts +75 -0
  72. package/dist/commands/init.d.ts.map +1 -0
  73. package/dist/commands/init.js +315 -0
  74. package/dist/commands/init.js.map +1 -0
  75. package/dist/commands/list.d.ts +17 -0
  76. package/dist/commands/list.d.ts.map +1 -0
  77. package/dist/commands/list.js +79 -0
  78. package/dist/commands/list.js.map +1 -0
  79. package/dist/commands/mcp.d.ts +8 -0
  80. package/dist/commands/mcp.d.ts.map +1 -0
  81. package/dist/commands/mcp.js +28 -0
  82. package/dist/commands/mcp.js.map +1 -0
  83. package/dist/commands/open-store.d.ts +25 -0
  84. package/dist/commands/open-store.d.ts.map +1 -0
  85. package/dist/commands/open-store.js +47 -0
  86. package/dist/commands/open-store.js.map +1 -0
  87. package/dist/commands/pack.d.ts +35 -0
  88. package/dist/commands/pack.d.ts.map +1 -0
  89. package/dist/commands/pack.js +83 -0
  90. package/dist/commands/pack.js.map +1 -0
  91. package/dist/commands/query.d.ts +85 -0
  92. package/dist/commands/query.d.ts.map +1 -0
  93. package/dist/commands/query.js +309 -0
  94. package/dist/commands/query.js.map +1 -0
  95. package/dist/commands/scan.d.ts +81 -0
  96. package/dist/commands/scan.d.ts.map +1 -0
  97. package/dist/commands/scan.js +407 -0
  98. package/dist/commands/scan.js.map +1 -0
  99. package/dist/commands/setup.d.ts +178 -0
  100. package/dist/commands/setup.d.ts.map +1 -0
  101. package/dist/commands/setup.js +370 -0
  102. package/dist/commands/setup.js.map +1 -0
  103. package/dist/commands/sql.d.ts +19 -0
  104. package/dist/commands/sql.d.ts.map +1 -0
  105. package/dist/commands/sql.js +51 -0
  106. package/dist/commands/sql.js.map +1 -0
  107. package/dist/commands/status.d.ts +13 -0
  108. package/dist/commands/status.d.ts.map +1 -0
  109. package/dist/commands/status.js +66 -0
  110. package/dist/commands/status.js.map +1 -0
  111. package/dist/commands/verdict-render.d.ts +33 -0
  112. package/dist/commands/verdict-render.d.ts.map +1 -0
  113. package/dist/commands/verdict-render.js +123 -0
  114. package/dist/commands/verdict-render.js.map +1 -0
  115. package/dist/commands/verdict.d.ts +61 -0
  116. package/dist/commands/verdict.d.ts.map +1 -0
  117. package/dist/commands/verdict.js +146 -0
  118. package/dist/commands/verdict.js.map +1 -0
  119. package/dist/commands/wiki.d.ts +26 -0
  120. package/dist/commands/wiki.d.ts.map +1 -0
  121. package/dist/commands/wiki.js +74 -0
  122. package/dist/commands/wiki.js.map +1 -0
  123. package/dist/editors/claude-code.d.ts +23 -0
  124. package/dist/editors/claude-code.d.ts.map +1 -0
  125. package/dist/editors/claude-code.js +58 -0
  126. package/dist/editors/claude-code.js.map +1 -0
  127. package/dist/editors/codex.d.ts +22 -0
  128. package/dist/editors/codex.d.ts.map +1 -0
  129. package/dist/editors/codex.js +59 -0
  130. package/dist/editors/codex.js.map +1 -0
  131. package/dist/editors/cursor.d.ts +13 -0
  132. package/dist/editors/cursor.d.ts.map +1 -0
  133. package/dist/editors/cursor.js +21 -0
  134. package/dist/editors/cursor.js.map +1 -0
  135. package/dist/editors/index.d.ts +12 -0
  136. package/dist/editors/index.d.ts.map +1 -0
  137. package/dist/editors/index.js +11 -0
  138. package/dist/editors/index.js.map +1 -0
  139. package/dist/editors/opencode.d.ts +23 -0
  140. package/dist/editors/opencode.d.ts.map +1 -0
  141. package/dist/editors/opencode.js +61 -0
  142. package/dist/editors/opencode.js.map +1 -0
  143. package/dist/editors/types.d.ts +33 -0
  144. package/dist/editors/types.d.ts.map +1 -0
  145. package/dist/editors/types.js +19 -0
  146. package/dist/editors/types.js.map +1 -0
  147. package/dist/editors/windows-wrap.d.ts +19 -0
  148. package/dist/editors/windows-wrap.d.ts.map +1 -0
  149. package/dist/editors/windows-wrap.js +28 -0
  150. package/dist/editors/windows-wrap.js.map +1 -0
  151. package/dist/editors/windsurf.d.ts +12 -0
  152. package/dist/editors/windsurf.d.ts.map +1 -0
  153. package/dist/editors/windsurf.js +21 -0
  154. package/dist/editors/windsurf.js.map +1 -0
  155. package/dist/embedder-downloader.d.ts +87 -0
  156. package/dist/embedder-downloader.d.ts.map +1 -0
  157. package/dist/embedder-downloader.js +261 -0
  158. package/dist/embedder-downloader.js.map +1 -0
  159. package/dist/fs-atomic.d.ts +22 -0
  160. package/dist/fs-atomic.d.ts.map +1 -0
  161. package/dist/fs-atomic.js +28 -0
  162. package/dist/fs-atomic.js.map +1 -0
  163. package/dist/groups.d.ts +64 -0
  164. package/dist/groups.d.ts.map +1 -0
  165. package/dist/groups.js +172 -0
  166. package/dist/groups.js.map +1 -0
  167. package/dist/index.d.ts +11 -0
  168. package/dist/index.d.ts.map +1 -0
  169. package/dist/index.js +703 -0
  170. package/dist/index.js.map +1 -0
  171. package/dist/lib/is-indexed.d.ts +20 -0
  172. package/dist/lib/is-indexed.d.ts.map +1 -0
  173. package/dist/lib/is-indexed.js +35 -0
  174. package/dist/lib/is-indexed.js.map +1 -0
  175. package/dist/registry.d.ts +64 -0
  176. package/dist/registry.d.ts.map +1 -0
  177. package/dist/registry.js +145 -0
  178. package/dist/registry.js.map +1 -0
  179. package/dist/scip-downloader.d.ts +138 -0
  180. package/dist/scip-downloader.d.ts.map +1 -0
  181. package/dist/scip-downloader.js +372 -0
  182. package/dist/scip-downloader.js.map +1 -0
  183. package/dist/scip-pins.d.ts +99 -0
  184. package/dist/scip-pins.d.ts.map +1 -0
  185. package/dist/scip-pins.js +195 -0
  186. package/dist/scip-pins.js.map +1 -0
  187. package/dist/skills-gen.d.ts +47 -0
  188. package/dist/skills-gen.d.ts.map +1 -0
  189. package/dist/skills-gen.js +292 -0
  190. package/dist/skills-gen.js.map +1 -0
  191. package/package.json +81 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * `codehub pack [path]` — produce a single-file repo snapshot suitable
3
+ * for dropping into an LLM context window, via the `repomix` CLI.
4
+ *
5
+ * Repomix is invoked with `--style xml --compress` by default so the
6
+ * output is Anthropic-friendly and tree-sitter-signature-compressed. The
7
+ * command is an OUTPUT-side convenience; OpenCodeHub does NOT use
8
+ * repomix for indexing or embedding (see ADR 0004).
9
+ *
10
+ * This is a thin wrapper — we shell to `npx repomix@<pin>` so operators
11
+ * can override by running repomix directly. The wrapper exists to make
12
+ * the output path discoverable and to put the produced file under
13
+ * `.codehub/pack/` so it's ignored by the standard gitignore pattern.
14
+ */
15
+ import { spawn } from "node:child_process";
16
+ import { existsSync, statSync } from "node:fs";
17
+ import { mkdir } from "node:fs/promises";
18
+ import { dirname, join, resolve } from "node:path";
19
+ const DEFAULT_REPOMIX_VERSION = "1.14.0";
20
+ export async function runPack(path, opts = {}) {
21
+ const start = Date.now();
22
+ const repoPath = resolve(path);
23
+ const style = opts.style ?? "xml";
24
+ const compress = opts.compress ?? true;
25
+ const version = opts.repomixVersion ?? DEFAULT_REPOMIX_VERSION;
26
+ const outputPath = opts.outputPath
27
+ ? resolve(opts.outputPath)
28
+ : join(repoPath, ".codehub", "pack", `repo.${extForStyle(style)}`);
29
+ await mkdir(dirname(outputPath), { recursive: true });
30
+ const args = [`repomix@${version}`, "--style", style, "--output", outputPath];
31
+ if (compress)
32
+ args.push("--compress");
33
+ if (opts.removeComments)
34
+ args.push("--remove-comments");
35
+ await new Promise((res, rej) => {
36
+ const child = spawn("npx", args, {
37
+ cwd: repoPath,
38
+ env: { ...process.env },
39
+ stdio: ["ignore", "inherit", "inherit"],
40
+ });
41
+ const timer = opts.timeoutMs
42
+ ? setTimeout(() => {
43
+ child.kill("SIGTERM");
44
+ }, opts.timeoutMs)
45
+ : undefined;
46
+ child.on("error", (err) => {
47
+ if (timer)
48
+ clearTimeout(timer);
49
+ if (err.code === "ENOENT") {
50
+ rej(new Error("codehub pack: `npx` not found on PATH. Install Node.js 20+."));
51
+ }
52
+ else {
53
+ rej(err);
54
+ }
55
+ });
56
+ child.on("exit", (code) => {
57
+ if (timer)
58
+ clearTimeout(timer);
59
+ if (code === 0)
60
+ res();
61
+ else
62
+ rej(new Error(`codehub pack: repomix exited ${code}`));
63
+ });
64
+ });
65
+ if (!existsSync(outputPath)) {
66
+ throw new Error(`codehub pack: repomix did not produce ${outputPath}`);
67
+ }
68
+ const bytes = statSync(outputPath).size;
69
+ return { outputPath, bytes, durationMs: Date.now() - start };
70
+ }
71
+ function extForStyle(style) {
72
+ switch (style) {
73
+ case "xml":
74
+ return "xml";
75
+ case "markdown":
76
+ return "md";
77
+ case "json":
78
+ return "json";
79
+ case "plain":
80
+ return "txt";
81
+ }
82
+ }
83
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../src/commands/pack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `codehub query <text>` — hybrid BM25 + vector search.
3
+ *
4
+ * Mirrors the MCP `query` tool's smart path: probe the `embeddings` table,
5
+ * try to open an embedder, run `hybridSearch` when both succeed, and
6
+ * collapse to BM25 with a single stderr warning on any failure. Shares the
7
+ * probe + open helpers (`embeddingsPopulated`, `tryOpenEmbedder`) via
8
+ * `@opencodehub/search` so CLI and MCP surfaces cannot drift.
9
+ *
10
+ * Flags:
11
+ * - `--bm25-only` — skip the embedder probe entirely.
12
+ * - `--rerank-top-k <n>` — number of fused hits RRF returns (default
13
+ * `DEFAULT_RRF_TOP_K = 50`); clamped by `--limit` at print time.
14
+ * - `--context <text>` + `--goal <text>` — prefixed to the search text.
15
+ * - `--content` — attach capped symbol source to each hit.
16
+ * - `--json` — emit machine-readable output.
17
+ * - `--zoom` — P03 coarse-to-fine retrieval (file tier → symbol tier).
18
+ * - `--fanout <n>` — files to shortlist at the coarse step for `--zoom`.
19
+ * - `--granularity <tier>` — restrict ANN to one hierarchical tier
20
+ * (symbol/file/community). Defaults to "symbol".
21
+ *
22
+ * Hybrid ranking priority matches the MCP tool:
23
+ * 1. `CODEHUB_EMBEDDING_URL` + `CODEHUB_EMBEDDING_MODEL` → HTTP embedder.
24
+ * 2. Otherwise local ONNX gte-modernbert-base weights.
25
+ * 3. On failure to open (missing weights, unreachable HTTP) → warn + BM25.
26
+ */
27
+ import { type Embedder } from "@opencodehub/embedder";
28
+ import { type OpenStoreResult } from "./open-store.js";
29
+ /**
30
+ * Hook for tests to inject a pre-built store without touching DuckDB. The
31
+ * default implementation delegates to {@link openStoreForCommand}. Kept
32
+ * separate from the public `QueryOptions` interface so end-user CLI callers
33
+ * aren't tempted to pass an in-process store.
34
+ */
35
+ export interface QueryRuntimeHooks {
36
+ readonly openStore?: (opts: QueryOptions) => Promise<OpenStoreResult>;
37
+ /**
38
+ * Embedder factory — production uses the default lazy-import path; tests
39
+ * inject a fake so they don't need gte-modernbert-base weights on disk. Any
40
+ * throw is caught by {@link tryOpenEmbedder} and collapses to BM25.
41
+ */
42
+ readonly openEmbedder?: () => Promise<Embedder>;
43
+ }
44
+ export interface QueryOptions {
45
+ readonly limit?: number;
46
+ readonly repo?: string;
47
+ readonly home?: string;
48
+ readonly json?: boolean;
49
+ /** `--content` — attach the symbol body (capped at 2000 chars) to each hit. */
50
+ readonly content?: boolean;
51
+ /** `--context <text>` — prefix to the search text before BM25 + embedding. */
52
+ readonly context?: string;
53
+ /** `--goal <text>` — additional prefix to the search text (steers ranking). */
54
+ readonly goal?: string;
55
+ /** `--max-symbols <n>` — cap on process-grouped symbols. Today: no-op. */
56
+ readonly maxSymbols?: number;
57
+ /** `--bm25-only` — skip the embedder probe, go straight to BM25. */
58
+ readonly bm25Only?: boolean;
59
+ /** `--rerank-top-k <n>` — number of fused hits RRF should return. */
60
+ readonly rerankTopK?: number;
61
+ /**
62
+ * `--zoom` — enable P03 coarse-to-fine retrieval. Requires the index to
63
+ * have been built with `--granularity symbol,file,community` AND an
64
+ * embedder to be available (weights on disk or `CODEHUB_EMBEDDING_URL`
65
+ * set). Falls back to BM25 when no embedder is available.
66
+ */
67
+ readonly zoom?: boolean;
68
+ /** `--fanout <n>` — files to shortlist at the coarse step when `--zoom` is on. */
69
+ readonly fanout?: number;
70
+ /**
71
+ * `--granularity <tier>` — restrict the ANN leg to this hierarchical
72
+ * tier. Defaults to "symbol". Pass "community" for architectural
73
+ * queries that should land on Community nodes.
74
+ */
75
+ readonly granularity?: "symbol" | "file" | "community";
76
+ /**
77
+ * `--force-backend-mismatch` — bypass the embedder fingerprint refusal.
78
+ * Lets a query proceed against an `embeddings` table that was populated
79
+ * by a different embedder than the one currently active. The vectors
80
+ * may be stale; results may misrank. Default `false`.
81
+ */
82
+ readonly forceBackendMismatch?: boolean;
83
+ }
84
+ export declare function runQuery(text: string, opts?: QueryOptions, hooks?: QueryRuntimeHooks): Promise<void>;
85
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,uBAAuB,CAAC;AAW/B,OAAO,EAAE,KAAK,eAAe,EAAuB,MAAM,iBAAiB,CAAC;AAO5E;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACtE;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACvD;;;;;OAKG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CACzC;AAoBD,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,YAAiB,EACvB,KAAK,GAAE,iBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAiHf"}
@@ -0,0 +1,309 @@
1
+ /**
2
+ * `codehub query <text>` — hybrid BM25 + vector search.
3
+ *
4
+ * Mirrors the MCP `query` tool's smart path: probe the `embeddings` table,
5
+ * try to open an embedder, run `hybridSearch` when both succeed, and
6
+ * collapse to BM25 with a single stderr warning on any failure. Shares the
7
+ * probe + open helpers (`embeddingsPopulated`, `tryOpenEmbedder`) via
8
+ * `@opencodehub/search` so CLI and MCP surfaces cannot drift.
9
+ *
10
+ * Flags:
11
+ * - `--bm25-only` — skip the embedder probe entirely.
12
+ * - `--rerank-top-k <n>` — number of fused hits RRF returns (default
13
+ * `DEFAULT_RRF_TOP_K = 50`); clamped by `--limit` at print time.
14
+ * - `--context <text>` + `--goal <text>` — prefixed to the search text.
15
+ * - `--content` — attach capped symbol source to each hit.
16
+ * - `--json` — emit machine-readable output.
17
+ * - `--zoom` — P03 coarse-to-fine retrieval (file tier → symbol tier).
18
+ * - `--fanout <n>` — files to shortlist at the coarse step for `--zoom`.
19
+ * - `--granularity <tier>` — restrict ANN to one hierarchical tier
20
+ * (symbol/file/community). Defaults to "symbol".
21
+ *
22
+ * Hybrid ranking priority matches the MCP tool:
23
+ * 1. `CODEHUB_EMBEDDING_URL` + `CODEHUB_EMBEDDING_MODEL` → HTTP embedder.
24
+ * 2. Otherwise local ONNX gte-modernbert-base weights.
25
+ * 3. On failure to open (missing weights, unreachable HTTP) → warn + BM25.
26
+ */
27
+ import { readFile } from "node:fs/promises";
28
+ import { isAbsolute, resolve } from "node:path";
29
+ import { assertEmbedderCompatible, openDefaultEmbedder, } from "@opencodehub/embedder";
30
+ import { bm25Search, DEFAULT_RRF_TOP_K, embeddingsPopulated, hybridSearch, tryOpenEmbedder, } from "@opencodehub/search";
31
+ import { openStoreForCommand } from "./open-store.js";
32
+ /** Per-symbol cap for `--content`. Matches the MCP `query` tool contract. */
33
+ const INCLUDE_CONTENT_CHAR_CAP = 2000;
34
+ /** Truncation cap for the text-mode SUMMARY column. Matches the MCP snippet cap. */
35
+ const SUMMARY_COLUMN_CHAR_CAP = 120;
36
+ export async function runQuery(text, opts = {}, hooks = {}) {
37
+ const limit = opts.limit ?? 10;
38
+ const rerankTopK = opts.rerankTopK ?? DEFAULT_RRF_TOP_K;
39
+ const openStore = hooks.openStore ?? openStoreForCommand;
40
+ // Shared HTTP-priority + ONNX-fallback factory. ONNX binding only loads
41
+ // on the fallback branch, so plain (non-dynamic) import is fine here.
42
+ const openEmbedder = hooks.openEmbedder ?? (() => openDefaultEmbedder());
43
+ const { store, repoPath } = await openStore(opts);
44
+ const graph = store.graph;
45
+ try {
46
+ const searchText = buildSearchText(text, opts.context, opts.goal);
47
+ let ranked;
48
+ let mode;
49
+ if (opts.bm25Only === true) {
50
+ // Explicit opt-out: never touch the embedder probe.
51
+ ranked = await runBm25(graph, searchText, limit);
52
+ mode = "bm25";
53
+ }
54
+ else if (await embeddingsPopulated(graph)) {
55
+ const embedder = await tryOpenEmbedder(openEmbedder, "[cli:query]");
56
+ if (embedder !== null) {
57
+ try {
58
+ // Refuse the hybrid path when the persisted embedder modelId
59
+ // differs from the current one. Same-dim vectors from different
60
+ // embedders silently corrupt ranking. `--force-backend-mismatch`
61
+ // lets the operator override; legacy stores have
62
+ // `embedderModelId === undefined` and the check passes.
63
+ const meta = await store.graph.getMeta();
64
+ const compat = assertEmbedderCompatible(meta?.embedderModelId, embedder.modelId, opts.forceBackendMismatch === true);
65
+ if (!compat.ok) {
66
+ process.stderr.write(`Embedder mismatch: store was indexed with '${compat.persistedModelId}', ` +
67
+ `current embedder is '${compat.currentModelId}'.\n${compat.hint}\n`);
68
+ process.exit(2);
69
+ }
70
+ const fused = await hybridSearch(graph, {
71
+ text: searchText,
72
+ limit: rerankTopK,
73
+ ...(opts.zoom === true ? { mode: "zoom" } : {}),
74
+ ...(opts.fanout !== undefined ? { zoomFanout: opts.fanout } : {}),
75
+ ...(opts.granularity !== undefined ? { granularity: opts.granularity } : {}),
76
+ }, embedder);
77
+ ranked = await hydrateFused(graph, fused, limit);
78
+ mode = "hybrid";
79
+ }
80
+ finally {
81
+ // Always release the native session — even on error — so the ONNX
82
+ // runtime resources aren't leaked between CLI invocations.
83
+ await embedder.close();
84
+ }
85
+ }
86
+ else {
87
+ ranked = await runBm25(graph, searchText, limit);
88
+ mode = "bm25";
89
+ }
90
+ }
91
+ else {
92
+ ranked = await runBm25(graph, searchText, limit);
93
+ mode = "bm25";
94
+ }
95
+ // Merge P04 summary-hydration onto the P02 hybrid/BM25 rows. Single
96
+ // round trip via the temporal-tier `lookupSymbolSummariesByNode`
97
+ // finder; missing table / missing rows / lookup failures all degrade
98
+ // silently — summaries are enrichment, not load-bearing.
99
+ const summaryMap = await joinSummaries(store, ranked.map((r) => r.nodeId));
100
+ const rows = summaryMap.size === 0
101
+ ? ranked
102
+ : ranked.map((r) => {
103
+ const row = summaryMap.get(r.nodeId);
104
+ if (row === undefined)
105
+ return r;
106
+ return {
107
+ ...r,
108
+ summary: row.summaryText,
109
+ ...(row.signatureSummary !== undefined
110
+ ? { signatureSummary: row.signatureSummary }
111
+ : {}),
112
+ };
113
+ });
114
+ // Best-effort `--content` attachment runs the same way for BM25 and
115
+ // hybrid; the store-native BM25 path already surfaces filePath but not
116
+ // line ranges, so the CLI reads the whole file (capped) — matching the
117
+ // previous CLI contract.
118
+ const withContent = opts.content === true
119
+ ? await Promise.all(rows.map(async (r) => {
120
+ const content = await readSymbolContent(repoPath, r);
121
+ return content !== null ? { ...r, content } : r;
122
+ }))
123
+ : rows;
124
+ if (opts.json === true) {
125
+ console.log(JSON.stringify({ repoPath, mode, results: withContent }, null, 2));
126
+ return;
127
+ }
128
+ printResults(withContent, text, repoPath, mode);
129
+ }
130
+ finally {
131
+ await store.close();
132
+ }
133
+ }
134
+ /**
135
+ * Run the BM25-only leg directly through `@opencodehub/search`. Same
136
+ * parameters the MCP tool passes, so ranking parity is automatic.
137
+ */
138
+ async function runBm25(graph, searchText, limit) {
139
+ const hits = await bm25Search(graph, { text: searchText, limit });
140
+ return hits.map((h) => ({
141
+ nodeId: h.nodeId,
142
+ name: h.name,
143
+ kind: h.kind,
144
+ filePath: h.filePath,
145
+ score: h.score,
146
+ sources: ["bm25"],
147
+ }));
148
+ }
149
+ /**
150
+ * Hybrid ranking returns `FusedHit`s which carry only `{ nodeId, score,
151
+ * sources }` — the CLI needs name/kind/filePath for each hit too. Re-read
152
+ * them from the `nodes` table in one round trip. Missing ids (stale
153
+ * embeddings) are silently dropped. Input order is preserved.
154
+ */
155
+ async function hydrateFused(graph, fused, limit) {
156
+ if (fused.length === 0)
157
+ return [];
158
+ const capped = fused.slice(0, limit);
159
+ const ids = Array.from(new Set(capped.map((f) => f.nodeId)));
160
+ const meta = new Map();
161
+ try {
162
+ // Typed-finder hydration replaces the legacy `SELECT id, name, kind,
163
+ // file_path FROM nodes WHERE id IN (...)`. `listNodes({ids})`
164
+ // already returns the rehydrated `GraphNode` shape with name + kind
165
+ // + filePath populated.
166
+ const nodes = await graph.listNodes({ ids });
167
+ for (const n of nodes) {
168
+ meta.set(n.id, { name: n.name, kind: n.kind, filePath: n.filePath });
169
+ }
170
+ }
171
+ catch {
172
+ // Any metadata-hydration failure collapses to "hit with blank fields"
173
+ // rather than aborting the whole query — we still have valid nodeIds
174
+ // + scores + sources. The agent can call `context` on the nodeId to
175
+ // recover the details.
176
+ }
177
+ const out = [];
178
+ for (const f of capped) {
179
+ const m = meta.get(f.nodeId);
180
+ if (m === undefined)
181
+ continue;
182
+ out.push({
183
+ nodeId: f.nodeId,
184
+ name: m.name,
185
+ kind: m.kind,
186
+ filePath: m.filePath,
187
+ score: f.score,
188
+ sources: f.sources,
189
+ });
190
+ }
191
+ return out;
192
+ }
193
+ /**
194
+ * Fetch `symbol_summaries` rows for every hit nodeId in a single query.
195
+ * Collapses multiple prompt-version rows per node by keeping the last
196
+ * row in the storage layer's documented `(node_id ASC, prompt_version
197
+ * ASC, content_hash ASC)` order, which deterministically selects the
198
+ * newest prompt version. Returns an empty map on any failure so a
199
+ * missing `symbol_summaries` table never blocks a query. Test fakes
200
+ * without `lookupSymbolSummariesByNode` get an empty join transparently.
201
+ */
202
+ async function joinSummaries(store, nodeIds) {
203
+ const out = new Map();
204
+ if (nodeIds.length === 0)
205
+ return out;
206
+ // Test fakes that omit a real temporal view (or set it to a partial
207
+ // shape) get an empty join transparently — `lookupSymbolSummariesByNode`
208
+ // is required on `ITemporalStore` but we still duck-check at runtime so
209
+ // a hand-rolled mock without the method doesn't blow up.
210
+ const temporal = store.temporal;
211
+ if (typeof temporal.lookupSymbolSummariesByNode !== "function")
212
+ return out;
213
+ const uniqIds = Array.from(new Set(nodeIds));
214
+ try {
215
+ const rows = await temporal.lookupSymbolSummariesByNode.call(store.temporal, uniqIds);
216
+ for (const row of rows) {
217
+ // Overwriting per node id keeps the newest prompt version because of
218
+ // the storage layer's ORDER BY contract on `lookupSymbolSummariesByNode`.
219
+ out.set(row.nodeId, row);
220
+ }
221
+ }
222
+ catch {
223
+ // Degrade silently — summaries are enrichment, not load-bearing.
224
+ }
225
+ return out;
226
+ }
227
+ /**
228
+ * Join `context — goal — text` with whitespace-safe em-dash separators.
229
+ * Missing / blank parts are dropped so the ranker never sees a dangling
230
+ * separator.
231
+ */
232
+ function buildSearchText(text, context, goal) {
233
+ const parts = [];
234
+ if (context !== undefined && context.trim() !== "")
235
+ parts.push(context.trim());
236
+ if (goal !== undefined && goal.trim() !== "")
237
+ parts.push(goal.trim());
238
+ parts.push(text);
239
+ return parts.join(" — ");
240
+ }
241
+ /**
242
+ * Read the symbol body from disk. The CLI `QueryRow` doesn't carry
243
+ * startLine / endLine, so on the CLI path we return the first
244
+ * {@link INCLUDE_CONTENT_CHAR_CAP} characters of the whole file — the MCP
245
+ * tool has access to the richer node metadata and can slice more tightly.
246
+ * Any read error returns `null`.
247
+ */
248
+ async function readSymbolContent(repoPath, r) {
249
+ const abs = isAbsolute(r.filePath) ? r.filePath : resolve(repoPath, r.filePath);
250
+ let source;
251
+ try {
252
+ source = await readFile(abs, "utf8");
253
+ }
254
+ catch {
255
+ return null;
256
+ }
257
+ if (source.length <= INCLUDE_CONTENT_CHAR_CAP)
258
+ return source;
259
+ return `${source.slice(0, INCLUDE_CONTENT_CHAR_CAP - 1)}…`;
260
+ }
261
+ function printResults(results, text, repoPath, mode) {
262
+ const label = mode === "hybrid" ? "hybrid" : "BM25";
263
+ console.warn(`query: "${text}" in ${repoPath} (${results.length} ${label} results)`);
264
+ if (results.length === 0)
265
+ return;
266
+ // Only render the SUMMARY column when at least one hit carries one —
267
+ // skip the extra whitespace on indexes that haven't run the summarize
268
+ // phase yet. SOURCES stays on every row so agents can tell which ranker
269
+ // contributed to each hit.
270
+ const anySummary = results.some((r) => typeof r.summary === "string" && r.summary.length > 0);
271
+ const header = anySummary
272
+ ? ["SCORE", "KIND", "NAME", "FILE", "SOURCES", "SUMMARY"]
273
+ : ["SCORE", "KIND", "NAME", "FILE", "SOURCES"];
274
+ const rows = results.map((r) => {
275
+ const base = [r.score.toFixed(3), r.kind, r.name, r.filePath, r.sources.join("+")];
276
+ if (!anySummary)
277
+ return base;
278
+ return [...base, truncateSummary(r.summary)];
279
+ });
280
+ const widths = header.map((h, i) => Math.max(h.length, ...rows.map((row) => (row[i] ?? "").length)));
281
+ const line = (cols) => cols.map((c, i) => c.padEnd(widths[i] ?? 0)).join(" ");
282
+ console.log(line(header));
283
+ for (const row of rows)
284
+ console.log(line(row));
285
+ // When --content was passed, append each symbol body below the table so
286
+ // agents piping the output can grep/read it without JSON parsing.
287
+ for (const r of results) {
288
+ if (r.content === undefined)
289
+ continue;
290
+ console.log("");
291
+ console.log(`# ${r.name} [${r.kind}] — ${r.filePath}`);
292
+ console.log(r.content);
293
+ }
294
+ }
295
+ /**
296
+ * Render a summary string to fit the single-line SUMMARY column. Newlines
297
+ * collapse to spaces so the column width survives; anything past the cap
298
+ * is trimmed and closed with an ellipsis. Absent summaries render as an
299
+ * empty string so the column aligns.
300
+ */
301
+ function truncateSummary(summary) {
302
+ if (summary === undefined || summary.length === 0)
303
+ return "";
304
+ const flattened = summary.replace(/\s+/g, " ").trim();
305
+ if (flattened.length <= SUMMARY_COLUMN_CHAR_CAP)
306
+ return flattened;
307
+ return `${flattened.slice(0, SUMMARY_COLUMN_CHAR_CAP - 1)}…`;
308
+ }
309
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `codehub scan [path]` — run P1 + profile-gated P2 scanners, emit merged
3
+ * SARIF, ingest findings into the graph.
4
+ *
5
+ * Flow:
6
+ * 1. Resolve the target repo (optionally from the registry).
7
+ * 2. Read the ProjectProfile row (if any) so we can gate scanners by
8
+ * detected languages / iacTypes / apiContracts.
9
+ * 3. Filter the catalog by --scanners, --with, and the profile:
10
+ * - P1 scanners are gated by language overlap (polyglot always in).
11
+ * - P2 scanners (trivy, checkov, hadolint, tflint, spectral) are
12
+ * opt-in via ProjectProfile fields. `--with trivy,checkov`
13
+ * force-adds them regardless of profile.
14
+ * 4. Build per-scanner context (checkov frameworks, hadolint Dockerfile
15
+ * list, spectral contract files, pip-audit requirements path) from
16
+ * the profile + filesystem probe.
17
+ * 5. Run the selected scanners in parallel via `runScanners`.
18
+ * 6. Merge SARIF and write to `.codehub/scan.sarif`.
19
+ * 7. Ingest findings into the graph via `runIngestSarif`.
20
+ * 8. Exit with 0 when no HIGH+CRIT findings, 1 when any, 2 when a
21
+ * scanner returned non-zero or crashed.
22
+ *
23
+ * The --severity filter gates the exit code only; every finding is
24
+ * still written to SARIF and the graph.
25
+ */
26
+ import { P1_SPECS, type ProjectProfileGate, type ScannerSpec } from "@opencodehub/scanners";
27
+ export interface ScanOptions {
28
+ /** Explicit scanner ids (--scanners=semgrep,osv). Overrides profile gating. */
29
+ readonly scanners?: readonly string[];
30
+ /** Additional scanner ids to include on top of defaults. */
31
+ readonly withScanners?: readonly string[];
32
+ /** Override output path. Defaults to `<repo>/.codehub/scan.sarif`. */
33
+ readonly output?: string;
34
+ /** Severity filter for the exit-code gate (default: HIGH,CRITICAL). */
35
+ readonly severity?: readonly string[];
36
+ /**
37
+ * Path to a baseline SARIF log. When supplied, every result in the
38
+ * scan output is tagged via `applyBaselineState` and findings with
39
+ * `baselineState === "unchanged"` are excluded from the severity-gate
40
+ * exit code — they are not new relative to the baseline.
41
+ */
42
+ readonly baseline?: string;
43
+ /** Override the registry home (tests). */
44
+ readonly home?: string;
45
+ /** `--repo <name>`: look up a registered repo instead of using `path`. */
46
+ readonly repo?: string;
47
+ /** Concurrency override. */
48
+ readonly concurrency?: number;
49
+ /** Per-scanner timeout override in ms. */
50
+ readonly timeoutMs?: number;
51
+ }
52
+ export interface ScanSummary {
53
+ readonly repoPath: string;
54
+ readonly outputPath: string;
55
+ readonly runs: readonly {
56
+ readonly scanner: string;
57
+ readonly findings: number;
58
+ readonly skipped?: string;
59
+ }[];
60
+ readonly totalFindings: number;
61
+ readonly bySeverity: Record<string, number>;
62
+ /** 0 = clean, 1 = findings above severity threshold, 2 = scanner error. */
63
+ readonly exitCode: 0 | 1 | 2;
64
+ }
65
+ export declare function runScan(path: string, opts?: ScanOptions): Promise<ScanSummary>;
66
+ /**
67
+ * Read the ProjectProfile node (if present) so we can gate scanners by
68
+ * detected languages, IaC types, and API contracts. If the graph is
69
+ * absent, every field is returned empty — falls back to the polyglot P1
70
+ * subset.
71
+ */
72
+ export declare function readProjectProfile(repoPath: string): Promise<ProjectProfileGate>;
73
+ /**
74
+ * Exported for tests: apply --scanners / --with / profile gating to
75
+ * produce the final scanner list.
76
+ */
77
+ export declare function selectScanners(profile: ProjectProfileGate, opts: ScanOptions): readonly ScannerSpec[];
78
+ /** Helper exposed for tests: parse a SARIF file from disk. */
79
+ export declare function readSarifFile(path: string): Promise<unknown>;
80
+ export { P1_SPECS };
81
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH,OAAO,EAOL,QAAQ,EAER,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAGjB,MAAM,uBAAuB,CAAC;AAK/B,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,EAAE,CAAC;IACJ,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC9B;AAID,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAoIxF;AA+BD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0BtF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,WAAW,GAChB,SAAS,WAAW,EAAE,CAuBxB;AAoID,8DAA8D;AAC9D,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGlE;AAyBD,OAAO,EAAE,QAAQ,EAAE,CAAC"}