@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.
- package/LICENSE +202 -0
- package/README.md +85 -0
- package/dist/agent-context.d.ts +54 -0
- package/dist/agent-context.d.ts.map +1 -0
- package/dist/agent-context.js +122 -0
- package/dist/agent-context.js.map +1 -0
- package/dist/cobol-proleap-setup.d.ts +77 -0
- package/dist/cobol-proleap-setup.d.ts.map +1 -0
- package/dist/cobol-proleap-setup.js +289 -0
- package/dist/cobol-proleap-setup.js.map +1 -0
- package/dist/commands/analyze.d.ts +234 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +1096 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/augment.d.ts +48 -0
- package/dist/commands/augment.d.ts.map +1 -0
- package/dist/commands/augment.js +249 -0
- package/dist/commands/augment.js.map +1 -0
- package/dist/commands/baseline.d.ts +68 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +110 -0
- package/dist/commands/baseline.js.map +1 -0
- package/dist/commands/bench.d.ts +54 -0
- package/dist/commands/bench.d.ts.map +1 -0
- package/dist/commands/bench.js +283 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/ci-init.d.ts +37 -0
- package/dist/commands/ci-init.d.ts.map +1 -0
- package/dist/commands/ci-init.js +115 -0
- package/dist/commands/ci-init.js.map +1 -0
- package/dist/commands/clean.d.ts +13 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +38 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/code-pack.d.ts +105 -0
- package/dist/commands/code-pack.d.ts.map +1 -0
- package/dist/commands/code-pack.js +187 -0
- package/dist/commands/code-pack.js.map +1 -0
- package/dist/commands/context.d.ts +30 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +237 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/detect-changes.d.ts +26 -0
- package/dist/commands/detect-changes.d.ts.map +1 -0
- package/dist/commands/detect-changes.js +73 -0
- package/dist/commands/detect-changes.js.map +1 -0
- package/dist/commands/doctor.d.ts +52 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +472 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/find-enclosing-symbol.d.ts +67 -0
- package/dist/commands/find-enclosing-symbol.d.ts.map +1 -0
- package/dist/commands/find-enclosing-symbol.js +106 -0
- package/dist/commands/find-enclosing-symbol.js.map +1 -0
- package/dist/commands/group.d.ts +123 -0
- package/dist/commands/group.d.ts.map +1 -0
- package/dist/commands/group.js +448 -0
- package/dist/commands/group.js.map +1 -0
- package/dist/commands/impact.d.ts +23 -0
- package/dist/commands/impact.d.ts.map +1 -0
- package/dist/commands/impact.js +91 -0
- package/dist/commands/impact.js.map +1 -0
- package/dist/commands/index-repo.d.ts +39 -0
- package/dist/commands/index-repo.d.ts.map +1 -0
- package/dist/commands/index-repo.js +148 -0
- package/dist/commands/index-repo.js.map +1 -0
- package/dist/commands/ingest-sarif.d.ts +64 -0
- package/dist/commands/ingest-sarif.d.ts.map +1 -0
- package/dist/commands/ingest-sarif.js +381 -0
- package/dist/commands/ingest-sarif.js.map +1 -0
- package/dist/commands/init.d.ts +75 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +315 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +17 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +79 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +8 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +28 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/open-store.d.ts +25 -0
- package/dist/commands/open-store.d.ts.map +1 -0
- package/dist/commands/open-store.js +47 -0
- package/dist/commands/open-store.js.map +1 -0
- package/dist/commands/pack.d.ts +35 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +83 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/query.d.ts +85 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +309 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/scan.d.ts +81 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +407 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/setup.d.ts +178 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +370 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/sql.d.ts +19 -0
- package/dist/commands/sql.d.ts.map +1 -0
- package/dist/commands/sql.js +51 -0
- package/dist/commands/sql.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +66 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/verdict-render.d.ts +33 -0
- package/dist/commands/verdict-render.d.ts.map +1 -0
- package/dist/commands/verdict-render.js +123 -0
- package/dist/commands/verdict-render.js.map +1 -0
- package/dist/commands/verdict.d.ts +61 -0
- package/dist/commands/verdict.d.ts.map +1 -0
- package/dist/commands/verdict.js +146 -0
- package/dist/commands/verdict.js.map +1 -0
- package/dist/commands/wiki.d.ts +26 -0
- package/dist/commands/wiki.d.ts.map +1 -0
- package/dist/commands/wiki.js +74 -0
- package/dist/commands/wiki.js.map +1 -0
- package/dist/editors/claude-code.d.ts +23 -0
- package/dist/editors/claude-code.d.ts.map +1 -0
- package/dist/editors/claude-code.js +58 -0
- package/dist/editors/claude-code.js.map +1 -0
- package/dist/editors/codex.d.ts +22 -0
- package/dist/editors/codex.d.ts.map +1 -0
- package/dist/editors/codex.js +59 -0
- package/dist/editors/codex.js.map +1 -0
- package/dist/editors/cursor.d.ts +13 -0
- package/dist/editors/cursor.d.ts.map +1 -0
- package/dist/editors/cursor.js +21 -0
- package/dist/editors/cursor.js.map +1 -0
- package/dist/editors/index.d.ts +12 -0
- package/dist/editors/index.d.ts.map +1 -0
- package/dist/editors/index.js +11 -0
- package/dist/editors/index.js.map +1 -0
- package/dist/editors/opencode.d.ts +23 -0
- package/dist/editors/opencode.d.ts.map +1 -0
- package/dist/editors/opencode.js +61 -0
- package/dist/editors/opencode.js.map +1 -0
- package/dist/editors/types.d.ts +33 -0
- package/dist/editors/types.d.ts.map +1 -0
- package/dist/editors/types.js +19 -0
- package/dist/editors/types.js.map +1 -0
- package/dist/editors/windows-wrap.d.ts +19 -0
- package/dist/editors/windows-wrap.d.ts.map +1 -0
- package/dist/editors/windows-wrap.js +28 -0
- package/dist/editors/windows-wrap.js.map +1 -0
- package/dist/editors/windsurf.d.ts +12 -0
- package/dist/editors/windsurf.d.ts.map +1 -0
- package/dist/editors/windsurf.js +21 -0
- package/dist/editors/windsurf.js.map +1 -0
- package/dist/embedder-downloader.d.ts +87 -0
- package/dist/embedder-downloader.d.ts.map +1 -0
- package/dist/embedder-downloader.js +261 -0
- package/dist/embedder-downloader.js.map +1 -0
- package/dist/fs-atomic.d.ts +22 -0
- package/dist/fs-atomic.d.ts.map +1 -0
- package/dist/fs-atomic.js +28 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/groups.d.ts +64 -0
- package/dist/groups.d.ts.map +1 -0
- package/dist/groups.js +172 -0
- package/dist/groups.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +703 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/is-indexed.d.ts +20 -0
- package/dist/lib/is-indexed.d.ts.map +1 -0
- package/dist/lib/is-indexed.js +35 -0
- package/dist/lib/is-indexed.js.map +1 -0
- package/dist/registry.d.ts +64 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +145 -0
- package/dist/registry.js.map +1 -0
- package/dist/scip-downloader.d.ts +138 -0
- package/dist/scip-downloader.d.ts.map +1 -0
- package/dist/scip-downloader.js +372 -0
- package/dist/scip-downloader.js.map +1 -0
- package/dist/scip-pins.d.ts +99 -0
- package/dist/scip-pins.d.ts.map +1 -0
- package/dist/scip-pins.js +195 -0
- package/dist/scip-pins.js.map +1 -0
- package/dist/skills-gen.d.ts +47 -0
- package/dist/skills-gen.d.ts.map +1 -0
- package/dist/skills-gen.js +292 -0
- package/dist/skills-gen.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codehub ingest-sarif <sarif-file>` — import a SARIF v2.1.0 log into
|
|
3
|
+
* the code graph as `Finding` nodes + `FOUND_IN` edges.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Read + parse + validate the SARIF file via `@opencodehub/sarif`.
|
|
7
|
+
* 2. Resolve the target repo (either `--repo <name>` or CWD).
|
|
8
|
+
* 3. Open the DuckDB store and pull a per-file, line-sorted symbol
|
|
9
|
+
* index over the SARIF's referenced URIs (used to resolve Finding
|
|
10
|
+
* → Symbol edges).
|
|
11
|
+
* 4. For every Result across every Run, build a Finding node keyed by
|
|
12
|
+
* `Finding:<scannerId>:<ruleId>:<uri>:<startLine>`. Emit FOUND_IN
|
|
13
|
+
* edges to the target File node (matched by `artifactLocation.uri`
|
|
14
|
+
* against `file_path`) plus a second FOUND_IN edge to the tightest
|
|
15
|
+
* enclosing symbol at `(uri, startLine)` when the graph contains
|
|
16
|
+
* one. A scanner-provided `opencodehub.symbolId` hint wins over the
|
|
17
|
+
* enclosing lookup when set.
|
|
18
|
+
* 5. UPSERT into DuckDB via `store.bulkLoad({ mode: "upsert" })`.
|
|
19
|
+
*
|
|
20
|
+
* The command is idempotent — re-running with the same SARIF produces
|
|
21
|
+
* the same nodes and edges. Results without a parsable location (no
|
|
22
|
+
* physicalLocation.artifactLocation.uri) are skipped with a warning.
|
|
23
|
+
*/
|
|
24
|
+
import { readFile } from "node:fs/promises";
|
|
25
|
+
import { resolve } from "node:path";
|
|
26
|
+
import { KnowledgeGraph, makeNodeId } from "@opencodehub/core-types";
|
|
27
|
+
import { applyBaselineState, enrichWithFingerprints, SarifLogSchema, } from "@opencodehub/sarif";
|
|
28
|
+
import { openStore, resolveDbPath, resolveRepoMetaDir, } from "@opencodehub/storage";
|
|
29
|
+
import { readRegistry } from "../registry.js";
|
|
30
|
+
import { ENCLOSING_SYMBOL_KINDS, findEnclosingSymbolId, indexNodesByFile, } from "./find-enclosing-symbol.js";
|
|
31
|
+
export async function runIngestSarif(sarifFile, opts = {}) {
|
|
32
|
+
const sarifPath = resolve(sarifFile);
|
|
33
|
+
const raw = await readFile(sarifPath, "utf8");
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
const validation = SarifLogSchema.safeParse(parsed);
|
|
36
|
+
if (!validation.success) {
|
|
37
|
+
throw new Error(`codehub ingest-sarif: ${sarifPath} is not a valid SARIF 2.1.0 log: ${validation.error.message}`);
|
|
38
|
+
}
|
|
39
|
+
let log = validation.data;
|
|
40
|
+
const repoPath = await resolveRepoPath(opts);
|
|
41
|
+
// Stamp `opencodehub/v1` + `primaryLocationLineHash` partial fingerprints
|
|
42
|
+
// onto every result. The call is idempotent: an already-enriched input
|
|
43
|
+
// produces the same fingerprints, so re-ingesting a SARIF file leaves the
|
|
44
|
+
// column stable.
|
|
45
|
+
log = enrichWithFingerprints(log);
|
|
46
|
+
// Optional baseline overlay. When `<repo>/.codehub/baseline.sarif` exists
|
|
47
|
+
// we tag every result with SARIF 2.1.0 `baselineState` so the
|
|
48
|
+
// `baseline_state` column is populated; missing baseline leaves it NULL
|
|
49
|
+
// (consumers treat NULL as "new" by convention).
|
|
50
|
+
const baselineLog = await loadRepoBaseline(repoPath);
|
|
51
|
+
if (baselineLog !== undefined) {
|
|
52
|
+
log = applyBaselineState(log, baselineLog);
|
|
53
|
+
}
|
|
54
|
+
const dbPath = resolveDbPath(repoPath);
|
|
55
|
+
const composed = await openStore({ path: dbPath, backend: "auto" });
|
|
56
|
+
let graph;
|
|
57
|
+
let summary;
|
|
58
|
+
try {
|
|
59
|
+
await composed.graph.open();
|
|
60
|
+
await composed.graph.createSchema();
|
|
61
|
+
// Pull the per-file symbol index out of the store once so every
|
|
62
|
+
// SARIF result can resolve its enclosing symbol without a round
|
|
63
|
+
// trip. Restricts to URIs that actually appear in the SARIF log
|
|
64
|
+
// and to the code-kind allow set shared with `buildFindingsGraph`.
|
|
65
|
+
const nodesByFile = await loadNodesByFileForSarif(composed.graph, log.runs);
|
|
66
|
+
({ graph, summary } = buildFindingsGraph(log.runs, nodesByFile));
|
|
67
|
+
await composed.graph.bulkLoad(graph, { mode: "upsert" });
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await composed.close();
|
|
71
|
+
}
|
|
72
|
+
const out = {
|
|
73
|
+
sarifFile: sarifPath,
|
|
74
|
+
repoPath,
|
|
75
|
+
findingsEmitted: summary.findingsEmitted,
|
|
76
|
+
edgesEmitted: summary.edgesEmitted,
|
|
77
|
+
resultsSkipped: summary.resultsSkipped,
|
|
78
|
+
warnings: summary.warnings,
|
|
79
|
+
};
|
|
80
|
+
for (const w of summary.warnings) {
|
|
81
|
+
console.warn(`codehub ingest-sarif: ${w}`);
|
|
82
|
+
}
|
|
83
|
+
console.warn(`codehub ingest-sarif: ${out.findingsEmitted} findings, ${out.edgesEmitted} edges from ${sarifPath} → ${repoPath}`);
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Pure builder over SARIF runs. Exposed for unit tests so we can exercise
|
|
88
|
+
* the node/edge emission logic without touching DuckDB.
|
|
89
|
+
*
|
|
90
|
+
* `nodesByFile` is the per-file, line-sorted symbol index (produced by
|
|
91
|
+
* {@link indexNodesByFile}) used to resolve each SARIF result back to the
|
|
92
|
+
* tightest-enclosing code symbol when the scanner did not populate
|
|
93
|
+
* `result.properties["opencodehub.symbolId"]` itself. Callers that only
|
|
94
|
+
* want the File-level edge (e.g. unit tests) can omit it — an empty map
|
|
95
|
+
* means every symbol lookup misses and only the File edge is emitted.
|
|
96
|
+
*/
|
|
97
|
+
export function buildFindingsGraph(runs, nodesByFile = new Map()) {
|
|
98
|
+
const graph = new KnowledgeGraph();
|
|
99
|
+
const warnings = [];
|
|
100
|
+
let findingsEmitted = 0;
|
|
101
|
+
let edgesEmitted = 0;
|
|
102
|
+
let resultsSkipped = 0;
|
|
103
|
+
for (const run of runs) {
|
|
104
|
+
const scannerId = run.tool.driver.name;
|
|
105
|
+
const results = run.results ?? [];
|
|
106
|
+
for (const result of results) {
|
|
107
|
+
const finding = buildFindingNode(scannerId, result);
|
|
108
|
+
if (!finding) {
|
|
109
|
+
resultsSkipped += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
graph.addNode(finding.node);
|
|
113
|
+
findingsEmitted += 1;
|
|
114
|
+
// FOUND_IN edge Finding → File (matched by URI). We always emit
|
|
115
|
+
// this edge — if the target File node does not exist in the graph
|
|
116
|
+
// (ingest-sarif runs independently of analyze), the relation is
|
|
117
|
+
// still recorded; downstream queries can left-join.
|
|
118
|
+
const fileId = makeNodeId("File", finding.uri, finding.uri);
|
|
119
|
+
graph.addEdge({
|
|
120
|
+
from: finding.node.id,
|
|
121
|
+
to: fileId,
|
|
122
|
+
type: "FOUND_IN",
|
|
123
|
+
confidence: 1,
|
|
124
|
+
reason: finding.reason,
|
|
125
|
+
});
|
|
126
|
+
edgesEmitted += 1;
|
|
127
|
+
// Resolve the Finding → Symbol edge. Priority order:
|
|
128
|
+
// 1. `opencodehub.symbolId` in the result properties bag — the
|
|
129
|
+
// explicit scanner-provided hint wins (e.g. semgrep rules that
|
|
130
|
+
// resolve to a specific function already).
|
|
131
|
+
// 2. Tightest-enclosing symbol at (uri, startLine) from the graph
|
|
132
|
+
// index. This is the common path for third-party SARIF tools
|
|
133
|
+
// that emit raw file+line locations.
|
|
134
|
+
// If neither resolves we keep the File-only edge.
|
|
135
|
+
const hintedSymbolId = extractSymbolId(result);
|
|
136
|
+
const symbolId = hintedSymbolId !== undefined
|
|
137
|
+
? hintedSymbolId
|
|
138
|
+
: findEnclosingSymbolId(nodesByFile, finding.uri, finding.node.startLine ?? 1);
|
|
139
|
+
if (symbolId !== undefined) {
|
|
140
|
+
graph.addEdge({
|
|
141
|
+
from: finding.node.id,
|
|
142
|
+
to: symbolId,
|
|
143
|
+
type: "FOUND_IN",
|
|
144
|
+
confidence: 1,
|
|
145
|
+
reason: finding.reason,
|
|
146
|
+
step: 1,
|
|
147
|
+
});
|
|
148
|
+
edgesEmitted += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
graph,
|
|
154
|
+
summary: { findingsEmitted, edgesEmitted, resultsSkipped, warnings },
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Convert a single SARIF Result into a FindingNode. Returns `undefined`
|
|
159
|
+
* when the result is missing a location we can key on — we need
|
|
160
|
+
* (ruleId, uri, startLine) to produce a stable id.
|
|
161
|
+
*/
|
|
162
|
+
function buildFindingNode(scannerId, result) {
|
|
163
|
+
const ruleId = result.ruleId;
|
|
164
|
+
if (typeof ruleId !== "string" || ruleId.length === 0)
|
|
165
|
+
return undefined;
|
|
166
|
+
const loc = result.locations?.[0]?.physicalLocation;
|
|
167
|
+
const uri = loc?.artifactLocation?.uri;
|
|
168
|
+
if (typeof uri !== "string" || uri.length === 0)
|
|
169
|
+
return undefined;
|
|
170
|
+
const region = loc?.region;
|
|
171
|
+
const startLine = region?.startLine ?? 1;
|
|
172
|
+
const endLine = region?.endLine;
|
|
173
|
+
// Severity: map SARIF level → Finding.severity. Default "note" when
|
|
174
|
+
// the scanner omits `level` (GHAS treats missing level as "warning",
|
|
175
|
+
// but we stay conservative).
|
|
176
|
+
const severity = mapSeverity(result.level);
|
|
177
|
+
const message = result.message?.text ?? "";
|
|
178
|
+
const id = makeNodeId("Finding", uri, `${scannerId}:${ruleId}:${startLine}`);
|
|
179
|
+
const propertiesBag = {};
|
|
180
|
+
if (result.properties) {
|
|
181
|
+
for (const [k, v] of Object.entries(result.properties)) {
|
|
182
|
+
propertiesBag[k] = v;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const suppressedJson = extractSuppressedJson(result);
|
|
186
|
+
const partialFingerprint = extractOpenCodeHubFingerprint(result);
|
|
187
|
+
const baselineState = extractBaselineState(result);
|
|
188
|
+
const node = {
|
|
189
|
+
id,
|
|
190
|
+
kind: "Finding",
|
|
191
|
+
name: `${scannerId}:${ruleId}`,
|
|
192
|
+
filePath: uri,
|
|
193
|
+
ruleId,
|
|
194
|
+
severity,
|
|
195
|
+
scannerId,
|
|
196
|
+
message,
|
|
197
|
+
propertiesBag,
|
|
198
|
+
startLine,
|
|
199
|
+
...(endLine !== undefined ? { endLine } : {}),
|
|
200
|
+
...(suppressedJson !== undefined ? { suppressedJson } : {}),
|
|
201
|
+
...(partialFingerprint !== undefined ? { partialFingerprint } : {}),
|
|
202
|
+
...(baselineState !== undefined ? { baselineState } : {}),
|
|
203
|
+
};
|
|
204
|
+
const reason = endLine !== undefined ? `startLine=${startLine};endLine=${endLine}` : `startLine=${startLine}`;
|
|
205
|
+
return { node, uri, reason };
|
|
206
|
+
}
|
|
207
|
+
function mapSeverity(level) {
|
|
208
|
+
switch (level) {
|
|
209
|
+
case "error":
|
|
210
|
+
case "warning":
|
|
211
|
+
case "note":
|
|
212
|
+
case "none":
|
|
213
|
+
return level;
|
|
214
|
+
default:
|
|
215
|
+
return "note";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Persist SARIF `suppressions[]` into the FindingNode's `suppressedJson`
|
|
220
|
+
* column. We keep every entry (external + inSource) so
|
|
221
|
+
* downstream consumers can distinguish waiver provenance; missing or
|
|
222
|
+
* empty arrays resolve to undefined so the column stays null and verdict
|
|
223
|
+
* treats the finding as un-suppressed.
|
|
224
|
+
*/
|
|
225
|
+
function extractSuppressedJson(result) {
|
|
226
|
+
const arr = result.suppressions;
|
|
227
|
+
if (!Array.isArray(arr) || arr.length === 0)
|
|
228
|
+
return undefined;
|
|
229
|
+
const entries = [];
|
|
230
|
+
for (const entry of arr) {
|
|
231
|
+
if (entry === null || typeof entry !== "object")
|
|
232
|
+
continue;
|
|
233
|
+
const record = entry;
|
|
234
|
+
const kind = record["kind"];
|
|
235
|
+
const justification = record["justification"];
|
|
236
|
+
if (typeof kind !== "string" || typeof justification !== "string")
|
|
237
|
+
continue;
|
|
238
|
+
const expiresAt = record["expiresAt"];
|
|
239
|
+
const out = {
|
|
240
|
+
kind,
|
|
241
|
+
justification,
|
|
242
|
+
};
|
|
243
|
+
if (typeof expiresAt === "string" && expiresAt.length > 0)
|
|
244
|
+
out.expiresAt = expiresAt;
|
|
245
|
+
entries.push(out);
|
|
246
|
+
}
|
|
247
|
+
return entries.length > 0 ? JSON.stringify(entries) : undefined;
|
|
248
|
+
}
|
|
249
|
+
function extractSymbolId(result) {
|
|
250
|
+
const props = result.properties;
|
|
251
|
+
if (!props || typeof props !== "object")
|
|
252
|
+
return undefined;
|
|
253
|
+
const record = props;
|
|
254
|
+
// Primary key scanners put inside their result properties bag.
|
|
255
|
+
const v = record["opencodehub.symbolId"];
|
|
256
|
+
if (typeof v === "string" && v.length > 0)
|
|
257
|
+
return v;
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Pull the `opencodehub/v1` entry out of `result.partialFingerprints` and
|
|
262
|
+
* persist it on the FindingNode. Enrichment runs before ingest so this
|
|
263
|
+
* lookup always succeeds for well-formed inputs — if it doesn't, the
|
|
264
|
+
* column stays NULL (e.g. SARIF files that predate enrichment).
|
|
265
|
+
*/
|
|
266
|
+
function extractOpenCodeHubFingerprint(result) {
|
|
267
|
+
const pf = result.partialFingerprints;
|
|
268
|
+
if (pf === null || pf === undefined || typeof pf !== "object")
|
|
269
|
+
return undefined;
|
|
270
|
+
const v = pf["opencodehub/v1"];
|
|
271
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Read `result.baselineState` as a typed literal. `applyBaselineState`
|
|
275
|
+
* only writes `"new" | "unchanged" | "updated"` (baseline-only findings
|
|
276
|
+
* stay outside the current log); `"absent"` can arrive via third-party
|
|
277
|
+
* tooling so we accept it for completeness.
|
|
278
|
+
*/
|
|
279
|
+
function extractBaselineState(result) {
|
|
280
|
+
const v = result.baselineState;
|
|
281
|
+
if (v === "new" || v === "unchanged" || v === "updated" || v === "absent") {
|
|
282
|
+
return v;
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Load `<repo>/.codehub/baseline.sarif` if present. Missing file resolves
|
|
288
|
+
* to undefined; malformed file raises so the caller can surface the
|
|
289
|
+
* validation error instead of silently dropping baselineState.
|
|
290
|
+
*/
|
|
291
|
+
async function loadRepoBaseline(repoPath) {
|
|
292
|
+
const candidate = resolve(`${resolveRepoMetaDir(repoPath)}/baseline.sarif`);
|
|
293
|
+
let raw;
|
|
294
|
+
try {
|
|
295
|
+
raw = await readFile(candidate, "utf8");
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
if (err.code === "ENOENT")
|
|
299
|
+
return undefined;
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
const parsed = JSON.parse(raw);
|
|
303
|
+
const result = SarifLogSchema.safeParse(parsed);
|
|
304
|
+
if (!result.success) {
|
|
305
|
+
throw new Error(`codehub ingest-sarif: baseline at ${candidate} is not a valid SARIF 2.1.0 log: ${result.error.message}`);
|
|
306
|
+
}
|
|
307
|
+
return result.data;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Collect every distinct `artifactLocation.uri` across every Result in
|
|
311
|
+
* every Run. Results without a parsable URI (or with an empty one) are
|
|
312
|
+
* silently skipped — downstream emission logic already discards them.
|
|
313
|
+
*/
|
|
314
|
+
function collectSarifUris(runs) {
|
|
315
|
+
const seen = new Set();
|
|
316
|
+
for (const run of runs) {
|
|
317
|
+
for (const result of run.results ?? []) {
|
|
318
|
+
const uri = result.locations?.[0]?.physicalLocation?.artifactLocation?.uri;
|
|
319
|
+
if (typeof uri === "string" && uri.length > 0)
|
|
320
|
+
seen.add(uri);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return [...seen];
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Query the graph store for every code-kind node whose `file_path`
|
|
327
|
+
* matches a URI that appears in the SARIF log, then build the per-file,
|
|
328
|
+
* line-sorted symbol index used by {@link findEnclosingSymbolId}.
|
|
329
|
+
*
|
|
330
|
+
* Scoping by the SARIF URIs keeps the query bounded even on large
|
|
331
|
+
* repos: a SARIF log typically references a few hundred files, not the
|
|
332
|
+
* whole codebase. Empty URI list short-circuits to an empty index — the
|
|
333
|
+
* caller will emit only File-level edges, which matches the v0 behavior
|
|
334
|
+
* before symbol-level linkage existed.
|
|
335
|
+
*/
|
|
336
|
+
async function loadNodesByFileForSarif(graph, runs) {
|
|
337
|
+
const uris = collectSarifUris(runs);
|
|
338
|
+
if (uris.length === 0)
|
|
339
|
+
return new Map();
|
|
340
|
+
// Fan one round-trip per code kind in the allow-set, narrowed by
|
|
341
|
+
// `filePath` set. `listNodesByKind` returns the typed node shape
|
|
342
|
+
// (`NodeOfKind<K>`) — the row projection only needs id / filePath /
|
|
343
|
+
// startLine / endLine / kind, all of which are present on every
|
|
344
|
+
// ENCLOSING_SYMBOL_KINDS member (LocatedNode subset).
|
|
345
|
+
const uriSet = new Set(uris);
|
|
346
|
+
const projected = [];
|
|
347
|
+
for (const kind of ENCLOSING_SYMBOL_KINDS) {
|
|
348
|
+
const nodes = await graph.listNodesByKind(kind);
|
|
349
|
+
for (const n of nodes) {
|
|
350
|
+
if (!uriSet.has(n.filePath))
|
|
351
|
+
continue;
|
|
352
|
+
const startLine = n.startLine;
|
|
353
|
+
const endLine = n.endLine;
|
|
354
|
+
if (typeof startLine !== "number" || !Number.isFinite(startLine))
|
|
355
|
+
continue;
|
|
356
|
+
if (typeof endLine !== "number" || !Number.isFinite(endLine))
|
|
357
|
+
continue;
|
|
358
|
+
projected.push({
|
|
359
|
+
id: n.id,
|
|
360
|
+
filePath: n.filePath,
|
|
361
|
+
startLine,
|
|
362
|
+
endLine,
|
|
363
|
+
kind: n.kind,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return indexNodesByFile(projected);
|
|
368
|
+
}
|
|
369
|
+
async function resolveRepoPath(opts) {
|
|
370
|
+
if (opts.repo !== undefined) {
|
|
371
|
+
const registryOpts = opts.home !== undefined ? { home: opts.home } : {};
|
|
372
|
+
const registry = await readRegistry(registryOpts);
|
|
373
|
+
const hit = registry[opts.repo];
|
|
374
|
+
if (hit)
|
|
375
|
+
return resolve(hit.path);
|
|
376
|
+
// Treat as raw path fallback for ergonomics (same convention as query CLI).
|
|
377
|
+
return resolve(opts.repo);
|
|
378
|
+
}
|
|
379
|
+
return resolve(process.cwd());
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=ingest-sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-sarif.js","sourceRoot":"","sources":["../../src/commands/ingest-sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codehub init` — bootstrap a repository for OpenCodeHub in one command.
|
|
3
|
+
*
|
|
4
|
+
* Project-scope installer. Copies plugin assets (skills/agents/commands/hooks)
|
|
5
|
+
* into `<repo>/.claude/`, writes `.mcp.json` so Claude Code launches the
|
|
6
|
+
* `codehub` MCP server, appends `.codehub/` to `.gitignore`, and seeds a
|
|
7
|
+
* starter `opencodehub.policy.yaml` (commented out; rules ship in spec 002).
|
|
8
|
+
*
|
|
9
|
+
* Why project scope:
|
|
10
|
+
* - Checking `.claude/` into git means every teammate's Claude Code
|
|
11
|
+
* auto-discovers the plugin on clone. No per-machine install.
|
|
12
|
+
* - Keeps the plugin pinned to the repo's graph schema; upgrades are
|
|
13
|
+
* explicit (`codehub init --upgrade`) rather than ambient.
|
|
14
|
+
*
|
|
15
|
+
* Hook rewrite note:
|
|
16
|
+
* The plugin's `hooks.json` uses `${CLAUDE_PLUGIN_ROOT}` which is only
|
|
17
|
+
* bound for user-scope plugins. Project-scope hooks must use
|
|
18
|
+
* `${CLAUDE_PROJECT_DIR}/.claude` for Claude Code to find the shell
|
|
19
|
+
* scripts. We rewrite the token at install time and write the result
|
|
20
|
+
* to `.claude/settings.json` (the project-scope equivalent of
|
|
21
|
+
* `hooks.json`).
|
|
22
|
+
*
|
|
23
|
+
* Idempotence: re-running with identical args produces byte-identical
|
|
24
|
+
* output. Re-running against an existing `.claude/` refuses unless
|
|
25
|
+
* `--force` is set, and the error lists every conflicting file.
|
|
26
|
+
*
|
|
27
|
+
* Filesystem access goes through the same `FsApi` seam used by
|
|
28
|
+
* `commands/setup.ts` so tests run against an in-memory implementation.
|
|
29
|
+
*/
|
|
30
|
+
import { type FsApi, type SetupResult } from "./setup.js";
|
|
31
|
+
export interface InitOptions {
|
|
32
|
+
/** Target repo. Defaults to `process.cwd()`. */
|
|
33
|
+
readonly repo?: string;
|
|
34
|
+
/** Plugin source dir. Defaults to the bundled `dist/plugin-assets/`. */
|
|
35
|
+
readonly sourceDir?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Overwrite `.claude/` on re-run. Without `--force`, conflicting files cause
|
|
38
|
+
* a refusal listing each one.
|
|
39
|
+
*/
|
|
40
|
+
readonly force?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Skip the `.mcp.json` edit. Useful for teams that manage MCP config
|
|
43
|
+
* elsewhere (e.g., via Claude Code user-scope config).
|
|
44
|
+
*/
|
|
45
|
+
readonly skipMcp?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Skip seeding `opencodehub.policy.yaml`. The starter file is commented out
|
|
48
|
+
* and purely informational; some repos may prefer to author their own.
|
|
49
|
+
*/
|
|
50
|
+
readonly skipPolicy?: boolean;
|
|
51
|
+
/** FS seam. Defaults to real filesystem. */
|
|
52
|
+
readonly fs?: FsApi;
|
|
53
|
+
/** Structured logger. Defaults to `console.warn`. */
|
|
54
|
+
readonly log?: (message: string) => void;
|
|
55
|
+
readonly warn?: (message: string) => void;
|
|
56
|
+
/** Override home dir (forwarded to `runSetup` for claude-code MCP write). */
|
|
57
|
+
readonly home?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface InitResult {
|
|
60
|
+
readonly repoRoot: string;
|
|
61
|
+
readonly sourceDir: string;
|
|
62
|
+
readonly claudeDir: string;
|
|
63
|
+
readonly filesCopied: number;
|
|
64
|
+
readonly hooksWrittenTo: string | null;
|
|
65
|
+
readonly mcpResult: SetupResult | null;
|
|
66
|
+
readonly gitignoreUpdated: boolean;
|
|
67
|
+
readonly policySeeded: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Entry point for `codehub init`. Returns a structured result; throws on
|
|
71
|
+
* unrecoverable error (e.g., plugin source missing, conflicts without
|
|
72
|
+
* `--force`).
|
|
73
|
+
*/
|
|
74
|
+
export declare function runInit(opts?: InitOptions): Promise<InitResult>;
|
|
75
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAgBH,OAAO,EAAE,KAAK,KAAK,EAA+B,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AA0CvF,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6EzE"}
|