@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,472 @@
1
+ /**
2
+ * `codehub doctor` — environment probe.
3
+ *
4
+ * Runs every `Check` registered in `CHECKS`, prints a table, and exits
5
+ * with a status code that maps to the worst result seen:
6
+ *
7
+ * 0 all ok
8
+ * 1 at least one warn (non-blocking)
9
+ * 2 at least one fail (blocking — something is broken)
10
+ *
11
+ * Checks are strictly diagnostic — they never auto-heal. Each one returns
12
+ * a `hint` string the user can copy-paste when things are off.
13
+ */
14
+ import { spawn } from "node:child_process";
15
+ import { access, readFile, stat } from "node:fs/promises";
16
+ import { createRequire } from "node:module";
17
+ import { homedir } from "node:os";
18
+ import { dirname, join, resolve } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ import Table from "cli-table3";
21
+ /**
22
+ * Entry point invoked by `codehub doctor`. Prints the table and sets
23
+ * `process.exitCode`. Also returns the structured report so tests and
24
+ * acceptance scripts can assert the outcome without parsing stdout.
25
+ */
26
+ export async function runDoctor(opts = {}) {
27
+ const checks = buildChecks(opts);
28
+ const rows = [];
29
+ for (const check of checks) {
30
+ const result = await check.run().catch((err) => ({
31
+ status: "fail",
32
+ message: `check threw: ${err instanceof Error ? err.message : String(err)}`,
33
+ hint: "report a bug against @opencodehub/cli — checks should never throw",
34
+ }));
35
+ rows.push({ name: check.name, result });
36
+ }
37
+ printTable(rows);
38
+ let exitCode = 0;
39
+ for (const { result } of rows) {
40
+ if (result.status === "fail") {
41
+ exitCode = 2;
42
+ break;
43
+ }
44
+ if (result.status === "warn" && exitCode === 0) {
45
+ exitCode = 1;
46
+ }
47
+ }
48
+ process.exitCode = exitCode;
49
+ return { rows, exitCode };
50
+ }
51
+ /**
52
+ * Ordered list of environment probes. Order matters only for the output
53
+ * table — runs are independent, but we surface foundational checks first
54
+ * (Node, pnpm, native bindings) so the user can fix a broken foundation
55
+ * before worrying about downstream extras.
56
+ */
57
+ export function buildChecks(opts = {}) {
58
+ const home = opts.home ?? homedir();
59
+ const repoRoot = opts.repoRoot ?? guessRepoRoot();
60
+ const list = [nodeVersionCheck(), pnpmInstalledCheck()];
61
+ if (opts.skipNative !== true) {
62
+ list.push(treeSitterNativeCheck(repoRoot));
63
+ list.push(duckdbWorksCheck(repoRoot));
64
+ list.push(lbugWorksCheck(repoRoot));
65
+ }
66
+ list.push(binaryOnPathCheck("semgrep", "P1 scanner — install semgrep via `brew install semgrep` or `uv tool install semgrep`"));
67
+ list.push(binaryOnPathCheck("osv-scanner", "P1 scanner — install from https://github.com/google/osv-scanner"));
68
+ list.push(binaryOnPathCheck("bandit", "P1 scanner — install with `uv tool install 'bandit[sarif]'`"));
69
+ list.push(binaryOnPathCheck("ruff", "P1 scanner — install with `uv tool install ruff`"));
70
+ list.push(binaryOnPathCheck("grype", "P1 scanner — install with `brew install anchore/grype/grype` or from https://github.com/anchore/grype"));
71
+ list.push(binaryOnPathCheck("vulture", "P1 scanner — install with `uv tool install vulture`"));
72
+ list.push(binaryOnPathCheck("pip-audit", "P1 scanner — install with `uv tool install pip-audit`"));
73
+ list.push(binaryOnPathCheck("radon", "P2 scanner — install with `uv tool install radon`"));
74
+ list.push(binaryOnPathCheck("ty", "P2 scanner (beta) — install with `uv tool install ty` (Astral)"));
75
+ list.push(embedderWeightsCheck(home));
76
+ list.push(registryPathCheck(home));
77
+ list.push(sarifSchemaCheck(repoRoot));
78
+ return list;
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Individual checks
82
+ // ---------------------------------------------------------------------------
83
+ function nodeVersionCheck() {
84
+ return {
85
+ name: "node >= 20",
86
+ async run() {
87
+ const v = process.versions.node;
88
+ const major = Number.parseInt(v.split(".")[0] ?? "0", 10);
89
+ if (!Number.isFinite(major) || major < 20) {
90
+ return {
91
+ status: "fail",
92
+ message: `node v${v} — minimum is v20.10`,
93
+ hint: "install node 20+ with `mise use node@20` or `nvm install 20`",
94
+ };
95
+ }
96
+ return { status: "ok", message: `node v${v}` };
97
+ },
98
+ };
99
+ }
100
+ function pnpmInstalledCheck() {
101
+ return {
102
+ name: "pnpm installed",
103
+ async run() {
104
+ const res = await runCommand("pnpm", ["--version"]);
105
+ if (res.status !== 0) {
106
+ return {
107
+ status: "fail",
108
+ message: "pnpm not on PATH",
109
+ hint: "install pnpm via `npm install -g pnpm` or `corepack enable`",
110
+ };
111
+ }
112
+ return { status: "ok", message: `pnpm ${res.stdout.trim()}` };
113
+ },
114
+ };
115
+ }
116
+ function treeSitterNativeCheck(repoRoot) {
117
+ return {
118
+ name: "tree-sitter native binding",
119
+ async run() {
120
+ try {
121
+ // tree-sitter ships a native .node binding. Loading the grammar
122
+ // for TS is the cheapest health signal.
123
+ const tsPath = resolveFromRoot(repoRoot, "tree-sitter");
124
+ const tssPath = resolveFromRoot(repoRoot, "tree-sitter-typescript");
125
+ if (!tsPath || !tssPath) {
126
+ return {
127
+ status: "warn",
128
+ message: "tree-sitter or tree-sitter-typescript not installed",
129
+ hint: "run `pnpm install` at the repo root",
130
+ };
131
+ }
132
+ const Parser = (await import(tsPath));
133
+ const tsMod = (await import(tssPath));
134
+ const ParserCtor = Parser.default ?? Parser;
135
+ const parser = new ParserCtor();
136
+ const language = tsMod.typescript ?? tsMod.default?.typescript;
137
+ if (!language) {
138
+ return {
139
+ status: "fail",
140
+ message: "tree-sitter-typescript has no `typescript` export",
141
+ hint: "re-run `pnpm install` to rebuild native grammars",
142
+ };
143
+ }
144
+ parser.setLanguage(language);
145
+ return { status: "ok", message: "tree-sitter + typescript grammar load OK" };
146
+ }
147
+ catch (err) {
148
+ return {
149
+ status: "fail",
150
+ message: `failed to load tree-sitter: ${err instanceof Error ? err.message : String(err)}`,
151
+ hint: "re-run `pnpm install` to rebuild native bindings (requires clang/g++)",
152
+ };
153
+ }
154
+ },
155
+ };
156
+ }
157
+ function duckdbWorksCheck(repoRoot) {
158
+ return {
159
+ name: "duckdb native binding",
160
+ async run() {
161
+ try {
162
+ const duckPath = resolveFromRoot(repoRoot, "@duckdb/node-api");
163
+ if (!duckPath) {
164
+ return {
165
+ status: "warn",
166
+ message: "@duckdb/node-api not installed",
167
+ hint: "run `pnpm install` at the repo root",
168
+ };
169
+ }
170
+ // The @duckdb/node-api 1.x surface exposes Sync teardown helpers
171
+ // (`disconnectSync`, `closeSync`). The async `.close()` accessors
172
+ // were dropped in 1.0.0; depending on them produced a false FAIL.
173
+ const mod = (await import(duckPath));
174
+ // In-memory instance: never touches disk, never lingers.
175
+ const inst = await mod.DuckDBInstance.create(":memory:");
176
+ const conn = await inst.connect();
177
+ if (typeof conn.disconnectSync === "function")
178
+ conn.disconnectSync();
179
+ else if (typeof conn.close === "function")
180
+ await conn.close();
181
+ if (typeof inst.closeSync === "function")
182
+ inst.closeSync();
183
+ else if (typeof inst.close === "function")
184
+ await inst.close();
185
+ return { status: "ok", message: "duckdb open/close OK" };
186
+ }
187
+ catch (err) {
188
+ return {
189
+ status: "fail",
190
+ message: `duckdb failed to open: ${err instanceof Error ? err.message : String(err)}`,
191
+ hint: "check platform support — pnpm only prebuilds linux-x64/arm64, darwin-arm64/x64, win32-x64",
192
+ };
193
+ }
194
+ },
195
+ };
196
+ }
197
+ /**
198
+ * Mirror of {@link duckdbWorksCheck} for the optional `@ladybugdb/core`
199
+ * graph-db backend. Emits `warn` (not `fail`) when the package is
200
+ * uninstalled because `@ladybugdb/core` is opt-in: a default `duck`
201
+ * deployment never needs it. When the package IS installed and the
202
+ * smoke test fails we surface `fail` so a broken native binding can be
203
+ * triaged the same way duckdb's is.
204
+ */
205
+ function lbugWorksCheck(repoRoot) {
206
+ return {
207
+ name: "graph-db native binding",
208
+ async run() {
209
+ try {
210
+ const lbugPath = resolveFromRoot(repoRoot, "@ladybugdb/core");
211
+ if (!lbugPath) {
212
+ return {
213
+ status: "warn",
214
+ message: "@ladybugdb/core not installed (optional graph-db backend)",
215
+ hint: "run `pnpm install` and set `CODEHUB_STORE=lbug` to opt in; otherwise ignore",
216
+ };
217
+ }
218
+ // The opt-in graph-db backend uses `@ladybugdb/core`'s `Database`
219
+ // entry. We exercise the load-and-close cycle the same way the
220
+ // duckdb check does — anything heavier would couple this probe to
221
+ // the adapter's evolving smoke-test surface.
222
+ const mod = (await import(lbugPath));
223
+ const ctorRaw = mod["Database"] ?? mod["default"]?.["Database"];
224
+ if (typeof ctorRaw !== "function") {
225
+ return {
226
+ status: "fail",
227
+ message: "@ladybugdb/core is installed but exports no Database constructor",
228
+ hint: "re-run `pnpm install` to refresh the graph-db backend bindings",
229
+ };
230
+ }
231
+ return { status: "ok", message: "@ladybugdb/core load OK" };
232
+ }
233
+ catch (err) {
234
+ return {
235
+ status: "fail",
236
+ message: `@ladybugdb/core failed to load: ${err instanceof Error ? err.message : String(err)}`,
237
+ hint: "the graph-db backend is opt-in; unset `CODEHUB_STORE=lbug` or reinstall the binding",
238
+ };
239
+ }
240
+ },
241
+ };
242
+ }
243
+ function binaryOnPathCheck(bin, hint) {
244
+ return {
245
+ name: `${bin} binary`,
246
+ async run() {
247
+ const res = await runCommand(bin, ["--version"]);
248
+ if (res.status !== 0) {
249
+ return {
250
+ status: "warn",
251
+ message: `${bin} not on PATH`,
252
+ hint,
253
+ };
254
+ }
255
+ return { status: "ok", message: `${bin}: ${firstLine(res.stdout)}` };
256
+ },
257
+ };
258
+ }
259
+ function embedderWeightsCheck(home) {
260
+ return {
261
+ name: "embedder weights",
262
+ async run() {
263
+ // Filename convention matches `embedder/src/paths.ts:modelFileName` —
264
+ // fp32 uses `model.onnx`, int8 uses `model_int8.onnx` (underscore,
265
+ // NOT hyphen). A historical hyphenated path name lingered here and
266
+ // caused false-negative `warn`s for users who had int8 weights on
267
+ // disk.
268
+ const base = join(home, ".codehub", "models", "gte-modernbert-base");
269
+ const fp32 = join(base, "fp32", "model.onnx");
270
+ const int8 = join(base, "int8", "model_int8.onnx");
271
+ const fp32Ok = await fileExists(fp32);
272
+ const int8Ok = await fileExists(int8);
273
+ if (!fp32Ok && !int8Ok) {
274
+ return {
275
+ status: "warn",
276
+ message: "no embedder weights found",
277
+ hint: "run `codehub setup --embeddings` (fp32) or `codehub setup --embeddings --int8`",
278
+ };
279
+ }
280
+ const variant = fp32Ok ? "fp32" : "int8";
281
+ return { status: "ok", message: `embedder weights present (${variant})` };
282
+ },
283
+ };
284
+ }
285
+ function registryPathCheck(home) {
286
+ return {
287
+ name: "registry path",
288
+ async run() {
289
+ const regPath = join(home, ".codehub", "registry.json");
290
+ // Single attempt: branch on `ENOENT` for the missing-file case so
291
+ // the existence check and the read share one syscall — closes the
292
+ // TOCTOU gap flagged by js/file-system-race.
293
+ let raw;
294
+ try {
295
+ raw = await readFile(regPath, "utf8");
296
+ }
297
+ catch (err) {
298
+ if (err.code === "ENOENT") {
299
+ return {
300
+ status: "warn",
301
+ message: `~/.codehub/registry.json missing`,
302
+ hint: "run `codehub analyze` in any git repo to create the registry",
303
+ };
304
+ }
305
+ return {
306
+ status: "fail",
307
+ message: `registry read failed: ${err instanceof Error ? err.message : String(err)}`,
308
+ hint: "delete ~/.codehub/registry.json and re-run `codehub analyze`",
309
+ };
310
+ }
311
+ try {
312
+ const parsed = JSON.parse(raw);
313
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
314
+ return {
315
+ status: "fail",
316
+ message: "registry.json is not an object",
317
+ hint: "back up the file and run `codehub analyze` again",
318
+ };
319
+ }
320
+ const count = Object.keys(parsed).length;
321
+ return {
322
+ status: "ok",
323
+ message: `registry readable (${count} repo${count === 1 ? "" : "s"})`,
324
+ };
325
+ }
326
+ catch (err) {
327
+ return {
328
+ status: "fail",
329
+ message: `registry parse failed: ${err instanceof Error ? err.message : String(err)}`,
330
+ hint: "delete ~/.codehub/registry.json and re-run `codehub analyze`",
331
+ };
332
+ }
333
+ },
334
+ };
335
+ }
336
+ function sarifSchemaCheck(repoRoot) {
337
+ return {
338
+ name: "@opencodehub/sarif build",
339
+ async run() {
340
+ const pkgDir = join(repoRoot, "packages", "sarif", "dist");
341
+ try {
342
+ const s = await stat(pkgDir);
343
+ if (!s.isDirectory()) {
344
+ return {
345
+ status: "fail",
346
+ message: "@opencodehub/sarif dist is not a directory",
347
+ hint: "run `pnpm -r build`",
348
+ };
349
+ }
350
+ return { status: "ok", message: "@opencodehub/sarif built" };
351
+ }
352
+ catch {
353
+ return {
354
+ status: "warn",
355
+ message: "@opencodehub/sarif not built yet",
356
+ hint: "run `pnpm -r build`",
357
+ };
358
+ }
359
+ },
360
+ };
361
+ }
362
+ // ---------------------------------------------------------------------------
363
+ // Helpers
364
+ // ---------------------------------------------------------------------------
365
+ function printTable(rows) {
366
+ const table = new Table({
367
+ head: ["CHECK", "STATUS", "DETAIL", "HINT"],
368
+ style: { head: [], border: [] },
369
+ colWidths: [32, 8, 48, 48],
370
+ wordWrap: true,
371
+ });
372
+ for (const { name, result } of rows) {
373
+ const glyph = result.status === "ok" ? "OK" : result.status === "warn" ? "WARN" : "FAIL";
374
+ table.push([name, glyph, result.message, result.hint ?? ""]);
375
+ }
376
+ console.log(table.toString());
377
+ }
378
+ async function runCommand(cmd, args) {
379
+ return await new Promise((resolveProm) => {
380
+ const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
381
+ let stdout = "";
382
+ let stderr = "";
383
+ child.stdout.on("data", (d) => {
384
+ stdout += d.toString("utf8");
385
+ });
386
+ child.stderr.on("data", (d) => {
387
+ stderr += d.toString("utf8");
388
+ });
389
+ child.on("error", () => {
390
+ resolveProm({ status: 127, stdout, stderr });
391
+ });
392
+ child.on("close", (code) => {
393
+ resolveProm({ status: code ?? 0, stdout, stderr });
394
+ });
395
+ });
396
+ }
397
+ async function fileExists(path) {
398
+ try {
399
+ await access(path);
400
+ return true;
401
+ }
402
+ catch {
403
+ return false;
404
+ }
405
+ }
406
+ function firstLine(s) {
407
+ const idx = s.indexOf("\n");
408
+ return idx < 0 ? s.trim() : s.slice(0, idx).trim();
409
+ }
410
+ function guessRepoRoot() {
411
+ // `codehub` ships from packages/cli/dist/commands — walking four levels
412
+ // up lands at the monorepo root where `packages/` and `node_modules/`
413
+ // live.
414
+ const here = dirname(fileURLToPath(import.meta.url));
415
+ return resolve(here, "..", "..", "..", "..");
416
+ }
417
+ /**
418
+ * Resolve an npm package for the native-binding checks. We try two
419
+ * environments in order, so doctor returns `OK` in every realistic install
420
+ * shape — monorepo checkout, global `pnpm i -g`, tarball, symlinked bin:
421
+ *
422
+ * 1. The CLI package's own `node_modules` — `createRequire` off
423
+ * `import.meta.url` walks outward through the same resolution chain
424
+ * Node would use for a real `await import(pkg)` from inside the CLI,
425
+ * so this is the authoritative answer for "can the CLI load this?".
426
+ * 2. The supplied `repoRoot` — legacy fallback for the in-monorepo case
427
+ * where the CLI is running from `packages/cli/dist/` and dependencies
428
+ * hoist to the workspace root.
429
+ *
430
+ * We stop at the first hit. Returning `null` preserves the existing
431
+ * semantics of the caller (`warn` result with a reinstall hint).
432
+ */
433
+ function resolveFromRoot(repoRoot, pkg) {
434
+ // 1. CLI's own resolution context — the canonical answer.
435
+ try {
436
+ const req = createRequire(import.meta.url);
437
+ return req.resolve(pkg);
438
+ }
439
+ catch {
440
+ // fall through to repoRoot
441
+ }
442
+ // 2. Workspace/monorepo root fallback.
443
+ try {
444
+ const req = createRequire(join(repoRoot, "package.json"));
445
+ return req.resolve(pkg);
446
+ }
447
+ catch {
448
+ // fall through to per-package fallbacks
449
+ }
450
+ // 3. Per-workspace fallback. Under pnpm strict isolation, native bindings
451
+ // are direct deps of the package that uses them — `tree-sitter*` lives
452
+ // in `packages/ingestion`, `@duckdb/node-api` in `packages/storage`.
453
+ // Probing those package.json contexts lets `codehub doctor` resolve
454
+ // the bindings even when neither the CLI nor the workspace root
455
+ // declare them as direct deps.
456
+ const owners = pkg.startsWith("@duckdb/") || pkg.startsWith("@ladybugdb/")
457
+ ? ["packages/storage"]
458
+ : pkg.startsWith("tree-sitter")
459
+ ? ["packages/ingestion"]
460
+ : [];
461
+ for (const owner of owners) {
462
+ try {
463
+ const req = createRequire(join(repoRoot, owner, "package.json"));
464
+ return req.resolve(pkg);
465
+ }
466
+ catch {
467
+ // try next
468
+ }
469
+ }
470
+ return null;
471
+ }
472
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `findEnclosingSymbolId` — deterministic tightest-span lookup that maps a
3
+ * `(filePath, line)` pair back to the OpenCodeHub graph node that owns the
4
+ * line (a Function / Method / Class / etc.). Used by `ingest-sarif` to link
5
+ * SARIF `Finding` nodes to the enclosing code symbol when the scanner did
6
+ * not populate `result.properties["opencodehub.symbolId"]` itself.
7
+ *
8
+ * This is a clone of the algorithm in
9
+ * `packages/ingestion/src/pipeline/phases/scip-index.ts:indexNodesByFile` +
10
+ * `findEnclosingNodeId`. The two call sites live in different packages
11
+ * (`@opencodehub/cli` vs `@opencodehub/ingestion`), and extracting a shared
12
+ * helper would require a cross-package refactor that is explicitly out of
13
+ * scope for the SARIF linkage task. If these functions need to converge
14
+ * later, promote this file to a shared util package (e.g.
15
+ * `@opencodehub/graph-utils`) and delete the duplicate in scip-index.ts in
16
+ * a single atomic change.
17
+ *
18
+ * Notes on 1-indexing: both SARIF 2.1.0 `region.startLine` and
19
+ * OpenCodeHub node `startLine`/`endLine` are 1-based, so no offset
20
+ * adjustment is needed at the call site.
21
+ */
22
+ import type { NodeId, NodeKind } from "@opencodehub/core-types";
23
+ /** A graph node projection carrying only the fields the lookup needs. */
24
+ export interface NodeRow {
25
+ readonly id: NodeId;
26
+ readonly filePath: string;
27
+ readonly startLine: number;
28
+ readonly endLine: number;
29
+ readonly kind: NodeKind;
30
+ }
31
+ /** Per-file, start-line-ascending index used by `findEnclosingSymbolId`. */
32
+ export type NodesByFile = ReadonlyMap<string, readonly NodeRow[]>;
33
+ /**
34
+ * Code-kind allow set used when resolving SARIF findings back to an
35
+ * enclosing symbol. Covers Function, Method, Constructor, Class,
36
+ * Interface, Struct, Enum, and Trait — a strict superset of
37
+ * `SCIP_SYMBOL_KINDS`; we additionally allow `Constructor` here because
38
+ * SARIF tooling routinely emits findings inside constructor bodies.
39
+ */
40
+ export declare const ENCLOSING_SYMBOL_KINDS: ReadonlySet<NodeKind>;
41
+ /**
42
+ * Build a per-file, start-line-ascending index over the supplied node
43
+ * rows, filtering to nodes whose `kind` is in `ENCLOSING_SYMBOL_KINDS`.
44
+ * Rows missing either `startLine` or `endLine` are skipped silently —
45
+ * they cannot participate in a range containment check.
46
+ *
47
+ * Ordering: within each file the array is sorted by `startLine` ascending
48
+ * with `endLine` ascending as the tie-breaker. `findEnclosingSymbolId`
49
+ * still scans the whole candidate list for the tightest span, so the
50
+ * sort is primarily an early-break optimization (once `startLine > line`
51
+ * we can stop).
52
+ */
53
+ export declare function indexNodesByFile(rows: readonly NodeRow[]): NodesByFile;
54
+ /**
55
+ * Return the id of the tightest-span node in `nodesByFile[filePath]`
56
+ * that encloses `line` (`startLine <= line <= endLine`). "Tightest"
57
+ * means smallest `endLine - startLine` span — this makes nested methods
58
+ * win over their containing classes. When two candidates have the same
59
+ * span, the earlier `startLine` wins (which falls out of the deterministic
60
+ * input sort).
61
+ *
62
+ * Returns `undefined` when the file is unknown, when no candidate
63
+ * contains the line, or when every candidate has been filtered out by
64
+ * the allow-set at index time.
65
+ */
66
+ export declare function findEnclosingSymbolId(nodesByFile: NodesByFile, filePath: string, line: number): NodeId | undefined;
67
+ //# sourceMappingURL=find-enclosing-symbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-enclosing-symbol.d.ts","sourceRoot":"","sources":["../../src/commands/find-enclosing-symbol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEhE,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,QAAQ,CASvD,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAgBtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAiBpB"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * `findEnclosingSymbolId` — deterministic tightest-span lookup that maps a
3
+ * `(filePath, line)` pair back to the OpenCodeHub graph node that owns the
4
+ * line (a Function / Method / Class / etc.). Used by `ingest-sarif` to link
5
+ * SARIF `Finding` nodes to the enclosing code symbol when the scanner did
6
+ * not populate `result.properties["opencodehub.symbolId"]` itself.
7
+ *
8
+ * This is a clone of the algorithm in
9
+ * `packages/ingestion/src/pipeline/phases/scip-index.ts:indexNodesByFile` +
10
+ * `findEnclosingNodeId`. The two call sites live in different packages
11
+ * (`@opencodehub/cli` vs `@opencodehub/ingestion`), and extracting a shared
12
+ * helper would require a cross-package refactor that is explicitly out of
13
+ * scope for the SARIF linkage task. If these functions need to converge
14
+ * later, promote this file to a shared util package (e.g.
15
+ * `@opencodehub/graph-utils`) and delete the duplicate in scip-index.ts in
16
+ * a single atomic change.
17
+ *
18
+ * Notes on 1-indexing: both SARIF 2.1.0 `region.startLine` and
19
+ * OpenCodeHub node `startLine`/`endLine` are 1-based, so no offset
20
+ * adjustment is needed at the call site.
21
+ */
22
+ /**
23
+ * Code-kind allow set used when resolving SARIF findings back to an
24
+ * enclosing symbol. Covers Function, Method, Constructor, Class,
25
+ * Interface, Struct, Enum, and Trait — a strict superset of
26
+ * `SCIP_SYMBOL_KINDS`; we additionally allow `Constructor` here because
27
+ * SARIF tooling routinely emits findings inside constructor bodies.
28
+ */
29
+ export const ENCLOSING_SYMBOL_KINDS = new Set([
30
+ "Function",
31
+ "Method",
32
+ "Constructor",
33
+ "Class",
34
+ "Interface",
35
+ "Struct",
36
+ "Enum",
37
+ "Trait",
38
+ ]);
39
+ /**
40
+ * Build a per-file, start-line-ascending index over the supplied node
41
+ * rows, filtering to nodes whose `kind` is in `ENCLOSING_SYMBOL_KINDS`.
42
+ * Rows missing either `startLine` or `endLine` are skipped silently —
43
+ * they cannot participate in a range containment check.
44
+ *
45
+ * Ordering: within each file the array is sorted by `startLine` ascending
46
+ * with `endLine` ascending as the tie-breaker. `findEnclosingSymbolId`
47
+ * still scans the whole candidate list for the tightest span, so the
48
+ * sort is primarily an early-break optimization (once `startLine > line`
49
+ * we can stop).
50
+ */
51
+ export function indexNodesByFile(rows) {
52
+ const map = new Map();
53
+ for (const row of rows) {
54
+ if (!ENCLOSING_SYMBOL_KINDS.has(row.kind))
55
+ continue;
56
+ if (!Number.isFinite(row.startLine) || !Number.isFinite(row.endLine))
57
+ continue;
58
+ const bucket = map.get(row.filePath);
59
+ if (bucket === undefined)
60
+ map.set(row.filePath, [row]);
61
+ else
62
+ bucket.push(row);
63
+ }
64
+ for (const arr of map.values()) {
65
+ arr.sort((a, b) => {
66
+ if (a.startLine !== b.startLine)
67
+ return a.startLine - b.startLine;
68
+ return a.endLine - b.endLine;
69
+ });
70
+ }
71
+ return map;
72
+ }
73
+ /**
74
+ * Return the id of the tightest-span node in `nodesByFile[filePath]`
75
+ * that encloses `line` (`startLine <= line <= endLine`). "Tightest"
76
+ * means smallest `endLine - startLine` span — this makes nested methods
77
+ * win over their containing classes. When two candidates have the same
78
+ * span, the earlier `startLine` wins (which falls out of the deterministic
79
+ * input sort).
80
+ *
81
+ * Returns `undefined` when the file is unknown, when no candidate
82
+ * contains the line, or when every candidate has been filtered out by
83
+ * the allow-set at index time.
84
+ */
85
+ export function findEnclosingSymbolId(nodesByFile, filePath, line) {
86
+ const candidates = nodesByFile.get(filePath);
87
+ if (candidates === undefined)
88
+ return undefined;
89
+ let best;
90
+ let bestSpan = Number.POSITIVE_INFINITY;
91
+ for (const rec of candidates) {
92
+ // Candidates are sorted by startLine; once we pass the target line
93
+ // no later row can enclose it.
94
+ if (rec.startLine > line)
95
+ break;
96
+ if (rec.endLine < line)
97
+ continue;
98
+ const span = rec.endLine - rec.startLine;
99
+ if (span < bestSpan) {
100
+ best = rec;
101
+ bestSpan = span;
102
+ }
103
+ }
104
+ return best?.id;
105
+ }
106
+ //# sourceMappingURL=find-enclosing-symbol.js.map