diff --git a/.claude/agents/code-analyst.md b/.claude/agents/code-analyst.md index 82dd41f9..b4402d18 100644 --- a/.claude/agents/code-analyst.md +++ b/.claude/agents/code-analyst.md @@ -1,7 +1,7 @@ --- name: code-analyst description: Use when the user asks to understand code structure, impact of changes, find owners, audit dependencies, or navigate the codebase graph. Specializes in the OpenCodeHub MCP toolkit and always grounds claims in graph queries rather than text search. -tools: mcp__opencodehub__query, mcp__opencodehub__context, mcp__opencodehub__impact, mcp__opencodehub__route_map, mcp__opencodehub__api_impact, mcp__opencodehub__shape_check, mcp__opencodehub__tool_map, mcp__opencodehub__verdict, mcp__opencodehub__owners, mcp__opencodehub__license_audit, mcp__opencodehub__list_findings, mcp__opencodehub__list_findings_delta, mcp__opencodehub__list_dead_code, mcp__opencodehub__signature, mcp__opencodehub__detect_changes, mcp__opencodehub__rename, Read, Grep, Glob +tools: mcp__opencodehub__query, mcp__opencodehub__context, mcp__opencodehub__impact, mcp__opencodehub__route_map, mcp__opencodehub__api_impact, mcp__opencodehub__shape_check, mcp__opencodehub__tool_map, mcp__opencodehub__verdict, mcp__opencodehub__owners, mcp__opencodehub__license_audit, mcp__opencodehub__list_findings, mcp__opencodehub__list_findings_delta, mcp__opencodehub__list_dead_code, mcp__opencodehub__signature, mcp__opencodehub__detect_changes, Read, Grep, Glob model: sonnet --- @@ -12,7 +12,7 @@ Tool selection rules: - **Impact / "what breaks if I change X"**: `impact` with `direction: "upstream"` for callers, `"downstream"` for callees; `api_impact` for public API boundaries; `shape_check` for structural drift across payloads and types. - **Ownership**: `owners` for top contributors. `signature` for canonical function signatures before quoting them. - **Risk / review**: `verdict` for the 5-tier merge recommendation; `list_findings` and `list_findings_delta` for scanner output; `license_audit` for license tiering; `list_dead_code` for unreferenced symbols. -- **Refactors**: `rename` always with `dry_run: true` first — never apply without showing the diff and getting explicit confirmation. +- **Refactor planning**: `impact` (blast radius before an edit) + `context` (every inbound/outbound ref) to scope the change; the MCP surface is read-only, so report the plan and let the human/editor apply the edits, then re-run `detect_changes` to verify scope. - **Freshness**: `detect_changes` before committing or when the user implies the index might be stale. Always cite with `path:line`. Never paraphrase signatures — quote them. If a tool returns nothing, say so; do not invent coverage. diff --git a/.claude/skills/opencodehub-guide/SKILL.md b/.claude/skills/opencodehub-guide/SKILL.md index dd420fa2..6a04266d 100644 --- a/.claude/skills/opencodehub-guide/SKILL.md +++ b/.claude/skills/opencodehub-guide/SKILL.md @@ -24,7 +24,7 @@ For any task that touches code understanding, debugging, impact analysis, refact | Understand architecture / "How does X work?" | `opencodehub-exploring` | | Blast radius / "What breaks if I change X?" | `opencodehub-impact-analysis` | | Trace bugs / "Why is X failing?" | `opencodehub-debugging` | -| Rename / extract / split / restructure | `opencodehub-refactoring` | +| Plan a rename / extract / move (analysis only) | `opencodehub-refactoring` | | Review a PR / "Is this safe to merge?" | `opencodehub-pr-review` | | Tools, resources, schema reference | `opencodehub-guide` (here) | @@ -46,7 +46,9 @@ for the scope rationale. Fire these directly; do not nest them inside analysis skills. Each is a standalone artifact producer with its own preconditions and output path. -## Tool Inventory (27 MCP tools) +## Tool Inventory (28 MCP tools) + +> Every tool is **read-only with respect to your source**. No MCP tool edits the working tree; planning and verification tools surface what to change, and you (or your editor) apply the edit. ### Code intelligence (per-repo) @@ -57,9 +59,8 @@ standalone artifact producer with its own preconditions and output path. | `mcp__opencodehub__context` | 360-degree symbol view + `confidenceBreakdown` + `cochanges` side-section | | `mcp__opencodehub__impact` | Blast radius with risk tier + `confidenceBreakdown` | | `mcp__opencodehub__detect_changes` | Map an uncommitted or committed diff to affected symbols and flows | -| `mcp__opencodehub__rename` | Graph-assisted multi-file rename; dry-run by default | | `mcp__opencodehub__sql` | Read-only query: `sql` arg → temporal DuckDB (cochanges/summaries); `cypher` arg → lbug graph (5 s timeout) | -| `mcp__opencodehub__signature` | Function signature lookup for a target symbol | +| `mcp__opencodehub__signature` | Symbol declaration + stubbed members (class/interface header + method/property signatures, bodies elided) | ### HTTP / RPC surface @@ -78,6 +79,8 @@ standalone artifact producer with its own preconditions and output path. | `mcp__opencodehub__group_query` | BM25 fan-out across a group with reciprocal-rank fusion | | `mcp__opencodehub__group_status` | Per-repo staleness + contract freshness for a group | | `mcp__opencodehub__group_contracts` | HTTP contract cross-links (consumer FETCHES edge → producer Route) | +| `mcp__opencodehub__group_cross_repo_links` | Audit trail of every typed cross-repo edge, both endpoints `repo_uri`-qualified | +| `mcp__opencodehub__group_sync` | Rebuild the cross-repo contract registry + link table after a re-index | ### Supply-chain / PR review (OpenCodeHub differentiators) @@ -87,13 +90,13 @@ standalone artifact producer with its own preconditions and output path. | `mcp__opencodehub__scan` | Run Priority-1 scanners (openWorld — spawns child processes) | | `mcp__opencodehub__list_findings` | Browse SARIF findings produced by `scan` or `ingest-sarif` | | `mcp__opencodehub__list_findings_delta` | Diff latest scan vs. frozen baseline (new / fixed / unchanged / updated) | -| `mcp__opencodehub__list_dead_code` | Unreferenced exported symbols | -| `mcp__opencodehub__remove_dead_code` | Scripted removal of dead-code items (dry-run by default) | +| `mcp__opencodehub__list_dead_code` | Unreferenced exported symbols (read-only listing — you delete them) | | `mcp__opencodehub__license_audit` | Copyleft / unknown / proprietary tier check over dependencies | | `mcp__opencodehub__dependencies` | External package list (ecosystem + version + manifest path) | | `mcp__opencodehub__owners` | File/symbol ownership from CODEOWNERS + git blame signal | | `mcp__opencodehub__risk_trends` | Per-community trend lines and 30-day projections | | `mcp__opencodehub__project_profile` | High-level repo summary (languages, stacks, entry points) | +| `mcp__opencodehub__pack_codebase` | Deterministic LLM-ready code-pack snapshot of the repo | ## Differentiators to surface in responses @@ -210,5 +213,5 @@ LIMIT 20; - Every per-repo tool accepts an optional `repo` argument. When exactly one repo is indexed, `repo` is optional. When two or more are indexed and `repo` is omitted, the tool returns `AMBIGUOUS_REPO` — pass `repo` explicitly. - Every response may carry `_meta.codehub/staleness` when the index is behind HEAD. Surface that to the user when it is present. - Every response includes a `next_steps` array under `structuredContent`. Use it to pick the next tool without guessing. -- `rename` is dry-run by default — explicitly pass `dry_run: false` to apply edits. +- No MCP tool edits user source. Tools like `impact`, `context`, and `detect_changes` tell you what a change touches; you (or your editor) make the edit, then re-run `detect_changes` to verify. - `scan` has `openWorldHint: true` — it spawns child processes. Do not invoke it on every turn. diff --git a/.claude/skills/opencodehub-refactoring/SKILL.md b/.claude/skills/opencodehub-refactoring/SKILL.md index 955fed39..840a722e 100644 --- a/.claude/skills/opencodehub-refactoring/SKILL.md +++ b/.claude/skills/opencodehub-refactoring/SKILL.md @@ -1,46 +1,66 @@ --- name: opencodehub-refactoring -description: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\"." +description: "Use when the user is planning a rename, extraction, split, move, or restructure and wants to know the blast radius and verify the result. Examples: \"What breaks if I rename this function?\", \"Map every caller before I extract this module.\", \"Did my refactor touch anything I didn't expect?\". OpenCodeHub does NOT edit source — it plans and verifies; you (or your editor) apply the edits." --- -# Refactoring with OpenCodeHub +# Refactoring support with OpenCodeHub + +OpenCodeHub's MCP surface is **read-only with respect to your source** — no +tool edits the working tree. This skill uses the graph to **plan** a refactor +(map the blast radius and every reference before you touch anything) and to +**verify** it afterward (confirm the diff matches the plan and nothing +unexpected moved). The actual edits are made by you or your editor; OpenCodeHub +tells you exactly what to change and then checks your work. ## When to Use -- "Rename this function safely." -- "Extract this into a module." -- "Split this service." -- "Move this to a new file." -- Any task involving renaming, extracting, splitting, or restructuring code. +- "What will break if I rename `validateUser`?" +- "Map every caller before I extract this into a module." +- "Where are all the references to this symbol — including dynamic ones?" +- "I just refactored — did the change touch only what I expected?" +- Any rename, extract, split, or move where you need the dependency picture + before editing and a verification pass after. ## Workflow ``` -1. mcp__opencodehub__impact({ name: target, direction: "upstream" }) → All dependents -2. mcp__opencodehub__context({ name: target }) → Incoming / outgoing / processes -3. mcp__opencodehub__rename({ ..., dry_run: true }) → Preview every edit -4. Review confidence tags on each edit (graph vs. text-search) -5. mcp__opencodehub__rename({ ..., dry_run: false }) → Apply -6. mcp__opencodehub__detect_changes → Verify the diff matches the plan -7. Run tests for the affected processes +PLAN +1. mcp__opencodehub__impact({ name: target, direction: "upstream" }) → All dependents (the edit list) +2. mcp__opencodehub__context({ name: target }) → Incoming / outgoing refs + processes +3. mcp__opencodehub__shape_check (if the symbol is a route/payload) → Producer/consumer drift to watch +4. Build the edit plan from impact's d=1 set + context's refs + +APPLY (you / your editor — OpenCodeHub does not do this) +5. Make the edits in your editor or with the language server's rename + +VERIFY +6. mcp__opencodehub__detect_changes({ scope: "unstaged" }) → What the diff actually touched +7. Cross-check changed_symbols against the plan — any surprise is a miss +8. Run tests for the affected processes ``` -> If the context envelope warns the index is stale, run `codehub analyze` first — a stale graph produces incomplete rename plans. +> If the context envelope warns the index is stale, run `codehub analyze` first — a stale graph produces an incomplete plan and a misleading verification. ## Checklists -### Rename a symbol +### Plan and verify a symbol rename ``` -- [ ] mcp__opencodehub__impact({ name, direction: "upstream" }) — enumerate all dependents -- [ ] mcp__opencodehub__rename({ name, new_name, dry_run: true }) +PLAN +- [ ] mcp__opencodehub__impact({ name, direction: "upstream" }) — enumerate every dependent; this is your edit list +- [ ] mcp__opencodehub__context({ name }) — see inbound + outbound refs, including ACCESSES edges (pass `file_path` and/or `kind` to disambiguate when the name is ambiguous) -- [ ] Review edits: graph edges (high confidence, LSP-backed where available) - vs. text_search edits (review line-by-line — config files, docs, tests) -- [ ] Cross-check the dry-run edit count against impact's d=1 count — - gaps mean a dynamic reference the rename missed -- [ ] mcp__opencodehub__rename({ ..., dry_run: false }) — apply -- [ ] mcp__opencodehub__detect_changes({ scope: "unstaged" }) — confirm scope +- [ ] Note the confidenceBreakdown: `unknown > 0` means a heuristic edge the SCIP + oracle contradicted — inspect those by reading source before trusting them +- [ ] Watch for dynamic references the graph cannot see (string-keyed dispatch, + reflection, config JSON, doc comments) — list them as manual-check items + +APPLY (your editor / LSP rename — not an OpenCodeHub tool) +- [ ] Apply the rename across the files impact + context named + +VERIFY +- [ ] mcp__opencodehub__detect_changes({ scope: "unstaged" }) — confirm the diff scope +- [ ] Cross-check detect_changes against impact's d=1 count — a gap means a missed reference - [ ] Run tests for every affected process ``` @@ -49,8 +69,8 @@ description: "Use when the user wants to rename, extract, split, move, or restru ``` - [ ] mcp__opencodehub__context({ name: target }) — see every external ref - [ ] mcp__opencodehub__impact({ name: target, direction: "upstream" }) — callers outside the new module -- [ ] Define the new public surface (exports only what external callers use) -- [ ] Move code; update imports +- [ ] Define the new public surface (export only what external callers use) +- [ ] Move code; update imports (in your editor) - [ ] mcp__opencodehub__detect_changes — verify scope - [ ] Run tests for the affected processes - [ ] Re-run codehub analyze so the next agent sees the new module boundary @@ -62,40 +82,14 @@ description: "Use when the user wants to rename, extract, split, move, or restru - [ ] mcp__opencodehub__context({ name: target }) — understand outgoing calls - [ ] Group outgoing calls by responsibility (the seams for the split) - [ ] mcp__opencodehub__impact({ name: target, direction: "upstream" }) — map callers to update -- [ ] Create the new functions / services -- [ ] Update callers +- [ ] Create the new functions / services and update callers (in your editor) - [ ] mcp__opencodehub__detect_changes — verify scope - [ ] Run tests ``` ## Tools -### `mcp__opencodehub__rename` — multi-file coordinated rename - -``` -mcp__opencodehub__rename({ - name: "validateUser", - new_name: "authenticateUser", - repo: "my-app", - dry_run: true // default: true -}) - -→ edits: [{ - file_path, - line, - old_text, - new_text, - confidence, // 0.95+ = graph-backed (ideally LSP-confirmed); lower = text_search - source // "graph" | "text_search" - }] -→ summary: {total, by_source: {graph, text_search}} -``` - -**Rule**: always review `text_search` edits line-by-line. They are the ones that hit dynamic references (config JSON, doc comments, test fixtures) where a rename may or may not be correct. Graph-backed edits on LSP-confirmed edges are safe to apply in bulk. - -Disambiguation: when `name` matches more than one symbol, pass `file_path` and optionally `kind` to pick the target. A future wave will add `symbol_uid` for a direct UID-only path. - -### `mcp__opencodehub__impact` — enumerate dependents before renaming +### `mcp__opencodehub__impact` — enumerate dependents before you edit ``` mcp__opencodehub__impact({ @@ -106,10 +100,29 @@ mcp__opencodehub__impact({ }) → byDepth.d1: direct callers — every one needs updating +→ affected_processes: which execution flows the change rides → confidenceBreakdown: {confirmed, heuristic, unknown} +→ risk: LOW | MEDIUM | HIGH | CRITICAL ``` -If `unknown > 0`, the demote phase contradicted a heuristic edge. That edge may not be a real call — inspect before updating. +The d=1 set IS your rename edit list. If `unknown > 0`, the demote phase +contradicted a heuristic edge — that edge may not be a real call, so inspect it +before counting it. + +### `mcp__opencodehub__context` — every inbound and outbound reference + +``` +mcp__opencodehub__context({ name: "validateUser", repo: "my-app" }) + +→ callers: inbound CALLS / REFERENCES edges +→ callees: outbound edges +→ accesses: ACCESSES edges (field / property reads and writes) +→ processes: execution flows the symbol participates in +``` + +Use `context` to catch references `impact` does not surface as direct callers — +re-exports, shadowed locals, and ACCESSES edges. The combination of `impact` +(d=1) and `context` (all refs) is the complete edit list the graph can see. ### `mcp__opencodehub__detect_changes` — verify the post-refactor diff @@ -121,60 +134,73 @@ mcp__opencodehub__detect_changes({ scope: "unstaged", repo: "my-app" }) → risk_level: LOW | MEDIUM | HIGH | CRITICAL ``` -Always run this **after** applying the rename. Any symbol you did not expect to change is a miss. +Always run this **after** you apply the edits. Any symbol you did not expect to +change is a miss; any planned symbol that does not appear was not edited. + +### `mcp__opencodehub__shape_check` — catch contract drift on routes/payloads + +When the refactor touches an HTTP route or a response payload, `shape_check` +flags producer/consumer mismatches so a response-shape change does not silently +break a downstream consumer. + +``` +mcp__opencodehub__shape_check({ route: "GET /users/:id", repo: "my-app" }) + +→ mismatches: [{ consumer, expected, actual }] +``` -### `mcp__opencodehub__sql` — custom reference query +### `mcp__opencodehub__sql` — custom reference query (temporal store) -All files referencing a symbol (useful when rename misses dynamic refs): +The `sql` arg is read-only DuckDB over the temporal store (cochanges + +symbol_summaries). To enumerate every file referencing a symbol from the graph, +use the `cypher` arg of the same tool instead (the node/edge graph lives in +`graph.lbug`, not the SQL store): -```sql -SELECT DISTINCT caller.file_path -FROM relations r -JOIN nodes caller ON caller.id = r.from_id -JOIN nodes target ON target.id = r.to_id -WHERE r.type IN ('CALLS', 'REFERENCES', 'IMPORTS') - AND target.name = 'validateUser' -ORDER BY caller.file_path; +```cypher +MATCH (caller:CodeNode)-[r:REFERENCES|CALLS|IMPORTS]->(target:CodeNode) +WHERE target.name = 'validateUser' +RETURN DISTINCT caller.file_path AS file +ORDER BY file ``` +This catches references a textual rename might miss — useful as a manual-check +list before and after you edit. + ## Risk Rules | Risk factor | Mitigation | | --------------------------------- | ----------------------------------------------------------------------- | -| Many callers (> 5) | Let `rename` do the mechanical work — do not hand-edit | -| Cross-module references | Run `detect_changes` after applying; watch for missed imports | -| String / dynamic references | Use `sql` with `type = 'REFERENCES'` + text_search edits | +| Many callers (> 5) | Use your editor's LSP rename for the mechanical work; `impact` is the checklist | +| Cross-module references | Run `detect_changes` after editing; watch for missed imports | +| String / dynamic references | Use the `cypher` arg with `REFERENCES`; the graph cannot see string-keyed dispatch — read those by hand | | Public / exported API | Version and deprecate; mirror symbol names in a transition layer | -| Heuristic edges (confirmed = 0) | Cross-check by reading source; LSP did not weigh in | +| Heuristic edges (confirmed = 0) | Cross-check by reading source; the SCIP oracle did not weigh in | -## Example: Rename `validateUser` to `authenticateUser` +## Example: plan and verify renaming `validateUser` to `authenticateUser` ``` +PLAN 1. mcp__opencodehub__impact({ name: "validateUser", direction: "upstream", repo: "my-app" }) → d=1: loginHandler, apiMiddleware, tests/auth.test.ts → affected_processes: [LoginFlow, TokenRefresh] → confidenceBreakdown: {confirmed: 3, heuristic: 0, unknown: 0} -2. mcp__opencodehub__rename({ - name: "validateUser", new_name: "authenticateUser", - repo: "my-app", dry_run: true - }) - → 12 edits across 8 files - → summary: {graph: 10, text_search: 2} - → text_search edits: config/routes.json (line 14), docs/auth.md (line 33) - -3. Review text_search edits: config/routes.json references validateUser by - string name — apply the rename manually, the JSON schema allows it. - docs/auth.md is prose, safe to rewrite. +2. mcp__opencodehub__context({ name: "validateUser", repo: "my-app" }) + → callers confirm the same three sites + → also surfaces a string reference in config/routes.json and a mention in docs/auth.md + (dynamic refs the graph flags but cannot edit) — add both to the manual-check list -4. mcp__opencodehub__rename({ ..., dry_run: false }) - → Applied 12 edits across 8 files. +APPLY (in your editor — OpenCodeHub does not write files) +3. Use the language server's rename across loginHandler, apiMiddleware, and the + test, then hand-edit config/routes.json and docs/auth.md. -5. mcp__opencodehub__detect_changes({ scope: "unstaged", repo: "my-app" }) +VERIFY +4. mcp__opencodehub__detect_changes({ scope: "unstaged", repo: "my-app" }) → changed_symbols: [authenticateUser, loginHandler, apiMiddleware, ...] → affected_processes: [LoginFlow, TokenRefresh] → risk_level: MEDIUM -6. Run LoginFlow + TokenRefresh integration tests. Re-run codehub analyze - so the graph picks up the new name. +5. Cross-check: every d=1 caller from step 1 appears in changed_symbols. No + surprises. Run LoginFlow + TokenRefresh integration tests, then re-run + codehub analyze so the graph picks up the new name. ``` diff --git a/.erpaval/INDEX.md b/.erpaval/INDEX.md index 74508cf2..2a058929 100644 --- a/.erpaval/INDEX.md +++ b/.erpaval/INDEX.md @@ -56,6 +56,14 @@ development sessions. Solutions are reusable; specs are per-feature. - [npm trusted publisher matches the ENTRY workflow, not the reusable one](solutions/conventions/npm-trusted-publisher-matches-entry-workflow-not-reusable.md) — npm OIDC matches "Workflow filename" against the workflow that INITIATED the run, not the one running `npm publish`. With `release-please.yml` → `workflow_call` → `release.yml`, register `release-please.yml`. Wrong registration silently 404s the token exchange; only `workflow_dispatch` runs (entry = release.yml) ever publish, so npm lags the tags. Config is web-UI-only, passkey-gated, one entry per package (17 here). +- [The MCP surface is read-only: tools write artifacts, never a user's source](solutions/architecture-patterns/mcp-surface-is-read-only-artifacts-not-source.md) — no source-mutating MCP tool, period (`rename` + `remove_dead_code` removed). `readOnlyHint:false` is fine for artifact writers (scan/pack_codebase/group_sync write to `.codehub/`); `destructiveHint:true` (source mutation) is the forbidden flag. Pin the 28-name set + "no destructiveHint tool" in a server contract test. Includes the full tool-removal surface checklist. + +- [Rip a superseded never-wired subsystem — but record the capability gap](solutions/architecture-patterns/rip-superseded-subsystem-record-the-gap.md) — stack-graphs (~1,900 LOC, test-only) was sound by design but superseded by SCIP-as-oracle and orphaned by upstream archival. Rip test: dead-at-runtime + superseded + upstream-dead. Investigate intent first (docstrings, vendor README, git log, prior research notes); record the precision gap the rip leaves (non-SCIP languages lose the precision overlay) in the commit/ADR so a future session doesn't revive the dead end. + +- [Subprocess kill-escalation must not race its own exit handler](solutions/best-practices/kill-escalation-races-the-exit-handler.md) — SIGTERM→grace→SIGKILL supervisor where both the kill-grace timer and `child.on('exit')` settle one idempotent Promise with DIFFERENT reasons; the exit handler wins the race and stamps the wrong reason. Derive the reason from state (`sigkillSent` flag + the OS-reported exit `signal`), not from which timer fired. Plus: a tight timeout flaked under parallel-suite load (SIGTERM arrived mid-boot before the handler armed) — generous timeout buys boot margin; confirm flaky fixes with repeat runs UNDER load. + +- [Parallel multi-package remediation: gate fails on build artifacts, not fixes](solutions/best-practices/parallel-remediation-gate-failures-are-build-artifacts.md) — 16 agents each self-reported green; the whole-repo gate failed 5× on infrastructure (biome nested-config in sibling worktrees, mise-vs-CI heavy-package divergence, stale dist, a flaky new test) — zero on actual package fixes. Establish+read the baseline correctly, gate from a clean rebuild, triage each failure source-vs-infra by running the package in isolation, re-run flaky tests 3×, and fix pre-existing infra breakage too. + ## Specs - [001-scip-replaces-lsp](specs/001-scip-replaces-lsp/spec.md) — rip-and-replace LSP with SCIP for TS/Py/Go/Rust/Java. Task map: [tasks.md](specs/001-scip-replaces-lsp/tasks.md). diff --git a/.erpaval/solutions/architecture-patterns/exclude-heavy-build-from-pnpm-recursive.md b/.erpaval/solutions/architecture-patterns/exclude-heavy-build-from-pnpm-recursive.md index 0c0e1176..b36a8143 100644 --- a/.erpaval/solutions/architecture-patterns/exclude-heavy-build-from-pnpm-recursive.md +++ b/.erpaval/solutions/architecture-patterns/exclude-heavy-build-from-pnpm-recursive.md @@ -71,3 +71,37 @@ package's `tsconfig.json`, including a hypothetical OCH applied the pattern to: `ci.yml` typecheck + test, `release.yml` build + publish-dry-run, `och-self-scan.yml` build. `pages.yml` is the sole owner of the docs build. + +## The local-tooling corollary: `mise run check` must mirror the CI filter + +(Added 2026-05-30, session-bba601 full-repo sweep.) The original fix only +touched `.github/workflows/*`. The `mise.toml` `build` / `test` / +`typecheck` tasks kept plain `pnpm -r ` — so `mise run check` (the +documented local gate, and what `CONTRIBUTING.md` tells contributors to +run) was RED on any machine without Playwright's Chromium cached, while CI +was green. The lesson's own line "the docs build worked LOCALLY (dev had +Chromium cached)" is the trap: a *fresh* clone, a CI-like container, or a +new contributor's laptop has no cached browser, so local check silently +diverges from the merge gate. A gate that only passes on the original +author's warm machine is not a gate. + +Fix: apply the identical `--filter '!@opencodehub/docs'` exclusion to the +`mise.toml` `build`, `test`, and `typecheck` tasks, and add a dedicated +`docs:build` task that runs `playwright install chromium` first so the +docs site stays buildable locally on demand: + +```toml +[tasks."docs:build"] +depends = ["install"] +run = """ +pnpm --filter @opencodehub/docs exec playwright install chromium +pnpm --filter @opencodehub/docs build +""" +``` + +**Rule:** whenever you add a heavy-package exclusion to CI, grep the repo's +task runner (`mise.toml`, `Makefile`, `package.json` scripts, `justfile`) +for the un-excluded `-r` form in the SAME change. CI fidelity is only real +if the local one-command gate runs the same filter. See also +[[parallel-act-subagents-with-shared-git-tree]] for the stale-`dist` / +clean-rebuild discipline that surfaced alongside this during the sweep. diff --git a/.erpaval/solutions/architecture-patterns/mcp-surface-is-read-only-artifacts-not-source.md b/.erpaval/solutions/architecture-patterns/mcp-surface-is-read-only-artifacts-not-source.md new file mode 100644 index 00000000..76c5ba7a --- /dev/null +++ b/.erpaval/solutions/architecture-patterns/mcp-surface-is-read-only-artifacts-not-source.md @@ -0,0 +1,72 @@ +--- +name: "The MCP surface is read-only: tools may write artifacts, never a user's source" +description: OpenCodeHub's MCP tools must never edit a user's source files. readOnlyHint=false is allowed ONLY for tools that write derived artifacts (SARIF, code-packs, contract registries under .codehub/); destructiveHint=true (source mutation) is forbidden. The rename + remove_dead_code tools were removed under this rail. Pin the invariant in a server contract test, not just prose. +type: architecture-patterns +--- + +(2026-05-31, session-bba601.) The user's standing rule: **no source-mutating +tools on the MCP surface, period.** An AI agent talking to OpenCodeHub over +stdio MCP can read the graph, search, analyze blast radius, scan, pack — but it +cannot reach through the MCP server to rewrite the user's `.ts`/`.py` files. +Refactors are planned/verified via read-only analysis and applied by the human +(or the agent's own editor), never by an OpenCodeHub tool. + +This is distinct from Roadmap rail #2 ("stdio MCP only — no HTTP"). That rail is +about transport; this is about *capability*. Both hold simultaneously. + +## The annotation distinction that matters + +MCP tool annotations have two flags that are easy to conflate: + +- `readOnlyHint: false` — "this tool writes *something*." TRUE for artifact + writers too: `scan` (writes SARIF to `.codehub/`), `pack_codebase` (writes a + code-pack to an output dir), `group_sync` (writes a contract registry to + `~/.codehub/groups/`). These are FINE — they emit derived outputs, never edit + source. +- `destructiveHint: true` — "this tool mutates/destroys user-authored content." + This is the one the rail forbids. Only `rename` (rewrote source to apply a + symbol rename) and `remove_dead_code` (deleted source ranges via + `fs.writeFileAtomic`) carried it. Both were removed. + +So when auditing the surface, "find write tools" via `readOnlyHint: false` +OVER-captures — it flags the harmless artifact writers. The precise query for +"does any tool violate the read-only-source rail" is `destructiveHint: true`. +Grep both, then split by *what each writes* (a `.codehub/` artifact or an output +dir → keep; a path under the user's source tree → rip). + +## Pin it in a contract test, not prose + +Prose drifts. The durable guard is a server test that: +1. enumerates `_registeredTools` and asserts the exact name set (so a dropped or + re-added tool fails the assertion), and +2. asserts NO registered tool has `destructiveHint === true`, plus explicit + `assert.equal(tools["rename"], undefined)` / `remove_dead_code` undefined so + re-adding either one fails loudly. + +See `packages/mcp/src/server.test.ts` (`registers exactly the expected +read-only tool set`) and `tools/annotations.test.ts` (`no source-mutating tool +is registered; non-read-only tools only write artifacts`). + +## Removal surface (for the next "rip a tool" task) + +A tool isn't just its `tools/.ts` + registration. The full surface: +- the tool module + its `.test.ts` (rename had no own test — it was covered in + `run-smoke.test.ts` and the analysis-layer `rename.test.ts`; grep, don't assume). +- the `register*Tool` import + call in `server.ts`. +- the `run*` re-export in the package `index.ts` (public API). +- the analysis-bridge wrapper (`callRun*`) + any now-orphaned imports it pulled + (`createNodeFs`/`FsAbstraction` became unused once `callRunRename` went). +- the underlying analysis logic IF it's tool-exclusive (`analysis/rename.ts` + + its `index.ts` export + its types in `types.ts`). CHECK shared helpers first: + `analysis/dead-code.ts` stayed because `classifyDeadness` backs the surviving + read-only `list_dead_code`; `fs.ts`/`git.ts` stayed (shared with staleness + + detect-changes). Only delete what's exclusive. +- every count + inventory in docs/skills (here: 20 files — CLAUDE/AGENTS/README/ + SPECS, tool-catalog.json `tool_count`, the guide skill inventory, decision + matrices, example prompts) and any skill whose premise WAS the tool (the + `opencodehub-refactoring` skill was rename-centric → reframed to analysis-only, + not deleted, because read-only refactoring guidance is still valuable). + +Related: [[rip-superseded-subsystem-record-the-gap]] (same investigate-then-rip +discipline) and [[no-spec-coordinate-leakage-into-source]] (the count-drift +class of doc debt a tool removal creates). diff --git a/.erpaval/solutions/architecture-patterns/rip-superseded-subsystem-record-the-gap.md b/.erpaval/solutions/architecture-patterns/rip-superseded-subsystem-record-the-gap.md new file mode 100644 index 00000000..25402a8d --- /dev/null +++ b/.erpaval/solutions/architecture-patterns/rip-superseded-subsystem-record-the-gap.md @@ -0,0 +1,70 @@ +--- +name: "Rip a superseded never-wired subsystem — but record the capability gap it covered" +description: When a sophisticated subsystem is dead at runtime (zero live callers), superseded by a better-resourced alternative, AND its upstream is archived, rip it under rip-and-replace latitude — but first investigate intent, and record the precision/capability gap it was meant to close so a future session doesn't re-discover the gap and revive it. +type: architecture-patterns +--- + +During the 2026-05-30 full-repo sweep, the OCH ingestion package carried +a ~1,900 LOC `stack-graphs` name-resolution subsystem +(`providers/resolution/stack-graphs/` + `stack-graphs-{python,ts}.ts` + +`resolver-strategy.ts` + `python-all-filter.ts` + vendored `.tsg` rules) +that ran ONLY under test. `getResolver()` had zero production callers; the +live `resolve()` path in `context.ts` never dispatched through it. Four +language providers still advertised `resolverStrategyName: "stack-graphs"` +in docstrings for a path that never executed. + +## Investigate intent BEFORE ripping (don't just delete dead code) + +The reflex is "no live callers → delete." Correct outcome here, but the +*reasoning* matters and the investigation is cheap. Read: the subsystem's +header docstring (design intent), the vendored asset's README (provenance + +"known gaps"), `git log` for when/why it landed, the ADRs, and prior +`.erpaval` research notes. That investigation revealed: + +- **Intent was sound.** Stack-graphs was a planned *precision* layer to + replace the three-tier walker's lossy "global tier" (0.5 confidence, + name-only matching) with graph-theoretic name binding — LSP-quality + resolution without a stateful server. Vendored `.tsg` rules + a + clean-room TS evaluator. Roadmapped Python→TS→JS. +- **Superseded by a better-resourced bet.** The `scip-index` phase (SCIP + indexers) emits compiler-grade edges at confidence 1.0, tagged + oracle-confirmed. SCIP is strictly better for the same job, so + stack-graphs became redundant for every SCIP-covered language. +- **Upstream died.** `github/stack-graphs` was archived 2025-09-09. A + prior research session had ALREADY recorded "a dead-end dependency, do + not adopt for new work" — the repo's own durable notes contained the + verdict. + +## The rip test (all three must hold) + +1. **Dead at runtime** — zero live callers outside the subsystem + its + tests (verify with grep across `src` minus `*.test.*` minus `dist`). +2. **Superseded** — a better-resourced alternative already does the job in + production (here: SCIP-as-oracle). +3. **Upstream dead or diverged** — reviving it means maintaining a fork of + something abandoned. + +When all three hold, delete under rip-and-replace latitude: the subsystem +files, the vendored assets, the NOTICE/attribution entry, and the now-false +docstrings/`resolverStrategyName` hints in the referencing providers. Also +drop now-orphaned re-exports (`defaultResolver`, `ResolverStrategy` from the +package barrel) once grep confirms no external consumer. + +## Record the capability gap — the part everyone forgets + +Ripping a precision layer means the capability it covered is now carried by +something else (or nothing). Here: non-SCIP-covered languages (Rust has a +SCIP gap; Swift, COBOL) now fall back to the lossy name-only global tier +with NO precision overlay. That's an acceptable v1 posture (it was the +de-facto behavior anyway, since the layer never ran) — but it MUST be +written into the rip commit message / an ADR. Otherwise a future session +re-discovers "our cross-language resolution is imprecise for Rust" and +revives the exact dead-end you just removed. The gap note is the durable +artifact, not the deletion. + +Generalizes to: any "elegant but never-wired" subsystem — a second cache +tier, an alternate serializer, a pluggable-backend seam with one +implementor. Related: [[doctor-probe-drift-after-rip-and-replace]] (sweep +probes/flags/CI branches at rip time) and +[[post-deletion-promise-debt-anti-pattern]] (the inverse failure: deleting +with intent to recreate, which never happens). diff --git a/.erpaval/solutions/best-practices/kill-escalation-races-the-exit-handler.md b/.erpaval/solutions/best-practices/kill-escalation-races-the-exit-handler.md new file mode 100644 index 00000000..09881e2b --- /dev/null +++ b/.erpaval/solutions/best-practices/kill-escalation-races-the-exit-handler.md @@ -0,0 +1,69 @@ +--- +name: "Subprocess kill-escalation must not race its own exit handler for the settle reason" +description: When a supervisor times out a child, sends SIGTERM, then escalates to SIGKILL after a grace window, the child's 'exit' event and the SIGKILL-grace-timer both try to settle the Promise — and the exit handler usually wins with a less-accurate reason. Track a sigkillSent flag AND read the exit signal so the settled reason is honest regardless of which path fires first. +type: best-practices +--- + +A cobol-proleap subprocess supervisor (`superviseProcess`) timed out a +wedged JVM like this: on `timeoutMs` send SIGTERM; if no exit within +`killGraceMs`, send SIGKILL and settle with reason "...ignored SIGTERM +(SIGKILL sent)". A separate `child.on('exit')` handler also settled, with +the plainer reason "...timed out after Nms". + +A unit test installed a SIGTERM-ignoring child to force the escalation +path and asserted the reason matched `/ignored SIGTERM \(SIGKILL sent\)/`. +It **flaked**: in the gate run the child's `exit` fired (the OS delivered a +signal despite the JS `SIGTERM` handler, or the handler raced its own +install), the exit handler settled FIRST with the plain reason, and the +SIGKILL-grace branch never got to run. Actual: `timed out after 150ms`. +Expected: the SIGKILL phrasing. Red gate. + +## The shape of the bug + +Two code paths can settle one Promise and they encode DIFFERENT reasons +for the same physical event: + +```ts +const timer = setTimeout(() => { + timedOut = true; + child.kill("SIGTERM"); + killTimer = setTimeout(() => { + child.kill("SIGKILL"); + settle({ reason: "...ignored SIGTERM (SIGKILL sent)" }); // path A + }, killGraceMs); +}, timeoutMs); + +child.on("exit", () => { + if (timedOut) settle({ reason: "...timed out" }); // path B — usually wins +}); +``` + +`settle()` is idempotent (first-wins), so correctness of the *reason* +depends on a RACE. Whichever fires first stamps the reason — and path B +(exit) almost always beats path A (a `killGraceMs` timer) because the child +often does die on SIGTERM. + +## Fix: make the reason a function of state, not of which timer won + +1. Track whether SIGKILL was sent (`let sigkillSent = false;` set in path A + before `child.kill("SIGKILL")`). +2. Read the **exit signal** the OS reports: `child.on("exit", (code, signal) => …)`. + `signal === "SIGKILL"` means the kill landed even if path B settles. +3. In the exit handler, derive the reason from state: + `const killed = sigkillSent || signal === "SIGKILL";` then pick the + SIGKILL phrasing when `killed`, the plain phrasing otherwise. + +Now whichever path settles first, the reason is accurate. The test passed +3/3 in a row after the change (flaky tests need repeat-run confirmation, +not a single green). + +## Generalizes to + +Any supervise/watchdog/cancellation pattern where multiple async sources +(timeout timer, abort signal, child exit, stream error) feed one +idempotent settle and each carries its own status string. Don't let the +*winner of the race* author the outcome — compute the outcome from +accumulated state (flags + the OS-reported signal/code). Caught during the +2026-05-30 sweep's full-remediation gate; see +[[parallel-act-subagents-with-shared-git-tree]] for the surrounding +clean-rebuild discipline that made the flake reproducible. diff --git a/.erpaval/solutions/best-practices/parallel-remediation-gate-failures-are-build-artifacts.md b/.erpaval/solutions/best-practices/parallel-remediation-gate-failures-are-build-artifacts.md new file mode 100644 index 00000000..90ab3b07 --- /dev/null +++ b/.erpaval/solutions/best-practices/parallel-remediation-gate-failures-are-build-artifacts.md @@ -0,0 +1,70 @@ +--- +name: "In a parallel multi-package remediation, the gate fails on build-system artifacts, not the fixes" +description: When N agents each fix their own package in parallel on a shared tree and every agent self-reports green, the orchestrator's whole-repo gate still fails — but the failures cluster in build infrastructure (stale dist, biome nested-config in worktrees, local-vs-CI task divergence, a flaky newly-added test), not in the package fixes. Budget orchestrator time for an integration-debugging tail and gate from a clean rebuild. +type: best-practices +--- + +The 2026-05-30 sweep ran 16 package agents in parallel to fix 44 confirmed +findings (one packet per package, exclusive file ownership). Every agent +self-reported its own package building + testing green. The orchestrator's +whole-repo `mise run check` then failed FOUR times in a row — and not once +on a package's actual fix. The failures were all integration/build-system +artifacts: + +1. **Biome v2 nested-config collision.** Locked git worktrees for OTHER + branches under `.claude/worktrees/*` each carry a root `biome.json`; + `biome check .` walks into them and dies on "nested root configuration." + Pre-existing (the BASELINE check failed for this too) but invisible until + a whole-repo lint ran. Fix: `vcs.useIgnoreFile: true` + a negated include + `"!!**/.claude/worktrees"` (NOT the deprecated `experimentalScannerIgnores`, + which runs after config discovery and so doesn't prevent the collision). + See [[worktree-isolation-pwd-pin-and-biome-exclusion]]. + +2. **Local gate diverged from CI.** `mise.toml` used plain `pnpm -r build` + while CI used `pnpm --filter '!@opencodehub/docs' -r build` (the heavy + Playwright/Chromium docs package). So `mise run check` was red on any + machine without a cached browser. See the local-corollary section of + [[exclude-heavy-build-from-pnpm-recursive]]. + +3. **Stale `dist` + phantom test counts.** Agents edited source and ran + their own incremental builds; the gate's `tsc -b` + mise `sources/outputs` + caching produced a `dist` that disagreed with source. A test that passed + in isolation failed in the gate (and vice versa). Only a clean + `pnpm -r run clean && build` gave a deterministic answer. See + [[parallel-act-subagents-with-shared-git-tree]]. + +4. **A newly-added test was flaky.** An agent's own new SIGKILL-escalation + test raced (see [[kill-escalation-races-the-exit-handler]]). Self-reported + green because the agent ran it once on warm dist; failed under the gate's + ordering. Flaky tests need REPEAT-RUN confirmation (3×), not one green. + +## Why this happens + +Each agent's world is its own package on a warm, already-built tree. Three +classes of truth live OUTSIDE any single package's view and so no agent can +see them: (a) cross-package build ordering and dist freshness, (b) +repo-root tooling config (biome, mise, tsconfig references), (c) +whole-repo-only checks (lint `.`, the negated-filter gate). A green +self-report is necessary but not sufficient. + +## How to apply (orchestrator discipline) + +- **Establish the baseline gate result BEFORE dispatching agents** and read + it correctly — a sub-command's "exit 0" (e.g. install) is not the whole + check's exit. The baseline here was already red; knowing that reframes + every later failure as "pre-existing or mine?" instantly. See + [[squash-merge-masks-pre-existing-debt]]. +- **Gate from a clean rebuild**, not incremental, for the authoritative + pass: `pnpm -r run clean` then `mise run check`. Stale dist is the single + most common false signal. +- **Budget an integration-debugging tail.** After "all agents reported + done," expect several gate iterations of pure build-system triage. This is + normal, not a sign the fixes were bad. +- **Triage each gate failure to source-vs-infrastructure first.** Run the + failing package's test in isolation: passes alone but fails in gate → + infrastructure (stale dist / config / ordering); fails alone too → a real + fix bug. This one question routes the fix. +- **Re-run flaky-looking tests 3×** before declaring them fixed. +- **Fix the pre-existing infra breakage too** (biome worktree, mise/CI + divergence) rather than working around it — those were latent gate-fidelity + bugs that would bite the next contributor. diff --git a/.gitignore b/.gitignore index a7961f76..55cf636f 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,7 @@ examples/fixtures/**/.codehub/ .claude/worktrees/ .handoff/ .claude/skills/generated/ + +# Release artifact — regenerated by cdxgen in release.yml; never committed. +# A stale committed copy poisons the local OSV scan (scans the whole tree). +SBOM.cdx.json diff --git a/AGENTS.md b/AGENTS.md index bc86bf5b..e86121e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,6 @@ tiers. - `context` — inbound/outbound refs and participating flows for one symbol. - `impact` — dependents of a target up to a configurable depth, with a risk tier. - `detect_changes` — map an uncommitted or committed diff to affected symbols. -- `rename` — graph-assisted multi-file rename; dry-run is the default. - `sql` — read-only SQL against the local temporal store (the `cochanges` and `symbol_summaries` tables), 5 s timeout. The node/edge graph lives in `graph.lbug` (ADR 0016) and is reached via the typed tools (`query`/`context`/`impact`) or Cypher via the MCP `sql` tool's `cypher` arg — NOT via this SQL path. Run `codehub analyze` after pulling new commits so the index stays aligned @@ -21,8 +20,8 @@ with the working tree. `codehub status` reports staleness. ## Full MCP surface -The full MCP surface is **29 tools** (see `packages/mcp/src/server.ts`); -the 7 listed above are the high-frequency exploration tools. For the +The full MCP surface is **28 tools** (see `packages/mcp/src/server.ts`); +the 6 listed above are the high-frequency exploration tools. For the full inventory, use the `/opencodehub-guide` skill. ## AMBIGUOUS_REPO @@ -79,7 +78,7 @@ making graph-index or retrieval changes. ## Claude Code plugin This repo ships a Claude Code plugin at `plugins/opencodehub/` — it -provides a `code-analyst` subagent and 10 skills. Install via +provides a `code-analyst` subagent and 11 skills. Install via `codehub init` (writes `.mcp.json` + links the plugin). ## Storage backend — lbug graph + DuckDB temporal diff --git a/CLAUDE.md b/CLAUDE.md index e781f027..13bd0f1e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,6 @@ tiers. - `context` — inbound/outbound refs and participating flows for one symbol. - `impact` — dependents of a target up to a configurable depth, with a risk tier. - `detect_changes` — map an uncommitted or committed diff to affected symbols. -- `rename` — graph-assisted multi-file rename; dry-run is the default. - `sql` — read-only SQL against the local temporal store (the `cochanges` and `symbol_summaries` tables), 5 s timeout. The node/edge graph lives in `graph.lbug` (ADR 0016) and is reached via the typed tools (`query`/`context`/`impact`) or Cypher via the MCP `sql` tool's `cypher` arg — NOT via this SQL path. Run `codehub analyze` after pulling new commits so the index stays aligned @@ -19,7 +18,7 @@ with the working tree. `codehub status` reports staleness. ## Full MCP surface The full MCP surface is **28 tools** (see `packages/mcp/src/server.ts`); -the 7 listed above are the high-frequency exploration tools. For the +the 6 listed above are the high-frequency exploration tools. For the full inventory, use the `/opencodehub-guide` skill. ## AMBIGUOUS_REPO @@ -77,7 +76,7 @@ making graph-index or retrieval changes. ## Claude Code plugin This repo ships a Claude Code plugin at `plugins/opencodehub/` — it -provides a `code-analyst` subagent and 10 skills. Install via +provides a `code-analyst` subagent and 11 skills. Install via `codehub init` (writes `.mcp.json` + links the plugin). ## Storage backend — lbug graph + DuckDB temporal diff --git a/NOTICE b/NOTICE index 47d20b99..388d732b 100644 --- a/NOTICE +++ b/NOTICE @@ -14,16 +14,3 @@ BSD-2-Clause, BSD-3-Clause, 0BSD, CC0-1.0, BlueOak-1.0.0, Python-2.0). Two SPDX license-data packages (spdx-exceptions, spdx-ranges) include CC-BY-3.0 attribution for the license list data. Attribution is preserved unmodified in the installed package metadata. - ----- - -This product vendors tree-sitter-stack-graphs Python rules from: - - github/stack-graphs (MIT OR Apache-2.0) - https://github.com/github/stack-graphs - Pinned at commit fcb7705d5b38ae13b3665a9b2c882e5a97243d44 - Vendored under vendor/stack-graphs-python/ (rule data only, no Rust evaluator code) - -The TypeScript rule evaluator that consumes these .tsg files is an -independent clean-room implementation authored for OpenCodeHub -(packages/ingestion/src/providers/resolution/stack-graphs/). diff --git a/OBJECTIVES.md b/OBJECTIVES.md index 6118a9dc..f6784b00 100644 --- a/OBJECTIVES.md +++ b/OBJECTIVES.md @@ -1,82 +1,73 @@ # OBJECTIVES.md — OpenCodeHub -A concise set of project objectives inferred from the repo. What success -looks like, where the quality bar sits, and what is deliberately out of -scope. +Project objectives inferred from the repo: what success looks like, where the +quality bar sits, and what is deliberately out of scope. ## Primary objectives -1. **Give AI coding agents graph-aware code context in one MCP tool - call.** *Because the README's problem statement is exactly this: - grep is textual, language servers are per-file, embeddings are - lossy; agents need callers, callees, processes, and blast radius - answered before they write a diff, and the 29-tool MCP surface is - the primary product.* +1. **Answer graph questions in one MCP call.** AI coding agents need callers, + callees, processes, and blast radius before they write a diff. Grep is + textual. Language servers work one file at a time. Embeddings lose + precision. OpenCodeHub answers all four from a persisted graph, and the + 28-tool MCP surface is the primary product. -2. **Stay Apache-2.0 end-to-end, with every transitive runtime - dependency on the permissive allowlist.** *Because CI already - enforces the allowlist (Apache-2.0 / MIT / BSD / ISC / CC0 / - BlueOak / 0BSD), scanner license incompatibilities (hadolint GPL, - tflint MPL/BUSL) are resolved by subprocess-only invocation, and the - README explicitly frames this as a fork-and-embed posture.* +2. **Stay Apache-2.0 across every runtime dependency.** Each transitive + dependency sits on the permissive allowlist: Apache-2.0, MIT, BSD, ISC, + CC0, BlueOak, 0BSD. CI enforces it on every push. Scanners with + incompatible licenses never link into the host. hadolint (GPL) and tflint + (MPL/BUSL) run as subprocesses instead. The README frames this as a + fork-and-embed posture. -3. **Keep the index local, offline-capable, and deterministic.** - *Because `--offline` is asserted to open zero sockets, `graphHash` - must be byte-identical across full and incremental runs at the same - commit, and `scripts/acceptance.sh` gate 6 gates on exactly that - invariant.* +3. **Keep the index local, offline, and deterministic.** `codehub analyze + --offline` opens zero sockets. The `graphHash` is byte-identical whether + you run a full or an incremental index at the same commit. Acceptance gate + 6 checks that invariant on every run. -4. **Cover the 15 GA languages (14 via tree-sitter plus a regex - provider for fixed-format COBOL) and upgrade five of them - (TypeScript, Python, Go, Rust, Java) with SCIP indexers.** - *Because heuristic call-graph edges miss cross-module resolution, - the `scip-index` phase runs each language's native SCIP indexer - once, the `confidence-demote` phase reconciles heuristic and - compiler-grade edges, and the gym harness (extracted to a sibling - testbed in M5) gates per-language F1 with SCIP-derived baselines.* +4. **Cover 15 languages; deepen five with SCIP.** Fourteen parse through + tree-sitter, and a regex provider handles fixed-format COBOL. Five + (TypeScript, Python, Go, Rust, Java) also run a native SCIP indexer. + Heuristic call edges miss cross-module resolution. So the `scip-index` + phase indexes each language once, and the `confidence-demote` phase + reconciles heuristic edges against compiler-grade ones. A sibling testbed + repo holds the gym that gates per-language F1 against SCIP baselines. ## Quality bar -5. **Hold a three-layer regression gate on every eval and gym run.** - *Because the sibling testbed's absolute-F1-floor + relative-F1-delta - + per-case non-regression layering is baked into the harness, and - acceptance gate 9 requires ≥ 40/49 Python-eval cases to pass — soft - regressions are not an option.* +5. **Gate every eval and gym run in three layers.** The testbed checks an + absolute F1 floor, a relative F1 delta, and per-case non-regression. + Acceptance gate 9 requires at least 40 of 49 Python-eval cases to pass. A + soft regression fails the run. -6. **Fail CI on any non-zero exit.** *Because `pnpm run check` chains - lint → typecheck → test → banned-strings and exits on first - failure, CI runs the same chain plus OSV, CodeQL, Scorecard, SARIF - schema validation, commitlint, and license allowlist — with the - banned-strings sweep enforcing clean-room IP hygiene.* +6. **Fail CI on any non-zero exit.** `pnpm run check` chains lint, typecheck, + test, and banned-strings, stopping at the first failure. CI runs the same + chain plus OSV, CodeQL, Scorecard, SARIF schema validation, commitlint, and + the license allowlist. The banned-strings sweep enforces clean-room IP + hygiene. -7. **Keep the MCP server responses structured, versioned, and - self-describing.** *Because every tool must return a `next_steps` - array, a `_meta["codehub/staleness"]` envelope when the index lags - HEAD, and a typed error code (e.g. `AMBIGUOUS_REPO`) rather than - free-form failure strings — this is what makes the tools safe for - automated agent loops.* +7. **Keep MCP responses structured, versioned, and self-describing.** Every + tool returns a `next_steps` array. When the index lags HEAD, it adds a + `_meta["codehub/staleness"]` envelope. Failures carry a typed code such as + `AMBIGUOUS_REPO`, never a free-form string. That shape is what makes the + tools safe inside automated agent loops. ## Non-goals -8. **Do not operate a server or SaaS.** *Because DuckDB is embedded, - the MCP server is a stdio process, and ADR 0001 explicitly rejects - engines that would require a daemon; the product is a CLI + MCP - server, not a hosted product.* +8. **Do not operate a server or SaaS.** DuckDB is embedded. The MCP server is + a stdio process. ADR 0001 rejects any engine that would need a daemon. The + product ships as a CLI plus an MCP server, nothing hosted. -9. **Do not port to Rust before it's needed.** *Because ADR 0002 - measured p95 single-file incremental at 195-250ms on the 100-file - fixture (well under the 1s hard gate) and extrapolates cold 100k-LOC - analyze to 3-5 seconds — Rust is deferred to v2.1+ with explicit - re-trigger conditions tied to measured latency on user repos.* +9. **Do not port to Rust before it is needed.** ADR 0002 measured p95 + single-file incremental analysis at 195-250ms on the 100-file fixture, well + under the 1s gate. It projects a cold 100k-LOC analyze at 3 to 5 seconds. + Rust is deferred to v2.1 or later. The re-trigger conditions are tied to + measured latency on real user repos. ## Measurable outcomes -10. **A user can go from `git clone` to a wired MCP server to their - agent calling `impact`, `detect_changes`, and `rename` on a real - repo in a single quick-start sequence (`mise install` → - `pnpm install --frozen-lockfile` → `codehub setup` → - `codehub analyze`), with acceptance gates 1-8 confirming the happy - path and gates 10-15 confirming the opt-in paths (embeddings, - scanners, SARIF validation, license audit, verdict).** *Because - that end-to-end flow is what the README promises and what - `scripts/acceptance.sh` verifies; everything else rolls up to it.* +10. **Get from clone to a working agent in one sequence.** A user runs `mise + install`, then `pnpm install --frozen-lockfile`, then `codehub setup`, + then `codehub analyze`. After that, their agent can call `impact`, + `detect_changes`, and `context` on a real repo. Acceptance gates 1-8 + confirm this happy path. Gates 10-15 confirm the opt-in paths for + embeddings, scanners, SARIF validation, license audit, and verdict. The + README promises this flow. `scripts/acceptance.sh` verifies it. diff --git a/README.md b/README.md index 6c8627f8..64872e5f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ npm install -g @opencodehub/cli cd /path/to/your/repo codehub init && codehub analyze -# your agent now has impact, query, context, detect_changes, rename — 29 tools over MCP +# your agent now has impact, query, context, detect_changes — 28 tools over MCP ``` ## Why this exists @@ -67,7 +67,7 @@ flowchart LR C -->|detect communities + flows| E[Processes / clusters] D --> F[MCP server] E --> F - F -->|29 tools| G[AI coding agent] + F -->|28 tools| G[AI coding agent] ``` ## Design choices worth knowing @@ -109,12 +109,12 @@ codehub init # index the repo (WASM parser, no native binaries needed) codehub analyze -# your agent can now call impact, query, context, detect_changes, rename, … +# your agent can now call impact, query, context, detect_changes, … ``` ### Build from source -**Requirements:** Node 22 or 24; pnpm 10+; Python 3.12 (only needed for +**Requirements:** Node 22 or 24; pnpm 11+; Python 3.12 (only needed for SCIP indexers on Python-heavy repos); `mise` recommended. ```bash @@ -126,20 +126,20 @@ pnpm run check # lint + typecheck + test + banned-strings mise run cli:link # puts `codehub` on your PATH ``` -## MCP tool surface (29 tools) +## MCP tool surface (28 tools) | Tool | Purpose | |---|---| | `query` | Process-grouped code intelligence — execution flows related to a concept | | `context` | 360-degree symbol view — callers, callees, processes, ACCESSES edges | +| `signature` | Symbol declaration + stubbed members — class/interface header with method & property signatures, bodies elided | | `impact` | Blast radius — what breaks at depth 1/2/3 with confidence + risk tier | | `detect_changes` | Git-diff impact — what do your current changes affect | -| `rename` | Multi-file coordinated rename with confidence-tagged edits | | `route_map` / `api_impact` / `shape_check` / `tool_map` | HTTP route & MCP tool intelligence | | `group_query` / `group_status` / `group_contracts` / `group_cross_repo_links` / `group_sync` / `group_list` | Cross-repo federation — fan out BM25, contracts, and staleness across a named group | | `list_repos` · `sql` | Registry & escape-hatch SQL (read-only, timeout-guarded) | | `pack_codebase` | Deterministic Repomix-compatible code pack export | -| …and the rest | `verdict`, `risk_trends`, `project_profile`, `dependencies`, `license_audit`, `owners`, `list_findings`, `list_findings_delta`, `list_dead_code`, `remove_dead_code`, `scan` | +| …and the rest | `verdict`, `risk_trends`, `project_profile`, `dependencies`, `license_audit`, `owners`, `list_findings`, `list_findings_delta`, `list_dead_code`, `scan` | Architecture decision records live in [`docs/adr/`](./docs/adr/). A Claude Code plugin at `plugins/opencodehub/` wraps the MCP tools into @@ -147,7 +147,7 @@ skills + a code-analyst subagent — install via `codehub init`. ## Repository layout -The monorepo is organised as 17 workspace packages under `packages/`: +The monorepo is organised as 18 workspace packages under `packages/`: | Package | Purpose | |---|---| @@ -158,11 +158,11 @@ The monorepo is organised as 17 workspace packages under `packages/`: | `embedder` | Embedding backends — local ONNX, HTTP, SageMaker; deterministic `embedderId` fingerprint | | `frameworks` | HTTP route + MCP tool detectors used by `route_map` / `api_impact` / `tool_map` | | `ingestion` | Tree-sitter + WASM parsers, symbol extraction, import resolution, complexity phase | -| `mcp` | Model Context Protocol server — 29 tools, resources, structured error envelopes | +| `mcp` | Model Context Protocol server — 28 tools, resources, structured error envelopes | | `pack` | Deterministic Repomix-compatible code-pack generator (M5) | | `policy` | Allowlist + license-tier policy engine driving `license_audit` and CI gates | | `sarif` | SARIF schema validation and scanner output normalisation | -| `scanners` | Subprocess wrappers for 20 scanners — OSV, Semgrep, hadolint, tflint, detect-secrets, and the rest | +| `scanners` | Subprocess wrappers for 19 scanners — OSV, Semgrep, hadolint, tflint, betterleaks, and the rest | | `scip-ingest` | SCIP indexer runners (TS, Python, Go, Rust, Java) — emits CALLS, REFERENCES, IMPLEMENTS, TYPE_OF | | `search` | Hybrid BM25 + HNSW (ACORN-1 + RaBitQ) query layer | | `storage` | `IGraphStore` (`@ladybugdb/core`) + `ITemporalStore` (DuckDB) adapters; deterministic `graphHash` | @@ -171,8 +171,8 @@ The monorepo is organised as 17 workspace packages under `packages/`: The retrieval / graph-quality evaluation harness and the per-language F1 regression gym used to live here as `eval` and `gym`; they were -extracted into a sibling testbed in M5 so the production package set -ships free of test-time dependencies. +extracted into the sibling `opencodehub-testbed` repository so the +production package set ships free of test-time dependencies. ## Embedding backends @@ -250,8 +250,8 @@ superseded. ## Status **v1 — feature-complete on M1–M7.** Tracks A (M7 graph-DB default + the -`IGraphStore` / `ITemporalStore` interface segregation), B (20-scanner -fleet incl. detect-secrets), C (debt sweep — embedder fingerprint, SCIP +`IGraphStore` / `ITemporalStore` interface segregation), B (19-scanner +fleet incl. betterleaks), C (debt sweep — embedder fingerprint, SCIP REFERENCES + TYPE_OF), and D (dogfood polish) have all merged. The current shipped tag remains `0.1.1`; `1.0.0` is cut once schema + tool-surface stability is signed off. diff --git a/SBOM.cdx.json b/SBOM.cdx.json deleted file mode 100644 index 76f40af4..00000000 --- a/SBOM.cdx.json +++ /dev/null @@ -1 +0,0 @@ -{"bomFormat":"CycloneDX","specVersion":"1.5","serialNumber":"urn:uuid:5a478f3f-c7c4-436a-b39e-10827e1eb5f5","version":1,"metadata":{"timestamp":"2026-04-23T00:09:06Z","tools":{"components":[{"group":"@cyclonedx","name":"cdxgen","version":"11.11.0","purl":"pkg:npm/%40cyclonedx/cdxgen@11.11.0","type":"application","bom-ref":"pkg:npm/@cyclonedx/cdxgen@11.11.0","author":"OWASP Foundation","publisher":"OWASP Foundation"}]},"authors":[{"name":"OWASP Foundation"}],"lifecycles":[{"phase":"build"}],"component":{"name":"opencodehub-monorepo","group":"","version":"0.1.1","description":"OpenCodeHub — Apache-2.0 code intelligence graph + MCP server for AI coding agents","purl":"pkg:npm/opencodehub-monorepo@0.1.1","bom-ref":"pkg:npm/opencodehub-monorepo@0.1.1","type":"application","components":[{"group":"@opencodehub","name":"analysis","version":"0.1.0","purl":"pkg:npm/%40opencodehub/analysis@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/analysis@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/analysis/package.json"},{"name":"internal:virtual_path","value":"packages/analysis"}]},{"group":"@opencodehub","name":"cli","version":"0.1.0","purl":"pkg:npm/%40opencodehub/cli@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/cli@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/cli/package.json"},{"name":"internal:virtual_path","value":"packages/cli"}]},{"group":"@opencodehub","name":"core-types","version":"0.1.0","purl":"pkg:npm/%40opencodehub/core-types@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/core-types@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/core-types/package.json"},{"name":"internal:virtual_path","value":"packages/core-types"}]},{"group":"@opencodehub","name":"embedder","version":"0.1.0","purl":"pkg:npm/%40opencodehub/embedder@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/embedder@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/embedder/package.json"},{"name":"internal:virtual_path","value":"packages/embedder"}]},{"group":"@opencodehub","name":"ingestion","version":"0.1.0","purl":"pkg:npm/%40opencodehub/ingestion@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/ingestion@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/ingestion/package.json"},{"name":"internal:virtual_path","value":"packages/ingestion"}]},{"group":"@opencodehub","name":"mcp","version":"0.1.0","purl":"pkg:npm/%40opencodehub/mcp@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/mcp@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/mcp/package.json"},{"name":"internal:virtual_path","value":"packages/mcp"}]},{"group":"@opencodehub","name":"sarif","version":"0.1.0","purl":"pkg:npm/%40opencodehub/sarif@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/sarif@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/sarif/package.json"},{"name":"internal:virtual_path","value":"packages/sarif"}]},{"group":"@opencodehub","name":"scanners","version":"0.1.0","purl":"pkg:npm/%40opencodehub/scanners@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/scanners@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/scanners/package.json"},{"name":"internal:virtual_path","value":"packages/scanners"}]},{"group":"@opencodehub","name":"search","version":"0.1.0","purl":"pkg:npm/%40opencodehub/search@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/search@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/search/package.json"},{"name":"internal:virtual_path","value":"packages/search"}]},{"group":"@opencodehub","name":"storage","version":"0.1.0","purl":"pkg:npm/%40opencodehub/storage@0.1.0","type":"application","bom-ref":"pkg:npm/@opencodehub/storage@0.1.0","properties":[{"name":"internal:is_workspace","value":"true"},{"name":"SrcFile","value":"packages/storage/package.json"},{"name":"internal:virtual_path","value":"packages/storage"}]}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}]},"properties":[{"name":"cdx:bom:componentTypes","value":"npm"},{"name":"cdx:bom:componentNamespaces","value":"@apidevtools\\n@arcanis\\n@babel\\n@biomejs\\n@colors\\n@commitlint\\n@conventional-changelog\\n@cyclonedx\\n@duckdb\\n@graphty\\n@homebridge\\n@hono\\n@huggingface\\n@iarna\\n@inquirer\\n@isaacs\\n@kwsites\\n@modelcontextprotocol\\n@napi-rs\\n@nodable\\n@nodelib\\n@npmcli\\n@pinojs\\n@pkgjs\\n@pnpm\\n@sec-ant\\n@simple-git\\n@simple-libs\\n@sindresorhus\\n@snyk\\n@szmarczak\\n@types\\n@yarnpkg"},{"name":"cdx:bom:componentSrcFiles","value":"pnpm-lock.yaml"}]},"components":[{"group":"@apidevtools","name":"json-schema-ref-parser","version":"14.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"39cf7acef9b1c757eaa1211d5267e6bef6f9f7f2833a752827bb36b7b6c84b2027d17133e7b2c20b0f24d98e0f7f79b029a64b31c8844802ce44b81f7b67eb0b"}],"purl":"pkg:npm/%40apidevtools/json-schema-ref-parser@14.0.1","type":"library","bom-ref":"pkg:npm/@apidevtools/json-schema-ref-parser@14.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@apidevtools","name":"openapi-schemas","version":"2.1.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"65cd4096aac9957dd2969ba9146a622e2d846ed7b23fb7d7994386ba9ebf0e79118233fd6e030cfda83e9fdd6bb2fd14d46a73d07dd520b03fa376d6ed46ba05"}],"purl":"pkg:npm/%40apidevtools/openapi-schemas@2.1.0","type":"library","bom-ref":"pkg:npm/@apidevtools/openapi-schemas@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@apidevtools","name":"swagger-methods","version":"3.0.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"400903e642b66f559f8c34bf5109ffa9091bc05df5baa4633d3aec0ace591bd050180923c2fa861638cfa80bb369cfc8633a4fb51ce308fd56ad3b8022332b5e"}],"purl":"pkg:npm/%40apidevtools/swagger-methods@3.0.2","type":"library","bom-ref":"pkg:npm/@apidevtools/swagger-methods@3.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@apidevtools","name":"swagger-parser","version":"12.1.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"7b9989a2cc2c9c05f48c6f89d3dc471584176ff6d47394b7a4ba4cc54b91500d87f13da49e2df2128cb3d91dc396dc397f803a8faacf34ca564cafa25431659e"}],"purl":"pkg:npm/%40apidevtools/swagger-parser@12.1.0","type":"library","bom-ref":"pkg:npm/@apidevtools/swagger-parser@12.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@arcanis","name":"slice-ansi","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"c60b8fd964760efd2043b6246dd6fb04d0a73e720f07de2e4e2d19d8dbe444112785bc23390ed0c902895eb550838a83a620fd840e65e5c0b0cbfcf639781cdf"}],"purl":"pkg:npm/%40arcanis/slice-ansi@1.1.1","type":"library","bom-ref":"pkg:npm/@arcanis/slice-ansi@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@babel","name":"code-frame","version":"7.29.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"f4d8427988eaf7edeec6076da76d0b4a225726f37415e0ad346a49c6e3056752afddc59435be833a127052f7150b328647ae9cc389e3c0cea1ac92ea80ec1b73"}],"purl":"pkg:npm/%40babel/code-frame@7.29.0","type":"library","bom-ref":"pkg:npm/@babel/code-frame@7.29.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@babel","name":"helper-validator-identifier","version":"7.28.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"a92b3889fc33289495dfdb9c363b2f73a5951ece9bed2d37b0e87639c1c5f541df54fa965802d4b0d515ce1481888b63459a0b1f1ee721aad58ea295bac519d5"}],"purl":"pkg:npm/%40babel/helper-validator-identifier@7.28.5","type":"library","bom-ref":"pkg:npm/@babel/helper-validator-identifier@7.28.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"biome","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"46ba3b69d425dcd2eaff324220bf7c784957288f1e12206da1ebb94db5c5fd4dea6e3b9273b25be6b8d46de1c772ab8359e49fdc79c63fb5c8e6a1ab95193fa4"}],"purl":"pkg:npm/%40biomejs/biome@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/biome@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-darwin-arm64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"067314e0f737722115b5e56967404adf730bafb5f9ec5e70d5dc032c39fefe2cbfc93ac0e10fcddb27ed89d16db00e2fac387414c5c3a5a70d57f4e5ddf6e69e"}],"purl":"pkg:npm/%40biomejs/cli-darwin-arm64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-darwin-arm64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-darwin-x64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"c7db89d1b235ac9b16202a77547f30ff93e70150f703b4aacc3a5badfa14417d50c96acae63494e1f44ba3fc128067a99428af6f10512a6b795eae3f9d6beaf0"}],"purl":"pkg:npm/%40biomejs/cli-darwin-x64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-darwin-x64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-linux-arm64-musl","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"1617e990002528bea4c2f7156a9d07829e008599ad7776089a0d242b58ddec2fda4a887849fb01d9ffbec86d6b53496bf18e4c08526b712926b2c88bf579e78a"}],"purl":"pkg:npm/%40biomejs/cli-linux-arm64-musl@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-linux-arm64-musl@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"},{"name":"cdx:pnpm:libc","value":"musl"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-linux-arm64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"b4ec2e0ae659b4a8b58d5cdb93fe675e622cce2381eb2a8df1cf6bf5033410960f53a0e94167f5d6e04e4827c528a3381f777d6686abbe580c7db72e0f4e753f"}],"purl":"pkg:npm/%40biomejs/cli-linux-arm64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-linux-arm64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-linux-x64-musl","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"7704c8819ac6badce19100aebc7ca70a4c96ea127152ec99a8a28ed1835f692d8652844efad3afc5766a641e9291600e75f653cf06b0f0811d5272241ca1e87b"}],"purl":"pkg:npm/%40biomejs/cli-linux-x64-musl@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-linux-x64-musl@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"},{"name":"cdx:pnpm:libc","value":"musl"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-linux-x64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"f2915e0272d4f50756f63088b2507fbfcd9b234961066cf664029cf2954c14f3b4b74c001eca0492b51051b324228ad34488dbab23591c0de51176afb0f5984b"}],"purl":"pkg:npm/%40biomejs/cli-linux-x64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-linux-x64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-win32-arm64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"0740cb9f1d2f03dc9aff7bfb5f209a3fefe50a99db59b30e7d4145bdefb16f93b1c98bdd1da4b9e58b1275daf6dbc63e240164e7c6a00ae653b2a362c366ba8a"}],"purl":"pkg:npm/%40biomejs/cli-win32-arm64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-win32-arm64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@biomejs","name":"cli-win32-x64","version":"2.4.12","scope":"optional","hashes":[{"alg":"SHA-512","content":"c8c7244734f267cde1924f220c55acc2a49d53cb6f671b2924a9d8361ec966bff385934e9731f5de4e1e71ba14e8cbab284c427b61d4907ef9a4623f3b627018"}],"purl":"pkg:npm/%40biomejs/cli-win32-x64@2.4.12","type":"library","bom-ref":"pkg:npm/@biomejs/cli-win32-x64@2.4.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@colors","name":"colors","version":"1.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"a28582ae564fd758bc1889928d31d81cb92f1433f8f274b8fb6d389c66f54625ff59760798903620823dfded8359569b08449d5bb841004cc746a527f4e515bd"}],"purl":"pkg:npm/%40colors/colors@1.5.0","type":"library","bom-ref":"pkg:npm/@colors/colors@1.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"cli","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"c8d93237fb6e293252df07557ec676b570cce06e068bbcfe8d6e780a48bc37cb59ab028196d6c8bd452b49b4f8873d5b856fe1d02751fb9b024a95c3fb030a69"}],"purl":"pkg:npm/%40commitlint/cli@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/cli@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"config-conventional","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"b77362f3cac5c355cc6b89d91e038a27c7c8013f4cda3e539cac93a89cecc5e6bb1547ad94d75816eb3d773f8c84845999cd7a3f43cfc847e1e97d9dfeac3c48"}],"purl":"pkg:npm/%40commitlint/config-conventional@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/config-conventional@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"config-validator","version":"19.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"087b63f761f9add84ab75ed19a000b85f42df7955acab528dad4aa63d836c3e95a017ca4ecafc47fab8f9bdb67e6a488c129ab2e329a5caf5e88dbb1990ac307"}],"purl":"pkg:npm/%40commitlint/config-validator@19.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/config-validator@19.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"config-validator","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"4ff521ea2254cf2c7b8f7e4698759d222191401fa8b990e4d29c00698ab8497801e78299845749d2f626c625ba00c608093216bb23310c19758caef8a05a7f1b"}],"purl":"pkg:npm/%40commitlint/config-validator@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/config-validator@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"ensure","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"2291ea0147ac05e5b51030dd8f325e68ec54f6d9e880b0325cb4419f4dd21e58f548610d9f624666a4961a416f049909cdf5c0b8236db286336b1f993d2fa9bb"}],"purl":"pkg:npm/%40commitlint/ensure@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/ensure@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"execute-rule","version":"19.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"6aac86832b57865d9e8e593efeb7e0b70a4f7b1632ae2e2df3f9f892eeab449a118466692c532aad9f986ae6de1b2b023faa33e26300df86124da48e28478dae"}],"purl":"pkg:npm/%40commitlint/execute-rule@19.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/execute-rule@19.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"execute-rule","version":"20.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"c720a83928683ee3cbe388156bee44759b0155aa3fa4dce9421933ab745db6515d299b6359c2e550540749606e864e6eb4a63291e24f4b3da2f00047400f9967"}],"purl":"pkg:npm/%40commitlint/execute-rule@20.0.0","type":"library","bom-ref":"pkg:npm/@commitlint/execute-rule@20.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"format","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"4c8f44c0553fa995922bb6b9ab25cca4a3cfbf7aad6bb14ee2d296f96b766a5eec80c6cb593b00703a57d5c53c935e9345db2289eb7d68ec34ce9bd15cd26eed"}],"purl":"pkg:npm/%40commitlint/format@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/format@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"is-ignored","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"2562daac0b2eac72613e8cdbb801fa19b3f8a7f85d382a2a4bdcc931fab0b309defbf18fb39574fabaec7ce90febc63c3d22e9870b455d5d04cc9f864d74d31a"}],"purl":"pkg:npm/%40commitlint/is-ignored@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/is-ignored@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"lint","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"8e233784d51dbb4e231417f55603dd8ed20fbdbb957c34c101ce8bf7c0167282e3179b18aa4ba50470739555a5978acc1754f9cde40507aafffdafacf8104a94"}],"purl":"pkg:npm/%40commitlint/lint@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/lint@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"load","version":"19.6.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"904e2644a596363bb6429b025ade36f1706f507e793844f637840a4346c5f39a92fd76ec4461b53224c1c83365115a443dc78c903af8e8b347f790ed47072c7c"}],"purl":"pkg:npm/%40commitlint/load@19.6.1","type":"library","bom-ref":"pkg:npm/@commitlint/load@19.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"load","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"b0b8616132ff2b178e4d98e369b28387089d1996a7f385ca2b5f97164c0360bff8f3cde421a8dccff75914084126664fb4bf3e9c1c7a6e793303de58c4f7833f"}],"purl":"pkg:npm/%40commitlint/load@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/load@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"message","version":"20.4.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"e9a930098af372b15c4d8cfd1b251a5a5862b18e25990dcabeb9da6e686878057c9d11f8757240878f84510dee02bb71c4a424bf1252efc84d5f6114dd44a0c5"}],"purl":"pkg:npm/%40commitlint/message@20.4.3","type":"library","bom-ref":"pkg:npm/@commitlint/message@20.4.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"parse","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"49e2961c1324ed83939e7116521c7e7756bdbc7b238eea3a528d7145f3cd7de6386dd6056ac087d5d0e902fd772f29fe7433ce7a5b3e8cfe83d86459ab37397c"}],"purl":"pkg:npm/%40commitlint/parse@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/parse@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"read","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"243108276f869d6a4af10ab07e65bb3b8da1d1ac9c24458da9c764267cb32c3d759dff5d5b67562d35446bc5ad968e0864518b3c04e34799de0394253af207d7"}],"purl":"pkg:npm/%40commitlint/read@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/read@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"resolve-extends","version":"19.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"094fc6b1c661094b09c1c293252f4d761dc028665334520ea100769fc0a61678b313456712e268ba6f82396f82d653400447aa93ab2c7dcd4a91a966e1b0e494"}],"purl":"pkg:npm/%40commitlint/resolve-extends@19.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/resolve-extends@19.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"resolve-extends","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"dd21cf5945b6bf4b72b2909371f4ac62697482c7acf7697a4e5a20c33bccd9c6f10e09a149173e7e57438ef1a409726b8d233cec105a5984cf5b0c32012651ae"}],"purl":"pkg:npm/%40commitlint/resolve-extends@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/resolve-extends@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"rules","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"e4d7505d011d9c33d3e692bc3b7f5903b1e88733d11c4b03194db773254234f432e167a001b030ad09379c8bbba76b25dddbad3e4f1165df75c8a4eb3139d191"}],"purl":"pkg:npm/%40commitlint/rules@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/rules@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"to-lines","version":"20.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"da5f609b088246a64d5a057ea57d57ef3e323f46f77b1ffce94994160a11b7aef6133e9c00cda5390787151513b84eac3e98bc5c10869ddf0a8776cca321f027"}],"purl":"pkg:npm/%40commitlint/to-lines@20.0.0","type":"library","bom-ref":"pkg:npm/@commitlint/to-lines@20.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"top-level","version":"20.4.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"a83f717cfe9d160e6343734cace846d3fc39cb76c152c546c89bd7c5d584c019bc872c7858e93790a5f0dbc4f9733058bf278254980927a6a82595160e969a71"}],"purl":"pkg:npm/%40commitlint/top-level@20.4.3","type":"library","bom-ref":"pkg:npm/@commitlint/top-level@20.4.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"types","version":"19.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"0d21da7b6a1b31232402d4c148eba58395fbff3fab18bc5c5d02248373a65af63ac227e88e07b9b9533275f854becef2423f95ee3366459d97ce5f06272a9182"}],"purl":"pkg:npm/%40commitlint/types@19.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/types@19.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@commitlint","name":"types","version":"20.5.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"649a12f284aad8201912973f608f52ba52eb7622325de1dbfce1aa1ab9143fa43b615fb4a2e3406bb1a3a917577903e77074080f3fe36c24e51d272f62f3bf80"}],"purl":"pkg:npm/%40commitlint/types@20.5.0","type":"library","bom-ref":"pkg:npm/@commitlint/types@20.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@conventional-changelog","name":"git-client","version":"2.7.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"8fb03cfcb04443edebba03333d7a0a633c943e9c3fd020500b2bed4d1ecb9aee28946e32442fd37e4abbf4caf7cae3ecd12522b653b61f018fde08ad3099d117"}],"purl":"pkg:npm/%40conventional-changelog/git-client@2.7.0","type":"library","bom-ref":"pkg:npm/@conventional-changelog/git-client@2.7.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@cyclonedx","name":"cyclonedx-library","version":"10.0.0","scope":"required","hashes":[{"alg":"SHA-512","content":"c435dfd9eab37851dd8da9a3ea8055dddb9149fae59ac279fb6cfdb57a7bab9ab124fe40c268dfe00052bad4b8aa45471e3ec9cca14bfc433457436285cef33e"}],"purl":"pkg:npm/%40cyclonedx/cyclonedx-library@10.0.0","type":"library","bom-ref":"pkg:npm/@cyclonedx/cyclonedx-library@10.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"@cyclonedx/cyclonedx-library,Enums,@cyclonedx/cyclonedx-library/Enums,Models,@cyclonedx/cyclonedx-library/Models,Serialize,@cyclonedx/cyclonedx-library/Serialize,Spec,@cyclonedx/cyclonedx-library/Spec"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/pipeline/phases/sbom.js#40"},{"location":"packages/ingestion/src/pipeline/phases/sbom.ts#41"}]}},{"group":"@duckdb","name":"node-api","version":"1.5.2-r.1","scope":"required","hashes":[{"alg":"SHA-512","content":"3b30419d2d091973284b99b328d63f625afb4ac85c45088b148a3143802578fc09d9f35e0cbfdf6c7bbf9278f152b5f05b57c9053520c167ed9b10dd9d965bdc"}],"purl":"pkg:npm/%40duckdb/node-api@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-api@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/storage/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/storage@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/storage/package.json"},{"name":"ImportedModules","value":"@duckdb/node-api,ARRAY,@duckdb/node-api/ARRAY,arrayValue,@duckdb/node-api/arrayValue,DuckDBInstance,@duckdb/node-api/DuckDBInstance,FLOAT,@duckdb/node-api/FLOAT,listValue,@duckdb/node-api/listValue,DuckDBConnection,@duckdb/node-api/DuckDBConnection,DuckDBPreparedStatement,@duckdb/node-api/DuckDBPreparedStatement"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/storage/dist/duckdb-adapter.js#20"},{"location":"packages/storage/src/duckdb-adapter.ts#29"}]}},{"group":"@duckdb","name":"node-bindings-darwin-arm64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"bf7e45c8a39bf04242bda88f17b93482f2a22535d1ecf3d00cda164741aef98497e4ef5bf83220bb3b75df8f0e7f71de6c7cba01348cce551e91a540f585eefa"}],"purl":"pkg:npm/%40duckdb/node-bindings-darwin-arm64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-darwin-arm64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings-darwin-x64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"494f5d209d4196e2a4906c62e14b0fe247aa924b2d076603c9217d29c62edc464a215337153bf6cdced73b7f1d5e71ceabaf85dd6aa159666f0fe5d4f78e69ec"}],"purl":"pkg:npm/%40duckdb/node-bindings-darwin-x64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-darwin-x64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings-linux-arm64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"dd3adaf7133768cddd7a76844782a127afffe8fa663db8a4f4408143eb21f4fc8a684807c3fd2401c2a72e6e44cd65275c5d2a6266657b0be40abb1ef0a98e63"}],"purl":"pkg:npm/%40duckdb/node-bindings-linux-arm64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-linux-arm64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings-linux-x64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"a5c42f6511e221f27d72af296ab91241ccd01c49a5fc87867e70c23006c4803ebe8da57d63d6393e1d643fd691f9b9ba8629b54b964812bde465b8a18ca9d66c"}],"purl":"pkg:npm/%40duckdb/node-bindings-linux-x64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-linux-x64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings-win32-arm64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"262f2dca6f8ddcb92b855b74529ddbb1a703fe4a60e3f25714942ac6cc2262f05a34240e93e0fe6a2552d06379a5caaf9a71bb57b4e9b0347390b105696a75bf"}],"purl":"pkg:npm/%40duckdb/node-bindings-win32-arm64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-win32-arm64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings-win32-x64","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"e57a9ca82fb847c82104411b9dcd9ad2ca9fcf5cf23c145bf587262167e2b83a026126056ca8661dc13235fb590c77300a82ce1d79d48a7e398eb69ec78493a9"}],"purl":"pkg:npm/%40duckdb/node-bindings-win32-x64@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings-win32-x64@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@duckdb","name":"node-bindings","version":"1.5.2-r.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"6d48376cb563ef461592ee9f2b24094bc01239197b90ced81551739ec101f6959bb5accf8fe304db1d85524d16893ce325dbad8f3492197174eba981e1b18664"}],"purl":"pkg:npm/%40duckdb/node-bindings@1.5.2-r.1","type":"library","bom-ref":"pkg:npm/@duckdb/node-bindings@1.5.2-r.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/storage@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/storage/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@graphty","name":"algorithms","version":"1.7.1","scope":"required","hashes":[{"alg":"SHA-512","content":"0fda07fb15075544ca643138be8c50fd001adcb05c31f92dbce86756bf03b9e398b856f6771eace7064882f5a1835883f3e980b89c9f73382702abdcbcece73e"}],"purl":"pkg:npm/%40graphty/algorithms@1.7.1","type":"library","bom-ref":"pkg:npm/@graphty/algorithms@1.7.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"@graphty/algorithms,Graph,@graphty/algorithms/Graph,leiden,@graphty/algorithms/leiden"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/pipeline/phases/communities.js#25"},{"location":"packages/ingestion/src/pipeline/phases/communities.ts#26"}]}},{"group":"@homebridge","name":"node-pty-prebuilt-multiarch","version":"0.11.14","scope":"optional","hashes":[{"alg":"SHA-512","content":"7ee8aae646f88b4028d014dfeceea4bedc148420822472e158b59a69468bba7883192e319a3fa0c6f922749a716154ffcd35dd6dc2ee098e38527a160bbc4e0e"}],"purl":"pkg:npm/%40homebridge/node-pty-prebuilt-multiarch@0.11.14","type":"library","bom-ref":"pkg:npm/@homebridge/node-pty-prebuilt-multiarch@0.11.14","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@hono","name":"node-server","version":"1.19.14","scope":"optional","hashes":[{"alg":"SHA-512","content":"1b0b6f82d5f19d6b2e717bdb41791182a92c887d9079ddfb1fdc47668704e6c03737c3bc3bcffc140dee725417c5757373d5c166e10e30aefeaf4d85992a47b7"}],"purl":"pkg:npm/%40hono/node-server@1.19.14","type":"library","bom-ref":"pkg:npm/@hono/node-server@1.19.14","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@huggingface","name":"tokenizers","version":"0.1.3","scope":"required","hashes":[{"alg":"SHA-512","content":"f2b17f4514f5d2efa49fb62e51b520d0e177d0af2b8d373bf1a1e9c53faa275b964aac53d4c1e2f3ef65b7061f905609cd3fe84beab0dc955f1ed34c18076ac8"}],"purl":"pkg:npm/%40huggingface/tokenizers@0.1.3","type":"library","bom-ref":"pkg:npm/@huggingface/tokenizers@0.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/embedder/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"},{"name":"ImportedModules","value":"@huggingface/tokenizers,Tokenizer,@huggingface/tokenizers/Tokenizer"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/embedder/dist/onnx-embedder.js#17"},{"location":"packages/embedder/src/onnx-embedder.ts#19"}]}},{"group":"@iarna","name":"toml","version":"2.2.5","scope":"required","hashes":[{"alg":"SHA-512","content":"b6b9ec018c54df19d2d603c73f253deb57281722e48788000ffd334399b2998e32399f8262fb0fa946ce152c34683338cb4b55eed885c4bff55df5cf342e883e"}],"purl":"pkg:npm/%40iarna/toml@2.2.5","type":"library","bom-ref":"pkg:npm/@iarna/toml@2.2.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/analysis/package.json"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/analysis@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/analysis/package.json"},{"name":"ImportedModules","value":"@iarna/toml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/analysis/dist/verdict.js#24"},{"location":"packages/cli/dist/editors/codex.js#18"},{"location":"packages/ingestion/dist/pipeline/dep-parsers/python.js#21"},{"location":"packages/ingestion/dist/pipeline/dep-parsers/rust.js#18"},{"location":"packages/analysis/src/verdict.ts#25"},{"location":"packages/cli/src/editors/codex.ts#19"},{"location":"packages/ingestion/src/pipeline/dep-parsers/python.ts#22"},{"location":"packages/ingestion/src/pipeline/dep-parsers/rust.ts#19"}]}},{"group":"@inquirer","name":"ansi","version":"1.0.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"4bca8d499898cc5774c007321b90170af5070b94abef1a59f70676a72f5747cf23533f30a284ad571e4ce9d4737336c15a389cf4d3fbfab6345e2eeaa8afda31"}],"purl":"pkg:npm/%40inquirer/ansi@1.0.2","type":"library","bom-ref":"pkg:npm/@inquirer/ansi@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"checkbox","version":"4.3.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"557ba41dfd1147576819ee929b8174126ed25982d31d2b1b19f25d4bd25ad9b5f9fc3e6ec153848ebd3b72770b44e741be69ef0804d8116947a98997bf622540"}],"purl":"pkg:npm/%40inquirer/checkbox@4.3.2","type":"library","bom-ref":"pkg:npm/@inquirer/checkbox@4.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"confirm","version":"5.1.21","scope":"optional","hashes":[{"alg":"SHA-512","content":"291f1e751908b146b231757ea371affaae2396110d17d9cc61466cf4f0361f3ad778723c339b836a0ef453b4499fdcb288c6526ed179fd47b106d015b591926d"}],"purl":"pkg:npm/%40inquirer/confirm@5.1.21","type":"library","bom-ref":"pkg:npm/@inquirer/confirm@5.1.21","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"core","version":"10.3.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"e37453b846df3fc31b2b379d36a06b96184d295c282bffef505356dd0def67cf012dcaece246291a0f81da69b9a762bf1dfca0a02c6e2b02498aff0f6c6984d0"}],"purl":"pkg:npm/%40inquirer/core@10.3.2","type":"library","bom-ref":"pkg:npm/@inquirer/core@10.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"editor","version":"4.2.23","scope":"optional","hashes":[{"alg":"SHA-512","content":"68b4913a41308aba2dc59d6905a3fcb6e8174450b15bde20c2b40bc577eb66c2a47e33980b5691bc066e86924e6f972ee0805325db028a05257f68823aee190d"}],"purl":"pkg:npm/%40inquirer/editor@4.2.23","type":"library","bom-ref":"pkg:npm/@inquirer/editor@4.2.23","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"expand","version":"4.0.23","scope":"optional","hashes":[{"alg":"SHA-512","content":"9d1cdd3b21589e97984d3476a85c04566216ca9cdd031fec22408c79335371f9453a8bdfa9493e1dc1614105417ed021f60986ae9163e9070612aac33033a27b"}],"purl":"pkg:npm/%40inquirer/expand@4.0.23","type":"library","bom-ref":"pkg:npm/@inquirer/expand@4.0.23","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"external-editor","version":"1.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"4566d2ac389898ee0b6de8d663bb6da71733bb043264b054cb282c03d36cbfde61a73516c27353550980ab7c6e87bbcdc02a74ed44e61398b5d57004131c7844"}],"purl":"pkg:npm/%40inquirer/external-editor@1.0.3","type":"library","bom-ref":"pkg:npm/@inquirer/external-editor@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"figures","version":"1.0.15","scope":"optional","hashes":[{"alg":"SHA-512","content":"b7620463eba71873b301a54ce57c7a0c458a7979430dc34f783c94a6c45ce8252105f53755038497e56cb21ed5369d5d47c31d50905686e39b8d70ac5698cde6"}],"purl":"pkg:npm/%40inquirer/figures@1.0.15","type":"library","bom-ref":"pkg:npm/@inquirer/figures@1.0.15","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"input","version":"4.3.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"90dd2900ce323eb2e32755c90630f1c9f0ddb973ae407ac107c68b0ccb9ebb05069febcda45ec6abb4efc95c71f2ee121e51458f8b6b9a3f9ad9c6e91b8267d6"}],"purl":"pkg:npm/%40inquirer/input@4.3.1","type":"library","bom-ref":"pkg:npm/@inquirer/input@4.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"number","version":"3.0.23","scope":"optional","hashes":[{"alg":"SHA-512","content":"e529afd0e2bb2b4294cd47d85170d741cf63adff0e1e8e24b6511ac8595e9428f0317cf4dbdf58f0eac8fa58fb8b8802058d7950e6e4efaab442dc63cc570572"}],"purl":"pkg:npm/%40inquirer/number@3.0.23","type":"library","bom-ref":"pkg:npm/@inquirer/number@3.0.23","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"password","version":"4.0.23","scope":"optional","hashes":[{"alg":"SHA-512","content":"cd11091e3853e6f2413195ff2146f223dcd5b557ce2e24ceeba32b17fdc6159619ed3e1820b5b931290460771e4a28bf2ad464fb88b7444ec4d425170368c770"}],"purl":"pkg:npm/%40inquirer/password@4.0.23","type":"library","bom-ref":"pkg:npm/@inquirer/password@4.0.23","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"prompts","version":"7.10.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"0f1ff2f5b0907172c8e68a10e4acaf03815381ea368d88ffee99567d5e40939c033ca41982e74a7b3da2c727f3eed297cdc25c893c6a2de2bb47d1c8b70881ca"}],"purl":"pkg:npm/%40inquirer/prompts@7.10.1","type":"library","bom-ref":"pkg:npm/@inquirer/prompts@7.10.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"rawlist","version":"4.1.11","scope":"optional","hashes":[{"alg":"SHA-512","content":"f8b2d007c5c6af72392d937f1ae007a3e1a90c97a0430b8f0112c286530808d7705bb3b05768b3942482c4de9caa92f4b0c5e66ca6c5708b4981d42ae440574f"}],"purl":"pkg:npm/%40inquirer/rawlist@4.1.11","type":"library","bom-ref":"pkg:npm/@inquirer/rawlist@4.1.11","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"search","version":"3.2.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"a766ef45f10d5c265d585fd4d815ef9d223d87eb6e03c88daad50a6fd5166e62d809143177c5a4bf05af627fb736061a370754907cad24c186e0c30bb9c23590"}],"purl":"pkg:npm/%40inquirer/search@3.2.2","type":"library","bom-ref":"pkg:npm/@inquirer/search@3.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"select","version":"4.4.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"978c4cb89a39e4c01ef8ded0af8ad7f74bf2a45c026a349a931e7da9efed31a0b56841d62f2c3af30178a3403848b018e04d2777e56df84cac91e0c8aaed4ceb"}],"purl":"pkg:npm/%40inquirer/select@4.4.2","type":"library","bom-ref":"pkg:npm/@inquirer/select@4.4.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"testing","version":"2.1.53","scope":"optional","hashes":[{"alg":"SHA-512","content":"d7ac9ac46e6d4b80640b1bc86e718c6f8596586a7cae5e6c341bba2e41bddf1ffbfb3a72172307d1cc7bf22cee362d36d3269da247bd7a8cff365df55c09042a"}],"purl":"pkg:npm/%40inquirer/testing@2.1.53","type":"library","bom-ref":"pkg:npm/@inquirer/testing@2.1.53","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@inquirer","name":"type","version":"3.0.10","scope":"optional","hashes":[{"alg":"SHA-512","content":"06fce2491c5fcf93aff1c874cff9f7a228d1484704b079e18209b8c4565ef770c771409396eb65abd3b1e125443407dc443db6510abb4ff6ad83d5abde4d3d78"}],"purl":"pkg:npm/%40inquirer/type@3.0.10","type":"library","bom-ref":"pkg:npm/@inquirer/type@3.0.10","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@isaacs","name":"cliui","version":"8.0.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"3bc8dc8da6d76a578e1bd0d0d3e0115d66414df9cfe16340ab3ba224aee5978e009b118abff2763384cf8f18d8df39c109fbc15c5cee726d6dc1dc85c9b16a10"}],"purl":"pkg:npm/%40isaacs/cliui@8.0.2","type":"library","bom-ref":"pkg:npm/@isaacs/cliui@8.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@isaacs","name":"cliui","version":"9.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"0289099b8b6e0478a5953f85a4cb7143ad27f0e6f25c16adabb8c3dbf240f5dc5b0c3a242909bc28c86de626c6ccb554f4824320a2b84cf2a0307118327de532"}],"purl":"pkg:npm/%40isaacs/cliui@9.0.0","type":"library","bom-ref":"pkg:npm/@isaacs/cliui@9.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@isaacs","name":"fs-minipass","version":"4.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"c209bd1219768e97aa3f7cf0ffb9a8de4447169e4c10386a01dc32d5f4c69070309e418e56c829bd084bf01e67d6a95bd358d5de7fdb23465f669e65580d64e3"}],"purl":"pkg:npm/%40isaacs/fs-minipass@4.0.1","type":"library","bom-ref":"pkg:npm/@isaacs/fs-minipass@4.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@kwsites","name":"file-exists","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"9bdff9606475f25230c52143c1f137a00edb5aeabd91d6aeeae80de07dab25eca115065c1bd020487910b520f5e5af16bd381fcfd6a291932b28f1ef6e9a858b"}],"purl":"pkg:npm/%40kwsites/file-exists@1.1.1","type":"library","bom-ref":"pkg:npm/@kwsites/file-exists@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@kwsites","name":"promise-deferred","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"19a1d89be7343bd323651bb4a278060516e29eef2054031dd946638e2ea3566a8ab5996e667a6d5c65a1cf513c8fc0f61c9ddfff2331280502d1bfb9ef09dc23"}],"purl":"pkg:npm/%40kwsites/promise-deferred@1.1.1","type":"library","bom-ref":"pkg:npm/@kwsites/promise-deferred@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@modelcontextprotocol","name":"sdk","version":"1.29.0","scope":"required","hashes":[{"alg":"SHA-512","content":"ce8dfb99903d8495a950b824469a307b07b3d72e8c2f91ac5c93d8f05234b4104277bec712fcdae230ea44a397807367f3aecf5460b24ddceaa73d22ceee598d"}],"purl":"pkg:npm/%40modelcontextprotocol/sdk@1.29.0","type":"library","bom-ref":"pkg:npm/@modelcontextprotocol/sdk@1.29.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/mcp/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"ImportedModules","value":"@modelcontextprotocol/sdk/server/mcp.js,ResourceTemplate,@modelcontextprotocol/sdk/server/mcp.js/ResourceTemplate,McpServer,@modelcontextprotocol/sdk/server/mcp.js/McpServer,@modelcontextprotocol/sdk/server/stdio.js,StdioServerTransport,@modelcontextprotocol/sdk/server/stdio.js/StdioServerTransport,@modelcontextprotocol/sdk/types.js,CallToolResult,@modelcontextprotocol/sdk/types.js/CallToolResult,ListResourcesResult,@modelcontextprotocol/sdk/types.js/ListResourcesResult,ReadResourceResult,@modelcontextprotocol/sdk/types.js/ReadResourceResult"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/mcp/dist/resources/repo-context.js#9"},{"location":"packages/mcp/dist/resources/repo-schema.js#10"},{"location":"packages/mcp/dist/server.js#14"},{"location":"packages/mcp/src/prompts/audit-dependencies.ts#9"},{"location":"packages/mcp/src/prompts/detect-impact.ts#10"},{"location":"packages/mcp/src/prompts/explore-area.ts#9"},{"location":"packages/mcp/src/prompts/review-pr.ts#10"},{"location":"packages/mcp/src/resources/repo-context.ts#10"},{"location":"packages/mcp/src/resources/repo-context.ts#11"},{"location":"packages/mcp/src/resources/repo-schema.ts#11"},{"location":"packages/mcp/src/resources/repo-schema.ts#12"},{"location":"packages/mcp/src/resources/repos.ts#9"},{"location":"packages/mcp/src/server.ts#15"},{"location":"packages/mcp/src/tools/api-impact.ts#24"},{"location":"packages/mcp/src/tools/context.ts#22"},{"location":"packages/mcp/src/tools/dependencies.ts#19"},{"location":"packages/mcp/src/tools/detect-changes.ts#5"},{"location":"packages/mcp/src/tools/group-contracts.ts#21"},{"location":"packages/mcp/src/tools/group-list.ts#7"},{"location":"packages/mcp/src/tools/group-query.ts#31"},{"location":"packages/mcp/src/tools/group-status.ts#15"},{"location":"packages/mcp/src/tools/impact.ts#10"},{"location":"packages/mcp/src/tools/license-audit.ts#27"},{"location":"packages/mcp/src/tools/list-dead-code.ts#15"},{"location":"packages/mcp/src/tools/list-findings-delta.ts#30"},{"location":"packages/mcp/src/tools/list-findings.ts#18"},{"location":"packages/mcp/src/tools/list-repos.ts#10"},{"location":"packages/mcp/src/tools/owners.ts#14"},{"location":"packages/mcp/src/tools/project-profile.ts#14"},{"location":"packages/mcp/src/tools/query.ts#23"},{"location":"packages/mcp/src/tools/remove-dead-code.ts#25"},{"location":"packages/mcp/src/tools/rename.ts#9"},{"location":"packages/mcp/src/tools/risk-trends.ts#11"},{"location":"packages/mcp/src/tools/route-map.ts#17"},{"location":"packages/mcp/src/tools/scan.ts#19"},{"location":"packages/mcp/src/tools/shape-check.ts#24"},{"location":"packages/mcp/src/tools/shared.ts#11"},{"location":"packages/mcp/src/tools/signature.ts#27"},{"location":"packages/mcp/src/tools/sql.ts#14"},{"location":"packages/mcp/src/tools/tool-map.ts#16"},{"location":"packages/mcp/src/tools/verdict.ts#14"},{"location":"packages/mcp/dist/server.js#15"},{"location":"packages/mcp/src/server.ts#16"},{"location":"packages/mcp/src/error-envelope.ts#15"},{"location":"packages/mcp/src/next-step-hints.ts#16"},{"location":"packages/mcp/src/resources/repo-context.ts#12"},{"location":"packages/mcp/src/resources/repo-schema.ts#13"},{"location":"packages/mcp/src/resources/repos.ts#10"},{"location":"packages/mcp/src/tools/shared.ts#12"}]}},{"group":"@napi-rs","name":"nice-android-arm-eabi","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"9238ab2f737a4e744fbf98ae1f0dfac27b9c36a5c03b8e9dccaf683dbd308f4efa4795e6f0f7d4540f6700507964d3267c940954a00200f77f676298329a6d87"}],"purl":"pkg:npm/%40napi-rs/nice-android-arm-eabi@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-android-arm-eabi@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"android"},{"name":"cdx:pnpm:cpu","value":"arm"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-android-arm64","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"6e51b48bb75781b227379bab38da1408d7fe0d41006af45f7eb3bb7d949ea11309739a83f8125e35ca6be789ac3c5eaa7c30fa933b3d010268819bd3d8a0f99f"}],"purl":"pkg:npm/%40napi-rs/nice-android-arm64@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-android-arm64@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"android"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-darwin-arm64","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"b3f13bc38e4d68ba931ae3a30b6a7dea972de2345fa3ad716fd6d4d6e9ccfcc27f44590a949c890f1ece248fced2597f86b7e9a8aa29b8014357cca8d217d3ec"}],"purl":"pkg:npm/%40napi-rs/nice-darwin-arm64@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-darwin-arm64@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-darwin-x64","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"746a04067569b1d702fa81c7996d4b44ae5e8b2ccbc1d80d42adc199921abfef7fe564d9c01617eebe599100b4ecdc5ddca1ce0a481b1d2878c0f907d4dd4b89"}],"purl":"pkg:npm/%40napi-rs/nice-darwin-x64@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-darwin-x64@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-freebsd-x64","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"907bf89041c0ca53189a5370710703b578e4958a781427f46f4e44fb487a9c31ec67e1746c37b4e14fed5cd3aaaf1e4298802d878bf07e48e3526a78738264b5"}],"purl":"pkg:npm/%40napi-rs/nice-freebsd-x64@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-freebsd-x64@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"freebsd"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-arm-gnueabihf","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"135b7b2b479fc8a5d90e8660d4bcc238bc60a25c55e7c1c291a12412f218431d7686dda96bc868068fb8381dea87b7be422065a754917febe8594645c72872aa"}],"purl":"pkg:npm/%40napi-rs/nice-linux-arm-gnueabihf@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-arm-gnueabihf@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-arm64-gnu","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"08828b035d834c866599369a2a140ff3c4775daa3e832271356127d38c19c02db09916a9367c42519915c208089cc26fb5512503e0f8673394e6c5f88d5f9299"}],"purl":"pkg:npm/%40napi-rs/nice-linux-arm64-gnu@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-arm64-gnu@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-arm64-musl","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"fb647375bde74c8619d18245e37a9fdadc21a8e0a4892ac7c763e0e8325a08f62185ac5b2dc76557c84244c1e0850f84b5941618d712db11782b10614867aeb6"}],"purl":"pkg:npm/%40napi-rs/nice-linux-arm64-musl@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-arm64-musl@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"},{"name":"cdx:pnpm:libc","value":"musl"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-ppc64-gnu","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"e054bca1cd06787a70befe2d29c88a930dd8e232ac2fb15185a39e88f7a2d17f53e0977ad7dc0735ee3108b98dd8432066878683e43b198ec1b2fc0aa4bdb64e"}],"purl":"pkg:npm/%40napi-rs/nice-linux-ppc64-gnu@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-ppc64-gnu@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"ppc64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-riscv64-gnu","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"1d4d27c3db83e053bfa06082938d3db428b9233219a47d9a804ea73787eaa705650a7e413aad0c4b57571a35da1b5ec9680beb96957965ec99c12a27d745ce5f"}],"purl":"pkg:npm/%40napi-rs/nice-linux-riscv64-gnu@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-riscv64-gnu@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"riscv64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-s390x-gnu","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"d98a8a2565a5db8130ad7d03cc24203cb290071603741c4e1e8b7529612aeda636b98797f94bedbf823cc45555ca024381febff7687d377638dd63f1f3e3c081"}],"purl":"pkg:npm/%40napi-rs/nice-linux-s390x-gnu@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-s390x-gnu@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"s390x"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-x64-gnu","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"fe068dcf747ddadf9d72b7c2c3ff7aa43a2972679cee809c010de5fccf99c6bf36293e039630f7ec2a60ae75d5fa9242dbadc9916e7bda975b3f784ff8a8dc22"}],"purl":"pkg:npm/%40napi-rs/nice-linux-x64-gnu@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-x64-gnu@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"},{"name":"cdx:pnpm:libc","value":"glibc"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-linux-x64-musl","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"c527021a7ca3fe8a69b0d3cc9debec05ede9bcd6a82bb146bcc8d3df9ae2cfd61d841d96b531b8ed995bc6d38ba6378ef52aaa4362768a00a6cfa209383e4963"}],"purl":"pkg:npm/%40napi-rs/nice-linux-x64-musl@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-linux-x64-musl@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"},{"name":"cdx:pnpm:libc","value":"musl"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-openharmony-arm64","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"eae24f455c1508b0dea0e68dc9e896d20a7690520ce2becf2f631ccdd6501e4162f60565826f959fe57a9d359172ef39ea62765a36098ae9846a37d29bb6ab3d"}],"purl":"pkg:npm/%40napi-rs/nice-openharmony-arm64@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-openharmony-arm64@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"openharmony"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-win32-arm64-msvc","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"ba84dbe1e02f3390766a3ff3f23f8dbfc3adb4f7f69be1d5c775230398dc171012bcd850ae2c82405d4e0759472f8dd9856f95c19960be398fe6a177fb9f5ab4"}],"purl":"pkg:npm/%40napi-rs/nice-win32-arm64-msvc@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-win32-arm64-msvc@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-win32-ia32-msvc","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"08d42a9504fd330b82b20d5577fa0a5e2b87f9372c48f26694015ce5fac5c97fca90e87452904b123edaa18eb9e9de5429940c4053fbbc935ad4335434e46fba"}],"purl":"pkg:npm/%40napi-rs/nice-win32-ia32-msvc@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-win32-ia32-msvc@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"ia32"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice-win32-x64-msvc","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"bc1fb81bf8c1402021d237a5313637fa4805cb4d07971d9fdbfd738cca07f36d486e995b5993a42e24d85d093280d4f34094eae5cbf0043827da9a470fe6e095"}],"purl":"pkg:npm/%40napi-rs/nice-win32-x64-msvc@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice-win32-x64-msvc@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@napi-rs","name":"nice","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"c4920fb3e6d8b9cf4048197e72f1ac29b1ab2664ba7c029a4990a74f49616a14f9ae1036555cbdfc470881dd8986d12e14e24dc7b507367fea8933d36389eb43"}],"purl":"pkg:npm/%40napi-rs/nice@1.1.1","type":"library","bom-ref":"pkg:npm/@napi-rs/nice@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@nodable","name":"entities","version":"2.1.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"9f24fb4f79db332048fe5bebe8be53c966c524023d15381545a90da01a820fe3e6203f03cc5aeb35d2cbb4733032ccceb6a65af0f00e576e19a839d0ae140834"}],"purl":"pkg:npm/%40nodable/entities@2.1.0","type":"library","bom-ref":"pkg:npm/@nodable/entities@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@nodelib","name":"fs.scandir","version":"2.1.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"beadb806adf29b91c4426d8d282af7c970f08dceef4ec1138510e7929d832bda75baa2d1f831eeae6fcd393a34286ec760753b7a9a4a663dcccaa62e3017fada"}],"purl":"pkg:npm/%40nodelib/fs.scandir@2.1.5","type":"library","bom-ref":"pkg:npm/@nodelib/fs.scandir@2.1.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@nodelib","name":"fs.stat","version":"2.0.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"46484f3e9db3aea0c0400ff68cd867ced70f025bfae17761229edaef8e78039a2f23b06e93182decc5fbb9dc00bb7ce0d437293d4d2bcf7555d5279aaaf638f8"}],"purl":"pkg:npm/%40nodelib/fs.stat@2.0.5","type":"library","bom-ref":"pkg:npm/@nodelib/fs.stat@2.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@nodelib","name":"fs.walk","version":"1.2.8","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0607e53196059c810920c28f067041b07a6a1316ddc520ef5a6da6c199a1b05c8a01299f864f2d293f5f396de1a0ecb96287f3521d25765c0b35967ce7a1c4a"}],"purl":"pkg:npm/%40nodelib/fs.walk@1.2.8","type":"library","bom-ref":"pkg:npm/@nodelib/fs.walk@1.2.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@npmcli","name":"fs","version":"3.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"abd0915a3a4708c221e6c57279fa03d5c03b3e4bc82ea099b2748e114522bce44b8f108efc8ae6b9ed83a6b11388d804aa4b4305968cd418be8eb6abc755dd0a"}],"purl":"pkg:npm/%40npmcli/fs@3.1.1","type":"library","bom-ref":"pkg:npm/@npmcli/fs@3.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@pinojs","name":"redact","version":"0.4.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"93610d9e606e804febcd07c471d58770263efc533754bcc7f5c604b1b76ca2aaefcc029185465e44d84066f07c3a2b502754c179ddf2a96b5d8f34bac09281c2"}],"purl":"pkg:npm/%40pinojs/redact@0.4.0","type":"library","bom-ref":"pkg:npm/@pinojs/redact@0.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@pkgjs","name":"parseargs","version":"0.11.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"fb55648dd0f44012cfa1d1ab2547aa6ab1fc54022f40e0c86f087d5e93f94b28ac7fb628420b0928f345a2aa8b425bbe550fed552b21311ea5a0f327f14f9d3e"}],"purl":"pkg:npm/%40pkgjs/parseargs@0.11.0","type":"library","bom-ref":"pkg:npm/@pkgjs/parseargs@0.11.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@pnpm","name":"crypto.base32-hash","version":"1.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"a73017367e8ac5303791b708de212762ce2fb47e755c456a98aff51220f5f0c68f2b2961ab2f14bcc24adf3286fa378ff3672a41ba337131a6e32390f22def37"}],"purl":"pkg:npm/%40pnpm/crypto.base32-hash@1.0.1","type":"library","bom-ref":"pkg:npm/@pnpm/crypto.base32-hash@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@pnpm","name":"types","version":"8.9.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"dcc607626f1ea677220299fac39173c7ab1ea5ac26b0d53b97a96f22af84476dbf0cf4abafcdd832153f1105a77f894e467d98ca25c58f41494b227312d2069b"}],"purl":"pkg:npm/%40pnpm/types@8.9.0","type":"library","bom-ref":"pkg:npm/@pnpm/types@8.9.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@sec-ant","name":"readable-stream","version":"0.4.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"f37d6aa24f6bdadf009712e4a38d32d9e6e048385e9de9c26ad2d5796fee06d9c73f28473af1b40bb4ef7e079c57ec07cc89b9294202833995a564be20c1007a"}],"purl":"pkg:npm/%40sec-ant/readable-stream@0.4.1","type":"library","bom-ref":"pkg:npm/@sec-ant/readable-stream@0.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@simple-git","name":"args-pathspec","version":"1.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"9e024c68796c5834df8f2abd17754843c6fb3576c12eae63f62e5b27a5cb62d0faaa50d74fb7dd298d8ab1c596505f2dd7cc71d39d98fcf50ed4ad5345cf7228"}],"purl":"pkg:npm/%40simple-git/args-pathspec@1.0.3","type":"library","bom-ref":"pkg:npm/@simple-git/args-pathspec@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@simple-git","name":"argv-parser","version":"1.1.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"43d94171f43e5500a942a1891477b9ca8a0e4b984674b1456c9e51f91e5a0ec9e43c26a1b67d614a431c3915fae49d99e65c52903d25428accb2772e050c5853"}],"purl":"pkg:npm/%40simple-git/argv-parser@1.1.1","type":"library","bom-ref":"pkg:npm/@simple-git/argv-parser@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@simple-libs","name":"child-process-utils","version":"1.0.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"ff847c40a9ddffc6a02729e435d266370d8c071b854d170d1671394a0fc6fa3912b15f3f5018142ccce18b35965b8da9f0be47edf948995d804e2efd3af7b64f"}],"purl":"pkg:npm/%40simple-libs/child-process-utils@1.0.2","type":"framework","bom-ref":"pkg:npm/@simple-libs/child-process-utils@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@simple-libs","name":"stream-utils","version":"1.2.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"2b15ef7daa5c8b1a73eab54407a1cf8ce5194f6db237abf4bc8d2ead04a4d4bf0c94458f0c5099921c36c66932a13198785c3bb564d977b7b7955cd165137f10"}],"purl":"pkg:npm/%40simple-libs/stream-utils@1.2.0","type":"library","bom-ref":"pkg:npm/@simple-libs/stream-utils@1.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@sindresorhus","name":"is","version":"4.6.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"b74f6f48ddcc75fb32087a057134421ff894b46ece2740ac8f307c72302629cfef6bf90881e0c8fd3c6c8a0767704ff86deef7e26d1cbc863035a5788b65ea03"}],"purl":"pkg:npm/%40sindresorhus/is@4.6.0","type":"library","bom-ref":"pkg:npm/@sindresorhus/is@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@sindresorhus","name":"merge-streams","version":"4.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"b65a98f71ab9ba4c53519066a0ea7e9bad5cab0403e691c9b45637327f0203ca6ceb28212c7fc7c3c50f76a83838b9855b720595c5e740d9a8fdd87c1f35d821"}],"purl":"pkg:npm/%40sindresorhus/merge-streams@4.0.0","type":"library","bom-ref":"pkg:npm/@sindresorhus/merge-streams@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@snyk","name":"dep-graph","version":"2.16.7","scope":"optional","hashes":[{"alg":"SHA-512","content":"626a16f3d163bbec6a204b2233b2198b1714547094f0b936f2cb06e3eb67353a0eb107981a1ff9456c04d50bc41da29e1323696db4858b44ca555eb22a9d7f81"}],"purl":"pkg:npm/%40snyk/dep-graph@2.16.7","type":"library","bom-ref":"pkg:npm/@snyk/dep-graph@2.16.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@snyk","name":"error-catalog-nodejs-public","version":"5.80.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"e7cbf5e4a1fb65c3ff71deb66c57b7fe9ef7e0fb879ebc9e65e99c641d017392e9d415db46ae947e46c4cc303a721024217b631b063ccb0d0dba4c44ccd7bf48"}],"purl":"pkg:npm/%40snyk/error-catalog-nodejs-public@5.80.0","type":"library","bom-ref":"pkg:npm/@snyk/error-catalog-nodejs-public@5.80.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@snyk","name":"graphlib","version":"2.1.9-patch.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"6c163d6fdba57cb8f4bf611eaf4c8561adecc95788c552a5d84a714abb15793e268c0d0296d6721ec174263a1a1973f6ee722d4dd252e6e56c8f5340fb7684f9"}],"purl":"pkg:npm/%40snyk/graphlib@2.1.9-patch.3","type":"library","bom-ref":"pkg:npm/@snyk/graphlib@2.1.9-patch.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@szmarczak","name":"http-timer","version":"4.0.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"e0101f7f29183a03bee67cc1598c04dd6f74b0180b26850f45659c2fcc25ca233c201f22a49cf750c27d29741dd512905e92a9f13bad9fcd0766d5acbb6bbbeb"}],"purl":"pkg:npm/%40szmarczak/http-timer@4.0.6","type":"library","bom-ref":"pkg:npm/@szmarczak/http-timer@4.0.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"cacheable-request","version":"6.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"210dc46d3cc6c488a06f5237a8f65cd6b5899c7d019922afe506136a5130c1e16fc810cb4807b6e333f495efe1ca2ede7067d9565215020e0166a6fc581c0aab"}],"purl":"pkg:npm/%40types/cacheable-request@6.0.3","type":"library","bom-ref":"pkg:npm/@types/cacheable-request@6.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"conventional-commits-parser","version":"5.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"eeecf9107773cf64eaa0c7d5ede7bad4481fe72e8d91c3b8141ff58820909db7a2235177c73bf7bcae5d0425d408b4201984be9947a280ad62290cef103f909d"}],"purl":"pkg:npm/%40types/conventional-commits-parser@5.0.1","type":"library","bom-ref":"pkg:npm/@types/conventional-commits-parser@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"emscripten","version":"1.41.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"70c426ee9c6ee81c6d1f2a89ee6419da45d657948b9ae8326c57470816c9e5e1f33a8e95841724120013dfffeb3f91703c734f7848aae12990e6e701c2c3a8e1"}],"purl":"pkg:npm/%40types/emscripten@1.41.5","type":"library","bom-ref":"pkg:npm/@types/emscripten@1.41.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kornel Lesiński (https://kornel.ski/)","group":"@types","name":"http-cache-semantics","version":"4.2.0","description":"Parses Cache-Control and other headers. Helps building correct HTTP caches and proxies","scope":"optional","hashes":[{"alg":"SHA-512","content":"2f72e08a62c75ed1a45a290a9ec3e0d3f545c7d38665a0be78dd6ee2bf8e0755d1a87de6781200542db3af559d307f911e69d192a962400387349fc4d23fded1"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/%40types/http-cache-semantics@4.2.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/kornelski/http-cache-semantics.git"}],"type":"library","bom-ref":"pkg:npm/@types/http-cache-semantics@4.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"LocalNodeModulesPath","value":"/Users/lalsaado/Projects/open-code-hub/node_modules/.pnpm/http-cache-semantics@4.2.0/node_modules/http-cache-semantics"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"json-schema","version":"7.0.15","scope":"optional","hashes":[{"alg":"SHA-512","content":"e7e7cff0ff0c14d0be0326420f1ac1da991914f1b3a90594ce949ebae54bbe6f1531ca2b3586af06aa057312bc6d0cf842c6e7e2850411e9b8c032df732b061c"}],"purl":"pkg:npm/%40types/json-schema@7.0.15","type":"library","bom-ref":"pkg:npm/@types/json-schema@7.0.15","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"keyv","version":"3.1.4","scope":"optional","hashes":[{"alg":"SHA-512","content":"050e5a64d482a63ec3e8ada4b2b4424e62912c4a673ef58388b3dfa06ca167efbc62d88af5dff70c128f260af2df9f57fcfd4f7ebbb2630be7bf0163b8488422"}],"purl":"pkg:npm/%40types/keyv@3.1.4","type":"library","bom-ref":"pkg:npm/@types/keyv@3.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"node","version":"22.19.17","scope":"optional","hashes":[{"alg":"SHA-512","content":"c0674c71ffaf3d833a8e29294bfaa183a5a2a9257f3a11be8de7874ff2a556ac587c3e3489825ff7f004d6e43155616f53b322a4a46446ff0d4878821a03ebf1"}],"purl":"pkg:npm/%40types/node@22.19.17","type":"library","bom-ref":"pkg:npm/@types/node@22.19.17","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/storage@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/storage/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/search@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/search/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/scanners@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/scanners/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/sarif@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/core-types@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/core-types/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/analysis@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/analysis/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"responselike","version":"1.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"1fff8bf94913577dee7f8f4f1f9a420140553cd8f69c30574cdfaa4b574ec32ca0db897709c89c89c080edc6be1ccbc9059705825e6bf1ef9147a7a5b1be0bcb"}],"purl":"pkg:npm/%40types/responselike@1.0.3","type":"library","bom-ref":"pkg:npm/@types/responselike@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"sarif","version":"2.1.7","scope":"optional","hashes":[{"alg":"SHA-512","content":"911cf4544909a962dfd4b2d5378a53d5c83567dc00baf23a2fded5de6d9fe41efa4e0f1de35ddd76f2c13d31070192659e7e174afbb416466d562090195cab5d"}],"purl":"pkg:npm/%40types/sarif@2.1.7","type":"library","bom-ref":"pkg:npm/@types/sarif@2.1.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/sarif@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/sarif/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"semver","version":"7.7.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"1668097eef8c39c437ef4483d1ebfb108f1394201f29853e0789b94f7c977350a244df7883f4993edb02924e74e9a503b65327159bdab03c071d4719504698b8"}],"purl":"pkg:npm/%40types/semver@7.7.1","type":"library","bom-ref":"pkg:npm/@types/semver@7.7.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"spdx-correct","version":"3.1.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"7dd434a32a9c2a9007f85c0d11ead9975a13b25b2b84562175b6f6d483b514a5caffe4c560cd883b573f36fb21ecc08f569f1360400f5c83a55d4251916a1459"}],"purl":"pkg:npm/%40types/spdx-correct@3.1.3","type":"library","bom-ref":"pkg:npm/@types/spdx-correct@3.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"treeify","version":"1.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"871d28ef3584514e11d809a7fa98c205042ddb72a1cbf0e4e7a810539c62e63b4f2f587cdc008909e15a07633ff963b5027b6f5ab4a85679c201f2350101f80a"}],"purl":"pkg:npm/%40types/treeify@1.0.3","type":"library","bom-ref":"pkg:npm/@types/treeify@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"uuid","version":"10.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"ee0a86dfc1321e0c8fd52fbbfb1a2616d2fe64d1dc2afe83c0d682666266a35be032e8321774c29d7560e2dd6e93cf662cdc272ed9d8dd3a4ea423b2a75ff11d"}],"purl":"pkg:npm/%40types/uuid@10.0.0","type":"library","bom-ref":"pkg:npm/@types/uuid@10.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@types","name":"write-file-atomic","version":"4.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"a9da3ebd945c8722481cd7ae2359eba6c2f0fa19e482a3fff2695a37a5a57bf34a87274799437d978a77644f323fdd0024d256e2e07c1d085e75be1fd6f35ac9"}],"purl":"pkg:npm/%40types/write-file-atomic@4.0.3","type":"library","bom-ref":"pkg:npm/@types/write-file-atomic@4.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/analysis@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/analysis/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"core","version":"4.6.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"cb32704bd74728b63ccbcd416040b408407ee9a8d68631e4cc1473577f72ec03487438860bbb02df64521d66068bfa7185b8cf29e3a192c8fe8084d41d48d2ec"}],"purl":"pkg:npm/%40yarnpkg/core@4.6.0","type":"library","bom-ref":"pkg:npm/@yarnpkg/core@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"fslib","version":"3.1.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"85768f21697919903ead7731fb268a594b9e3c9aee66e0fedc0e40d97ea968405f16cc820fba04a7cf2548c8f5ca6d5e8415949a1347fd818ea7e4ab6e64813e"}],"purl":"pkg:npm/%40yarnpkg/fslib@3.1.5","type":"library","bom-ref":"pkg:npm/@yarnpkg/fslib@3.1.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"libzip","version":"3.2.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"2aac608dfcba4b0c02e2d4c64184e8216b548483914e9928c2a81df6490c8818b1a2bd1ea2eac765902082dffb3785902adf620b23d290edd7bebe38bd750103"}],"purl":"pkg:npm/%40yarnpkg/libzip@3.2.2","type":"library","bom-ref":"pkg:npm/@yarnpkg/libzip@3.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"lockfile","version":"1.1.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"1a94b0bf25ce70e3a557bd2f6e7ce38f87d6e715bf15d505ea7404b7510dcbb9b86427338b5fbf6ee5543c0aa619fab39ec391345cd432372d4c8a7c6bdb6e09"}],"purl":"pkg:npm/%40yarnpkg/lockfile@1.1.0","type":"library","bom-ref":"pkg:npm/@yarnpkg/lockfile@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"parsers","version":"3.0.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"990660512805bab52d034edc78c8f1ad6918cfc42d0ee624bcf96ed19aa77208e8a50d2de82344a3f39279a9649a76a0494c7c643e5ec2f7b353052e32abad42"}],"purl":"pkg:npm/%40yarnpkg/parsers@3.0.3","type":"library","bom-ref":"pkg:npm/@yarnpkg/parsers@3.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"@yarnpkg","name":"shell","version":"4.1.3","scope":"optional","hashes":[{"alg":"SHA-512","content":"e62830b076cfb520252dd98c74aa94ddab578f086d2c5417b18006d2c9f55dc3dbdf217c5b1c6d5b137a7f2701a14bc5c881c5cf51b429e45e7e7032f27ea077"}],"purl":"pkg:npm/%40yarnpkg/shell@4.1.3","type":"library","bom-ref":"pkg:npm/@yarnpkg/shell@4.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"abbrev","version":"2.0.0","description":"Like ruby's abbrev module, but in js","scope":"optional","hashes":[{"alg":"SHA-512","content":"ebf9a1d44daed98804b021dd634631e685beeb581953ed6f5daa221c7ae929eb9134d805bd2fbf8ebc07890841e5aa407f9a01ed407b135f689764762ca1fc85"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/abbrev@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/npm/abbrev-js.git"}],"type":"library","bom-ref":"pkg:npm/abbrev@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"accepts","version":"2.0.0","description":"Higher-level content negotiation","scope":"optional","hashes":[{"alg":"SHA-512","content":"e5cbe0e82b4ac1f81d995a98d562225ca7374356e446a18b8bed96ffa6a8fba63b82efd10b046e021184ce1e41e0a96ccd2b932e0658baa16aa396c89a334a9e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/accepts@2.0.0","type":"library","bom-ref":"pkg:npm/accepts@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Nasca Iacob (https://github.com/cthackers)","group":"","name":"adm-zip","version":"0.5.16","description":"Javascript implementation of zip for nodejs with support for electron original-fs. Allows user to create or extract zip files both in memory or to/from disk","scope":"optional","hashes":[{"alg":"SHA-512","content":"4c6c39c958b8b1a6a3b12120cf6e60ace6c61c451a0eb9e2c2f036ab0482d3ad0a7ea18f760961bcf300da53c8a31b373d0208b63da26a0df97ce35c42a81469"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/adm-zip@0.5.16","externalReferences":[{"type":"vcs","url":"https://github.com/cthackers/adm-zip"},{"type":"vcs","url":"https://github.com/cthackers/adm-zip.git"}],"type":"library","bom-ref":"pkg:npm/adm-zip@0.5.16","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"aggregate-error","version":"3.1.0","description":"Create an error from multiple errors","scope":"optional","hashes":[{"alg":"SHA-512","content":"e08ed3774d6ab96fd1a6871f35ac85745564d6a4aea21d04ec9adb449d7a9c7d351e128543cf0836af5277e9ddef6cea4724a5afd0660c0f3194427abc932b60"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/aggregate-error@3.1.0","type":"library","bom-ref":"pkg:npm/aggregate-error@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"ajv-draft-04","version":"1.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"9afd344deea79986d1a790c2c1c971b6def257f8e85c93c64bb9ccfbdec6771beeb6d08e7e02372b8536e736e8c9e5f43be9b223c11196ec507b9c258cc99523"}],"purl":"pkg:npm/ajv-draft-04@1.0.0","type":"library","bom-ref":"pkg:npm/ajv-draft-04@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"ajv-formats-draft2019","version":"1.6.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"2503ef6afa64583bc8b01a76677dd49180ad5c24a95b81c3ded019fa82f888414e93da1b4197dfc74c80370102b7abf3afa113fbb1cde5ccd1caa5db9eafeed1"}],"purl":"pkg:npm/ajv-formats-draft2019@1.6.1","type":"library","bom-ref":"pkg:npm/ajv-formats-draft2019@1.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"ajv-formats","version":"3.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"f2252a979d04511fae51c7514371c3a9ae84572a3776870bf20e5627714d7169aeeb621b90652e7bfa44c8b056f1518a2ae7133e0a9e92ce1f214d43038ca8c1"}],"purl":"pkg:npm/ajv-formats@3.0.1","type":"library","bom-ref":"pkg:npm/ajv-formats@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Evgeny Poberezkin","group":"","name":"ajv","version":"8.18.0","description":"Another JSON Schema Validator","scope":"optional","hashes":[{"alg":"SHA-512","content":"3e55cf78458c5cc67bb0f60e1ea983c8227371f36b52bddf18d2ad7b35f5e3291738422fc8af3577eab2771f3d298e4eef514a30f690daf05f04523934747adc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ajv@8.18.0","externalReferences":[{"type":"website","url":"https://ajv.js.org"}],"type":"library","bom-ref":"pkg:npm/ajv@8.18.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"nexdrew","group":"","name":"ansi-align","version":"3.0.1","description":"align-text with ANSI support for CLIs","scope":"optional","hashes":[{"alg":"SHA-512","content":"20e7f0c0117989ccce8e9fd6798e18c728ea005310a19b9f750583775f52104c5b54b357aafa73489fcced96b8fec08f990d3e191aaea00edb19c20d7317b0eb"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/ansi-align@3.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/nexdrew/ansi-align#readme"},{"type":"vcs","url":"git+https://github.com/nexdrew/ansi-align.git"}],"type":"library","bom-ref":"pkg:npm/ansi-align@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ansi-escapes","version":"4.3.2","description":"ANSI escape codes for manipulating the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"80a5e3e402eb29640bb181bd8e54d1991ff12a5bb11d5f99f501303488027ccd7fbb03cc0aecd55678799b04ddf8eb8165cc1220c6eab2c356466d65139d5069"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-escapes@4.3.2","type":"library","bom-ref":"pkg:npm/ansi-escapes@4.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ansi-escapes","version":"7.1.0","description":"ANSI escape codes for manipulating the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"61d86d09dd7db0a44a7c0014b2b702d70ce6e09bb3268897e29389a88a16daa98a8f95b31bf74bf2e509d37eb5cda5ed1ea2bb80484ec2d02dcfbb7762add7e6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-escapes@7.1.0","type":"library","bom-ref":"pkg:npm/ansi-escapes@7.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"ansi-regex","version":"5.0.1","description":"Regular expression for matching ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"aae2505e54d25062f62c7f52517a3c570b18e2ca1a9e1828e8b3529bce04d4b05c13cb373b4c29762473c91f73fd9649325316bf7eea38e6fda5d26531410a15"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-regex@5.0.1","type":"library","bom-ref":"pkg:npm/ansi-regex@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ansi-regex","version":"6.2.2","description":"Regular expression for matching ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"06add2992a721476968cf93c21ff7273ab2f33c739e9d079040b56e106f0e631d3c305d77132e844c9290c9a7a54bd17ce559a0874d7ae415444c6260f4b0baa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-regex@6.2.2","type":"library","bom-ref":"pkg:npm/ansi-regex@6.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"ansi-styles","version":"3.2.1","description":"ANSI escape codes for styling strings in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"553d1923a91945d4e1f18c89c3748c6d89bfbbe36a7ec03112958ed0f7fdb2af3f7bde16c713a93cac7d151d459720ad3950cd390fbc9ed96a17189173eaf9a8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-styles@3.2.1","type":"library","bom-ref":"pkg:npm/ansi-styles@3.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"ansi-styles","version":"4.3.0","description":"ANSI escape codes for styling strings in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"cdb07dac22404f5adb8e25436f686a2851cd60bc60b64f0d511c59dc86700f717a36dc5b5d94029e74a2d4b931f880e885d3e5169db6db05402c885e64941212"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-styles@4.3.0","type":"library","bom-ref":"pkg:npm/ansi-styles@4.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ansi-styles","version":"6.2.3","description":"ANSI escape codes for styling strings in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"e038fa336f0907ea001fc9059132d4a3e6b68f038592ea9bdf2b9c53408035c45151bc52d1c3f49d96021a371cdc1357c1122c5159831a0cdac267bbcef247be"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ansi-styles@6.2.3","type":"library","bom-ref":"pkg:npm/ansi-styles@6.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"argparse","version":"2.0.1","description":"CLI arguments parser. Native port of python's argparse.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f3ef56a9e6db173a57f4e47e59ae8edbd6ac22881e44ccdc1ad00835da4c1c7c80835d1fd3969215505b704a867ff3d7c35123019faadbf6c4060dc3beeacadd"}],"licenses":[{"license":{"id":"Python-2.0","url":"https://opensource.org/licenses/Python-2.0"}}],"purl":"pkg:npm/argparse@2.0.1","type":"library","bom-ref":"pkg:npm/argparse@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"array-find-index","version":"1.0.2","description":"ES2015 `Array#findIndex()` ponyfill","scope":"optional","hashes":[{"alg":"SHA-512","content":"3351d0c885dc046b55cb006df1655d8a6fa5acd68aed51e9f7d42de6948dce25f39ca1d588805b5d6b5f453a4416917f73815d7f1340b020b03e9e69841609b7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/array-find-index@1.0.2","type":"library","bom-ref":"pkg:npm/array-find-index@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steve Mao (https://github.com/stevemao)","group":"","name":"array-ify","version":"1.0.0","description":"Turn anything into an array","scope":"optional","hashes":[{"alg":"SHA-512","content":"73900c7f7e1b29dbcf850eed04686a92028d53332be1652cf960ed0b665418e52771bc4a313beac5872d8ac796dfe2f86c0f1e73e19c67afc0fc55b89bcba49e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/array-ify@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/stevemao/array-ify"}],"type":"library","bom-ref":"pkg:npm/array-ify@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Caolan McMahon","group":"","name":"async","version":"3.2.6","description":"Higher-order functions and common patterns for asynchronous code","scope":"optional","hashes":[{"alg":"SHA-512","content":"86d0940e5c72c822cc81a337c578340b42d6db1a9fb90ea9d39a42108b17bb243e6b592860a4ee04ccd13709b26df2e0bc90cc774af52d39f8f84d138ba0b600"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/async@3.2.6","externalReferences":[{"type":"vcs","url":"https://caolan.github.io/async/"},{"type":"vcs","url":"https://github.com/caolan/async.git"}],"type":"library","bom-ref":"pkg:npm/async@3.2.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ryan Zimmerman ","group":"","name":"at-least-node","version":"1.0.0","description":"Lightweight Node.js version sniffing/comparison","scope":"optional","hashes":[{"alg":"SHA-512","content":"faafedec492fd440d8da5e8675ae8b2e25f5e2b53d4d5db459ade87de426c0f1596ce328f435eb2db3a315a69c9645ca5a27486a8a7000e6d00eac16b46523aa"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/at-least-node@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/RyanZim/at-least-node#readme"},{"type":"vcs","url":"git+https://github.com/RyanZim/at-least-node.git"}],"type":"library","bom-ref":"pkg:npm/at-least-node@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"David Mark Clements (@davidmarkclem)","group":"","name":"atomic-sleep","version":"1.0.0","description":"Zero CPU overhead, zero dependency, true event-loop blocking sleep","scope":"optional","hashes":[{"alg":"SHA-512","content":"90d3a30ea021ee9c745d6348fb841bce8891fe74e41c058db9ddaebe726ab83d7fc796bb11064c253d00733a8ad109faee863f4d34352db50a6a3669a7723db5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/atomic-sleep@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/davidmarkclements/atomic-sleep#readme"},{"type":"vcs","url":"git+https://github.com/davidmarkclements/atomic-sleep.git"}],"type":"library","bom-ref":"pkg:npm/atomic-sleep@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Julian Gruber (http://juliangruber.com)","group":"","name":"balanced-match","version":"1.0.2","description":"Match balanced character pairs, like \"{\" and \"}\"","scope":"optional","hashes":[{"alg":"SHA-512","content":"de849e50ed13315ebb84dd4099b5ec2b8c9aa94eed8e21e56f144364ea47d0a5bdf82797e1b440697d009f1b74b71d8cae94695b041a3f02252121098585393f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/balanced-match@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/juliangruber/balanced-match"},{"type":"vcs","url":"git://github.com/juliangruber/balanced-match.git"}],"type":"library","bom-ref":"pkg:npm/balanced-match@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"balanced-match","version":"4.0.4","description":"Match balanced character pairs, like \"{\" and \"}\"","scope":"optional","hashes":[{"alg":"SHA-512","content":"04bae011c453c17da8ea01b118e08dc8cbc64a9df96287ee633c3d87520c4d198aaadb40659554ebb6dd6fd3ebdaf50703cfa3de2dad25f8cee82ebee26c864c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/balanced-match@4.0.4","externalReferences":[{"type":"vcs","url":"git://github.com/juliangruber/balanced-match.git"}],"type":"library","bom-ref":"pkg:npm/balanced-match@4.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"T. Jameson Little ","group":"","name":"base64-js","version":"1.5.1","description":"Base64 encoding/decoding in pure JS","scope":"optional","hashes":[{"alg":"SHA-512","content":"00aa5a6251e7f2de1255b3870b2f9be7e28a82f478bebb03f2f6efadb890269b3b7ca0d3923903af2ea38b4ad42630b49336cd78f2f0cf1abc8b2a68e35a9e58"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/base64-js@1.5.1","externalReferences":[{"type":"vcs","url":"https://github.com/beatgammit/base64-js"},{"type":"vcs","url":"git://github.com/beatgammit/base64-js.git"}],"type":"library","bom-ref":"pkg:npm/base64-js@1.5.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"bl","version":"4.1.0","description":"Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!","scope":"optional","hashes":[{"alg":"SHA-512","content":"d56d3b70cf604ba0dc2e97ab65f1528fe6d62ed68f1923875a13e21b35e6bd525b44b746f36b07fca9fc12d5b556a595039e0029fda1e64e416e721bc05de1eb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/bl@4.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/rvagg/bl"},{"type":"vcs","url":"https://github.com/rvagg/bl.git"}],"type":"library","bom-ref":"pkg:npm/bl@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"body-parser","version":"2.2.2","description":"Node.js body parsing middleware","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0fe559004ca94dc1c82fc62d2f334a7f0f79f60b7111798557f83358b394e36459ffa10b768fee2c549b5232bd7ca5d46bf308d370197a2e857e154c333e634"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/body-parser@2.2.2","type":"library","bom-ref":"pkg:npm/body-parser@2.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"boolean","version":"3.2.0","description":"boolean converts lots of things to boolean.","scope":"optional","hashes":[{"alg":"SHA-512","content":"774208fc63bdb9ff657d41c7d8142c8f1cd125905db2382c0625b806f85693fdeaa0ac1016320354dd7d3df5fc1760ffafd3c2313b4b5a3615085ae9798533b3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/boolean@3.2.0","externalReferences":[{"type":"vcs","url":"git://github.com/thenativeweb/boolean.git"}],"type":"library","bom-ref":"pkg:npm/boolean@3.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"boxen","version":"8.0.1","description":"Create boxes in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"1773c7e64e63bb1a26e3192dca74bb328158f8d5161f92c2e029c7d75601f0d3dec3e1cba6604b0b26d2004c9bd85fb8a515e1ba15aa15eb2841de8303273687"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/boxen@8.0.1","type":"library","bom-ref":"pkg:npm/boxen@8.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Julian Gruber (http://juliangruber.com)","group":"","name":"brace-expansion","version":"1.1.13","description":"Brace expansion as known from sh/bash","scope":"optional","hashes":[{"alg":"SHA-512","content":"f592e9ad64ba1043668443a98c2616d9cf159263af72420965f92beeb056e9d39b99f809fcbd46a52616e47a68f650f3e03d7e9f40a4cfcad4ec5c070d0886ff"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/brace-expansion@1.1.13","externalReferences":[{"type":"vcs","url":"https://github.com/juliangruber/brace-expansion"},{"type":"vcs","url":"git://github.com/juliangruber/brace-expansion.git"}],"type":"library","bom-ref":"pkg:npm/brace-expansion@1.1.13","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Julian Gruber (http://juliangruber.com)","group":"","name":"brace-expansion","version":"2.1.0","description":"Brace expansion as known from sh/bash","scope":"optional","hashes":[{"alg":"SHA-512","content":"4cdd64099020760c1e261596a60298ad068c34770350b1e45b0408b2976d8d5e18e5abab45d6698c0aa7eb25f714fa9303d9e01c2738849c4c00c8067ef7bce7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/brace-expansion@2.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/juliangruber/brace-expansion"},{"type":"vcs","url":"git://github.com/juliangruber/brace-expansion.git"}],"type":"library","bom-ref":"pkg:npm/brace-expansion@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"brace-expansion","version":"5.0.5","description":"Brace expansion as known from sh/bash","scope":"optional","hashes":[{"alg":"SHA-512","content":"559ce72e0b70867f8c69cb7db5f8b0c7ae1f03d7ab1c7fcc0971147c1ff46d7ffa173ea7cb91064d7625b4ca1caa0e31140419b673b70c75965e2f118ae37b71"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/brace-expansion@5.0.5","externalReferences":[{"type":"vcs","url":"git+ssh://git@github.com/juliangruber/brace-expansion.git"}],"type":"library","bom-ref":"pkg:npm/brace-expansion@5.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"braces","version":"3.0.3","description":"Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c906d780efce499543d88b222e5ae8fbb1dfe90d7c109aca484b1da0ccca78f29772dde0bc3f282dc390748cc6ba9af9163f840def203bf9717350737cca71bc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/braces@3.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/micromatch/braces"}],"type":"library","bom-ref":"pkg:npm/braces@3.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"buffer","version":"5.7.1","description":"Node.js Buffer API, for the browser","scope":"optional","hashes":[{"alg":"SHA-512","content":"10773220f050e0148696f8c1d7a9392a0009dbb088b0763fd8906609145ea38f32f6b43731a533597dca56505ae14eccc97d361dd563d0aec2dd6681de3bbb15"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/buffer@5.7.1","externalReferences":[{"type":"vcs","url":"https://github.com/feross/buffer"},{"type":"vcs","url":"git://github.com/feross/buffer.git"}],"type":"library","bom-ref":"pkg:npm/buffer@5.7.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk (http://tjholowaychuk.com)","group":"","name":"bytes","version":"3.1.2","description":"Utility to parse a string bytes to bytes and vice-versa","scope":"optional","hashes":[{"alg":"SHA-512","content":"fcd7fb4f2cd3c7a4b7c9124e6ce015efde7aafc72bdbe3a3f000b976df3048fdc1400a1e5f9f0da07c8253c3fccc690d5d2b634d28ba7f33ba174a4175c61b12"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/bytes@3.1.2","type":"library","bom-ref":"pkg:npm/bytes@3.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Szymon Marczak","group":"","name":"cacheable-lookup","version":"5.0.4","description":"A cacheable dns.lookup(…) that respects the TTL","scope":"optional","hashes":[{"alg":"SHA-512","content":"dbf90db1c3e1a5cc6b3a280c6736e2585eddcfc8a585bfe72075371326625d65e97aafdabbca89f1585d7ed324b72de7ec68fa1c819a9501bca2204d07700980"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cacheable-lookup@5.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/szmarczak/cacheable-lookup#readme"},{"type":"vcs","url":"git+https://github.com/szmarczak/cacheable-lookup.git"}],"type":"library","bom-ref":"pkg:npm/cacheable-lookup@5.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Luke Childs (http://lukechilds.co.uk)","group":"","name":"cacheable-request","version":"7.0.4","description":"Wrap native HTTP requests with RFC compliant cache support","scope":"optional","hashes":[{"alg":"SHA-512","content":"bfea7aa2782cae9d324c66c95e38313e8c36f832fddc30123f891708329bf3f6f046db7d384177c218209240e418dce0716cb65da1786bc9d98250bbb8496c72"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cacheable-request@7.0.4","type":"library","bom-ref":"pkg:npm/cacheable-request@7.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Linus Unnebäck ","group":"","name":"cachedir","version":"2.3.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"03e15ece9e33c678ade856a70e6bfd12a5cd022defb7d0d6a79d7fef5504857ba46fb414bafb6ff77e3887dd5dc80c6e4cba12609154dbdf6acd1dedcf03c087"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cachedir@2.3.0","type":"library","bom-ref":"pkg:npm/cachedir@2.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"call-bind-apply-helpers","version":"1.0.2","description":"Helper functions around Function call/apply/bind, for use in `call-bind`","scope":"optional","hashes":[{"alg":"SHA-512","content":"4a9d5a6e52748af0e44b38dc68977112e9cde7f5ef92c149dac30115fabac74af285057fd9bfcac057b6d5c329987b4f3928a3f0af7dff049fa04b9339b9ae31"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/call-bind-apply-helpers@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/call-bind-apply-helpers#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/call-bind-apply-helpers.git"}],"type":"library","bom-ref":"pkg:npm/call-bind-apply-helpers@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"call-bound","version":"1.0.4","description":"Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.","scope":"optional","hashes":[{"alg":"SHA-512","content":"fb2b3df7b53dea9a382b1fc0069042aa103d12ec49690583420ef6f791f8841a61bf72198346e804abb0629b78617a7a319e4099942753fb72313951a5a49e8e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/call-bound@1.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/call-bound#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/call-bound.git"}],"type":"library","bom-ref":"pkg:npm/call-bound@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Eric McCarthy (http://www.limulus.net/)","group":"","name":"call-me-maybe","version":"1.0.2","description":"Let your JS API users either give you a callback or receive a promise","scope":"optional","hashes":[{"alg":"SHA-512","content":"1e95fae68d479ebf471f6e688c2d581acec70902ead0608e89b49a58447478da6027f675319bf699373bfb187a58e3f16d155c9a06efe21194fae490ff6c4565"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/call-me-maybe@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/limulus/call-me-maybe#readme"},{"type":"vcs","url":"git+https://github.com/limulus/call-me-maybe.git"}],"type":"library","bom-ref":"pkg:npm/call-me-maybe@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"callsites","version":"3.1.0","description":"Get callsites from the V8 stack trace API","scope":"optional","hashes":[{"alg":"SHA-512","content":"3fc06302c5ef652f95203508d7584709012fef8613ebb6148b924914d588a8bdb7e6c0668d7e3eab1f4cbaf96ce62bf234435cb71e3ac502d0dda4ee13bb2c69"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/callsites@3.1.0","type":"library","bom-ref":"pkg:npm/callsites@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"camelcase","version":"5.3.1","description":"Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`","scope":"optional","hashes":[{"alg":"SHA-512","content":"2f6f124c1d7bd27c164badd48ed944384ddd95d400a5a257664388d6e3057f37f7ad1b8f7a01da1deb3279ef98c50f96e92bd10d057a52b74e751891d79df026"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/camelcase@5.3.1","type":"library","bom-ref":"pkg:npm/camelcase@5.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"camelcase","version":"8.0.0","description":"Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`","scope":"optional","hashes":[{"alg":"SHA-512","content":"f1607725c6acdecc12be321e036caf089f8c8b2cf997566607a1c56fd475df5eddb7d2c2428b3083f046ae6026916544b334abae0e11c263b8eaa226d8e12748"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/camelcase@8.0.0","type":"library","bom-ref":"pkg:npm/camelcase@8.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"chalk","version":"2.4.2","description":"Terminal string styling done right","scope":"optional","hashes":[{"alg":"SHA-512","content":"32d8be7fd96924d730178b5657cfcead34ed1758198be7fc16a97201da2eada95c156150585dbe3600874a18e409bf881412eaf5bb99c04d71724414e29792b9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chalk@2.4.2","type":"library","bom-ref":"pkg:npm/chalk@2.4.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"chalk","version":"4.1.2","description":"Terminal string styling done right","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0a9db845c91217a54b9ecfc881326c846b89db8f820e432ba173fc32f6463bfd654f73020ef5503aebc3eef1190eefed06efa48b44e7b2c3d0a9434eb58b898"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chalk@4.1.2","type":"library","bom-ref":"pkg:npm/chalk@4.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"chalk","version":"5.3.0","description":"Terminal string styling done right","scope":"optional","hashes":[{"alg":"SHA-512","content":"74b8ad1bbf5df8657535bfd561c083162bc978ad618ae92df508d13553ac52d4f2d6b475609b26a46193677a89a2cfaec3b5a6585e3053005df63c63a1c142db"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chalk@5.3.0","type":"library","bom-ref":"pkg:npm/chalk@5.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"chalk","version":"5.6.2","description":"Terminal string styling done right","scope":"optional","hashes":[{"alg":"SHA-512","content":"ecdcc12f4acde9f3145be7fb03a228e21e34a90946fb11a6b4cc5f6e71ff2bb4c0b6df094165502beea0d145ca31e549a3257912490db04ad0f29543ddb2c76c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chalk@5.6.2","type":"library","bom-ref":"pkg:npm/chalk@5.6.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dmitry Shirokov ","group":"","name":"chardet","version":"0.7.0","description":"Character detector","scope":"optional","hashes":[{"alg":"SHA-512","content":"993f220dcae1d37a83191466a00da1981267c69965311fb4ff4aa5ce3a99112e8d762583719902340938acf159f50f39af6eee9e488d360f193a2c195c11f070"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chardet@0.7.0","externalReferences":[{"type":"vcs","url":"https://github.com/runk/node-chardet"}],"type":"library","bom-ref":"pkg:npm/chardet@0.7.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dmitry Shirokov ","group":"","name":"chardet","version":"2.1.1","description":"Character encoding detector","scope":"optional","hashes":[{"alg":"SHA-512","content":"3ec7b31f5aea755f55bf2361c71396df6fddef9af4d4d63b4d00a63aaa26468d7965238a6e94c556c7e3821c68e899684140869c7e24d4b1aed11e3208b95641"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/chardet@2.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/runk/node-chardet"},{"type":"vcs","url":"https://github.com/runk/node-chardet.git"}],"type":"library","bom-ref":"pkg:npm/chardet@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"chownr","version":"1.1.4","description":"like `chown -R`","scope":"optional","hashes":[{"alg":"SHA-512","content":"8c9d1bab36b296626d567360cd37923acf033dabe96d8804aff6f460bf3fd863b7c4912122716684a3149c42508d9ba62bb297185854cbcf4faec25695a90156"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/chownr@1.1.4","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/chownr.git"}],"type":"library","bom-ref":"pkg:npm/chownr@1.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"chownr","version":"3.0.0","description":"like `chown -R`","scope":"optional","hashes":[{"alg":"SHA-512","content":"f88c7363d05939077f5ee60f466aef1158c5fe7aa3e64813e2412aed5a1fac3a0cd4cc6846311692b082dc4b4b8b9f5355ac314c09fea2b27015072ba84375fa"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/chownr@3.0.0","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/chownr.git"}],"type":"library","bom-ref":"pkg:npm/chownr@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Thomas Watson Steen (https://twitter.com/wa7son)","group":"","name":"ci-info","version":"4.4.0","description":"Get details about the current Continuous Integration environment","scope":"optional","hashes":[{"alg":"SHA-512","content":"efb3d2c1eadc09953615ce2c5fde1e17c93c3f1b5ee890302f8fc80992c58c92ea7a0b3b902b80b2aaa3ffbd0d405e93bc3a6016392e6d0076156d9965d76f42"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ci-info@4.4.0","externalReferences":[{"type":"vcs","url":"https://github.com/watson/ci-info"}],"type":"library","bom-ref":"pkg:npm/ci-info@4.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"clean-stack","version":"2.2.0","description":"Clean up error stack traces","scope":"optional","hashes":[{"alg":"SHA-512","content":"e1d882f4769313e29100c5a10e1ac63840a0599c687af31ce5396439b32a352b1553ad8f6335d9fd23138f3c8600517562eb20c46712593117061a7408fc10d4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/clean-stack@2.2.0","type":"library","bom-ref":"pkg:npm/clean-stack@2.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"cli-boxes","version":"3.0.0","description":"Boxes for use in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"fe5cc6a4458bffc3df23406604e3d1c29d1cff014d5f545d50c2f78cafd1712040f53f26643750a6a6012ad0854ce7d06f03ea5843a98ea5be1676b2734f7af6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-boxes@3.0.0","type":"library","bom-ref":"pkg:npm/cli-boxes@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"cli-cursor","version":"3.1.0","description":"Toggle the CLI cursor","scope":"optional","hashes":[{"alg":"SHA-512","content":"23fcc7030b0a7fd16a1a85cce16591002a1bf7e48dba465377de03585e7b138b68a2e46e95b0b171487a44a5043909584c7267ce43ccc92bcf35a6922cd7cb67"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-cursor@3.1.0","type":"library","bom-ref":"pkg:npm/cli-cursor@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"cli-cursor","version":"5.0.0","description":"Toggle the CLI cursor","scope":"optional","hashes":[{"alg":"SHA-512","content":"6828f83b9c0acacce33260d3e2d663f77931cb274dfcae733d64827baff4015fc0035a6a7b9641230d1ad997cf415ee52f9ff26f91ec52b789e94140175b4443"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-cursor@5.0.0","type":"library","bom-ref":"pkg:npm/cli-cursor@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"cli-spinners","version":"2.9.2","description":"Spinners for use in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"cb0a95fb9326c8be04ef26d780acace03ba065b5f4142e8b9f0ae18eeca42239caf64f0e41a710edac462a78c35d63619ecd31a2dddb648e61e791fcca8f5c26"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-spinners@2.9.2","type":"library","bom-ref":"pkg:npm/cli-spinners@2.9.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Talmage","group":"","name":"cli-table3","version":"0.6.5","description":"Pretty unicode tables for the command line. Based on the original cli-table.","scope":"required","hashes":[{"alg":"SHA-512","content":"f96ff979f4d1ef2e47443ee0002c3dc908ea315bc430b04799ba0cfe43d66a6f87f879b2ae08e1e989dc54a2b5db6619917acbb9dcd3b80ba4530f459cc7fb21"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-table3@0.6.5","externalReferences":[{"type":"vcs","url":"https://github.com/cli-table/cli-table3"},{"type":"vcs","url":"https://github.com/cli-table/cli-table3.git"}],"type":"library","bom-ref":"pkg:npm/cli-table3@0.6.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"ImportedModules","value":"cli-table3"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/cli/dist/commands/bench.js#21"},{"location":"packages/cli/dist/commands/doctor.js#20"},{"location":"packages/cli/src/commands/bench.ts#22"},{"location":"packages/cli/src/commands/doctor.ts#21"}]}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"cli-truncate","version":"5.2.0","description":"Truncate a string to a specific width in the terminal","scope":"optional","hashes":[{"alg":"SHA-512","content":"c51c2f20e306adf3809ccd4962da90226b9a36d0c4bfdbfaa08600b382c81f04e229e7bcbb0bc88b7eb78a0b2c382d0ee54d388b802510db3bf4b40bbbdb4433"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cli-truncate@5.2.0","type":"library","bom-ref":"pkg:npm/cli-truncate@5.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ilya Radchenko ","group":"","name":"cli-width","version":"3.0.0","description":"Get stdout window width, with two fallbacks, tty and then a default.","scope":"optional","hashes":[{"alg":"SHA-512","content":"171aa990f3f0bb51e3b8df773a67e6e21f2e21a9d7a1f5b44715445b793944ac7e9892584ad873361a77d8acf1c72dd800467f0dcfc458dd6f651634fa43a16f"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/cli-width@3.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/knownasilya/cli-width"}],"type":"library","bom-ref":"pkg:npm/cli-width@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ilya Radchenko ","group":"","name":"cli-width","version":"4.1.0","description":"Get stdout window width, with two fallbacks, tty and then a default.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a2eb99778fdd9b64b0e469aacba6c6c8d34d7b5aadf51a66c6f78b48eeca720b139d4ed15dfb30fbf6ee9161a8d5a6e006230089cd3af2b72566c3b82169a6c5"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/cli-width@4.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/knownasilya/cli-width"}],"type":"library","bom-ref":"pkg:npm/cli-width@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"clipanion","version":"4.0.0-rc.4","scope":"optional","hashes":[{"alg":"SHA-512","content":"09790c43153ab3d1a494eff57fbd7876428132ed65a294b558517407ca380313f2911d61a68cf14a2519e5951e0637cf8025aa6dc34eb59545841f0b91fa75f9"}],"purl":"pkg:npm/clipanion@4.0.0-rc.4","type":"library","bom-ref":"pkg:npm/clipanion@4.0.0-rc.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Coe ","group":"","name":"cliui","version":"8.0.1","description":"easily create complex multi-column command-line-interfaces","scope":"optional","hashes":[{"alg":"SHA-512","content":"05278d9f2bacef90b8fff350f6042dd7f72c4d7ca8ffc49bf9a7cb024cc0a6d16e32ca1df4716890636e759a62fe8415ef786754afac47ee4f55131df83afb61"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/cliui@8.0.1","type":"library","bom-ref":"pkg:npm/cliui@8.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Luke Childs (http://lukechilds.co.uk)","group":"","name":"clone-response","version":"1.0.3","description":"Clone a Node.js HTTP response stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"44ea0bf788c91f675454c2f663fe4f10335a48781e39d48389c5324bb8b3705eb71bab1373f1538cbb9be1bf0897d4bc4b46de39f62dd13680e6abc52bec34c0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/clone-response@1.0.3","externalReferences":[{"type":"vcs","url":"git+https://github.com/sindresorhus/clone-response.git"}],"type":"library","bom-ref":"pkg:npm/clone-response@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Paul Vorbach (http://paul.vorba.ch/)","group":"","name":"clone","version":"1.0.4","description":"deep cloning of objects and arrays","scope":"optional","hashes":[{"alg":"SHA-512","content":"2501d9d90316ea5dda1ff8fac42a904e163ff4e1f80fff65b37e1c8245018847a87114d4d38b477ca3c1b142b53ea64251033b1a20342085c94ae5c723ae0a6e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/clone@1.0.4","externalReferences":[{"type":"vcs","url":"git://github.com/pvorb/node-clone.git"}],"type":"library","bom-ref":"pkg:npm/clone@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Heather Arthur ","group":"","name":"color-convert","version":"1.9.3","description":"Plain color conversion functions","scope":"optional","hashes":[{"alg":"SHA-512","content":"41f014b5dfaf15d02d150702f020b262dd5f616c52a8088ad9c483eb30c1f0dddca6c10102f471a7dcce1a0e86fd21c7258013f3cfdacff22e0c600bb0d55b1a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/color-convert@1.9.3","type":"library","bom-ref":"pkg:npm/color-convert@1.9.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Heather Arthur ","group":"","name":"color-convert","version":"2.0.1","description":"Plain color conversion functions","scope":"optional","hashes":[{"alg":"SHA-512","content":"4511023ec8fb8aeff16f9a0a61cb051d2a6914d9ec8ffe763954d129be333f9a275f0545df3566993a0d70e7c60be0910e97cafd4e7ce1f320dfc64709a12529"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/color-convert@2.0.1","type":"library","bom-ref":"pkg:npm/color-convert@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"DY ","group":"","name":"color-name","version":"1.1.3","description":"A list of color names and its values","scope":"optional","hashes":[{"alg":"SHA-512","content":"ef67d27a784cc361d931354778203d2829a91086f35a242c8cdf811dc05b4bdbebd66b6dfaf2633dd92c20135498a016f131540cf24ae52514dd0844f4d1170f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/color-name@1.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/dfcreative/color-name"}],"type":"library","bom-ref":"pkg:npm/color-name@1.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"DY ","group":"","name":"color-name","version":"1.1.4","description":"A list of color names and its values","scope":"optional","hashes":[{"alg":"SHA-512","content":"74ecbedc0b96ddadb035b64722e319a537208c6b8b53fb812ffb9b71917d3976c3a3c7dfe0ef32569e417f479f4bcb84a18a39ab8171edd63d3a04065e002c40"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/color-name@1.1.4","externalReferences":[{"type":"vcs","url":"https://github.com/colorjs/color-name"}],"type":"library","bom-ref":"pkg:npm/color-name@1.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jorge Bucaran","group":"","name":"colorette","version":"2.0.20","description":"🌈Easily set your terminal text color & styles.","scope":"optional","hashes":[{"alg":"SHA-512","content":"21f103c70a1622391e5cbd5e5dc0e2a30e146ca8e12ddabafc4b92551f4630deca547debf6043cddeef786ccf535dd53de28dde71bf5c1c59160ef83ea4088db"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/colorette@2.0.20","type":"library","bom-ref":"pkg:npm/colorette@2.0.20","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matthew Conlen","group":"","name":"command-exists","version":"1.2.9","description":"check whether a command line command exists in the current environment","scope":"optional","hashes":[{"alg":"SHA-512","content":"2d343f48673eb345dcd05bb959a2a7474622ca06649bd78a16fc92f9f46c53bfd958517cca414ce8f73d68255fd7ec5ab0e399a4edc1015815acabeca8a1d5ef"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/command-exists@1.2.9","externalReferences":[{"type":"vcs","url":"https://github.com/mathisonian/command-exists"},{"type":"vcs","url":"http://github.com/mathisonian/command-exists"}],"type":"library","bom-ref":"pkg:npm/command-exists@1.2.9","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk ","group":"","name":"commander","version":"14.0.3","description":"the complete solution for node.js command-line programs","scope":"required","hashes":[{"alg":"SHA-512","content":"1fecb4268fd3d5167da8f3f8121d6991c41c2d1825ada25a48ba323ad1f1bba01aa648d6542cb64a2b75410e31dc39e0f2a0e54ac6447adee0e4fab4e8cbd383"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/commander@14.0.3","externalReferences":[{"type":"vcs","url":"git+https://github.com/tj/commander.js.git"}],"type":"library","bom-ref":"pkg:npm/commander@14.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"ImportedModules","value":"commander,Command,commander/Command"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/cli/dist/commands/group.js#26"},{"location":"packages/cli/dist/index.js#10"},{"location":"packages/cli/src/commands/group.ts#28"},{"location":"packages/cli/src/index.ts#11"}]}},{"author":"TJ Holowaychuk ","group":"","name":"commander","version":"2.20.3","description":"the complete solution for node.js command-line programs","scope":"required","hashes":[{"alg":"SHA-512","content":"1a956498cf2f176bd05248f62ef6660f7e49c5e24e2c2c09f5c524ba0ca4da7ba16efdfe989be92d862dfb4f9448cc44fa88fe7b2fe52449e1670ef9c7f38c71"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/commander@2.20.3","externalReferences":[{"type":"vcs","url":"https://github.com/tj/commander.js.git"}],"type":"library","bom-ref":"pkg:npm/commander@2.20.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"ImportedModules","value":"commander,Command,commander/Command"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/cli/dist/commands/group.js#26"},{"location":"packages/cli/dist/index.js#10"},{"location":"packages/cli/src/commands/group.ts#28"},{"location":"packages/cli/src/index.ts#11"}]}},{"author":"Jim Cummins (https://github.com/jimthedev)","group":"","name":"commitizen","version":"4.3.1","description":"Git commit, but play nice with conventions.","scope":"optional","hashes":[{"alg":"SHA-512","content":"83000f0154f2fe3e5870e39e6dc0912228e7fa64a35890be2182a2bd3bba686f048bfb1ca1781fb0c467b8093a6f4191b2d7b6278346c5574c3776697cd6956b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/commitizen@4.3.1","externalReferences":[{"type":"vcs","url":"https://github.com/commitizen/cz-cli"},{"type":"vcs","url":"https://github.com/commitizen/cz-cli.git"}],"type":"library","bom-ref":"pkg:npm/commitizen@4.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steve Mao (https://github.com/stevemao)","group":"","name":"compare-func","version":"2.0.0","description":"Get a compare function for array to sort","scope":"optional","hashes":[{"alg":"SHA-512","content":"cc78a0e4dfad3d6011a280676f4671d4c15c75fa7226b7d32776392fe205bd49bcaccef8847cfaeb3d20c34c78b628e1e42a2b2a42940a75bcd91daf9a978244"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/compare-func@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/stevemao/compare-func"}],"type":"library","bom-ref":"pkg:npm/compare-func@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"concat-map","version":"0.0.1","description":"concatenative mapdashery","scope":"optional","hashes":[{"alg":"SHA-512","content":"fd2aefe1db30c903417e8846a73f68e986f71b3dd2ad40ea047e6b4ee84647b6a1b656d82a7571c366c214c4658da03b1171da5d9f30b07768745bdb9212a6aa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/concat-map@0.0.1","externalReferences":[{"type":"vcs","url":"git://github.com/substack/node-concat-map.git"}],"type":"library","bom-ref":"pkg:npm/concat-map@0.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"content-disposition","version":"1.1.0","description":"Create and parse Content-Disposition header","scope":"optional","hashes":[{"alg":"SHA-512","content":"e634421fd67ff8344feeb92f63cdc1fb2188197f7a3987499b39e0aa7c364814b1a8214f774c36926dece626b0a465fc92b9f0486985d055b9361af41df6a2de"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/content-disposition@1.1.0","type":"library","bom-ref":"pkg:npm/content-disposition@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"content-type","version":"1.0.5","description":"Create and parse HTTP Content-Type header","scope":"optional","hashes":[{"alg":"SHA-512","content":"9d38ea7dc045122a4a7570afe180d05827e670b64a9bcd65745d29028a53bf2ac51956dc47a3ff54001de46ecdfb4b53afc42a894d2d15a743e852b836d27038"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/content-type@1.0.5","type":"library","bom-ref":"pkg:npm/content-type@1.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steve Mao","group":"","name":"conventional-changelog-angular","version":"8.3.1","description":"Angular preset for conventional-changelog.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ea07c8de8b572b93e1e437c2388d5d6e5afe90ddc5026e5af7b858a1092a359c4e6986b958a7d71fe027a6c992fa2507da68150b60a0d90c3d9b938a72636b22"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/conventional-changelog-angular@8.3.1","externalReferences":[{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular#readme"},{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog.git"}],"type":"library","bom-ref":"pkg:npm/conventional-changelog-angular@8.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Coe","group":"","name":"conventional-changelog-conventionalcommits","version":"9.3.1","description":"Conventionalcommits.org preset for conventional-changelog.","scope":"optional","hashes":[{"alg":"SHA-512","content":"75362da4869c46971982bbc162f05f02b3262b6c6f229bf64dac4cd3f648e4206d354cef176c74b75e47b1b440056a6b4ba50f9af8fe3f31d58d2c78a8054acb"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/conventional-changelog-conventionalcommits@9.3.1","externalReferences":[{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits#readme"},{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog.git"}],"type":"library","bom-ref":"pkg:npm/conventional-changelog-conventionalcommits@9.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Anders D. Johnson","group":"","name":"conventional-commit-types","version":"3.0.0","description":"List of conventional commit types.","scope":"optional","hashes":[{"alg":"SHA-512","content":"4a69826273a7892b006bd1aa58e78ba9cd7bf657ebe5346ee5be1014391bb2b2794d98cf271f39c2d3abdf39fed5d6de3625c32863db67bd8829b3e12d787f2e"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/conventional-commit-types@3.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/commitizen/conventional-commit-types.git"}],"type":"library","bom-ref":"pkg:npm/conventional-commit-types@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steve Mao (https://github.com/stevemao)","group":"","name":"conventional-commits-parser","version":"6.4.0","description":"Parse raw conventional commits.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b6f460ec5201365c8fce3746f3059194f1d02491c8ec3ca586d446794f4babe26ea0f87904aa4f457f3765d2ebbd7b8e4aee44a3f7bb4b3390854e07776322bb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/conventional-commits-parser@6.4.0","externalReferences":[{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser#readme"},{"type":"vcs","url":"https://github.com/conventional-changelog/conventional-changelog.git"}],"type":"library","bom-ref":"pkg:npm/conventional-commits-parser@6.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk ","group":"","name":"cookie-signature","version":"1.2.2","description":"Sign and unsign cookies","scope":"optional","hashes":[{"alg":"SHA-512","content":"0fbeae53bdee9525eb0f5517178284d93331555c21b270a07c0c9383d93c3fa2866639572ab38b7b874940a2370718b9c237ac6681572561253560633d931b86"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cookie-signature@1.2.2","externalReferences":[{"type":"vcs","url":"https://github.com/visionmedia/node-cookie-signature.git"}],"type":"library","bom-ref":"pkg:npm/cookie-signature@1.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Roman Shtylman ","group":"","name":"cookie","version":"0.7.2","description":"HTTP server cookie parsing and serialization","scope":"optional","hashes":[{"alg":"SHA-512","content":"ca48b95e72ae7fbe74979d2e193965b7a90a20b6389d0d5e34841ab685c40726797568272aa6e7aa64eb044e41e0dee5acc24a436cad58c8933b6a34bfa130ff"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cookie@0.7.2","type":"library","bom-ref":"pkg:npm/cookie@0.7.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Troy Goode (https://github.com/troygoode/)","group":"","name":"cors","version":"2.8.6","description":"Node.js CORS middleware","scope":"optional","hashes":[{"alg":"SHA-512","content":"b49b590411c0eaf8c801a17a12721aaba95a0413fd6aafd8de8b9526311fa076d105c1c100761c321ff0f0b0eb9363ef20c32af209a8a5ae43e15f1199baf11b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cors@2.8.6","type":"library","bom-ref":"pkg:npm/cors@2.8.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"cosmiconfig-typescript-loader","version":"6.1.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"b49d70df965152233915e4f34fb0ed61600516fdfb64ba924641a2da87822b580f86f6968e402d7d7bcb9af135a517f1c69f5a428e9b6bf3ef83574a8f4e43e2"}],"purl":"pkg:npm/cosmiconfig-typescript-loader@6.1.0","type":"library","bom-ref":"pkg:npm/cosmiconfig-typescript-loader@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"cosmiconfig","version":"9.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"8adbcbe61f1111300298e4c573851f23207645f1078bbd40c7a13f3e2bd5c6af4db1e29b396a5ec8710e21b980c35aecf0093456eaec40dc30ee57fb62d530ce"}],"purl":"pkg:npm/cosmiconfig@9.0.0","type":"library","bom-ref":"pkg:npm/cosmiconfig@9.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"cosmiconfig","version":"9.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"86be22870f8306a72faec1038a844edf5675ef1ef5a5462835eff887a674c01ef6a7b314efff601fc437b35d8d1611cf7d8041395deac9f5319abfd89f7b219d"}],"purl":"pkg:npm/cosmiconfig@9.0.1","type":"library","bom-ref":"pkg:npm/cosmiconfig@9.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"André Cruz ","group":"","name":"cross-spawn","version":"7.0.6","description":"Cross platform child_process#spawn and child_process#spawnSync","scope":"optional","hashes":[{"alg":"SHA-512","content":"b95d903963f69d6ceccb668ca7c69189b862f5d9731791e0879487681f4e893184c834e2249cb1d2ecb9d505ddc966ed00736e6b85c9cd429c6b73b3294777bc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cross-spawn@7.0.6","externalReferences":[{"type":"vcs","url":"https://github.com/moxystudio/node-cross-spawn"}],"type":"library","bom-ref":"pkg:npm/cross-spawn@7.0.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jim Cummins ","group":"","name":"cz-conventional-changelog","version":"3.3.0","description":"Commitizen adapter following the conventional-changelog format.","scope":"optional","hashes":[{"alg":"SHA-512","content":"538eba7c8cd4e54db67844b995336235b67e77c75f70772cb07e28ed0b1d59a09c46cfdf7883c2c4a6125a460136ce669f2eccbc47f0a532d68ef6e6f7885e73"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/cz-conventional-changelog@3.3.0","externalReferences":[{"type":"vcs","url":"https://github.com/commitizen/cz-conventional-changelog"},{"type":"vcs","url":"https://github.com/commitizen/cz-conventional-changelog.git"}],"type":"library","bom-ref":"pkg:npm/cz-conventional-changelog@3.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steven Levithan","group":"","name":"dateformat","version":"4.6.3","description":"A node.js package for Steven Levithan's excellent dateFormat() function.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d8fd29d29146cc74b910c9e1771422eda24dfa23217ae7745211b87651350cb025bcbf091e32494d7fc24a6e095f057429ae671a4df30b999c6f96d4414c7130"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/dateformat@4.6.3","externalReferences":[{"type":"vcs","url":"https://github.com/felixge/node-dateformat"},{"type":"vcs","url":"https://github.com/felixge/node-dateformat.git"}],"type":"library","bom-ref":"pkg:npm/dateformat@4.6.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Josh Junon (https://github.com/qix-)","group":"","name":"debug","version":"4.4.3","description":"Lightweight debugging utility for Node.js and the browser","scope":"optional","hashes":[{"alg":"SHA-512","content":"446c305a7c10be455f6af295b76d8518bc3ec5849dcc04709b4aeee83853540dee994e6165cdbc57790ee2cb6062bcab4e52e9baf808f468a28e5b408cd6dca8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/debug@4.4.3","externalReferences":[{"type":"vcs","url":"git://github.com/debug-js/debug.git"}],"type":"library","bom-ref":"pkg:npm/debug@4.4.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"decompress-response","version":"6.0.0","description":"Decompress a HTTP response if needed","scope":"optional","hashes":[{"alg":"SHA-512","content":"696df9c9933a05bff8a099599dc307d8b0a866d2574d1c444b5eef137868462a305369161da24a1644810e70d1f9c9bd27ef5085799113221fbf4a638bd7a309"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/decompress-response@6.0.0","type":"library","bom-ref":"pkg:npm/decompress-response@6.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Desmond Brand (http://desmondbrand.com)","group":"","name":"dedent","version":"0.7.0","description":"An ES6 string tag that strips indentation from multi-line strings","scope":"optional","hashes":[{"alg":"SHA-512","content":"43a7ca50faa7007032862520154ec15332e2bf491df2c687f5a97bb67bb943fa248fa767ba9c724e01480635732404dd7c8026f4d02cbd73738da29af9bc55c8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/dedent@0.7.0","externalReferences":[{"type":"vcs","url":"https://github.com/dmnd/dedent"},{"type":"vcs","url":"git://github.com/dmnd/dedent.git"}],"type":"library","bom-ref":"pkg:npm/dedent@0.7.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Viacheslav Lotsmanov ","group":"","name":"deep-extend","version":"0.6.0","description":"Recursive object extending","scope":"optional","hashes":[{"alg":"SHA-512","content":"2ce1f120e68f61d1e5251b4241f0c8559b5fc3fb9f33cfab563eb8f51207cdc9bfbc6c1045716de8e3ea2055ac9b65c432b34812d591eb8b18d4b10a0f6bc038"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/deep-extend@0.6.0","externalReferences":[{"type":"vcs","url":"https://github.com/unclechu/node-deep-extend"},{"type":"vcs","url":"git://github.com/unclechu/node-deep-extend.git"}],"type":"library","bom-ref":"pkg:npm/deep-extend@0.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Elijah Insua ","group":"","name":"defaults","version":"1.0.4","description":"merge single level defaults over a config object","scope":"optional","hashes":[{"alg":"SHA-512","content":"785b9a2e8cbf4716a5bf692bfa5a8c9549eb0d657ede3e299633882602c8848d39f0841f589eef5e1c84207bbe1ed0bbdfc9251802d8c4e2833b46d03f7b60f0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/defaults@1.0.4","externalReferences":[{"type":"vcs","url":"git://github.com/sindresorhus/node-defaults.git"}],"type":"library","bom-ref":"pkg:npm/defaults@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Szymon Marczak","group":"","name":"defer-to-connect","version":"2.0.1","description":"The safe way to handle the `connect` socket event","scope":"optional","hashes":[{"alg":"SHA-512","content":"e2dbedb5ea571b555a606ad189b93913025dd6de2e76e9d239531d2d200bea621dd62c78dfca0fc0f64c00b638d450a28ee90ed4bd2dc0d706b1dcd2edd1e00e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/defer-to-connect@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/szmarczak/defer-to-connect#readme"},{"type":"vcs","url":"git+https://github.com/szmarczak/defer-to-connect.git"}],"type":"library","bom-ref":"pkg:npm/defer-to-connect@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"define-data-property","version":"1.1.4","description":"Define a data property on an object. Will fall back to assignment in an engine without descriptors.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ac132f23396903cbfa13e489668a3ef87018aac2eb920ecc49f2229cc3c5866928af0ed7f9d39754942cf904faf731a4cccc9f0e720c3765a2775f8d6cbdd3f8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/define-data-property@1.1.4","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/define-data-property#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/define-data-property.git"}],"type":"library","bom-ref":"pkg:npm/define-data-property@1.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"define-properties","version":"1.2.1","description":"Define multiple non-enumerable properties at once. Uses `Object.defineProperty` when available; falls back to standard assignment in older engines.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f109902aa10048b7799f1d14d41d6890b1256d4baeb6d27f0276264576db6c60d687ab92db4f048c3e17aaafc8f702bbbb4bfa3b4f178535a7b795ed11b47a0e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/define-properties@1.2.1","externalReferences":[{"type":"vcs","url":"git://github.com/ljharb/define-properties.git"}],"type":"library","bom-ref":"pkg:npm/define-properties@1.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"depd","version":"2.0.0","description":"Deprecate all the things","scope":"optional","hashes":[{"alg":"SHA-512","content":"83b9c7e8fe9dc838a8268800006a6b1a90ad5489898693e4feba02cdd6f77c887ad7fb3f9cfb1f47aa27c8cc2408047f3a50b7c810b49444af52840402cb08af"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/depd@2.0.0","type":"library","bom-ref":"pkg:npm/depd@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"dependency-path","version":"9.2.8","description":"Utilities for working with symlinked node_modules","scope":"optional","hashes":[{"alg":"SHA-512","content":"4b43a120aeec23202ca61f21547fcb3024c32f78e8ccab65acfc7774c42b944da70255d3aae4d3f8070eb9fa610cc4d0a8b91f9f869cbdf89e9bd2352166788d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/dependency-path@9.2.8","externalReferences":[{"type":"vcs","url":"https://github.com/pnpm/pnpm/blob/main/packages/dependency-path#readme"}],"type":"library","bom-ref":"pkg:npm/dependency-path@9.2.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Brian Woodward (https://github.com/doowb)","group":"","name":"detect-file","version":"1.0.0","description":"Detects if a file exists and returns the resolved filepath.","scope":"optional","hashes":[{"alg":"SHA-512","content":"0ed08e2c6f7c3f4d3bc7bc2288e99f2347e2dde20ac9688b4c62763039d58bf134e255866dff89ceb447326d2b808219246b47a4aa5b5602d61ebbfcc57a5cdd"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/detect-file@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/doowb/detect-file"}],"type":"library","bom-ref":"pkg:npm/detect-file@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"detect-indent","version":"6.1.0","description":"Detect the indentation of code","scope":"optional","hashes":[{"alg":"SHA-512","content":"ade6244d424065bf6052e67646f542361547760eb64479c9ed6265f1fb4c8b876267a35695c88ecd037cf295214842c4c1f94986de28403bf417404c970698b4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/detect-indent@6.1.0","type":"library","bom-ref":"pkg:npm/detect-indent@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Lovell Fuller ","group":"","name":"detect-libc","version":"2.1.2","description":"Node.js module to detect the C standard library (libc) implementation family and version","scope":"optional","hashes":[{"alg":"SHA-512","content":"06d8f604e38ef37a375b21f9f5ef0c817b3111055c6ab9143a9118aee6c1d2eaf09cdd74c90dfae2bb22072535d67665a966199b4e62fe87fb8a8e26ce2841b5"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/detect-libc@2.1.2","externalReferences":[{"type":"vcs","url":"git://github.com/lovell/detect-libc.git"}],"type":"library","bom-ref":"pkg:npm/detect-libc@2.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ilya Kantor","group":"","name":"detect-node","version":"2.1.0","description":"Detect Node.JS (as opposite to browser environment) (reliable)","scope":"optional","hashes":[{"alg":"SHA-512","content":"4f4348b90a674ef14301336e1cde6ba0fc12046f37ac5b2e3be3175c7f7fdcdd5e15b9f8c1c3e3b6dbe330b10f589d11194620404edc1a04b7b4dc5ba8218cee"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/detect-node@2.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/iliakan/detect-node"},{"type":"vcs","url":"https://github.com/iliakan/detect-node"}],"type":"library","bom-ref":"pkg:npm/detect-node@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"diff","version":"5.2.2","description":"A JavaScript text diff implementation.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bed7037c7dd33a33fc51e932b6f9c71f5a353f815c51db78790d58f806daa75b64fce07631642f7304b60a509dd73b8885cdc928ec7aa7792877c55fcacdc4f8"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/diff@5.2.2","externalReferences":[{"type":"vcs","url":"git://github.com/kpdecker/jsdiff.git"}],"type":"library","bom-ref":"pkg:npm/diff@5.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"David Tudury ","group":"","name":"discontinuous-range","version":"1.0.0","description":"for adding, subtracting, and indexing discontinuous ranges of numbers","scope":"optional","hashes":[{"alg":"SHA-512","content":"73af0ba4b6cefbb90ffdbd47af5aacf3f049d3d1799216464f1ab166e873c699b024a3a04451c95886fdfca9aa9ea1e12dd3b9e5a3b1147fc4181bd441b2ff45"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/discontinuous-range@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/dtudury/discontinuous-range"},{"type":"vcs","url":"https://github.com/dtudury/discontinuous-range.git"}],"type":"library","bom-ref":"pkg:npm/discontinuous-range@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"dot-prop","version":"5.3.0","description":"Get, set, or delete a property from a nested object using a dot path","scope":"optional","hashes":[{"alg":"SHA-512","content":"40cf2adf30dee7c86a52a8eb6903a6cd9d4b207f525902539442821f8909da842f2d993b45b417bed0ccd9712addfc2457d082bef1f82c0d0057ea2016c04cd9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/dot-prop@5.3.0","type":"library","bom-ref":"pkg:npm/dot-prop@5.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"dotenv","version":"16.6.1","description":"Loads environment variables from .env file","scope":"optional","hashes":[{"alg":"SHA-512","content":"b81ab87a05874dc4eddf76bbdafa521b4cf71e73ee225e8da98713aca120d9ace81329768695b4cea971cacab6a4af47943207c87c9a91e61a627480c1df1ba3"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/dotenv@16.6.1","externalReferences":[{"type":"vcs","url":"https://github.com/motdotla/dotenv#readme"},{"type":"vcs","url":"git://github.com/motdotla/dotenv.git"}],"type":"library","bom-ref":"pkg:npm/dotenv@16.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"dunder-proto","version":"1.0.1","description":"If available, the `Object.prototype.__proto__` accessor and mutator, call-bound","scope":"optional","hashes":[{"alg":"SHA-512","content":"28837f9c3241411717c3430b561644f62407986ebca80548060f42aa65188e64088608a3f54e4c16faea9142f915bb72cb366e39e3add3375e45ee1463b72df8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/dunder-proto@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/es-shims/dunder-proto#readme"},{"type":"vcs","url":"git+https://github.com/es-shims/dunder-proto.git"}],"type":"library","bom-ref":"pkg:npm/dunder-proto@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Masaki Komagata","group":"","name":"eastasianwidth","version":"0.2.0","description":"Get East Asian Width from a character.","scope":"optional","hashes":[{"alg":"SHA-512","content":"23cf1361959cf578981d1438ff7739ae38df8248e12f25b696e18885e18445b350e8e63bc93c9b6a74a90d765af32ed550ff589837186be7b2ab871aee22ea58"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/eastasianwidth@0.2.0","type":"library","bom-ref":"pkg:npm/eastasianwidth@0.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jonathan Ong (http://jongleberry.com)","group":"","name":"ee-first","version":"1.1.1","description":"return the first event in a set of ee/event pairs","scope":"optional","hashes":[{"alg":"SHA-512","content":"58cc26f4b851528f9651a44dfaf46e113a86f3d22066985548d91d16079beac4bf1383ab0c837bb78f0201ec121d773a0bc95e7c3f0a29faf9bd8eb56eb425a3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ee-first@1.1.1","type":"library","bom-ref":"pkg:npm/ee-first@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Bynens (https://mathiasbynens.be/)","group":"","name":"emoji-regex","version":"10.4.0","description":"A regular expression to match all Emoji-only symbols as per the Unicode Standard.","scope":"optional","hashes":[{"alg":"SHA-512","content":"102fb4a14318d51aa6e0ee8b2eb823b580ef715613cbb7210e733843bd37d2d3f82b08f7bbfa51ea03fdca09e9d8224c2b91aafbd436a2a9ab149033d350d78f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/emoji-regex@10.4.0","externalReferences":[{"type":"website","url":"https://mths.be/emoji-regex"},{"type":"vcs","url":"https://github.com/mathiasbynens/emoji-regex.git"}],"type":"library","bom-ref":"pkg:npm/emoji-regex@10.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Bynens (https://mathiasbynens.be/)","group":"","name":"emoji-regex","version":"8.0.0","description":"A regular expression to match all Emoji-only symbols as per the Unicode Standard.","scope":"optional","hashes":[{"alg":"SHA-512","content":"3128d8cdc58d380d1ec001e9cf4331a5816fc20eb28f2d4d1b7c6d7a8ab3eb8e150a8fd13e09ebd7f186b7e89cde2253cd0f04bb74dd335e126b09d5526184e8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/emoji-regex@8.0.0","externalReferences":[{"type":"website","url":"https://mths.be/emoji-regex"},{"type":"vcs","url":"https://github.com/mathiasbynens/emoji-regex.git"}],"type":"library","bom-ref":"pkg:npm/emoji-regex@8.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Bynens (https://mathiasbynens.be/)","group":"","name":"emoji-regex","version":"9.2.2","description":"A regular expression to match all Emoji-only symbols as per the Unicode Standard.","scope":"optional","hashes":[{"alg":"SHA-512","content":"2f5f03689b17494936fb8da9bfc98bb398c94f686a164144e23db5c0e9a06d4aac67684bef636c514efce60f515e0a37b3464d815978d93887a7766d3affd5ca"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/emoji-regex@9.2.2","externalReferences":[{"type":"website","url":"https://mths.be/emoji-regex"},{"type":"vcs","url":"https://github.com/mathiasbynens/emoji-regex.git"}],"type":"library","bom-ref":"pkg:npm/emoji-regex@9.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Zoltan Kochan (https://www.kochan.io/)","group":"","name":"encode-registry","version":"3.0.1","description":"Encodes a registry URL. Memoized.","scope":"optional","hashes":[{"alg":"SHA-512","content":"eaa3b0925d60d1fbf40cddd8de082bd846995cdef56a802a3e9de9fe955a592052228f988cb39637ad45bdae37a15c9034f7fb9209bc964b9dce5ce88f01368f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/encode-registry@3.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/pnpm/encode-registry#readme"},{"type":"vcs","url":"git+https://github.com/pnpm/encode-registry.git"}],"type":"library","bom-ref":"pkg:npm/encode-registry@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"encodeurl","version":"2.0.0","description":"Encode a URL to a percent-encoded form, excluding already-encoded sequences","scope":"optional","hashes":[{"alg":"SHA-512","content":"4349fd1d18b89ba26e188575785966bc907b644571bbddc8accca232c182d25acc24c5b3460c7a586aaec9f4206556f7d6f8468179df98f34d5e6c673a4441ae"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/encodeurl@2.0.0","type":"library","bom-ref":"pkg:npm/encodeurl@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus ","group":"","name":"end-of-stream","version":"1.4.5","description":"Call a callback when a readable/writable/duplex stream has completed or failed.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a2810673a1cfdbac57abf37e18218e4f424a08b0c6aead9b41466b43b832ac989900d27ff180d3c53a5005718c9fe59b2105cd569c96ca69bb2985480909f23a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/end-of-stream@1.4.5","externalReferences":[{"type":"vcs","url":"https://github.com/mafintosh/end-of-stream"},{"type":"vcs","url":"git://github.com/mafintosh/end-of-stream.git"}],"type":"library","bom-ref":"pkg:npm/end-of-stream@1.4.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"env-paths","version":"2.2.1","description":"Get paths for storing things like data, config, cache, etc","scope":"optional","hashes":[{"alg":"SHA-512","content":"fa1d6590b2a164c4d88e8835544a49346ecd64959cb9cd830e4feab2a49345108e5e22e3790d5dd7fb9dad41a1a8cc5480097028d67471fdaea9a9f918bb92d8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/env-paths@2.2.1","type":"library","bom-ref":"pkg:npm/env-paths@2.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"tabrindle@gmail.com","group":"","name":"envinfo","version":"7.21.0","description":"Info about your dev environment for debugging purposes","scope":"optional","hashes":[{"alg":"SHA-512","content":"2f0ec8f19a7960a1c50972fbf83cfde60e0271b304a60bea64d36adc09a54f95c057a0a000093a83200caa7db38f0d3c2bd0477dc36e2ab32208f2c1c8669fa3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/envinfo@7.21.0","type":"library","bom-ref":"pkg:npm/envinfo@7.21.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"environment","version":"1.1.0","description":"Check which JavaScript environment your code is running in at runtime: browser, Node.js, Bun, etc","scope":"optional","hashes":[{"alg":"SHA-512","content":"c54b683e432081bcf430fc8f8885abd4aa7869e5898c367a48cbd44618a68dd660b11b83a65179fecf617201a1c97321b3eeafa67ba8899da4162bb714c9d2f1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/environment@1.1.0","type":"library","bom-ref":"pkg:npm/environment@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"error-ex","version":"1.3.4","description":"Easy error subclassing and stack customization","scope":"optional","hashes":[{"alg":"SHA-512","content":"b2a41a9809d1d785600abd40eb5f00dec1abca07292be1c46de9c0fc7884024914c1c648201fed816a871715a03b20e1e270782424629a1efd751e58c1cf4c0d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/error-ex@1.3.4","type":"library","bom-ref":"pkg:npm/error-ex@1.3.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"es-define-property","version":"1.0.1","description":"`Object.defineProperty`, but not IE 8's broken one.","scope":"optional","hashes":[{"alg":"SHA-512","content":"7b79d17e07d4678acd18bdb7da05205f4e90372c9ecf4e0a76316b17e2d34683979ab3a014a0e0e0109db235bc1274faf5ea9d606991a49c223d560dac2696de"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/es-define-property@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/es-define-property#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/es-define-property.git"}],"type":"library","bom-ref":"pkg:npm/es-define-property@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"es-errors","version":"1.3.0","description":"A simple cache for a few of the JS Error constructors.","scope":"optional","hashes":[{"alg":"SHA-512","content":"65fe47d8ac6ddb18d3bdb26f3f66562c4202c40ea3fa1026333225ca9cb8c5c060d6f2959f1f3d5b2d066d2fa47f9730095145cdd0858765d20853542d2e9cb3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/es-errors@1.3.0","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/es-errors#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/es-errors.git"}],"type":"library","bom-ref":"pkg:npm/es-errors@1.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"es-object-atoms","version":"1.1.1","description":"ES Object-related atoms: Object, ToObject, RequireObjectCoercible","scope":"optional","hashes":[{"alg":"SHA-512","content":"146807da1f3328d8a6f658e3edd6a79053dc20220af42a796e6f9cda041261e3e1a5a1b9f9eb2b2ce0e2848a2b9fe3dee85189cd6857428b4fbfbde34da95d5c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/es-object-atoms@1.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/es-object-atoms#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/es-object-atoms.git"}],"type":"library","bom-ref":"pkg:npm/es-object-atoms@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"es-toolkit","version":"1.45.1","description":"A state-of-the-art, high-performance JavaScript utility library with a small bundle size and strong type annotations.","scope":"optional","hashes":[{"alg":"SHA-512","content":"fe38683a3fc5c7e03e208c8334ebcedd322d1a694c2a1b57f084801ca13dd1ce1bfe4d6da9aa8467eb94a9fa54f033275b9720349bfad3acd2e798c6bf36b45f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/es-toolkit@1.45.1","externalReferences":[{"type":"website","url":"https://es-toolkit.dev"},{"type":"vcs","url":"https://github.com/toss/es-toolkit.git"}],"type":"library","bom-ref":"pkg:npm/es-toolkit@1.45.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Youngblood","group":"","name":"es6-error","version":"4.1.1","description":"Easily-extendable error for use with ES6 classes","scope":"optional","hashes":[{"alg":"SHA-512","content":"526ffe17132bf422125a1d1b8b966fd22383fb8705879a8b7a4b35aa1028a4a540270dddae029b2b24a2929ef01a10cbd073de6a36b43f950b66bc4b92789456"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/es6-error@4.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/bjyoungblood/es6-error"},{"type":"vcs","url":"https://github.com/bjyoungblood/es6-error.git"}],"type":"library","bom-ref":"pkg:npm/es6-error@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Luke Edwards (https://lukeed.com)","group":"","name":"escalade","version":"3.2.0","description":"A tiny (183B to 210B) and fast utility to ascend parent directories","scope":"optional","hashes":[{"alg":"SHA-512","content":"5948f6aa5c5a42d3b883a3eae5cdbd193716183c9df22b4bf334e58a98040b3dc97ac02288e2a8b5df0953aa2d0773c00a01bac64254c9585ba0c4be6e37bf8c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/escalade@3.2.0","type":"library","bom-ref":"pkg:npm/escalade@3.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"escape-html","version":"1.0.3","description":"Escape string for use in HTML","scope":"optional","hashes":[{"alg":"SHA-512","content":"3624aea59e0e7ae1b0afaf251887b29bf92c219309a1d506392099fc54a74f172b7a46efaab81d53194938ca628da299563009ad6ac6b3fe89cbc38cbb28fda3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/escape-html@1.0.3","type":"library","bom-ref":"pkg:npm/escape-html@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"escape-string-regexp","version":"1.0.5","description":"Escape RegExp special characters","scope":"optional","hashes":[{"alg":"SHA-512","content":"bdb468ac1e455105af95ad7a53c47faa06852326b6a86cf00eb366099b982ab6dd494306e88d5908641179f911561b8e9081959deec1437e4349fa35aaf26a16"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/escape-string-regexp@1.0.5","type":"library","bom-ref":"pkg:npm/escape-string-regexp@1.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"escape-string-regexp","version":"4.0.0","description":"Escape RegExp special characters","scope":"optional","hashes":[{"alg":"SHA-512","content":"4eda5c349dd7033c771aaf2c591cc96956a346cd2e57103660091d6f58e6d9890fcf81ba7a05050320379f9bed10865e7cf93959ae145db2ae4b97ca90959d80"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/escape-string-regexp@4.0.0","type":"library","bom-ref":"pkg:npm/escape-string-regexp@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"etag","version":"1.8.1","description":"Create simple HTTP ETags","scope":"optional","hashes":[{"alg":"SHA-512","content":"6882f9171ee66b055adf4d1a976067104e2236fa35a844f12eb3c8fe8d392fbcfa828edf0b0d49e844266cae05989d804bb920545fca1195ae7c17dd0a531c3e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/etag@1.8.1","type":"library","bom-ref":"pkg:npm/etag@1.8.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"snyk.io","group":"","name":"event-loop-spinner","version":"2.3.2","description":"Tiny helper to prevent blocking Node.js event loop","scope":"optional","hashes":[{"alg":"SHA-512","content":"3b4efc2e4c62ff26443cf89f722cc338651e2b53853a53c2eac7c20abc75d2876fa97ded122f572da211b7d70897d4c115c3d9cee31a5db2746fe4fa0f64e78e"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/event-loop-spinner@2.3.2","externalReferences":[{"type":"vcs","url":"https://github.com/snyk/eventloop-spinner#readme"},{"type":"vcs","url":"git+https://github.com/snyk/eventloop-spinner.git"}],"type":"library","bom-ref":"pkg:npm/event-loop-spinner@2.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Arnout Kazemier","group":"","name":"eventemitter3","version":"5.0.4","description":"EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.","scope":"optional","hashes":[{"alg":"SHA-512","content":"9a5b1347219a3c18cf79d93a06fc3e6aa6ec5c3b680320339b930eec9814fb2551c8c4393bc6c3e0a71c8bb052f397fddef79e81e08f90bf11e062c29678cb17"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/eventemitter3@5.0.4","externalReferences":[{"type":"vcs","url":"git://github.com/primus/eventemitter3.git"}],"type":"library","bom-ref":"pkg:npm/eventemitter3@5.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Irakli Gozalishvili (http://jeditoolkit.com)","group":"","name":"events","version":"3.3.0","description":"Node's event emitter for all engines.","scope":"optional","hashes":[{"alg":"SHA-512","content":"990c3ed9f9106c02f343b574318d08a9d9d734e793b4fe2bd2537dcfb0006b009782a79aedb0e28b6d0062b201ac577f1f1d0cd8e733e92d75d4268591471bd1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/events@3.3.0","externalReferences":[{"type":"vcs","url":"git://github.com/Gozala/events.git"}],"type":"library","bom-ref":"pkg:npm/events@3.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Espen Hovlandsdal ","group":"","name":"eventsource-parser","version":"3.0.6","description":"Streaming, source-agnostic EventSource/Server-Sent Events parser","scope":"optional","hashes":[{"alg":"SHA-512","content":"568d5a6fe4173f3678b426bc4b0207245692cf2e11e921dfec163bf6b1410dfd2276b699580918ac38c38fcb966929b74b64cafa127bfedd4212667b8d7c3ea6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/eventsource-parser@3.0.6","externalReferences":[{"type":"vcs","url":"https://github.com/rexxars/eventsource-parser#readme"},{"type":"vcs","url":"git+ssh://git@github.com/rexxars/eventsource-parser.git"}],"type":"library","bom-ref":"pkg:npm/eventsource-parser@3.0.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Espen Hovlandsdal ","group":"","name":"eventsource","version":"3.0.7","description":"WhatWG/W3C compliant EventSource client for Node.js and browsers","scope":"optional","hashes":[{"alg":"SHA-512","content":"0914f5593cae4280fbef5196e7a5c464543f66849f5a27756a52860d832692ddb297c517ad5478a6ca6a58d11ca8abd523383a3c096d5a3c5c4923eb6e803888"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/eventsource@3.0.7","externalReferences":[{"type":"vcs","url":"https://github.com/EventSource/eventsource#readme"},{"type":"vcs","url":"git://git@github.com/EventSource/eventsource.git"}],"type":"library","bom-ref":"pkg:npm/eventsource@3.0.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"execa","version":"9.6.1","description":"Process execution for humans","scope":"optional","hashes":[{"alg":"SHA-512","content":"f417b76683782e6611f74b54a15bb6b5ed81b1bcc77e12727c488055fcfb379ff3bfe8ddb887cbad5db17505ce1db683e8a82919d3bd3d13ccd58e10f5090f90"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/execa@9.6.1","type":"library","bom-ref":"pkg:npm/execa@9.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"LM ","group":"","name":"expand-template","version":"2.0.3","description":"Expand placeholders in a template string","scope":"optional","hashes":[{"alg":"SHA-512","content":"5d87ee28cbe3e0edf97ffa4e5cb39b9dd211bf243effee8084e0e1f8e2968fd4bde3df291c79ff20cb331fe82dd1f04245630d7e4d594a9e71dc089f9a7236be"}],"licenses":[{"expression":"(MIT OR WTFPL)"}],"purl":"pkg:npm/expand-template@2.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/ralphtheninja/expand-template"},{"type":"vcs","url":"https://github.com/ralphtheninja/expand-template.git"}],"type":"library","bom-ref":"pkg:npm/expand-template@2.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"expand-tilde","version":"2.0.2","description":"Bash-like tilde expansion for node.js. Expands a leading tilde in a file path to the user home directory, or `~+` to the cwd.","scope":"optional","hashes":[{"alg":"SHA-512","content":"0391267ac1d6eab7e767dcac1d08cf7494678b44916abd2d8ed1b930db66f67e5352fb1853ca28ce9aed443e00a87c5c6565a556e026428da758a7cdf68ca34f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/expand-tilde@2.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/expand-tilde"}],"type":"library","bom-ref":"pkg:npm/expand-tilde@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"express-rate-limit","version":"8.3.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"efb56615e2643b4febbe29840ee502e47df4a2a502e04c8e87219c71faa82de6c1d2888461f33b9f03ebb03b012f582c4e9c1fcd7f12172d8c4f74c3c91abe6e"}],"purl":"pkg:npm/express-rate-limit@8.3.2","type":"framework","bom-ref":"pkg:npm/express-rate-limit@8.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk ","group":"","name":"express","version":"5.2.1","description":"Fast, unopinionated, minimalist web framework","scope":"optional","hashes":[{"alg":"SHA-512","content":"8484b889d5966a2ebd35ecc8751b76c455687da1788fee8834ea4995538b0cef335c6a54544573218935d94522d89ce313358bdc8380c5c4ee6e0cfd3e8d325f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/express@5.2.1","externalReferences":[{"type":"website","url":"https://expressjs.com/"}],"type":"framework","bom-ref":"pkg:npm/express@5.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"extend-shallow","version":"2.0.1","description":"Extend an object with the properties of additional objects. node.js/javascript util.","scope":"optional","hashes":[{"alg":"SHA-512","content":"cc29d3b65c4da0088373782a636698016171ed759689ab2e1762bc31ee566cdf28b4729350a0708cfb4da51b3fadb5199bb2b158068d8fb3f56bfa79d866d5ba"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/extend-shallow@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/extend-shallow"}],"type":"library","bom-ref":"pkg:npm/extend-shallow@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Stefan Thomas (http://www.justmoon.net)","group":"","name":"extend","version":"3.0.2","description":"Port of jQuery.extend for node.js and the browser","scope":"optional","hashes":[{"alg":"SHA-512","content":"7e3aae0b9f5c0fb0b25babab3572b4141b9f9197288861bcd304ee3ee8d7e7dd1c0794ed967db4136501e12fd601156a8577df665d8b3604be81074f2088a6fe"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/extend@3.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/justmoon/node-extend.git"}],"type":"library","bom-ref":"pkg:npm/extend@3.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kevin Gravier (https://mrkmg.com)","group":"","name":"external-editor","version":"3.1.0","description":"Edit a string with the users preferred text editor using $VISUAL or $ENVIRONMENT","scope":"optional","hashes":[{"alg":"SHA-512","content":"84c438097d69d62ce6b8b63266a2cc3bfa86370d74c12bfd40308f7f35dfc85ace682492a117ea13529fd6ce5a9fae89e49642eb635ec06fa62b8f63382b507b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/external-editor@3.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/mrkmg/node-external-editor#readme"},{"type":"vcs","url":"git+https://github.com/mrkmg/node-external-editor.git"}],"type":"library","bom-ref":"pkg:npm/external-editor@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"tony_quetano@planttheidea.com","group":"","name":"fast-copy","version":"4.0.3","description":"A blazing fast deep object copier","scope":"optional","hashes":[{"alg":"SHA-512","content":"e7c6a95abd065220c533cfb769facee9e6302419fd6408433b31b72feffd96569bfe16820114b65087df7e63aead82f06e00d1b3c9f4adfafaa8000f100b8043"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-copy@4.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/planttheidea/fast-copy#readme"},{"type":"vcs","url":"git+https://github.com/planttheidea/fast-copy.git"}],"type":"library","bom-ref":"pkg:npm/fast-copy@4.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Evgeny Poberezkin","group":"","name":"fast-deep-equal","version":"3.1.3","description":"Fast deep equal","scope":"optional","hashes":[{"alg":"SHA-512","content":"7f7a90f68432f63d808417bf1fd542f75c0b98a042094fe00ce9ca340606e61b303bb04b2a3d3d1dce4760dcfd70623efb19690c22200da8ad56cd3701347ce1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-deep-equal@3.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/epoberezkin/fast-deep-equal#readme"},{"type":"vcs","url":"git+https://github.com/epoberezkin/fast-deep-equal.git"}],"type":"library","bom-ref":"pkg:npm/fast-deep-equal@3.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Denis Malinochkin (https://mrmlnc.com)","group":"","name":"fast-glob","version":"3.3.3","description":"It's a very fast and efficient glob library for Node.js","scope":"optional","hashes":[{"alg":"SHA-512","content":"ecca6d2fc53472a705773233c0e4c7a22957f71e41acdab27bb67f2ee0bb9023118a8d44312caa44adc1100503eec5d1ab8893e00cd356e65d8604364c2bd82e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-glob@3.3.3","type":"library","bom-ref":"pkg:npm/fast-glob@3.3.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"David Mark Clements","group":"","name":"fast-safe-stringify","version":"2.1.1","description":"Safely and quickly serialize JavaScript objects","scope":"optional","hashes":[{"alg":"SHA-512","content":"5be28973676620b94fa650ff1f82bd97d2dc00701f3ed3fa058f38b952d743a12f733f4b720df7636cf52156e54fac5d639e0f5d854712ffb45a9abc228eb390"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-safe-stringify@2.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/davidmarkclements/fast-safe-stringify#readme"},{"type":"vcs","url":"git+https://github.com/davidmarkclements/fast-safe-stringify.git"}],"type":"library","bom-ref":"pkg:npm/fast-safe-stringify@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Vincent Le Goff (https://github.com/zekth)","group":"","name":"fast-uri","version":"3.1.0","description":"Dependency-free RFC 3986 URI toolbox","scope":"optional","hashes":[{"alg":"SHA-512","content":"88f79e0ca25259fe0810e6ac555ae49d7a5a055d08029cff829ed2d9b6fb6782e58db976306251a889d9894ad0c15d7a729cf0fc3dd2e63e49ba58ff813e7600"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/fast-uri@3.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/fastify/fast-uri"},{"type":"vcs","url":"git+https://github.com/fastify/fast-uri.git"}],"type":"library","bom-ref":"pkg:npm/fast-uri@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Amit Gupta (https://solothought.com)","group":"","name":"fast-xml-builder","version":"1.1.5","description":"Build XML from JSON without C/C++ based libraries","scope":"optional","hashes":[{"alg":"SHA-512","content":"e13267ffc14a2deb252c01f7767a215ea137412a31921bda3337a9388672b7025764eebd05fcf41c17431f33933a89fa1b9f59ae4e954366c48afd6deb5adf90"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-xml-builder@1.1.5","externalReferences":[{"type":"vcs","url":"git+https://github.com/NaturalIntelligence/fast-xml-builder.git"}],"type":"library","bom-ref":"pkg:npm/fast-xml-builder@1.1.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Amit Gupta (https://solothought.com)","group":"","name":"fast-xml-parser","version":"5.7.1","description":"Validate XML, Parse XML, Build XML without C/C++ based libraries","scope":"required","hashes":[{"alg":"SHA-512","content":"f027377fc19419450b837e2905c87f286c8f2e0952f8e16cd3975ec8e958edf2f63136a060f2ab5503664757cb17fc89f4f1f96524dddd80c5ea99e679953ecc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fast-xml-parser@5.7.1","externalReferences":[{"type":"vcs","url":"git+https://github.com/NaturalIntelligence/fast-xml-parser.git"}],"type":"library","bom-ref":"pkg:npm/fast-xml-parser@5.7.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"fast-xml-parser,XMLParser,fast-xml-parser/XMLParser"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/pipeline/dep-parsers/maven.js#24"},{"location":"packages/ingestion/dist/pipeline/dep-parsers/nuget.js#18"},{"location":"packages/ingestion/dist/pipeline/phases/coverage-parsers/cobertura.js#23"},{"location":"packages/ingestion/dist/pipeline/phases/coverage-parsers/jacoco.js#22"},{"location":"packages/ingestion/src/pipeline/dep-parsers/maven.ts#25"},{"location":"packages/ingestion/src/pipeline/dep-parsers/nuget.ts#19"},{"location":"packages/ingestion/src/pipeline/phases/coverage-parsers/cobertura.ts#24"},{"location":"packages/ingestion/src/pipeline/phases/coverage-parsers/jacoco.ts#23"}]}},{"author":"Matteo Collina ","group":"","name":"fastq","version":"1.20.1","description":"Fast, in memory work queue","scope":"optional","hashes":[{"alg":"SHA-512","content":"1864e8c49ff0d71df6b3f0f610a343ee44e29789fc39593ff66c9c4dce150f36b5de53afa54653197de61520ad57d92c746055cefb320152ccea61c54e1c57c7"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/fastq@1.20.1","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/fastq#readme"},{"type":"vcs","url":"git+https://github.com/mcollina/fastq.git"}],"type":"library","bom-ref":"pkg:npm/fastq@1.20.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"figures","version":"3.2.0","description":"Unicode symbols with Windows CMD fallbacks","scope":"optional","hashes":[{"alg":"SHA-512","content":"c9a76e40544a2d760e1a0127e8065abbdd23de08123b28aa5d4d05f4965f79762135af899385feb38e40db38398e7b3cec60056b7e01066da45f0e17a4d71b76"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/figures@3.2.0","type":"library","bom-ref":"pkg:npm/figures@3.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"figures","version":"6.1.0","description":"Unicode symbols with fallbacks for older terminals","scope":"optional","hashes":[{"alg":"SHA-512","content":"77e977ab18d27ac4f857bbf67e1f909e6167516bfd952a636ab85284d4e004e7c0d2db5e8db4140251cb8ad6e39284620ee956d0e3e840f6081a1202f2885e8e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/figures@6.1.0","type":"library","bom-ref":"pkg:npm/figures@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"fill-range","version":"7.1.1","description":"Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`","scope":"optional","hashes":[{"alg":"SHA-512","content":"62c1a97b75872caf19622e2d583836272dde6d1cf6ad7a300f19e57786e4401d3471cff5670f405a70b48bdced0c98ad8afb50bda23d29a2f22ab73e8415b4ca"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fill-range@7.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/fill-range"}],"type":"library","bom-ref":"pkg:npm/fill-range@7.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"finalhandler","version":"2.1.1","description":"Node.js final http responder","scope":"optional","hashes":[{"alg":"SHA-512","content":"4bc2a866045937e6b9acdc2a4f195965e3e34ffe1c9e6d11395ef42de7511d9d29f2ef5f4480f484951942990a2f3ae8f0b7e60bcb31db76d8ead54dc6ff2940"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/finalhandler@2.1.1","type":"library","bom-ref":"pkg:npm/finalhandler@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Callum Macrae ","group":"","name":"find-node-modules","version":"2.1.3","description":"Return an array of all parent node_modules directories","scope":"optional","hashes":[{"alg":"SHA-512","content":"502d88dbe9f1d59b8e05c95654d75c9db0d1e5d96b39d55bef1363993fe51c4f8bb20ced5a4b37746edba09dfbc934bfbde9d7c3ce01fe6016f6e1d5a02e5046"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/find-node-modules@2.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/callumacrae/find-node-modules"},{"type":"vcs","url":"https://github.com/callumacrae/find-node-modules"}],"type":"library","bom-ref":"pkg:npm/find-node-modules@2.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"jsdnxx","group":"","name":"find-root","version":"1.1.0","description":"find the closest package.json","scope":"optional","hashes":[{"alg":"SHA-512","content":"34a7d6e9b79ce867ca734486c757b4ed0658f4f13df6ed017edff4af3483e64dec3bfbf09155290d42959b91a9a7951edd87d284f1535f6d3bd2d0ece6407d36"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/find-root@1.1.0","type":"library","bom-ref":"pkg:npm/find-root@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gulp Team (https://gulpjs.com/)","group":"","name":"findup-sync","version":"4.0.0","description":"Find the first file matching a given pattern in the current directory or the nearest ancestor directory.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ea3bef9ffd76202e2ab8b04bd4a3689310bbc164ef62771a554612a315b0ed8ca43cbb91ae7bf8a9d1dc48ecb0388e51a6439519e411b5633cfeaf86e96eaa7d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/findup-sync@4.0.0","type":"library","bom-ref":"pkg:npm/findup-sync@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"foreground-child","version":"3.3.1","description":"Run a child as if it's the foreground process. Give it stdio. Exit when it exits.","scope":"optional","hashes":[{"alg":"SHA-512","content":"8085e32aab45b96120cc544903d58241e4892d90e380950e302333c6dbc5abfdfb2a88ccd41146b9faac0b2d2be2a4909982ec65831ec91ab321638cba9d37b3"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/foreground-child@3.3.1","externalReferences":[{"type":"vcs","url":"git+https://github.com/tapjs/foreground-child.git"}],"type":"library","bom-ref":"pkg:npm/foreground-child@3.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"forwarded","version":"0.2.0","description":"Parse HTTP X-Forwarded-For header","scope":"optional","hashes":[{"alg":"SHA-512","content":"6ee446d1fa41b511d24c238049eea10f6e7cb44b9b16844b6f864d03a3713151cdc3680e7301e8f70c9a6e5ccccce039cfdc40f4bd4a36393f36de8c4fd698a3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/forwarded@0.2.0","type":"library","bom-ref":"pkg:npm/forwarded@0.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk (http://tjholowaychuk.com)","group":"","name":"fresh","version":"2.0.0","description":"HTTP response freshness testing","scope":"optional","hashes":[{"alg":"SHA-512","content":"471fd6c9c67ad0739aa8b2808ba70744a288ef3c566c9df53219ed9adc0ca1a4de17b5c51fd861069f2f21368c89d7e58d143a7985822a46932beca45491cfd8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fresh@2.0.0","type":"library","bom-ref":"pkg:npm/fresh@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus (@mafintosh)","group":"","name":"fs-constants","version":"1.0.0","description":"Require constants across node and the browser","scope":"optional","hashes":[{"alg":"SHA-512","content":"cba380c284887fb1728cc22ff78bbe6f9add85e6448f347adc64f26499b9aa1e018bed988302c2708fdf3c56642f93d28b13ade9934a9bec3e1dfa7f05c8b0a3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fs-constants@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/mafintosh/fs-constants"},{"type":"vcs","url":"https://github.com/mafintosh/fs-constants.git"}],"type":"library","bom-ref":"pkg:npm/fs-constants@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"JP Richardson ","group":"","name":"fs-extra","version":"11.3.4","description":"fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.","scope":"optional","hashes":[{"alg":"SHA-512","content":"0935ddeab93f337fd42cd423f0506a0561d80556326d0dd53c1c34c462857b7b6e1fbcad4fa0029efce9210dd466d07ccaf50a0b67179f56bec7ee445502e8bc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fs-extra@11.3.4","externalReferences":[{"type":"vcs","url":"https://github.com/jprichardson/node-fs-extra"},{"type":"vcs","url":"git+https://github.com/jprichardson/node-fs-extra.git"}],"type":"library","bom-ref":"pkg:npm/fs-extra@11.3.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"JP Richardson ","group":"","name":"fs-extra","version":"9.1.0","description":"fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.","scope":"optional","hashes":[{"alg":"SHA-512","content":"85c8376667a94b7d3fec1485a91be8a370ce310bbb223ab13b99c20edfb333d5d68dbdf75a0ef388d4fe42fa9bb9cdfe816a733b4d89b9b5729361b866fa3539"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/fs-extra@9.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/jprichardson/node-fs-extra"},{"type":"vcs","url":"https://github.com/jprichardson/node-fs-extra"}],"type":"library","bom-ref":"pkg:npm/fs-extra@9.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"fs.realpath","version":"1.0.0","description":"Use node's fs.realpath, but fall back to the JS implementation if the native one fails","scope":"optional","hashes":[{"alg":"SHA-512","content":"38ed291f694ae9ad2166701d6aee48b731cf23aa5496f23b8cc567c54411b70e28c05db093c94e49a6ed1830933f81a0ae0d8c6c69d63bd5fc2b5b78f9f18c0f"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/fs.realpath@1.0.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/fs.realpath.git"}],"type":"library","bom-ref":"pkg:npm/fs.realpath@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Raynos ","group":"","name":"function-bind","version":"1.1.2","description":"Implementation of Function.prototype.bind","scope":"optional","hashes":[{"alg":"SHA-512","content":"ed71cdc47eea5fdc46e66230c6486e993a31fcc21135c3a00ebc56b0cb76a40af6dd61e9e8cad194dec50521690a9afea153b417be38894811f369c931f1b648"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/function-bind@1.1.2","externalReferences":[{"type":"vcs","url":"https://github.com/Raynos/function-bind"},{"type":"vcs","url":"https://github.com/Raynos/function-bind.git"}],"type":"library","bom-ref":"pkg:npm/function-bind@1.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Stefan Penner","group":"","name":"get-caller-file","version":"2.0.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"0f214fdc133fdd81d340e0942ffc343991d1d25a4a786af1a2d70759ca8d11d9e5b6a1705d57e110143de1e228df801f429a34ac6922e1cc8889fb58d3a87616"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/get-caller-file@2.0.5","externalReferences":[{"type":"vcs","url":"https://github.com/stefanpenner/get-caller-file#readme"},{"type":"vcs","url":"git+https://github.com/stefanpenner/get-caller-file.git"}],"type":"library","bom-ref":"pkg:npm/get-caller-file@2.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"get-east-asian-width","version":"1.5.0","description":"Determine the East Asian Width of a Unicode character","scope":"optional","hashes":[{"alg":"SHA-512","content":"090f9b10ef93bdafea966c36e1d09e8ee94ae6933356750e14e8a356881ddca42cd3b1e7448829f131a2a6f082453d3ac5e6046e96e0c1a0b18251728ae21c98"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/get-east-asian-width@1.5.0","type":"library","bom-ref":"pkg:npm/get-east-asian-width@1.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"get-intrinsic","version":"1.3.0","description":"Get and robustly cache all JS language-level intrinsics at first require time","scope":"optional","hashes":[{"alg":"SHA-512","content":"f5f4a349aa2cfdf448548a7ec5226513a95fc21112ecb36d29a08121a987b23af69dad418800493e8d263a38f3f062435116ab9823c6a9a89583999f8dbf7c09"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/get-intrinsic@1.3.0","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/get-intrinsic#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/get-intrinsic.git"}],"type":"library","bom-ref":"pkg:npm/get-intrinsic@1.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"get-proto","version":"1.0.1","description":"Robustly get the [[Prototype]] of an object","scope":"optional","hashes":[{"alg":"SHA-512","content":"b1349f063a17069f3d26f20a21e7eac3b53608279bb1cef892263a6b0886a202ada1219b823604fc6ffe97db05dcc5853cd73d21ca0e0b83837ca1dfc459a9d2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/get-proto@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/get-proto#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/get-proto.git"}],"type":"library","bom-ref":"pkg:npm/get-proto@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"get-stream","version":"5.2.0","description":"Get a stream as a string, buffer, or array","scope":"optional","hashes":[{"alg":"SHA-512","content":"9c117e175ac06550aefe9eeb8f3800f986f895f617ae997b6ba56626b53cc05f48d422af3ff4303cd6479ce9706d3918e9dbed148cc5312c905db2e84d03d1a4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/get-stream@5.2.0","type":"library","bom-ref":"pkg:npm/get-stream@5.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"get-stream","version":"9.0.1","description":"Get a stream as a string, Buffer, ArrayBuffer or array","scope":"optional","hashes":[{"alg":"SHA-512","content":"9150b13c5def40cfcdd01d4f9a8a9552a8073fe11e5639994909fed680913f17763f6d4fd85d7d94881b4771c1a2c6c1d4f521380a1cb499df127d866cdd9e64"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/get-stream@9.0.1","type":"library","bom-ref":"pkg:npm/get-stream@9.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"git-raw-commits","version":"5.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"63e72c4a6d860ff3c24a1e88b1dfd688c8cd03276ed150621bd27b11d42c340e4ff6e5ef2dacaa8e64ec3652b91acf4885b94566a394d3289de4897924fa0b89"}],"purl":"pkg:npm/git-raw-commits@5.0.1","type":"library","bom-ref":"pkg:npm/git-raw-commits@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"github-from-package","version":"0.0.0","description":"return the github url from a package.json file","scope":"optional","hashes":[{"alg":"SHA-512","content":"4b21f2dd3d6fd8d5179f6f4eb1677198ae91c070febe48f7bfc7a7f00381675c0143f842010e5b0104c3d36916e16f6d529ff7421e89f2bf44be7b62c8298e3b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/github-from-package@0.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/substack/github-from-package"},{"type":"vcs","url":"git://github.com/substack/github-from-package.git"}],"type":"library","bom-ref":"pkg:npm/github-from-package@0.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gulp Team (https://gulpjs.com/)","group":"","name":"glob-parent","version":"5.1.2","description":"Extract the non-magic parent path from a glob string.","scope":"optional","hashes":[{"alg":"SHA-512","content":"00e22049009ea62258c0fdc04671b1fb95674eed870587736c63f8e5e2f0d6faf7cc1def64b7b279dd6c0bd8676dc39cf7f4ab33233944f42b906cf8692f59a3"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/glob-parent@5.1.2","type":"library","bom-ref":"pkg:npm/glob-parent@5.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (https://blog.izs.me/)","group":"","name":"glob","version":"10.5.0","description":"the most correct and second fastest glob implementation in JavaScript","scope":"optional","hashes":[{"alg":"SHA-512","content":"0df5cdf037e127b347dce7bb7059aedcd0aed7029b911789f13a2bcd20056d22ab94d69048a7c8cea62a558f3395bb3634b05b5a9462539d865f63db68154d92"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/glob@10.5.0","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/node-glob.git"}],"type":"library","bom-ref":"pkg:npm/glob@10.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:deprecation_notice","value":"Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (https://blog.izs.me/)","group":"","name":"glob","version":"11.1.0","description":"the most correct and second fastest glob implementation in JavaScript","scope":"optional","hashes":[{"alg":"SHA-512","content":"bee37029268a8aa9bb8344c7501bb6c7b7244acdd724b5c4fb6b2c2fbfcc7d318f2cb72b250ff852ad428cf4ed3b9702222471aaf37a4f0cb5a8ec212f45e373"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/glob@11.1.0","type":"library","bom-ref":"pkg:npm/glob@11.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:deprecation_notice","value":"Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"glob","version":"7.2.3","description":"a little globber","scope":"optional","hashes":[{"alg":"SHA-512","content":"9c5474ccba54d9809a471c28089bcbe94bc21f6245c85548bf04cbb087f6d40b8794cb240358614dd93e2e5609b4e958b7dbfa76fb330f604646a04bfa240af5"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/glob@7.2.3","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/node-glob.git"}],"type":"library","bom-ref":"pkg:npm/glob@7.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gajus Kuizinas (http://gajus.com)","group":"","name":"global-agent","version":"3.0.0","description":"Global HTTP/HTTPS proxy configurable using environment variables.","scope":"optional","hashes":[{"alg":"SHA-512","content":"3d3e9745e27e0f4ec9bc6a3140c913eaa8e2fe354d7d7fe1dfae171d9396791cf2eb8b1216bfb1279397ecb2376f830f43374be07f18f0cd31ccfa6c54cc00f1"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/global-agent@3.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/gajus/global-agent"}],"type":"library","bom-ref":"pkg:npm/global-agent@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"global-directory","version":"4.0.1","description":"Get the directory of globally installed packages and binaries","scope":"optional","hashes":[{"alg":"SHA-512","content":"c074d47035286751f9ff4895a84b9d616e3f90094de5c6778ff6d79f40e96e2ce5f6269455e5921ea88e8ec824e8e5b66e42dc95b063cdec01cfac1e435cc0d9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/global-directory@4.0.1","type":"library","bom-ref":"pkg:npm/global-directory@4.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"global-modules","version":"1.0.0","description":"The directory used by npm for globally installed npm modules.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b0ace91247f5d46a4e16ec346738f39ade01e146708ce706ef9ecf3efadf87170b15bab4c29b20a4eab1a71b71162086e03b46f7733a5d155b176a0675ebfb6e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/global-modules@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/global-modules"}],"type":"library","bom-ref":"pkg:npm/global-modules@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"global-prefix","version":"1.0.2","description":"Get the npm global path prefix.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e65b31d4d5031ed4a37e0d1e1e5998bd92aff3f9d5a97e1c9056ccf85ac6710fb4e0a59c585a3d3f93313d9612cd4bf2ce67536c8ec48b1f10e086c42c3ab32a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/global-prefix@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/global-prefix"}],"type":"library","bom-ref":"pkg:npm/global-prefix@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"globalthis","version":"1.0.4","description":"ECMAScript spec-compliant polyfill/shim for `globalThis`","scope":"optional","hashes":[{"alg":"SHA-512","content":"0e92ca6cd5385b2969c49ca442e8df09cc185a257f2619b9d06a28d30ad520b02fe633abf5df87f944773e14820f6ac2084220d2e73e1be9ae053c03e782610d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/globalthis@1.0.4","externalReferences":[{"type":"vcs","url":"git://github.com/ljharb/System.global.git"}],"type":"library","bom-ref":"pkg:npm/globalthis@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"gopd","version":"1.2.0","description":"`Object.getOwnPropertyDescriptor`, but accounts for IE's broken implementation.","scope":"optional","hashes":[{"alg":"SHA-512","content":"65429187afe4505a0089302d4d83d9277870f70371c7e04804e8a39e51bd3e7ac9b027128ecd70cb20fabc9a5a62d827cc3aca6114aa7f738ee917daf77c6c46"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/gopd@1.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/gopd#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/gopd.git"}],"type":"library","bom-ref":"pkg:npm/gopd@1.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"got","version":"11.8.6","description":"Human-friendly and powerful HTTP request library for Node.js","scope":"optional","hashes":[{"alg":"SHA-512","content":"ead7d9f756ceafb6ce5e72bb3d10c21812dad47e14d3cd181cd6804362ac30694b13345b938e27b1917613521e45cdefb491cf55b2826207456da18eda58ddf2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/got@11.8.6","type":"library","bom-ref":"pkg:npm/got@11.8.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"graceful-fs","version":"4.2.11","description":"A drop-in replacement for fs, making various improvements.","scope":"optional","hashes":[{"alg":"SHA-512","content":"45b279fe398570d342703579a3d7939c12c9fc7b33595d0fef76dcf857f89d2feb263f98692e881b288e2f45680585fe9755ab97793ade1fcaac7fa7849d17bd"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/graceful-fs@4.2.11","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/node-graceful-fs"}],"type":"library","bom-ref":"pkg:npm/graceful-fs@4.2.11","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Orlin Georgiev","group":"","name":"grapheme-splitter","version":"1.0.4","description":"A JavaScript library that breaks strings into their individual user-perceived characters. It supports emojis!","scope":"optional","hashes":[{"alg":"SHA-512","content":"6f3879d035bd9133ccd344fccb8a3cbd083cf438bda0b2552d6fca68e1885c958ffe2a8237a58a6246cd385d38bc52c987072961487dc9d87ffb9be93aa57861"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/grapheme-splitter@1.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/orling/grapheme-splitter"},{"type":"vcs","url":"https://github.com/orling/grapheme-splitter.git"}],"type":"library","bom-ref":"pkg:npm/grapheme-splitter@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"graphology-dag","version":"0.4.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"ddc87da0e0271d90e84f4e37bf283bba499290a279d399c5cda1da60e9f4205d8f806a3956d21abf254ae14a5b21ae2d962de18469b5653741b2e6d399ac6eff"}],"purl":"pkg:npm/graphology-dag@0.4.1","type":"library","bom-ref":"pkg:npm/graphology-dag@0.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"graphology-types","version":"0.24.8","description":"TypeScript declaration for graphology.","scope":"optional","hashes":[{"alg":"SHA-512","content":"84344a6176bc4eca191e38046b2b124723dd4fab81efc0a2f169e381b4ad950caccfbc51e763c89f136c9a707b20138cd4186293190dc8254416098f2a7a71dd"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/graphology-types@0.24.8","externalReferences":[{"type":"vcs","url":"https://github.com/graphology/graphology#readme"},{"type":"vcs","url":"git+https://github.com/graphology/graphology.git"}],"type":"library","bom-ref":"pkg:npm/graphology-types@0.24.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"graphology-utils","version":"2.5.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"7241e0f0c5eb5c990e011939e9969208cd60d568a17b677a8939b3d5e9c63b3e16fe5f37d4c0422926b278541fa30805f3077e3d0e2b95c87fe7a56cfd564b0d"}],"purl":"pkg:npm/graphology-utils@2.5.2","type":"library","bom-ref":"pkg:npm/graphology-utils@2.5.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"graphology","version":"0.26.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"f124889b38145180bcf59d38dacfb4affbcc89b63b197fc49b3e0b0cee5eee361786ea167c72123c52588e944b512246aba510eb195e9ef5f5a7f84975f5ee5e"}],"purl":"pkg:npm/graphology@0.26.0","type":"library","bom-ref":"pkg:npm/graphology@0.26.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"gray-matter","version":"4.0.3","description":"Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML or Coffee Front-Matter, with options to set custom delimiters. Used by metalsmith, assemble, verb and many other projects.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e6feb265de092b778c23716aa82a2eb3056ac2e81a03daf874d641d70c1c9ab0f4d90915e47d32ed70505bc43042a11a658d6933d6aa39148e46125174d2b8e1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/gray-matter@4.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/gray-matter"}],"type":"library","bom-ref":"pkg:npm/gray-matter@4.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Yehuda Katz","group":"","name":"handlebars","version":"4.7.9","description":"Handlebars provides the power necessary to let you build semantic templates effectively with no frustration","scope":"optional","hashes":[{"alg":"SHA-512","content":"e04ef5134ae939a42e251d80df10d9f863351f2598bf5725479f2d0bc7a640d7907b7447ecc03349b6adf95d301bbf0b401a3a9ba6f3486fcbe2906e0ac82751"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/handlebars@4.7.9","externalReferences":[{"type":"website","url":"https://handlebarsjs.com/"},{"type":"vcs","url":"https://github.com/handlebars-lang/handlebars.js.git"}],"type":"library","bom-ref":"pkg:npm/handlebars@4.7.9","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"has-flag","version":"3.0.0","description":"Check if argv has a specific flag","scope":"optional","hashes":[{"alg":"SHA-512","content":"b0a25fd7e71e401af848c92f427043343b5fe135e95615466ad7aed2df75f1b977d059db1369b8bcd2d7f9559efdda6395bf87ba0198cd6eee4171fdf073c463"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/has-flag@3.0.0","type":"library","bom-ref":"pkg:npm/has-flag@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"has-flag","version":"4.0.0","description":"Check if argv has a specific flag","scope":"optional","hashes":[{"alg":"SHA-512","content":"1329094ff4352a34d672da698080207d23b4b4a56e6548e180caf5ee4a93ba6325e807efdc421295e53ba99533a170c54c01d30c2e0d3a81bf67153712f94c3d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/has-flag@4.0.0","type":"library","bom-ref":"pkg:npm/has-flag@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"has-property-descriptors","version":"1.0.2","description":"Does the environment have full property descriptor support? Handles IE 8's broken defineProperty/gOPD.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e7924d2ae216fafab829ed418ce4e333661cb5022f093ec61731f099f64f1a8e709eb82489dd1842d9c095e152aae9999b86b3de7d814be7ab6f2e62a49760ae"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/has-property-descriptors@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/inspect-js/has-property-descriptors#readme"},{"type":"vcs","url":"git+https://github.com/inspect-js/has-property-descriptors.git"}],"type":"library","bom-ref":"pkg:npm/has-property-descriptors@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband (http://ljharb.codes)","group":"","name":"has-symbols","version":"1.1.0","description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d5c0cd77027625aa2199bdec8383a629a301c2e0b8f2c6278b91d4c360efb02f0b8c64cb2bd87e79bd57e91cae3877b8853d142c25baf22a26863528294aa53d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/has-symbols@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/has-symbols#readme"},{"type":"vcs","url":"git://github.com/inspect-js/has-symbols.git"}],"type":"library","bom-ref":"pkg:npm/has-symbols@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"hasown","version":"2.0.2","description":"A robust, ES3 compatible, \"has own property\" predicate.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d21254f5208fbe633320175916a34f5d66ba76a87b59d1f470823dcbe0b24bcac6de72f8f01725adaf4798a8555541f23d6347e58ef10f0001edb7e04a391431"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/hasown@2.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/inspect-js/hasOwn#readme"},{"type":"vcs","url":"git+https://github.com/inspect-js/hasOwn.git"}],"type":"library","bom-ref":"pkg:npm/hasown@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"help-me","version":"5.0.0","description":"Help command for node, partner of minimist and commist","scope":"optional","hashes":[{"alg":"SHA-512","content":"ef18289945fa00399c6331629341f303187fef7625291f5b985cdfe75099c11f9be98b735369b4bb8f61402a95e92be5a88aac6b1a2f7f076f6e7b30ddbff3a6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/help-me@5.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/help-me"},{"type":"vcs","url":"https://github.com/mcollina/help-me.git"}],"type":"library","bom-ref":"pkg:npm/help-me@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Brian Woodward (https://github.com/doowb)","group":"","name":"homedir-polyfill","version":"1.0.3","description":"Node.js os.homedir polyfill for older versions of node.js.","scope":"optional","hashes":[{"alg":"SHA-512","content":"7929a6584e5b6532b6368bb8834008df367daecc29ec644aa0a5d2d412d492f3ef88eaace184cdd5d8d022aad7cbd939804b5d2cfcbce898d1c2c34cf6d9c370"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/homedir-polyfill@1.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/doowb/homedir-polyfill"}],"type":"library","bom-ref":"pkg:npm/homedir-polyfill@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Yusuke Wada (https://github.com/yusukebe)","group":"","name":"hono","version":"4.12.14","description":"Web framework built on Web Standards","scope":"optional","hashes":[{"alg":"SHA-512","content":"6a6e737e0df2bbab2a9f9ca328136a8674d7ec2bfe9b4d28c7eee36da2a4acb311278ac076575dd713ddfc9cdb056b29a9a42fe91493ae014df7912c7e10beef"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/hono@4.12.14","externalReferences":[{"type":"website","url":"https://hono.dev"},{"type":"vcs","url":"git+https://github.com/honojs/hono.git"}],"type":"framework","bom-ref":"pkg:npm/hono@4.12.14","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"hosted-git-info","version":"6.1.3","description":"Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab","scope":"optional","hashes":[{"alg":"SHA-512","content":"1d5272cd4acb20bd5cd109af89587913c546c944bbc4214f4bacb275a55dd547a05be89b57f0a886a4c7f4c90e2c3a79a3eadbf360cca3becf4ee73d17ff062b"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/hosted-git-info@6.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/npm/hosted-git-info"},{"type":"vcs","url":"git+https://github.com/npm/hosted-git-info.git"}],"type":"library","bom-ref":"pkg:npm/hosted-git-info@6.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Tomas Della Vedova","group":"","name":"hpagent","version":"1.2.0","description":"A ready to use http and https agent for working with proxies that keeps connections alive!","scope":"optional","hashes":[{"alg":"SHA-512","content":"03dd5d61378807a3685c6f8fc534290820c39df1ec5bd91cd3a2efa6ed5311ef609dde9915e881a113bd26fcc4bfac4aec45fdeff75413c83fbc1313a934b708"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/hpagent@1.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/delvedor/hpagent#readme"},{"type":"vcs","url":"git+https://github.com/delvedor/hpagent.git"}],"type":"library","bom-ref":"pkg:npm/hpagent@1.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kornel Lesiński (https://kornel.ski/)","group":"","name":"http-cache-semantics","version":"4.2.0","description":"Parses Cache-Control and other headers. Helps building correct HTTP caches and proxies","scope":"optional","hashes":[{"alg":"SHA-512","content":"753c5cbcf5ea3ef5c1429ab9754afa9843095f8a08105bfa6f0a26dc50f02910ecb888e324600daa106ea009fd73545024874029abf7dc40fae44db2b3ef3b41"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/http-cache-semantics@4.2.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/kornelski/http-cache-semantics.git"}],"type":"library","bom-ref":"pkg:npm/http-cache-semantics@4.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jonathan Ong (http://jongleberry.com)","group":"","name":"http-errors","version":"2.0.1","description":"Create HTTP error objects","scope":"optional","hashes":[{"alg":"SHA-512","content":"e056d17405fe6d2766a3801416e4b458d88fcfc36016dfabf138603569a5ae3423b7543b651f7ecd395c7b6f39f71e0a497af022c69e4ea0e82909ad3fca4b99"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/http-errors@2.0.1","type":"library","bom-ref":"pkg:npm/http-errors@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Szymon Marczak","group":"","name":"http2-wrapper","version":"1.0.3","description":"HTTP2 client, just with the familiar `https` API","scope":"optional","hashes":[{"alg":"SHA-512","content":"57edb7b0332bd765a7cfb893703789af73ba008c659ef4ff6e66800003ff5dd6b7e42f74a7de7df69d05d5e1d1fcdd4a20b592a1654088e3058c105769748cc6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/http2-wrapper@1.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/szmarczak/http2-wrapper#readme"},{"type":"vcs","url":"git+https://github.com/szmarczak/http2-wrapper.git"}],"type":"library","bom-ref":"pkg:npm/http2-wrapper@1.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"ehmicky (https://github.com/ehmicky)","group":"","name":"human-signals","version":"8.0.1","description":"Human-friendly process signals","scope":"optional","hashes":[{"alg":"SHA-512","content":"78a09ae9bc27261bf18f5e24664e4d08f73a1dbe8176c53d0d970e9e640a4a73b554aadf574cc2bedb6d3d952c06f8e63439fcae97097e9c126271c18dcf7931"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/human-signals@8.0.1","externalReferences":[{"type":"vcs","url":"https://www.github.com/ehmicky/human-signals"},{"type":"vcs","url":"git+https://github.com/ehmicky/human-signals.git"}],"type":"library","bom-ref":"pkg:npm/human-signals@8.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Alexander Shtuchkin ","group":"","name":"iconv-lite","version":"0.4.24","description":"Convert character encodings in pure javascript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bf73179d901cbe7cb091350466898801cb657bb4575de79d391df5c3097b565ca85cee108bd6abbd27a73505a77b54dc4708422f51f02c8db56c4a9da63f3fac"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/iconv-lite@0.4.24","externalReferences":[{"type":"vcs","url":"https://github.com/ashtuchkin/iconv-lite"},{"type":"vcs","url":"git://github.com/ashtuchkin/iconv-lite.git"}],"type":"library","bom-ref":"pkg:npm/iconv-lite@0.4.24","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Alexander Shtuchkin ","group":"","name":"iconv-lite","version":"0.7.2","description":"Convert character encodings in pure javascript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"8a6f438c40d0e79b3d7cbe04633380bf4c8caa6301499a7a1b456f1724cc3ca5b1892047523f4d5bfaaa2e65d4c17aeb33b02fa9295309fbea45bd1c33cc98ab"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/iconv-lite@0.7.2","externalReferences":[{"type":"vcs","url":"https://github.com/pillarjs/iconv-lite"},{"type":"vcs","url":"https://github.com/pillarjs/iconv-lite.git"}],"type":"library","bom-ref":"pkg:npm/iconv-lite@0.7.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"ieee754","version":"1.2.1","description":"Read/write IEEE754 floating point numbers from/to a Buffer or array-like object","scope":"optional","hashes":[{"alg":"SHA-512","content":"75ccaa843bd7d42e3a95765c56a0a92be16d31141574830debf0dfe63b36ce8b94b2a1bb23ab05c62b480beeca60adbd29d5ce2c776ef732f8b059e85509ea68"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/ieee754@1.2.1","externalReferences":[{"type":"vcs","url":"git://github.com/feross/ieee754.git"}],"type":"library","bom-ref":"pkg:npm/ieee754@1.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"import-fresh","version":"3.3.0","description":"Import a module while bypassing the cache","scope":"optional","hashes":[{"alg":"SHA-512","content":"bde6188506be0f54012b39ef8541f16fc7dac65af0527c6c78301b029e39ec4d302cd8a8d9b3922a78d80e1323f98880abad71acc1a1424f625d593917381033"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/import-fresh@3.3.0","type":"library","bom-ref":"pkg:npm/import-fresh@3.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"import-fresh","version":"3.3.1","description":"Import a module while bypassing the cache","scope":"optional","hashes":[{"alg":"SHA-512","content":"4d1dca7eb4d94d82cf07a8d48dfc7a305f56716ac72fdb2ee5339b2b866462005d58a3ce1684a8408744b93b91f36a66b711f6b29586f61e9eb707ebd692c1a9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/import-fresh@3.3.1","type":"library","bom-ref":"pkg:npm/import-fresh@3.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Titus Wormer (https://wooorm.com)","group":"","name":"import-meta-resolve","version":"4.1.0","description":"Resolve things like Node.js — ponyfill for `import.meta.resolve`","scope":"optional","hashes":[{"alg":"SHA-512","content":"23a7e2697d3d5e2bed93e4c768c7c0c270373150390628355871750dfc7d845baf3485a95e7a2b964ce17107fa7a1aea42289910246dd69a0e0243e67abdebbb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/import-meta-resolve@4.1.0","type":"library","bom-ref":"pkg:npm/import-meta-resolve@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"indent-string","version":"4.0.0","description":"Indent each line in a string","scope":"optional","hashes":[{"alg":"SHA-512","content":"11d0c366ee00d8ec882bb2ebff6cc6fb0e6399bba4d435419c4c11110bc1ceca412640846d16bc1b153596085871a1890a745689b8c35e5abbefd5f5ff2e71c2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/indent-string@4.0.0","type":"library","bom-ref":"pkg:npm/indent-string@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"inflight","version":"1.0.6","description":"Add callbacks to requests in flight to avoid async duplication","scope":"optional","hashes":[{"alg":"SHA-512","content":"93dd88fdbd3cab8c2f16c71708bbea7ec1c2ae3ac5ef2897b10b8856f544ecdf365b7f9aaa9cee51d05b7e159ccbf159477ff82207e532028b3acbcf0eb18224"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/inflight@1.0.6","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/inflight"},{"type":"vcs","url":"https://github.com/npm/inflight.git"}],"type":"library","bom-ref":"pkg:npm/inflight@1.0.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful."}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"inherits","version":"2.0.4","description":"Browser-friendly inheritance fully compatible with standard node.js inherits()","scope":"optional","hashes":[{"alg":"SHA-512","content":"93fbc6697e3f6256b75b3c8c0af4d039761e207bea38ab67a8176ecd31e9ce9419cc0b2428c859d8af849c189233dcc64a820578ca572b16b8758799210a9ec1"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/inherits@2.0.4","type":"library","bom-ref":"pkg:npm/inherits@2.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"ini","version":"1.3.8","description":"An ini encoder/decoder for node","scope":"optional","hashes":[{"alg":"SHA-512","content":"255ff2ba0576bb35b988c4528990320ed41dfa7c6d5278de2edd1a70d770f7c90a2ebbee455c81f34b6c444384ef2bc65606a5859e913570a61079142812b17b"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/ini@1.3.8","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/ini.git"}],"type":"library","bom-ref":"pkg:npm/ini@1.3.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"ini","version":"4.1.1","description":"An ini encoder/decoder for node","scope":"optional","hashes":[{"alg":"SHA-512","content":"4109e7c4dc9fbe61c522c8fb8243dc632991f09770fe8ee6a796458a1c67ea1f028ba7e1dc3c7813580f8e9404a48b8fa3d5d5358fd953087804f3231b22f4d6"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/ini@4.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/npm/ini.git"}],"type":"library","bom-ref":"pkg:npm/ini@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Simon Boudrias ","group":"","name":"inquirer","version":"8.2.5","description":"A collection of common interactive command line user interfaces.","scope":"optional","hashes":[{"alg":"SHA-512","content":"40080f0d030482b0ecb24d578b0c07a0e19817d0406d471cd7e8fe16112f68eb7cfdc291ab22e7d14e6a03a17be1f1a14cc1b17fdda93af3c17a1dbd8d02434d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/inquirer@8.2.5","type":"library","bom-ref":"pkg:npm/inquirer@8.2.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Beau Gunderson (https://beaugunderson.com/)","group":"","name":"ip-address","version":"10.1.0","description":"A library for parsing IPv4 and IPv6 IP addresses in node and the browser.","scope":"optional","hashes":[{"alg":"SHA-512","content":"5d70031f15e6bd3f7e091c615e0e7a2c9a2f13e6e65a711607bf0b07cdd5653a6b29399a0b941faee5e8731cd36762a5d033702ae05d9488632fc2de63c49ff1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ip-address@10.1.0","externalReferences":[{"type":"vcs","url":"git://github.com/beaugunderson/ip-address.git"}],"type":"library","bom-ref":"pkg:npm/ip-address@10.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"whitequark ","group":"","name":"ipaddr.js","version":"1.9.1","description":"A library for manipulating IPv4 and IPv6 addresses in JavaScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d0a23feb4ef1a31493a07ec68cdd457d26cba14d3e6ed4e2723b1049642587f859ca437c2a998c7fbb98c0f5b747e6a467a47fc35f199574870585e26143cede"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ipaddr.js@1.9.1","type":"library","bom-ref":"pkg:npm/ipaddr.js@1.9.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Qix (http://github.com/qix-)","group":"","name":"is-arrayish","version":"0.2.1","description":"Determines if an object can be used as an array","scope":"optional","hashes":[{"alg":"SHA-512","content":"cf3d3a4bcb74a33a035cc1beb9b7b6eb37824cd5dc2883c96498bc841ac5e227422e6b38086f50b4aeea065d5ba22e4e0f31698ecc1be493e61c26cca63698ce"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-arrayish@0.2.1","externalReferences":[{"type":"vcs","url":"https://github.com/qix-/node-is-arrayish.git"}],"type":"library","bom-ref":"pkg:npm/is-arrayish@0.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"is-core-module","version":"2.16.1","description":"Is this specifier a node.js core module?","scope":"optional","hashes":[{"alg":"SHA-512","content":"51fa1e300e9f209f304d81445237a59da188ebbbfaf8deea5c912f42e2066bdf65e31b02aee498395490d2e3c0367e1d0339bc96460f68a2ebae28cbabbc76df"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-core-module@2.16.1","externalReferences":[{"type":"vcs","url":"https://github.com/inspect-js/is-core-module"},{"type":"vcs","url":"git+https://github.com/inspect-js/is-core-module.git"}],"type":"library","bom-ref":"pkg:npm/is-core-module@2.16.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"is-extendable","version":"0.1.1","description":"Returns true if a value is any of the object types: array, regexp, plain object, function or date. This is useful for determining if a value can be extended, e.g. \"can the value have keys?\"","scope":"optional","hashes":[{"alg":"SHA-512","content":"e413142cda1bd6f8055fa123430e62cd60f1ade7162bd00cef6aee80daf44c595d30e8b47e3e8993ecde288b74c468f87047d0209b61e30dce296389e1ff8017"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-extendable@0.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/is-extendable"}],"type":"library","bom-ref":"pkg:npm/is-extendable@0.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"is-extglob","version":"2.1.1","description":"Returns true if a string has an extglob.","scope":"optional","hashes":[{"alg":"SHA-512","content":"49b29b00d90deb4dd58b88c466fe3d2de549327e321b0b1bcd9c28ac4a32122badb0dde725875b3b7eb37e1189e90103a4e6481640ed9eae494719af9778eca1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-extglob@2.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/is-extglob"}],"type":"library","bom-ref":"pkg:npm/is-extglob@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"is-fullwidth-code-point","version":"3.0.0","description":"Check if the character represented by a given Unicode code point is fullwidth","scope":"optional","hashes":[{"alg":"SHA-512","content":"cf29a6e7ebbeb02b125b20fda8d69e8d5dc316f84229c94a762cd868952e1c0f3744b8dbee74ae1a775d0871afd2193e298ec130096c59e2b851e83a115e9742"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-fullwidth-code-point@3.0.0","type":"library","bom-ref":"pkg:npm/is-fullwidth-code-point@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-fullwidth-code-point","version":"5.1.0","description":"Check if the character represented by a given Unicode code point is fullwidth","scope":"optional","hashes":[{"alg":"SHA-512","content":"e571d8692ca2a800dbe119d9d4175a77a70fa7c4e88ac7b84f312370e6031d991309b2a089497b593502a4d587bb1983b7dd709ec64173dc629cdce8a6fdc931"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-fullwidth-code-point@5.1.0","type":"library","bom-ref":"pkg:npm/is-fullwidth-code-point@5.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"is-glob","version":"4.0.3","description":"Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a better user experience.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c5e9526b21c7dfa66013b6568658bba56df884d6cd97c3a3bf92959a4243e2105d0f7b61f137e4f6f61ab0b33e99758e6611648197f184b4a7af046be1e9524a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-glob@4.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/micromatch/is-glob"}],"type":"library","bom-ref":"pkg:npm/is-glob@4.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"is-interactive","version":"1.0.0","description":"Check if stdout or stderr is interactive","scope":"optional","hashes":[{"alg":"SHA-512","content":"d87bc810a468a92eb682e102faa063a6f46e6dd5fdd7458232e25367e23dcafa8a536ff5d9e48be78f47330b5a6dbe28ba9763dac30fe7493e5c97c1ffc244eb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-interactive@1.0.0","type":"library","bom-ref":"pkg:npm/is-interactive@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-interactive","version":"2.0.0","description":"Check if stdout or stderr is interactive","scope":"optional","hashes":[{"alg":"SHA-512","content":"a8fd6fa3341123e04c38f723173ae35ee42f75a936a47354319a1e1b67916e24aacaf6c47ffc10b44393397d60ba4e84deddfa4646aa892b7c03fe981253712d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-interactive@2.0.0","type":"library","bom-ref":"pkg:npm/is-interactive@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"is-number","version":"7.0.0","description":"Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e350a27e483a7bc4f2952a5db53a5e2d532abd20445734edb47bc4443ef8d7ea6767c00dbf4d34e0c44be3740a3c394af5c1af369e8d6566540656c65d8c719e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-number@7.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/is-number"}],"type":"library","bom-ref":"pkg:npm/is-number@7.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"is-obj","version":"2.0.0","description":"Check if a value is an object","scope":"optional","hashes":[{"alg":"SHA-512","content":"76ba831b771b733c7110946839770e8ed769d49fe5ca9d66367d316b39d1b3cfa6b8186041cae76eca68c795f97cec341e73276df0f3be710c12da83109128f3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-obj@2.0.0","type":"library","bom-ref":"pkg:npm/is-obj@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-plain-obj","version":"4.1.0","description":"Check if a value is a plain object","scope":"optional","hashes":[{"alg":"SHA-512","content":"f8f822faf32e50d909c84c62301b792251683322a7af9ce127852ca73e7c58e841179428219905c8d1c86c102d1f0cd502093946d9dd54db0344deb5fe6983aa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-plain-obj@4.1.0","type":"library","bom-ref":"pkg:npm/is-plain-obj@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"ForbesLindesay","group":"","name":"is-promise","version":"4.0.0","description":"Test whether an object looks like a promises-a+ promise","scope":"optional","hashes":[{"alg":"SHA-512","content":"86fa6823a928ae124c9de8f6f3975283a9eed7e7babb1b3bcc6dc16009b96f2a83b2024d5b0c7333acfa8998808104784c9df42660533b0a99530dd69721f701"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-promise@4.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/then/is-promise.git"}],"type":"library","bom-ref":"pkg:npm/is-promise@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-stream","version":"4.0.1","description":"Check if something is a Node.js stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"0e7cfdd8d2270ea61c90611426febcf516d18934841c243bc0e55e00b6e43b3f7df58a6a4f8eb2311206b52365da208bd70680652c78d3e879f4d57f130cd5fc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-stream@4.0.1","type":"library","bom-ref":"pkg:npm/is-stream@4.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-unicode-supported","version":"0.1.0","description":"Detect whether the terminal supports Unicode","scope":"optional","hashes":[{"alg":"SHA-512","content":"927c46daae140b7bbcb2d446c8054908e771166bf90d989171d94868041701b49f2726be3a1a29368b4b42bb2d061aaeaaee19a6e29b0dcffc4ba9a05e03c53f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-unicode-supported@0.1.0","type":"library","bom-ref":"pkg:npm/is-unicode-supported@0.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-unicode-supported","version":"1.3.0","description":"Detect whether the terminal supports Unicode","scope":"optional","hashes":[{"alg":"SHA-512","content":"e37af6991bf3fbc25120a9d627ede3f09b6344a67a1a68f37da13faa20499e290d9d8bffe9b6a0449d64521364f11e445ff1a4a1b0feafeb1fc423c9b222812d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-unicode-supported@1.3.0","type":"library","bom-ref":"pkg:npm/is-unicode-supported@1.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"is-unicode-supported","version":"2.1.0","description":"Detect whether the terminal supports Unicode","scope":"optional","hashes":[{"alg":"SHA-512","content":"984d341a7cdae44101dc3b341df3329656736c1ae62ce5f7bdf5a88fd03d3c49d37eb6ad43f05c6893ae3219e48635ef6f6f85918dd5b87aad006a533e682515"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-unicode-supported@2.1.0","type":"library","bom-ref":"pkg:npm/is-unicode-supported@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"wayfind","group":"","name":"is-utf8","version":"0.2.1","description":"Detect if a buffer is utf8 encoded.","scope":"optional","hashes":[{"alg":"SHA-512","content":"acc60f62f0b3b17cb022c95d80b692a0f970e4f7e807fb2cafb858e292df72876b03933f780af36b56bd5664e234804d323386af53b0f664f2536a3af54e94f5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-utf8@0.2.1","type":"library","bom-ref":"pkg:npm/is-utf8@0.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"is-windows","version":"1.0.2","description":"Returns true if the platform is windows. UMD module, works with node.js, commonjs, browser, AMD, electron, etc.","scope":"optional","hashes":[{"alg":"SHA-512","content":"7972b55089ead9b3e68f25fa7b754723330ba1b73827de22e005a7f87a6adce5392a4ad10bde8e01c4773d127fa46bba9bc4d19c11cff5d917415b13fc239520"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/is-windows@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/is-windows"}],"type":"library","bom-ref":"pkg:npm/is-windows@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"isexe","version":"2.0.0","description":"Minimal module to check if a file is executable.","scope":"optional","hashes":[{"alg":"SHA-512","content":"447c4c2e9f659ca1c61d19e0f5016144231b600715a67ebdb2648672addfdfac638155564e18f8aaa2db4cb96aed2b23f01f9f210d44b8210623694ab3241e23"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/isexe@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/isexe#readme"},{"type":"vcs","url":"git+https://github.com/isaacs/isexe.git"}],"type":"library","bom-ref":"pkg:npm/isexe@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter ","group":"","name":"jackspeak","version":"3.4.3","description":"A very strict and proper argument parser.","scope":"optional","hashes":[{"alg":"SHA-512","content":"386959429cf6c9f6a103f45dd58f0277d48812caaf5e42d5a12c3f720c219e114c0dbb1015e658a0927b6c86414bd05c6a6516f7a6acabf9e93d6ba033e45007"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/jackspeak@3.4.3","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/jackspeak.git"}],"type":"library","bom-ref":"pkg:npm/jackspeak@3.4.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter ","group":"","name":"jackspeak","version":"4.2.3","description":"A very strict and proper argument parser.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ca4915470ad8bc59b59dbd8025f28a60faf47a617a2225c3614685c78667f59b881fb32bcc4677b03e5196a197351a47b6f5072723a70841713a5343b46a3bc2"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/jackspeak@4.2.3","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/jackspeak.git"}],"type":"library","bom-ref":"pkg:npm/jackspeak@4.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"jiti","version":"2.4.1","description":"Runtime typescript and ESM support for Node.js","scope":"optional","hashes":[{"alg":"SHA-512","content":"c8f05387079ca75c12f439a8038c78291da1dd0a2c95a7270d1f32a6e14cf7ada4238ff8e7a232d681f1d910208788df64d767d1b72db1d69d71e881520a54d6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/jiti@2.4.1","type":"library","bom-ref":"pkg:npm/jiti@2.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Filip Skokan ","group":"","name":"jose","version":"6.2.2","description":"JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes","scope":"optional","hashes":[{"alg":"SHA-512","content":"77b90f0dddf828efd89f36833a58a41a93aeadf1740720b6b04578700342b5da8b9537c1970da9d783bfe5dff3bf8d2024f6c84317c44b79d2c75fe860dcae65"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/jose@6.2.2","externalReferences":[{"type":"vcs","url":"https://github.com/panva/jose"}],"type":"library","bom-ref":"pkg:npm/jose@6.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"egoist <0x142857@gmail.com>","group":"","name":"joycon","version":"3.1.1","description":"Load config with ease.","scope":"optional","hashes":[{"alg":"SHA-512","content":"df8c01fd8ecc5bb6f38ca46350a4dae3a23667b795eb646486f6be2a4a295bb42fbff392581aaf91263bbeeb0e3eb36e65c506ded029bdd560341f0f3a3dd23f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/joycon@3.1.1","type":"library","bom-ref":"pkg:npm/joycon@3.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Simon Lydell","group":"","name":"js-tokens","version":"4.0.0","description":"A regex that tokenizes JavaScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"45d2547e5704ddc5332a232a420b02bb4e853eef5474824ed1b7986cf84737893a6a9809b627dca02b53f5b7313a9601b690f690233a49bce0e026aeb16fcf29"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/js-tokens@4.0.0","type":"library","bom-ref":"pkg:npm/js-tokens@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Vladimir Zapparov ","group":"","name":"js-yaml","version":"4.1.1","description":"YAML 1.2 parser and serializer","scope":"optional","hashes":[{"alg":"SHA-512","content":"a90293e334315e5f252f006d1cc5b06937067c5399be23897addcecbfc661a4da0647ebbec224cf44bed7dd4a48167004d9863ff9e49674ae6cb79b2093e65b0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/js-yaml@4.1.1","type":"library","bom-ref":"pkg:npm/js-yaml@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dominic Tarr (http://dominictarr.com)","group":"","name":"json-buffer","version":"3.0.1","description":"JSON parse & stringify that supports binary via bops & base64","scope":"optional","hashes":[{"alg":"SHA-512","content":"e1b57905f4769aa7d04c99be579b4f3dd7fe669ba1888bd3b8007983c91cad7399a534ff430c15456072c17d68cebea512e3dd6c7c70689966f46ea6236b1f49"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/json-buffer@3.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/dominictarr/json-buffer"},{"type":"vcs","url":"git://github.com/dominictarr/json-buffer.git"}],"type":"library","bom-ref":"pkg:npm/json-buffer@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kat Marchán ","group":"","name":"json-parse-even-better-errors","version":"2.3.1","description":"JSON.parse with context information on error","scope":"optional","hashes":[{"alg":"SHA-512","content":"c72170ca1ae8fc91287fa1a17b68b3d8d717a23dac96836c5abfd7b044432bfa223c27da36197938d7e9fa341d01945043420958dcc7f7321917b962f75921db"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/json-parse-even-better-errors@2.3.1","type":"library","bom-ref":"pkg:npm/json-parse-even-better-errors@2.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"json-parse-even-better-errors","version":"3.0.2","description":"JSON.parse with context information on error","scope":"optional","hashes":[{"alg":"SHA-512","content":"7e2d0d1b86cf8c21ee9d425f7e62ddd20c6cb0882436602b32f8ace2235a87a3b08353022635a111c0cb9ac2ba886909ab7b47c1b0e44e571eef4fed99737871"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/json-parse-even-better-errors@3.0.2","externalReferences":[{"type":"vcs","url":"git+https://github.com/npm/json-parse-even-better-errors.git"}],"type":"library","bom-ref":"pkg:npm/json-parse-even-better-errors@3.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Evgeny Poberezkin","group":"","name":"json-schema-traverse","version":"1.0.0","description":"Traverse JSON Schema passing each schema object to callback","scope":"optional","hashes":[{"alg":"SHA-512","content":"34cf3f3fd9f75e35e12199f594b86415a0024ce5114178d6855e0103f4673aff31be0aadaa9017f483b89914314b1d51968e2dab37aa6f4b0e96bb9a3b2dddba"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/json-schema-traverse@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/epoberezkin/json-schema-traverse#readme"},{"type":"vcs","url":"git+https://github.com/epoberezkin/json-schema-traverse.git"}],"type":"library","bom-ref":"pkg:npm/json-schema-traverse@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Remy Rylan (https://github.com/RemyRylan)","group":"","name":"json-schema-typed","version":"8.0.2","description":"JSON Schema TypeScript definitions with complete inline documentation.","scope":"optional","hashes":[{"alg":"SHA-512","content":"7d08685dd72f737576f31ec2ec132ce0fe7e90d96051445eda39944f54fffe8051303aeacb540f7a52629b0646a3b1e0f553d5dc4415e419eae216c5cb6bdeb4"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/json-schema-typed@8.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/RemyRylan/json-schema-typed/tree/main/dist/node"},{"type":"vcs","url":"https://github.com/RemyRylan/json-schema-typed.git"}],"type":"library","bom-ref":"pkg:npm/json-schema-typed@8.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"json-stringify-safe","version":"5.0.1","description":"Like JSON.stringify, but doesn't blow up on circular refs.","scope":"optional","hashes":[{"alg":"SHA-512","content":"642960e80698bda9af60413cd9ddc8c9ddef49222343ea1d823693cd1b8edeceeda0274529cce86f68b4cc287b244f245a7d7bcaf016854571bea1b051a96c44"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/json-stringify-safe@5.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/json-stringify-safe"},{"type":"vcs","url":"git://github.com/isaacs/json-stringify-safe"}],"type":"library","bom-ref":"pkg:npm/json-stringify-safe@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"JP Richardson ","group":"","name":"jsonfile","version":"6.1.0","description":"Easily read/write JSON files.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e5d8277563ab8984a6e5c9d86893616a52cd0ca3aa170c8307faebd44f59b067221af28fb3c476c5818269cb9fdf3e8ad58283cf5f367ddf9f637727de932a5d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/jsonfile@6.1.0","type":"library","bom-ref":"pkg:npm/jsonfile@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"JP Richardson ","group":"","name":"jsonfile","version":"6.2.0","description":"Easily read/write JSON files.","scope":"optional","hashes":[{"alg":"SHA-512","content":"146b8fc37d0074e2144d1302d8e311b5057e8e4563d9c7cfa927965efd4d100275a99e736f55facf598585b7ce07f8b2decb09083fb72ae67cafc0b7b9516502"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/jsonfile@6.2.0","type":"library","bom-ref":"pkg:npm/jsonfile@6.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jared Wray (http://jaredwray.com)","group":"","name":"keyv","version":"4.5.4","description":"Simple key-value storage with support for multiple backends","scope":"optional","hashes":[{"alg":"SHA-512","content":"a3154790747f1097f608d5e75b144b5ba9a0ec9c82094706d03b441a62f672d528d4f3538a7d4f52297eafffb8af93295600bf7e7d648ecc7b9a34ae8caa88a7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/keyv@4.5.4","externalReferences":[{"type":"vcs","url":"https://github.com/jaredwray/keyv"},{"type":"vcs","url":"git+https://github.com/jaredwray/keyv.git"}],"type":"library","bom-ref":"pkg:npm/keyv@4.5.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"kind-of","version":"6.0.3","description":"Get the native type of a value.","scope":"optional","hashes":[{"alg":"SHA-512","content":"75c4b5ba5fbdb66783f794fec76f3f7a12e077d98435adcbb2f0d3b739b7bf20443bb44fa6dbc00feb78e165576948d305172ba45785942f160abb94478e7a87"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/kind-of@6.0.3","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/kind-of"}],"type":"library","bom-ref":"pkg:npm/kind-of@6.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-darwin-arm64","version":"2.1.6","description":"The macOS ARM 64-bit binary for lefthook, git hooks manager.","scope":"optional","hashes":[{"alg":"SHA-512","content":"87207b79e897efc052eba7fbd1bc9325a7032c2ff1575be032ff67fa274552caccec9dd475dfda83d020e4d3f7b7478dd04a90a80b6b367b77e441f4d65c6745"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lefthook-darwin-arm64@2.1.6","externalReferences":[{"type":"vcs","url":"https://github.com/evilmartians/lefthook#readme"},{"type":"vcs","url":"git+https://github.com/evilmartians/lefthook.git"}],"type":"library","bom-ref":"pkg:npm/lefthook-darwin-arm64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-darwin-x64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"e4a6ba705c621fcde4aedf8e31142d992eb3aa8651e522d74ae74b8d36d903573766a174f9daa479be17701ea9971e964741858b369bb9ce818b78973c87b579"}],"purl":"pkg:npm/lefthook-darwin-x64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-darwin-x64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"darwin"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-freebsd-arm64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"56cc323a0e4254ddeb31a389d87b67925b6230a8051d6ff0a2e5b15ec57c47149ae2d81638ac4cd049925e2f2a7368d7f8b4606ba074b8e63ab685d2d35cd6c4"}],"purl":"pkg:npm/lefthook-freebsd-arm64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-freebsd-arm64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"freebsd"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-freebsd-x64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"bd7b0250562e570ad55b0732a41ed9b761dffb9a65d55d656bb65fbc661969345446ed3317f5d49cc17f9cecff3de6c6bf47f8c7f88e5975b03fb138db1456b2"}],"purl":"pkg:npm/lefthook-freebsd-x64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-freebsd-x64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"freebsd"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-linux-arm64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"58326242125d64ebca39165dfa417f9acda5e8d4ac5f3740f5a85f972afae55f74002e23112db6dd6f15b4431b18f52d1ee19630467fbff5efc255afd08a33f6"}],"purl":"pkg:npm/lefthook-linux-arm64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-linux-arm64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-linux-x64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"0b5f2709dee74d7d4054be1372fc0c98b00ed552353ae1a5b883938e23e4050ef8e8bb351e12f9ae5fff8cc3c00a64f6f189b121027665c2cf36686f115059bf"}],"purl":"pkg:npm/lefthook-linux-x64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-linux-x64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"linux"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-openbsd-arm64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"99938cc4cf0788fc551570cedcfb4251b1f8181f2b92f79786c817176ee80194d857343780ef6f4faaffa718adea6b2a457cf77ef7308a62d52606fc791b1af0"}],"purl":"pkg:npm/lefthook-openbsd-arm64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-openbsd-arm64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"openbsd"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-openbsd-x64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"b06f402cb6529e730e7d7bbe07b4a6c4584986ea008786ea8b9127e5a6872444f8f13aab2ce716599b87fbb02b14cea0aff53929f494bdd98716663c5aa09c22"}],"purl":"pkg:npm/lefthook-openbsd-x64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-openbsd-x64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"openbsd"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-windows-arm64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"943f321566380acba58ddd11aacec441ac920b456f0dfed5deb37516144c5084934431eeb5e6c8a26d4ba1cf1c9103ef298182ea67ed4fd9341af8a9b12f81af"}],"purl":"pkg:npm/lefthook-windows-arm64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-windows-arm64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"arm64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"lefthook-windows-x64","version":"2.1.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"ab8cf69f7c6e70bb1ca168b23301558848f737c3034a43ee94cc1c258b826051e83e13f587e89ca8dbbb4112c6623e809d5ac24307a25096374dbd97f866c3fc"}],"purl":"pkg:npm/lefthook-windows-x64@2.1.6","type":"library","bom-ref":"pkg:npm/lefthook-windows-x64@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32"},{"name":"cdx:pnpm:cpu","value":"x64"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"mrexox","group":"","name":"lefthook","version":"2.1.6","description":"Simple git hooks manager","scope":"optional","hashes":[{"alg":"SHA-512","content":"c3db01a11d2674dfa425cdd207ce55ce9880025e39d7faf174245c665c16ef540b8e4787dc405016073854c8f96a978fc9c8580c7025a8459307c27c24afe3d5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lefthook@2.1.6","externalReferences":[{"type":"vcs","url":"https://github.com/evilmartians/lefthook#readme"},{"type":"vcs","url":"git+https://github.com/evilmartians/lefthook.git"}],"type":"library","bom-ref":"pkg:npm/lefthook@2.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Roman Seidelsohn ","group":"","name":"license-checker-rseidelsohn","version":"4.4.2","description":"Extract NPM package licenses - Feature enhanced version of the original license-checker v25.0.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"49ff1668985ddaf10bbc29def9fad2f405ea9d8fefbf9f75b36fe765c243c139e836096d1b89809a87a77df55bf0bd983d161bc4044bcab1c9042dfc0157e9b8"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/license-checker-rseidelsohn@4.4.2","externalReferences":[{"type":"vcs","url":"https://github.com/RSeidelsohn/license-checker-rseidelsohn.git"}],"type":"library","bom-ref":"pkg:npm/license-checker-rseidelsohn@4.4.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Brian Donovan ","group":"","name":"lines-and-columns","version":"1.2.4","description":"Maps lines and columns to character offsets and back.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ef297295eb1943f3d5dbd8e110397751f8e8e995fb802a89af917b3caaea73ddefedfcd2ca6b75069c0453c9c0517b3cab3cefaa16e384ae50660e8cb7f1e406"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lines-and-columns@1.2.4","externalReferences":[{"type":"vcs","url":"https://github.com/eventualbuddha/lines-and-columns#readme"},{"type":"vcs","url":"https://github.com/eventualbuddha/lines-and-columns.git"}],"type":"library","bom-ref":"pkg:npm/lines-and-columns@1.2.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Cenk Kilic (https://cenk.kilic.dev)","group":"","name":"listr2","version":"10.2.1","description":"Terminal task list reborn! Create beautiful CLI interfaces via easy and logical to implement task lists that feel alive and interactive.","scope":"required","hashes":[{"alg":"SHA-512","content":"ec8e649c42ec24a4d48d71be03a064280886916d62db97cd6bfc65525f6116d935e566c4f639dd03cf71bb917340aad8e6595a8c4d617d96453082d79031e4fd"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/listr2@10.2.1","type":"library","bom-ref":"pkg:npm/listr2@10.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"ImportedModules","value":"listr2,Listr,listr2/Listr"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/cli/dist/commands/bench.js#22"},{"location":"packages/cli/src/commands/bench.ts#23"}]}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.camelcase","version":"4.3.0","description":"The lodash method `_.camelCase` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"4f0b849c29f16dcdeb02f85ffcb6c6eed2540f386a5f2167bf776dccb38f8021bf84e0cbed6167b1bc24b640fbc9457446bade3ff9753c02eafd84a0e95be394"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.camelcase@4.3.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.camelcase@4.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.clone","version":"4.5.0","description":"The lodash method `_.clone` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"1a1ad57b07a24c3eae4e69a7e6157f97380242171cc117885512c7a7b2d3e12a293a3a8467905b5fc6f959612d00a6ac8e6cbc851ed93f0b1897144292ee6876"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.clone@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.clone@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"This package is deprecated. Use structuredClone instead."},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.clonedeep","version":"4.5.0","description":"The lodash method `_.cloneDeep` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"1f9661085db9ae215df6e0795029152a8eb59b74bfc59935c78c00eb2a7f2f74453fa67f7871f5ca641c18ba3b27718c3df9b457fee6d6daf21ee195c69a8405"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.clonedeep@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.clonedeep@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.constant","version":"3.0.0","description":"The modern build of lodash’s `_.constant` as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"5f95ccac1f92748d6615af35d7ad8d493a3f60d776dd22dd2ce2f37174f04b88a70d9e5808bf17ebb505cd92401f80aa21ae91f1caf9e824a17c0e4ae4c16261"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.constant@3.0.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.constant@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.filter","version":"4.6.0","description":"The lodash method `_.filter` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a57614cbb3d1f0108bc17e66809fda36dc8ebee253759028f4440552f30862e82a989c67ad868036f4db82774ecc7482491d309e50417d15c92f949b5b1a3715"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.filter@4.6.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.filter@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.flatmap","version":"4.5.0","description":"The lodash method `_.flatMap` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"fce72970019696b672a0719e1e1ddc0286ba9c6757e9062d9b334ff3826aa25e94110436808694dc7fb478809c8dc286974fd717c2d63ae8cd9fd95f7ec9da3a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.flatmap@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.flatmap@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.foreach","version":"4.5.0","description":"The lodash method `_.forEach` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"6845d317877e9b4e6b54e0141b7cf8bd9678c557b12ca646174948c6e1d9d47a65a64ff707a675fbf2022026112e6edce35676c627a36e40a42684e5ca9a1785"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.foreach@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.foreach@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.has","version":"4.5.2","description":"The lodash method `_.has` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ae7614748a3ac510899d099b545130731175e387ab943f8cdd87095157ac7e553da5a41a13ca7e7c90dc210ae53186f1a003452fe001f6166bcd20419393cbfa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.has@4.5.2","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.has@4.5.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.isempty","version":"4.4.0","description":"The lodash method `_.isEmpty` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0a32e177c447aa0e5b6b18c7c3c403c6215312491bfcb5b4523836ebb38286b1144b121ad6f0df117780d1801dbeeb6d6163c03c5f0c2b4d58575a9c45bccce"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.isempty@4.4.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.isempty@4.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.isequal","version":"4.5.0","description":"The Lodash method `_.isEqual` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a43a3796ef0985f8ea96ce8690c8296a1b05f640b26b2860ca48f22cc3454ca5aba5574042d6320789ae00c5a8cc10788a0fddb56026b0cc4b108f30bb3f8361"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.isequal@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.isequal@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"This package is deprecated. Use require('node:util').isDeepStrictEqual instead."},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.isfunction","version":"3.0.9","description":"The Lodash method `_.isFunction` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"022ad7363d79b9120c30f8a19e42270788b734779be2206d360f56456b8ada8df54be78fc303660cf6932f7a3b75327e5573598a6eeb16ce2bc4de18535a1427"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.isfunction@3.0.9","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.isfunction@3.0.9","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.isplainobject","version":"4.0.6","description":"The lodash method `_.isPlainObject` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"a125f3696ca908c1e43c2dcdbc111a3c77f42ac0399af3eb38f810583b1b83c9fba2b676f743340660bf8e0459e2f709e834c0863aec49881db16fc5f8c14e04"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.isplainobject@4.0.6","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.isplainobject@4.0.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.isundefined","version":"3.0.1","description":"The modern build of lodash’s `_.isUndefined` as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"3170758acdecf3df7f703f2385e61813657da931f02afb7e9e90b0a43fb54b19b74377040970a26078de1d65cdc2be90d123813eb6140f17a776b3ba82855310"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.isundefined@3.0.1","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.isundefined@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.kebabcase","version":"4.1.1","description":"The lodash method `_.kebabCase` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"37c5d14c830caaa0e04b2e152ca3e727ffa1a4664df8f1d08899d27a762a3da555fcd0aa1288139c07592d08862a1c57f890acf30696ab6b755c758a3a6958f2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.kebabcase@4.1.1","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.kebabcase@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.keys","version":"4.2.0","description":"The lodash method `_.keys` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"27bf4c909729ec37f99a2cc78953698e81d72e2e072e387d54b4bf33b950486a10fb4a10fa55848a044492a2b28b33c1d486b0bd02cb298f26cc4726d6d932c5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.keys@4.2.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.keys@4.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.map","version":"4.6.0","description":"The lodash method `_.map` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c28acd1c628b0deb66704603be1dacb4f0abad04643f6d04e25d22212ec5f04bf332a0418bb96dbc53799b51ef4dfd4fec9935b712a1bc572662c0abf0ed85d5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.map@4.6.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.map@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton ","group":"","name":"lodash.merge","version":"4.6.2","description":"The Lodash method `_.merge` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d0aa63a97455beb6320ac5f5b3047f5d32b4bdae9542440ce8c368ecfa96efb0728c086801103c11facfd4de3e2a52a3f184b46540ad453fd852e872603ba321"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.merge@4.6.2","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.merge@4.6.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton ","group":"","name":"lodash.mergewith","version":"4.6.2","description":"The Lodash method `_.mergeWith` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"18ade0e513d959345278b4a980ff1786b6bea678c10b9eaaf456587b577944ddd3277e5d6e41b2dd8a8148c6f20ab95b8be08cf59dc9939c932fb442b2a6e8c9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.mergewith@4.6.2","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.mergewith@4.6.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.reduce","version":"4.6.0","description":"The lodash method `_.reduce` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"eab6917b6bf1098061a41bbe07e4ed3465336a1fa142355dbb7135ef07eeb23cab5c191ada7052f0e1ff823559e4fbc73a15a62194d8ae2d3d67a9ff41f9cd33"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.reduce@4.6.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.reduce@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.size","version":"4.2.0","description":"The lodash method `_.size` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c1bbb7485d570b98a3aa6d2988dc70e7dc826ee51fda469286e99804b594adc0afc21e82f28773e9263fc065730964d04c52ffd5881bbea83678bb6ca5455501"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.size@4.2.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.size@4.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.snakecase","version":"4.1.1","description":"The lodash method `_.snakeCase` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"419d5de31a011d851eba8ba112ade593752aee5760c855c6061834e3ea112e7233f28f53eb9121fbc61dae85309fce3acdc86403dc83b0397909555a5769ea63"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.snakecase@4.1.1","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.snakecase@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.startcase","version":"4.4.0","description":"The lodash method `_.startCase` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f962aab0adbde073127368c46cd8291e977425f201869eeb115e1aa975aa16be80957a2ff9295c801d45bf4d72da419edc673c9cc5bb540d12ac6b929117c812"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.startcase@4.4.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.startcase@4.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.topairs","version":"4.3.0","description":"The lodash method `_.toPairs` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"aab44c6f290148419d3a040b249a9549d3d6303ed0f86249e633117d061bf8b4cbb30ded61521a6e70b346a4c96f6593a35ecf1b980dcd7b856998181fff5201"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.topairs@4.3.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.topairs@4.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.transform","version":"4.6.0","description":"The lodash method `_.transform` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"2cedfb667866055c741af394fdc690ba2a4487818df364dc5afdf21e579b18380ec5bc62c30cd6e4f731d8072fa48bf65a6be648ca02e3ddb2405361cbf97f51"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.transform@4.6.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.transform@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.union","version":"4.6.0","description":"The lodash method `_.union` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"738a41d82746ac676330a60b03e5e24433bb6343d141b9bf1b383ca8c8fe407fa91550284e9e6c0693b4a1d2f7163a0f0868caf7aa7aaac3fec90a222e838173"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.union@4.6.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.union@4.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.uniq","version":"4.5.0","description":"The lodash method `_.uniq` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c5f05a5d077daf277d765483be7bc8d25bf17cb2656006735e89946332cab5478e42f38fe698aa016b86b6b8567aa6972bd861a0cfe2c89739015f977ec5f71d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.uniq@4.5.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.uniq@4.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.upperfirst","version":"4.3.1","description":"The lodash method `_.upperFirst` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b1178a39824825fef876124e3615387b4fec8738b5b6b55b4960ce84a604e575763be1fb49bd838a1c2e73bc56c5597e0e014fc93a88377ccc7d3f5cab98960e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.upperfirst@4.3.1","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.upperfirst@4.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton (http://allyoucanleet.com/)","group":"","name":"lodash.values","version":"4.3.0","description":"The lodash method `_.values` exported as a module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"af4470bdd0aff2277d4d46e56ff3bbad83f0572ea57ab09b71ac2b7dda3d882ff5b75c2c34c2649ceefd58d060c241f4848789d3c8e6bef1126c5a4d6f88c7d1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash.values@4.3.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash.values@4.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"John-David Dalton ","group":"","name":"lodash","version":"4.18.0","description":"Lodash modular utilities.","scope":"optional","hashes":[{"alg":"SHA-512","content":"97599f8f66ad32a9dd00723796ced7a8fc448d5d89f5991c3721e9a19037af64f52cbc0307af6382431687bd582b08416cad06d9fe164a7d396a19905d5c6ea4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lodash@4.18.0","externalReferences":[{"type":"website","url":"https://lodash.com/"}],"type":"library","bom-ref":"pkg:npm/lodash@4.18.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"Bad release. Please use lodash@4.17.21 instead."}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"log-symbols","version":"4.1.0","description":"Colored symbols for various log levels. Example: `✔︎ Success`","scope":"optional","hashes":[{"alg":"SHA-512","content":"f173efa4003cbb285fb5ebbca48bd0c69259ed2618769522bd9a46cbab05b01b8a458ffbad019abde75e07c68af99932ababa930554bffd016eaf398cdf4722e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/log-symbols@4.1.0","type":"library","bom-ref":"pkg:npm/log-symbols@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"log-symbols","version":"6.0.0","description":"Colored symbols for various log levels. Example: `✔︎ Success`","scope":"optional","hashes":[{"alg":"SHA-512","content":"8b6e26f2ba708663c84b8cec70dccae8c484864d035166bff226105b185f7d5f23908e0f86fb3717eaae2f9c6f4b481d411d05c9308c307df763bf1d0d3cf323"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/log-symbols@6.0.0","type":"library","bom-ref":"pkg:npm/log-symbols@6.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"log-update","version":"6.1.0","description":"Log by overwriting the previous output in the terminal. Useful for rendering progress bars, animations, etc.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f627bc22d3d1ead8d8e6e60987c2bf66bbff44c67954e94e5afb597441d849314a65f2013d06bdb4e0047805a177e02722778b276db0e5f8ce62da2eb695aae7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/log-update@6.1.0","type":"library","bom-ref":"pkg:npm/log-update@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"longest","version":"2.0.1","description":"Get the longest item in an array.","scope":"optional","hashes":[{"alg":"SHA-512","content":"023cf16fc08ce960271638229683ec2376c5f960b172f85d206dca340d8a37deb6fad741b0772e438938a97fc4712ff609191c734880911f79e8d89be9a5d4fd"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/longest@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/longest"}],"type":"library","bom-ref":"pkg:npm/longest@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"lowercase-keys","version":"2.0.0","description":"Lowercase the keys of an object","scope":"optional","hashes":[{"alg":"SHA-512","content":"b6a357ad2efca0c384ef734cc4ae0430b42c428c167fc8caa281fd83bc4f6af453ef4e91e9b91027a0d8d937bb42e91a66cba5c5adf4c10edb934a66e1788798"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/lowercase-keys@2.0.0","type":"library","bom-ref":"pkg:npm/lowercase-keys@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter ","group":"","name":"lru-cache","version":"10.4.3","description":"A cache object that deletes the least-recently-used items.","scope":"required","hashes":[{"alg":"SHA-512","content":"24d03365c5eb0ade365462ee633d337c0cc37c0bc9596e807d8943050c835790c2948da6e6c0262be3883bbb39f577ec46c587a74da3009ad169d3d1193b7a49"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/lru-cache@10.4.3","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/node-lru-cache.git"}],"type":"library","bom-ref":"pkg:npm/lru-cache@10.4.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"ImportedModules","value":"lru-cache,LRUCache,lru-cache/LRUCache"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/mcp/dist/connection-pool.js#22"},{"location":"packages/mcp/src/connection-pool.ts#23"}]}},{"author":"Isaac Z. Schlueter ","group":"","name":"lru-cache","version":"11.3.5","description":"A cache object that deletes the least-recently-used items.","scope":"required","hashes":[{"alg":"SHA-512","content":"371545c0b027addf62eca501c42e03ad4866823cceb3ed509b9d03de8175fe82feaf5369678800ef1bc6d3fcc9f1ebd1ef320a9f8bcb7fba9335db9616d0ab47"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/lru-cache@11.3.5","externalReferences":[{"type":"vcs","url":"git+ssh://git@github.com/isaacs/node-lru-cache.git"}],"type":"library","bom-ref":"pkg:npm/lru-cache@11.3.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/mcp/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"ImportedModules","value":"lru-cache,LRUCache,lru-cache/LRUCache"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/mcp/dist/connection-pool.js#22"},{"location":"packages/mcp/src/connection-pool.ts#23"}]}},{"author":"Isaac Z. Schlueter ","group":"","name":"lru-cache","version":"7.18.3","description":"A cache object that deletes the least-recently-used items.","scope":"required","hashes":[{"alg":"SHA-512","content":"8ee9a573404852b4b7a891a0224599b327c033b3425a205c08386777edcd34ce4a6c198b4e01d57d605c83a5beacb52c229ce91113ecbf050fec272401048ea0"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/lru-cache@7.18.3","type":"library","bom-ref":"pkg:npm/lru-cache@7.18.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"ImportedModules","value":"lru-cache,LRUCache,lru-cache/LRUCache"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/mcp/dist/connection-pool.js#22"},{"location":"packages/mcp/src/connection-pool.ts#23"}]}},{"author":"Sam Verschueren (github.com/SamVerschueren)","group":"","name":"map-age-cleaner","version":"0.1.3","description":"Automatically cleanup expired items in a Map","scope":"optional","hashes":[{"alg":"SHA-512","content":"6c9cf1ea73283fa3c32cf0459a0efec5129e159bc56e832b1a5c66363f4296f5f9dcaae6bcce5b5c55c45a36f3e1ccf50059fe8d627dcff0c94b3ee1aecd30df"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/map-age-cleaner@0.1.3","type":"library","bom-ref":"pkg:npm/map-age-cleaner@0.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"matcher","version":"3.0.0","description":"Simple wildcard matching","scope":"optional","hashes":[{"alg":"SHA-512","content":"3a478368067f6d00b1785028ccce793ca70a534c8930f1a27cbc15e108238adbbee4ca007d240de25b0b25e5d9d5bf30d31fbf12675ae8c6605d2d63bec6a99e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/matcher@3.0.0","type":"library","bom-ref":"pkg:npm/matcher@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"math-intrinsics","version":"1.1.0","description":"ES Math-related intrinsics and helpers, robustly cached.","scope":"optional","hashes":[{"alg":"SHA-512","content":"fc85ed6f0124e474cfc84c32297ea11a4617c4cf676e3eb807e8a55499c2fd1e81d291f91b85776f4a556cbec3063e2d921040a696d05257fa17a5e5f4b1eed6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/math-intrinsics@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/es-shims/math-intrinsics#readme"},{"type":"vcs","url":"git+https://github.com/es-shims/math-intrinsics.git"}],"type":"library","bom-ref":"pkg:npm/math-intrinsics@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"media-typer","version":"1.1.0","description":"Simple RFC 6838 media type parser and formatter","scope":"optional","hashes":[{"alg":"SHA-512","content":"6a2b27ac33f818d7b4e9470a1675796df30d3c1530e23b0b19a5b059f9c7defd361a706e5d7d8c0959f945bad6a348f7a5ccd48a561b96aedf43b370dade572b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/media-typer@1.1.0","type":"library","bom-ref":"pkg:npm/media-typer@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"mem","version":"8.1.1","description":"Memoize functions - An optimization used to speed up consecutive function calls by caching the result of calls with identical input","scope":"optional","hashes":[{"alg":"SHA-512","content":"a85085503b3b5376fc9810cfcb3e444e810aa009200b3aaab88822f6792447d6e2c713953ab7bed3d95bb87efef4a9f6345a66e7a3371945956d4da142074008"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mem@8.1.1","type":"library","bom-ref":"pkg:npm/mem@8.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"meow","version":"13.2.0","description":"CLI app helper","scope":"optional","hashes":[{"alg":"SHA-512","content":"a7140943307a76318f5e1d3c75a704968305a29b0ea865512853d8bcf3adf570d9d476ac6e00d903be02246ade494e06dc093b105246a221f66aeabec9721280"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/meow@13.2.0","type":"library","bom-ref":"pkg:npm/meow@13.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"merge-descriptors","version":"2.0.0","description":"Merge objects using their property descriptors","scope":"optional","hashes":[{"alg":"SHA-512","content":"4a7937d785796b214b869ddf914444b9be96b6305f3dd08f6352e7f3ff26ba7b8bba2621b000600555aca33006f8c58c6d512f71d7296e2f51ef0c36da5f50de"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/merge-descriptors@2.0.0","type":"library","bom-ref":"pkg:npm/merge-descriptors@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"merge2","version":"1.4.1","description":"Merge multiple streams into one stream in sequence or parallel.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f2aed51203095b827cb5c7d53f2f20d3d35c43065d6f0144aa17bf5999282338e7ff74c60f0b4e098b571b10373bcb4fce97330820e0bfe3f63f9cb4d1924e3a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/merge2@1.4.1","externalReferences":[{"type":"vcs","url":"https://github.com/teambition/merge2"}],"type":"library","bom-ref":"pkg:npm/merge2@1.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"yeikos","group":"","name":"merge","version":"2.1.1","description":"(recursive)? merging of (cloned)? objects.","scope":"optional","hashes":[{"alg":"SHA-512","content":"8f3f827eb83d1963996d00270d0e219559d0932fb7e35624e6bbbc6d949eeac2034c2220f27f62feeee149019254e1770bb947ea61adaa39224fd1b8c052cbd3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/merge@2.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/yeikos/js.merge"},{"type":"vcs","url":"https://github.com/yeikos/js.merge.git"}],"type":"library","bom-ref":"pkg:npm/merge@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"micromatch","version":"4.0.8","description":"Glob matching for javascript/node.js. A replacement and faster alternative to minimatch and multimatch.","scope":"optional","hashes":[{"alg":"SHA-512","content":"3d7c1f06162ed210423f0f039f413e58361beda7f77522d558a8b71c6bfce08745e13c85a02d32b3115dd06a31c3b9d2bf84ff3f3109431b18b0488508aa3604"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/micromatch@4.0.8","externalReferences":[{"type":"vcs","url":"https://github.com/micromatch/micromatch"}],"type":"library","bom-ref":"pkg:npm/micromatch@4.0.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"mime-db","version":"1.54.0","description":"Media Type Database","scope":"optional","hashes":[{"alg":"SHA-512","content":"694e4426e20dd960de982700a76bc505fa7f9b810085626750d492c348b1b3bfe45db77a3e0eb8126c0990d745841f1a5add6c1f60935eb2f1a3f880195de83d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mime-db@1.54.0","type":"library","bom-ref":"pkg:npm/mime-db@1.54.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"mime-types","version":"3.0.2","description":"The ultimate javascript content-type utility.","scope":"optional","hashes":[{"alg":"SHA-512","content":"2db833764d21e23ba843d7c22975b86f2d1426a8fe9ce3ab23d309d6c4a3e2723c688d9ea35aa6bd01227b8543d6096c4b6e749f5e4bb16b18b42ba0892b52e4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mime-types@3.0.2","type":"library","bom-ref":"pkg:npm/mime-types@3.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"mimic-fn","version":"2.1.0","description":"Make a function mimic another one","scope":"optional","hashes":[{"alg":"SHA-512","content":"3aa6ce939a0441e019f165d6c9d96ef47263cfd59574422f6a63027179aea946234e49c7fecaac5af850def830285451d47a63bcd04a437ee76c9818cc6a8672"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mimic-fn@2.1.0","type":"library","bom-ref":"pkg:npm/mimic-fn@2.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"mimic-fn","version":"3.1.0","description":"Make a function mimic another one","scope":"optional","hashes":[{"alg":"SHA-512","content":"62c6e2f6e616f611727eb4e1743110bb290de04cba06ec0f0f6929239112fe71530e7ffdf32c5834b64972050028f4ff99cacb2ca686cc947d615c49ac874049"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mimic-fn@3.1.0","type":"library","bom-ref":"pkg:npm/mimic-fn@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"mimic-function","version":"5.0.1","description":"Make a function mimic another one","scope":"optional","hashes":[{"alg":"SHA-512","content":"54fefd5d43f15760a28183f78d6c005054a4bb668aa811fbb9301aa4558206abadb1b983a3de8a639a3cba1e94fbf612227e4ec499d7a7bddb795929a8c20684"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mimic-function@5.0.1","type":"library","bom-ref":"pkg:npm/mimic-function@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"mimic-response","version":"1.0.1","description":"Mimic a Node.js HTTP response stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"8f911cb67907eda99f57fab91e09a86a5d60d901c5251ada3ad9b1d09a48aa4c6106123f9494a5d67329438e6155aaf03444cea161229a7759e102b4447c6ec5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mimic-response@1.0.1","type":"library","bom-ref":"pkg:npm/mimic-response@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"mimic-response","version":"3.1.0","description":"Mimic a Node.js HTTP response stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"cf4c9623ee050ebaf0792f199ade048f91dd266932d79f8bd9ee96827dfe88ae5f5b36fa4f77e1345ab6f8c79345bd3ae1ce96af837fc2fd03cd04e33731cd19"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mimic-response@3.1.0","type":"library","bom-ref":"pkg:npm/mimic-response@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"minimatch","version":"10.2.5","description":"a glob matcher in javascript","scope":"optional","hashes":[{"alg":"SHA-512","content":"3142e454b7ca1980c561e8cfd3b40ebab0cb2d0a5c8e4ec5c3eee35d2d91d9ccd1433479eb21d1bde5393432443af887fa111364a4a4224e5cf93db71a315432"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/minimatch@10.2.5","type":"library","bom-ref":"pkg:npm/minimatch@10.2.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"minimatch","version":"3.1.4","description":"a glob matcher in javascript","scope":"optional","hashes":[{"alg":"SHA-512","content":"b7098bf92f3eef222c13dc2caa0cd4dc4f3f2ee98ddccdd010bac1676d0e76643d8c1d89bd6e6866d04499fb7ce24fc6b39086f66aad59ce98f6f5be244cc6c7"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/minimatch@3.1.4","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/minimatch.git"}],"type":"library","bom-ref":"pkg:npm/minimatch@3.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"minimatch","version":"9.0.9","description":"a glob matcher in javascript","scope":"optional","hashes":[{"alg":"SHA-512","content":"381c0137d00be1daa61139694b6cdab31faf4de59c956ce46e57d993b2930398f78de38e373fed4429d9a26532bcd83cdf02f966ff52b3a1cc2570202fc47662"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/minimatch@9.0.9","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/minimatch.git"}],"type":"library","bom-ref":"pkg:npm/minimatch@9.0.9","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"minimist","version":"1.2.7","description":"parse argument options","scope":"optional","hashes":[{"alg":"SHA-512","content":"6f37cbd58519b0fe35826bbfaa3ac493443a8b68b1fdc55e0216c26ea1fdbb7cd8bad4b570b834d2a86b0f4336315742731e127345293f67815a8f6ba2da6aea"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/minimist@1.2.7","externalReferences":[{"type":"vcs","url":"https://github.com/minimistjs/minimist"},{"type":"vcs","url":"git://github.com/minimistjs/minimist.git"}],"type":"library","bom-ref":"pkg:npm/minimist@1.2.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"minimist","version":"1.2.8","description":"parse argument options","scope":"optional","hashes":[{"alg":"SHA-512","content":"db2c8047ca8190ddd8ba17896a7529582e54ddb6f9a2c0f2c0d07c4730d5943c031dba1c009bdeaaa8f5bbcf92543ee39164f8cafb070a95aaa96a80c5bd3308"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/minimist@1.2.8","externalReferences":[{"type":"vcs","url":"https://github.com/minimistjs/minimist"},{"type":"vcs","url":"git://github.com/minimistjs/minimist.git"}],"type":"library","bom-ref":"pkg:npm/minimist@1.2.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"minipass","version":"7.1.3","description":"minimal implementation of a PassThrough stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"b44047a839c8a0cff5ad7304d738246bd83a43695ca029311cbb9cece0c9e41c5b3f977873667970682d5c092ee7ddb4d02c7b762b874ce61f4810fc9fa9a6f0"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/minipass@7.1.3","type":"library","bom-ref":"pkg:npm/minipass@7.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Luca Ongaro","group":"","name":"minisearch","version":"7.2.0","description":"Tiny but powerful full-text search engine for browser and Node","scope":"optional","hashes":[{"alg":"SHA-512","content":"76a4f65c16143993a20b9b761d19f00038e1352d9c79ca7dbbe4c9462275429fdfe6a8e4793e403dc18f8c7c3e6f3f3d32cf09a7e706e00944f9067f4270e096"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/minisearch@7.2.0","externalReferences":[{"type":"vcs","url":"https://lucaong.github.io/minisearch/"},{"type":"vcs","url":"https://github.com/lucaong/minisearch.git"}],"type":"library","bom-ref":"pkg:npm/minisearch@7.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"minizlib","version":"3.1.0","description":"A small fast zlib stream built on [minipass](http://npm.im/minipass) and Node.js's zlib binding.","scope":"optional","hashes":[{"alg":"SHA-512","content":"299c58a350549160f64d514baf4310a0cf2f5148a0583dcb943f376bfef906a0bee2a1341dbd55a39bf516071f68d5ef7d7cebfb912143a8a783f09a0628d397"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/minizlib@3.1.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/minizlib.git"}],"type":"library","bom-ref":"pkg:npm/minizlib@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus (@mafintosh)","group":"","name":"mkdirp-classic","version":"0.5.3","description":"Mirror of mkdirp 0.5.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"80a2dc444321b6e651c1101fa8fdd1156f932b826a029541b4e21fb55823b8006902da7184f19a0dc7ef6e136f0f407c883d6852bfedc57df936371a63a36cfc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mkdirp-classic@0.5.3","externalReferences":[{"type":"vcs","url":"https://github.com/mafintosh/mkdirp-classic"},{"type":"vcs","url":"https://github.com/mafintosh/mkdirp-classic.git"}],"type":"library","bom-ref":"pkg:npm/mkdirp-classic@0.5.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"mkdirp","version":"1.0.4","description":"Recursively mkdir, like `mkdir -p`","scope":"optional","hashes":[{"alg":"SHA-512","content":"bd5a95650c9fdd62f1d9285dd2a27dc6ebea800c8a3cb022a884c4b6a5b4a08523ce8dcf78f0dde9f5bd885cf7d1e7fb62ca7fa225aa6e1b33786596d93e86cf"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mkdirp@1.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/node-mkdirp.git"}],"type":"library","bom-ref":"pkg:npm/mkdirp@1.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Guillaume Plique (http://github.com/Yomguithereal)","group":"","name":"mnemonist","version":"0.39.8","description":"Curated collection of data structures for the JavaScript/TypeScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bf25a8d8addf8eb530f1879e675cc5d1fcba32ee7d447a2451196577cca67543e33250fd102f68bf5fd83ea4e0a91bd437d9d3af71aa7f3dbd2d8026bb43c73d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/mnemonist@0.39.8","externalReferences":[{"type":"vcs","url":"https://github.com/yomguithereal/mnemonist#readme"},{"type":"vcs","url":"git+https://github.com/yomguithereal/mnemonist.git"}],"type":"library","bom-ref":"pkg:npm/mnemonist@0.39.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Tim Radvan ","group":"","name":"moo","version":"0.5.2","description":"Optimised tokenizer/lexer generator! 🐄 Much performance. Moo!","scope":"optional","hashes":[{"alg":"SHA-512","content":"8920092c760a9d7e3598a7092a3aaf9c037db1fd0b3034d70c4bc5bfe7dfb9147d6b5308b972e330d2fa12c9c31d290a2d358da90439ba8eb53f811b538354f9"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/moo@0.5.2","type":"library","bom-ref":"pkg:npm/moo@0.5.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"ms","version":"2.1.3","description":"Tiny millisecond conversion utility","scope":"optional","hashes":[{"alg":"SHA-512","content":"e85973b9b4cb646dc9d9afcd542025784863ceae68c601f268253dc985ef70bb2fa1568726afece715c8ebf5d73fab73ed1f7100eb479d23bfb57b45dd645394"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ms@2.1.3","type":"library","bom-ref":"pkg:npm/ms@2.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/fixture-js@0.0.0"},{"name":"internal:workspaceSrcFile","value":"packages/eval/src/opencodehub_eval/fixtures/js/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"mute-stream","version":"0.0.8","description":"Bytes go in, but they don't come out (when muted).","scope":"optional","hashes":[{"alg":"SHA-512","content":"9e76d658e9285b252c4e32ab8600f475ccf6da67644a7a58a9b123226da787086ec654a4a72c09981a3c87466a25d929ef799bf744acb0790de2bb1168101f00"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/mute-stream@0.0.8","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/mute-stream"}],"type":"library","bom-ref":"pkg:npm/mute-stream@0.0.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"mute-stream","version":"2.0.0","description":"Bytes go in, but they don't come out (when muted).","scope":"optional","hashes":[{"alg":"SHA-512","content":"596748c69ca3127f8585025042ff5a4006251e835577323353248d57580750f0d2759277c999fba4001b41c57b079e8cbeef3cd6af45655fb4571db10b8e1558"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/mute-stream@2.0.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/npm/mute-stream.git"}],"type":"library","bom-ref":"pkg:npm/mute-stream@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"nan","version":"2.26.2","description":"Native Abstractions for Node.js: C++ header for Node 0.8 -> 25 compatibility","scope":"optional","hashes":[{"alg":"SHA-512","content":"d2d4ef053624b77b5d1b0db69eb032e74c7b8296c6082147dc015cc92e56894bbb12ee2f5a5ae2d70a04eaa1c149fcb5d6f92c0ea922c233a7951ed657c1b51f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/nan@2.26.2","externalReferences":[{"type":"vcs","url":"git://github.com/nodejs/nan.git"}],"type":"library","bom-ref":"pkg:npm/nan@2.26.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jim Schlight","group":"","name":"napi-build-utils","version":"2.0.0","description":"A set of utilities to assist developers of tools that build N-API native add-ons","scope":"optional","hashes":[{"alg":"SHA-512","content":"1846eb6246df17b32835aa21da2186f383277ff5997c1d0674612c33cc33ec4c69c7f2e559fe54f2df67bc92974a9deaaf922c11b2b4e1c04686897f2b03ac58"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/napi-build-utils@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/inspiredware/napi-build-utils#readme"},{"type":"vcs","url":"git+https://github.com/inspiredware/napi-build-utils.git"}],"type":"library","bom-ref":"pkg:npm/napi-build-utils@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Hardmath123","group":"","name":"nearley","version":"2.20.1","description":"Simple, fast, powerful parser toolkit for JavaScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f8c73c51a01e6c5ce057e2a92399fb0dab2eb9008703cf5d9b09bb257c374d5e37ba47cd43d0e7047dcc75bda0fc8e05771736ea9c229815af8c8c3450020b49"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/nearley@2.20.1","externalReferences":[{"type":"vcs","url":"https://github.com/hardmath123/nearley.git"}],"type":"library","bom-ref":"pkg:npm/nearley@2.20.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"negotiator","version":"1.0.0","description":"HTTP content negotiation","scope":"optional","hashes":[{"alg":"SHA-512","content":"f0e7ecfc051087c31a11cae5ab9c4e5f4090f72a53179765efc9a394c35f38ad3c7f3a604c741140f07170f944b48c34c91a70b3e668ff7afee5645bcbbbb71a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/negotiator@1.0.0","type":"library","bom-ref":"pkg:npm/negotiator@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"neo-async","version":"2.6.2","description":"Neo-Async is a drop-in replacement for Async, it almost fully covers its functionality and runs faster ","scope":"optional","hashes":[{"alg":"SHA-512","content":"61ddd4112e665824aa47ea8d4fddd2dd4a18524a8067d94b83c6bb83dae29ac5a66062bc7154e8038fec17746bb21772577b0018c5d5526a4c60ec3e74ba4ebb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/neo-async@2.6.2","externalReferences":[{"type":"vcs","url":"https://github.com/suguru03/neo-async"}],"type":"library","bom-ref":"pkg:npm/neo-async@2.6.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Lukas Geiger","group":"","name":"node-abi","version":"3.89.0","description":"Get the Node ABI for a given target and runtime, and vice versa.","scope":"optional","hashes":[{"alg":"SHA-512","content":"eaef54c0bd07940976d7e6a030ddd800c5dc281c8cab01b1fa9abe3fbeaf8a2e5fee14cfb4a0e9d3cfc7f69cba0d8f9c7c3c3b9103531848ffae5cb72206cd40"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/node-abi@3.89.0","externalReferences":[{"type":"vcs","url":"https://github.com/electron/node-abi#readme"},{"type":"vcs","url":"git+https://github.com/electron/node-abi.git"}],"type":"library","bom-ref":"pkg:npm/node-abi@3.89.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"node-addon-api","version":"7.1.1","description":"Node.js API (Node-API)","scope":"optional","hashes":[{"alg":"SHA-512","content":"e66ddbb32ae3156135c5fee7cfb61774de2e76756d5caebf61f827e6a9da84be9b0a47f6c8ab789379ee4ca02d4f8af7206f09351da772c20c759b80cc98c741"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/node-addon-api@7.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/nodejs/node-addon-api"},{"type":"vcs","url":"git://github.com/nodejs/node-addon-api.git"}],"type":"library","bom-ref":"pkg:npm/node-addon-api@7.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"node-addon-api","version":"8.5.0","description":"Node.js API (Node-API)","scope":"optional","hashes":[{"alg":"SHA-512","content":"fdb459b72da65d420563fc54e472efbde34796cc0d25e8fe467c418ce32489d59fc19ce04db3c6d44dcae533b144b391fb9857edb4a87f2f327f58597af312f0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/node-addon-api@8.5.0","externalReferences":[{"type":"vcs","url":"https://github.com/nodejs/node-addon-api"},{"type":"vcs","url":"git://github.com/nodejs/node-addon-api.git"}],"type":"library","bom-ref":"pkg:npm/node-addon-api@8.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"node-addon-api","version":"8.7.0","description":"Node.js API (Node-API)","scope":"optional","hashes":[{"alg":"SHA-512","content":"f4c745c6690a68e6154d5f975511bc02b0f0c10efb5c88083f2280481d64dc93eadccf1f19041013762930eaca9ba83ffcab71f22bd9afcc68d509adaae6fe18"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/node-addon-api@8.7.0","externalReferences":[{"type":"vcs","url":"https://github.com/nodejs/node-addon-api"},{"type":"vcs","url":"git://github.com/nodejs/node-addon-api.git"}],"type":"library","bom-ref":"pkg:npm/node-addon-api@8.7.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus (@mafintosh)","group":"","name":"node-gyp-build","version":"4.8.4","description":"Build tool and bindings loader for node-gyp that supports prebuilds","scope":"optional","hashes":[{"alg":"SHA-512","content":"2c0e198f096751b947560ab4a01177265ffa87f36fb397f304bc1d105e27bb19c576c7da8dd7b859fc6d26bdc26a21fe17a7b070807fab88d0e14ccfca277e09"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/node-gyp-build@4.8.4","externalReferences":[{"type":"vcs","url":"https://github.com/prebuild/node-gyp-build"},{"type":"vcs","url":"https://github.com/prebuild/node-gyp-build.git"}],"type":"library","bom-ref":"pkg:npm/node-gyp-build@4.8.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"nopt","version":"7.2.1","description":"Option parsing for Node, supporting types, shorthands, etc. Used by npm.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b5a336e158a28a64ff5e7b716cfc89433086fa9e0428ea600f79b11705b7f261a3554adf11140e798e040c78dd9e9b6db5f1ee1d05c5c7e9533f4f10fa62daff"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/nopt@7.2.1","externalReferences":[{"type":"vcs","url":"git+https://github.com/npm/nopt.git"}],"type":"library","bom-ref":"pkg:npm/nopt@7.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"normalize-package-data","version":"5.0.0","description":"Normalizes data that can be found in package.json files.","scope":"optional","hashes":[{"alg":"SHA-512","content":"87d88f5487eb559f70558427c4582dc35ba04af18430e9723d65ad9bc04c2619f0c842ff14b6d86d36375773e98c8fc150aebb9fd3c4583bba787ceed5f075e5"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/normalize-package-data@5.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/npm/normalize-package-data.git"}],"type":"library","bom-ref":"pkg:npm/normalize-package-data@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"normalize-url","version":"6.1.0","description":"Normalize a URL","scope":"optional","hashes":[{"alg":"SHA-512","content":"0e52fe5f03b2dcdc4043cc6e0b4a243e02b8ea2b953402b4d5837b46e79806aa85786b018d5f5798203301d82dfbaebb6c297990f87d12a28a0f09da3c6d48ec"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/normalize-url@6.1.0","type":"library","bom-ref":"pkg:npm/normalize-url@6.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"npm-normalize-package-bin","version":"3.0.1","description":"Turn any flavor of allowable package.json bin into a normalized object","scope":"optional","hashes":[{"alg":"SHA-512","content":"74cc427fecd9fb7cde4195cac66cae08a9480cf1aebfc105f78d316e40b89105434edaa887aac914ef894ca480ebf4708b481eb569adbb2e08b6ea7400c71a0d"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/npm-normalize-package-bin@3.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/npm/npm-normalize-package-bin.git"}],"type":"library","bom-ref":"pkg:npm/npm-normalize-package-bin@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"npm-run-path","version":"6.0.0","description":"Get your PATH prepended with locally installed binaries","scope":"optional","hashes":[{"alg":"SHA-512","content":"f6a9f2ed9f43b1053c3aedfd111b0f5383994254933f8ed2850cee299e8f457a582ed205825fc31016045ca96f7046bef6d1d570af0217cd58d95ef6a9a66158"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/npm-run-path@6.0.0","type":"library","bom-ref":"pkg:npm/npm-run-path@6.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"object-assign","version":"4.1.1","description":"ES2015 `Object.assign()` ponyfill","scope":"optional","hashes":[{"alg":"SHA-512","content":"ac98134279149c7d6c170f324fa552537cc3dec5a6bbab19848b1e63c557f8646edcfe85ec5bbe24d0e85df9251256cb2529dcdc55101d57b8714e618fe05c52"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/object-assign@4.1.1","type":"library","bom-ref":"pkg:npm/object-assign@4.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Scott Puleo ","group":"","name":"object-hash","version":"3.0.0","description":"Generate hashes from javascript objects in node and the browser.","scope":"optional","hashes":[{"alg":"SHA-512","content":"4529fd17af0f8c7f47aad96db129ea602d575e859ef418eee7edb5dd1f7c70d1adb5a83dabdc80393cdd6ecaaf21aeda366e567df059169598af6696ae495603"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/object-hash@3.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/puleos/object-hash"},{"type":"vcs","url":"https://github.com/puleos/object-hash"}],"type":"library","bom-ref":"pkg:npm/object-hash@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"object-inspect","version":"1.13.4","description":"string representations of objects in node and the browser","scope":"optional","hashes":[{"alg":"SHA-512","content":"5baee22e5e09d845c41936df78709f7eb8c37e2b6f2c0360d14957df01545124f1f762974457a0307515812a84fb0be101b8b85aa8c683d733cac4d5d84a5b7b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/object-inspect@1.13.4","externalReferences":[{"type":"vcs","url":"https://github.com/inspect-js/object-inspect"},{"type":"vcs","url":"git://github.com/inspect-js/object-inspect.git"}],"type":"library","bom-ref":"pkg:npm/object-inspect@1.13.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband (http://ljharb.codes)","group":"","name":"object-keys","version":"1.1.1","description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","scope":"optional","hashes":[{"alg":"SHA-512","content":"36e00449439432b9485ce7c72b30fa6e93eeded62ddf1be335d44843e15e4f494d6f82bc591ef409a0f186e360b92d971be1a39323303b3b0de5992d2267e12c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/object-keys@1.1.1","externalReferences":[{"type":"vcs","url":"git://github.com/ljharb/object-keys.git"}],"type":"library","bom-ref":"pkg:npm/object-keys@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Guillaume Plique (http://github.com/Yomguithereal)","group":"","name":"obliterator","version":"2.0.5","description":"Higher order iterator library for JavaScript/TypeScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"e3608f13d0216a1651b0c36c95ccead1cb4012daa4f0491adba4287e7a82df8e8165d1c3c929372d691adb72c8ed42c8c35d4d9a5b6988b6a022af20068cf14f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/obliterator@2.0.5","externalReferences":[{"type":"vcs","url":"https://github.com/yomguithereal/obliterator#readme"},{"type":"vcs","url":"git+https://github.com/yomguithereal/obliterator.git"}],"type":"library","bom-ref":"pkg:npm/obliterator@2.0.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"on-exit-leak-free","version":"2.1.2","description":"Execute a function on exit without leaking memory, allowing all objects to be garbage collected","scope":"optional","hashes":[{"alg":"SHA-512","content":"d1e24963a8572c67f5b9d1f07cd7ed06a1fe83bdc4538079d389d978aa73d6c61129a7c0821c31109ba70763bbac36642f83c67ec3159d35d9d848e26dba9cb0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/on-exit-leak-free@2.1.2","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/on-exit-or-gc#readme"},{"type":"vcs","url":"git+https://github.com/mcollina/on-exit-or-gc.git"}],"type":"library","bom-ref":"pkg:npm/on-exit-leak-free@2.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"on-finished","version":"2.4.1","description":"Execute a callback when a request closes, finishes, or errors","scope":"optional","hashes":[{"alg":"SHA-512","content":"a15973920dc4340842936cddbfb209c1dfd0503e33d91c51c2991c198f29b0255c09864dab8c189d55802c733e6ebb6e26378f5a2605fc2966b83afc0a1e7e92"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/on-finished@2.4.1","type":"library","bom-ref":"pkg:npm/on-finished@2.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"once","version":"1.4.0","description":"Run a function exactly one time","scope":"optional","hashes":[{"alg":"SHA-512","content":"94d689808fb643951140191c7042874d038f697754c67659125413658d0c15402e684a9ed44f8dcaf81dcff688c8d8ba67d3333b976fd47f27e7cfc610ba77fb"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/once@1.4.0","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/once"}],"type":"library","bom-ref":"pkg:npm/once@1.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"onetime","version":"5.1.2","description":"Ensure a function is only called once","scope":"optional","hashes":[{"alg":"SHA-512","content":"91ba5a4921894d674063928f55e30e2974ab3edafc0bc0bbc287496dcb1de758d19e60fe199bbc63456853a0e6e59e2f5abd0883fd4d2ae59129fee3e5a6984a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/onetime@5.1.2","type":"library","bom-ref":"pkg:npm/onetime@5.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"onetime","version":"7.0.0","description":"Ensure a function is only called once","scope":"optional","hashes":[{"alg":"SHA-512","content":"55726373cec549c17cf2e69f4b726594382f026f9cfd295fcf4ea5a2b8f6b80637e2b954bb436dfaff30ade88899cae00238c81e9d6b5e94c394b386c36f56c1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/onetime@7.0.0","type":"library","bom-ref":"pkg:npm/onetime@7.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"fs-eire","group":"","name":"onnxruntime-common","version":"1.24.3","description":"ONNXRuntime JavaScript API library","scope":"optional","hashes":[{"alg":"SHA-512","content":"19eb8f64ee94fcb0495efc1d6aa1dbb949a85e211d7828d68bf106ed8d473670f0258ba4e9651b357a45ea5b92518f32012ba5ddc99494b1ab0822f5660557a8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/onnxruntime-common@1.24.3","externalReferences":[{"type":"vcs","url":"https://github.com/Microsoft/onnxruntime.git"}],"type":"library","bom-ref":"pkg:npm/onnxruntime-common@1.24.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"fs-eire","group":"","name":"onnxruntime-node","version":"1.24.3","description":"ONNXRuntime Node.js binding","scope":"required","hashes":[{"alg":"SHA-512","content":"247efe7336dcf002c0f35f6f95381c57e436d78ffe56319e0470e35fcd7e6420f43c254220518536d4f4578b171bfd495f2a4a3e049c41c0778a3fe19376274e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/onnxruntime-node@1.24.3","externalReferences":[{"type":"vcs","url":"https://github.com/Microsoft/onnxruntime.git"}],"type":"library","bom-ref":"pkg:npm/onnxruntime-node@1.24.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"undefined"},{"name":"cdx:pnpm:os","value":"win32, darwin, linux"},{"name":"cdx:npm:package_json","value":"packages/embedder/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"},{"name":"ImportedModules","value":"onnxruntime-node,InferenceSession,onnxruntime-node/InferenceSession,Tensor,onnxruntime-node/Tensor"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/embedder/dist/onnx-embedder.js#18"},{"location":"packages/embedder/src/onnx-embedder.ts#20"}]}},{"author":"Joseph Spencer","group":"","name":"openapi-types","version":"12.1.3","description":"Types for OpenAPI documents.","scope":"optional","hashes":[{"alg":"SHA-512","content":"37862d49826a82156ee227a4d9952f70dff46aa1f59110ee36acdcc9c0f184e52983b19dbcb6b61770e04bac8136122786fdabffa234165927ec2a8bf3e9c873"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/openapi-types@12.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/kogosoftwarellc/open-api/tree/master/packages/openapi-types#readme"}],"type":"library","bom-ref":"pkg:npm/openapi-types@12.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ora","version":"5.4.1","description":"Elegant terminal spinner","scope":"optional","hashes":[{"alg":"SHA-512","content":"e5be98f39b4fc5967b432b4ef81433cac5b7d47264bb6edc4489646c05da371f8175c562f8b951166557cde17a6bb242c09a72c397386fe61254899022b069b9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ora@5.4.1","type":"library","bom-ref":"pkg:npm/ora@5.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"ora","version":"8.2.0","description":"Elegant terminal spinner","scope":"optional","hashes":[{"alg":"SHA-512","content":"c1e3fe059f0c54d9e50a6f1cd1075cd56496ab8427ec8fbd0891a6ed06a58ba838e1efcf5336e336a257e4d27d96394d328b1f26f8357ca10620b9252bd7309f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ora@8.2.0","type":"library","bom-ref":"pkg:npm/ora@8.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"p-cancelable","version":"2.1.1","description":"Create a promise that can be canceled","scope":"optional","hashes":[{"alg":"SHA-512","content":"0593abde74501ce9ed5234eb1fcf8b879e2c98a1e81f2babf167b557c0d2315ae5e40da66a538ec2e2519ca4438d29e4a1e061e1ab7a0701276f923b265df5c2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/p-cancelable@2.1.1","type":"library","bom-ref":"pkg:npm/p-cancelable@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"p-defer","version":"1.0.0","description":"Create a deferred promise","scope":"optional","hashes":[{"alg":"SHA-512","content":"c01df07c0c59a64d80cce7d430934bf9ddfac68b61452ca3f045ce6b87fa18ca980cdf4125a6922126ec93ec12f5a6e69de6d5c3de42d8a6f9b7ce549a90b1bb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/p-defer@1.0.0","type":"library","bom-ref":"pkg:npm/p-defer@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"p-limit","version":"2.3.0","description":"Run multiple promise-returning & async functions with limited concurrency","scope":"optional","hashes":[{"alg":"SHA-512","content":"ffff3c985592271f25c42cf07400014c92f6332581d76f9e218ecc0cbd92a8b98091e294f6ac51bd6b92c938e6dc5526a4110cb857dc90022a11a546503c5beb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/p-limit@2.3.0","type":"library","bom-ref":"pkg:npm/p-limit@2.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"p-map","version":"4.0.0","description":"Map over promises concurrently","scope":"optional","hashes":[{"alg":"SHA-512","content":"fdb8ceaa68044c1601e41a0478655e6bc766bc76f69bd18bcb513d5b8df27b27cfe9040264614d6be5d171e244b8307aceaafe80aa4802694b79b329ca4c3f31"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/p-map@4.0.0","type":"library","bom-ref":"pkg:npm/p-map@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"p-try","version":"2.2.0","description":"`Start a promise chain","scope":"optional","hashes":[{"alg":"SHA-512","content":"4789cf0154c053407d0f7e7f1a4dee25fffb5d86d0732a2148a76f03121148d821165e1eef5855a069c1350cfd716697c4ed88d742930bede331dbefa0ac3a75"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/p-try@2.2.0","type":"library","bom-ref":"pkg:npm/p-try@2.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (https://izs.me)","group":"","name":"package-json-from-dist","version":"1.0.1","description":"Load the local package.json from either src or dist folder","scope":"optional","hashes":[{"alg":"SHA-512","content":"5046484b7fdbcb8382f2f2f73f67535d1113a5e6cb236362239bc8ae3683ff952dae4157fed35bc234d2440182ffeec2028da921c05a4605a670104772c68223"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/package-json-from-dist@1.0.1","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/package-json-from-dist.git"}],"type":"library","bom-ref":"pkg:npm/package-json-from-dist@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"the purl authors","group":"","name":"packageurl-js","version":"2.0.1","description":"JavaScript library to parse and build \"purl\" aka. package URLs. This is a microlibrary implementing the purl spec at https://github.com/package-url","scope":"optional","hashes":[{"alg":"SHA-512","content":"3798b15e3cd3cb8403407d10f58163a8859deb31faf77e838e99769bd40d1660efe45ba6f2af018e4a4070734ccce144d08c10ac584959ec7700dea44b4392c2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/packageurl-js@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/package-url/packageurl-js#readme"},{"type":"vcs","url":"git+https://github.com/package-url/packageurl-js.git"}],"type":"library","bom-ref":"pkg:npm/packageurl-js@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"parent-module","version":"1.0.1","description":"Get the path of the parent module","scope":"optional","hashes":[{"alg":"SHA-512","content":"190d84591a5057cfe8f80c3c62ab5f6593df3515996246e2744f64e6ba65fe10b7bed1c705f1a6d887e2eaa595f9ca031a4ad42990311372e8b7991cb11961fa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/parent-module@1.0.1","type":"library","bom-ref":"pkg:npm/parent-module@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"parse-json","version":"5.2.0","description":"Parse JSON with more helpful errors","scope":"optional","hashes":[{"alg":"SHA-512","content":"6b208abe6fe98421b13a461148233cda20f072df3f1289d2120092c56c43eef7ba8c7820b059787d955004f44d810a0a8ae57fa1d845ac6cd05d9c1b89f0bc46"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/parse-json@5.2.0","type":"library","bom-ref":"pkg:npm/parse-json@5.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"parse-ms","version":"4.0.0","description":"Parse milliseconds into an object","scope":"optional","hashes":[{"alg":"SHA-512","content":"4d77ebca2adb9aadf8cbc401c20a8254b8bef28037a16c767809d29fad884f2121118696465559d83bcc33d7996ccb3f45fc4fbbf3cafda04bc868f822ba8c1f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/parse-ms@4.0.0","type":"library","bom-ref":"pkg:npm/parse-ms@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Brian Woodward (https://github.com/doowb)","group":"","name":"parse-passwd","version":"1.0.0","description":"Parse a passwd file into a list of users.","scope":"optional","hashes":[{"alg":"SHA-512","content":"d58d40fff4145c464aed82b3fab0fd5b275c135f84b8fafa64180a79c001f2d9a85ba505bf435111525ed69fa3471b5386471b6ca91fc086d625efc8784ea6d9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/parse-passwd@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/doowb/parse-passwd"}],"type":"library","bom-ref":"pkg:npm/parse-passwd@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"parseurl","version":"1.3.3","description":"parse a url with memoization","scope":"optional","hashes":[{"alg":"SHA-512","content":"0a2c9e3b1153fc96723799b4cfd3df5f0e1208127a4b2833d43a65d30aa39610c418604fd469ec51510bd29eb78681b57dc8f77c7ca75e2f4d60ee2758e2fea9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/parseurl@1.3.3","type":"library","bom-ref":"pkg:npm/parseurl@1.3.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Amit Gupta (https://solothought.com)","group":"","name":"path-expression-matcher","version":"1.5.0","description":"Efficient path tracking and pattern matching for XML/JSON parsers","scope":"optional","hashes":[{"alg":"SHA-512","content":"71badead957eeabbdd42bac3fa218c7191448a24ab6eff537dd92f9eeb32eb2d31d062815d110583f63ae468487e6d2d9cb9ed4e18730a77cac29d56463782c9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/path-expression-matcher@1.5.0","externalReferences":[{"type":"vcs","url":"https://github.com/NaturalIntelligence/path-expression-matcher#readme"},{"type":"vcs","url":"https://github.com/NaturalIntelligence/path-expression-matcher"}],"type":"library","bom-ref":"pkg:npm/path-expression-matcher@1.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"path-is-absolute","version":"1.0.1","description":"Node.js 0.12 path.isAbsolute() ponyfill","scope":"optional","hashes":[{"alg":"SHA-512","content":"0156f0dd42767bd6eaeb8bd2692f409b47e37b53daf296c6a934ec9977da2223299ebe4394385f24eb8b8fd49ff7964f5430147ab0df124f3c30f98f7bb50242"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/path-is-absolute@1.0.1","type":"library","bom-ref":"pkg:npm/path-is-absolute@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"path-key","version":"3.1.1","description":"Get the PATH environment variable key cross-platform","scope":"optional","hashes":[{"alg":"SHA-512","content":"a2399e374a9dfb2d23b3312da18e3caf43deab97703049089423aee90e5fe3595f92cc17b8ab58ae18284e92e7c887079b6e1486ac7ee53aa6d889d2c0b844e9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/path-key@3.1.1","type":"library","bom-ref":"pkg:npm/path-key@3.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"path-key","version":"4.0.0","description":"Get the PATH environment variable key cross-platform","scope":"optional","hashes":[{"alg":"SHA-512","content":"85a444ca9abbc6433b12b7e0232034cfe063e0018a94c49d9501368ef268ea1b960f511d90a615f86fd3e27ab4604176be04d3f24a8c14aa35b879fde74af849"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/path-key@4.0.0","type":"library","bom-ref":"pkg:npm/path-key@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (https://blog.izs.me)","group":"","name":"path-scurry","version":"1.11.1","description":"walk paths fast and efficiently","scope":"optional","hashes":[{"alg":"SHA-512","content":"5dae0dc35ec54bd02940527dba62e2252e28ac68e6ed9cf052bc1a99c190b874b30f2b61f5ba0a0dac9c61d0dc643baa6004d7c381c55e06aa59372d5bfbf51c"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/path-scurry@1.11.1","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/path-scurry"}],"type":"library","bom-ref":"pkg:npm/path-scurry@1.11.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (https://blog.izs.me)","group":"","name":"path-scurry","version":"2.0.2","description":"walk paths fast and efficiently","scope":"optional","hashes":[{"alg":"SHA-512","content":"dcefe2555b0900fb0e9e9c1621e0fe77acffecf9aa029c9078f52d0a77636ad8fff48e4bca51efb79aa5b856814f7239877af57a37d1d39e9cf850aff8d9cd5e"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/path-scurry@2.0.2","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/path-scurry"}],"type":"library","bom-ref":"pkg:npm/path-scurry@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"path-to-regexp","version":"8.4.2","description":"Express style path to RegExp utility","scope":"optional","hashes":[{"alg":"SHA-512","content":"a9172e21d3faf4d3e6e2a6c008af9a0e8808e42043322d6329ed2bcb9ad2409cfc2552ec0bb8d5f17a622631912cba25dcdf91e6284661cacf2d39e8ea901d04"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/path-to-regexp@8.4.2","externalReferences":[{"type":"vcs","url":"https://github.com/pillarjs/path-to-regexp.git"}],"type":"library","bom-ref":"pkg:npm/path-to-regexp@8.4.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Alexey Raspopov","group":"","name":"picocolors","version":"1.1.1","description":"The tiniest and the fastest library for terminal output formatting with ANSI colors","scope":"optional","hashes":[{"alg":"SHA-512","content":"c5c787dac9e1b5be4cf658aa0ec984c39ea57b7efa993664117fe311bfd1c4d1727a036e97b78db250973fd1438ff2dcbb45fc284c8c71e3f69eda5a1eb0c454"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/picocolors@1.1.1","type":"library","bom-ref":"pkg:npm/picocolors@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"picomatch","version":"2.3.2","description":"Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.","scope":"optional","hashes":[{"alg":"SHA-512","content":"57bfaf404274e99f9ce6d4b27bc4be9c751f239d71a172178c743df5c42d294910aaa3b590efca08951f657b81e3b87f60194385a8761f785d5065e7f227b4a0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/picomatch@2.3.2","externalReferences":[{"type":"vcs","url":"https://github.com/micromatch/picomatch"}],"type":"library","bom-ref":"pkg:npm/picomatch@2.3.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"pino-abstract-transport","version":"2.0.0","description":"Write Pino transports easily","scope":"optional","hashes":[{"alg":"SHA-512","content":"17adf1e6d8b357a58287847a447ca2d8c97e33bd033515edffe1c0368c0c7e5a60185300ca6fd52a6e86ed9390463a8ded76c6c4ad4b83db7a66bb7339a8af33"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pino-abstract-transport@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/pino-abstract-transport#readme"},{"type":"vcs","url":"git+https://github.com/pinojs/pino-abstract-transport.git"}],"type":"library","bom-ref":"pkg:npm/pino-abstract-transport@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"pino-abstract-transport","version":"3.0.0","description":"Write Pino transports easily","scope":"optional","hashes":[{"alg":"SHA-512","content":"c257d473353e9fb1f2fc76b98fd6bf819372ed67b9f9c5e9f182fe5fe3c6f12d0a5f1c3b9ff257037738e98d334339c827cdd44498b0cbb2e1e7a58ba2436ec6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pino-abstract-transport@3.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/pino-abstract-transport#readme"},{"type":"vcs","url":"git+https://github.com/pinojs/pino-abstract-transport.git"}],"type":"library","bom-ref":"pkg:npm/pino-abstract-transport@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Sumners ","group":"","name":"pino-pretty","version":"13.1.3","description":"Prettifier for Pino log lines","scope":"optional","hashes":[{"alg":"SHA-512","content":"b6d5d19243b3e96582f7929e63dfb1c562fa02d226c1bc8c1eb2f5992c2ac16f6efaf2e9fd620496f1ef0920e0d313bf0f3ae0833d73bf7acde68bd3455a302a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pino-pretty@13.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/pino-pretty#readme"},{"type":"vcs","url":"git+ssh://git@github.com/pinojs/pino-pretty.git"}],"type":"library","bom-ref":"pkg:npm/pino-pretty@13.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Sumners ","group":"","name":"pino-std-serializers","version":"7.1.0","description":"A collection of standard object serializers for Pino","scope":"optional","hashes":[{"alg":"SHA-512","content":"06774f1faeff271184c518225f5757d30d45bd9724e566b869a97dd7df12ad18598c7dc6c4a4142880676094dd8f61c3377510012d3a1e57dc49b423d8e1e66b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pino-std-serializers@7.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/pino-std-serializers#readme"},{"type":"vcs","url":"git+ssh://git@github.com/pinojs/pino-std-serializers.git"}],"type":"library","bom-ref":"pkg:npm/pino-std-serializers@7.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"pino","version":"9.14.0","description":"super fast, all natural json logger","scope":"optional","hashes":[{"alg":"SHA-512","content":"f0e1302a9e63b84bdbfcc8e921ce218ea7e008dcacad2f7844838c5d8be9602766fe3825aca1220189a2ba66e61a10afb3e21c227b298580c5c2aae3afbdfdf3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pino@9.14.0","externalReferences":[{"type":"website","url":"https://getpino.io"},{"type":"vcs","url":"git+https://github.com/pinojs/pino.git"}],"type":"library","bom-ref":"pkg:npm/pino@9.14.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James M Snell ","group":"","name":"piscina","version":"5.1.4","description":"A fast, efficient Node.js Worker Thread Pool implementation","scope":"required","hashes":[{"alg":"SHA-512","content":"eee53866729e42adb6b7d02c987183db0e0e6101a89f01674f2a43ca96968bb42bd84bd020556d1bc2790ffddb13b5b5db759d73dfafe02643bb98495d50ad06"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/piscina@5.1.4","externalReferences":[{"type":"vcs","url":"https://github.com/piscinajs/piscina#readme"},{"type":"vcs","url":"git+https://github.com/piscinajs/piscina.git"}],"type":"library","bom-ref":"pkg:npm/piscina@5.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"piscina,Piscina,piscina/Piscina"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/parse/worker-pool.js#16"},{"location":"packages/ingestion/src/parse/worker-pool.ts#17"}]}},{"author":"crouchcd","group":"","name":"pkce-challenge","version":"5.0.1","description":"Generate or verify a Proof Key for Code Exchange (PKCE) challenge pair","scope":"optional","hashes":[{"alg":"SHA-512","content":"c10d1bfd6e05af4d6ab691e5a92aaca5c8f712106f8a6b1d8742a51e11fc1d166732c11ad1e6b67d350b397392f5c710af7a26f86706464e1efa2b2b65657ca9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pkce-challenge@5.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/crouchcd/pkce-challenge#readme"},{"type":"vcs","url":"git+https://github.com/crouchcd/pkce-challenge.git"}],"type":"library","bom-ref":"pkg:npm/pkce-challenge@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus (@mafintosh)","group":"","name":"prebuild-install","version":"7.1.3","description":"A command line tool to easily install prebuilt binaries for multiple version of node/iojs on a specific platform","scope":"optional","hashes":[{"alg":"SHA-512","content":"f0c7f671b57bc757173d420b003188df0ba17ea5afb622c0d627254c36c54599204504b436ab0f6698676bd57e1f24c469d85eb8f9a368932c6f329014eccbba"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/prebuild-install@7.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/prebuild/prebuild-install"},{"type":"vcs","url":"https://github.com/prebuild/prebuild-install.git"}],"type":"library","bom-ref":"pkg:npm/prebuild-install@7.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:deprecation_notice","value":"No longer maintained. Please contact the author of the relevant native addon; alternatives are available."},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"pretty-ms","version":"9.3.0","description":"Convert milliseconds to a human readable string: `1337000000` → `15d 11h 23m 20s`","scope":"optional","hashes":[{"alg":"SHA-512","content":"823552e6138ff8cdf0326e6798d3ae71b22baae773b3dbffe7b6d64474162d89255eaa172ab55f616d96f7e8257c6b2ab4f8298b3e56c3210407e92c5c8c7781"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pretty-ms@9.3.0","type":"library","bom-ref":"pkg:npm/pretty-ms@9.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Tomas Della Vedova","group":"","name":"process-warning","version":"5.0.0","description":"A small utility for creating warnings and emitting them.","scope":"optional","hashes":[{"alg":"SHA-512","content":"6b7f6df40a47371d8be3e1c19d02aac711cdb35afb285f889ed77c43f8356d487aab4588a7dbe83d727fc748fe64be39285d6925df7eab68cb21131fbc4b2190"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/process-warning@5.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/fastify/fastify-warning#readme"},{"type":"vcs","url":"git+https://github.com/fastify/process-warning.git"}],"type":"library","bom-ref":"pkg:npm/process-warning@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"proxy-addr","version":"2.0.7","description":"Determine address of proxied request","scope":"optional","hashes":[{"alg":"SHA-512","content":"96542c30b4940d43d3e388ddad4fcedfbaa59e27e2b433fe670ae699972848ac8b2afb59c69c95d27dbf6c3fcde2d040019fe024475953b28cadaa0ad7e5d802"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/proxy-addr@2.0.7","type":"library","bom-ref":"pkg:npm/proxy-addr@2.0.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus Madsen ","group":"","name":"pump","version":"3.0.4","description":"pipe streams together and close all of them if one of them closes","scope":"optional","hashes":[{"alg":"SHA-512","content":"552eec8dce8a47b7b5ba4445850498e4b336b8158050f88e3dafc0de690a9a23304a64455084edd31ba3fbf95eb209c2bfe74f204625933adcc9782ab881cc70"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/pump@3.0.4","type":"library","bom-ref":"pkg:npm/pump@3.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Bynens (https://mathiasbynens.be/)","group":"","name":"punycode","version":"2.3.1","description":"A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bd8b7b503d54f5683ad77f2c84bb4b3af740bbef03b02fe2945b44547707fb0c9d712a4d136d007d239db9fe8c91115a84be4563b5f5a14ee7295645b5fabc16"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/punycode@2.3.1","externalReferences":[{"type":"website","url":"https://mths.be/punycode"},{"type":"vcs","url":"https://github.com/mathiasbynens/punycode.js.git"}],"type":"library","bom-ref":"pkg:npm/punycode@2.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"pupt","version":"1.4.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"d7224059815088ed69c24385a0f0b0d7b13efa44712e9004ce1bf2d8548052c842d313ef5e1e5c624ea2a75768ed7f3a70a4c301b73d3fe6f6761e2e8c1cff65"}],"purl":"pkg:npm/pupt@1.4.1","type":"library","bom-ref":"pkg:npm/pupt@1.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"qs","version":"6.15.1","description":"A querystring parser that supports nesting and arrays, with a depth limit","scope":"optional","hashes":[{"alg":"SHA-512","content":"e981c41512fd99f81c02faa5fd78704ef7f98ca70e8a2ba9b761622711e4897d73e23ed62fc27f8d11d82e5b8e46f7355f1079ad5db42a878ad3481525a9aca6"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/qs@6.15.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/qs"},{"type":"vcs","url":"https://github.com/ljharb/qs.git"}],"type":"library","bom-ref":"pkg:npm/qs@6.15.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"queue-microtask","version":"1.2.3","description":"fast, tiny `queueMicrotask` shim for modern engines","scope":"optional","hashes":[{"alg":"SHA-512","content":"36e68d49ae9f94a4f925a498433268934e09cd32f5080e9a1a1bf9adf2d6dcf82a03e3360a1a59427002f21f22e19164052f17e51aa40c11c0eebe217a3dcaf4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/queue-microtask@1.2.3","externalReferences":[{"type":"vcs","url":"https://github.com/feross/queue-microtask"},{"type":"vcs","url":"git://github.com/feross/queue-microtask.git"}],"type":"library","bom-ref":"pkg:npm/queue-microtask@1.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"David Mark Clements","group":"","name":"quick-format-unescaped","version":"4.0.4","description":"Solves a problem with util.format","scope":"optional","hashes":[{"alg":"SHA-512","content":"b580b5435860c91b87825a15fd85ecdb0d79ba73d587ca9fbbfa824df85361a99ac3b7f286e98a6b6c86a5d4a8f3bbd8df6ac87258fee1f59841750cb3d107be"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/quick-format-unescaped@4.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/davidmarkclements/quick-format#readme"},{"type":"vcs","url":"git+https://github.com/davidmarkclements/quick-format.git"}],"type":"library","bom-ref":"pkg:npm/quick-format-unescaped@4.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"quick-lru","version":"5.1.1","description":"Simple “Least Recently Used” (LRU) cache","scope":"optional","hashes":[{"alg":"SHA-512","content":"5aec802d18d63c31adb7fc3326269d3b901763ef2167cd215697ba3328af82b691116ef9d57dd26e146f1b778b28e60dfbc544bea2dc7f7c1d9ede386784b848"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/quick-lru@5.1.1","type":"library","bom-ref":"pkg:npm/quick-lru@5.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Tab Atkins Jr.","group":"","name":"railroad-diagrams","version":"1.0.0","description":"A small JS+SVG library for drawing railroad syntax diagrams.","scope":"optional","hashes":[{"alg":"SHA-512","content":"733f770e335e2d8d2276b08d387e8fbe264644df46261b1d9bd8699f56024bcefd7e3e16fb1e4814986191165cc1580c98517b47cd9403fece87e47c94b660e8"}],"licenses":[{"license":{"id":"CC0-1.0","url":"https://opensource.org/licenses/CC0-1.0"}}],"purl":"pkg:npm/railroad-diagrams@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/tabatkins/railroad-diagrams"},{"type":"vcs","url":"https://github.com/tabatkins/railroad-diagrams.git"}],"type":"library","bom-ref":"pkg:npm/railroad-diagrams@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Roly Fentanes (https://github.com/fent)","group":"","name":"randexp","version":"0.4.6","description":"Create random strings that match a given regular expression.","scope":"optional","hashes":[{"alg":"SHA-512","content":"f3458d99df43034b6666bc3da906bad863cf59fb972649eb99598b731beae2e641758a9bd706282939a794651c86f5567b45e22eea58901a1754ec73dc2f0361"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/randexp@0.4.6","externalReferences":[{"type":"vcs","url":"http://fent.github.io/randexp.js/"},{"type":"vcs","url":"git://github.com/fent/randexp.js.git"}],"type":"library","bom-ref":"pkg:npm/randexp@0.4.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk (http://tjholowaychuk.com)","group":"","name":"range-parser","version":"1.2.1","description":"Range header field string parser","scope":"optional","hashes":[{"alg":"SHA-512","content":"1eb82cc7ea2baa8ca09e68456ca68713a736f7a27e1d30105e8c4417a80dba944e9a6189468cb37c6ddc700bdea8206bc2bff6cb143905577f1939796a03b04a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/range-parser@1.2.1","type":"library","bom-ref":"pkg:npm/range-parser@1.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jonathan Ong (http://jongleberry.com)","group":"","name":"raw-body","version":"3.0.2","description":"Get and validate the raw body of a readable stream.","scope":"optional","hashes":[{"alg":"SHA-512","content":"2b9cd08c3965c5691fed9e7125d574fc1d164cdab1eafc46ef4cc9138374901b382e8be6118589cd01b10bd6d2f5100abb76e0338d25adde73a0b275d8c43904"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/raw-body@3.0.2","type":"library","bom-ref":"pkg:npm/raw-body@3.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dominic Tarr (dominictarr.com)","group":"","name":"rc","version":"1.2.8","description":"hardwired configuration loader","scope":"optional","hashes":[{"alg":"SHA-512","content":"cb76c682a2a3dd005dc4b6cb9289a5a2192fb00f207408944254812670617e7f813f18386dceb677c4dc056d79c1abc37e07b10a071c72485c66fcb0c9060f3b"}],"licenses":[{"expression":"(BSD-2-Clause OR MIT OR Apache-2.0)"}],"purl":"pkg:npm/rc@1.2.8","externalReferences":[{"type":"vcs","url":"https://github.com/dominictarr/rc.git"}],"type":"library","bom-ref":"pkg:npm/rc@1.2.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dmitry Semigradsky ","group":"","name":"read-installed-packages","version":"2.0.1","description":"Read all the installed packages in a folder, and return a tree structure with all the data.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b7e7c93853986992230694d5c6257c324b7bc90cb2e04e8c4abae7b791663dde1e9d8be953ff436068ab0e63755d0c247deb9622133f8aed2ddab9ba892af408"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/read-installed-packages@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/Semigradsky/read-installed.git"}],"type":"library","bom-ref":"pkg:npm/read-installed-packages@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"read-package-json","version":"6.0.4","description":"The thing npm uses to read package.json files with semantics and defaults and validation","scope":"optional","hashes":[{"alg":"SHA-512","content":"004b565d87e8a418f6cf93793db90039e34744f520e6af8d7a7ed02f157c336cc9ab5ca6ebf942cf77d83530977b5f69baed9dd3a8df1e1acfeeffd2d8fb4c33"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/read-package-json@6.0.4","externalReferences":[{"type":"vcs","url":"https://github.com/npm/read-package-json.git"}],"type":"library","bom-ref":"pkg:npm/read-package-json@6.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:deprecation_notice","value":"This package is no longer supported. Please use @npmcli/package-json instead."}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"readable-stream","version":"3.6.2","description":"Streams3, a user-land copy of the stream library from Node.js","scope":"optional","hashes":[{"alg":"SHA-512","content":"f6efec9e20ab6370f959db04447cc71381b66025eaa06e454c7522082e1221bafa5dc2d9058d39c9af442a361e93d3b9c4e0308c6abed497460404bb43d49ca0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/readable-stream@3.6.2","externalReferences":[{"type":"vcs","url":"git://github.com/nodejs/readable-stream"}],"type":"library","bom-ref":"pkg:npm/readable-stream@3.6.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Paolo Insogna ","group":"","name":"real-require","version":"0.2.0","description":"Keep require and import consistent after bundling or transpiling","scope":"optional","hashes":[{"alg":"SHA-512","content":"e7b7ebac633f3824cba8b3808749a1540f5504c1ddfbd53b65bd931cc19d054a1954eb466c9ce3c6c6060c9dc0f4061808fe219facb54d56da39fcd6b66e4616"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/real-require@0.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/real-require"},{"type":"vcs","url":"git+https://github.com/pinojs/real-require.git"}],"type":"library","bom-ref":"pkg:npm/real-require@0.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Troy Goode (http://github.com/troygoode/)","group":"","name":"require-directory","version":"2.1.1","description":"Recursively iterates over specified directory, require()'ing each file, and returning a nested hash structure containing those modules.","scope":"optional","hashes":[{"alg":"SHA-512","content":"7c6c4423bfb0b06f71aef763b2b9662f6d8e3134e21d1c0032ba2211e320abc833a0b0bf3d0afb46c4434932d483f6d9019b45f9354890773aff84482abba2f9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/require-directory@2.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/troygoode/node-require-directory/"},{"type":"vcs","url":"git://github.com/troygoode/node-require-directory.git"}],"type":"library","bom-ref":"pkg:npm/require-directory@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Vsevolod Strukchinsky (github.com/floatdrop)","group":"","name":"require-from-string","version":"2.0.2","description":"Require module from string","scope":"optional","hashes":[{"alg":"SHA-512","content":"5dfd2759ee91b1ece214cbbe029f5b8a251b9a996ae92f7fa7eef0ed85cffc904786b5030d48706bebc0372b9bbaa7d9593bde53ffc36151ac0c6ed128bfef13"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/require-from-string@2.0.2","type":"library","bom-ref":"pkg:npm/require-from-string@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Szymon Marczak","group":"","name":"resolve-alpn","version":"1.2.1","description":"Detects the ALPN protocol","scope":"optional","hashes":[{"alg":"SHA-512","content":"d1ad45e25ef7fd915939a9099d0dc5be4276fa0493416cffaf6284e4e7436344f13e6e61e0692a91659f338ed3ec7b1b9ceb5c255105e1ea42572eaeed0dcafa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/resolve-alpn@1.2.1","externalReferences":[{"type":"vcs","url":"https://github.com/szmarczak/resolve-alpn#readme"},{"type":"vcs","url":"git+https://github.com/szmarczak/resolve-alpn.git"}],"type":"library","bom-ref":"pkg:npm/resolve-alpn@1.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"resolve-dir","version":"1.0.1","description":"Resolve a directory that is either local, global or in the user's home directory.","scope":"optional","hashes":[{"alg":"SHA-512","content":"47bba24e3102cef3ac5927dd33440a14d05515c2b6eda1ce53076f2b9dc1716f33aa719d629d056e3f36732e78fb60383f6b45336d89e6445f7b547e94cff5ca"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/resolve-dir@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/resolve-dir"}],"type":"library","bom-ref":"pkg:npm/resolve-dir@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"resolve-from","version":"4.0.0","description":"Resolve the path of a module like `require.resolve()` but from a given path","scope":"optional","hashes":[{"alg":"SHA-512","content":"a5bfcc6265ecb40932b11171f2988d235b4614d408140def904dc6ab812e035745ea01e9ffebe066ab021896a9bf2f0ddd0fb8a3b170beab8f25c9d9ed1632e2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/resolve-from@4.0.0","type":"library","bom-ref":"pkg:npm/resolve-from@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"resolve-from","version":"5.0.0","description":"Resolve the path of a module like `require.resolve()` but from a given path","scope":"optional","hashes":[{"alg":"SHA-512","content":"a9883d28fdb8743e6a91af49e3b774695932d0df9be1f4d4f3d2cdf620e78c1e706a4b220b8f6bbcc0743eb509406a13987e745cf8aa3af0230df6a28c6c5867"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/resolve-from@5.0.0","type":"library","bom-ref":"pkg:npm/resolve-from@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"lukechilds","group":"","name":"responselike","version":"2.0.1","description":"A response-like object for mocking a Node.js HTTP response stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"e20974df09f7863d473f7cb381d23b777942905f79176d4fcf804f1af2878a7c90cc02d1e426a9c02f32222d11879f0310c43f4a0b82d37c058f693433f98787"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/responselike@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/sindresorhus/responselike.git"}],"type":"library","bom-ref":"pkg:npm/responselike@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"restore-cursor","version":"3.1.0","description":"Gracefully restore the CLI cursor on exit","scope":"optional","hashes":[{"alg":"SHA-512","content":"97eb1279fcc7a63e6a8a6845484e5af27b9f65800cdec05254c00fb589260bee041f66a7486684317483d22cd141bbbd9dfc90f72e49ad59a9ec4f2866b523bc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/restore-cursor@3.1.0","type":"library","bom-ref":"pkg:npm/restore-cursor@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"restore-cursor","version":"5.1.0","description":"Gracefully restore the CLI cursor on exit","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0c03675caf0eaed187f12505e6df8d9b14a5ff138b06f6b6d3ccef69b54711fdef00df7707baf4ad8983b01fb7ecce4665675cffb5af400283e4d85e2a20e1c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/restore-cursor@5.1.0","type":"library","bom-ref":"pkg:npm/restore-cursor@5.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Roly Fentanes (https://github.com/fent)","group":"","name":"ret","version":"0.1.15","description":"Tokenizes a string that represents a regular expression.","scope":"optional","hashes":[{"alg":"SHA-512","content":"4d3958a5af8e2febcc30d1b6e314a5406109dc1fd1cc47d494b72dedbe46ff2b5abfec0fae9942a55305bb0cd76e479c26b6fa218a358856f44bdbf7efbe789a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/ret@0.1.15","externalReferences":[{"type":"vcs","url":"git://github.com/fent/ret.js.git"}],"type":"library","bom-ref":"pkg:npm/ret@0.1.15","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"reusify","version":"1.1.0","description":"Reuse objects and functions with style","scope":"optional","hashes":[{"alg":"SHA-512","content":"83a4147dfd38a19a47b34786e69f37ac52e11de574d2e83f61ff6764ce9f2de52b3e0b814e44d039da40596b29321e794d97d54033da37735025f6d5440c5d23"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/reusify@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/reusify#readme"},{"type":"vcs","url":"git+https://github.com/mcollina/reusify.git"}],"type":"library","bom-ref":"pkg:npm/reusify@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"William Swanson","group":"","name":"rfc4648","version":"1.5.4","description":"Encoding and decoding for base64, base32, base16, and friends","scope":"optional","hashes":[{"alg":"SHA-512","content":"ad183fe8b6fe2067c9a8ed391d990de7452d63b2bf261c496a0d643f6dfecf231faefa01d01ed15afd3a31b3b3a1cefd46009d35389468db134f500967b67e72"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/rfc4648@1.5.4","type":"library","bom-ref":"pkg:npm/rfc4648@1.5.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"David Mark Clements ","group":"","name":"rfdc","version":"1.4.1","description":"Really Fast Deep Clone","scope":"optional","hashes":[{"alg":"SHA-512","content":"ab56f737942445459497b8b2ca569a8f790ea484f43768bd32a2044173fbdc656c37d730ddf771f17eb77049968491a2d8f3c2176dc88e9ee4b66777f6b6b020"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/rfdc@1.4.1","externalReferences":[{"type":"vcs","url":"https://github.com/davidmarkclements/rfdc#readme"},{"type":"vcs","url":"git+https://github.com/davidmarkclements/rfdc.git"}],"type":"library","bom-ref":"pkg:npm/rfdc@1.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gajus Kuizinas (http://gajus.com)","group":"","name":"roarr","version":"2.15.4","description":"JSON logger for Node.js and browser.","scope":"optional","hashes":[{"alg":"SHA-512","content":"08784f87e50d1c3d864d735884f58b9d4f0e347748fb90c8fb811820039a883eb7ac7798959bf287c3fe8a7e7df7d4d348581462e294023cd123899d87fa7ed8"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/roarr@2.15.4","type":"library","bom-ref":"pkg:npm/roarr@2.15.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"router","version":"2.2.0","description":"Simple middleware-style router","scope":"optional","hashes":[{"alg":"SHA-512","content":"9cb4eb50a9b653288beeb9616a9bbf665e3917036091919a0a965b2076a30d883094908eccb4a4f9c20f027b04a95f79e468c82c99ca6dd402d6754fcfe80061"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/router@2.2.0","type":"library","bom-ref":"pkg:npm/router@2.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Simon Boudrias ","group":"","name":"run-async","version":"2.4.1","description":"Utility method to run function either synchronously or asynchronously using the common `this.async()` style.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b6f56756fd356fc73546b03a129ec9912b63f391aebff62b31cc2a6109f08ec012d9c4e698f181063023a425bb46b4a874d4a8136fea83d3b86dc78dbd4b8381"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/run-async@2.4.1","type":"library","bom-ref":"pkg:npm/run-async@2.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"run-parallel","version":"1.2.0","description":"Run an array of functions in parallel","scope":"optional","hashes":[{"alg":"SHA-512","content":"e65e15c9947ce8b67f943c594d1ea3a8bf00144d92d0814b30fdba01b8ec2d5003c4776107f734194b07fb2dfd51f0a2dddcf3f0e950b8f9a768938ca031d004"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/run-parallel@1.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/feross/run-parallel"},{"type":"vcs","url":"git://github.com/feross/run-parallel.git"}],"type":"library","bom-ref":"pkg:npm/run-parallel@1.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Lesh ","group":"","name":"rxjs","version":"7.8.1","description":"Reactive Extensions for modern JavaScript","scope":"optional","hashes":[{"alg":"SHA-512","content":"000dd3563fb40368ae2284245842bfb6a16306ada3fba3cee98d3325cbf32c016110520edc72f4be5b3d8562e77196c001b2b499aafba19e15d3bf48fea3ccc6"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/rxjs@7.8.1","externalReferences":[{"type":"website","url":"https://rxjs.dev"},{"type":"vcs","url":"https://github.com/reactivex/rxjs.git"}],"type":"library","bom-ref":"pkg:npm/rxjs@7.8.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"safe-buffer","version":"5.2.1","description":"Safer Node.js Buffer API","scope":"optional","hashes":[{"alg":"SHA-512","content":"ae9dd2a34eca71d9a629b1af81a37141226bedb1954959394bd12ad45fa9a5b468ef4f9879a0f1930e4377c34f37e183e9b8e7626d95b8fb825e6a6e62f9825d"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/safe-buffer@5.2.1","externalReferences":[{"type":"vcs","url":"https://github.com/feross/safe-buffer"},{"type":"vcs","url":"git://github.com/feross/safe-buffer.git"}],"type":"library","bom-ref":"pkg:npm/safe-buffer@5.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ruben Bridgewater","group":"","name":"safe-stable-stringify","version":"2.5.0","description":"Deterministic and safely JSON.stringify to quickly serialize JavaScript objects","scope":"optional","hashes":[{"alg":"SHA-512","content":"6f7ae9a532a6f53f8fb1508110e511e3a19623b7dd3acd3454a675fbd7351160da0ccbe341cead530b85c88a6b806813716a151d22ab53c1f7d591c0d9ed111c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/safe-stable-stringify@2.5.0","externalReferences":[{"type":"vcs","url":"https://github.com/BridgeAR/safe-stable-stringify#readme"},{"type":"vcs","url":"git+https://github.com/BridgeAR/safe-stable-stringify.git"}],"type":"library","bom-ref":"pkg:npm/safe-stable-stringify@2.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Nikita Skovoroda (https://github.com/ChALkeR)","group":"","name":"safer-buffer","version":"2.1.2","description":"Modern Buffer API polyfill without footguns","scope":"optional","hashes":[{"alg":"SHA-512","content":"619a372bcd920fb462ca2d04d4440fa232f3ee4a5ea6749023d2323db1c78355d75debdbe5d248eeda72376003c467106c71bbbdcc911e4d1c6f0a9c42b894b6"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/safer-buffer@2.1.2","externalReferences":[{"type":"vcs","url":"git+https://github.com/ChALkeR/safer-buffer.git"}],"type":"library","bom-ref":"pkg:npm/safer-buffer@2.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Peter Müller ","group":"","name":"schemes","version":"1.4.0","description":"IANA Uniform Resource Identifier (URI) Schemes list, including crowd sourced unofficial ones","scope":"optional","hashes":[{"alg":"SHA-512","content":"226172f456c2b109558271374c25a62cf0859d5cf1d251cbfe5fae987a650ea00a7747731699d2ea51592296a00656212b0cd59a5577e9e7346355d3bbc24101"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/schemes@1.4.0","externalReferences":[{"type":"vcs","url":"https://github.com/Munter/schemes"},{"type":"vcs","url":"git://github.com/Munter/schemes.git"}],"type":"library","bom-ref":"pkg:npm/schemes@1.4.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"section-matter","version":"1.0.0","description":"Like front-matter, but supports multiple sections in a document.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bdf0f7a664f31a9b9f8d2701879d181cacc4bb6971056855107b0d1a81179829a7da12867de34b60ccc22697bc703ee0a97ed3265b8e56906401eaaee9d80c98"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/section-matter@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/section-matter"}],"type":"library","bom-ref":"pkg:npm/section-matter@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Eran Hammer ","group":"","name":"secure-json-parse","version":"4.1.0","description":"JSON parse with prototype poisoning protection","scope":"optional","hashes":[{"alg":"SHA-512","content":"9782a761f132a982710f094d57245f3b61383531df30a016754b80f09d32bded83cff13f3dd05ea58d3746fc89a6cb08a51170268083e79c00fa6103b3a07258"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/secure-json-parse@4.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/fastify/secure-json-parse#readme"},{"type":"vcs","url":"git+https://github.com/fastify/secure-json-parse.git"}],"type":"library","bom-ref":"pkg:npm/secure-json-parse@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"semver-compare","version":"1.0.0","description":"compare two semver version strings, returning -1, 0, or 1","scope":"optional","hashes":[{"alg":"SHA-512","content":"60cdff213876309e4cb7368ce36f5a9e1fb1da388b563a882c5e26e28c90075f16ec681e6bb05fa9d1ffc0630aedd0e232086fffa586ef39d6330503cc9897a3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/semver-compare@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/substack/semver-compare"},{"type":"vcs","url":"git://github.com/substack/semver-compare.git"}],"type":"library","bom-ref":"pkg:npm/semver-compare@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"semver","version":"7.7.4","description":"The semantic version parser used by npm.","scope":"optional","hashes":[{"alg":"SHA-512","content":"bc5282d8812d427561a53efc875629f30cf0adff0233e33328c1c62597c1b738593727111675ec1e4e84e53c4892432c80d4bb99d5f700607bc7640cd9d8b894"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/semver@7.7.4","externalReferences":[{"type":"vcs","url":"git+https://github.com/npm/node-semver.git"}],"type":"library","bom-ref":"pkg:npm/semver@7.7.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"TJ Holowaychuk ","group":"","name":"send","version":"1.2.1","description":"Better streaming static file server with Range and conditional-GET support","scope":"optional","hashes":[{"alg":"SHA-512","content":"d609d97fb0c572821c6a34e34f08f0b838f3bb3e0f3dc6364ad28f96c180435f981f6d08455ac169749699d8e8c1327abbc45cd353e860e1875df3f599871935"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/send@1.2.1","type":"library","bom-ref":"pkg:npm/send@1.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"serialize-error","version":"7.0.1","description":"Serialize/deserialize an error into a plain object","scope":"optional","hashes":[{"alg":"SHA-512","content":"f08f138d6e4a30e2ac6504efa318ee4886bb7e80303d618eb6cfbaa3bb208f3e35fea303f55407103c62e8f06f2b6974317526a99c8da542be4f6b5069a125bf"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/serialize-error@7.0.1","type":"library","bom-ref":"pkg:npm/serialize-error@7.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"serve-static","version":"2.2.1","description":"Serve static files","scope":"optional","hashes":[{"alg":"SHA-512","content":"c515c19f4a4fa904d54220bcc3242b2acd8c3a55f6e334343ce19a8f492e96dbe8382b2d050339caf3a1015494c0e32342d4efb0f5a83421df3c6c1a6902614f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/serve-static@2.2.1","type":"library","bom-ref":"pkg:npm/serve-static@2.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Wes Todd","group":"","name":"setprototypeof","version":"1.2.0","description":"A small polyfill for Object.setprototypeof","scope":"optional","hashes":[{"alg":"SHA-512","content":"1392c35fb5aba7ce4a8a5e5b859bf8ea3f2339e6e82aae4932660cde05467461fcc45a4f59750cb0dae53830ab928c4c11e362fd7648c2e46f6385cdc18309a7"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/setprototypeof@1.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/wesleytodd/setprototypeof"},{"type":"vcs","url":"https://github.com/wesleytodd/setprototypeof.git"}],"type":"library","bom-ref":"pkg:npm/setprototypeof@1.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kevin Mårtensson (github.com/kevva)","group":"","name":"shebang-command","version":"2.0.0","description":"Get the command from a shebang","scope":"optional","hashes":[{"alg":"SHA-512","content":"907c6bdb366962d766acdd6a0e3aeb5ff675ad1d641bc0f1fa09292b51b87979af5ecc26704d614d6056614ce5ada630d7fc99a7a62e0d8efb62dbdb3747660c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/shebang-command@2.0.0","type":"library","bom-ref":"pkg:npm/shebang-command@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"shebang-regex","version":"3.0.0","description":"Regular expression for matching a shebang line","scope":"optional","hashes":[{"alg":"SHA-512","content":"efef9d161b5cc77df9dee05aabc0c347836ec417ad0730bb6503a19934089c711de9b4ab5dd884cb30af1b4ed9e3851874b4a1594c97b7933fca1cfc7a471bd4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/shebang-regex@3.0.0","type":"library","bom-ref":"pkg:npm/shebang-regex@3.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"side-channel-list","version":"1.0.1","description":"Store information about any JS value in a side channel, using a linked list","scope":"optional","hashes":[{"alg":"SHA-512","content":"9a39ffd1b8bfa145118dce5797b21a5a2fce24926e9aea0915025f0c3c8ee3afa1056b1f69533ae53047ab67a864187397d11c8713a28e991b442f12541414d3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/side-channel-list@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/side-channel-list#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/side-channel-list.git"}],"type":"library","bom-ref":"pkg:npm/side-channel-list@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"side-channel-map","version":"1.0.1","description":"Store information about any JS value in a side channel, using a Map","scope":"optional","hashes":[{"alg":"SHA-512","content":"5428c235f80cb1bcb7b53768d369db8ed33f7b0adaea33c79a94e17a7913621f291bdb9c67fd4ff12a38bb814605e93f063a4e56c0c23282c0fe2b8128815744"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/side-channel-map@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/side-channel-map#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/side-channel-map.git"}],"type":"library","bom-ref":"pkg:npm/side-channel-map@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"side-channel-weakmap","version":"1.0.2","description":"Store information about any JS value in a side channel. Uses WeakMap if available.","scope":"optional","hashes":[{"alg":"SHA-512","content":"58f4bf1ef1d04d89c78ac2e8f4c72a0473899361641cefed969be5772ae77a6e1a790a7885a8b7832b61b3083aa74d684a84e5e7cadca621408c5d9baf6024d8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/side-channel-weakmap@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/side-channel-weakmap#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/side-channel-weakmap.git"}],"type":"library","bom-ref":"pkg:npm/side-channel-weakmap@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jordan Harband ","group":"","name":"side-channel","version":"1.1.0","description":"Store information about any JS value in a side channel. Uses WeakMap if available.","scope":"optional","hashes":[{"alg":"SHA-512","content":"657f7d7bab51c1ea145ea47e541aec96175ae75361e4c4d0c28bb9b6750381bb723347418268440ed5863ffc5b2a7ea1a9f3d11ee8d4370cf97f2ff06db867a7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/side-channel@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/ljharb/side-channel#readme"},{"type":"vcs","url":"git+https://github.com/ljharb/side-channel.git"}],"type":"library","bom-ref":"pkg:npm/side-channel@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Coe ","group":"","name":"signal-exit","version":"3.0.7","description":"when you want to fire an event no matter how a process exits.","scope":"optional","hashes":[{"alg":"SHA-512","content":"c270f6644fa5f923c2feea12d2f5de13d2f5fb4c2e68ca8a95fcfd00c528dfc26cc8b48159215c1d1d51ae2eb62d9735daf2ebd606f78e5ee2c10860c2901b19"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/signal-exit@3.0.7","externalReferences":[{"type":"vcs","url":"https://github.com/tapjs/signal-exit"},{"type":"vcs","url":"https://github.com/tapjs/signal-exit.git"}],"type":"library","bom-ref":"pkg:npm/signal-exit@3.0.7","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ben Coe ","group":"","name":"signal-exit","version":"4.1.0","description":"when you want to fire an event no matter how a process exits.","scope":"optional","hashes":[{"alg":"SHA-512","content":"6f3c99d5ef3cc3d3b588d25b2a73a5bd84eb58f0e5e3a3b56c6d03dd7227bfef6d90faf1acdf235144e21650e4926296827d4ce827c8035dd2b86a8e6bd2a8af"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/signal-exit@4.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/tapjs/signal-exit.git"}],"type":"library","bom-ref":"pkg:npm/signal-exit@4.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"simple-concat","version":"1.0.1","description":"Super-minimalist version of `concat-stream`. Less than 15 lines!","scope":"optional","hashes":[{"alg":"SHA-512","content":"71216d00fb518658efebd20ad214d5650f8e7c4f6778f8bfaed266c395231de57256ba04a895cfd6c173b4a532d6a53ec6fcf7bbfb1f6092daf78edbee700dd9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/simple-concat@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/feross/simple-concat"},{"type":"vcs","url":"git://github.com/feross/simple-concat.git"}],"type":"library","bom-ref":"pkg:npm/simple-concat@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Feross Aboukhadijeh (https://feross.org)","group":"","name":"simple-get","version":"4.0.1","description":"Simplest way to make http get requests. Supports HTTPS, redirects, gzip/deflate, streams in < 100 lines.","scope":"optional","hashes":[{"alg":"SHA-512","content":"6ebbfba795a01f48e6409af56430df2833927965a0f8e572a46f7d03fe6f6063ea27aa7189a1cbcbc9f1b458c103ba0c6b4d5e6c0f607e1d6e30216a3ae5f1bc"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/simple-get@4.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/feross/simple-get"},{"type":"vcs","url":"git://github.com/feross/simple-get.git"}],"type":"library","bom-ref":"pkg:npm/simple-get@4.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Steve King ","group":"","name":"simple-git","version":"3.36.0","description":"Simple GIT interface for node.js","scope":"optional","hashes":[{"alg":"SHA-512","content":"7064232e32bc6f1270e10b984fb8311f0dff228b951126e16a14ac1eb5fdecccc22f582edaeee8cb7f16e8bd9920610211f2011b805a6ec5833e307124436ff5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/simple-git@3.36.0","externalReferences":[{"type":"vcs","url":"https://github.com/steveukx/git-js.git"}],"type":"library","bom-ref":"pkg:npm/simple-git@3.36.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"slice-ansi","version":"7.1.2","description":"Slice a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"88e056160517edc688662baeb480b15605f549dc700151452b0b7512f31861e73f3563b999e389e8ae6e43186b6017502677b82b18aa65cf8aa6d14e585488f7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/slice-ansi@7.1.2","type":"library","bom-ref":"pkg:npm/slice-ansi@7.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"slice-ansi","version":"8.0.0","description":"Slice a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"b2dc41cabd76a1e78ec98d8196f893350958579c4e8f8ec68ab3ebe32035844f490adc5f40dd3eb556e4c700ad603416844296147b042b8f0e460e63ae8b8202"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/slice-ansi@8.0.0","type":"library","bom-ref":"pkg:npm/slice-ansi@8.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"slide","version":"1.1.6","description":"A flow control lib small enough to fit on in a slide presentation. Derived live at Oak.JS","scope":"optional","hashes":[{"alg":"SHA-512","content":"370aed8c283e959a2a84553c7cec25e1acb67a2f0f6aa081394577fde92d3d8f6daced72435a1711f987021280ad4554aff84e1efcb97fa01d3f5edd08cd3333"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/slide@1.1.6","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/slide-flow-control.git"}],"type":"library","bom-ref":"pkg:npm/slide@1.1.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gene Hightower (https://digilicious.com/)","group":"","name":"smtp-address-parser","version":"1.1.0","description":"Parse an SMTP (RFC-5321) address","scope":"optional","hashes":[{"alg":"SHA-512","content":"1b3d758db354d2996b45e53d4a3edf9ac852071c49f5285d0f6ab892d1c81e8feba531fa945c90a181d828834f26d3def1a1c59ec6ed5b8e8bb34b42081b0866"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/smtp-address-parser@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/gene-hightower/smtp-address-parser#readme"},{"type":"vcs","url":"git+https://github.com/gene-hightower/smtp-address-parser.git"}],"type":"library","bom-ref":"pkg:npm/smtp-address-parser@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Remy Sharp","group":"","name":"snyk-config","version":"5.3.0","description":"Config setup for snyk shared across projects","scope":"optional","hashes":[{"alg":"SHA-512","content":"60fc616195c15e09d876fa2fc2529fe4961c3a9fa7c41ee57a5ded58b6ab62a678870c4dd75f05a1d2056fc9d2a8cadea6c3ddc8e69060a2a7ad362abd079a24"}],"licenses":[{"expression":"(Apache-2.0 AND MIT)"}],"purl":"pkg:npm/snyk-config@5.3.0","externalReferences":[{"type":"vcs","url":"https://github.com/snyk/config.git"}],"type":"library","bom-ref":"pkg:npm/snyk-config@5.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"snyk-nodejs-lockfile-parser","version":"2.7.0","scope":"required","hashes":[{"alg":"SHA-512","content":"d0ce2968b9e12920ed8f66a44faab3b450b630f7cb3bbbc7c48797608d7eaccbfc7c10b945fcea1dc7c2ada15b1037bdeb5a0f58e317852af22c036d0b939c57"}],"purl":"pkg:npm/snyk-nodejs-lockfile-parser@2.7.0","type":"library","bom-ref":"pkg:npm/snyk-nodejs-lockfile-parser@2.7.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"snyk-nodejs-lockfile-parser,InvalidUserInputError,snyk-nodejs-lockfile-parser/InvalidUserInputError,OutOfSyncError,snyk-nodejs-lockfile-parser/OutOfSyncError,parseNpmLockV2Project,snyk-nodejs-lockfile-parser/parseNpmLockV2Project,parsePnpmProject,snyk-nodejs-lockfile-parser/parsePnpmProject"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/pipeline/dep-parsers/npm.js#25"},{"location":"packages/ingestion/src/pipeline/dep-parsers/npm.ts#31"}]}},{"author":"Matteo Collina ","group":"","name":"sonic-boom","version":"4.2.1","description":"Extremely fast utf8 only stream implementation","scope":"optional","hashes":[{"alg":"SHA-512","content":"c3a031b6e6d76b6c135c052c64c316111aec21101dacad1273e154cad5af60084124bcae238965acc202d43b65352748f7d108f3a299ba6d8c32adc4019945f5"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/sonic-boom@4.2.1","externalReferences":[{"type":"vcs","url":"https://github.com/pinojs/sonic-boom#readme"},{"type":"vcs","url":"git+https://github.com/pinojs/sonic-boom.git"}],"type":"library","bom-ref":"pkg:npm/sonic-boom@4.2.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Nick Fitzgerald ","group":"","name":"source-map","version":"0.6.1","description":"Generates and consumes source maps","scope":"optional","hashes":[{"alg":"SHA-512","content":"52381aa6e99695b3219018334fb624739617513e3a17488abbc4865ead1b7303f9773fe1d0f963e9e9c9aa3cf565bab697959aa989eb55bc16396332177178ee"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/source-map@0.6.1","externalReferences":[{"type":"vcs","url":"https://github.com/mozilla/source-map"},{"type":"vcs","url":"http://github.com/mozilla/source-map.git"}],"type":"library","bom-ref":"pkg:npm/source-map@0.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kyle E. Mitchell (https://kemitchell.com)","group":"","name":"spdx-compare","version":"1.0.0","description":"compare SPDX license expressions","scope":"optional","hashes":[{"alg":"SHA-512","content":"0b598364e5f4867bb47a9f5d7e6ba88b4dfe78e743a33db2bcaefd4716dcad5106d4d3b53e1df9c96d74d831d628de2993cd27c0281e326498e4bdb9544e03f4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/spdx-compare@1.0.0","type":"library","bom-ref":"pkg:npm/spdx-compare@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"spdx-correct","version":"3.2.0","description":"correct invalid SPDX expressions","scope":"required","hashes":[{"alg":"SHA-512","content":"90df5d25bbe7c921d42c896e0c7cb7d961d152edce83b07db1b63bb6c14b72d42422a9cc877844ad881d3234d8baa99c5d7fa52b94f596752ddc6ef336cc2664"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/spdx-correct@3.2.0","type":"library","bom-ref":"pkg:npm/spdx-correct@3.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"ImportedModules","value":"spdx-correct"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/ingestion/dist/pipeline/dep-parsers/spdx-normalize.js#13"},{"location":"packages/ingestion/src/pipeline/dep-parsers/spdx-normalize.ts#14"}]}},{"author":"The Linux Foundation","group":"","name":"spdx-exceptions","version":"2.3.0","description":"list of SPDX standard license exceptions","scope":"optional","hashes":[{"alg":"SHA-512","content":"fed4eb60e0bb3cf2359d4020c77e21529a97bb2246f834c72539c850b1b8ac3ca08b8c6efed7e09aad5ed5c211c11cf0660a3834bc928beae270b919930e22e4"}],"licenses":[{"license":{"id":"CC-BY-3.0","url":"https://opensource.org/licenses/CC-BY-3.0"}}],"purl":"pkg:npm/spdx-exceptions@2.3.0","type":"library","bom-ref":"pkg:npm/spdx-exceptions@2.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kyle E. Mitchell (https://kemitchell.com)","group":"","name":"spdx-expression-parse","version":"3.0.1","description":"parse SPDX license expressions","scope":"optional","hashes":[{"alg":"SHA-512","content":"71ba87ba7b105a724d13a2a155232c31e1f91ff2fd129ca66f3a93437b8bc0d08b675438f35a166a87ea1fb9cee95d3bc655f063a3e141d43621e756c7f64ae1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/spdx-expression-parse@3.0.1","type":"library","bom-ref":"pkg:npm/spdx-expression-parse@3.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Shinnosuke Watanabe (https://github.com/shinnn)","group":"","name":"spdx-license-ids","version":"3.0.12","description":"A list of SPDX license identifiers","scope":"optional","hashes":[{"alg":"SHA-512","content":"aebf955525ed4613b83876d7522005ef15b7068f43bae17a0b98c7faaff1d798f68e78b272029bc54d3d1ebd16aa548b52ce22e25b47197a937bb547725616c8"}],"licenses":[{"license":{"id":"CC0-1.0","url":"https://opensource.org/licenses/CC0-1.0"}}],"purl":"pkg:npm/spdx-license-ids@3.0.12","type":"library","bom-ref":"pkg:npm/spdx-license-ids@3.0.12","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"The Linux Foundation","group":"","name":"spdx-ranges","version":"2.1.1","description":"list of SPDX standard license ranges","scope":"optional","hashes":[{"alg":"SHA-512","content":"99c76940557b5030202e95c413f8ce32abcae0b0683b4b93420d2ebd751ec26105869899c79c894992131c1f30d596a129d85f66a3f918f10e28bc84ab9a7c5c"}],"licenses":[{"expression":"(MIT AND CC-BY-3.0)"}],"purl":"pkg:npm/spdx-ranges@2.1.1","type":"library","bom-ref":"pkg:npm/spdx-ranges@2.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kyle E. Mitchell (https://kemitchell.com)","group":"","name":"spdx-satisfies","version":"5.0.1","description":"test whether SPDX expressions satisfy licensing criteria","scope":"optional","hashes":[{"alg":"SHA-512","content":"370a2be96ea0cc5a7c5d7e2779a290ec2855e309a94a1dac4837a63054b31f1a53c38eb48f115878e9fe8eae326e7492c3fe6c737a6391af4c40fa2e92c40da7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/spdx-satisfies@5.0.1","type":"library","bom-ref":"pkg:npm/spdx-satisfies@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"split2","version":"4.2.0","description":"split a Text Stream into a Line Stream, using Stream 3","scope":"optional","hashes":[{"alg":"SHA-512","content":"51c8dc24e5a49eb36417a3cb5fcdea70733a28781528d915eb663c6b9b980d5bfdc9d19057000730aa877498ded554d6a658c6d1662908386b09d00e607e135a"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/split2@4.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/split2.git"}],"type":"library","bom-ref":"pkg:npm/split2@4.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Alexandru Mărășteanu ","group":"","name":"sprintf-js","version":"1.1.3","description":"JavaScript sprintf implementation","scope":"optional","hashes":[{"alg":"SHA-512","content":"3a8fb4444155e7dfebcf781f24d2908819707c7692112975a5c1b200142c9e721f58e16de89363e600a883653a30b67ffc81980fe9c0f2723e9934a144445e68"}],"licenses":[{"license":{"id":"BSD-3-Clause","url":"https://opensource.org/licenses/BSD-3-Clause"}}],"purl":"pkg:npm/sprintf-js@1.1.3","externalReferences":[{"type":"vcs","url":"https://github.com/alexei/sprintf.js.git"}],"type":"library","bom-ref":"pkg:npm/sprintf-js@1.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"statuses","version":"2.0.2","description":"HTTP status utility","scope":"optional","hashes":[{"alg":"SHA-512","content":"0ef132e795770c1eee927468fb888e193e5f3f5b2547cc10a2155d9278a064f32932cb5a289416870898040089137525da94e70138a18416274616501c606247"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/statuses@2.0.2","type":"library","bom-ref":"pkg:npm/statuses@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"stdin-discarder","version":"0.2.2","description":"Discard stdin input except for Ctrl+C","scope":"optional","hashes":[{"alg":"SHA-512","content":"5210df1e603dd9802536709f866ab455e34be5b0db8991a0eec67622f3ec5ee6c692235af440beb544ec8c146c614033f3b6ed23aff5c1fe17a15bd0dee46799"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/stdin-discarder@0.2.2","type":"library","bom-ref":"pkg:npm/stdin-discarder@0.2.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"string-width","version":"4.2.3","description":"Get the visual width of a string - the number of columns required to display it","scope":"optional","hashes":[{"alg":"SHA-512","content":"c0ac90450a63274b08a7ad84ad265d1ac8cc256b1aa79a1136284786ee86ec954effd8c807a5327af2feb57b8eaab9e0f23fdcc4a4d6c96530bd24eb8a2673fe"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/string-width@4.2.3","type":"library","bom-ref":"pkg:npm/string-width@4.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"string-width","version":"5.1.2","description":"Get the visual width of a string - the number of columns required to display it","scope":"optional","hashes":[{"alg":"SHA-512","content":"1e72ce091def8dc63c6dea0d2ed723679fe7c67d9a7e6304ea586b0eb79ba24a8c6a9f976de5bc9fd4d7a4f0cea9d18ae6a708de84f418a4d6eb00bb10c895a8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/string-width@5.1.2","type":"library","bom-ref":"pkg:npm/string-width@5.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"string-width","version":"7.2.0","description":"Get the visual width of a string - the number of columns required to display it","scope":"optional","hashes":[{"alg":"SHA-512","content":"b6c693224296f5be0df80123f92540f96849cd5effccc85c4aeefc98b2964a4edc5cc3921ec04a15652cd1f5b0abc4322b73202414115fa19b8b89186ddbc691"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/string-width@7.2.0","type":"library","bom-ref":"pkg:npm/string-width@7.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"string-width","version":"8.2.0","description":"Get the visual width of a string - the number of columns required to display it","scope":"optional","hashes":[{"alg":"SHA-512","content":"ea124f43c3745743f748d98fea1d89f7d44bbb3ad6cf682f4fb5672b9b4abeb36a268c92f56e3f15bf26a37d5488fbf2d34cfb0d05e43f686728155abfbead87"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/string-width@8.2.0","type":"library","bom-ref":"pkg:npm/string-width@8.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"string_decoder","version":"1.3.0","description":"The string_decoder module from Node core","scope":"optional","hashes":[{"alg":"SHA-512","content":"864457f14d568c915df0bb03276c90ff0596c5aa2912c0015355df90cf00fa3d3ef392401a9a6dd7a72bd56860e8a21b6f8a2453a32a97a04e8febaea7fc0a78"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/string_decoder@1.3.0","externalReferences":[{"type":"vcs","url":"https://github.com/nodejs/string_decoder"},{"type":"vcs","url":"git://github.com/nodejs/string_decoder.git"}],"type":"library","bom-ref":"pkg:npm/string_decoder@1.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"strip-ansi","version":"6.0.1","description":"Strip ANSI escape codes from a string","scope":"optional","hashes":[{"alg":"SHA-512","content":"637f153d21dcaa416b0a916743dbee4979aabaebf9a1738aa46793e9a1abaf7a3719cf409556ba2417d448e0a76f1186645fbfd28a08ecaacfb944b3b54754e4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-ansi@6.0.1","type":"library","bom-ref":"pkg:npm/strip-ansi@6.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"strip-ansi","version":"7.2.0","description":"Strip ANSI escape codes from a string","scope":"optional","hashes":[{"alg":"SHA-512","content":"c833cc363a785b27d80641e78c844b7dc6b58ba28cc860adb1582829eff3d7eeafba481a10d76018166df9998a3dce206afbc46793a01df1ddadace180dc86ef"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-ansi@7.2.0","type":"library","bom-ref":"pkg:npm/strip-ansi@7.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"strip-bom-string","version":"1.0.0","description":"Strip a byte order mark (BOM) from a string.","scope":"optional","hashes":[{"alg":"SHA-512","content":"b820b6547bd0458bbe94c878332fec14d985da4945ca62d7d701c97979db109111a55fd9b15ba89f37ab8dfac6a48185ecb0556b53bb8bd923896bc9885724f2"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-bom-string@1.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/strip-bom-string"}],"type":"library","bom-ref":"pkg:npm/strip-bom-string@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"strip-bom","version":"4.0.0","description":"Strip UTF-8 byte order mark (BOM) from a string","scope":"optional","hashes":[{"alg":"SHA-512","content":"df1bab16fe6d1208a2df7662f09b69e79c042082d1f5e877e05016d343d97fe2674ac4e657f8a87b04a0425f7b247be08e8446c0f4a1b169be21daf1077e5dd3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-bom@4.0.0","type":"library","bom-ref":"pkg:npm/strip-bom@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"strip-final-newline","version":"4.0.0","description":"Strip the final newline character from a string or Uint8Array","scope":"optional","hashes":[{"alg":"SHA-512","content":"6ae94525c0fa60af15d46ee2441e6d8a000fe13b0705966b395f298d5fbecdd53099e57cbb36d8ef29fa87d32cc0deb669d4ad35916e088c7885a06746e3036b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-final-newline@4.0.0","type":"library","bom-ref":"pkg:npm/strip-final-newline@4.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"strip-json-comments","version":"2.0.1","description":"Strip comments from JSON. Lets you use comments in your JSON files!","scope":"optional","hashes":[{"alg":"SHA-512","content":"e2007c9dad3b7de715564388e91b387bb4fa34e4e48b91262fb4d476e4ece9bbb711d9d2c9c9ed549e2b7bc920640fb0c7d22e788d98d756df6e0c2dcee13429"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-json-comments@2.0.1","type":"library","bom-ref":"pkg:npm/strip-json-comments@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"strip-json-comments","version":"3.1.1","description":"Strip comments from JSON. Lets you use comments in your JSON files!","scope":"optional","hashes":[{"alg":"SHA-512","content":"e9f3dcf91e22870a8fe8dfda22fd9fd60307f25395b56407a2a0b8c8aea8483555a1cba602c7c2aa39179ea89832198cc12fe61072e9ed57a196ddea97a9448a"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-json-comments@3.1.1","type":"library","bom-ref":"pkg:npm/strip-json-comments@3.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"strip-json-comments","version":"5.0.3","description":"Strip comments from JSON. Lets you use comments in your JSON files!","scope":"optional","hashes":[{"alg":"SHA-512","content":"d6d0799a1568ed4f844c128d7fddb14f886b41ade99b4319d0f42fb839d68000061c3b1fa7894f4a9892ea9b2b4a27adf3bc3218f87d7edeb09a138c4348438b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strip-json-comments@5.0.3","type":"library","bom-ref":"pkg:npm/strip-json-comments@5.0.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Amit Gupta (https://amitkumargupta.work/)","group":"","name":"strnum","version":"2.2.3","description":"Parse String to Number based on configuration","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0ac7a4540ae1df4f7a32563b67ae69f5f47d52882aa025283ee785ea5112a9e5a08c6eb5eb84b8d137d4e3bb0323898b2d6743330eb1ea9066797454ca77ece"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/strnum@2.2.3","externalReferences":[{"type":"vcs","url":"https://github.com/NaturalIntelligence/strnum"}],"type":"library","bom-ref":"pkg:npm/strnum@2.2.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"supports-color","version":"5.5.0","description":"Detect whether a terminal supports color","scope":"optional","hashes":[{"alg":"SHA-512","content":"423563c1d5c8b78d3c308880a825f8a142ac814d84a801b3b363e9926e1a4186e39be644584716e127c5353af8b8c35999ad1ecb87f99602eb901d1a5f440ca3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/supports-color@5.5.0","type":"library","bom-ref":"pkg:npm/supports-color@5.5.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"supports-color","version":"7.2.0","description":"Detect whether a terminal supports color","scope":"optional","hashes":[{"alg":"SHA-512","content":"aa9080bd197db2db8e1ef78ab27ec79dc251befe74d6a21a70acd094effe2f0c5cf7ed2adb02f2bf80dfbedf34fc33e7da9a8e06c25d0e2a205c647df8ebf047"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/supports-color@7.2.0","type":"library","bom-ref":"pkg:npm/supports-color@7.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus","group":"","name":"tar-fs","version":"2.1.4","description":"filesystem bindings for tar-stream","scope":"optional","hashes":[{"alg":"SHA-512","content":"983023c2665d87b2d34faa4d95e674e58b7ae470b713e36243397aef6bd01b7f2322b7a1b4993f27798dc0883ebd28503dc2c5fcb57b08c9c35babe38fab1f61"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tar-fs@2.1.4","externalReferences":[{"type":"vcs","url":"https://github.com/mafintosh/tar-fs"},{"type":"vcs","url":"https://github.com/mafintosh/tar-fs.git"}],"type":"library","bom-ref":"pkg:npm/tar-fs@2.1.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mathias Buus ","group":"","name":"tar-stream","version":"2.2.0","description":"tar-stream is a streaming tar parser and generator and nothing else. It is streams2 and operates purely using streams which means you can easily extract/parse tarballs without ever hitting the file system.","scope":"optional","hashes":[{"alg":"SHA-512","content":"ba37aa6dc780060c0c6711099e4d870d8d83967519fbda0471bd4acd355f6078a8d1413a746ef59fad1df03d88e2a36f95e5abad7a668e9b7bbd9785d4b9cc65"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tar-stream@2.2.0","externalReferences":[{"type":"vcs","url":"https://github.com/mafintosh/tar-stream"},{"type":"vcs","url":"git+https://github.com/mafintosh/tar-stream.git"}],"type":"library","bom-ref":"pkg:npm/tar-stream@2.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter","group":"","name":"tar","version":"7.5.13","description":"tar for node","scope":"optional","hashes":[{"alg":"SHA-512","content":"b4e1bfec6c97a457af857561f2338f26b9ad469393b18a9422455d568a196094bfcfc5a17d0517f112482e67ae24d8a7180312bb5bde06be1ab121c5b79fe19e"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/tar@7.5.13","externalReferences":[{"type":"vcs","url":"https://github.com/isaacs/node-tar.git"}],"type":"library","bom-ref":"pkg:npm/tar@7.5.13","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Matteo Collina ","group":"","name":"thread-stream","version":"3.1.0","description":"A streaming way to send data to a Node.js Worker Thread","scope":"optional","hashes":[{"alg":"SHA-512","content":"3aac8f67dbbde95a2103231f2729339a2bcead8db07cc49fdc2e53b4525580df879ba6a3fafa058652be919108bf61418755fa5e9dc396708e7c443707627ce8"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/thread-stream@3.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/mcollina/thread-stream#readme"},{"type":"vcs","url":"git+https://github.com/mcollina/thread-stream.git"}],"type":"library","bom-ref":"pkg:npm/thread-stream@3.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Dominic Tarr (dominictarr.com)","group":"","name":"through","version":"2.3.8","description":"simplified stream construction","scope":"optional","hashes":[{"alg":"SHA-512","content":"c3cf6a83b3c8f3001dbd7eb46cc0cff9b1680f90ef866f682e1785a793b86b6405d1c4811ac057e2a66669d3ccbd5aa52c9041722f96a8618e00fbdc0de35256"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/through@2.3.8","externalReferences":[{"type":"vcs","url":"https://github.com/dominictarr/through"},{"type":"vcs","url":"https://github.com/dominictarr/through.git"}],"type":"library","bom-ref":"pkg:npm/through@2.3.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Garbutt (https://github.com/43081j)","group":"","name":"tinyexec","version":"1.1.1","description":"A minimal library for executing processes in Node","scope":"optional","hashes":[{"alg":"SHA-512","content":"54a4bf65a42186428530036600e8615d5a087c15db950c465f59b2090d9f690adf9a86cc7ed5de24f7191a9d204b4ee872f1895832d91b23990da74306a60826"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tinyexec@1.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/tinylibs/tinyexec#readme"},{"type":"vcs","url":"git+https://github.com/tinylibs/tinyexec.git"}],"type":"library","bom-ref":"pkg:npm/tinyexec@1.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tinylogic","version":"2.0.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"7658d39222cb213b6c8ea06f4c0d4c45940afec18fe2423750929cdf203d7cccd86cc17646170dd3849e44e56a24121860ea0930cf2ed160e7845c0c485428b7"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tinylogic@2.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/arcanis/tinylogic"}],"type":"library","bom-ref":"pkg:npm/tinylogic@2.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"KARASZI István ","group":"","name":"tmp","version":"0.2.4","description":"Temporary file and directory creator","scope":"optional","hashes":[{"alg":"SHA-512","content":"51d892a17eb2a627cb9ab7d0fd77e26b037a8648d20690a384ac7165c5a55149a85cb6822905346f1e0717fb5d0cadaecd1b9c85fd6dc46beb581cd8444b2ca1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tmp@0.2.4","externalReferences":[{"type":"vcs","url":"http://github.com/raszi/node-tmp"}],"type":"library","bom-ref":"pkg:npm/tmp@0.2.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"to-regex-range","version":"5.0.1","description":"Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.78 million test assertions.","scope":"optional","hashes":[{"alg":"SHA-512","content":"eb93fb8b3e97e7212bd5cc1c82f4316db230ed493780ecb974876d678ac3bde2ea86b7493fe2e2fc7c7ab722b43446fed860b29de08c2621aaac00c248d93cb1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/to-regex-range@5.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/micromatch/to-regex-range"}],"type":"library","bom-ref":"pkg:npm/to-regex-range@5.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"toidentifier","version":"1.0.1","description":"Convert a string of words to a JavaScript identifier","scope":"optional","hashes":[{"alg":"SHA-512","content":"a39b123ca12483f0c840d987e37574fee7ab2eba7355e764521f2d18dbda797a5fa6ec2329e9e54a8c7fd8efc14e5654b447be246eece58844cfad3c3e500744"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/toidentifier@1.0.1","type":"library","bom-ref":"pkg:npm/toidentifier@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-c-sharp","version":"0.23.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"c4918e7973cc9a57749c44b9fb4f3437fd3ac98e8b422f8a586595e0b7d96997bab2b24fb54b5f8486d148dec464de8869abb35b6f2fe96e101c5c267945ba1d"}],"purl":"pkg:npm/tree-sitter-c-sharp@0.23.5","type":"library","bom-ref":"pkg:npm/tree-sitter-c-sharp@0.23.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-c","version":"0.23.6","scope":"optional","hashes":[{"alg":"SHA-512","content":"d1dc572b39d5c94034b3a3e336894936cdb217cecee5a2f9dfc03f791ea78c0e68a97dc2def1f8be7c7741d38ac2e51da4a11c15d1ee883a5128b2c203fb63bd"}],"purl":"pkg:npm/tree-sitter-c@0.23.6","type":"library","bom-ref":"pkg:npm/tree-sitter-c@0.23.6","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-c","version":"0.24.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"96463058ddd245e729bda7aa9852a4b8f351dd96ed9ef1d4fb85c010490976ba7725f4a9da906b8573adbdfb043549dec9eefa83cf3d63475d1760ccd20103a4"}],"purl":"pkg:npm/tree-sitter-c@0.24.1","type":"library","bom-ref":"pkg:npm/tree-sitter-c@0.24.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Max Brunsfeld","group":"","name":"tree-sitter-cli","version":"0.23.2","description":"CLI for generating fast incremental parsers","scope":"optional","hashes":[{"alg":"SHA-512","content":"90f3d7a6b3aa4445fe0bf160529d90a6df63774bd2c27fa13a08f356fffb85aa5da16a5af9579621de77adfe2834d776f62921785d76058b460ff5bdd1f78c6c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tree-sitter-cli@0.23.2","externalReferences":[{"type":"vcs","url":"https://github.com/tree-sitter/tree-sitter.git"}],"type":"library","bom-ref":"pkg:npm/tree-sitter-cli@0.23.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-cpp","version":"0.23.4","scope":"optional","hashes":[{"alg":"SHA-512","content":"a91e6a503ca16798c9e95f3fba6881c689116def3d6c21a672afdd93de3023890df3aa9f755f24d061c850429aa9681cbb8db029a97913decb2a92de556f2c2b"}],"purl":"pkg:npm/tree-sitter-cpp@0.23.4","type":"library","bom-ref":"pkg:npm/tree-sitter-cpp@0.23.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-dart","version":"0fc19c3a57b1109802af41d2b8f60d8835c5da3a","scope":"optional","purl":"pkg:npm/tree-sitter-dart@0fc19c3a57b1109802af41d2b8f60d8835c5da3a?vcs_url=https%3A//codeload.github.com/UserNobody14/tree-sitter-dart/tar.gz/0fc19c3a57b1109802af41d2b8f60d8835c5da3a","type":"library","bom-ref":"pkg:npm/tree-sitter-dart@0fc19c3a57b1109802af41d2b8f60d8835c5da3a?vcs_url=https://codeload.github.com/UserNobody14/tree-sitter-dart/tar.gz/0fc19c3a57b1109802af41d2b8f60d8835c5da3a","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-go","version":"0.25.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"00f05cfc3ab7c73fdedf95e99216f56e5bb95205bed84dd1c8659ac1948235c6c6c1aee385240f4bc2ac52ea6ecc195af0f0a8f3e973f56fc90c98e67d16b6b7"}],"purl":"pkg:npm/tree-sitter-go@0.25.0","type":"library","bom-ref":"pkg:npm/tree-sitter-go@0.25.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-java","version":"0.23.5","scope":"optional","hashes":[{"alg":"SHA-512","content":"623bbba10d17c7b19c513d359948253cff9b61fbea8cd086771aa28139dec3d9cb1a8208e363cd54fddb1eb61e331b3088244cd32b9b5a6379aa4fb3b20d3330"}],"purl":"pkg:npm/tree-sitter-java@0.23.5","type":"library","bom-ref":"pkg:npm/tree-sitter-java@0.23.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-javascript","version":"0.23.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"fdb9e16eb4c3f5fad46074138989cf731c87391230d79ec44416ba76acda2b1bd1ff1dcf0b863377e0f5a5920c4baccd836bf76bc059d282bb8c7aac428f5f58"}],"purl":"pkg:npm/tree-sitter-javascript@0.23.1","type":"library","bom-ref":"pkg:npm/tree-sitter-javascript@0.23.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-javascript","version":"0.25.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"d5f09b9b302c919931719ccde35b056766ebda2a9360fded2a5b356ff1ca18d3d05153a9b14c69986c5d37fc0ca809378d866760147575dff2ff46af31e53b77"}],"purl":"pkg:npm/tree-sitter-javascript@0.25.0","type":"library","bom-ref":"pkg:npm/tree-sitter-javascript@0.25.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-kotlin","version":"0.3.8","scope":"optional","hashes":[{"alg":"SHA-512","content":"038a1baba6e3ce662b03e17424b2e885e14f7287c591cb4d6994a99c377e18f9f549f5592d8e3f186e02d1c61505339286e3c118600e3cb3352562d9415e26d6"}],"purl":"pkg:npm/tree-sitter-kotlin@0.3.8","type":"library","bom-ref":"pkg:npm/tree-sitter-kotlin@0.3.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-php","version":"0.24.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"cf080078f73f1e8ccd69638e7f044003ede9f3286e7a1470f056fbbe7e6a7765e388873ddee24faf20d33184d28c1463548520fca63aa4cdeb473b3c752c0a7f"}],"purl":"pkg:npm/tree-sitter-php@0.24.2","type":"library","bom-ref":"pkg:npm/tree-sitter-php@0.24.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-python","version":"0.25.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"782989c7acd06b7e46c5a0ad403fb05c73a162a07110bf9ba7bd56fecddf70332ed3a32bb739155d1e37edd46b0ab6eb0dbc8bb940c9a4083272cee772780b5f"}],"purl":"pkg:npm/tree-sitter-python@0.25.0","type":"library","bom-ref":"pkg:npm/tree-sitter-python@0.25.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-ruby","version":"0.23.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"77dfd15e05a347a1da9cdef04d8852e5ba4140bcf55641f4e3c566dc2a1d3c6c895676a65cc19bf281210f2a55081ab84271ee8bdb135ee24104fdd6b42c3944"}],"purl":"pkg:npm/tree-sitter-ruby@0.23.1","type":"library","bom-ref":"pkg:npm/tree-sitter-ruby@0.23.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-rust","version":"0.24.0","scope":"optional","hashes":[{"alg":"SHA-512","content":"3567a65037fadbd4df73dd18d19e79ceec0f0801e42f15a731fd91ce762ee2206492b425da8fc21c607b0abe764f23791750c0c7c1665270ed0b2f625245d911"}],"purl":"pkg:npm/tree-sitter-rust@0.24.0","type":"library","bom-ref":"pkg:npm/tree-sitter-rust@0.24.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-swift","version":"0.7.1","scope":"optional","hashes":[{"alg":"SHA-512","content":"a6778a553b866a6681b2aaaa7c1f41bcd42392dce1ff42153d1e788cb07916afc94c34306077745a8ea95726798c06296f3b73abeac9feba4bf6bbb14e64a82b"}],"purl":"pkg:npm/tree-sitter-swift@0.7.1","type":"library","bom-ref":"pkg:npm/tree-sitter-swift@0.7.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"tree-sitter-typescript","version":"0.23.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"7b4e095142b14d3e77ff1dd4ab5cc82f8e43a182957c71f8099ab081984f839a9844e9799d08d5fbce6bb85cc5199c6ef9078555b4533c3467a8bf546d4e1578"}],"purl":"pkg:npm/tree-sitter-typescript@0.23.2","type":"library","bom-ref":"pkg:npm/tree-sitter-typescript@0.23.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Max Brunsfeld ","group":"","name":"tree-sitter","version":"0.25.0","description":"Node.js bindings to the Tree-sitter parsing library","scope":"optional","hashes":[{"alg":"SHA-512","content":"3c6659cc55badde1256490defdbfd1fcb6ec8c30d825ae541232d9241e7d450b0c5fe7e8d23e787ea04f9f530629abff40d6b4251d3306255a8a46035828f929"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/tree-sitter@0.25.0","type":"library","bom-ref":"pkg:npm/tree-sitter@0.25.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Luke Plaster ","group":"","name":"treeify","version":"1.1.0","description":"converts a JS object into a nice and readable tree structure for the console","scope":"optional","hashes":[{"alg":"SHA-512","content":"d66e1103bc55009ad21abad71acd0bdd84f0caf06cd92f0f6d11da2d9024170ec947ca0817062dcacc6505985821aef14b289824a677886aeb939e5c180de2f4"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/treeify@1.1.0","externalReferences":[{"type":"vcs","url":"https://github.com/notatestuser/treeify.git"}],"type":"library","bom-ref":"pkg:npm/treeify@1.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Microsoft Corp.","group":"","name":"tslib","version":"1.14.1","description":"Runtime library for TypeScript helper functions","scope":"optional","hashes":[{"alg":"SHA-512","content":"5e78b7e4d2b38e032bc1ebf2b074c202bb4b0e93efc9ef3357fd04e04c989f8dcfeffeeabd0c0f87d0469077b06ccba5567b5b8a099c4fbadd5f704da3dc1126"}],"licenses":[{"license":{"id":"0BSD","url":"https://opensource.org/licenses/0BSD"}}],"purl":"pkg:npm/tslib@1.14.1","externalReferences":[{"type":"website","url":"https://www.typescriptlang.org/"},{"type":"vcs","url":"https://github.com/Microsoft/tslib.git"}],"type":"library","bom-ref":"pkg:npm/tslib@1.14.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Microsoft Corp.","group":"","name":"tslib","version":"2.8.1","description":"Runtime library for TypeScript helper functions","scope":"optional","hashes":[{"alg":"SHA-512","content":"a0916ef781d06fe29576e49440bef09e99aa9df98bb0e03f9c087a6fa107d30084a0ad3f98f79753a737c0a0d5f373243ae1cf447b525ca294f7d2016b34bfdb"}],"licenses":[{"license":{"id":"0BSD","url":"https://opensource.org/licenses/0BSD"}}],"purl":"pkg:npm/tslib@2.8.1","externalReferences":[{"type":"website","url":"https://www.typescriptlang.org/"},{"type":"vcs","url":"https://github.com/Microsoft/tslib.git"}],"type":"library","bom-ref":"pkg:npm/tslib@2.8.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mikeal Rogers (http://www.futurealoof.com)","group":"","name":"tunnel-agent","version":"0.6.0","description":"HTTP proxy tunneling agent. Formerly part of mikeal/request, now a standalone module.","scope":"optional","hashes":[{"alg":"SHA-512","content":"31c9cd895d65f1161e63cb41804a6ea1d082d662d475b48df826012fb909b093489ce3fc5230c3130764e8cc3ad2f74b2ebaf934729984c00e4ab476359b90fb"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/tunnel-agent@0.6.0","externalReferences":[{"type":"vcs","url":"https://github.com/mikeal/tunnel-agent"}],"type":"library","bom-ref":"pkg:npm/tunnel-agent@0.6.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"typanion","version":"3.14.0","description":"Simple runtime TypeScript validator library","scope":"optional","hashes":[{"alg":"SHA-512","content":"656fe554c45a6c44ee60277d3bd66f321021f06b254aa6948f198afc92cf0a1ea5ef70af2c18ae5ecc23ffeecb76758e818b10d77d05a8bcc5cf96864f823752"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/typanion@3.14.0","externalReferences":[{"type":"website","url":"https://mael.dev/typanion/"},{"type":"vcs","url":"https://github.com/arcanis/typanion"}],"type":"library","bom-ref":"pkg:npm/typanion@3.14.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"type-fest","version":"0.13.1","description":"A collection of essential TypeScript types","scope":"optional","hashes":[{"alg":"SHA-512","content":"df847b1d39c6d172097014a7e5784377b9cd14f45c5d8459ac10763b68dd2aa60e0e5752cc102acec5a865862f76e932ef7b68612fc44aac4fbe40dffc5d1732"}],"licenses":[{"expression":"(MIT OR CC0-1.0)"}],"purl":"pkg:npm/type-fest@0.13.1","type":"library","bom-ref":"pkg:npm/type-fest@0.13.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"type-fest","version":"0.21.3","description":"A collection of essential TypeScript types","scope":"optional","hashes":[{"alg":"SHA-512","content":"b74af306af3b9b77d571db870d41612a6cb25fef5ea3a5908d9bdfe7511afccd10efe4f7ef8269d5a522c9497418ac69f0cfce113547483be69323e0bd7f97db"}],"licenses":[{"expression":"(MIT OR CC0-1.0)"}],"purl":"pkg:npm/type-fest@0.21.3","type":"library","bom-ref":"pkg:npm/type-fest@0.21.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"type-fest","version":"4.41.0","description":"A collection of essential TypeScript types","scope":"optional","hashes":[{"alg":"SHA-512","content":"4de4d243a1f9607be9a95c0145c9cb0c20670ce1d662eec8bc66c74fa37c00eca672bf4f2468dcd464ed896653620d0d0a0630be761612454285002bf5b8dfc0"}],"licenses":[{"expression":"(MIT OR CC0-1.0)"}],"purl":"pkg:npm/type-fest@4.41.0","type":"library","bom-ref":"pkg:npm/type-fest@4.41.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"type-is","version":"2.0.1","description":"Infer the content-type of a request.","scope":"optional","hashes":[{"alg":"SHA-512","content":"399b3a82c8c5e2f329df6aab09b8954a4ac5997b46fc0661637b7488032b30188067257da002ed5cef21b2b1db31717dc7a2f782b945bb05b6a00cfb71abfe1b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/type-is@2.0.1","type":"library","bom-ref":"pkg:npm/type-is@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Daniel Lemire (http://lemire.me/en/)","group":"","name":"typedfastbitset","version":"0.6.1","description":"Speed-optimized BitSet implementation for modern browsers and JavaScript engines, using typed arrays","scope":"optional","hashes":[{"alg":"SHA-512","content":"f913f68dd7a15ecefbe3e96a8aeaa0fe0f437263de09d592f1ab5a8166ff8997fea58e810c6da61f95b00444ba9ea297877412b787498e6c283ac26ade249644"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/typedfastbitset@0.6.1","externalReferences":[{"type":"vcs","url":"https://github.com/lemire/TypedFastBitSet.js#readme"},{"type":"vcs","url":"git+https://github.com/lemire/TypedFastBitSet.js.git"}],"type":"library","bom-ref":"pkg:npm/typedfastbitset@0.6.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Microsoft Corp.","group":"","name":"typescript","version":"5.9.3","description":"TypeScript is a language for application scale JavaScript development","scope":"optional","hashes":[{"alg":"SHA-512","content":"8e5d6f6733c38a72ebf5e52ddc9feded5e8580d130f508ef04f772b33f4a7d00c3e357d0ac2d98e2f290762694a454f86d795bd511e12e9a7cc2d9ba3394e04b"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/typescript@5.9.3","externalReferences":[{"type":"website","url":"https://www.typescriptlang.org/"},{"type":"vcs","url":"https://github.com/microsoft/TypeScript.git"}],"type":"library","bom-ref":"pkg:npm/typescript@5.9.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/storage@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/storage/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/search@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/search/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/scanners@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/scanners/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/sarif@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/embedder@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/embedder/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/core-types@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/core-types/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/analysis@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/analysis/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Mihai Bazon (http://lisperator.net/)","group":"","name":"uglify-js","version":"3.19.3","description":"JavaScript parser, mangler/compressor and beautifier toolkit","scope":"optional","hashes":[{"alg":"SHA-512","content":"bf75eefb2bb00578aca7a4184dc1f851b1fec58257aa7ab69bf2ed41558a5b361cd627a16272e2c6840337d147ebf8fdfe8c9b7dde96f46870925f3e50c29329"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/uglify-js@3.19.3","type":"library","bom-ref":"pkg:npm/uglify-js@3.19.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"undici-types","version":"6.21.0","description":"A stand-alone types package for Undici","scope":"optional","hashes":[{"alg":"SHA-512","content":"8b00d9aa0d10006ae0f516afe47e27d0ceb87379a4479f5c27ac10a7eec2e2723482c984c5a79d6982cd3b8e1e4f802d041c236d38863cc96dd8c7744fd1fd25"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/undici-types@6.21.0","externalReferences":[{"type":"website","url":"https://undici.nodejs.org"},{"type":"vcs","url":"git+https://github.com/nodejs/undici.git"}],"type":"library","bom-ref":"pkg:npm/undici-types@6.21.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/storage@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/storage/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"unicorn-magic","version":"0.3.0","description":"Some useful utilities I often need","scope":"optional","hashes":[{"alg":"SHA-512","content":"f900415c10af89f739e9fb1bbb1650e9289cdf0aaa7375966aac6ce7c82f26b70eb8df371c64c2c33de84b9a61cd4f4bb6144d13d56b2421422d480779e1677c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/unicorn-magic@0.3.0","type":"library","bom-ref":"pkg:npm/unicorn-magic@0.3.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Ryan Zimmerman ","group":"","name":"universalify","version":"2.0.1","description":"Make a callback- or promise-based function support both promises and callbacks.","scope":"optional","hashes":[{"alg":"SHA-512","content":"829b4735082120d9dcfef4c6224d12385185357c3b255ae5454b42a2725196f6b0e83b97d303b925e928f6c5ab301861f8fb18019ee85c088e9dffd42a88328b"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/universalify@2.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/RyanZim/universalify#readme"},{"type":"vcs","url":"git+https://github.com/RyanZim/universalify.git"}],"type":"library","bom-ref":"pkg:npm/universalify@2.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"unpipe","version":"1.0.0","description":"Unpipe a stream from all destinations","scope":"optional","hashes":[{"alg":"SHA-512","content":"a63cb66d8852b2e7f05a52b03dcfa5ddc37bfb0b8994aeaecf461d2443a54036e5ea3a3f6253e2e266fc6a0524542f0117b57c36ecdec8f36a464b00de1ced29"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/unpipe@1.0.0","type":"library","bom-ref":"pkg:npm/unpipe@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Gary Court ","group":"","name":"uri-js","version":"4.4.1","description":"An RFC 3986/3987 compliant, scheme extendable URI/IRI parsing/validating/resolving library for JavaScript.","scope":"optional","hashes":[{"alg":"SHA-512","content":"eeb294cb2df7435c9cf7ca50d430262edc17d74f45ed321f5a55b561da3c5a5d628b549e1e279e8741c77cf78bd9f3172bacf4b3c79c2acf5fac2b8b26f9dd06"}],"licenses":[{"license":{"id":"BSD-2-Clause","url":"https://opensource.org/licenses/BSD-2-Clause"}}],"purl":"pkg:npm/uri-js@4.4.1","externalReferences":[{"type":"vcs","url":"https://github.com/garycourt/uri-js"},{"type":"vcs","url":"http://github.com/garycourt/uri-js"}],"type":"library","bom-ref":"pkg:npm/uri-js@4.4.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Nathan Rajlich (http://n8.io/)","group":"","name":"util-deprecate","version":"1.0.2","description":"The Node.js `util.deprecate()` function with browser support","scope":"optional","hashes":[{"alg":"SHA-512","content":"10f0f9ab5b97c85c49a42acb9c27359c79eade039ae83641a1c008888d93692080ed5089d5424331a802cc891736c5187c3d5d68afff2d3110f318886eb1ed73"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/util-deprecate@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/TooTallNate/util-deprecate"},{"type":"vcs","url":"git://github.com/TooTallNate/util-deprecate.git"}],"type":"library","bom-ref":"pkg:npm/util-deprecate@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"uuid","version":"14.0.0","description":"RFC9562 UUIDs","scope":"optional","hashes":[{"alg":"SHA-512","content":"428fae5a08a57d298085708c6afd6e605ca79503bb7cc16231566c42a6513085e9d0eeeb47baa3923f9c3ef0472e006a8bdeb4402a28fcf1f6ffa66d56a2afae"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/uuid@14.0.0","externalReferences":[{"type":"vcs","url":"https://github.com/uuidjs/uuid.git"}],"type":"library","bom-ref":"pkg:npm/uuid@14.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Kyle E. Mitchell (https://kemitchell.com)","group":"","name":"validate-npm-package-license","version":"3.0.4","description":"Give me a string and I'll tell you if it's a valid npm package license string","scope":"optional","hashes":[{"alg":"SHA-512","content":"0e92a6d948bfc4deff1d0282b69671a11581859f59d24aadca01bc5c280d43c6650e7c6e4265a18f9eba8fc7cde02bb7fc999b86c0e8edf70026ae2cf61dbb13"}],"licenses":[{"license":{"id":"Apache-2.0","url":"https://opensource.org/licenses/Apache-2.0"}}],"purl":"pkg:npm/validate-npm-package-license@3.0.4","type":"library","bom-ref":"pkg:npm/validate-npm-package-license@3.0.4","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Douglas Christopher Wilson ","group":"","name":"vary","version":"1.1.2","description":"Manipulate the HTTP Vary header","scope":"optional","hashes":[{"alg":"SHA-512","content":"04d19b58b7ddd1e50f69b8645d4566d23f2ebaf444c93879a2f45afddca8c3f06a01b649c82fb97d4f88cd03b39802b362a6110084a8461750af778867f3d7aa"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/vary@1.1.2","type":"library","bom-ref":"pkg:npm/vary@1.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Tim Oxley","group":"","name":"wcwidth","version":"1.0.1","description":"Port of C's wcwidth() and wcswidth()","scope":"optional","hashes":[{"alg":"SHA-512","content":"5c73c4c12d2ae936b172f1bce7ef046246e20aec765ed586da691ce3b360d80efb050bbdf83a8838995d493e0780f92e79aeddbca4a3e55817dcfd5de2b5bc4e"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wcwidth@1.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/timoxley/wcwidth#readme"},{"type":"vcs","url":"git+https://github.com/timoxley/wcwidth.git"}],"type":"library","bom-ref":"pkg:npm/wcwidth@1.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Max Brunsfeld ","group":"","name":"web-tree-sitter","version":"0.26.8","description":"Tree-sitter bindings for the web","scope":"optional","hashes":[{"alg":"SHA-512","content":"e2c5308bb6723ab224e4a2e060b91cd80fc5d0b14c4270617dbfb609d97b8a4e1e3c9e890fe7e4e287c8dac0397866b004a05a2b8567b6ded6c3929c12c6b2e0"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/web-tree-sitter@0.26.8","externalReferences":[{"type":"vcs","url":"git+https://github.com/tree-sitter/tree-sitter.git"}],"type":"library","bom-ref":"pkg:npm/web-tree-sitter@0.26.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"which","version":"1.3.1","description":"Like which(1) unix command. Find the first instance of an executable in the PATH.","scope":"optional","hashes":[{"alg":"SHA-512","content":"1f125d616ab53132106c9de7c3472ab2c1e84cd536ebb2a5ac3b866755989710d2b54b4a52139a266875d76fd36661f1c547ee26a3d748e9bbb43c9ab3439221"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/which@1.3.1","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/node-which.git"}],"type":"library","bom-ref":"pkg:npm/which@1.3.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me)","group":"","name":"which","version":"2.0.2","description":"Like which(1) unix command. Find the first instance of an executable in the PATH.","scope":"optional","hashes":[{"alg":"SHA-512","content":"04b2374e5d535b73ef97bd25df2ab763ae22f9ac29c17aac181616924a8cb676d782b303fb28fbae15b492e103c7325a6171a3116e6881aa4a34c10a34c8e26c"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/which@2.0.2","externalReferences":[{"type":"vcs","url":"git://github.com/isaacs/node-which.git"}],"type":"library","bom-ref":"pkg:npm/which@2.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"widest-line","version":"5.0.0","description":"Get the visual width of the widest line in a string - the number of columns required to display it","scope":"optional","hashes":[{"alg":"SHA-512","content":"73d6d9a7b6f962d463db039ee9d963df630af81c7f33f77ef550764873353adb141d1d1a574b5d3fa0d687f88cb749168b5b7983522e76eea141135dd40e0f54"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/widest-line@5.0.0","type":"library","bom-ref":"pkg:npm/widest-line@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Jon Schlinkert (https://github.com/jonschlinkert)","group":"","name":"word-wrap","version":"1.2.5","description":"Wrap words to a specified length.","scope":"optional","hashes":[{"alg":"SHA-512","content":"04ddb607979a30c23d50cb63ac677983978260fa423c3532d052576d8b1a4f9cd8c6314e7244b9dd2403137a56915a16a475d56f706b61c10de13c1ae7907970"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/word-wrap@1.2.5","externalReferences":[{"type":"vcs","url":"https://github.com/jonschlinkert/word-wrap"}],"type":"library","bom-ref":"pkg:npm/word-wrap@1.2.5","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"James Halliday (http://substack.net)","group":"","name":"wordwrap","version":"1.0.0","description":"Wrap those words. Show them at what columns to start and stop.","scope":"optional","hashes":[{"alg":"SHA-512","content":"82f57324594fc9c29ce5d64de323e43fcc3b0dcdfb06d3f5c9ccc49de39be2eab7e295d972faed45399657c5be5267be5c2c4a81b8ccfa77af93214f3326dde1"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wordwrap@1.0.0","externalReferences":[{"type":"vcs","url":"git://github.com/substack/node-wordwrap.git"}],"type":"library","bom-ref":"pkg:npm/wordwrap@1.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"wrap-ansi","version":"10.0.0","description":"Wordwrap a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"48672f83cd1fd30532dbf7d7112d7d7de1cccfc1342685efdae3601cebb80e08b63ab0b2d65ab0158109cf504b6c32347b18cf31efd977367f6291840811bf69"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wrap-ansi@10.0.0","type":"library","bom-ref":"pkg:npm/wrap-ansi@10.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (sindresorhus.com)","group":"","name":"wrap-ansi","version":"6.2.0","description":"Wordwrap a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"afa94f7011b1657948732984bbb227c43321756d0a0f1a4b82814b720b9ab3109a27f48e219c0835ab4af4a63fb5ff99ae5cb038a5345038f70135d405fc495c"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wrap-ansi@6.2.0","type":"library","bom-ref":"pkg:npm/wrap-ansi@6.2.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"wrap-ansi","version":"7.0.0","description":"Wordwrap a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"6151888f691a98b493c70e8db198e80717d2c2c9f4c9c75eb26738a7e436d5ce733ee675a65f8d7f155dc4fb5d1ef98d54e43a5d2606e0052dcadfc58bb0f5e9"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wrap-ansi@7.0.0","type":"library","bom-ref":"pkg:npm/wrap-ansi@7.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"wrap-ansi","version":"8.1.0","description":"Wordwrap a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"b22ed0588eb350cab9e9b11216f6a0b66ccc7463ada317d1f927b3d753286df73bb66f9591472493d6d6d9479f7d319551b3a4b31992c34000da0b3c83bd4d09"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wrap-ansi@8.1.0","type":"library","bom-ref":"pkg:npm/wrap-ansi@8.1.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"wrap-ansi","version":"9.0.2","description":"Wordwrap a string with ANSI escape codes","scope":"optional","hashes":[{"alg":"SHA-512","content":"e3602d9a0aa357e5f556974e7f24c6398462d3fceca0baad5d07244e6a937b26d3f810c86ccfc6bb1a3bc77a44dafb69af5a24eb146a33d3a905ef89ca8ab2c3"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/wrap-ansi@9.0.2","type":"library","bom-ref":"pkg:npm/wrap-ansi@9.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"wrappy","version":"1.0.2","description":"Callback wrapping utility","scope":"optional","hashes":[{"alg":"SHA-512","content":"9784a9fc346c7a8afdc0be84bd5dbe4ee427eb774c90f8d9feca7d5e48214c46d5f4a94f4b5c54b19deeeff2103b8c31b5c141e1b82940f45c477402bdeccf71"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/wrappy@1.0.2","externalReferences":[{"type":"vcs","url":"https://github.com/npm/wrappy"},{"type":"vcs","url":"https://github.com/npm/wrappy"}],"type":"library","bom-ref":"pkg:npm/wrappy@1.0.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"GitHub Inc.","group":"","name":"write-file-atomic","version":"7.0.1","description":"Write files in an atomic fashion w/configurable ownership","scope":"required","hashes":[{"alg":"SHA-512","content":"393224f2247cfda091581aafc6bcf1474860c56a676016e56354b984358141f93f545989c3398981014ddd6b2850a1c84afd9e0307be3e96d4cf22f508a4cb5e"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/write-file-atomic@7.0.1","externalReferences":[{"type":"vcs","url":"https://github.com/npm/write-file-atomic"},{"type":"vcs","url":"git+https://github.com/npm/write-file-atomic.git"}],"type":"library","bom-ref":"pkg:npm/write-file-atomic@7.0.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/analysis/package.json"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"cdx:npm:package_json","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/analysis@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/analysis/package.json"},{"name":"ImportedModules","value":"write-file-atomic,default,write-file-atomic/default"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/analysis/dist/risk-snapshot.js#16"},{"location":"packages/analysis/dist/wiki.js#14"},{"location":"packages/cli/dist/fs-atomic.js#12"},{"location":"packages/ingestion/dist/pipeline/phases/content-cache.js#32"},{"location":"packages/ingestion/dist/pipeline/phases/sbom.js#41"},{"location":"packages/analysis/src/risk-snapshot.ts#19"},{"location":"packages/analysis/src/wiki.ts#16"},{"location":"packages/cli/src/fs-atomic.ts#13"},{"location":"packages/ingestion/src/pipeline/phases/content-cache.ts#33"},{"location":"packages/ingestion/src/pipeline/phases/sbom.ts#43"}]}},{"author":"Ben Coe ","group":"","name":"y18n","version":"5.0.8","description":"the bare-bones internationalization library used by yargs","scope":"optional","hashes":[{"alg":"SHA-512","content":"d297c5cde81e0d62472480264cb44fd83c078dd179b3b8e8f6dbb3b5d43102120d09dbd2fb79c620da8f774d00a61a8947fd0b8403544baffeed209bf7c60e7c"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/y18n@5.0.8","externalReferences":[{"type":"vcs","url":"https://github.com/yargs/y18n"}],"type":"library","bom-ref":"pkg:npm/y18n@5.0.8","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Isaac Z. Schlueter (http://blog.izs.me/)","group":"","name":"yallist","version":"5.0.0","description":"Yet Another Linked List","scope":"optional","hashes":[{"alg":"SHA-512","content":"620bd44dfc2ac9ced45d532b07e4889ac5584a64d2f17fed4abb5d35930898cfa7efe413ae2457c978a6d2606b4d735eab3545d0a5868073de8b2562145acd0f"}],"licenses":[{"license":{"id":"BlueOak-1.0.0","url":"https://opensource.org/licenses/BlueOak-1.0.0"}}],"purl":"pkg:npm/yallist@5.0.0","externalReferences":[{"type":"vcs","url":"git+https://github.com/isaacs/yallist.git"}],"type":"library","bom-ref":"pkg:npm/yallist@5.0.0","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Eemeli Aro ","group":"","name":"yaml","version":"2.8.3","description":"JavaScript parser and stringifier for YAML","scope":"required","hashes":[{"alg":"SHA-512","content":"02f6da08b38ed8eb70fe55b96e687d77f58475c0c5750a766766541f7a57f54da2872518d27bcbbfb27a4eb5a8c2495118f61b07f22e20c7d88316823e0e41a6"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/yaml@2.8.3","externalReferences":[{"type":"website","url":"https://eemeli.org/yaml/"}],"type":"library","bom-ref":"pkg:npm/yaml@2.8.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:has_binary","value":"true"},{"name":"cdx:npm:package_json","value":"packages/cli/package.json"},{"name":"cdx:npm:package_json","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/sarif@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/cli@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/cli/package.json"},{"name":"ImportedModules","value":"yaml,parse,yaml/parse"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/sarif/dist/suppressions.js#24"},{"location":"packages/sarif/src/suppressions.ts#25"}]}},{"author":"Ben Coe ","group":"","name":"yargs-parser","version":"21.1.1","description":"the mighty option parser used by yargs","scope":"optional","hashes":[{"alg":"SHA-512","content":"b55a6c256ec376379c0221696c80757b7ab1210b04e8da0f739fde4ddadb6c80b88742d5b16867a1ade0fa6d87725048ba31f3b31678549540f8652e736fcb07"}],"licenses":[{"license":{"id":"ISC","url":"https://opensource.org/licenses/ISC"}}],"purl":"pkg:npm/yargs-parser@21.1.1","externalReferences":[{"type":"vcs","url":"https://github.com/yargs/yargs-parser.git"}],"type":"library","bom-ref":"pkg:npm/yargs-parser@21.1.1","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"yargs","version":"17.7.2","description":"yargs the modern, pirate-themed, successor to optimist.","scope":"optional","hashes":[{"alg":"SHA-512","content":"edd4b3cd143ef822a7348fe4aca9d8455ec928a3d45cc121eb5b286872a0f66ad6121cc55a1167c4fc4697eebd703d4ebbadc2d773543c29e621caefa82b8ceb"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/yargs@17.7.2","externalReferences":[{"type":"website","url":"https://yargs.js.org/"},{"type":"vcs","url":"https://github.com/yargs/yargs.git"}],"type":"library","bom-ref":"pkg:npm/yargs@17.7.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"yoctocolors-cjs","version":"2.1.3","description":"CommonJS version - The smallest and fastest command-line coloring package on the internet","scope":"optional","hashes":[{"alg":"SHA-512","content":"53f3c1b437f7e5f7f40fc5fc0f48df7731d810f171008ee3265c595f00927b3e4cdf5f749be4286c87e1fac5835921cc0965893760166a691e827eafafa6014f"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/yoctocolors-cjs@2.1.3","type":"library","bom-ref":"pkg:npm/yoctocolors-cjs@2.1.3","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Sindre Sorhus (https://sindresorhus.com)","group":"","name":"yoctocolors","version":"2.1.2","description":"The smallest and fastest command-line coloring package on the internet","scope":"optional","hashes":[{"alg":"SHA-512","content":"0b384efa914da3c6a32ccd9dd885bf47dde2a72f7d2d68edc1b96f0b546ca1250c660c8b6d816bdb6d539d2353ec68c6758ba2e8fe39f66c3d247fe0ff35b6ba"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/yoctocolors@2.1.2","type":"library","bom-ref":"pkg:npm/yoctocolors@2.1.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/ingestion@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/ingestion/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"group":"","name":"zod-to-json-schema","version":"3.25.2","scope":"optional","hashes":[{"alg":"SHA-512","content":"3bf3e07e7a53d712920de4184827d12391b2de13dff7598a543b982d41d924c883169b6f3f8d4c4a75a87e6f1d9c29b4db9e9935f64833b0d2cee48c0059e31c"}],"purl":"pkg:npm/zod-to-json-schema@3.25.2","type":"library","bom-ref":"pkg:npm/zod-to-json-schema@3.25.2","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]}}},{"author":"Colin McDonnell ","group":"","name":"zod","version":"3.25.76","description":"TypeScript-first schema declaration and validation library with static type inference","scope":"required","hashes":[{"alg":"SHA-512","content":"83352dfeab7cd675ec14628815c0b76277c4031e4d92e9c27e70e5bee0524854b4d9b717bb82e679ad001485306cb5b158fc7777da7c4b94286ae8ca70d43171"}],"licenses":[{"license":{"id":"MIT","url":"https://opensource.org/licenses/MIT"}}],"purl":"pkg:npm/zod@3.25.76","externalReferences":[{"type":"website","url":"https://zod.dev"},{"type":"vcs","url":"git+https://github.com/colinhacks/zod.git"}],"type":"library","bom-ref":"pkg:npm/zod@3.25.76","properties":[{"name":"SrcFile","value":"pnpm-lock.yaml"},{"name":"cdx:npm:package_json","value":"packages/mcp/package.json"},{"name":"cdx:npm:package_json","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/sarif@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/sarif/package.json"},{"name":"internal:workspaceRef","value":"pkg:npm/@opencodehub/mcp@0.1.0"},{"name":"internal:workspaceSrcFile","value":"packages/mcp/package.json"},{"name":"ImportedModules","value":"zod,zod/z"}],"evidence":{"identity":{"field":"purl","confidence":1,"methods":[{"technique":"manifest-analysis","confidence":1,"value":"/Users/lalsaado/Projects/open-code-hub/pnpm-lock.yaml"}]},"occurrences":[{"location":"packages/mcp/dist/prompts/audit-dependencies.js#8"},{"location":"packages/mcp/dist/prompts/detect-impact.js#9"},{"location":"packages/mcp/dist/prompts/explore-area.js#8"},{"location":"packages/mcp/dist/prompts/review-pr.js#9"},{"location":"packages/mcp/dist/tools/api-impact.js#23"},{"location":"packages/mcp/dist/tools/context.js#21"},{"location":"packages/mcp/dist/tools/dependencies.js#18"},{"location":"packages/mcp/dist/tools/detect-changes.js#4"},{"location":"packages/mcp/dist/tools/group-contracts.js#21"},{"location":"packages/mcp/dist/tools/group-query.js#32"},{"location":"packages/mcp/dist/tools/group-status.js#15"},{"location":"packages/mcp/dist/tools/impact.js#9"},{"location":"packages/mcp/dist/tools/license-audit.js#26"},{"location":"packages/mcp/dist/tools/list-dead-code.js#15"},{"location":"packages/mcp/dist/tools/list-findings-delta.js#31"},{"location":"packages/mcp/dist/tools/list-findings.js#17"},{"location":"packages/mcp/dist/tools/owners.js#13"},{"location":"packages/mcp/dist/tools/project-profile.js#13"},{"location":"packages/mcp/dist/tools/query.js#23"},{"location":"packages/mcp/dist/tools/remove-dead-code.js#25"},{"location":"packages/mcp/dist/tools/rename.js#8"},{"location":"packages/mcp/dist/tools/risk-trends.js#11"},{"location":"packages/mcp/dist/tools/route-map.js#16"},{"location":"packages/mcp/dist/tools/scan.js#20"},{"location":"packages/mcp/dist/tools/shape-check.js#23"},{"location":"packages/mcp/dist/tools/signature.js#26"},{"location":"packages/mcp/dist/tools/sql.js#15"},{"location":"packages/mcp/dist/tools/tool-map.js#15"},{"location":"packages/mcp/dist/tools/verdict.js#14"},{"location":"packages/sarif/dist/schemas.js#21"},{"location":"packages/mcp/src/prompts/audit-dependencies.ts#10"},{"location":"packages/mcp/src/prompts/detect-impact.ts#11"},{"location":"packages/mcp/src/prompts/explore-area.ts#10"},{"location":"packages/mcp/src/prompts/review-pr.ts#11"},{"location":"packages/mcp/src/tools/api-impact.ts#26"},{"location":"packages/mcp/src/tools/context.ts#23"},{"location":"packages/mcp/src/tools/dependencies.ts#20"},{"location":"packages/mcp/src/tools/detect-changes.ts#6"},{"location":"packages/mcp/src/tools/group-contracts.ts#24"},{"location":"packages/mcp/src/tools/group-query.ts#34"},{"location":"packages/mcp/src/tools/group-status.ts#18"},{"location":"packages/mcp/src/tools/impact.ts#11"},{"location":"packages/mcp/src/tools/license-audit.ts#28"},{"location":"packages/mcp/src/tools/list-dead-code.ts#17"},{"location":"packages/mcp/src/tools/list-findings-delta.ts#39"},{"location":"packages/mcp/src/tools/list-findings.ts#19"},{"location":"packages/mcp/src/tools/owners.ts#15"},{"location":"packages/mcp/src/tools/project-profile.ts#15"},{"location":"packages/mcp/src/tools/query.ts#28"},{"location":"packages/mcp/src/tools/remove-dead-code.ts#33"},{"location":"packages/mcp/src/tools/rename.ts#10"},{"location":"packages/mcp/src/tools/risk-trends.ts#13"},{"location":"packages/mcp/src/tools/route-map.ts#18"},{"location":"packages/mcp/src/tools/scan.ts#30"},{"location":"packages/mcp/src/tools/shape-check.ts#26"},{"location":"packages/mcp/src/tools/signature.ts#28"},{"location":"packages/mcp/src/tools/sql.ts#17"},{"location":"packages/mcp/src/tools/tool-map.ts#17"},{"location":"packages/mcp/src/tools/verdict.ts#16"},{"location":"packages/sarif/src/schemas.ts#22"}]}}],"dependencies":[{"ref":"pkg:npm/@opencodehub/analysis@0.1.0","dependsOn":["pkg:npm/@iarna/toml@2.2.5","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/sarif@0.1.0","pkg:npm/@opencodehub/storage@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/@types/write-file-atomic@4.0.3","pkg:npm/typescript@5.9.3","pkg:npm/write-file-atomic@7.0.1"]},{"ref":"pkg:npm/@opencodehub/cli@0.1.0","dependsOn":["pkg:npm/@iarna/toml@2.2.5","pkg:npm/@opencodehub/analysis@0.1.0","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/embedder@0.1.0","pkg:npm/@opencodehub/ingestion@0.1.0","pkg:npm/@opencodehub/mcp@0.1.0","pkg:npm/@opencodehub/sarif@0.1.0","pkg:npm/@opencodehub/scanners@0.1.0","pkg:npm/@opencodehub/search@0.1.0","pkg:npm/@opencodehub/storage@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/@types/write-file-atomic@4.0.3","pkg:npm/cli-table3@0.6.5","pkg:npm/commander@14.0.3","pkg:npm/envinfo@7.21.0","pkg:npm/listr2@10.2.1","pkg:npm/typescript@5.9.3","pkg:npm/write-file-atomic@7.0.1","pkg:npm/yaml@2.8.3"]},{"ref":"pkg:npm/@opencodehub/core-types@0.1.0","dependsOn":["pkg:npm/@types/node@22.19.17","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/@opencodehub/embedder@0.1.0","dependsOn":["pkg:npm/@huggingface/tokenizers@0.1.3","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/onnxruntime-node@1.24.3","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/@opencodehub/ingestion@0.1.0","dependsOn":["pkg:npm/@apidevtools/swagger-parser@12.1.0","pkg:npm/@cyclonedx/cyclonedx-library@10.0.0","pkg:npm/@graphty/algorithms@1.7.1","pkg:npm/@iarna/toml@2.2.5","pkg:npm/@opencodehub/analysis@0.1.0","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/embedder@0.1.0","pkg:npm/@opencodehub/storage@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/@types/spdx-correct@3.1.3","pkg:npm/@types/write-file-atomic@4.0.3","pkg:npm/ajv-formats-draft2019@1.6.1","pkg:npm/ajv-formats@3.0.1","pkg:npm/ajv@8.18.0","pkg:npm/fast-xml-parser@5.7.1","pkg:npm/graphology-dag@0.4.1","pkg:npm/graphology@0.26.0","pkg:npm/piscina@5.1.4","pkg:npm/snyk-nodejs-lockfile-parser@2.7.0","pkg:npm/spdx-correct@3.2.0","pkg:npm/tree-sitter-c-sharp@0.23.5","pkg:npm/tree-sitter-c@0.24.1","pkg:npm/tree-sitter-cpp@0.23.4","pkg:npm/tree-sitter-dart@https://codeload.github.com/UserNobody14/tree-sitter-dart/tar.gz/0fc19c3a57b1109802af41d2b8f60d8835c5da3a","pkg:npm/tree-sitter-go@0.25.0","pkg:npm/tree-sitter-java@0.23.5","pkg:npm/tree-sitter-javascript@0.25.0","pkg:npm/tree-sitter-kotlin@0.3.8","pkg:npm/tree-sitter-php@0.24.2","pkg:npm/tree-sitter-python@0.25.0","pkg:npm/tree-sitter-ruby@0.23.1","pkg:npm/tree-sitter-rust@0.24.0","pkg:npm/tree-sitter-swift@0.7.1","pkg:npm/tree-sitter-typescript@0.23.2","pkg:npm/tree-sitter@0.25.0","pkg:npm/typescript@5.9.3","pkg:npm/web-tree-sitter@0.26.8","pkg:npm/write-file-atomic@7.0.1"]},{"ref":"pkg:npm/@opencodehub/mcp@0.1.0","dependsOn":["pkg:npm/@modelcontextprotocol/sdk@1.29.0","pkg:npm/@opencodehub/analysis@0.1.0","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/embedder@0.1.0","pkg:npm/@opencodehub/sarif@0.1.0","pkg:npm/@opencodehub/scanners@0.1.0","pkg:npm/@opencodehub/search@0.1.0","pkg:npm/@opencodehub/storage@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/lru-cache@11.3.5","pkg:npm/typescript@5.9.3","pkg:npm/zod@3.25.76"]},{"ref":"pkg:npm/@opencodehub/sarif@0.1.0","dependsOn":["pkg:npm/@types/node@22.19.17","pkg:npm/@types/sarif@2.1.7","pkg:npm/typescript@5.9.3","pkg:npm/yaml@2.8.3","pkg:npm/zod@3.25.76"]},{"ref":"pkg:npm/@opencodehub/scanners@0.1.0","dependsOn":["pkg:npm/@opencodehub/sarif@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/@opencodehub/search@0.1.0","dependsOn":["pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/storage@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/@opencodehub/storage@0.1.0","dependsOn":["pkg:npm/@duckdb/node-api@1.5.2-r.1","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@types/node@22.19.17","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/opencodehub-monorepo@0.1.1","dependsOn":["pkg:npm/@opencodehub/analysis@0.1.0","pkg:npm/@opencodehub/cli@0.1.0","pkg:npm/@opencodehub/core-types@0.1.0","pkg:npm/@opencodehub/embedder@0.1.0","pkg:npm/@opencodehub/ingestion@0.1.0","pkg:npm/@opencodehub/mcp@0.1.0","pkg:npm/@opencodehub/sarif@0.1.0","pkg:npm/@opencodehub/scanners@0.1.0","pkg:npm/@opencodehub/search@0.1.0","pkg:npm/@opencodehub/storage@0.1.0"]},{"ref":"pkg:npm/@apidevtools/json-schema-ref-parser@14.0.1","dependsOn":["pkg:npm/@types/json-schema@7.0.15","pkg:npm/js-yaml@4.1.1"]},{"ref":"pkg:npm/@apidevtools/openapi-schemas@2.1.0","dependsOn":[]},{"ref":"pkg:npm/@apidevtools/swagger-methods@3.0.2","dependsOn":[]},{"ref":"pkg:npm/@apidevtools/swagger-parser@12.1.0","dependsOn":["pkg:npm/@apidevtools/json-schema-ref-parser@14.0.1","pkg:npm/@apidevtools/openapi-schemas@2.1.0","pkg:npm/@apidevtools/swagger-methods@3.0.2","pkg:npm/ajv-draft-04@1.0.0","pkg:npm/ajv@8.18.0","pkg:npm/call-me-maybe@1.0.2","pkg:npm/openapi-types@12.1.3"]},{"ref":"pkg:npm/@arcanis/slice-ansi@1.1.1","dependsOn":["pkg:npm/grapheme-splitter@1.0.4"]},{"ref":"pkg:npm/@babel/code-frame@7.29.0","dependsOn":["pkg:npm/@babel/helper-validator-identifier@7.28.5","pkg:npm/js-tokens@4.0.0","pkg:npm/picocolors@1.1.1"]},{"ref":"pkg:npm/@babel/helper-validator-identifier@7.28.5","dependsOn":[]},{"ref":"pkg:npm/@biomejs/biome@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-darwin-arm64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-darwin-x64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-linux-arm64-musl@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-linux-arm64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-linux-x64-musl@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-linux-x64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-win32-arm64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@biomejs/cli-win32-x64@2.4.12","dependsOn":[]},{"ref":"pkg:npm/@colors/colors@1.5.0","dependsOn":[]},{"ref":"pkg:npm/@commitlint/cli@20.5.0","dependsOn":["pkg:npm/@commitlint/format@20.5.0","pkg:npm/@commitlint/lint@20.5.0","pkg:npm/@commitlint/load@20.5.0","pkg:npm/@commitlint/read@20.5.0","pkg:npm/@commitlint/types@20.5.0","pkg:npm/tinyexec@1.1.1","pkg:npm/yargs@17.7.2"]},{"ref":"pkg:npm/@commitlint/config-conventional@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/conventional-changelog-conventionalcommits@9.3.1"]},{"ref":"pkg:npm/@commitlint/config-validator@19.5.0","dependsOn":["pkg:npm/@commitlint/types@19.5.0","pkg:npm/ajv@8.18.0"]},{"ref":"pkg:npm/@commitlint/config-validator@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/ajv@8.18.0"]},{"ref":"pkg:npm/@commitlint/ensure@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/lodash.camelcase@4.3.0","pkg:npm/lodash.kebabcase@4.1.1","pkg:npm/lodash.snakecase@4.1.1","pkg:npm/lodash.startcase@4.4.0","pkg:npm/lodash.upperfirst@4.3.1"]},{"ref":"pkg:npm/@commitlint/execute-rule@19.5.0","dependsOn":[]},{"ref":"pkg:npm/@commitlint/execute-rule@20.0.0","dependsOn":[]},{"ref":"pkg:npm/@commitlint/format@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/picocolors@1.1.1"]},{"ref":"pkg:npm/@commitlint/is-ignored@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/semver@7.7.4"]},{"ref":"pkg:npm/@commitlint/lint@20.5.0","dependsOn":["pkg:npm/@commitlint/is-ignored@20.5.0","pkg:npm/@commitlint/parse@20.5.0","pkg:npm/@commitlint/rules@20.5.0","pkg:npm/@commitlint/types@20.5.0"]},{"ref":"pkg:npm/@commitlint/load@19.6.1","dependsOn":["pkg:npm/@commitlint/config-validator@19.5.0","pkg:npm/@commitlint/execute-rule@19.5.0","pkg:npm/@commitlint/resolve-extends@19.5.0","pkg:npm/@commitlint/types@19.5.0","pkg:npm/chalk@5.3.0","pkg:npm/cosmiconfig-typescript-loader@6.1.0","pkg:npm/cosmiconfig@9.0.0","pkg:npm/lodash.isplainobject@4.0.6","pkg:npm/lodash.merge@4.6.2","pkg:npm/lodash.uniq@4.5.0"]},{"ref":"pkg:npm/@commitlint/load@20.5.0","dependsOn":["pkg:npm/@commitlint/config-validator@20.5.0","pkg:npm/@commitlint/execute-rule@20.0.0","pkg:npm/@commitlint/resolve-extends@20.5.0","pkg:npm/@commitlint/types@20.5.0","pkg:npm/cosmiconfig-typescript-loader@6.1.0","pkg:npm/cosmiconfig@9.0.1","pkg:npm/is-plain-obj@4.1.0","pkg:npm/lodash.mergewith@4.6.2","pkg:npm/picocolors@1.1.1"]},{"ref":"pkg:npm/@commitlint/message@20.4.3","dependsOn":[]},{"ref":"pkg:npm/@commitlint/parse@20.5.0","dependsOn":["pkg:npm/@commitlint/types@20.5.0","pkg:npm/conventional-changelog-angular@8.3.1","pkg:npm/conventional-commits-parser@6.4.0"]},{"ref":"pkg:npm/@commitlint/read@20.5.0","dependsOn":["pkg:npm/@commitlint/top-level@20.4.3","pkg:npm/@commitlint/types@20.5.0","pkg:npm/git-raw-commits@5.0.1","pkg:npm/minimist@1.2.8","pkg:npm/tinyexec@1.1.1"]},{"ref":"pkg:npm/@commitlint/resolve-extends@19.5.0","dependsOn":["pkg:npm/@commitlint/config-validator@19.5.0","pkg:npm/@commitlint/types@19.5.0","pkg:npm/global-directory@4.0.1","pkg:npm/import-meta-resolve@4.1.0","pkg:npm/lodash.mergewith@4.6.2","pkg:npm/resolve-from@5.0.0"]},{"ref":"pkg:npm/@commitlint/resolve-extends@20.5.0","dependsOn":["pkg:npm/@commitlint/config-validator@20.5.0","pkg:npm/@commitlint/types@20.5.0","pkg:npm/global-directory@4.0.1","pkg:npm/import-meta-resolve@4.1.0","pkg:npm/lodash.mergewith@4.6.2","pkg:npm/resolve-from@5.0.0"]},{"ref":"pkg:npm/@commitlint/rules@20.5.0","dependsOn":["pkg:npm/@commitlint/ensure@20.5.0","pkg:npm/@commitlint/message@20.4.3","pkg:npm/@commitlint/to-lines@20.0.0","pkg:npm/@commitlint/types@20.5.0"]},{"ref":"pkg:npm/@commitlint/to-lines@20.0.0","dependsOn":[]},{"ref":"pkg:npm/@commitlint/top-level@20.4.3","dependsOn":["pkg:npm/escalade@3.2.0"]},{"ref":"pkg:npm/@commitlint/types@19.5.0","dependsOn":["pkg:npm/@types/conventional-commits-parser@5.0.1","pkg:npm/chalk@5.3.0"]},{"ref":"pkg:npm/@commitlint/types@20.5.0","dependsOn":["pkg:npm/conventional-commits-parser@6.4.0","pkg:npm/picocolors@1.1.1"]},{"ref":"pkg:npm/@conventional-changelog/git-client@2.7.0","dependsOn":["pkg:npm/@simple-libs/child-process-utils@1.0.2","pkg:npm/@simple-libs/stream-utils@1.2.0","pkg:npm/semver@7.7.4"]},{"ref":"pkg:npm/@cyclonedx/cyclonedx-library@10.0.0","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-api@1.5.2-r.1","dependsOn":["pkg:npm/@duckdb/node-bindings@1.5.2-r.1"]},{"ref":"pkg:npm/@duckdb/node-bindings-darwin-arm64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings-darwin-x64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings-linux-arm64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings-linux-x64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings-win32-arm64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings-win32-x64@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@duckdb/node-bindings@1.5.2-r.1","dependsOn":[]},{"ref":"pkg:npm/@graphty/algorithms@1.7.1","dependsOn":["pkg:npm/pupt@1.4.1","pkg:npm/typedfastbitset@0.6.1"]},{"ref":"pkg:npm/@homebridge/node-pty-prebuilt-multiarch@0.11.14","dependsOn":["pkg:npm/nan@2.26.2","pkg:npm/prebuild-install@7.1.3"]},{"ref":"pkg:npm/@hono/node-server@1.19.14","dependsOn":["pkg:npm/hono@4.12.14"]},{"ref":"pkg:npm/@huggingface/tokenizers@0.1.3","dependsOn":[]},{"ref":"pkg:npm/@iarna/toml@2.2.5","dependsOn":[]},{"ref":"pkg:npm/@inquirer/ansi@1.0.2","dependsOn":[]},{"ref":"pkg:npm/@inquirer/checkbox@4.3.2","dependsOn":["pkg:npm/@inquirer/ansi@1.0.2","pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/figures@1.0.15","pkg:npm/@inquirer/type@3.0.10","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/confirm@5.1.21","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10"]},{"ref":"pkg:npm/@inquirer/core@10.3.2","dependsOn":["pkg:npm/@inquirer/ansi@1.0.2","pkg:npm/@inquirer/figures@1.0.15","pkg:npm/@inquirer/type@3.0.10","pkg:npm/cli-width@4.1.0","pkg:npm/mute-stream@2.0.0","pkg:npm/signal-exit@4.1.0","pkg:npm/wrap-ansi@6.2.0","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/editor@4.2.23","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/external-editor@1.0.3","pkg:npm/@inquirer/type@3.0.10"]},{"ref":"pkg:npm/@inquirer/expand@4.0.23","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/external-editor@1.0.3","dependsOn":["pkg:npm/chardet@2.1.1","pkg:npm/iconv-lite@0.7.2"]},{"ref":"pkg:npm/@inquirer/figures@1.0.15","dependsOn":[]},{"ref":"pkg:npm/@inquirer/input@4.3.1","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10"]},{"ref":"pkg:npm/@inquirer/number@3.0.23","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10"]},{"ref":"pkg:npm/@inquirer/password@4.0.23","dependsOn":["pkg:npm/@inquirer/ansi@1.0.2","pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10"]},{"ref":"pkg:npm/@inquirer/prompts@7.10.1","dependsOn":["pkg:npm/@inquirer/checkbox@4.3.2","pkg:npm/@inquirer/confirm@5.1.21","pkg:npm/@inquirer/editor@4.2.23","pkg:npm/@inquirer/expand@4.0.23","pkg:npm/@inquirer/input@4.3.1","pkg:npm/@inquirer/number@3.0.23","pkg:npm/@inquirer/password@4.0.23","pkg:npm/@inquirer/rawlist@4.1.11","pkg:npm/@inquirer/search@3.2.2","pkg:npm/@inquirer/select@4.4.2"]},{"ref":"pkg:npm/@inquirer/rawlist@4.1.11","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/type@3.0.10","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/search@3.2.2","dependsOn":["pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/figures@1.0.15","pkg:npm/@inquirer/type@3.0.10","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/select@4.4.2","dependsOn":["pkg:npm/@inquirer/ansi@1.0.2","pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/figures@1.0.15","pkg:npm/@inquirer/type@3.0.10","pkg:npm/yoctocolors-cjs@2.1.3"]},{"ref":"pkg:npm/@inquirer/testing@2.1.53","dependsOn":["pkg:npm/@inquirer/type@3.0.10","pkg:npm/mute-stream@2.0.0"]},{"ref":"pkg:npm/@inquirer/type@3.0.10","dependsOn":[]},{"ref":"pkg:npm/@isaacs/cliui@8.0.2","dependsOn":["pkg:npm/string-width@4.2.3","pkg:npm/string-width@5.1.2","pkg:npm/strip-ansi@6.0.1","pkg:npm/strip-ansi@7.2.0","pkg:npm/wrap-ansi@7.0.0","pkg:npm/wrap-ansi@8.1.0"]},{"ref":"pkg:npm/@isaacs/cliui@9.0.0","dependsOn":[]},{"ref":"pkg:npm/@isaacs/fs-minipass@4.0.1","dependsOn":["pkg:npm/minipass@7.1.3"]},{"ref":"pkg:npm/@kwsites/file-exists@1.1.1","dependsOn":["pkg:npm/debug@4.4.3"]},{"ref":"pkg:npm/@kwsites/promise-deferred@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@modelcontextprotocol/sdk@1.29.0","dependsOn":["pkg:npm/@hono/node-server@1.19.14","pkg:npm/ajv-formats@3.0.1","pkg:npm/ajv@8.18.0","pkg:npm/content-type@1.0.5","pkg:npm/cors@2.8.6","pkg:npm/cross-spawn@7.0.6","pkg:npm/eventsource-parser@3.0.6","pkg:npm/eventsource@3.0.7","pkg:npm/express-rate-limit@8.3.2","pkg:npm/express@5.2.1","pkg:npm/hono@4.12.14","pkg:npm/jose@6.2.2","pkg:npm/json-schema-typed@8.0.2","pkg:npm/pkce-challenge@5.0.1","pkg:npm/raw-body@3.0.2","pkg:npm/zod-to-json-schema@3.25.2","pkg:npm/zod@3.25.76"]},{"ref":"pkg:npm/@napi-rs/nice-android-arm-eabi@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-android-arm64@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-darwin-arm64@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-darwin-x64@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-freebsd-x64@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-arm-gnueabihf@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-arm64-gnu@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-arm64-musl@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-ppc64-gnu@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-riscv64-gnu@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-s390x-gnu@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-x64-gnu@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-linux-x64-musl@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-openharmony-arm64@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-win32-arm64-msvc@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-win32-ia32-msvc@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice-win32-x64-msvc@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@napi-rs/nice@1.1.1","dependsOn":[]},{"ref":"pkg:npm/@nodable/entities@2.1.0","dependsOn":[]},{"ref":"pkg:npm/@nodelib/fs.scandir@2.1.5","dependsOn":["pkg:npm/@nodelib/fs.stat@2.0.5","pkg:npm/run-parallel@1.2.0"]},{"ref":"pkg:npm/@nodelib/fs.stat@2.0.5","dependsOn":[]},{"ref":"pkg:npm/@nodelib/fs.walk@1.2.8","dependsOn":["pkg:npm/@nodelib/fs.scandir@2.1.5","pkg:npm/fastq@1.20.1"]},{"ref":"pkg:npm/@npmcli/fs@3.1.1","dependsOn":["pkg:npm/semver@7.7.4"]},{"ref":"pkg:npm/@pinojs/redact@0.4.0","dependsOn":[]},{"ref":"pkg:npm/@pkgjs/parseargs@0.11.0","dependsOn":[]},{"ref":"pkg:npm/@pnpm/crypto.base32-hash@1.0.1","dependsOn":["pkg:npm/rfc4648@1.5.4"]},{"ref":"pkg:npm/@pnpm/types@8.9.0","dependsOn":[]},{"ref":"pkg:npm/@sec-ant/readable-stream@0.4.1","dependsOn":[]},{"ref":"pkg:npm/@simple-git/args-pathspec@1.0.3","dependsOn":[]},{"ref":"pkg:npm/@simple-git/argv-parser@1.1.1","dependsOn":["pkg:npm/@simple-git/args-pathspec@1.0.3"]},{"ref":"pkg:npm/@simple-libs/child-process-utils@1.0.2","dependsOn":["pkg:npm/@simple-libs/stream-utils@1.2.0"]},{"ref":"pkg:npm/@simple-libs/stream-utils@1.2.0","dependsOn":[]},{"ref":"pkg:npm/@sindresorhus/is@4.6.0","dependsOn":[]},{"ref":"pkg:npm/@sindresorhus/merge-streams@4.0.0","dependsOn":[]},{"ref":"pkg:npm/@snyk/dep-graph@2.16.7","dependsOn":["pkg:npm/event-loop-spinner@2.3.2","pkg:npm/lodash.clone@4.5.0","pkg:npm/lodash.constant@3.0.0","pkg:npm/lodash.filter@4.6.0","pkg:npm/lodash.foreach@4.5.0","pkg:npm/lodash.isempty@4.4.0","pkg:npm/lodash.isequal@4.5.0","pkg:npm/lodash.isfunction@3.0.9","pkg:npm/lodash.isundefined@3.0.1","pkg:npm/lodash.map@4.6.0","pkg:npm/lodash.reduce@4.6.0","pkg:npm/lodash.size@4.2.0","pkg:npm/lodash.transform@4.6.0","pkg:npm/lodash.union@4.6.0","pkg:npm/lodash.values@4.3.0","pkg:npm/object-hash@3.0.0","pkg:npm/packageurl-js@2.0.1","pkg:npm/semver@7.7.4","pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/@snyk/error-catalog-nodejs-public@5.80.0","dependsOn":["pkg:npm/tslib@2.8.1","pkg:npm/uuid@14.0.0"]},{"ref":"pkg:npm/@snyk/graphlib@2.1.9-patch.3","dependsOn":["pkg:npm/lodash.clone@4.5.0","pkg:npm/lodash.constant@3.0.0","pkg:npm/lodash.filter@4.6.0","pkg:npm/lodash.foreach@4.5.0","pkg:npm/lodash.has@4.5.2","pkg:npm/lodash.isempty@4.4.0","pkg:npm/lodash.isfunction@3.0.9","pkg:npm/lodash.isundefined@3.0.1","pkg:npm/lodash.keys@4.2.0","pkg:npm/lodash.map@4.6.0","pkg:npm/lodash.reduce@4.6.0","pkg:npm/lodash.size@4.2.0","pkg:npm/lodash.transform@4.6.0","pkg:npm/lodash.union@4.6.0","pkg:npm/lodash.values@4.3.0"]},{"ref":"pkg:npm/@szmarczak/http-timer@4.0.6","dependsOn":["pkg:npm/defer-to-connect@2.0.1"]},{"ref":"pkg:npm/@types/cacheable-request@6.0.3","dependsOn":["pkg:npm/@types/http-cache-semantics@4.2.0","pkg:npm/@types/keyv@3.1.4","pkg:npm/@types/node@22.19.17","pkg:npm/@types/responselike@1.0.3"]},{"ref":"pkg:npm/@types/conventional-commits-parser@5.0.1","dependsOn":["pkg:npm/@types/node@22.19.17"]},{"ref":"pkg:npm/@types/emscripten@1.41.5","dependsOn":[]},{"ref":"pkg:npm/@types/http-cache-semantics@4.2.0","dependsOn":[]},{"ref":"pkg:npm/@types/json-schema@7.0.15","dependsOn":[]},{"ref":"pkg:npm/@types/keyv@3.1.4","dependsOn":["pkg:npm/@types/node@22.19.17"]},{"ref":"pkg:npm/@types/node@22.19.17","dependsOn":["pkg:npm/undici-types@6.21.0"]},{"ref":"pkg:npm/@types/responselike@1.0.3","dependsOn":["pkg:npm/@types/node@22.19.17"]},{"ref":"pkg:npm/@types/sarif@2.1.7","dependsOn":[]},{"ref":"pkg:npm/@types/semver@7.7.1","dependsOn":[]},{"ref":"pkg:npm/@types/spdx-correct@3.1.3","dependsOn":[]},{"ref":"pkg:npm/@types/treeify@1.0.3","dependsOn":[]},{"ref":"pkg:npm/@types/uuid@10.0.0","dependsOn":[]},{"ref":"pkg:npm/@types/write-file-atomic@4.0.3","dependsOn":["pkg:npm/@types/node@22.19.17"]},{"ref":"pkg:npm/@yarnpkg/core@4.6.0","dependsOn":["pkg:npm/@arcanis/slice-ansi@1.1.1","pkg:npm/@types/semver@7.7.1","pkg:npm/@types/treeify@1.0.3","pkg:npm/@yarnpkg/fslib@3.1.5","pkg:npm/@yarnpkg/libzip@3.2.2","pkg:npm/@yarnpkg/parsers@3.0.3","pkg:npm/@yarnpkg/shell@4.1.3","pkg:npm/camelcase@5.3.1","pkg:npm/chalk@4.1.2","pkg:npm/ci-info@4.4.0","pkg:npm/clipanion@4.0.0-rc.4","pkg:npm/cross-spawn@7.0.6","pkg:npm/diff@5.2.2","pkg:npm/dotenv@16.6.1","pkg:npm/es-toolkit@1.45.1","pkg:npm/fast-glob@3.3.3","pkg:npm/got@11.8.6","pkg:npm/hpagent@1.2.0","pkg:npm/micromatch@4.0.8","pkg:npm/p-limit@2.3.0","pkg:npm/semver@7.7.4","pkg:npm/strip-ansi@6.0.1","pkg:npm/tar@7.5.13","pkg:npm/tinylogic@2.0.0","pkg:npm/treeify@1.1.0","pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/@yarnpkg/fslib@3.1.5","dependsOn":["pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/@yarnpkg/libzip@3.2.2","dependsOn":["pkg:npm/@types/emscripten@1.41.5","pkg:npm/@yarnpkg/fslib@3.1.5","pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/@yarnpkg/lockfile@1.1.0","dependsOn":[]},{"ref":"pkg:npm/@yarnpkg/parsers@3.0.3","dependsOn":["pkg:npm/js-yaml@4.1.1","pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/@yarnpkg/shell@4.1.3","dependsOn":["pkg:npm/@yarnpkg/fslib@3.1.5","pkg:npm/@yarnpkg/parsers@3.0.3","pkg:npm/chalk@4.1.2","pkg:npm/clipanion@4.0.0-rc.4","pkg:npm/cross-spawn@7.0.6","pkg:npm/fast-glob@3.3.3","pkg:npm/micromatch@4.0.8","pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/abbrev@2.0.0","dependsOn":[]},{"ref":"pkg:npm/accepts@2.0.0","dependsOn":["pkg:npm/mime-types@3.0.2","pkg:npm/negotiator@1.0.0"]},{"ref":"pkg:npm/adm-zip@0.5.16","dependsOn":[]},{"ref":"pkg:npm/aggregate-error@3.1.0","dependsOn":["pkg:npm/clean-stack@2.2.0","pkg:npm/indent-string@4.0.0"]},{"ref":"pkg:npm/ajv-draft-04@1.0.0","dependsOn":[]},{"ref":"pkg:npm/ajv-formats-draft2019@1.6.1","dependsOn":["pkg:npm/ajv@8.18.0","pkg:npm/punycode@2.3.1","pkg:npm/schemes@1.4.0","pkg:npm/smtp-address-parser@1.1.0","pkg:npm/uri-js@4.4.1"]},{"ref":"pkg:npm/ajv-formats@3.0.1","dependsOn":[]},{"ref":"pkg:npm/ajv@8.18.0","dependsOn":["pkg:npm/fast-deep-equal@3.1.3","pkg:npm/fast-uri@3.1.0","pkg:npm/json-schema-traverse@1.0.0","pkg:npm/require-from-string@2.0.2"]},{"ref":"pkg:npm/ansi-align@3.0.1","dependsOn":["pkg:npm/string-width@4.2.3"]},{"ref":"pkg:npm/ansi-escapes@4.3.2","dependsOn":["pkg:npm/type-fest@0.21.3"]},{"ref":"pkg:npm/ansi-escapes@7.1.0","dependsOn":["pkg:npm/environment@1.1.0"]},{"ref":"pkg:npm/ansi-regex@5.0.1","dependsOn":[]},{"ref":"pkg:npm/ansi-regex@6.2.2","dependsOn":[]},{"ref":"pkg:npm/ansi-styles@3.2.1","dependsOn":["pkg:npm/color-convert@1.9.3"]},{"ref":"pkg:npm/ansi-styles@4.3.0","dependsOn":["pkg:npm/color-convert@2.0.1"]},{"ref":"pkg:npm/ansi-styles@6.2.3","dependsOn":[]},{"ref":"pkg:npm/argparse@2.0.1","dependsOn":[]},{"ref":"pkg:npm/array-find-index@1.0.2","dependsOn":[]},{"ref":"pkg:npm/array-ify@1.0.0","dependsOn":[]},{"ref":"pkg:npm/async@3.2.6","dependsOn":[]},{"ref":"pkg:npm/at-least-node@1.0.0","dependsOn":[]},{"ref":"pkg:npm/atomic-sleep@1.0.0","dependsOn":[]},{"ref":"pkg:npm/balanced-match@1.0.2","dependsOn":[]},{"ref":"pkg:npm/balanced-match@4.0.4","dependsOn":[]},{"ref":"pkg:npm/base64-js@1.5.1","dependsOn":[]},{"ref":"pkg:npm/bl@4.1.0","dependsOn":["pkg:npm/buffer@5.7.1","pkg:npm/inherits@2.0.4","pkg:npm/readable-stream@3.6.2"]},{"ref":"pkg:npm/body-parser@2.2.2","dependsOn":["pkg:npm/bytes@3.1.2","pkg:npm/content-type@1.0.5","pkg:npm/debug@4.4.3","pkg:npm/http-errors@2.0.1","pkg:npm/iconv-lite@0.7.2","pkg:npm/on-finished@2.4.1","pkg:npm/qs@6.15.1","pkg:npm/raw-body@3.0.2","pkg:npm/type-is@2.0.1"]},{"ref":"pkg:npm/boolean@3.2.0","dependsOn":[]},{"ref":"pkg:npm/boxen@8.0.1","dependsOn":["pkg:npm/ansi-align@3.0.1","pkg:npm/camelcase@8.0.0","pkg:npm/chalk@5.6.2","pkg:npm/cli-boxes@3.0.0","pkg:npm/string-width@7.2.0","pkg:npm/type-fest@4.41.0","pkg:npm/widest-line@5.0.0","pkg:npm/wrap-ansi@9.0.2"]},{"ref":"pkg:npm/brace-expansion@1.1.13","dependsOn":["pkg:npm/balanced-match@1.0.2","pkg:npm/concat-map@0.0.1"]},{"ref":"pkg:npm/brace-expansion@2.1.0","dependsOn":["pkg:npm/balanced-match@1.0.2"]},{"ref":"pkg:npm/brace-expansion@5.0.5","dependsOn":["pkg:npm/balanced-match@4.0.4"]},{"ref":"pkg:npm/braces@3.0.3","dependsOn":["pkg:npm/fill-range@7.1.1"]},{"ref":"pkg:npm/buffer@5.7.1","dependsOn":["pkg:npm/base64-js@1.5.1","pkg:npm/ieee754@1.2.1"]},{"ref":"pkg:npm/bytes@3.1.2","dependsOn":[]},{"ref":"pkg:npm/cacheable-lookup@5.0.4","dependsOn":[]},{"ref":"pkg:npm/cacheable-request@7.0.4","dependsOn":["pkg:npm/clone-response@1.0.3","pkg:npm/get-stream@5.2.0","pkg:npm/http-cache-semantics@4.2.0","pkg:npm/keyv@4.5.4","pkg:npm/lowercase-keys@2.0.0","pkg:npm/normalize-url@6.1.0","pkg:npm/responselike@2.0.1"]},{"ref":"pkg:npm/cachedir@2.3.0","dependsOn":[]},{"ref":"pkg:npm/call-bind-apply-helpers@1.0.2","dependsOn":["pkg:npm/es-errors@1.3.0","pkg:npm/function-bind@1.1.2"]},{"ref":"pkg:npm/call-bound@1.0.4","dependsOn":["pkg:npm/call-bind-apply-helpers@1.0.2","pkg:npm/get-intrinsic@1.3.0"]},{"ref":"pkg:npm/call-me-maybe@1.0.2","dependsOn":[]},{"ref":"pkg:npm/callsites@3.1.0","dependsOn":[]},{"ref":"pkg:npm/camelcase@5.3.1","dependsOn":[]},{"ref":"pkg:npm/camelcase@8.0.0","dependsOn":[]},{"ref":"pkg:npm/chalk@2.4.2","dependsOn":["pkg:npm/ansi-styles@3.2.1","pkg:npm/escape-string-regexp@1.0.5","pkg:npm/supports-color@5.5.0"]},{"ref":"pkg:npm/chalk@4.1.2","dependsOn":["pkg:npm/ansi-styles@4.3.0","pkg:npm/supports-color@7.2.0"]},{"ref":"pkg:npm/chalk@5.3.0","dependsOn":[]},{"ref":"pkg:npm/chalk@5.6.2","dependsOn":[]},{"ref":"pkg:npm/chardet@0.7.0","dependsOn":[]},{"ref":"pkg:npm/chardet@2.1.1","dependsOn":[]},{"ref":"pkg:npm/chownr@1.1.4","dependsOn":[]},{"ref":"pkg:npm/chownr@3.0.0","dependsOn":[]},{"ref":"pkg:npm/ci-info@4.4.0","dependsOn":[]},{"ref":"pkg:npm/clean-stack@2.2.0","dependsOn":[]},{"ref":"pkg:npm/cli-boxes@3.0.0","dependsOn":[]},{"ref":"pkg:npm/cli-cursor@3.1.0","dependsOn":["pkg:npm/restore-cursor@3.1.0"]},{"ref":"pkg:npm/cli-cursor@5.0.0","dependsOn":["pkg:npm/restore-cursor@5.1.0"]},{"ref":"pkg:npm/cli-spinners@2.9.2","dependsOn":[]},{"ref":"pkg:npm/cli-table3@0.6.5","dependsOn":["pkg:npm/string-width@4.2.3"]},{"ref":"pkg:npm/cli-truncate@5.2.0","dependsOn":["pkg:npm/slice-ansi@8.0.0","pkg:npm/string-width@8.2.0"]},{"ref":"pkg:npm/cli-width@3.0.0","dependsOn":[]},{"ref":"pkg:npm/cli-width@4.1.0","dependsOn":[]},{"ref":"pkg:npm/clipanion@4.0.0-rc.4","dependsOn":["pkg:npm/typanion@3.14.0"]},{"ref":"pkg:npm/cliui@8.0.1","dependsOn":["pkg:npm/string-width@4.2.3","pkg:npm/strip-ansi@6.0.1","pkg:npm/wrap-ansi@7.0.0"]},{"ref":"pkg:npm/clone-response@1.0.3","dependsOn":["pkg:npm/mimic-response@1.0.1"]},{"ref":"pkg:npm/clone@1.0.4","dependsOn":[]},{"ref":"pkg:npm/color-convert@1.9.3","dependsOn":["pkg:npm/color-name@1.1.3"]},{"ref":"pkg:npm/color-convert@2.0.1","dependsOn":["pkg:npm/color-name@1.1.4"]},{"ref":"pkg:npm/color-name@1.1.3","dependsOn":[]},{"ref":"pkg:npm/color-name@1.1.4","dependsOn":[]},{"ref":"pkg:npm/colorette@2.0.20","dependsOn":[]},{"ref":"pkg:npm/command-exists@1.2.9","dependsOn":[]},{"ref":"pkg:npm/commander@14.0.3","dependsOn":[]},{"ref":"pkg:npm/commander@2.20.3","dependsOn":[]},{"ref":"pkg:npm/commitizen@4.3.1","dependsOn":["pkg:npm/cachedir@2.3.0","pkg:npm/cz-conventional-changelog@3.3.0","pkg:npm/dedent@0.7.0","pkg:npm/detect-indent@6.1.0","pkg:npm/find-node-modules@2.1.3","pkg:npm/find-root@1.1.0","pkg:npm/fs-extra@9.1.0","pkg:npm/glob@7.2.3","pkg:npm/inquirer@8.2.5","pkg:npm/is-utf8@0.2.1","pkg:npm/lodash@4.18.0","pkg:npm/minimist@1.2.7","pkg:npm/strip-bom@4.0.0","pkg:npm/strip-json-comments@3.1.1"]},{"ref":"pkg:npm/compare-func@2.0.0","dependsOn":["pkg:npm/array-ify@1.0.0","pkg:npm/dot-prop@5.3.0"]},{"ref":"pkg:npm/concat-map@0.0.1","dependsOn":[]},{"ref":"pkg:npm/content-disposition@1.1.0","dependsOn":[]},{"ref":"pkg:npm/content-type@1.0.5","dependsOn":[]},{"ref":"pkg:npm/conventional-changelog-angular@8.3.1","dependsOn":["pkg:npm/compare-func@2.0.0"]},{"ref":"pkg:npm/conventional-changelog-conventionalcommits@9.3.1","dependsOn":["pkg:npm/compare-func@2.0.0"]},{"ref":"pkg:npm/conventional-commit-types@3.0.0","dependsOn":[]},{"ref":"pkg:npm/conventional-commits-parser@6.4.0","dependsOn":["pkg:npm/@simple-libs/stream-utils@1.2.0","pkg:npm/meow@13.2.0"]},{"ref":"pkg:npm/cookie-signature@1.2.2","dependsOn":[]},{"ref":"pkg:npm/cookie@0.7.2","dependsOn":[]},{"ref":"pkg:npm/cors@2.8.6","dependsOn":["pkg:npm/object-assign@4.1.1","pkg:npm/vary@1.1.2"]},{"ref":"pkg:npm/cosmiconfig-typescript-loader@6.1.0","dependsOn":["pkg:npm/@types/node@22.19.17","pkg:npm/cosmiconfig@9.0.0","pkg:npm/cosmiconfig@9.0.1","pkg:npm/jiti@2.4.1","pkg:npm/typescript@5.9.3"]},{"ref":"pkg:npm/cosmiconfig@9.0.0","dependsOn":["pkg:npm/env-paths@2.2.1","pkg:npm/import-fresh@3.3.0","pkg:npm/js-yaml@4.1.1","pkg:npm/parse-json@5.2.0"]},{"ref":"pkg:npm/cosmiconfig@9.0.1","dependsOn":["pkg:npm/env-paths@2.2.1","pkg:npm/import-fresh@3.3.1","pkg:npm/js-yaml@4.1.1","pkg:npm/parse-json@5.2.0"]},{"ref":"pkg:npm/cross-spawn@7.0.6","dependsOn":["pkg:npm/path-key@3.1.1","pkg:npm/shebang-command@2.0.0","pkg:npm/which@2.0.2"]},{"ref":"pkg:npm/cz-conventional-changelog@3.3.0","dependsOn":["pkg:npm/chalk@2.4.2","pkg:npm/commitizen@4.3.1","pkg:npm/conventional-commit-types@3.0.0","pkg:npm/lodash.map@4.6.0","pkg:npm/longest@2.0.1","pkg:npm/word-wrap@1.2.5"]},{"ref":"pkg:npm/dateformat@4.6.3","dependsOn":[]},{"ref":"pkg:npm/debug@4.4.3","dependsOn":["pkg:npm/ms@2.1.3"]},{"ref":"pkg:npm/decompress-response@6.0.0","dependsOn":["pkg:npm/mimic-response@3.1.0"]},{"ref":"pkg:npm/dedent@0.7.0","dependsOn":[]},{"ref":"pkg:npm/deep-extend@0.6.0","dependsOn":[]},{"ref":"pkg:npm/defaults@1.0.4","dependsOn":["pkg:npm/clone@1.0.4"]},{"ref":"pkg:npm/defer-to-connect@2.0.1","dependsOn":[]},{"ref":"pkg:npm/define-data-property@1.1.4","dependsOn":["pkg:npm/es-define-property@1.0.1","pkg:npm/es-errors@1.3.0","pkg:npm/gopd@1.2.0"]},{"ref":"pkg:npm/define-properties@1.2.1","dependsOn":["pkg:npm/define-data-property@1.1.4","pkg:npm/has-property-descriptors@1.0.2","pkg:npm/object-keys@1.1.1"]},{"ref":"pkg:npm/depd@2.0.0","dependsOn":[]},{"ref":"pkg:npm/dependency-path@9.2.8","dependsOn":["pkg:npm/@pnpm/crypto.base32-hash@1.0.1","pkg:npm/@pnpm/types@8.9.0","pkg:npm/encode-registry@3.0.1","pkg:npm/semver@7.7.4"]},{"ref":"pkg:npm/detect-file@1.0.0","dependsOn":[]},{"ref":"pkg:npm/detect-indent@6.1.0","dependsOn":[]},{"ref":"pkg:npm/detect-libc@2.1.2","dependsOn":[]},{"ref":"pkg:npm/detect-node@2.1.0","dependsOn":[]},{"ref":"pkg:npm/diff@5.2.2","dependsOn":[]},{"ref":"pkg:npm/discontinuous-range@1.0.0","dependsOn":[]},{"ref":"pkg:npm/dot-prop@5.3.0","dependsOn":["pkg:npm/is-obj@2.0.0"]},{"ref":"pkg:npm/dotenv@16.6.1","dependsOn":[]},{"ref":"pkg:npm/dunder-proto@1.0.1","dependsOn":["pkg:npm/call-bind-apply-helpers@1.0.2","pkg:npm/es-errors@1.3.0","pkg:npm/gopd@1.2.0"]},{"ref":"pkg:npm/eastasianwidth@0.2.0","dependsOn":[]},{"ref":"pkg:npm/ee-first@1.1.1","dependsOn":[]},{"ref":"pkg:npm/emoji-regex@10.4.0","dependsOn":[]},{"ref":"pkg:npm/emoji-regex@8.0.0","dependsOn":[]},{"ref":"pkg:npm/emoji-regex@9.2.2","dependsOn":[]},{"ref":"pkg:npm/encode-registry@3.0.1","dependsOn":["pkg:npm/mem@8.1.1"]},{"ref":"pkg:npm/encodeurl@2.0.0","dependsOn":[]},{"ref":"pkg:npm/end-of-stream@1.4.5","dependsOn":["pkg:npm/once@1.4.0"]},{"ref":"pkg:npm/env-paths@2.2.1","dependsOn":[]},{"ref":"pkg:npm/envinfo@7.21.0","dependsOn":[]},{"ref":"pkg:npm/environment@1.1.0","dependsOn":[]},{"ref":"pkg:npm/error-ex@1.3.4","dependsOn":["pkg:npm/is-arrayish@0.2.1"]},{"ref":"pkg:npm/es-define-property@1.0.1","dependsOn":[]},{"ref":"pkg:npm/es-errors@1.3.0","dependsOn":[]},{"ref":"pkg:npm/es-object-atoms@1.1.1","dependsOn":["pkg:npm/es-errors@1.3.0"]},{"ref":"pkg:npm/es-toolkit@1.45.1","dependsOn":[]},{"ref":"pkg:npm/es6-error@4.1.1","dependsOn":[]},{"ref":"pkg:npm/escalade@3.2.0","dependsOn":[]},{"ref":"pkg:npm/escape-html@1.0.3","dependsOn":[]},{"ref":"pkg:npm/escape-string-regexp@1.0.5","dependsOn":[]},{"ref":"pkg:npm/escape-string-regexp@4.0.0","dependsOn":[]},{"ref":"pkg:npm/etag@1.8.1","dependsOn":[]},{"ref":"pkg:npm/event-loop-spinner@2.3.2","dependsOn":["pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/eventemitter3@5.0.4","dependsOn":[]},{"ref":"pkg:npm/events@3.3.0","dependsOn":[]},{"ref":"pkg:npm/eventsource-parser@3.0.6","dependsOn":[]},{"ref":"pkg:npm/eventsource@3.0.7","dependsOn":["pkg:npm/eventsource-parser@3.0.6"]},{"ref":"pkg:npm/execa@9.6.1","dependsOn":["pkg:npm/@sindresorhus/merge-streams@4.0.0","pkg:npm/cross-spawn@7.0.6","pkg:npm/figures@6.1.0","pkg:npm/get-stream@9.0.1","pkg:npm/human-signals@8.0.1","pkg:npm/is-plain-obj@4.1.0","pkg:npm/is-stream@4.0.1","pkg:npm/npm-run-path@6.0.0","pkg:npm/pretty-ms@9.3.0","pkg:npm/signal-exit@4.1.0","pkg:npm/strip-final-newline@4.0.0","pkg:npm/yoctocolors@2.1.2"]},{"ref":"pkg:npm/expand-template@2.0.3","dependsOn":[]},{"ref":"pkg:npm/expand-tilde@2.0.2","dependsOn":["pkg:npm/homedir-polyfill@1.0.3"]},{"ref":"pkg:npm/express-rate-limit@8.3.2","dependsOn":["pkg:npm/express@5.2.1","pkg:npm/ip-address@10.1.0"]},{"ref":"pkg:npm/express@5.2.1","dependsOn":["pkg:npm/accepts@2.0.0","pkg:npm/body-parser@2.2.2","pkg:npm/content-disposition@1.1.0","pkg:npm/content-type@1.0.5","pkg:npm/cookie-signature@1.2.2","pkg:npm/cookie@0.7.2","pkg:npm/debug@4.4.3","pkg:npm/depd@2.0.0","pkg:npm/encodeurl@2.0.0","pkg:npm/escape-html@1.0.3","pkg:npm/etag@1.8.1","pkg:npm/finalhandler@2.1.1","pkg:npm/fresh@2.0.0","pkg:npm/http-errors@2.0.1","pkg:npm/merge-descriptors@2.0.0","pkg:npm/mime-types@3.0.2","pkg:npm/on-finished@2.4.1","pkg:npm/once@1.4.0","pkg:npm/parseurl@1.3.3","pkg:npm/proxy-addr@2.0.7","pkg:npm/qs@6.15.1","pkg:npm/range-parser@1.2.1","pkg:npm/router@2.2.0","pkg:npm/send@1.2.1","pkg:npm/serve-static@2.2.1","pkg:npm/statuses@2.0.2","pkg:npm/type-is@2.0.1","pkg:npm/vary@1.1.2"]},{"ref":"pkg:npm/extend-shallow@2.0.1","dependsOn":["pkg:npm/is-extendable@0.1.1"]},{"ref":"pkg:npm/extend@3.0.2","dependsOn":[]},{"ref":"pkg:npm/external-editor@3.1.0","dependsOn":["pkg:npm/chardet@0.7.0","pkg:npm/iconv-lite@0.4.24","pkg:npm/tmp@0.2.4"]},{"ref":"pkg:npm/fast-copy@4.0.3","dependsOn":[]},{"ref":"pkg:npm/fast-deep-equal@3.1.3","dependsOn":[]},{"ref":"pkg:npm/fast-glob@3.3.3","dependsOn":["pkg:npm/@nodelib/fs.stat@2.0.5","pkg:npm/@nodelib/fs.walk@1.2.8","pkg:npm/glob-parent@5.1.2","pkg:npm/merge2@1.4.1","pkg:npm/micromatch@4.0.8"]},{"ref":"pkg:npm/fast-safe-stringify@2.1.1","dependsOn":[]},{"ref":"pkg:npm/fast-uri@3.1.0","dependsOn":[]},{"ref":"pkg:npm/fast-xml-builder@1.1.5","dependsOn":["pkg:npm/path-expression-matcher@1.5.0"]},{"ref":"pkg:npm/fast-xml-parser@5.7.1","dependsOn":["pkg:npm/@nodable/entities@2.1.0","pkg:npm/fast-xml-builder@1.1.5","pkg:npm/path-expression-matcher@1.5.0","pkg:npm/strnum@2.2.3"]},{"ref":"pkg:npm/fastq@1.20.1","dependsOn":["pkg:npm/reusify@1.1.0"]},{"ref":"pkg:npm/figures@3.2.0","dependsOn":["pkg:npm/escape-string-regexp@1.0.5"]},{"ref":"pkg:npm/figures@6.1.0","dependsOn":["pkg:npm/is-unicode-supported@2.1.0"]},{"ref":"pkg:npm/fill-range@7.1.1","dependsOn":["pkg:npm/to-regex-range@5.0.1"]},{"ref":"pkg:npm/finalhandler@2.1.1","dependsOn":["pkg:npm/debug@4.4.3","pkg:npm/encodeurl@2.0.0","pkg:npm/escape-html@1.0.3","pkg:npm/on-finished@2.4.1","pkg:npm/parseurl@1.3.3","pkg:npm/statuses@2.0.2"]},{"ref":"pkg:npm/find-node-modules@2.1.3","dependsOn":["pkg:npm/findup-sync@4.0.0","pkg:npm/merge@2.1.1"]},{"ref":"pkg:npm/find-root@1.1.0","dependsOn":[]},{"ref":"pkg:npm/findup-sync@4.0.0","dependsOn":["pkg:npm/detect-file@1.0.0","pkg:npm/is-glob@4.0.3","pkg:npm/micromatch@4.0.8","pkg:npm/resolve-dir@1.0.1"]},{"ref":"pkg:npm/foreground-child@3.3.1","dependsOn":["pkg:npm/cross-spawn@7.0.6","pkg:npm/signal-exit@4.1.0"]},{"ref":"pkg:npm/forwarded@0.2.0","dependsOn":[]},{"ref":"pkg:npm/fresh@2.0.0","dependsOn":[]},{"ref":"pkg:npm/fs-constants@1.0.0","dependsOn":[]},{"ref":"pkg:npm/fs-extra@11.3.4","dependsOn":["pkg:npm/graceful-fs@4.2.11","pkg:npm/jsonfile@6.2.0","pkg:npm/universalify@2.0.1"]},{"ref":"pkg:npm/fs-extra@9.1.0","dependsOn":["pkg:npm/at-least-node@1.0.0","pkg:npm/graceful-fs@4.2.11","pkg:npm/jsonfile@6.1.0","pkg:npm/universalify@2.0.1"]},{"ref":"pkg:npm/fs.realpath@1.0.0","dependsOn":[]},{"ref":"pkg:npm/function-bind@1.1.2","dependsOn":[]},{"ref":"pkg:npm/get-caller-file@2.0.5","dependsOn":[]},{"ref":"pkg:npm/get-east-asian-width@1.5.0","dependsOn":[]},{"ref":"pkg:npm/get-intrinsic@1.3.0","dependsOn":["pkg:npm/call-bind-apply-helpers@1.0.2","pkg:npm/es-define-property@1.0.1","pkg:npm/es-errors@1.3.0","pkg:npm/es-object-atoms@1.1.1","pkg:npm/function-bind@1.1.2","pkg:npm/get-proto@1.0.1","pkg:npm/gopd@1.2.0","pkg:npm/has-symbols@1.1.0","pkg:npm/hasown@2.0.2","pkg:npm/math-intrinsics@1.1.0"]},{"ref":"pkg:npm/get-proto@1.0.1","dependsOn":["pkg:npm/dunder-proto@1.0.1","pkg:npm/es-object-atoms@1.1.1"]},{"ref":"pkg:npm/get-stream@5.2.0","dependsOn":["pkg:npm/pump@3.0.4"]},{"ref":"pkg:npm/get-stream@9.0.1","dependsOn":["pkg:npm/@sec-ant/readable-stream@0.4.1","pkg:npm/is-stream@4.0.1"]},{"ref":"pkg:npm/git-raw-commits@5.0.1","dependsOn":["pkg:npm/@conventional-changelog/git-client@2.7.0","pkg:npm/meow@13.2.0"]},{"ref":"pkg:npm/github-from-package@0.0.0","dependsOn":[]},{"ref":"pkg:npm/glob-parent@5.1.2","dependsOn":["pkg:npm/is-glob@4.0.3"]},{"ref":"pkg:npm/glob@10.5.0","dependsOn":["pkg:npm/foreground-child@3.3.1","pkg:npm/jackspeak@3.4.3","pkg:npm/minimatch@9.0.9","pkg:npm/minipass@7.1.3","pkg:npm/package-json-from-dist@1.0.1","pkg:npm/path-scurry@1.11.1"]},{"ref":"pkg:npm/glob@11.1.0","dependsOn":["pkg:npm/foreground-child@3.3.1","pkg:npm/jackspeak@4.2.3","pkg:npm/minimatch@10.2.5","pkg:npm/minipass@7.1.3","pkg:npm/package-json-from-dist@1.0.1","pkg:npm/path-scurry@2.0.2"]},{"ref":"pkg:npm/glob@7.2.3","dependsOn":["pkg:npm/fs.realpath@1.0.0","pkg:npm/inflight@1.0.6","pkg:npm/inherits@2.0.4","pkg:npm/minimatch@3.1.4","pkg:npm/once@1.4.0","pkg:npm/path-is-absolute@1.0.1"]},{"ref":"pkg:npm/global-agent@3.0.0","dependsOn":["pkg:npm/boolean@3.2.0","pkg:npm/es6-error@4.1.1","pkg:npm/matcher@3.0.0","pkg:npm/roarr@2.15.4","pkg:npm/semver@7.7.4","pkg:npm/serialize-error@7.0.1"]},{"ref":"pkg:npm/global-directory@4.0.1","dependsOn":["pkg:npm/ini@4.1.1"]},{"ref":"pkg:npm/global-modules@1.0.0","dependsOn":["pkg:npm/global-prefix@1.0.2","pkg:npm/is-windows@1.0.2","pkg:npm/resolve-dir@1.0.1"]},{"ref":"pkg:npm/global-prefix@1.0.2","dependsOn":["pkg:npm/expand-tilde@2.0.2","pkg:npm/homedir-polyfill@1.0.3","pkg:npm/ini@1.3.8","pkg:npm/is-windows@1.0.2","pkg:npm/which@1.3.1"]},{"ref":"pkg:npm/globalthis@1.0.4","dependsOn":["pkg:npm/define-properties@1.2.1","pkg:npm/gopd@1.2.0"]},{"ref":"pkg:npm/gopd@1.2.0","dependsOn":[]},{"ref":"pkg:npm/got@11.8.6","dependsOn":["pkg:npm/@sindresorhus/is@4.6.0","pkg:npm/@szmarczak/http-timer@4.0.6","pkg:npm/@types/cacheable-request@6.0.3","pkg:npm/@types/responselike@1.0.3","pkg:npm/cacheable-lookup@5.0.4","pkg:npm/cacheable-request@7.0.4","pkg:npm/decompress-response@6.0.0","pkg:npm/http2-wrapper@1.0.3","pkg:npm/lowercase-keys@2.0.0","pkg:npm/p-cancelable@2.1.1","pkg:npm/responselike@2.0.1"]},{"ref":"pkg:npm/graceful-fs@4.2.11","dependsOn":[]},{"ref":"pkg:npm/grapheme-splitter@1.0.4","dependsOn":[]},{"ref":"pkg:npm/graphology-dag@0.4.1","dependsOn":["pkg:npm/graphology-types@0.24.8","pkg:npm/graphology-utils@2.5.2","pkg:npm/mnemonist@0.39.8"]},{"ref":"pkg:npm/graphology-types@0.24.8","dependsOn":[]},{"ref":"pkg:npm/graphology-utils@2.5.2","dependsOn":["pkg:npm/graphology-types@0.24.8"]},{"ref":"pkg:npm/graphology@0.26.0","dependsOn":["pkg:npm/events@3.3.0","pkg:npm/graphology-types@0.24.8"]},{"ref":"pkg:npm/gray-matter@4.0.3","dependsOn":["pkg:npm/js-yaml@4.1.1","pkg:npm/kind-of@6.0.3","pkg:npm/section-matter@1.0.0","pkg:npm/strip-bom-string@1.0.0"]},{"ref":"pkg:npm/handlebars@4.7.9","dependsOn":["pkg:npm/minimist@1.2.8","pkg:npm/neo-async@2.6.2","pkg:npm/source-map@0.6.1","pkg:npm/wordwrap@1.0.0"]},{"ref":"pkg:npm/has-flag@3.0.0","dependsOn":[]},{"ref":"pkg:npm/has-flag@4.0.0","dependsOn":[]},{"ref":"pkg:npm/has-property-descriptors@1.0.2","dependsOn":["pkg:npm/es-define-property@1.0.1"]},{"ref":"pkg:npm/has-symbols@1.1.0","dependsOn":[]},{"ref":"pkg:npm/hasown@2.0.2","dependsOn":["pkg:npm/function-bind@1.1.2"]},{"ref":"pkg:npm/help-me@5.0.0","dependsOn":[]},{"ref":"pkg:npm/homedir-polyfill@1.0.3","dependsOn":["pkg:npm/parse-passwd@1.0.0"]},{"ref":"pkg:npm/hono@4.12.14","dependsOn":[]},{"ref":"pkg:npm/hosted-git-info@6.1.3","dependsOn":["pkg:npm/lru-cache@7.18.3"]},{"ref":"pkg:npm/hpagent@1.2.0","dependsOn":[]},{"ref":"pkg:npm/http-cache-semantics@4.2.0","dependsOn":[]},{"ref":"pkg:npm/http-errors@2.0.1","dependsOn":["pkg:npm/depd@2.0.0","pkg:npm/inherits@2.0.4","pkg:npm/setprototypeof@1.2.0","pkg:npm/statuses@2.0.2","pkg:npm/toidentifier@1.0.1"]},{"ref":"pkg:npm/http2-wrapper@1.0.3","dependsOn":["pkg:npm/quick-lru@5.1.1","pkg:npm/resolve-alpn@1.2.1"]},{"ref":"pkg:npm/human-signals@8.0.1","dependsOn":[]},{"ref":"pkg:npm/iconv-lite@0.4.24","dependsOn":["pkg:npm/safer-buffer@2.1.2"]},{"ref":"pkg:npm/iconv-lite@0.7.2","dependsOn":["pkg:npm/safer-buffer@2.1.2"]},{"ref":"pkg:npm/ieee754@1.2.1","dependsOn":[]},{"ref":"pkg:npm/import-fresh@3.3.0","dependsOn":["pkg:npm/parent-module@1.0.1","pkg:npm/resolve-from@4.0.0"]},{"ref":"pkg:npm/import-fresh@3.3.1","dependsOn":["pkg:npm/parent-module@1.0.1","pkg:npm/resolve-from@4.0.0"]},{"ref":"pkg:npm/import-meta-resolve@4.1.0","dependsOn":[]},{"ref":"pkg:npm/indent-string@4.0.0","dependsOn":[]},{"ref":"pkg:npm/inflight@1.0.6","dependsOn":["pkg:npm/once@1.4.0","pkg:npm/wrappy@1.0.2"]},{"ref":"pkg:npm/inherits@2.0.4","dependsOn":[]},{"ref":"pkg:npm/ini@1.3.8","dependsOn":[]},{"ref":"pkg:npm/ini@4.1.1","dependsOn":[]},{"ref":"pkg:npm/inquirer@8.2.5","dependsOn":["pkg:npm/ansi-escapes@4.3.2","pkg:npm/chalk@4.1.2","pkg:npm/cli-cursor@3.1.0","pkg:npm/cli-width@3.0.0","pkg:npm/external-editor@3.1.0","pkg:npm/figures@3.2.0","pkg:npm/lodash@4.18.0","pkg:npm/mute-stream@0.0.8","pkg:npm/ora@5.4.1","pkg:npm/run-async@2.4.1","pkg:npm/rxjs@7.8.1","pkg:npm/string-width@4.2.3","pkg:npm/strip-ansi@6.0.1","pkg:npm/through@2.3.8","pkg:npm/wrap-ansi@7.0.0"]},{"ref":"pkg:npm/ip-address@10.1.0","dependsOn":[]},{"ref":"pkg:npm/ipaddr.js@1.9.1","dependsOn":[]},{"ref":"pkg:npm/is-arrayish@0.2.1","dependsOn":[]},{"ref":"pkg:npm/is-core-module@2.16.1","dependsOn":["pkg:npm/hasown@2.0.2"]},{"ref":"pkg:npm/is-extendable@0.1.1","dependsOn":[]},{"ref":"pkg:npm/is-extglob@2.1.1","dependsOn":[]},{"ref":"pkg:npm/is-fullwidth-code-point@3.0.0","dependsOn":[]},{"ref":"pkg:npm/is-fullwidth-code-point@5.1.0","dependsOn":["pkg:npm/get-east-asian-width@1.5.0"]},{"ref":"pkg:npm/is-glob@4.0.3","dependsOn":["pkg:npm/is-extglob@2.1.1"]},{"ref":"pkg:npm/is-interactive@1.0.0","dependsOn":[]},{"ref":"pkg:npm/is-interactive@2.0.0","dependsOn":[]},{"ref":"pkg:npm/is-number@7.0.0","dependsOn":[]},{"ref":"pkg:npm/is-obj@2.0.0","dependsOn":[]},{"ref":"pkg:npm/is-plain-obj@4.1.0","dependsOn":[]},{"ref":"pkg:npm/is-promise@4.0.0","dependsOn":[]},{"ref":"pkg:npm/is-stream@4.0.1","dependsOn":[]},{"ref":"pkg:npm/is-unicode-supported@0.1.0","dependsOn":[]},{"ref":"pkg:npm/is-unicode-supported@1.3.0","dependsOn":[]},{"ref":"pkg:npm/is-unicode-supported@2.1.0","dependsOn":[]},{"ref":"pkg:npm/is-utf8@0.2.1","dependsOn":[]},{"ref":"pkg:npm/is-windows@1.0.2","dependsOn":[]},{"ref":"pkg:npm/isexe@2.0.0","dependsOn":[]},{"ref":"pkg:npm/jackspeak@3.4.3","dependsOn":["pkg:npm/@isaacs/cliui@8.0.2"]},{"ref":"pkg:npm/jackspeak@4.2.3","dependsOn":["pkg:npm/@isaacs/cliui@9.0.0"]},{"ref":"pkg:npm/jiti@2.4.1","dependsOn":[]},{"ref":"pkg:npm/jose@6.2.2","dependsOn":[]},{"ref":"pkg:npm/joycon@3.1.1","dependsOn":[]},{"ref":"pkg:npm/js-tokens@4.0.0","dependsOn":[]},{"ref":"pkg:npm/js-yaml@4.1.1","dependsOn":["pkg:npm/argparse@2.0.1"]},{"ref":"pkg:npm/json-buffer@3.0.1","dependsOn":[]},{"ref":"pkg:npm/json-parse-even-better-errors@2.3.1","dependsOn":[]},{"ref":"pkg:npm/json-parse-even-better-errors@3.0.2","dependsOn":[]},{"ref":"pkg:npm/json-schema-traverse@1.0.0","dependsOn":[]},{"ref":"pkg:npm/json-schema-typed@8.0.2","dependsOn":[]},{"ref":"pkg:npm/json-stringify-safe@5.0.1","dependsOn":[]},{"ref":"pkg:npm/jsonfile@6.1.0","dependsOn":["pkg:npm/universalify@2.0.1"]},{"ref":"pkg:npm/jsonfile@6.2.0","dependsOn":["pkg:npm/universalify@2.0.1"]},{"ref":"pkg:npm/keyv@4.5.4","dependsOn":["pkg:npm/json-buffer@3.0.1"]},{"ref":"pkg:npm/kind-of@6.0.3","dependsOn":[]},{"ref":"pkg:npm/lefthook-darwin-arm64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-darwin-x64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-freebsd-arm64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-freebsd-x64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-linux-arm64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-linux-x64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-openbsd-arm64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-openbsd-x64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-windows-arm64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook-windows-x64@2.1.6","dependsOn":[]},{"ref":"pkg:npm/lefthook@2.1.6","dependsOn":[]},{"ref":"pkg:npm/license-checker-rseidelsohn@4.4.2","dependsOn":["pkg:npm/chalk@4.1.2","pkg:npm/debug@4.4.3","pkg:npm/lodash.clonedeep@4.5.0","pkg:npm/mkdirp@1.0.4","pkg:npm/nopt@7.2.1","pkg:npm/read-installed-packages@2.0.1","pkg:npm/semver@7.7.4","pkg:npm/spdx-correct@3.2.0","pkg:npm/spdx-expression-parse@3.0.1","pkg:npm/spdx-satisfies@5.0.1","pkg:npm/treeify@1.1.0"]},{"ref":"pkg:npm/lines-and-columns@1.2.4","dependsOn":[]},{"ref":"pkg:npm/listr2@10.2.1","dependsOn":["pkg:npm/cli-truncate@5.2.0","pkg:npm/eventemitter3@5.0.4","pkg:npm/log-update@6.1.0","pkg:npm/rfdc@1.4.1","pkg:npm/wrap-ansi@10.0.0"]},{"ref":"pkg:npm/lodash.camelcase@4.3.0","dependsOn":[]},{"ref":"pkg:npm/lodash.clone@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.clonedeep@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.constant@3.0.0","dependsOn":[]},{"ref":"pkg:npm/lodash.filter@4.6.0","dependsOn":[]},{"ref":"pkg:npm/lodash.flatmap@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.foreach@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.has@4.5.2","dependsOn":[]},{"ref":"pkg:npm/lodash.isempty@4.4.0","dependsOn":[]},{"ref":"pkg:npm/lodash.isequal@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.isfunction@3.0.9","dependsOn":[]},{"ref":"pkg:npm/lodash.isplainobject@4.0.6","dependsOn":[]},{"ref":"pkg:npm/lodash.isundefined@3.0.1","dependsOn":[]},{"ref":"pkg:npm/lodash.kebabcase@4.1.1","dependsOn":[]},{"ref":"pkg:npm/lodash.keys@4.2.0","dependsOn":[]},{"ref":"pkg:npm/lodash.map@4.6.0","dependsOn":[]},{"ref":"pkg:npm/lodash.merge@4.6.2","dependsOn":[]},{"ref":"pkg:npm/lodash.mergewith@4.6.2","dependsOn":[]},{"ref":"pkg:npm/lodash.reduce@4.6.0","dependsOn":[]},{"ref":"pkg:npm/lodash.size@4.2.0","dependsOn":[]},{"ref":"pkg:npm/lodash.snakecase@4.1.1","dependsOn":[]},{"ref":"pkg:npm/lodash.startcase@4.4.0","dependsOn":[]},{"ref":"pkg:npm/lodash.topairs@4.3.0","dependsOn":[]},{"ref":"pkg:npm/lodash.transform@4.6.0","dependsOn":[]},{"ref":"pkg:npm/lodash.union@4.6.0","dependsOn":[]},{"ref":"pkg:npm/lodash.uniq@4.5.0","dependsOn":[]},{"ref":"pkg:npm/lodash.upperfirst@4.3.1","dependsOn":[]},{"ref":"pkg:npm/lodash.values@4.3.0","dependsOn":[]},{"ref":"pkg:npm/lodash@4.18.0","dependsOn":[]},{"ref":"pkg:npm/log-symbols@4.1.0","dependsOn":["pkg:npm/chalk@4.1.2","pkg:npm/is-unicode-supported@0.1.0"]},{"ref":"pkg:npm/log-symbols@6.0.0","dependsOn":["pkg:npm/chalk@5.6.2","pkg:npm/is-unicode-supported@1.3.0"]},{"ref":"pkg:npm/log-update@6.1.0","dependsOn":["pkg:npm/ansi-escapes@7.1.0","pkg:npm/cli-cursor@5.0.0","pkg:npm/slice-ansi@7.1.2","pkg:npm/strip-ansi@7.2.0","pkg:npm/wrap-ansi@9.0.2"]},{"ref":"pkg:npm/longest@2.0.1","dependsOn":[]},{"ref":"pkg:npm/lowercase-keys@2.0.0","dependsOn":[]},{"ref":"pkg:npm/lru-cache@10.4.3","dependsOn":[]},{"ref":"pkg:npm/lru-cache@11.3.5","dependsOn":[]},{"ref":"pkg:npm/lru-cache@7.18.3","dependsOn":[]},{"ref":"pkg:npm/map-age-cleaner@0.1.3","dependsOn":["pkg:npm/p-defer@1.0.0"]},{"ref":"pkg:npm/matcher@3.0.0","dependsOn":["pkg:npm/escape-string-regexp@4.0.0"]},{"ref":"pkg:npm/math-intrinsics@1.1.0","dependsOn":[]},{"ref":"pkg:npm/media-typer@1.1.0","dependsOn":[]},{"ref":"pkg:npm/mem@8.1.1","dependsOn":["pkg:npm/map-age-cleaner@0.1.3","pkg:npm/mimic-fn@3.1.0"]},{"ref":"pkg:npm/meow@13.2.0","dependsOn":[]},{"ref":"pkg:npm/merge-descriptors@2.0.0","dependsOn":[]},{"ref":"pkg:npm/merge2@1.4.1","dependsOn":[]},{"ref":"pkg:npm/merge@2.1.1","dependsOn":[]},{"ref":"pkg:npm/micromatch@4.0.8","dependsOn":["pkg:npm/braces@3.0.3","pkg:npm/picomatch@2.3.2"]},{"ref":"pkg:npm/mime-db@1.54.0","dependsOn":[]},{"ref":"pkg:npm/mime-types@3.0.2","dependsOn":["pkg:npm/mime-db@1.54.0"]},{"ref":"pkg:npm/mimic-fn@2.1.0","dependsOn":[]},{"ref":"pkg:npm/mimic-fn@3.1.0","dependsOn":[]},{"ref":"pkg:npm/mimic-function@5.0.1","dependsOn":[]},{"ref":"pkg:npm/mimic-response@1.0.1","dependsOn":[]},{"ref":"pkg:npm/mimic-response@3.1.0","dependsOn":[]},{"ref":"pkg:npm/minimatch@10.2.5","dependsOn":["pkg:npm/brace-expansion@5.0.5"]},{"ref":"pkg:npm/minimatch@3.1.4","dependsOn":["pkg:npm/brace-expansion@1.1.13"]},{"ref":"pkg:npm/minimatch@9.0.9","dependsOn":["pkg:npm/brace-expansion@2.1.0"]},{"ref":"pkg:npm/minimist@1.2.7","dependsOn":[]},{"ref":"pkg:npm/minimist@1.2.8","dependsOn":[]},{"ref":"pkg:npm/minipass@7.1.3","dependsOn":[]},{"ref":"pkg:npm/minisearch@7.2.0","dependsOn":[]},{"ref":"pkg:npm/minizlib@3.1.0","dependsOn":["pkg:npm/minipass@7.1.3"]},{"ref":"pkg:npm/mkdirp-classic@0.5.3","dependsOn":[]},{"ref":"pkg:npm/mkdirp@1.0.4","dependsOn":[]},{"ref":"pkg:npm/mnemonist@0.39.8","dependsOn":["pkg:npm/obliterator@2.0.5"]},{"ref":"pkg:npm/moo@0.5.2","dependsOn":[]},{"ref":"pkg:npm/ms@2.1.3","dependsOn":[]},{"ref":"pkg:npm/mute-stream@0.0.8","dependsOn":[]},{"ref":"pkg:npm/mute-stream@2.0.0","dependsOn":[]},{"ref":"pkg:npm/nan@2.26.2","dependsOn":[]},{"ref":"pkg:npm/napi-build-utils@2.0.0","dependsOn":[]},{"ref":"pkg:npm/nearley@2.20.1","dependsOn":["pkg:npm/commander@2.20.3","pkg:npm/moo@0.5.2","pkg:npm/railroad-diagrams@1.0.0","pkg:npm/randexp@0.4.6"]},{"ref":"pkg:npm/negotiator@1.0.0","dependsOn":[]},{"ref":"pkg:npm/neo-async@2.6.2","dependsOn":[]},{"ref":"pkg:npm/node-abi@3.89.0","dependsOn":["pkg:npm/semver@7.7.4"]},{"ref":"pkg:npm/node-addon-api@7.1.1","dependsOn":[]},{"ref":"pkg:npm/node-addon-api@8.5.0","dependsOn":[]},{"ref":"pkg:npm/node-addon-api@8.7.0","dependsOn":[]},{"ref":"pkg:npm/node-gyp-build@4.8.4","dependsOn":[]},{"ref":"pkg:npm/nopt@7.2.1","dependsOn":["pkg:npm/abbrev@2.0.0"]},{"ref":"pkg:npm/normalize-package-data@5.0.0","dependsOn":["pkg:npm/hosted-git-info@6.1.3","pkg:npm/is-core-module@2.16.1","pkg:npm/semver@7.7.4","pkg:npm/validate-npm-package-license@3.0.4"]},{"ref":"pkg:npm/normalize-url@6.1.0","dependsOn":[]},{"ref":"pkg:npm/npm-normalize-package-bin@3.0.1","dependsOn":[]},{"ref":"pkg:npm/npm-run-path@6.0.0","dependsOn":["pkg:npm/path-key@4.0.0","pkg:npm/unicorn-magic@0.3.0"]},{"ref":"pkg:npm/object-assign@4.1.1","dependsOn":[]},{"ref":"pkg:npm/object-hash@3.0.0","dependsOn":[]},{"ref":"pkg:npm/object-inspect@1.13.4","dependsOn":[]},{"ref":"pkg:npm/object-keys@1.1.1","dependsOn":[]},{"ref":"pkg:npm/obliterator@2.0.5","dependsOn":[]},{"ref":"pkg:npm/on-exit-leak-free@2.1.2","dependsOn":[]},{"ref":"pkg:npm/on-finished@2.4.1","dependsOn":["pkg:npm/ee-first@1.1.1"]},{"ref":"pkg:npm/once@1.4.0","dependsOn":["pkg:npm/wrappy@1.0.2"]},{"ref":"pkg:npm/onetime@5.1.2","dependsOn":["pkg:npm/mimic-fn@2.1.0"]},{"ref":"pkg:npm/onetime@7.0.0","dependsOn":["pkg:npm/mimic-function@5.0.1"]},{"ref":"pkg:npm/onnxruntime-common@1.24.3","dependsOn":[]},{"ref":"pkg:npm/onnxruntime-node@1.24.3","dependsOn":["pkg:npm/adm-zip@0.5.16","pkg:npm/global-agent@3.0.0","pkg:npm/onnxruntime-common@1.24.3"]},{"ref":"pkg:npm/openapi-types@12.1.3","dependsOn":[]},{"ref":"pkg:npm/ora@5.4.1","dependsOn":["pkg:npm/bl@4.1.0","pkg:npm/chalk@4.1.2","pkg:npm/cli-cursor@3.1.0","pkg:npm/cli-spinners@2.9.2","pkg:npm/is-interactive@1.0.0","pkg:npm/is-unicode-supported@0.1.0","pkg:npm/log-symbols@4.1.0","pkg:npm/strip-ansi@6.0.1","pkg:npm/wcwidth@1.0.1"]},{"ref":"pkg:npm/ora@8.2.0","dependsOn":["pkg:npm/chalk@5.6.2","pkg:npm/cli-cursor@5.0.0","pkg:npm/cli-spinners@2.9.2","pkg:npm/is-interactive@2.0.0","pkg:npm/is-unicode-supported@2.1.0","pkg:npm/log-symbols@6.0.0","pkg:npm/stdin-discarder@0.2.2","pkg:npm/string-width@7.2.0","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/p-cancelable@2.1.1","dependsOn":[]},{"ref":"pkg:npm/p-defer@1.0.0","dependsOn":[]},{"ref":"pkg:npm/p-limit@2.3.0","dependsOn":["pkg:npm/p-try@2.2.0"]},{"ref":"pkg:npm/p-map@4.0.0","dependsOn":["pkg:npm/aggregate-error@3.1.0"]},{"ref":"pkg:npm/p-try@2.2.0","dependsOn":[]},{"ref":"pkg:npm/package-json-from-dist@1.0.1","dependsOn":[]},{"ref":"pkg:npm/packageurl-js@2.0.1","dependsOn":[]},{"ref":"pkg:npm/parent-module@1.0.1","dependsOn":["pkg:npm/callsites@3.1.0"]},{"ref":"pkg:npm/parse-json@5.2.0","dependsOn":["pkg:npm/@babel/code-frame@7.29.0","pkg:npm/error-ex@1.3.4","pkg:npm/json-parse-even-better-errors@2.3.1","pkg:npm/lines-and-columns@1.2.4"]},{"ref":"pkg:npm/parse-ms@4.0.0","dependsOn":[]},{"ref":"pkg:npm/parse-passwd@1.0.0","dependsOn":[]},{"ref":"pkg:npm/parseurl@1.3.3","dependsOn":[]},{"ref":"pkg:npm/path-expression-matcher@1.5.0","dependsOn":[]},{"ref":"pkg:npm/path-is-absolute@1.0.1","dependsOn":[]},{"ref":"pkg:npm/path-key@3.1.1","dependsOn":[]},{"ref":"pkg:npm/path-key@4.0.0","dependsOn":[]},{"ref":"pkg:npm/path-scurry@1.11.1","dependsOn":["pkg:npm/lru-cache@10.4.3","pkg:npm/minipass@7.1.3"]},{"ref":"pkg:npm/path-scurry@2.0.2","dependsOn":["pkg:npm/lru-cache@11.3.5","pkg:npm/minipass@7.1.3"]},{"ref":"pkg:npm/path-to-regexp@8.4.2","dependsOn":[]},{"ref":"pkg:npm/picocolors@1.1.1","dependsOn":[]},{"ref":"pkg:npm/picomatch@2.3.2","dependsOn":[]},{"ref":"pkg:npm/pino-abstract-transport@2.0.0","dependsOn":["pkg:npm/split2@4.2.0"]},{"ref":"pkg:npm/pino-abstract-transport@3.0.0","dependsOn":["pkg:npm/split2@4.2.0"]},{"ref":"pkg:npm/pino-pretty@13.1.3","dependsOn":["pkg:npm/colorette@2.0.20","pkg:npm/dateformat@4.6.3","pkg:npm/fast-copy@4.0.3","pkg:npm/fast-safe-stringify@2.1.1","pkg:npm/help-me@5.0.0","pkg:npm/joycon@3.1.1","pkg:npm/minimist@1.2.8","pkg:npm/on-exit-leak-free@2.1.2","pkg:npm/pino-abstract-transport@3.0.0","pkg:npm/pump@3.0.4","pkg:npm/secure-json-parse@4.1.0","pkg:npm/sonic-boom@4.2.1","pkg:npm/strip-json-comments@5.0.3"]},{"ref":"pkg:npm/pino-std-serializers@7.1.0","dependsOn":[]},{"ref":"pkg:npm/pino@9.14.0","dependsOn":["pkg:npm/@pinojs/redact@0.4.0","pkg:npm/atomic-sleep@1.0.0","pkg:npm/on-exit-leak-free@2.1.2","pkg:npm/pino-abstract-transport@2.0.0","pkg:npm/pino-std-serializers@7.1.0","pkg:npm/process-warning@5.0.0","pkg:npm/quick-format-unescaped@4.0.4","pkg:npm/real-require@0.2.0","pkg:npm/safe-stable-stringify@2.5.0","pkg:npm/sonic-boom@4.2.1","pkg:npm/thread-stream@3.1.0"]},{"ref":"pkg:npm/piscina@5.1.4","dependsOn":[]},{"ref":"pkg:npm/pkce-challenge@5.0.1","dependsOn":[]},{"ref":"pkg:npm/prebuild-install@7.1.3","dependsOn":["pkg:npm/detect-libc@2.1.2","pkg:npm/expand-template@2.0.3","pkg:npm/github-from-package@0.0.0","pkg:npm/minimist@1.2.8","pkg:npm/mkdirp-classic@0.5.3","pkg:npm/napi-build-utils@2.0.0","pkg:npm/node-abi@3.89.0","pkg:npm/pump@3.0.4","pkg:npm/rc@1.2.8","pkg:npm/simple-get@4.0.1","pkg:npm/tar-fs@2.1.4","pkg:npm/tunnel-agent@0.6.0"]},{"ref":"pkg:npm/pretty-ms@9.3.0","dependsOn":["pkg:npm/parse-ms@4.0.0"]},{"ref":"pkg:npm/process-warning@5.0.0","dependsOn":[]},{"ref":"pkg:npm/proxy-addr@2.0.7","dependsOn":["pkg:npm/forwarded@0.2.0","pkg:npm/ipaddr.js@1.9.1"]},{"ref":"pkg:npm/pump@3.0.4","dependsOn":["pkg:npm/end-of-stream@1.4.5","pkg:npm/once@1.4.0"]},{"ref":"pkg:npm/punycode@2.3.1","dependsOn":[]},{"ref":"pkg:npm/pupt@1.4.1","dependsOn":["pkg:npm/@homebridge/node-pty-prebuilt-multiarch@0.11.14","pkg:npm/@inquirer/core@10.3.2","pkg:npm/@inquirer/prompts@7.10.1","pkg:npm/@inquirer/testing@2.1.53","pkg:npm/@inquirer/type@3.0.10","pkg:npm/@types/uuid@10.0.0","pkg:npm/boxen@8.0.1","pkg:npm/chalk@5.6.2","pkg:npm/command-exists@1.2.9","pkg:npm/commander@14.0.3","pkg:npm/cosmiconfig@9.0.1","pkg:npm/execa@9.6.1","pkg:npm/fs-extra@11.3.4","pkg:npm/glob@11.1.0","pkg:npm/gray-matter@4.0.3","pkg:npm/handlebars@4.7.9","pkg:npm/minimatch@10.2.5","pkg:npm/minisearch@7.2.0","pkg:npm/ora@8.2.0","pkg:npm/pino-pretty@13.1.3","pkg:npm/pino@9.14.0","pkg:npm/simple-git@3.36.0","pkg:npm/strip-ansi@7.2.0","pkg:npm/uuid@14.0.0","pkg:npm/yaml@2.8.3","pkg:npm/zod@3.25.76"]},{"ref":"pkg:npm/qs@6.15.1","dependsOn":["pkg:npm/side-channel@1.1.0"]},{"ref":"pkg:npm/queue-microtask@1.2.3","dependsOn":[]},{"ref":"pkg:npm/quick-format-unescaped@4.0.4","dependsOn":[]},{"ref":"pkg:npm/quick-lru@5.1.1","dependsOn":[]},{"ref":"pkg:npm/railroad-diagrams@1.0.0","dependsOn":[]},{"ref":"pkg:npm/randexp@0.4.6","dependsOn":["pkg:npm/discontinuous-range@1.0.0","pkg:npm/ret@0.1.15"]},{"ref":"pkg:npm/range-parser@1.2.1","dependsOn":[]},{"ref":"pkg:npm/raw-body@3.0.2","dependsOn":["pkg:npm/bytes@3.1.2","pkg:npm/http-errors@2.0.1","pkg:npm/iconv-lite@0.7.2","pkg:npm/unpipe@1.0.0"]},{"ref":"pkg:npm/rc@1.2.8","dependsOn":["pkg:npm/deep-extend@0.6.0","pkg:npm/ini@1.3.8","pkg:npm/minimist@1.2.8","pkg:npm/strip-json-comments@2.0.1"]},{"ref":"pkg:npm/read-installed-packages@2.0.1","dependsOn":["pkg:npm/@npmcli/fs@3.1.1","pkg:npm/debug@4.4.3","pkg:npm/read-package-json@6.0.4","pkg:npm/semver@7.7.4","pkg:npm/slide@1.1.6"]},{"ref":"pkg:npm/read-package-json@6.0.4","dependsOn":["pkg:npm/glob@10.5.0","pkg:npm/json-parse-even-better-errors@3.0.2","pkg:npm/normalize-package-data@5.0.0","pkg:npm/npm-normalize-package-bin@3.0.1"]},{"ref":"pkg:npm/readable-stream@3.6.2","dependsOn":["pkg:npm/inherits@2.0.4","pkg:npm/string_decoder@1.3.0","pkg:npm/util-deprecate@1.0.2"]},{"ref":"pkg:npm/real-require@0.2.0","dependsOn":[]},{"ref":"pkg:npm/require-directory@2.1.1","dependsOn":[]},{"ref":"pkg:npm/require-from-string@2.0.2","dependsOn":[]},{"ref":"pkg:npm/resolve-alpn@1.2.1","dependsOn":[]},{"ref":"pkg:npm/resolve-dir@1.0.1","dependsOn":["pkg:npm/expand-tilde@2.0.2","pkg:npm/global-modules@1.0.0"]},{"ref":"pkg:npm/resolve-from@4.0.0","dependsOn":[]},{"ref":"pkg:npm/resolve-from@5.0.0","dependsOn":[]},{"ref":"pkg:npm/responselike@2.0.1","dependsOn":["pkg:npm/lowercase-keys@2.0.0"]},{"ref":"pkg:npm/restore-cursor@3.1.0","dependsOn":["pkg:npm/onetime@5.1.2","pkg:npm/signal-exit@3.0.7"]},{"ref":"pkg:npm/restore-cursor@5.1.0","dependsOn":["pkg:npm/onetime@7.0.0","pkg:npm/signal-exit@4.1.0"]},{"ref":"pkg:npm/ret@0.1.15","dependsOn":[]},{"ref":"pkg:npm/reusify@1.1.0","dependsOn":[]},{"ref":"pkg:npm/rfc4648@1.5.4","dependsOn":[]},{"ref":"pkg:npm/rfdc@1.4.1","dependsOn":[]},{"ref":"pkg:npm/roarr@2.15.4","dependsOn":["pkg:npm/boolean@3.2.0","pkg:npm/detect-node@2.1.0","pkg:npm/globalthis@1.0.4","pkg:npm/json-stringify-safe@5.0.1","pkg:npm/semver-compare@1.0.0","pkg:npm/sprintf-js@1.1.3"]},{"ref":"pkg:npm/router@2.2.0","dependsOn":["pkg:npm/debug@4.4.3","pkg:npm/depd@2.0.0","pkg:npm/is-promise@4.0.0","pkg:npm/parseurl@1.3.3","pkg:npm/path-to-regexp@8.4.2"]},{"ref":"pkg:npm/run-async@2.4.1","dependsOn":[]},{"ref":"pkg:npm/run-parallel@1.2.0","dependsOn":["pkg:npm/queue-microtask@1.2.3"]},{"ref":"pkg:npm/rxjs@7.8.1","dependsOn":["pkg:npm/tslib@2.8.1"]},{"ref":"pkg:npm/safe-buffer@5.2.1","dependsOn":[]},{"ref":"pkg:npm/safe-stable-stringify@2.5.0","dependsOn":[]},{"ref":"pkg:npm/safer-buffer@2.1.2","dependsOn":[]},{"ref":"pkg:npm/schemes@1.4.0","dependsOn":["pkg:npm/extend@3.0.2"]},{"ref":"pkg:npm/section-matter@1.0.0","dependsOn":["pkg:npm/extend-shallow@2.0.1","pkg:npm/kind-of@6.0.3"]},{"ref":"pkg:npm/secure-json-parse@4.1.0","dependsOn":[]},{"ref":"pkg:npm/semver-compare@1.0.0","dependsOn":[]},{"ref":"pkg:npm/semver@7.7.4","dependsOn":[]},{"ref":"pkg:npm/send@1.2.1","dependsOn":["pkg:npm/debug@4.4.3","pkg:npm/encodeurl@2.0.0","pkg:npm/escape-html@1.0.3","pkg:npm/etag@1.8.1","pkg:npm/fresh@2.0.0","pkg:npm/http-errors@2.0.1","pkg:npm/mime-types@3.0.2","pkg:npm/ms@2.1.3","pkg:npm/on-finished@2.4.1","pkg:npm/range-parser@1.2.1","pkg:npm/statuses@2.0.2"]},{"ref":"pkg:npm/serialize-error@7.0.1","dependsOn":["pkg:npm/type-fest@0.13.1"]},{"ref":"pkg:npm/serve-static@2.2.1","dependsOn":["pkg:npm/encodeurl@2.0.0","pkg:npm/escape-html@1.0.3","pkg:npm/parseurl@1.3.3","pkg:npm/send@1.2.1"]},{"ref":"pkg:npm/setprototypeof@1.2.0","dependsOn":[]},{"ref":"pkg:npm/shebang-command@2.0.0","dependsOn":["pkg:npm/shebang-regex@3.0.0"]},{"ref":"pkg:npm/shebang-regex@3.0.0","dependsOn":[]},{"ref":"pkg:npm/side-channel-list@1.0.1","dependsOn":["pkg:npm/es-errors@1.3.0","pkg:npm/object-inspect@1.13.4"]},{"ref":"pkg:npm/side-channel-map@1.0.1","dependsOn":["pkg:npm/call-bound@1.0.4","pkg:npm/es-errors@1.3.0","pkg:npm/get-intrinsic@1.3.0","pkg:npm/object-inspect@1.13.4"]},{"ref":"pkg:npm/side-channel-weakmap@1.0.2","dependsOn":["pkg:npm/call-bound@1.0.4","pkg:npm/es-errors@1.3.0","pkg:npm/get-intrinsic@1.3.0","pkg:npm/object-inspect@1.13.4","pkg:npm/side-channel-map@1.0.1"]},{"ref":"pkg:npm/side-channel@1.1.0","dependsOn":["pkg:npm/es-errors@1.3.0","pkg:npm/object-inspect@1.13.4","pkg:npm/side-channel-list@1.0.1","pkg:npm/side-channel-map@1.0.1","pkg:npm/side-channel-weakmap@1.0.2"]},{"ref":"pkg:npm/signal-exit@3.0.7","dependsOn":[]},{"ref":"pkg:npm/signal-exit@4.1.0","dependsOn":[]},{"ref":"pkg:npm/simple-concat@1.0.1","dependsOn":[]},{"ref":"pkg:npm/simple-get@4.0.1","dependsOn":["pkg:npm/decompress-response@6.0.0","pkg:npm/once@1.4.0","pkg:npm/simple-concat@1.0.1"]},{"ref":"pkg:npm/simple-git@3.36.0","dependsOn":["pkg:npm/@kwsites/file-exists@1.1.1","pkg:npm/@kwsites/promise-deferred@1.1.1","pkg:npm/@simple-git/args-pathspec@1.0.3","pkg:npm/@simple-git/argv-parser@1.1.1","pkg:npm/debug@4.4.3"]},{"ref":"pkg:npm/slice-ansi@7.1.2","dependsOn":["pkg:npm/ansi-styles@6.2.3","pkg:npm/is-fullwidth-code-point@5.1.0"]},{"ref":"pkg:npm/slice-ansi@8.0.0","dependsOn":["pkg:npm/ansi-styles@6.2.3","pkg:npm/is-fullwidth-code-point@5.1.0"]},{"ref":"pkg:npm/slide@1.1.6","dependsOn":[]},{"ref":"pkg:npm/smtp-address-parser@1.1.0","dependsOn":["pkg:npm/nearley@2.20.1"]},{"ref":"pkg:npm/snyk-config@5.3.0","dependsOn":["pkg:npm/async@3.2.6","pkg:npm/debug@4.4.3","pkg:npm/lodash.merge@4.6.2","pkg:npm/minimist@1.2.8"]},{"ref":"pkg:npm/snyk-nodejs-lockfile-parser@2.7.0","dependsOn":["pkg:npm/@snyk/dep-graph@2.16.7","pkg:npm/@snyk/error-catalog-nodejs-public@5.80.0","pkg:npm/@snyk/graphlib@2.1.9-patch.3","pkg:npm/@yarnpkg/core@4.6.0","pkg:npm/@yarnpkg/lockfile@1.1.0","pkg:npm/dependency-path@9.2.8","pkg:npm/event-loop-spinner@2.3.2","pkg:npm/js-yaml@4.1.1","pkg:npm/lodash.clonedeep@4.5.0","pkg:npm/lodash.flatmap@4.5.0","pkg:npm/lodash.isempty@4.4.0","pkg:npm/lodash.topairs@4.3.0","pkg:npm/micromatch@4.0.8","pkg:npm/p-map@4.0.0","pkg:npm/semver@7.7.4","pkg:npm/snyk-config@5.3.0","pkg:npm/tslib@1.14.1","pkg:npm/uuid@14.0.0"]},{"ref":"pkg:npm/sonic-boom@4.2.1","dependsOn":["pkg:npm/atomic-sleep@1.0.0"]},{"ref":"pkg:npm/source-map@0.6.1","dependsOn":[]},{"ref":"pkg:npm/spdx-compare@1.0.0","dependsOn":["pkg:npm/array-find-index@1.0.2","pkg:npm/spdx-expression-parse@3.0.1","pkg:npm/spdx-ranges@2.1.1"]},{"ref":"pkg:npm/spdx-correct@3.2.0","dependsOn":["pkg:npm/spdx-expression-parse@3.0.1","pkg:npm/spdx-license-ids@3.0.12"]},{"ref":"pkg:npm/spdx-exceptions@2.3.0","dependsOn":[]},{"ref":"pkg:npm/spdx-expression-parse@3.0.1","dependsOn":["pkg:npm/spdx-exceptions@2.3.0","pkg:npm/spdx-license-ids@3.0.12"]},{"ref":"pkg:npm/spdx-license-ids@3.0.12","dependsOn":[]},{"ref":"pkg:npm/spdx-ranges@2.1.1","dependsOn":[]},{"ref":"pkg:npm/spdx-satisfies@5.0.1","dependsOn":["pkg:npm/spdx-compare@1.0.0","pkg:npm/spdx-expression-parse@3.0.1","pkg:npm/spdx-ranges@2.1.1"]},{"ref":"pkg:npm/split2@4.2.0","dependsOn":[]},{"ref":"pkg:npm/sprintf-js@1.1.3","dependsOn":[]},{"ref":"pkg:npm/statuses@2.0.2","dependsOn":[]},{"ref":"pkg:npm/stdin-discarder@0.2.2","dependsOn":[]},{"ref":"pkg:npm/string-width@4.2.3","dependsOn":["pkg:npm/emoji-regex@8.0.0","pkg:npm/is-fullwidth-code-point@3.0.0","pkg:npm/strip-ansi@6.0.1"]},{"ref":"pkg:npm/string-width@5.1.2","dependsOn":["pkg:npm/eastasianwidth@0.2.0","pkg:npm/emoji-regex@9.2.2","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/string-width@7.2.0","dependsOn":["pkg:npm/emoji-regex@10.4.0","pkg:npm/get-east-asian-width@1.5.0","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/string-width@8.2.0","dependsOn":["pkg:npm/get-east-asian-width@1.5.0","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/string_decoder@1.3.0","dependsOn":["pkg:npm/safe-buffer@5.2.1"]},{"ref":"pkg:npm/strip-ansi@6.0.1","dependsOn":["pkg:npm/ansi-regex@5.0.1"]},{"ref":"pkg:npm/strip-ansi@7.2.0","dependsOn":["pkg:npm/ansi-regex@6.2.2"]},{"ref":"pkg:npm/strip-bom-string@1.0.0","dependsOn":[]},{"ref":"pkg:npm/strip-bom@4.0.0","dependsOn":[]},{"ref":"pkg:npm/strip-final-newline@4.0.0","dependsOn":[]},{"ref":"pkg:npm/strip-json-comments@2.0.1","dependsOn":[]},{"ref":"pkg:npm/strip-json-comments@3.1.1","dependsOn":[]},{"ref":"pkg:npm/strip-json-comments@5.0.3","dependsOn":[]},{"ref":"pkg:npm/strnum@2.2.3","dependsOn":[]},{"ref":"pkg:npm/supports-color@5.5.0","dependsOn":["pkg:npm/has-flag@3.0.0"]},{"ref":"pkg:npm/supports-color@7.2.0","dependsOn":["pkg:npm/has-flag@4.0.0"]},{"ref":"pkg:npm/tar-fs@2.1.4","dependsOn":["pkg:npm/chownr@1.1.4","pkg:npm/mkdirp-classic@0.5.3","pkg:npm/pump@3.0.4","pkg:npm/tar-stream@2.2.0"]},{"ref":"pkg:npm/tar-stream@2.2.0","dependsOn":["pkg:npm/bl@4.1.0","pkg:npm/end-of-stream@1.4.5","pkg:npm/fs-constants@1.0.0","pkg:npm/inherits@2.0.4","pkg:npm/readable-stream@3.6.2"]},{"ref":"pkg:npm/tar@7.5.13","dependsOn":["pkg:npm/@isaacs/fs-minipass@4.0.1","pkg:npm/chownr@3.0.0","pkg:npm/minipass@7.1.3","pkg:npm/minizlib@3.1.0","pkg:npm/yallist@5.0.0"]},{"ref":"pkg:npm/thread-stream@3.1.0","dependsOn":["pkg:npm/real-require@0.2.0"]},{"ref":"pkg:npm/through@2.3.8","dependsOn":[]},{"ref":"pkg:npm/tinyexec@1.1.1","dependsOn":[]},{"ref":"pkg:npm/tinylogic@2.0.0","dependsOn":[]},{"ref":"pkg:npm/tmp@0.2.4","dependsOn":[]},{"ref":"pkg:npm/to-regex-range@5.0.1","dependsOn":["pkg:npm/is-number@7.0.0"]},{"ref":"pkg:npm/toidentifier@1.0.1","dependsOn":[]},{"ref":"pkg:npm/tree-sitter-c-sharp@0.23.5","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-c@0.23.6","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-c@0.24.1","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-cli@0.23.2","dependsOn":[]},{"ref":"pkg:npm/tree-sitter-cpp@0.23.4","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4","pkg:npm/tree-sitter-c@0.23.6"]},{"ref":"pkg:npm/tree-sitter-dart@0fc19c3a57b1109802af41d2b8f60d8835c5da3a?vcs_url=https://codeload.github.com/UserNobody14/tree-sitter-dart/tar.gz/0fc19c3a57b1109802af41d2b8f60d8835c5da3a","dependsOn":["pkg:npm/node-addon-api@7.1.1","pkg:npm/node-gyp-build@4.8.4","pkg:npm/tree-sitter@0.25.0"]},{"ref":"pkg:npm/tree-sitter-go@0.25.0","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-java@0.23.5","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-javascript@0.23.1","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-javascript@0.25.0","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-kotlin@0.3.8","dependsOn":["pkg:npm/node-addon-api@7.1.1","pkg:npm/node-gyp-build@4.8.4","pkg:npm/tree-sitter@0.25.0"]},{"ref":"pkg:npm/tree-sitter-php@0.24.2","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-python@0.25.0","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-ruby@0.23.1","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-rust@0.24.0","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/tree-sitter-swift@0.7.1","dependsOn":["pkg:npm/node-addon-api@8.5.0","pkg:npm/node-gyp-build@4.8.4","pkg:npm/tree-sitter-cli@0.23.2","pkg:npm/tree-sitter@0.25.0","pkg:npm/which@2.0.2"]},{"ref":"pkg:npm/tree-sitter-typescript@0.23.2","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4","pkg:npm/tree-sitter-javascript@0.23.1"]},{"ref":"pkg:npm/tree-sitter@0.25.0","dependsOn":["pkg:npm/node-addon-api@8.7.0","pkg:npm/node-gyp-build@4.8.4"]},{"ref":"pkg:npm/treeify@1.1.0","dependsOn":[]},{"ref":"pkg:npm/tslib@1.14.1","dependsOn":[]},{"ref":"pkg:npm/tslib@2.8.1","dependsOn":[]},{"ref":"pkg:npm/tunnel-agent@0.6.0","dependsOn":["pkg:npm/safe-buffer@5.2.1"]},{"ref":"pkg:npm/typanion@3.14.0","dependsOn":[]},{"ref":"pkg:npm/type-fest@0.13.1","dependsOn":[]},{"ref":"pkg:npm/type-fest@0.21.3","dependsOn":[]},{"ref":"pkg:npm/type-fest@4.41.0","dependsOn":[]},{"ref":"pkg:npm/type-is@2.0.1","dependsOn":["pkg:npm/content-type@1.0.5","pkg:npm/media-typer@1.1.0","pkg:npm/mime-types@3.0.2"]},{"ref":"pkg:npm/typedfastbitset@0.6.1","dependsOn":[]},{"ref":"pkg:npm/typescript@5.9.3","dependsOn":[]},{"ref":"pkg:npm/uglify-js@3.19.3","dependsOn":[]},{"ref":"pkg:npm/undici-types@6.21.0","dependsOn":[]},{"ref":"pkg:npm/unicorn-magic@0.3.0","dependsOn":[]},{"ref":"pkg:npm/universalify@2.0.1","dependsOn":[]},{"ref":"pkg:npm/unpipe@1.0.0","dependsOn":[]},{"ref":"pkg:npm/uri-js@4.4.1","dependsOn":["pkg:npm/punycode@2.3.1"]},{"ref":"pkg:npm/util-deprecate@1.0.2","dependsOn":[]},{"ref":"pkg:npm/uuid@14.0.0","dependsOn":[]},{"ref":"pkg:npm/validate-npm-package-license@3.0.4","dependsOn":["pkg:npm/spdx-correct@3.2.0","pkg:npm/spdx-expression-parse@3.0.1"]},{"ref":"pkg:npm/vary@1.1.2","dependsOn":[]},{"ref":"pkg:npm/wcwidth@1.0.1","dependsOn":["pkg:npm/defaults@1.0.4"]},{"ref":"pkg:npm/web-tree-sitter@0.26.8","dependsOn":[]},{"ref":"pkg:npm/which@1.3.1","dependsOn":["pkg:npm/isexe@2.0.0"]},{"ref":"pkg:npm/which@2.0.2","dependsOn":["pkg:npm/isexe@2.0.0"]},{"ref":"pkg:npm/widest-line@5.0.0","dependsOn":["pkg:npm/string-width@7.2.0"]},{"ref":"pkg:npm/word-wrap@1.2.5","dependsOn":[]},{"ref":"pkg:npm/wordwrap@1.0.0","dependsOn":[]},{"ref":"pkg:npm/wrap-ansi@10.0.0","dependsOn":["pkg:npm/ansi-styles@6.2.3","pkg:npm/string-width@8.2.0","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/wrap-ansi@6.2.0","dependsOn":["pkg:npm/ansi-styles@4.3.0","pkg:npm/string-width@4.2.3","pkg:npm/strip-ansi@6.0.1"]},{"ref":"pkg:npm/wrap-ansi@7.0.0","dependsOn":["pkg:npm/ansi-styles@4.3.0","pkg:npm/string-width@4.2.3","pkg:npm/strip-ansi@6.0.1"]},{"ref":"pkg:npm/wrap-ansi@8.1.0","dependsOn":["pkg:npm/ansi-styles@6.2.3","pkg:npm/string-width@5.1.2","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/wrap-ansi@9.0.2","dependsOn":["pkg:npm/ansi-styles@6.2.3","pkg:npm/string-width@7.2.0","pkg:npm/strip-ansi@7.2.0"]},{"ref":"pkg:npm/wrappy@1.0.2","dependsOn":[]},{"ref":"pkg:npm/write-file-atomic@7.0.1","dependsOn":["pkg:npm/signal-exit@4.1.0"]},{"ref":"pkg:npm/y18n@5.0.8","dependsOn":[]},{"ref":"pkg:npm/yallist@5.0.0","dependsOn":[]},{"ref":"pkg:npm/yaml@2.8.3","dependsOn":[]},{"ref":"pkg:npm/yargs-parser@21.1.1","dependsOn":[]},{"ref":"pkg:npm/yargs@17.7.2","dependsOn":["pkg:npm/cliui@8.0.1","pkg:npm/escalade@3.2.0","pkg:npm/get-caller-file@2.0.5","pkg:npm/require-directory@2.1.1","pkg:npm/string-width@4.2.3","pkg:npm/y18n@5.0.8","pkg:npm/yargs-parser@21.1.1"]},{"ref":"pkg:npm/yoctocolors-cjs@2.1.3","dependsOn":[]},{"ref":"pkg:npm/yoctocolors@2.1.2","dependsOn":[]},{"ref":"pkg:npm/zod-to-json-schema@3.25.2","dependsOn":["pkg:npm/zod@3.25.76"]},{"ref":"pkg:npm/zod@3.25.76","dependsOn":[]}]} \ No newline at end of file diff --git a/SPECS.md b/SPECS.md index 52e3e930..d26ccf7e 100644 --- a/SPECS.md +++ b/SPECS.md @@ -4,31 +4,37 @@ OpenCodeHub is an Apache-2.0, local-first code-intelligence toolchain for AI coding agents. It ingests a source tree into a hybrid knowledge graph -(structural relations + semantic vectors) stored in an embedded DuckDB file -and exposes that graph over the Model Context Protocol and a `codehub` CLI. -Agents use it to answer "what breaks if I change this, what depends on it, -where does this data flow" *before* they produce a diff. - -At ingestion time the system parses 14 languages via native `tree-sitter` -bindings (WASM fallback available), runs native SCIP indexers for -TypeScript/JavaScript, Python, Go, Rust, and Java to upgrade tree-sitter +(structural relations + semantic vectors) stored as a two-tier split — an +lbug graph (`@ladybugdb/core`, `graph.lbug`) plus a DuckDB temporal sibling +(`temporal.duckdb`), both under `/.codehub/` (ADR 0016) — and exposes +that graph over the Model Context Protocol and a `codehub` CLI. Agents use +it to answer "what breaks if I change this, what depends on it, where does +this data flow" *before* they produce a diff. + +At ingestion time the system parses 15 GA languages via `web-tree-sitter` +(WASM) — the only parse runtime, with no native opt-in (ADR 0015) — for the +first 14 plus a regex provider for fixed-format COBOL, runs SCIP indexers +for TypeScript/JavaScript, Python, Go, Rust, and Java to upgrade tree-sitter heuristic edges to compiler-grade edges, clusters the graph into Communities and Processes, and optionally populates embeddings from a pinned gte-modernbert-base ONNX model (fp32 ~596 MB or int8 ~150 MB) or an OpenAI-compatible HTTP endpoint. -At query time it exposes an MCP server with roughly 27 tools (`query`, -`context`, `impact`, `detect_changes`, `rename`, `sql`, scanner / +At query time it exposes an MCP server with 28 tools (`query`, `context`, +`impact`, `signature`, `detect_changes`, `sql`, scanner / finding / dependency / verdict / route tools, and cross-repo `group_*` tools), along with a CLI that mirrors the main tools plus administrative -commands (`analyze`, `setup`, `doctor`, `ci-init`, `wiki`, etc.). +commands (`analyze`, `setup`, `doctor`, `ci-init`, `wiki`, etc.). The MCP +surface is read-only with respect to user source: no tool edits the +working tree. ## What this system is not - Not a language server. It runs SCIP indexers as one-shot artifact producers and does not speak LSP to editors directly. -- Not a SaaS. There is no server to operate; the graph is a single DuckDB - file under `~/.codehub/` (or `${CODEHUB_HOME}`). +- Not a SaaS. There is no server to operate; the graph lives as two + embedded files under `/.codehub/` (the lbug `graph.lbug` plus the + DuckDB `temporal.duckdb`). - Not a hosted vector DB. Embeddings are optional and local; there is no network dependency for analyze or query. - Not a ranking / recommendation product. The graph is precomputed at index @@ -88,12 +94,14 @@ files reuse prior extraction output. ## 2. Language coverage -2.1 The system shall provide tree-sitter extractors for TypeScript, -JavaScript (incl. TSX/JSX), Python, Go, Rust, Java, C#, C, C++, Ruby, -Kotlin, Swift, PHP, and Dart. +2.1 The system shall provide `web-tree-sitter` (WASM) extractors for +TypeScript, JavaScript (incl. TSX/JSX), Python, Go, Rust, Java, C#, C, C++, +Ruby, Kotlin, Swift, PHP, and Dart, plus a regex provider for fixed-format +COBOL — 15 GA languages in total. -2.2 Where a native tree-sitter binding is unavailable at runtime, the -system shall fall back to a WASM grammar. +2.2 The system shall run `web-tree-sitter` (WASM) as the only parse runtime +on Node 20, 22, and 24; there is no native opt-in (ADR 0015). All 15 +grammar `.wasm` blobs are vendored at `packages/ingestion/vendor/wasms/`. 2.3 Adding a new language shall require: registering a grammar dependency, implementing the `LanguageProvider` interface, and registering the @@ -122,27 +130,35 @@ indexers agree on `package{manager,name,version}`. ## 3. Storage & schema -3.1 The system shall persist graphs to a single DuckDB file -(`DB_FILE_NAME`) located under `resolveDbPath(repoMetaDir)` and managed -via `@duckdb/node-api`. - -3.2 The storage layer shall expose `IGraphStore` (open, close, -createSchema, bulkLoad, upsertEmbeddings, query, search, vectorSearch, -traverse, get/setMeta, healthCheck) so an alternate backend (e.g. -LanceDB) can slot in without consumer changes. +3.1 The system shall persist the graph tier to an lbug graph file +(`graph.lbug`, `@ladybugdb/core`) and the temporal tier — cochanges and +structured symbol summaries — to a DuckDB file (`temporal.duckdb`), both +under `/.codehub/`. Both files are written on every analyze; there is +no `CODEHUB_STORE` env var, no backend probe, and no single-file DuckDB +graph layout (ADR 0016). + +3.2 The storage layer shall segregate `IGraphStore` (graph workload: nodes, +edges, embeddings, multi-hop traversal) from `ITemporalStore` (cochanges, +summary cache). `IGraphStore` lives only on `GraphDbStore`; `DuckDbStore` +implements `ITemporalStore` only; `openStore()` composes them. The +segregated interfaces are the v1.0 contract for community-fork adapters +(AGE / Memgraph / Neo4j / Neptune target `IGraphStore`). If the lbug +binding fails to load, `open()` throws `GraphDbBindingError`. 3.3 While executing the `sql` MCP tool or `codehub sql` CLI, the system -shall reject non-read-only statements via `assertReadOnlySql` and apply a -5-second default timeout. +shall reject non-read-only statements and apply a 5-second default timeout. +The `sql` path targets the DuckDB temporal store (`cochanges` + +`symbol_summaries`); the node/edge graph is queried via the typed tools or +via Cypher (the `sql` tool's `cypher` argument), not this SQL path. -3.4 The vector search path shall use `hnsw_acorn` with filter-aware -predicate pushdown when embeddings are populated. +3.4 The vector search path shall use the lbug graph's filter-aware +nearest-neighbour traversal when embeddings are populated. -3.5 The full-text search path shall use the DuckDB `fts` extension with -BM25 scoring. +3.5 The full-text search path shall use BM25 scoring over the indexed +symbols. -3.6 Graph traversal beyond depth 1 shall be implemented with recursive -CTEs using `USING KEY`. +3.6 Multi-hop graph traversal shall be expressed in the lbug graph's Cypher +dialect rather than recursive SQL CTEs. 3.7 The storage layer shall write metadata (schema version, graph hash, last-analyzed commit) atomically and expose it via `getMeta`. @@ -172,7 +188,7 @@ single repo errors — per-repo errors must surface in --- -## 5. Impact, rename, diff, verdict +## 5. Impact, diff, verdict 5.1 When `impact` is invoked, the system shall traverse CALLS / REFERENCES / EXTENDS / METHOD_* / IMPLEMENTS / ACCESSES edges from the target up to @@ -184,16 +200,12 @@ breakdown, and a risk tier (`LOW` / `MEDIUM` / `HIGH` / `CRITICAL`). changed hunks to `AffectedSymbol`, `AffectedModule`, and `AffectedProcess` records with line-range precision. -5.3 When `rename` is invoked, the system shall default to dry-run and -produce `RenameEdit` objects tagged with per-site confidence; callers -must opt in to apply the edits. - -5.4 When `verdict` is invoked, the system shall classify a diff into one +5.3 When `verdict` is invoked, the system shall classify a diff into one of five tiers (`auto_merge`, `single_review`, `dual_review`, `expert_review`, `block`) with structured reasoning signals and recommended reviewers. -5.5 The CLI `verdict` command shall set `process.exitCode` to 0 for +5.4 The CLI `verdict` command shall set `process.exitCode` to 0 for merge-safe tiers, 1 for review-required tiers, and 2 for `block`. --- @@ -203,13 +215,15 @@ merge-safe tiers, 1 for review-required tiers, and 2 for `block`. 6.1 The MCP server shall advertise itself as `opencodehub` over stdio with an `instructions` block steering clients to call `list_repos` first. -6.2 The server shall register tools for: `list_repos`, `query`, `context`, -`impact`, `detect_changes`, `rename`, `sql`, `group_list`, `group_query`, -`group_status`, `group_contracts`, `group_sync`, `project_profile`, -`dependencies`, `license_audit`, `owners`, `list_findings`, -`list_findings_delta`, `list_dead_code`, `remove_dead_code`, `scan`, -`verdict`, `risk_trends`, `route_map`, `api_impact`, `shape_check`, and -`tool_map`. +6.2 The server shall register 28 tools: `list_repos`, `query`, `context`, +`impact`, `signature`, `detect_changes`, `sql`, `group_list`, +`group_query`, `group_status`, `group_contracts`, `group_cross_repo_links`, +`group_sync`, `project_profile`, `dependencies`, `license_audit`, `owners`, +`list_findings`, `list_findings_delta`, `list_dead_code`, +`scan`, `verdict`, `risk_trends`, `route_map`, +`api_impact`, `shape_check`, `tool_map`, and `pack_codebase`. No registered +tool mutates user source files; the MCP surface is read-only with respect +to the working tree. 6.3 Every per-repo tool shall accept an optional `repo` argument; when exactly one repo is registered, `repo` shall default to that repo; when @@ -304,9 +318,9 @@ buckets. and topic contracts for every repo in the group and write `contracts.json` matching the `ContractRegistry` shape. -9.3 The `group_contracts` MCP tool shall return DuckDB-backed -FETCHES↔Route edges together with the registry's signature-matched -cross-links. +9.3 The `group_contracts` MCP tool shall return graph-backed +FETCHES↔Route cross-links together with the registry's signature-matched +contracts. 9.4 While `group_query` is executing, the system shall never abort the fan-out on a single-repo failure. @@ -315,27 +329,25 @@ fan-out on a single-repo failure. ## 10. Evaluation & quality gates -10.1 The Python eval harness (`packages/eval`) shall run 49 parametrized -cases (7 MVP languages × 7 MCP tools) against the real `codehub mcp` -stdio server; the acceptance gate requires ≥ 40 passes. - -10.2 The gym harness (`packages/gym`) shall replay SCIP indexer golden -manifests for TypeScript, Python, Go, Rust, and (optionally) Java, and -gate on three layers: absolute F1 floor, relative F1 delta, and -per-case non-regression. +10.1 The retrieval / graph-quality evaluation harness and the per-language +F1 regression gym shall live in the sibling `opencodehub-testbed` +repository, not in this core repo's package set. The on-disk +`packages/eval/` directory carries no git-tracked files; any local +`.venv/`, `.pytest_cache/`, `.ruff_cache/`, or `src/` is untracked and +gitignored. -10.3 Every gym run shall emit a JSONL freeze/replay manifest pinning -`{manifest_version, corpus_commit, tool{name,version,sha256}, request, -result_set, captured_at}` for bit-exact replay — the `tool.name` is the -SCIP indexer identifier (e.g. `scip-python`, `rust-analyzer`) and the -indexer is re-invoked per replay rather than a long-running server. +10.2 The evaluation harness in `opencodehub-testbed` shall run its case +matrix against the real `codehub mcp` stdio server, and the gym shall +replay SCIP indexer golden manifests gating on three layers: absolute F1 +floor, relative F1 delta, and per-case non-regression. The full freeze / +replay manifest contract lives with the testbed. -10.4 `scripts/acceptance.sh` shall execute 15 named gates and exit +10.3 `scripts/acceptance.sh` shall execute 15 named gates and exit non-zero if any mandatory gate fails; soft gates (incremental p95, scanner smoke without semgrep, embeddings determinism without weights) may `SKIP` without changing the exit code. -10.5 `pnpm run check` shall run lint, typecheck, test, and banned-strings +10.4 `pnpm run check` shall run lint, typecheck, test, and banned-strings in that order and shall fail on the first non-zero exit. --- @@ -386,10 +398,10 @@ blocks. ## 13. Environment & distribution -13.1 The repo shall declare Node `>= 22` and pnpm `>= 10` in -`package.json.engines`. +13.1 The repo shall declare Node `>= 22` and pnpm `>= 11` in +`package.json.engines` (with `packageManager` pinned to `pnpm@11.1.0`). -13.2 `mise.toml` shall pin `node = "22"`, `pnpm = "10.32"`, `python = +13.2 `mise.toml` shall pin `node = "22"`, `pnpm = "11.1.0"`, `python = "3.12"`, and `uv = "latest"`; `mise install` shall be sufficient bootstrap. @@ -418,20 +430,3 @@ transport. 14.3 When the embedder weights probe fails, the server shall log a single structured warning and continue with BM25-only search. - ---- - -## 15. Open questions I noticed but couldn't resolve from the code - -- CLI self-version is hardcoded as `"0.0.0"` in `packages/cli/src/index.ts` - and the MCP server version is `"0.0.0"` in `packages/mcp/src/server.ts`, - while the root `package.json` is `0.1.1` and the README names `v0.1.0` - as the initial public release. I couldn't tell whether a release-time - rewrite is expected to stamp these. -- The README advertises "27 tools" but the `server.ts` registration list - I counted enumerates 27 tool registrations; the instructions prose - lists a similar but not identical subset. I couldn't tell whether - `group_contracts` is user-visible in the prose on purpose. -- ADR 0002 references a v2.0 and v2.1+ without a PRD file in the tree I - can see, so I could not verify which milestone the current main branch - corresponds to. diff --git a/USECASE.md b/USECASE.md index 50c3f9e5..f5c32973 100644 --- a/USECASE.md +++ b/USECASE.md @@ -91,11 +91,13 @@ for agents". `codehub setup` and `codehub analyze` once. - **Frame 5 — Action.** She re-asks the agent. This time the agent calls `impact("validateUser")`, gets `risk: HIGH, direct callers: 14, - affected processes: 3`, then `rename validateUser validateAccount` - (dry-run by default) and gets a coordinated edit plan with - per-site confidence. -- **Frame 6 — Resolution.** She reviews the plan, applies it. Tests - pass. Zero Monday fires. + affected processes: 3`, and `context("validateUser")` for every + inbound reference — including the two Python call sites grep missed. + OpenCodeHub is read-only, so the agent uses that complete edit list to + drive the rename in the editor itself, then calls `detect_changes` to + confirm the diff covers all 14 sites. +- **Frame 6 — Resolution.** She reviews the verified diff, applies it. + Tests pass. Zero Monday fires. - **Frame 7 — Ongoing benefit.** The plugin's `PostToolUse` hook re-analyzes after every `git commit | merge | rebase | pull`, so the graph stays current without her thinking about it. diff --git a/biome.json b/biome.json index 654be550..317849fd 100644 --- a/biome.json +++ b/biome.json @@ -1,8 +1,18 @@ { "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json", "root": true, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, "files": { - "includes": ["packages/**/src/**", "packages/**/test/**", "scripts/**/*.{js,mjs,ts}"], + "includes": [ + "packages/**/src/**", + "packages/**/test/**", + "scripts/**/*.{js,mjs,ts}", + "!!**/.claude/worktrees" + ], "ignoreUnknown": true }, "formatter": { diff --git a/mise.toml b/mise.toml index ff3db904..26f30709 100644 --- a/mise.toml +++ b/mise.toml @@ -39,9 +39,14 @@ depends = ["install"] # --------------------------------------------------------------------------- [tasks.build] -description = "Build all TypeScript packages" +description = "Build all TypeScript packages (excludes @opencodehub/docs — see docs:build)" depends = ["install"] -run = "pnpm -r build" +# Mirror CI (.github/workflows/ci.yml): @opencodehub/docs runs astro + +# rehype-mermaid + headless-Chromium (Playwright) and is built only by the +# docs owner workflow (pages.yml) / the `docs:build` task here. Excluding it +# keeps `mise run check` a faithful local mirror of the CI merge gate on a +# machine without Playwright browsers installed. +run = "pnpm --filter '!@opencodehub/docs' -r build" sources = ["packages/*/src/**/*.ts", "packages/*/package.json"] outputs = ["packages/*/dist/**"] @@ -104,9 +109,10 @@ run = "pnpm remove -g @opencodehub/cli" # --------------------------------------------------------------------------- [tasks.test] -description = "Run all package tests" +description = "Run all package tests (excludes @opencodehub/docs — mirrors CI)" depends = ["build"] -run = "pnpm -r test" +# Mirror CI: docs has no unit tests and its astro build needs Playwright. +run = "pnpm --filter '!@opencodehub/docs' -r test" [tasks.lint] description = "Biome check" @@ -121,8 +127,19 @@ description = "Biome format (write)" run = "pnpm exec biome format --write ." [tasks.typecheck] -description = "TypeScript typecheck (no emit) across workspace" -run = "pnpm -r exec tsc --noEmit" +description = "TypeScript typecheck (no emit) across workspace (excludes @opencodehub/docs — mirrors CI)" +run = "pnpm --filter '!@opencodehub/docs' -r exec tsc --noEmit" + +[tasks."docs:build"] +description = "Build the Astro docs site (installs headless Chromium for rehype-mermaid first)" +depends = ["install"] +# The docs build renders mermaid diagrams via rehype-mermaid → Playwright +# headless Chromium. CI does this in pages.yml; locally we install the +# browser on demand so the heavy dep never blocks `mise run check`. +run = """ +pnpm --filter @opencodehub/docs exec playwright install chromium +pnpm --filter @opencodehub/docs build +""" [tasks."banned-strings"] description = "Verify no banned identifiers leaked in" diff --git a/packages/analysis/README.md b/packages/analysis/README.md index 377d6f0b..5a8b7611 100644 --- a/packages/analysis/README.md +++ b/packages/analysis/README.md @@ -1,33 +1,32 @@ # @opencodehub/analysis -Impact analysis, rename, change detection, and staleness primitives for +Impact analysis, change detection, and staleness primitives for OpenCodeHub. Consumed by the MCP server and CLI; not intended for direct -use by end users. +use by end users. Every primitive is **read-only with respect to user +source** — analysis reads the graph and reports; it never edits the +working tree. ## Surface ```ts -import { computeImpact } from "@opencodehub/analysis/impact"; -import { detectChanges } from "@opencodehub/analysis/detect-changes"; -import { rename } from "@opencodehub/analysis/rename"; +import { runImpact } from "@opencodehub/analysis"; +import { runDetectChanges } from "@opencodehub/analysis"; +import { computeStaleness } from "@opencodehub/analysis"; ``` -- **`computeImpact`** — walks the graph from a target symbol to its +- **`runImpact`** — walks the graph from a target symbol to its dependents up to a configurable depth, returning a risk-tiered list of affected nodes (`packages/analysis/src/impact.ts`). -- **`detectChanges`** — maps an uncommitted or committed diff to the set of +- **`runDetectChanges`** — maps an uncommitted or committed diff to the set of symbols that were added, removed, or modified, grouped by execution flow (`packages/analysis/src/detect-changes.ts`). -- **`rename`** — graph-aware multi-file rename; dry-run is the default; writes - are gated behind `apply: true` (`packages/analysis/src/rename.ts`). -- **`staleness`** — compares the stored graph hash against the current HEAD to - report how stale the index is (`packages/analysis/src/staleness.ts`). +- **`computeStaleness`** — compares the stored graph hash against the current + HEAD to report how stale the index is (`packages/analysis/src/staleness.ts`). ## Design -- All writes use `write-file-atomic` so a crash between analyses never - leaves a corrupt graph on disk. -- The `rename` operation validates that the proposed new name does not - collide with any existing symbol before writing. +- Analysis primitives are pure reads over the graph and temporal stores; + the package writes only its own derived artifacts (e.g. risk snapshots + via `write-file-atomic`), never user source files. - Staleness is measured at call time and embedded in every MCP response as `_meta.codehub/staleness` so agents can surface it without an extra round-trip. diff --git a/packages/analysis/package.json b/packages/analysis/package.json index b1cf3cb7..0cf7b3bb 100644 --- a/packages/analysis/package.json +++ b/packages/analysis/package.json @@ -1,7 +1,7 @@ { "name": "@opencodehub/analysis", "version": "0.3.3", - "description": "OpenCodeHub — impact, rename, detect_changes, staleness", + "description": "OpenCodeHub — impact, detect_changes, staleness", "license": "Apache-2.0", "repository": { "type": "git", @@ -62,7 +62,6 @@ "static-analysis", "blast-radius", "impact-analysis", - "rename", "detect-changes" ], "engines": { diff --git a/packages/analysis/src/api-impact.test.ts b/packages/analysis/src/api-impact.test.ts new file mode 100644 index 00000000..01a7eb7d --- /dev/null +++ b/packages/analysis/src/api-impact.test.ts @@ -0,0 +1,111 @@ +/** + * `listApiImpact` tests. + * + * Focus: the accessed-keys index is built once per call, not once per + * (route × consumer file). A counting wrapper around the store proves the + * ACCESSES table is fetched a single time even when several routes each + * fan out to several consumer files. + */ + +import assert from "node:assert/strict"; +import { test } from "node:test"; +import type { RelationType } from "@opencodehub/core-types"; +import type { ListEdgesByTypeOptions } from "@opencodehub/storage"; +import { buildAccessedKeysByFileForTest, listApiImpact } from "./api-impact.js"; +import { FakeStore } from "./test-utils.js"; + +/** + * Wraps {@link FakeStore} and tallies how often a given relation type is + * fetched via `listEdgesByType`, so a test can assert the hoist removed the + * per-(route × consumer) re-scan. + */ +class CountingStore extends FakeStore { + readonly edgeTypeCalls = new Map(); + + override listEdgesByType(type: RelationType, opts: ListEdgesByTypeOptions = {}) { + this.edgeTypeCalls.set(type, (this.edgeTypeCalls.get(type) ?? 0) + 1); + return super.listEdgesByType(type, opts); + } +} + +/** Two routes, each consumed by two files that read response properties. */ +function seedTwoRoutesTwoConsumers(store: CountingStore): void { + // Routes. + store.addNode({ + id: "Route:GET /users", + kind: "Route", + name: "GET /users", + filePath: "src/routes/users.ts", + url: "/users", + method: "GET", + responseKeys: ["id", "name"], + }); + store.addNode({ + id: "Route:GET /orders", + kind: "Route", + name: "GET /orders", + filePath: "src/routes/orders.ts", + url: "/orders", + method: "GET", + responseKeys: ["total"], + }); + + // Consumer symbols (one per file) + their host files, wired FETCHES → route. + const consumers: Array<{ sym: string; file: string; route: string }> = [ + { sym: "Function:a", file: "src/a.ts", route: "Route:GET /users" }, + { sym: "Function:b", file: "src/b.ts", route: "Route:GET /users" }, + { sym: "Function:c", file: "src/c.ts", route: "Route:GET /orders" }, + { sym: "Function:d", file: "src/d.ts", route: "Route:GET /orders" }, + ]; + for (const { sym, file, route } of consumers) { + store.addNode({ id: sym, kind: "Function", name: sym, filePath: file }); + store.addEdge({ fromId: sym, toId: route, type: "FETCHES", confidence: 1 }); + } + + // ACCESSES edges: each consumer file reads a property off the response. + // `src/d.ts` reads a key (`bogus`) absent from /orders.responseKeys → MISMATCH. + const accesses: Array<{ from: string; file: string; prop: string }> = [ + { from: "Function:a", file: "src/a.ts", prop: "id" }, + { from: "Function:b", file: "src/b.ts", prop: "name" }, + { from: "Function:c", file: "src/c.ts", prop: "total" }, + { from: "Function:d", file: "src/d.ts", prop: "bogus" }, + ]; + for (const { from, prop } of accesses) { + const propId = `Property:${prop}`; + store.addNode({ id: propId, kind: "Property", name: prop, filePath: "" }); + store.addEdge({ fromId: from, toId: propId, type: "ACCESSES", confidence: 1 }); + } +} + +test("listApiImpact: ACCESSES table is fetched exactly once across all routes", async () => { + const store = new CountingStore(); + seedTwoRoutesTwoConsumers(store); + + const rows = await listApiImpact(store); + + // Before the hoist this was 4 (2 routes × 2 consumer files each); the index + // is now built a single time per call regardless of route/consumer fan-out. + assert.equal(store.edgeTypeCalls.get("ACCESSES"), 1); + + // Sanity: the mismatch on /orders (key `bogus` not in responseKeys) is still + // detected, proving the hoisted index produces identical classification. + const orders = rows.find((r) => r.route.url === "/orders"); + assert.ok(orders, "expected an /orders row"); + assert.deepEqual(orders.mismatches, ["src/d.ts"]); + + const users = rows.find((r) => r.route.url === "/users"); + assert.ok(users, "expected a /users row"); + assert.deepEqual(users.mismatches, []); +}); + +test("buildAccessedKeysByFile: buckets property names per source file, sorted", async () => { + const store = new FakeStore(); + store.addNode({ id: "Function:a", kind: "Function", name: "a", filePath: "src/a.ts" }); + store.addNode({ id: "Property:z", kind: "Property", name: "z", filePath: "" }); + store.addNode({ id: "Property:a", kind: "Property", name: "a", filePath: "" }); + store.addEdge({ fromId: "Function:a", toId: "Property:z", type: "ACCESSES", confidence: 1 }); + store.addEdge({ fromId: "Function:a", toId: "Property:a", type: "ACCESSES", confidence: 1 }); + + const index = await buildAccessedKeysByFileForTest(store); + assert.deepEqual(index.get("src/a.ts"), ["a", "z"]); +}); diff --git a/packages/analysis/src/api-impact.ts b/packages/analysis/src/api-impact.ts index 21fe319e..4e207135 100644 --- a/packages/analysis/src/api-impact.ts +++ b/packages/analysis/src/api-impact.ts @@ -67,6 +67,11 @@ export async function listApiImpact( return am < bm ? -1 : am > bm ? 1 : 0; }); + // Build the file → accessed-keys index once. The ACCESSES table and the + // endpoint hydration are global to the call; scanning them per + // (route × consumer file) re-fetched identical data R×C times. + const accessedKeysByFile = await buildAccessedKeysByFile(graph); + const out: ApiImpactRow[] = []; for (const r of sorted) { const responseKeys = r.responseKeys ?? []; @@ -80,7 +85,7 @@ export async function listApiImpact( const mismatches: string[] = []; for (const file of consumerFiles) { - const accessedKeys = await collectAccessedKeys(graph, file); + const accessedKeys = accessedKeysByFile.get(file) ?? []; const { status } = classifyShape(accessedKeys, responseKeys); if (status === "MISMATCH") mismatches.push(file); } @@ -112,6 +117,13 @@ export function worseRisk(a: RiskLevel, b: RiskLevel): RiskLevel { return order[a] >= order[b] ? a : b; } +/** @internal test-visible accessed-keys index builder. */ +export function buildAccessedKeysByFileForTest( + graph: IGraphStore, +): Promise> { + return buildAccessedKeysByFile(graph); +} + async function fetchFromIds( graph: IGraphStore, targetId: string, @@ -137,9 +149,18 @@ async function resolveFiles( return Array.from(set).sort(); } -async function collectAccessedKeys(graph: IGraphStore, file: string): Promise { +/** + * Build a `filePath → sorted accessed keys` index in a single pass over the + * ACCESSES table. Hoisted out of the per-(route × consumer file) loop in + * {@link listApiImpact}: the ACCESSES edges and the endpoint hydration are + * global to the call, so scanning them once and bucketing by source file + * replaces R×C identical full scans with one fetch. + */ +async function buildAccessedKeysByFile( + graph: IGraphStore, +): Promise> { const edges = await graph.listEdgesByType("ACCESSES"); - if (edges.length === 0) return []; + if (edges.length === 0) return new Map(); const allIds = new Set(); for (const e of edges) { allIds.add(e.from); @@ -148,15 +169,25 @@ async function collectAccessedKeys(graph: IGraphStore, file: string): Promise(); for (const n of allNodes) byId.set(n.id, n); - const names = new Set(); + const namesByFile = new Map>(); for (const e of edges) { const src = byId.get(e.from); - if (!src || src.filePath !== file) continue; + if (!src?.filePath || src.filePath.length === 0) continue; const target = byId.get(e.to); if (!target || target.kind !== "Property") continue; - if (target.name && target.name.length > 0) names.add(target.name); + if (!target.name || target.name.length === 0) continue; + let bucket = namesByFile.get(src.filePath); + if (bucket === undefined) { + bucket = new Set(); + namesByFile.set(src.filePath, bucket); + } + bucket.add(target.name); } - return Array.from(names).sort(); + const out = new Map(); + for (const [file, names] of namesByFile) { + out.set(file, Array.from(names).sort()); + } + return out; } async function fetchAffectedProcesses( diff --git a/packages/analysis/src/dead-code.ts b/packages/analysis/src/dead-code.ts index 3b2d6701..29f24807 100644 --- a/packages/analysis/src/dead-code.ts +++ b/packages/analysis/src/dead-code.ts @@ -50,9 +50,8 @@ export interface DeadCodeResult { /** * Relation types whose inbound edges count as "this symbol is referenced". * - * Mirrors `GRAPH_REFERRER_RELATIONS` in `rename.ts`, plus `REFERENCES` so a - * generic reference edge (e.g. type-only usage on the Python provider) also - * keeps a symbol alive. + * Includes `REFERENCES` so a generic reference edge (e.g. type-only usage on + * the Python provider) also keeps a symbol alive. */ const REFERRER_RELATIONS: readonly RelationType[] = [ "CALLS", diff --git a/packages/analysis/src/index.ts b/packages/analysis/src/index.ts index 5dee00d9..19ee0e25 100644 --- a/packages/analysis/src/index.ts +++ b/packages/analysis/src/index.ts @@ -89,7 +89,6 @@ export type { OwnerRow } from "./owners.js"; export { listOwners } from "./owners.js"; export type { Adjacency, EdgeLike } from "./page-rank.js"; export { buildAdjacency, pageRank } from "./page-rank.js"; -export { runRename } from "./rename.js"; export type { OrphanGrade } from "./risk.js"; export { maxOrphanMultiplier, @@ -134,9 +133,6 @@ export type { ImpactQuery, ImpactResult, NodeRef, - RenameEdit, - RenameQuery, - RenameResult, RiskLevel, StalenessResult, } from "./types.js"; diff --git a/packages/analysis/src/rename.test.ts b/packages/analysis/src/rename.test.ts deleted file mode 100644 index 2552caf2..00000000 --- a/packages/analysis/src/rename.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import assert from "node:assert/strict"; -import { join } from "node:path"; -import { test } from "node:test"; -import { runRename } from "./rename.js"; -import { FakeFs, FakeStore } from "./test-utils.js"; - -const REPO_ROOT = "/repo"; - -function abs(rel: string): string { - return join(REPO_ROOT, rel); -} - -test("runRename: dry-run emits graph edits for definition + internal caller", async () => { - const store = new FakeStore(); - const filePath = "src/a.ts"; - store.addNode({ id: `File:${filePath}:a.ts`, kind: "File", name: "a.ts", filePath }); - store.addNode({ - id: `Function:${filePath}:foo#0`, - kind: "Function", - name: "foo", - filePath, - startLine: 1, - endLine: 3, - }); - store.addNode({ - id: `Function:${filePath}:bar#0`, - kind: "Function", - name: "bar", - filePath, - startLine: 5, - endLine: 7, - }); - store.addEdge({ - fromId: `Function:${filePath}:bar#0`, - toId: `Function:${filePath}:foo#0`, - type: "CALLS", - confidence: 0.9, - }); - - const source = [ - "function foo() {", - " return 1;", - "}", - "", - "function bar() {", - " return foo();", - "}", - "", - ].join("\n"); - const fs = new FakeFs({ [abs(filePath)]: source }); - - const res = await runRename(store, { symbolName: "foo", newName: "foo2" }, fs, REPO_ROOT); - - assert.equal(res.applied, false); - assert.equal(res.ambiguous, false); - // Two graph edits: the definition on line 1 and the call inside bar on line 6. - assert.equal(res.edits.length, 2); - for (const edit of res.edits) { - assert.equal(edit.source, "graph"); - assert.equal(edit.confidence, 1.0); - assert.equal(edit.before, "foo"); - assert.equal(edit.after, "foo2"); - } - const lines = res.edits.map((e) => e.line).sort((a, b) => a - b); - assert.deepEqual(lines, [1, 6]); - // File on disk must not have changed in dry-run mode. - assert.equal(fs.files.get(abs(filePath)), source); -}); - -test("runRename: text fallback emits confidence-0.5 edits in uncovered files", async () => { - const store = new FakeStore(); - // Declare the graph-known symbol in file `a.ts` so the text sweep targets a - // sibling file `b.ts` that merely mentions the name in a comment. - const fileA = "src/a.ts"; - const fileB = "src/b.ts"; - store.addNode({ id: `File:${fileA}:a.ts`, kind: "File", name: "a.ts", filePath: fileA }); - store.addNode({ id: `File:${fileB}:b.ts`, kind: "File", name: "b.ts", filePath: fileB }); - store.addNode({ - id: `Function:${fileA}:fooHelper#0`, - kind: "Function", - name: "fooHelper", - filePath: fileA, - startLine: 1, - endLine: 2, - }); - - const aSrc = "function fooHelper() {}\n"; - const bSrc = "// mentions fooHelper in a comment\nexport const x = 1;\n"; - const fs = new FakeFs({ [abs(fileA)]: aSrc, [abs(fileB)]: bSrc }); - - const res = await runRename( - store, - { symbolName: "fooHelper", newName: "fooHelperV2" }, - fs, - REPO_ROOT, - ); - - const textEdits = res.edits.filter((e) => e.source === "text"); - const graphEdits = res.edits.filter((e) => e.source === "graph"); - assert.equal(graphEdits.length, 1, "definition site is graph-backed"); - assert.equal(graphEdits[0]?.filePath, fileA); - assert.equal(textEdits.length, 1, "the comment hit in b.ts should be text-backed"); - assert.equal(textEdits[0]?.filePath, fileB); - assert.equal(textEdits[0]?.confidence, 0.5); - assert.ok(res.hint?.includes("text-only")); -}); - -test("runRename: apply mode writes rewritten content atomically", async () => { - const store = new FakeStore(); - const filePath = "src/a.ts"; - store.addNode({ id: `File:${filePath}:a.ts`, kind: "File", name: "a.ts", filePath }); - store.addNode({ - id: `Function:${filePath}:foo#0`, - kind: "Function", - name: "foo", - filePath, - startLine: 1, - endLine: 1, - }); - - const source = "function foo() { return 1; }\n"; - const fs = new FakeFs({ [abs(filePath)]: source }); - - const res = await runRename( - store, - { symbolName: "foo", newName: "bar", dryRun: false }, - fs, - REPO_ROOT, - ); - - assert.equal(res.applied, true); - assert.equal(res.skipped.length, 0); - assert.equal(fs.files.get(abs(filePath)), "function bar() { return 1; }\n"); -}); - -test("runRename: apply mode handles multiple edits on the same line in right-to-left order", async () => { - const store = new FakeStore(); - const filePath = "src/a.ts"; - store.addNode({ id: `File:${filePath}:a.ts`, kind: "File", name: "a.ts", filePath }); - store.addNode({ - id: `Function:${filePath}:x#0`, - kind: "Function", - name: "x", - filePath, - startLine: 1, - endLine: 1, - }); - - const source = "function x() { return x() + x(); }\n"; - const fs = new FakeFs({ [abs(filePath)]: source }); - - const res = await runRename( - store, - { symbolName: "x", newName: "xx", dryRun: false }, - fs, - REPO_ROOT, - ); - - assert.equal(res.applied, true); - assert.equal(fs.files.get(abs(filePath)), "function xx() { return xx() + xx(); }\n"); -}); - -test("runRename: ambiguous target without scope returns hint and zero edits", async () => { - const store = new FakeStore(); - store.addNode({ - id: "Function:src/a.ts:foo#0", - kind: "Function", - name: "foo", - filePath: "src/a.ts", - startLine: 1, - endLine: 1, - }); - store.addNode({ - id: "Function:src/b.ts:foo#0", - kind: "Function", - name: "foo", - filePath: "src/b.ts", - startLine: 1, - endLine: 1, - }); - const fs = new FakeFs(); - - const res = await runRename(store, { symbolName: "foo", newName: "foo2" }, fs, REPO_ROOT); - - assert.equal(res.ambiguous, true); - assert.equal(res.edits.length, 0); - assert.ok(res.hint?.includes("scope.filePath")); -}); - -test("runRename: symbol not found returns empty result with hint", async () => { - const store = new FakeStore(); - const fs = new FakeFs(); - const res = await runRename(store, { symbolName: "nope", newName: "never" }, fs, REPO_ROOT); - assert.equal(res.edits.length, 0); - assert.equal(res.applied, false); - assert.ok(res.hint?.includes("not found")); -}); diff --git a/packages/analysis/src/rename.ts b/packages/analysis/src/rename.ts deleted file mode 100644 index 23d9c125..00000000 --- a/packages/analysis/src/rename.ts +++ /dev/null @@ -1,375 +0,0 @@ -/** - * Text-level rename driven by the code graph. - * - * The graph tells us which symbols reference the target (high-confidence - * hits); for everything else we fall back to a repo-wide word-boundary - * regex sweep (low-confidence hits, surfaced in the hint so the caller - * can review). This module only *generates* edits in dry-run mode; the - * actual file write happens via the injected {@link FsAbstraction} when - * `dryRun` is explicitly false. - */ - -import { isAbsolute, join } from "node:path"; -import type { RelationType } from "@opencodehub/core-types"; -import type { IGraphStore } from "@opencodehub/storage"; -import type { FsAbstraction, NodeRef, RenameEdit, RenameQuery, RenameResult } from "./types.js"; - -interface SymbolLocation extends NodeRef { - readonly startLine: number; - readonly endLine: number; -} - -const GRAPH_REFERRER_RELATIONS: readonly RelationType[] = [ - "CALLS", - "ACCESSES", - "EXTENDS", - "IMPLEMENTS", - "METHOD_OVERRIDES", - "METHOD_IMPLEMENTS", - "HAS_METHOD", -]; - -/** - * Escape characters that would otherwise be interpreted by the RegExp - * constructor. We pin the call sites to identifier names (letters, - * digits, underscores) so escaping should never fire in practice, but - * defensive escaping keeps us correct if someone routes a renamed - * operator through this helper. - */ -function escapeRegex(s: string): string { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - -function resolveAbs(repoRoot: string, relPath: string): string { - return isAbsolute(relPath) ? relPath : join(repoRoot, relPath); -} - -async function findCandidates( - store: IGraphStore, - symbolName: string, - scopeFile: string | undefined, -): Promise { - // Typed `listNodesByName(name, {filePath})` replaces a raw - // `WHERE name = ? [AND file_path = ?]` SELECT. The finder returns full - // GraphNodes; we map onto the local SymbolLocation shape so downstream - // rename logic stays unchanged. - const nodes = await store.listNodesByName( - symbolName, - scopeFile !== undefined ? { filePath: scopeFile } : {}, - ); - const out: SymbolLocation[] = []; - for (const node of nodes) { - const located = node as { readonly startLine?: unknown; readonly endLine?: unknown }; - const start = Number(located.startLine ?? Number.NaN); - const end = Number(located.endLine ?? Number.NaN); - out.push({ - id: node.id, - name: node.name, - filePath: node.filePath, - kind: node.kind, - startLine: Number.isFinite(start) ? start : 0, - endLine: Number.isFinite(end) ? end : 0, - }); - } - return out; -} - -async function referrersOf( - store: IGraphStore, - targetId: string, -): Promise { - // Typed `listEdges({types, toIds})` replaces a raw `WHERE - // r.to_id = ? AND r.type IN (...)` SELECT joined to nodes. The TS-side - // join hydrates referrer node metadata via `listNodes({ids})`. - const edges = await store.listEdges({ - types: GRAPH_REFERRER_RELATIONS, - toIds: [targetId], - }); - const referrerIds = Array.from(new Set(edges.map((e) => e.from))).filter((s) => s.length > 0); - if (referrerIds.length === 0) return []; - const nodes = await store.listNodes({ ids: referrerIds }); - const out: SymbolLocation[] = []; - for (const node of nodes) { - const located = node as { readonly startLine?: unknown; readonly endLine?: unknown }; - const start = Number(located.startLine ?? Number.NaN); - const end = Number(located.endLine ?? Number.NaN); - out.push({ - id: node.id, - name: node.name, - filePath: node.filePath, - kind: node.kind, - startLine: Number.isFinite(start) ? start : 0, - endLine: Number.isFinite(end) ? end : 0, - }); - } - return out; -} - -async function allRepoFiles(store: IGraphStore): Promise { - // Typed `listNodesByKind("File")` replaces a `SELECT DISTINCT - // file_path FROM nodes WHERE kind = 'File'` raw SELECT. - const files = await store.listNodesByKind("File"); - const seen = new Set(); - for (const node of files) { - if (node.filePath.length > 0) seen.add(node.filePath); - } - return [...seen].sort(); -} - -/** Sweep a buffer for every word-bounded hit. Returns edits in source order. */ -function findMatches( - content: string, - needle: string, - replacement: string, - filePath: string, - source: "graph" | "text", - confidence: number, - lineRange?: { readonly start: number; readonly end: number }, -): readonly RenameEdit[] { - const edits: RenameEdit[] = []; - const re = new RegExp(`\\b${escapeRegex(needle)}\\b`, "g"); - const lines = content.split("\n"); - for (let i = 0; i < lines.length; i += 1) { - const lineNumber = i + 1; - if (lineRange && (lineNumber < lineRange.start || lineNumber > lineRange.end)) { - continue; - } - const lineText = lines[i]; - if (lineText === undefined) continue; - re.lastIndex = 0; - for (;;) { - const match = re.exec(lineText); - if (match === null) break; - edits.push({ - filePath, - line: lineNumber, - column: match.index + 1, - before: needle, - after: replacement, - confidence, - source, - }); - } - } - return edits; -} - -/** Key used to dedupe edits across graph-phase and text-phase sweeps. */ -function editKey(e: RenameEdit): string { - return `${e.filePath}|${e.line}|${e.column}`; -} - -/** Apply edits to a single file buffer. Edits must target the same file. */ -function applyEdits(content: string, edits: readonly RenameEdit[]): string { - if (edits.length === 0) return content; - const byLine = new Map(); - for (const e of edits) { - const bucket = byLine.get(e.line) ?? []; - bucket.push(e); - byLine.set(e.line, bucket); - } - const lines = content.split("\n"); - const sortedLineNumbers = [...byLine.keys()].sort((a, b) => b - a); - for (const ln of sortedLineNumbers) { - const idx = ln - 1; - if (idx < 0 || idx >= lines.length) continue; - const edits = byLine.get(ln) ?? []; - // Apply right-to-left within the line so earlier column offsets stay valid. - const sorted = [...edits].sort((a, b) => b.column - a.column); - let line = lines[idx] ?? ""; - for (const e of sorted) { - const col = e.column - 1; - line = line.slice(0, col) + e.after + line.slice(col + e.before.length); - } - lines[idx] = line; - } - return lines.join("\n"); -} - -export async function runRename( - store: IGraphStore, - q: RenameQuery, - fs: FsAbstraction, - repoRoot: string, -): Promise { - const dryRun = q.dryRun !== false; // default true — destructive writes are opt-in. - const scopeFile = q.scope?.filePath; - - // 1. Resolve target. - const candidates = await findCandidates(store, q.symbolName, scopeFile); - if (candidates.length === 0) { - return { - edits: [], - applied: false, - skipped: [], - ambiguous: false, - hint: `Symbol "${q.symbolName}" was not found in the graph.`, - }; - } - if (candidates.length > 1 && !scopeFile) { - const locations = candidates.map((c) => `${c.filePath}:${c.startLine}`).join(", "); - return { - edits: [], - applied: false, - skipped: [], - ambiguous: true, - hint: `Multiple symbols named "${q.symbolName}" were found (${locations}). Rerun with scope.filePath to disambiguate.`, - }; - } - - const targets = candidates.length === 1 ? candidates : candidates; - - // 2. Graph-backed referrer edits (confidence 1.0). Also emit the - // definition site so the rename actually touches the declaration. - const graphEditsByKey = new Map(); - const graphFilesCovered = new Set(); - const readCache = new Map(); - - async function readFileCached(abs: string, rel: string): Promise { - if (readCache.has(rel)) return readCache.get(rel); - try { - const content = await fs.readFile(abs); - readCache.set(rel, content); - return content; - } catch { - return undefined; - } - } - - for (const tgt of targets) { - const abs = resolveAbs(repoRoot, tgt.filePath); - const defContent = await readFileCached(abs, tgt.filePath); - if (defContent !== undefined && tgt.startLine > 0) { - const defEdits = findMatches( - defContent, - q.symbolName, - q.newName, - tgt.filePath, - "graph", - 1.0, - { start: tgt.startLine, end: Math.max(tgt.startLine, tgt.endLine) }, - ); - for (const e of defEdits) { - graphEditsByKey.set(editKey(e), e); - graphFilesCovered.add(e.filePath); - } - } - - const referrers = await referrersOf(store, tgt.id); - // Group referrers by file so we only read each file once. - const referrersByFile = new Map(); - for (const r of referrers) { - // Skip self-references at the exact definition site; they're already - // covered by the defEdits sweep above. - if (r.id === tgt.id) continue; - const bucket = referrersByFile.get(r.filePath) ?? []; - bucket.push(r); - referrersByFile.set(r.filePath, bucket); - } - for (const [relPath, refs] of referrersByFile) { - const absPath = resolveAbs(repoRoot, relPath); - const content = await readFileCached(absPath, relPath); - if (content === undefined) continue; - for (const ref of refs) { - if (ref.startLine <= 0 || ref.endLine <= 0) continue; - const edits = findMatches(content, q.symbolName, q.newName, relPath, "graph", 1.0, { - start: ref.startLine, - end: ref.endLine, - }); - for (const e of edits) { - graphEditsByKey.set(editKey(e), e); - graphFilesCovered.add(e.filePath); - } - } - } - } - - // 3. Text-fallback sweep (confidence 0.5) over every file not yet - // covered by a graph edit. Honors `scope.filePath` if set. - const textEditsByKey = new Map(); - const filesToSweep = scopeFile ? [scopeFile] : await allRepoFiles(store); - for (const rel of filesToSweep) { - if (graphFilesCovered.has(rel)) continue; - const abs = resolveAbs(repoRoot, rel); - const content = await readFileCached(abs, rel); - if (content === undefined) continue; - const matches = findMatches(content, q.symbolName, q.newName, rel, "text", 0.5); - for (const e of matches) { - const key = editKey(e); - if (graphEditsByKey.has(key)) continue; - textEditsByKey.set(key, e); - } - } - - const graphEdits = [...graphEditsByKey.values()]; - const textEdits = [...textEditsByKey.values()]; - const combined = [...graphEdits, ...textEdits].sort((a, b) => { - const byFile = a.filePath.localeCompare(b.filePath); - if (byFile !== 0) return byFile; - if (a.line !== b.line) return a.line - b.line; - return a.column - b.column; - }); - - if (dryRun) { - const hint = - textEdits.length > 0 - ? `Includes ${textEdits.length} text-only edit${textEdits.length === 1 ? "" : "s"} (confidence 0.5). Review before applying.` - : undefined; - return { - edits: combined, - applied: false, - skipped: [], - ambiguous: false, - ...(hint ? { hint } : {}), - }; - } - - // 4. Apply. Group edits by file, rewrite in-memory, then write atomically. - const editsByFile = new Map(); - for (const e of combined) { - const bucket = editsByFile.get(e.filePath) ?? []; - bucket.push(e); - editsByFile.set(e.filePath, bucket); - } - - const skipped: { filePath: string; reason: string }[] = []; - for (const [rel, edits] of editsByFile) { - const abs = resolveAbs(repoRoot, rel); - const original = readCache.get(rel); - if (original === undefined) { - skipped.push({ filePath: rel, reason: "Could not read file for rewrite" }); - continue; - } - const rewritten = applyEdits(original, edits); - if (rewritten === original) continue; - try { - await fs.writeFileAtomic(abs, rewritten); - } catch (err) { - skipped.push({ - filePath: rel, - reason: `Write failed: ${(err as Error).message}`, - }); - } - } - - const hintParts: string[] = []; - if (textEdits.length > 0) { - hintParts.push( - `Applied ${textEdits.length} text-only edit${textEdits.length === 1 ? "" : "s"}; verify the changes compile.`, - ); - } - if (skipped.length > 0) { - hintParts.push( - `${skipped.length} file${skipped.length === 1 ? "" : "s"} skipped — see skipped[].`, - ); - } - const hint = hintParts.length > 0 ? hintParts.join(" ") : undefined; - - return { - edits: combined, - applied: true, - skipped, - ambiguous: false, - ...(hint ? { hint } : {}), - }; -} diff --git a/packages/analysis/src/types.ts b/packages/analysis/src/types.ts index 491c32bd..e4837c39 100644 --- a/packages/analysis/src/types.ts +++ b/packages/analysis/src/types.ts @@ -88,31 +88,6 @@ export interface ImpactResult { readonly hint?: string; } -export interface RenameQuery { - readonly symbolName: string; - readonly newName: string; - readonly dryRun?: boolean; - readonly scope?: { readonly filePath?: string }; -} - -export interface RenameEdit { - readonly filePath: string; - readonly line: number; - readonly column: number; - readonly before: string; - readonly after: string; - readonly confidence: number; - readonly source: "graph" | "text"; -} - -export interface RenameResult { - readonly edits: readonly RenameEdit[]; - readonly applied: boolean; - readonly skipped: readonly { readonly filePath: string; readonly reason: string }[]; - readonly ambiguous: boolean; - readonly hint?: string; -} - export interface DetectChangesQuery { readonly scope: "unstaged" | "staged" | "all" | "compare"; readonly compareRef?: string; diff --git a/packages/analysis/src/verdict.test.ts b/packages/analysis/src/verdict.test.ts index b1ac5ff3..df589128 100644 --- a/packages/analysis/src/verdict.test.ts +++ b/packages/analysis/src/verdict.test.ts @@ -12,8 +12,12 @@ */ import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; import { test } from "node:test"; +import { FakeStore } from "./test-utils.js"; import { + collectFindingsForTest, + collectReviewersForTest, computeBoundaryForTest, computeLabelsForTest, decideTierFromAggregate, @@ -23,6 +27,11 @@ import { import { renderVerdictMarkdown } from "./verdict-markdown.js"; import { DEFAULT_VERDICT_CONFIG, type VerdictTier } from "./verdict-types.js"; +/** sha256 of the lowercased email — the persisted Contributor.emailHash form. */ +function emailHash(email: string): string { + return createHash("sha256").update(email.toLowerCase()).digest("hex"); +} + function emptyFindings(): { errorCount: number; warningCount: number; @@ -298,3 +307,119 @@ test("verdict tier-flip: Function with cyclomaticComplexity=15 + low coverage assert.equal(tierBaseline, "auto_merge"); assert.equal(exitCodeForTier(tierBaseline), 0); }); + +test("collectReviewers: PR author excluded by emailHash in privacy mode (no emailPlain)", async () => { + const store = new FakeStore(); + const file = "src/payments.ts"; + const fileNodeId = `File:${file}:${file}`; + // Privacy mode: contributors carry only `emailHash` (no `emailPlain`). + // The author has the heaviest blame weight, so a naive top-N would + // recommend them as a reviewer of their own PR unless excluded by hash. + store.addNode({ + id: "Contributor:author", + kind: "Contributor", + name: "Author", + filePath: "", + emailHash: emailHash("author@example.com"), + }); + store.addNode({ + id: "Contributor:reviewer", + kind: "Contributor", + name: "Reviewer", + filePath: "", + emailHash: emailHash("reviewer@example.com"), + }); + store.addEdge({ + fromId: fileNodeId, + toId: "Contributor:author", + type: "OWNED_BY", + confidence: 0.9, + }); + store.addEdge({ + fromId: fileNodeId, + toId: "Contributor:reviewer", + type: "OWNED_BY", + confidence: 0.3, + }); + + const reviewers = await collectReviewersForTest(store, [file], "author@example.com"); + const names = reviewers.map((r) => r.name); + // The author must NOT recommend themselves; only the reviewer remains. + assert.deepEqual(names, ["Reviewer"]); +}); + +test("collectReviewers: author match is case-insensitive via lowercased hash", async () => { + const store = new FakeStore(); + const file = "src/a.ts"; + const fileNodeId = `File:${file}:${file}`; + store.addNode({ + id: "Contributor:author", + kind: "Contributor", + name: "Author", + filePath: "", + emailHash: emailHash("author@example.com"), + }); + store.addEdge({ + fromId: fileNodeId, + toId: "Contributor:author", + type: "OWNED_BY", + confidence: 1, + }); + + // Caller supplies a mixed-case author email; exclusion still fires because + // both sides hash the lowercased form. + const reviewers = await collectReviewersForTest(store, [file], "Author@Example.com"); + assert.deepEqual(reviewers, []); +}); + +test("collectFindings: overlapping symbol+file finding on same ruleId is counted once, not dropped", async () => { + const store = new FakeStore(); + const sym = "Function:handler"; + store.addNode({ id: sym, kind: "Function", name: "handler", filePath: "src/h.ts" }); + // A symbol-level WARNING and a file-level ERROR that share one ruleId. + // The old fallback de-duped by ruleId, so the file error's severity was + // never counted — understating errorCount and lowering the tier. + store.addNode({ + id: "Finding:warn", + kind: "Finding", + name: "warn", + filePath: "src/h.ts", + ruleId: "no-unused", + severity: "warning", + }); + store.addNode({ + id: "Finding:err", + kind: "Finding", + name: "err", + filePath: "src/h.ts", + ruleId: "no-unused", + severity: "error", + }); + store.addEdge({ fromId: "Finding:warn", toId: sym, type: "FOUND_IN", confidence: 1 }); + + const summary = await collectFindingsForTest(store, [sym], ["src/h.ts"]); + assert.equal(summary.errorCount, 1); + assert.equal(summary.warningCount, 1); + // Two distinct findings sharing one ruleId → byRule count of 2. + assert.equal(summary.byRule.get("no-unused"), 2); +}); + +test("collectFindings: a finding reachable by both symbol and file paths is not double-counted", async () => { + const store = new FakeStore(); + const sym = "Function:handler"; + store.addNode({ id: sym, kind: "Function", name: "handler", filePath: "src/h.ts" }); + store.addNode({ + id: "Finding:err", + kind: "Finding", + name: "err", + filePath: "src/h.ts", + ruleId: "boom", + severity: "error", + }); + // Same finding is tied to the symbol AND lives in a changed file. + store.addEdge({ fromId: "Finding:err", toId: sym, type: "FOUND_IN", confidence: 1 }); + + const summary = await collectFindingsForTest(store, [sym], ["src/h.ts"]); + assert.equal(summary.errorCount, 1); + assert.equal(summary.byRule.get("boom"), 1); +}); diff --git a/packages/analysis/src/verdict.ts b/packages/analysis/src/verdict.ts index 468e5e4b..8396a347 100644 --- a/packages/analysis/src/verdict.ts +++ b/packages/analysis/src/verdict.ts @@ -19,6 +19,7 @@ */ import { execFile } from "node:child_process"; +import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { promisify } from "node:util"; @@ -435,6 +436,35 @@ export function exitCodeForTier(tier: VerdictTier): 0 | 1 | 2 { return TIER_EXIT_CODES[tier]; } +/** @internal test-visible findings aggregation over a store. */ +export async function collectFindingsForTest( + store: IGraphStore, + symbolIds: readonly string[], + files: readonly string[], +): Promise { + const state: AggregateState = { + signals: [], + communities: new Set(), + communityLabels: new Set(), + blastRadius: 0, + maxOrphanGrade: undefined, + maxFixFollowFeat: 0, + findings: { errorCount: 0, warningCount: 0, byRule: new Map() }, + complexAndUntested: false, + }; + await collectFindings(store, symbolIds, files, state); + return state.findings; +} + +/** @internal test-visible reviewer recommendation over a store. */ +export function collectReviewersForTest( + store: IGraphStore, + files: readonly string[], + authorEmail: string | undefined, +): Promise { + return collectReviewers(store, files, authorEmail); +} + function decideTier(state: AggregateState, cfg: VerdictConfig): VerdictTier { const { blastRadius, communities, findings, maxOrphanGrade, maxFixFollowFeat } = state; @@ -547,6 +577,23 @@ async function collectFindings( const byRule = new Map(); let errorCount = 0; let warningCount = 0; + // De-dup by finding id (not ruleId): the symbol path and the file fallback + // can surface the same finding, and many distinct findings legitimately + // share one ruleId. Counting once per id keeps overlapping symbol+file + // findings from being double-counted while still tallying every unique + // finding's severity — de-duping by ruleId silently dropped file-level + // errors/warnings whose rule was already seen on a symbol. + const countedIds = new Set(); + + const tally = (f: FindingNode): void => { + if (countedIds.has(f.id)) return; + if (isFindingSuppressed(f)) return; + countedIds.add(f.id); + const ruleId = f.ruleId ?? ""; + if (ruleId.length > 0) byRule.set(ruleId, (byRule.get(ruleId) ?? 0) + 1); + if (f.severity === "error") errorCount += 1; + else if (f.severity === "warning") warningCount += 1; + }; if (symbolIds.length > 0) { try { @@ -564,11 +611,7 @@ async function collectFindings( const targetSet = new Set(findingIds); for (const f of findings) { if (!targetSet.has(f.id)) continue; - if (isFindingSuppressed(f)) continue; - const ruleId = f.ruleId ?? ""; - if (ruleId.length > 0) byRule.set(ruleId, (byRule.get(ruleId) ?? 0) + 1); - if (f.severity === "error") errorCount += 1; - else if (f.severity === "warning") warningCount += 1; + tally(f); } } } catch { @@ -589,13 +632,7 @@ async function collectFindings( const findings = await store.listFindings(); for (const f of findings) { if (!fileSet.has(f.filePath)) continue; - if (isFindingSuppressed(f)) continue; - const ruleId = f.ruleId ?? ""; - if (ruleId.length > 0 && !byRule.has(ruleId)) { - byRule.set(ruleId, 1); - if (f.severity === "error") errorCount += 1; - else if (f.severity === "warning") warningCount += 1; - } + tally(f); } } catch { // Ignore. @@ -777,11 +814,11 @@ async function collectReviewers( } function hashEmail(email: string): string { - // We avoid importing crypto here; the cheap contains-check on raw email - // handles the common case. `email_hash` collisions are still rare in - // practice because blame always supplies plaintext in memory; the hashed - // column is stable at write time. - return email.toLowerCase(); + // Contributor.emailHash is the sha256 of the lowercased email (the + // ownership phase lowercases blame emails before hashing). Mirror that + // exactly so author-exclusion still works in privacy-hash mode, where + // `emailPlain` is omitted and the only handle on the author is the hash. + return createHash("sha256").update(email.toLowerCase()).digest("hex"); } async function discoverAuthorEmail(repoPath: string): Promise { diff --git a/packages/cli/src/agent-context.ts b/packages/cli/src/agent-context.ts index 1db8fb95..4e822b5e 100644 --- a/packages/cli/src/agent-context.ts +++ b/packages/cli/src/agent-context.ts @@ -37,7 +37,7 @@ tiers. - \`context\` — inbound/outbound refs and participating flows for one symbol. - \`impact\` — dependents of a target up to a configurable depth, with a risk tier. - \`detect_changes\` — map an uncommitted or committed diff to affected symbols. -- \`rename\` — graph-assisted multi-file rename; dry-run is the default. +- \`list_findings\` — browse SARIF findings from the latest scan by severity and rule. - \`sql\` — read-only SQL against the local temporal store (cochanges + symbol_summaries), 5 s timeout; the node/edge graph is queried via the typed tools or Cypher via the MCP \`sql\` tool. Run \`codehub analyze\` after pulling new commits so the index stays aligned diff --git a/packages/cli/src/commands/analyze.test.ts b/packages/cli/src/commands/analyze.test.ts index 2ede5161..ec401cf1 100644 --- a/packages/cli/src/commands/analyze.test.ts +++ b/packages/cli/src/commands/analyze.test.ts @@ -20,6 +20,7 @@ import { join } from "node:path"; import { test } from "node:test"; import { upsertRegistry } from "../registry.js"; import { + buildStoreMeta, checkFastPath, detectCoverageReport, isWorkingTreeDirty, @@ -393,3 +394,80 @@ test("resolveCoverageEnabled: undefined + report found → true (auto-on)", asyn await writeFile(join(dir, "lcov.info"), "TN:\n"); assert.equal(await resolveCoverageEnabled(undefined, dir), true); }); + +// --------------------------------------------------------------------------- +// buildStoreMeta — the embedder fingerprint tag must be persisted so the +// query-path mismatch guard (assertEmbedderCompatible) has a value to compare. +// --------------------------------------------------------------------------- + +test("buildStoreMeta: stamps embedderModelId when the embedder ran with a model id", () => { + const meta = buildStoreMeta({ + indexedAt: "2026-05-30T00:00:00Z", + nodeCount: 100, + edgeCount: 200, + stats: {}, + cacheSizeBytes: 0, + embeddings: { ranEmbedder: true, embeddingsModelId: "gte-modernbert-base/fp32" }, + }); + assert.equal( + meta.embedderModelId, + "gte-modernbert-base/fp32", + "the embedder tag must round-trip into StoreMeta so the fingerprint guard can fire", + ); +}); + +test("buildStoreMeta: omits embedderModelId when no embedder ran", () => { + // A bare `codehub analyze` (no --embeddings) must not claim an embedder + // produced vectors. The query-path guard then treats the store as untagged. + const meta = buildStoreMeta({ + indexedAt: "2026-05-30T00:00:00Z", + nodeCount: 1, + edgeCount: 0, + stats: {}, + cacheSizeBytes: 0, + embeddings: { ranEmbedder: false, embeddingsModelId: "" }, + }); + assert.equal(meta.embedderModelId, undefined); +}); + +test("buildStoreMeta: omits embedderModelId when the embeddings phase is absent", () => { + const meta = buildStoreMeta({ + indexedAt: "2026-05-30T00:00:00Z", + nodeCount: 1, + edgeCount: 0, + stats: {}, + cacheSizeBytes: 0, + }); + assert.equal(meta.embedderModelId, undefined); +}); + +test("buildStoreMeta: omits embedderModelId when ranEmbedder is true but the model id is empty", () => { + // ranEmbedder true with an empty modelId is a no-op signal from the phase; + // tagging the store with "" would let the guard compare against an empty + // string and falsely flag every future query as a mismatch. + const meta = buildStoreMeta({ + indexedAt: "2026-05-30T00:00:00Z", + nodeCount: 1, + edgeCount: 0, + stats: {}, + cacheSizeBytes: 0, + embeddings: { ranEmbedder: true, embeddingsModelId: "" }, + }); + assert.equal(meta.embedderModelId, undefined); +}); + +test("buildStoreMeta: carries lastCommit and cacheHitRatio through verbatim", () => { + const meta = buildStoreMeta({ + indexedAt: "2026-05-30T00:00:00Z", + nodeCount: 5, + edgeCount: 7, + currentCommit: "abc123", + stats: { Function: 3 }, + cacheHitRatio: 0.5, + cacheSizeBytes: 1024, + }); + assert.equal(meta.lastCommit, "abc123"); + assert.equal(meta.cacheHitRatio, 0.5); + assert.equal(meta.cacheSizeBytes, 1024); + assert.deepEqual(meta.stats, { Function: 3 }); +}); diff --git a/packages/cli/src/commands/analyze.ts b/packages/cli/src/commands/analyze.ts index 5cf36ccb..ad02ff3b 100644 --- a/packages/cli/src/commands/analyze.ts +++ b/packages/cli/src/commands/analyze.ts @@ -38,6 +38,7 @@ import { resolveGraphPath, resolveRepoMetaDir, type Store, + type StoreMeta, writeStoreMeta, } from "@opencodehub/storage"; import { writeAgentContextFiles } from "../agent-context.js"; @@ -381,18 +382,18 @@ export async function runAnalyze(path: string, opts: AnalyzeOptions = {}): Promi const parseCache = result.stats.parseCache; const cacheDir = join(repoPath, ".codehub", "parse-cache"); const cacheSize = await pipeline.computeCacheSize(cacheDir); - const storeMeta = { - schemaVersion: SCHEMA_VERSION, + const storeMeta = buildStoreMeta({ indexedAt, nodeCount: result.graph.nodeCount(), edgeCount: result.graph.edgeCount(), ...(result.stats.currentCommit !== undefined - ? { lastCommit: result.stats.currentCommit } + ? { currentCommit: result.stats.currentCommit } : {}), stats: byKindStats, ...(parseCache !== undefined ? { cacheHitRatio: parseCache.ratio } : {}), cacheSizeBytes: cacheSize.bytes, - }; + ...(result.embeddings !== undefined ? { embeddings: result.embeddings } : {}), + }); await store.graph.setMeta(storeMeta); await writeStoreMeta(repoPath, storeMeta); @@ -627,6 +628,57 @@ export function resolveScanEnabled(flag: boolean | undefined): boolean { return flag !== false; } +/** + * Minimal slice of the `embeddings` phase output {@link buildStoreMeta} + * needs to tag the store with the embedder that populated it. Declared + * locally (rather than importing `EmbedderPhaseOutput`) so the helper stays + * trivially unit-testable without staging the full phase result. + */ +export interface StoreMetaEmbeddingsInput { + readonly ranEmbedder: boolean; + readonly embeddingsModelId: string; +} + +/** Fields {@link buildStoreMeta} folds into a {@link StoreMeta}. */ +export interface BuildStoreMetaInput { + readonly indexedAt: string; + readonly nodeCount: number; + readonly edgeCount: number; + readonly currentCommit?: string; + readonly stats: Record; + readonly cacheHitRatio?: number; + readonly cacheSizeBytes: number; + readonly embeddings?: StoreMetaEmbeddingsInput; +} + +/** + * Assemble the {@link StoreMeta} persisted by `setMeta` + `writeStoreMeta`. + * + * Critically, this stamps `embedderModelId` from the embeddings phase output + * so the query-path fingerprint guard (`assertEmbedderCompatible` in + * cli/query + mcp/query) has a persisted value to compare against. We only + * tag it when the embedder actually ran AND reported a non-empty modelId — + * a `--no-embeddings` run leaves the field unset so the meta does not claim + * an embedder produced vectors it never wrote. + * + * Exported for unit tests; the production call site is in {@link runAnalyze}. + */ +export function buildStoreMeta(input: BuildStoreMetaInput): StoreMeta { + return { + schemaVersion: SCHEMA_VERSION, + indexedAt: input.indexedAt, + nodeCount: input.nodeCount, + edgeCount: input.edgeCount, + ...(input.currentCommit !== undefined ? { lastCommit: input.currentCommit } : {}), + stats: input.stats, + ...(input.cacheHitRatio !== undefined ? { cacheHitRatio: input.cacheHitRatio } : {}), + cacheSizeBytes: input.cacheSizeBytes, + ...(input.embeddings?.ranEmbedder === true && input.embeddings.embeddingsModelId.length > 0 + ? { embedderModelId: input.embeddings.embeddingsModelId } + : {}), + }; +} + /** * Coverage-report candidate paths, mirrored from * `packages/ingestion/src/pipeline/phases/coverage.ts:58-64`. Kept in sync diff --git a/packages/cli/src/commands/ci-init.test.ts b/packages/cli/src/commands/ci-init.test.ts index 8f757924..8cd89a4c 100644 --- a/packages/cli/src/commands/ci-init.test.ts +++ b/packages/cli/src/commands/ci-init.test.ts @@ -5,6 +5,8 @@ * 1. Fresh repo + `--platform github` → writes 4 workflow files. * 2. Existing workflow + no `--force` → refuses, error names the conflicts. * 3. Every emitted workflow parses as valid YAML. + * 4. Every `codehub ...` line in the emitted workflows only uses + * flags that the CLI actually declares (template/CLI drift guard). */ import assert from "node:assert/strict"; @@ -19,6 +21,72 @@ async function mkRepo(): Promise { return mkdtemp(join(tmpdir(), "codehub-ci-init-")); } +/** + * Long-option flags declared per `codehub` subcommand in `src/index.ts`. + * Kept in lock-step with the command wiring there — this is the source of + * truth the templates must not drift away from. Only the subcommands the + * emitted CI workflows actually invoke need an entry. Update both this map + * and the matching `.option(...)` block in `index.ts` together. + */ +const DECLARED_FLAGS: Readonly>> = { + analyze: new Set([ + "--force", + "--embeddings", + "--embeddings-int8", + "--granularity", + "--embeddings-workers", + "--embeddings-batch-size", + "--offline", + "--verbose", + "--skip-agents-md", + "--sbom", + "--no-sbom", + "--coverage", + "--no-coverage", + "--scan", + "--no-scan", + "--summaries", + "--no-summaries", + "--max-summaries", + "--summary-model", + "--skills", + "--strict-detectors", + "--allow-build-scripts", + ]), + verdict: new Set(["--base", "--head", "--repo", "--json"]), + scan: new Set([ + "--scanners", + "--with", + "--output", + "--severity", + "--repo", + "--concurrency", + "--timeout", + ]), +}; + +/** + * Walk a rendered workflow body and yield every `codehub ` invocation + * with the long-option flags that follow it on the same logical command + * (joined across `\`-continued lines). Quotes/values are ignored — we only + * care about the `--flag` tokens. + */ +function extractCodehubInvocations(body: string): { cmd: string; flags: string[] }[] { + // Collapse YAML/`shell line continuations so a wrapped command is one line. + const flattened = body.replace(/\\\s*\n\s*/g, " "); + const out: { cmd: string; flags: string[] }[] = []; + const re = /\bcodehub\s+([a-z][a-z-]*)\b([^\n]*)/g; + let m: RegExpExecArray | null = re.exec(flattened); + while (m !== null) { + const cmd = m[1] as string; + const rest = m[2] as string; + const flags = rest.match(/--[a-z][a-z-]*/g) ?? []; + out.push({ cmd, flags: [...flags] }); + m = re.exec(flattened); + } + return out; +} + test("runCiInit: fresh repo + --platform github writes 4 workflow files", async () => { const repo = await mkRepo(); try { @@ -105,3 +173,59 @@ test("runCiInit: every emitted workflow parses as valid YAML (platform=both)", a await rm(repo, { recursive: true, force: true }); } }); + +test("runCiInit: every emitted codehub invocation uses only CLI-declared flags", async () => { + const repo = await mkRepo(); + try { + await runCiInit({ repo, platform: "both", mainBranch: "main" }); + const paths = [ + join(repo, ".github", "workflows", "opencodehub-verdict.yml"), + join(repo, ".github", "workflows", "opencodehub-nightly.yml"), + join(repo, ".github", "workflows", "opencodehub-weekly.yml"), + join(repo, ".github", "workflows", "opencodehub-rescan.yml"), + join(repo, ".gitlab-ci.yml"), + ]; + for (const p of paths) { + const body = await readFile(p, "utf8"); + for (const { cmd, flags } of extractCodehubInvocations(body)) { + const declared = DECLARED_FLAGS[cmd]; + if (declared === undefined) continue; // command not under audit here + for (const flag of flags) { + assert.ok( + declared.has(flag), + `${p}: \`codehub ${cmd}\` uses undeclared flag ${flag} — the CLI parser would exit non-zero`, + ); + } + } + } + } finally { + await rm(repo, { recursive: true, force: true }); + } +}); + +// Precise regression guard for the specific phantom flags that previously +// shipped in the verdict + gitlab templates: `analyze --incremental` (analyze +// is incremental by default; the flag is unregistered) and `verdict +// --output-format --pr-comment` (never wired in index.ts). None may +// reappear in ANY emitted workflow. +test("runCiInit: emitted workflows never reference removed phantom flags", async () => { + const repo = await mkRepo(); + try { + await runCiInit({ repo, platform: "both", mainBranch: "main" }); + const bodies = await Promise.all( + [ + join(repo, ".github", "workflows", "opencodehub-verdict.yml"), + join(repo, ".github", "workflows", "opencodehub-nightly.yml"), + join(repo, ".github", "workflows", "opencodehub-weekly.yml"), + join(repo, ".github", "workflows", "opencodehub-rescan.yml"), + join(repo, ".gitlab-ci.yml"), + ].map((p) => readFile(p, "utf8")), + ); + const combined = bodies.join("\n"); + assert.doesNotMatch(combined, /--incremental\b/, "analyze --incremental is unregistered"); + assert.doesNotMatch(combined, /--output-format\b/, "verdict --output-format is unwired"); + assert.doesNotMatch(combined, /--pr-comment\b/, "verdict --pr-comment is unwired"); + } finally { + await rm(repo, { recursive: true, force: true }); + } +}); diff --git a/packages/cli/src/commands/ci-templates/github-rescan.yml b/packages/cli/src/commands/ci-templates/github-rescan.yml index 06f8b132..2671d020 100644 --- a/packages/cli/src/commands/ci-templates/github-rescan.yml +++ b/packages/cli/src/commands/ci-templates/github-rescan.yml @@ -26,7 +26,6 @@ jobs: id: rescan run: | codehub scan --scanners osv-scanner,grype \ - --sbom .codehub/sbom.cyclonedx.json \ --severity HIGH,CRITICAL \ --output /tmp/rescan.sarif || true NEW_CVES=$(jq -r '.runs[].results[]?.ruleId // empty' /tmp/rescan.sarif | sort -u) diff --git a/packages/cli/src/commands/ci-templates/github-verdict.yml b/packages/cli/src/commands/ci-templates/github-verdict.yml index c8939ba6..e7d8f5a2 100644 --- a/packages/cli/src/commands/ci-templates/github-verdict.yml +++ b/packages/cli/src/commands/ci-templates/github-verdict.yml @@ -11,13 +11,12 @@ jobs: with: node-version: 20 - run: npm install -g @opencodehub/cli@latest - - run: codehub analyze --incremental + - run: codehub analyze - name: verdict id: verdict run: | - codehub verdict --base origin/${MAIN_BRANCH} --head HEAD \ - --output-format markdown --pr-comment > /tmp/verdict.md - echo "exit_code=$?" >> $GITHUB_OUTPUT + codehub verdict --base origin/${MAIN_BRANCH} --head HEAD > /tmp/verdict.md || true + echo "exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT - run: gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/verdict.md env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/cli/src/commands/ci-templates/github-weekly.yml b/packages/cli/src/commands/ci-templates/github-weekly.yml index 5abe839b..1e76d246 100644 --- a/packages/cli/src/commands/ci-templates/github-weekly.yml +++ b/packages/cli/src/commands/ci-templates/github-weekly.yml @@ -20,8 +20,8 @@ jobs: - run: npm install -g @opencodehub/cli@latest - name: deep scan (P1 + P2) run: | - codehub analyze --force --sbom --scan \ - --with trivy,checkov,hadolint,tflint,spectral + codehub analyze --force --sbom --scan + codehub scan --with trivy,checkov,hadolint,tflint,spectral - name: license audit id: license run: | diff --git a/packages/cli/src/commands/ci-templates/gitlab-ci.yml b/packages/cli/src/commands/ci-templates/gitlab-ci.yml index f311e3f5..0850329d 100644 --- a/packages/cli/src/commands/ci-templates/gitlab-ci.yml +++ b/packages/cli/src/commands/ci-templates/gitlab-ci.yml @@ -12,10 +12,8 @@ verdict: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" script: - - codehub analyze --incremental - - | - codehub verdict --base origin/${MAIN_BRANCH} --head HEAD \ - --output-format markdown --pr-comment > verdict.md + - codehub analyze + - codehub verdict --base origin/${MAIN_BRANCH} --head HEAD > verdict.md - cat verdict.md artifacts: when: always @@ -44,9 +42,8 @@ weekly: rules: - if: $CI_PIPELINE_SOURCE == "schedule" && $OCH_JOB == "weekly" script: - - | - codehub analyze --force --sbom --scan \ - --with trivy,checkov,hadolint,tflint,spectral + - codehub analyze --force --sbom --scan + - codehub scan --with trivy,checkov,hadolint,tflint,spectral - codehub scan --scanners license_audit --output license.sarif || true artifacts: when: always diff --git a/packages/cli/src/commands/doctor.test.ts b/packages/cli/src/commands/doctor.test.ts index df2255c9..71e83588 100644 --- a/packages/cli/src/commands/doctor.test.ts +++ b/packages/cli/src/commands/doctor.test.ts @@ -436,3 +436,65 @@ test("bandit check warns (not fails) when the binary is absent", async () => { await rm(home, { recursive: true, force: true }); } }); + +// --------------------------------------------------------------------------- +// Graph binding (@ladybugdb/core) — mandatory, so absence is a HARD fail. +// --------------------------------------------------------------------------- + +// The graph tier is always-on with no selector env var, no probe, and no +// fallback — a failed binding throws GraphDbBindingError and aborts every +// graph op. So a missing/unresolvable binding must be `fail`, never `warn`. +// The real package is installed in every dev/CI checkout, so we inject a +// resolver that returns null to exercise the absent path. +test("graph-db binding check hard-fails (not warns) when @ladybugdb/core cannot be resolved", async () => { + const home = await mkdtemp(join(tmpdir(), "codehub-doctor-lbug-miss-")); + try { + const checks = buildChecks({ + home, + resolveBinding: (_root, pkg) => (pkg === "@ladybugdb/core" ? null : "/fake/duckdb"), + }); + const lbug = checks.find((c) => c.name === "graph-db native binding"); + assert.ok(lbug, "graph-db binding check must be registered when skipNative is false"); + const result = await lbug.run(); + assert.equal( + result.status, + "fail", + `a missing mandatory graph binding must fail, never warn; got ${result.status}`, + ); + // The hint must not reference the removed CODEHUB_STORE selector. + assert.doesNotMatch(result.hint ?? "", /CODEHUB_STORE/); + assert.doesNotMatch(result.message, /optional/); + } finally { + await rm(home, { recursive: true, force: true }); + } +}); + +// No doctor row may mention the phantom CODEHUB_STORE env var or frame the +// graph backend as "optional" — that selector was removed when lbug became +// the mandatory graph tier. +test("doctor surfaces no CODEHUB_STORE selector or optional-backend framing", async () => { + const home = await mkdtemp(join(tmpdir(), "codehub-doctor-no-store-var-")); + try { + await mkdir(join(home, ".codehub"), { recursive: true }); + await writeFile(join(home, ".codehub", "registry.json"), JSON.stringify({})); + const prev = process.exitCode; + const report = await runDoctor({ + home, + runCommand: okRunCommand, + resolveBinding: (_root, pkg) => (pkg === "@ladybugdb/core" ? null : "/fake/duckdb"), + }); + process.exitCode = prev; + for (const { result } of report.rows) { + assert.doesNotMatch(result.message, /CODEHUB_STORE/); + assert.doesNotMatch(result.hint ?? "", /CODEHUB_STORE/); + } + // The absent graph binding makes the whole probe blocking (exit 2). + assert.equal( + report.exitCode, + 2, + "an absent mandatory graph binding must block the doctor exit", + ); + } finally { + await rm(home, { recursive: true, force: true }); + } +}); diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index ac5e5f52..3ac6eb25 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/packages/cli/src/commands/doctor.ts @@ -57,6 +57,13 @@ export interface DoctorOptions { * Defaults to the real {@link runCommand}. Same signature. */ readonly runCommand?: RunCommandFn; + /** + * Injectable npm-package resolver for the native-binding checks. Lets a + * test simulate an absent `@ladybugdb/core` (the mandatory graph binding + * is installed in every dev/CI checkout, so the only way to exercise the + * hard-fail path is to stub resolution). Defaults to {@link resolveFromRoot}. + */ + readonly resolveBinding?: (root: string, pkg: string) => string | null; } /** Signature of the injectable command runner (see {@link runCommand}). */ @@ -118,7 +125,11 @@ export function buildChecks(opts: DoctorOptions = {}): readonly Check[] { const list: Check[] = [nodeVersionCheck(), pnpmInstalledCheck(run)]; if (opts.skipNative !== true) { list.push(duckdbWorksCheck(repoRoot)); - list.push(lbugWorksCheck(repoRoot)); + list.push( + opts.resolveBinding !== undefined + ? lbugWorksCheck(repoRoot, opts.resolveBinding) + : lbugWorksCheck(repoRoot), + ); } // Vendored parse grammars: a shipped artifact, so absence/corruption is // always a hard fail. One row covering all 16 blobs + the manifest pin. @@ -254,30 +265,36 @@ function duckdbWorksCheck(repoRoot: string): Check { } /** - * Mirror of {@link duckdbWorksCheck} for the optional `@ladybugdb/core` - * graph-db backend. Emits `warn` (not `fail`) when the package is - * uninstalled because `@ladybugdb/core` is opt-in: a default `duck` - * deployment never needs it. When the package IS installed and the - * smoke test fails we surface `fail` so a broken native binding can be - * triaged the same way duckdb's is. + * Mirror of {@link duckdbWorksCheck} for the `@ladybugdb/core` graph + * binding. The graph tier is mandatory and always-on: there is no + * selector env var, no probe, and no fallback — a failed binding throws + * `GraphDbBindingError` and aborts every graph operation. So a + * missing/broken binding is a hard `fail` here, exactly like a missing + * shipped artifact (see {@link vendoredWasmsCheck}) — never a soft `warn`. + * + * `resolve` is injectable so tests can simulate an absent binding without + * uninstalling the real dependency; production passes {@link resolveFromRoot}. */ -function lbugWorksCheck(repoRoot: string): Check { +function lbugWorksCheck( + repoRoot: string, + resolve: (root: string, pkg: string) => string | null = resolveFromRoot, +): Check { return { name: "graph-db native binding", async run() { try { - const lbugPath = resolveFromRoot(repoRoot, "@ladybugdb/core"); + const lbugPath = resolve(repoRoot, "@ladybugdb/core"); if (!lbugPath) { return { - status: "warn", - message: "@ladybugdb/core not installed (optional graph-db backend)", - hint: "run `pnpm install` and set `CODEHUB_STORE=lbug` to opt in; otherwise ignore", + status: "fail", + message: "@ladybugdb/core not installed (required graph backend)", + hint: "run `pnpm install` — the lbug graph binding ships with the CLI and is mandatory", }; } - // The opt-in graph-db backend uses `@ladybugdb/core`'s `Database` - // entry. We exercise the load-and-close cycle the same way the - // duckdb check does — anything heavier would couple this probe to - // the adapter's evolving smoke-test surface. + // The graph binding uses `@ladybugdb/core`'s `Database` entry. We + // exercise the load-and-close cycle the same way the duckdb check + // does — anything heavier would couple this probe to the adapter's + // evolving smoke-test surface. const mod = (await import(lbugPath)) as Record; const ctorRaw = mod["Database"] ?? (mod["default"] as Record | undefined)?.["Database"]; @@ -285,7 +302,7 @@ function lbugWorksCheck(repoRoot: string): Check { return { status: "fail", message: "@ladybugdb/core is installed but exports no Database constructor", - hint: "re-run `pnpm install` to refresh the graph-db backend bindings", + hint: "re-run `pnpm install` to refresh the graph backend binding", }; } return { status: "ok", message: "@ladybugdb/core load OK" }; @@ -293,7 +310,7 @@ function lbugWorksCheck(repoRoot: string): Check { return { status: "fail", message: `@ladybugdb/core failed to load: ${err instanceof Error ? err.message : String(err)}`, - hint: "the graph-db backend is opt-in; unset `CODEHUB_STORE=lbug` or reinstall the binding", + hint: "reinstall the graph backend binding with `pnpm install`", }; } }, diff --git a/packages/cli/src/commands/query.test.ts b/packages/cli/src/commands/query.test.ts index 4a3b169b..7adba79a 100644 --- a/packages/cli/src/commands/query.test.ts +++ b/packages/cli/src/commands/query.test.ts @@ -45,6 +45,13 @@ interface FakeStoreOptions { readonly nodes?: ReadonlyMap; /** P04 symbol summaries — keyed by nodeId. Omit to simulate legacy indexes. */ readonly summaryRows?: ReadonlyMap; + /** + * Persisted embedder model id returned by `getMeta()`. Omit to simulate a + * legacy / untagged store (the compatibility check passes). Set to a value + * that differs from the active embedder's modelId to exercise the + * mismatch-refusal path. + */ + readonly metaModelId?: string; } interface FakeStoreHandle { @@ -62,6 +69,7 @@ function makeFakeStore(opts: FakeStoreOptions = {}): FakeStoreHandle { const vectorRows = opts.vectorRows ?? []; const nodes = opts.nodes ?? new Map(); const summaryRows = opts.summaryRows; + const metaModelId = opts.metaModelId; const handle: FakeStoreHandle = { lastQuery: null, @@ -115,7 +123,11 @@ function makeFakeStore(opts: FakeStoreOptions = {}): FakeStoreHandle { // modelId against the currently-active embedder. Returning `undefined` // makes every fake store look like a legacy / pre-tag store, so the // compatibility check passes without any test having to set a model id. - getMeta: async () => undefined, + // Set `metaModelId` to exercise the mismatch-refusal branch instead. + getMeta: async () => + metaModelId !== undefined + ? ({ embedderModelId: metaModelId } as Awaited>) + : undefined, }; // The temporal-tier surface the query path touches is just @@ -636,3 +648,83 @@ test("cli query: store without lookupSymbolSummariesByNode degrades silently", a const parsed = JSON.parse(stdout) as { results: Array<{ summary?: string }> }; assert.equal(parsed.results[0]?.summary, undefined); }); + +// --------------------------------------------------------------------------- +// Embedder fingerprint mismatch — the abort path must run cleanup, not leak. +// --------------------------------------------------------------------------- + +// On a persisted-vs-active embedder mismatch, the query must abort with exit +// code 2 BUT still close the native ONNX session and the composed store. An +// immediate process.exit(2) would have skipped both finally blocks, leaking +// the embedder session and the graph + temporal handles. +test("cli query: embedder mismatch sets exit code 2 and still closes embedder + store", async () => { + const handle = makeFakeStore({ + embeddingRows: 5, + searchRows: [ + { nodeId: "F:foo", score: 2, filePath: "src/foo.ts", name: "foo", kind: "Function" }, + ], + vectorRows: [{ nodeId: "F:foo", distance: 0.1 }], + // Persisted model id differs from the active embedder's "fake-embedder/test". + metaModelId: "gte-modernbert-base/fp32", + }); + const fake = new FakeEmbedder(); + const prevExitCode = process.exitCode; + try { + await captureStderr(async () => { + await runQuery( + "auth handler", + { json: true }, + { ...hooksFor(handle, "/tmp/fake"), openEmbedder: async () => fake }, + ); + }); + assert.equal(process.exitCode, 2, "an embedder mismatch must set the distinct exit code 2"); + assert.equal( + fake.closeCount, + 1, + "embedder.close() must run on the mismatch abort path (inner finally)", + ); + assert.equal( + handle.closed, + true, + "store.close() must run on the mismatch abort path (outer finally)", + ); + assert.equal(handle.vectorCalls, 0, "hybridSearch must not run after a refused mismatch"); + } finally { + process.exitCode = prevExitCode; + } +}); + +// `--force-backend-mismatch` overrides the refusal: the mismatch is accepted, +// the hybrid search runs, and cleanup still happens (the normal happy path). +test("cli query: --force-backend-mismatch bypasses the refusal and runs hybrid", async () => { + const nodes: ReadonlyMap = new Map([ + ["F:foo", { name: "foo", kind: "Function", filePath: "src/foo.ts" }], + ]); + const handle = makeFakeStore({ + embeddingRows: 5, + searchRows: [ + { nodeId: "F:foo", score: 2, filePath: "src/foo.ts", name: "foo", kind: "Function" }, + ], + vectorRows: [{ nodeId: "F:foo", distance: 0.1 }], + nodes, + metaModelId: "gte-modernbert-base/fp32", + }); + const fake = new FakeEmbedder(); + const prevExitCode = process.exitCode; + try { + const stdout = await captureStdout(async () => { + await runQuery( + "auth handler", + { json: true, forceBackendMismatch: true }, + { ...hooksFor(handle, "/tmp/fake"), openEmbedder: async () => fake }, + ); + }); + const parsed = JSON.parse(stdout) as { mode: "bm25" | "hybrid" }; + assert.equal(parsed.mode, "hybrid", "force-mismatch must let the hybrid path run"); + assert.equal(handle.vectorCalls, 1, "hybridSearch must run when the mismatch is forced"); + assert.equal(fake.closeCount, 1, "embedder.close() must still run on the happy path"); + assert.equal(handle.closed, true, "store.close() must still run on the happy path"); + } finally { + process.exitCode = prevExitCode; + } +}); diff --git a/packages/cli/src/commands/query.ts b/packages/cli/src/commands/query.ts index 4f6a866f..9de2c19b 100644 --- a/packages/cli/src/commands/query.ts +++ b/packages/cli/src/commands/query.ts @@ -167,7 +167,16 @@ export async function runQuery( `Embedder mismatch: store was indexed with '${compat.persistedModelId}', ` + `current embedder is '${compat.currentModelId}'.\n${compat.hint}\n`, ); - process.exit(2); + // Set the distinct exit code and return rather than calling + // process.exit(2): an immediate process.exit() terminates the + // event loop before either the inner `embedder.close()` finally + // (line ~186) or the outer `store.close()` finally (line ~241) + // runs, leaking the native ONNX session and the composed store's + // graph + temporal handles on this abort path. `return` unwinds + // through both finally blocks first, honoring this file's own + // "always release the native session" contract. + process.exitCode = 2; + return; } const fused = await hybridSearch( graph, diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index a714d789..92a31fc7 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -537,7 +537,7 @@ program .option("--base ", "Base git ref", "main") .option("--head ", "Head git ref", "HEAD") .option("--repo ", "Registered repo name (default: current directory)") - .option("--json", "Emit JSON on stdout instead of Markdown") + .option("--json", "Emit JSON on stdout instead of the default text summary") .action(async (opts: Record) => { const mod = await import("./commands/verdict.js"); await mod.runVerdict({ diff --git a/packages/cobol-proleap/src/jre-probe.test.ts b/packages/cobol-proleap/src/jre-probe.test.ts index 7c0d2b90..f7691e28 100644 --- a/packages/cobol-proleap/src/jre-probe.test.ts +++ b/packages/cobol-proleap/src/jre-probe.test.ts @@ -20,6 +20,28 @@ test("parseJreMajor: openjdk 21", () => { assert.equal(parseJreMajor(out), 21); }); +test("parseJreMajor: single-digit modern major (Java 9) is not misread as 1", () => { + // The release date "2018-01-16" contains "01"; the parser must anchor to + // the leading version token and report 9, not 1. + const out = "openjdk 9.0.4 2018-01-16"; + assert.equal(parseJreMajor(out), 9); +}); + +test("parseJreMajor: single-digit modern major (Java 10)", () => { + const out = "openjdk 10.0.2 2018-07-17"; + assert.equal(parseJreMajor(out), 10); +}); + +test('parseJreMajor: quoted single-digit modern major (java version "9")', () => { + const out = 'java version "9" 2017-09-21'; + assert.equal(parseJreMajor(out), 9); +}); + +test("parseJreMajor: leading version token wins over a date-only second line", () => { + const out = "openjdk 17.0.9 2023-10-17\nOpenJDK 64-Bit Server VM (build 17.0.9+9)"; + assert.equal(parseJreMajor(out), 17); +}); + test("parseJreMajor: legacy java 8 (1.8.0 form)", () => { const out = 'java version "1.8.0_292"'; assert.equal(parseJreMajor(out), 8); diff --git a/packages/cobol-proleap/src/jre-probe.ts b/packages/cobol-proleap/src/jre-probe.ts index a43b4cda..70ad8463 100644 --- a/packages/cobol-proleap/src/jre-probe.ts +++ b/packages/cobol-proleap/src/jre-probe.ts @@ -56,25 +56,33 @@ export const defaultJreProbe: JreProbe = async () => { * * openjdk 17.0.2 2022-01-18 → 17 * openjdk 21 2023-09-19 → 21 + * openjdk 9.0.4 2018-01-16 → 9 (single-digit modern major) * java 17.0.12 2024-07-16 LTS → 17 * java version "1.8.0_292" → 8 (Java 8 used 1.x naming) + * java version "9" → 9 (single-digit, quoted) * java version "11.0.12" 2021-07-20 → 11 */ export function parseJreMajor(output: string | undefined): number | undefined { if (output === undefined) return undefined; - // Legacy 1.x form (Java 1.8 = Java 8). - const legacy = output.match(/\b1\.(\d+)(?:\.[\d_]+)?\b/); - if (legacy?.[1] !== undefined) { - const parsed = Number.parseInt(legacy[1], 10); - if (Number.isFinite(parsed)) return parsed; - } - // Modern N.x form: take the first standalone leading integer that's not a - // preceding "1." (already handled above). - const modern = output.match(/\b(\d{2,3})(?:\.\d+)?\b/); - if (modern?.[1] !== undefined) { - const parsed = Number.parseInt(modern[1], 10); + // Anchor extraction to the leading version token only. Scanning the whole + // string for any digit run mis-reads the release date (e.g. "2018-01-16" + // yields a spurious "01") — the version token always follows the vendor + // word (`openjdk`/`java`), optionally preceded by the literal `version` + // and an opening quote. + const token = output.match(/(?:openjdk|java)(?:\s+version)?\s+"?(\d+(?:\.\d+)*)/i); + if (token?.[1] === undefined) return undefined; + const segments = token[1].split("."); + const head = segments[0]; + if (head === undefined) return undefined; + // Legacy 1.x form (Java 1.8 = Java 8): the real major is the second segment. + if (head === "1" && segments[1] !== undefined) { + const parsed = Number.parseInt(segments[1], 10); if (Number.isFinite(parsed)) return parsed; } + // Modern N.x form (including single-digit majors like 9): the major is the + // first segment of the anchored token. + const parsed = Number.parseInt(head, 10); + if (Number.isFinite(parsed)) return parsed; return undefined; } diff --git a/packages/cobol-proleap/src/parse.test.ts b/packages/cobol-proleap/src/parse.test.ts index 369c41dc..f214918b 100644 --- a/packages/cobol-proleap/src/parse.test.ts +++ b/packages/cobol-proleap/src/parse.test.ts @@ -14,9 +14,32 @@ */ import assert from "node:assert/strict"; +import { mkdtempSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { test } from "node:test"; import { parseCobolDeep } from "./parse.js"; -import { JarMissingError } from "./subprocess.js"; +import { JarMissingError, type RunOutcome } from "./subprocess.js"; + +const STUB_OPTS = { jarPath: "/unused.jar", wrapperClassPath: "/unused" }; + +// A tiny fixed-format COBOL fixture the regex fallback can parse. +const FIXTURE = [ + "000100 IDENTIFICATION DIVISION.", + "000200 PROGRAM-ID. WORLD.", + "000300 PROCEDURE DIVISION.", + "000400 MAIN-PARA.", + "000500 PERFORM SALUTE.", + "000600 SALUTE.", + "000700 DISPLAY 'WORLD'.", +].join("\n"); + +function writeFixture(name: string, body: string): string { + const dir = mkdtempSync(join(tmpdir(), "cobol-proleap-parse-")); + const path = join(dir, name); + writeFileSync(path, body, "utf8"); + return path; +} test("parseCobolDeep: empty path list resolves to an empty result", async () => { const res = await parseCobolDeep([], { @@ -37,3 +60,66 @@ test("parseCobolDeep: missing JAR surfaces JarMissingError from the first batch" (err: unknown) => err instanceof JarMissingError, ); }); + +test("parseCobolDeep: a crashed batch salvages partial parse records and only re-parses uncovered files", async () => { + const covered = writeFixture("covered.cbl", FIXTURE); + const uncovered = writeFixture("uncovered.cbl", FIXTURE.replace(/WORLD/g, "HELLO")); + + // The JVM finished `covered` (emitting an authoritative program-id record) + // and then timed out before reaching `uncovered`. + const crashed: RunOutcome = { + kind: "crashed", + reason: "JVM subprocess timed out after 60000ms", + partial: [{ kind: "program-id", name: "WORLD", filePath: covered, startLine: 2, endLine: 2 }], + }; + + const res = await parseCobolDeep([covered, uncovered], STUB_OPTS, async () => crashed); + + assert.equal(res.fellBackToRegex, true); + + // The salvaged record is preserved at "parse" confidence and NOT re-derived. + const coveredEls = res.elements.filter((el) => el.filePath === covered); + assert.ok( + coveredEls.some((el) => el.kind === "program-id" && el.name === "WORLD"), + "expected the salvaged WORLD program-id", + ); + assert.ok( + coveredEls.every((el) => el.confidence === "parse"), + "covered file keeps authoritative parse confidence and is not re-parsed", + ); + + // The uncovered file is re-derived through the regex fallback. + const uncoveredEls = res.elements.filter((el) => el.filePath === uncovered); + assert.ok( + uncoveredEls.some((el) => el.kind === "program-id" && el.name === "HELLO"), + "expected the uncovered file to be regex-reparsed", + ); + assert.ok( + uncoveredEls.every((el) => el.confidence === "heuristic"), + "uncovered file is downgraded to heuristic confidence", + ); + + assert.ok( + res.diagnostics.some((d) => /salvaged 1 file\(s\)/.test(d)), + "the crash note reports the salvage count", + ); +}); + +test("parseCobolDeep: a partial diagnostic falls back to regex for that path", async () => { + const flagged = writeFixture("flagged.cbl", FIXTURE); + const crashed: RunOutcome = { + kind: "crashed", + reason: "JVM exited 1. Stderr tail: boom", + partial: [{ kind: "diagnostic", filePath: flagged, message: "ASG walker NPE" }], + }; + + const res = await parseCobolDeep([flagged], STUB_OPTS, async () => crashed); + + assert.equal(res.fellBackToRegex, true); + // A diagnostic-only path is NOT treated as covered — it is regex-reparsed. + assert.ok( + res.elements.some((el) => el.kind === "program-id" && el.confidence === "heuristic"), + "diagnostic path is re-derived via regex", + ); + assert.ok(res.diagnostics.some((d) => /ASG crash on .*flagged\.cbl/.test(d))); +}); diff --git a/packages/cobol-proleap/src/parse.ts b/packages/cobol-proleap/src/parse.ts index d0359fd0..bf1dc07e 100644 --- a/packages/cobol-proleap/src/parse.ts +++ b/packages/cobol-proleap/src/parse.ts @@ -5,10 +5,12 @@ * 1. Batch the input paths (default 64 per JVM invocation) to amortize * the ~500 ms JVM startup cost. * 2. For each batch, call `runBatch()` in `subprocess.ts`. - * 3. On a `crashed` outcome, silently reparse every path in that batch - * through `fallbackParseBatch()` (regex hot path) and emit one - * diagnostic note so the ingestion phase can surface a graph-level - * marker. + * 3. On a `crashed` outcome, salvage any authoritative records the JVM + * emitted before it died (`outcome.partial`): project those at + * `confidence:"parse"`, then reparse only the paths NOT covered by a + * partial record through `fallbackParseBatch()` (regex hot path), and + * emit one diagnostic note so the ingestion phase can surface a + * graph-level marker. * 4. On `ok`, project the records onto the public `CobolDeepElement` * shape. A `diagnostic` record inside an otherwise-ok batch * triggers a per-file fallback for that specific path — the @@ -20,14 +22,25 @@ */ import { fallbackParseBatch, fallbackParseFile } from "./fallback.js"; -import { recordToElement, runBatch } from "./subprocess.js"; +import { type RunOutcome, recordToElement, runBatch } from "./subprocess.js"; import type { CobolDeepElement, CobolDeepResult, ParseCobolDeepOptions } from "./types.js"; const DEFAULT_BATCH_SIZE = 64; +/** + * Batch runner signature. Defaults to the real JVM {@link runBatch}; the + * crash-salvage path is exercised through an injected runner so the test + * suite never needs a live JVM on PATH. + */ +export type BatchRunner = ( + batch: readonly string[], + opts: ParseCobolDeepOptions, +) => Promise; + export async function parseCobolDeep( paths: readonly string[], opts: ParseCobolDeepOptions, + run: BatchRunner = runBatch, ): Promise { if (paths.length === 0) { return { elements: [], diagnostics: [], fellBackToRegex: false }; @@ -41,16 +54,35 @@ export async function parseCobolDeep( for (let i = 0; i < paths.length; i += batchSize) { const batch = paths.slice(i, i + batchSize); - const outcome = await runBatch(batch, opts); + const outcome = await run(batch, opts); if (outcome.kind === "crashed") { fellBackToRegex = true; + // Salvage authoritative records the JVM emitted before it died. Each + // file with at least one ASG element keeps its high-confidence + // emissions; only the paths the JVM never finished — including any it + // reached but flagged with a per-file diagnostic — are re-derived via + // the regex fallback. + const covered = new Set(); + for (const rec of outcome.partial) { + if (rec.kind === "diagnostic") { + diagnostics.push(`cobol-proleap: ASG crash on ${rec.filePath}: ${rec.message}`); + continue; + } + const el = recordToElement(rec); + if (el !== undefined) { + covered.add(rec.filePath); + elements.push(el); + } + } + const uncovered = batch.filter((path) => !covered.has(path)); const note = `cobol-proleap: JVM batch of ${batch.length} file(s) crashed; ` + - `falling back to regex hot path. Reason: ${outcome.reason}`; + `salvaged ${covered.size} file(s), falling back to regex hot path for ` + + `${uncovered.length} file(s). Reason: ${outcome.reason}`; diagnostics.push(note); log(note); - const { elements: fallbackElems, notes } = await fallbackParseBatch(batch); + const { elements: fallbackElems, notes } = await fallbackParseBatch(uncovered); elements.push(...fallbackElems); diagnostics.push(...notes); continue; diff --git a/packages/cobol-proleap/src/subprocess.test.ts b/packages/cobol-proleap/src/subprocess.test.ts index cd9c7615..7704ff61 100644 --- a/packages/cobol-proleap/src/subprocess.test.ts +++ b/packages/cobol-proleap/src/subprocess.test.ts @@ -8,11 +8,18 @@ */ import assert from "node:assert/strict"; -import { mkdtempSync } from "node:fs"; +import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from "node:test"; -import { JarMissingError, type JvmRecord, recordToElement, runBatch } from "./subprocess.js"; +import { + JarMissingError, + type JvmRecord, + parseRecords, + recordToElement, + runBatch, + superviseProcess, +} from "./subprocess.js"; test("runBatch: empty path list returns an ok outcome with no records", async () => { const res = await runBatch([], { @@ -58,3 +65,103 @@ test("recordToElement: drops diagnostic records", () => { }; assert.equal(recordToElement(rec), undefined); }); + +test("parseRecords: accepts a well-formed symbol record", () => { + const out = parseRecords( + '{"kind":"program-id","name":"HELLO","filePath":"a.cbl","startLine":1,"endLine":1}\n', + ); + assert.equal(out.length, 1); + assert.equal(out.malformed, 0); +}); + +test("parseRecords: rejects a symbol record missing name/startLine/endLine as malformed", () => { + // A truncated wrapper line that carries only kind+filePath used to pass + // validation and leak undefined name/startLine/endLine into the graph. + const out = parseRecords('{"kind":"program-id","filePath":"a.cbl"}\n'); + assert.equal(out.length, 0); + assert.equal(out.malformed, 1); +}); + +test("parseRecords: rejects a symbol record with a non-numeric startLine", () => { + const out = parseRecords( + '{"kind":"paragraph","name":"P1","filePath":"a.cbl","startLine":"3","endLine":5}\n', + ); + assert.equal(out.length, 0); + assert.equal(out.malformed, 1); +}); + +test("parseRecords: accepts a diagnostic record with a message", () => { + const out = parseRecords('{"kind":"diagnostic","filePath":"a.cbl","message":"NPE"}\n'); + assert.equal(out.length, 1); + assert.equal(out.malformed, 0); +}); + +test("parseRecords: rejects a diagnostic record missing its message", () => { + const out = parseRecords('{"kind":"diagnostic","filePath":"a.cbl"}\n'); + assert.equal(out.length, 0); + assert.equal(out.malformed, 1); +}); + +test("parseRecords: rejects an unknown kind as malformed", () => { + const out = parseRecords( + '{"kind":"section","name":"S1","filePath":"a.cbl","startLine":1,"endLine":2}\n', + ); + assert.equal(out.length, 0); + assert.equal(out.malformed, 1); +}); + +test("superviseProcess: escalates to SIGKILL and settles when the child ignores SIGTERM", async () => { + // A stand-in process that installs a SIGTERM handler and then spins + // forever, modelling a JVM wedged in native code. Without the SIGKILL + // escalation the supervising Promise would never resolve. + const dir = mkdtempSync(join(tmpdir(), "cobol-proleap-kill-")); + const script = join(dir, "ignore-sigterm.mjs"); + // The child installs a no-op SIGTERM handler, then spins forever. The + // handler must be armed before the supervisor's SIGTERM lands or the child + // dies on SIGTERM's default action and the escalation path never runs — the + // generous timeoutMs below buys that boot margin. + writeFileSync( + script, + ["process.on('SIGTERM', () => {});", "setInterval(() => {}, 1000);"].join("\n"), + "utf8", + ); + + // timeoutMs is generous so a heavily-loaded CI box arms the child's SIGTERM + // handler well before the supervisor's SIGTERM lands; the child ignores + // SIGTERM regardless, so the longer timeout only delays — never skips — the + // deterministic SIGKILL escalation. (A tight 150ms timeout flaked under the + // full parallel suite: SIGTERM arrived mid-boot, before the handler, and the + // child died on SIGTERM's default action instead of escalating.) + const started = Date.now(); + const outcome = await superviseProcess(process.execPath, [script], [], { + timeoutMs: 2_000, + killGraceMs: 500, + }); + const elapsed = Date.now() - started; + + assert.equal(outcome.kind, "crashed"); + assert.match( + outcome.kind === "crashed" ? outcome.reason : "", + /timed out .* ignored SIGTERM \(SIGKILL sent\)/, + ); + // Resolved via the kill escalation, not by hanging forever. + assert.ok(elapsed < 10_000, `expected resolution well under the grace ceiling, got ${elapsed}ms`); +}); + +test("superviseProcess: a child that exits 0 with clean NDJSON yields an ok outcome", async () => { + const dir = mkdtempSync(join(tmpdir(), "cobol-proleap-ok-")); + const script = join(dir, "emit-ndjson.mjs"); + writeFileSync( + script, + 'process.stdout.write(\'{"kind":"program-id","name":"OK","filePath":"x.cbl","startLine":1,"endLine":1}\\n\');', + "utf8", + ); + + const outcome = await superviseProcess(process.execPath, [script], [], { + timeoutMs: 5_000, + killGraceMs: 1_000, + }); + + assert.equal(outcome.kind, "ok"); + assert.equal(outcome.kind === "ok" ? outcome.records.length : -1, 1); +}); diff --git a/packages/cobol-proleap/src/subprocess.ts b/packages/cobol-proleap/src/subprocess.ts index 38e08f99..21e8ca8b 100644 --- a/packages/cobol-proleap/src/subprocess.ts +++ b/packages/cobol-proleap/src/subprocess.ts @@ -12,8 +12,10 @@ * silently reparses every input path via the regex hot path. * * Timeouts: the default 60 s cap per batch is generous enough that even a - * large copybook tree finishes; beyond that the subprocess is killed and - * the batch is treated as a crash. + * large copybook tree finishes; beyond that the subprocess is sent SIGTERM + * and the batch is treated as a crash. A wedged JVM that ignores SIGTERM is + * escalated to SIGKILL after a short grace window, and the batch Promise is + * resolved from the buffered partial so it never hangs. */ import { spawn } from "node:child_process"; @@ -78,19 +80,70 @@ export async function runBatch( } await requireJre17(); - const timeoutMs = opts.timeoutMs ?? 60_000; const javaBin = opts.javaBin ?? "java"; const classpath = [opts.jarPath, opts.wrapperClassPath].join(delimiter); const args = ["-cp", classpath, "cobol_to_scip"]; + return await superviseProcess(javaBin, args, paths, { + timeoutMs: opts.timeoutMs ?? 60_000, + killGraceMs: opts.killGraceMs ?? 3_000, + }); +} + +/** + * Spawn `command`, feed `stdinLines` (one per line) on stdin, buffer stdout + * as NDJSON, and supervise the lifetime: + * + * - On a clean exit, the buffered stdout is parsed and returned (`ok`, or + * `crashed` for a non-zero code / malformed NDJSON). + * - On `timeoutMs`, the child is sent SIGTERM. If it ignores SIGTERM and no + * `'exit'` follows within `killGraceMs`, it is escalated to SIGKILL and + * the returned Promise is settled from the buffered partial — so a wedged + * child can never leave this Promise unresolved. + * + * Extracted from {@link runBatch} so the timeout/kill supervision can be + * exercised against a stand-in process without a live JVM. + */ +export async function superviseProcess( + command: string, + args: readonly string[], + stdinLines: readonly string[], + opts: { readonly timeoutMs: number; readonly killGraceMs: number }, +): Promise { + const { timeoutMs, killGraceMs } = opts; return await new Promise((resolve) => { - const child = spawn(javaBin, args, { stdio: ["pipe", "pipe", "pipe"] }); + const child = spawn(command, [...args], { stdio: ["pipe", "pipe", "pipe"] }); let stdoutBuf = ""; let stderrBuf = ""; let timedOut = false; + let sigkillSent = false; + let settled = false; + let killTimer: ReturnType | undefined; + const clearTimers = (): void => { + clearTimeout(timer); + if (killTimer !== undefined) clearTimeout(killTimer); + }; + const settle = (outcome: RunOutcome): void => { + if (settled) return; + settled = true; + clearTimers(); + resolve(outcome); + }; const timer = setTimeout(() => { timedOut = true; child.kill("SIGTERM"); + // Escalate to SIGKILL if the child ignores SIGTERM (e.g. a JVM wedged + // in native code), and settle from the buffered partial so the Promise + // never hangs even if no 'exit' event ever fires. + killTimer = setTimeout(() => { + sigkillSent = true; + child.kill("SIGKILL"); + settle({ + kind: "crashed", + reason: `JVM subprocess timed out after ${timeoutMs}ms and ignored SIGTERM (SIGKILL sent)`, + partial: parseRecords(stdoutBuf), + }); + }, killGraceMs); }, timeoutMs); child.stdout.setEncoding("utf8"); @@ -102,27 +155,33 @@ export async function runBatch( stderrBuf += d; }); child.on("error", (err) => { - clearTimeout(timer); - resolve({ + settle({ kind: "crashed", - reason: `spawn ${javaBin}: ${err.message}`, + reason: `spawn ${command}: ${err.message}`, partial: parseRecords(stdoutBuf), }); }); - child.on("exit", (code) => { - clearTimeout(timer); + child.on("exit", (code, signal) => { const records = parseRecords(stdoutBuf); if (timedOut) { - resolve({ + // Distinguish how the timed-out child actually died: a clean SIGTERM + // exit vs. an escalation to SIGKILL (either the kill timer already + // fired, or the OS reports the exit signal as SIGKILL). The test + // harness installs a SIGTERM-ignoring child to force the escalation + // path, so the reason must name SIGKILL whenever it was sent. + const killed = sigkillSent || signal === "SIGKILL"; + settle({ kind: "crashed", - reason: `JVM subprocess timed out after ${timeoutMs}ms`, + reason: killed + ? `JVM subprocess timed out after ${timeoutMs}ms and ignored SIGTERM (SIGKILL sent)` + : `JVM subprocess timed out after ${timeoutMs}ms`, partial: records, }); return; } if (code !== 0) { const tail = stderrBuf.trim().slice(-400); - resolve({ + settle({ kind: "crashed", reason: `JVM exited ${code}. Stderr tail: ${tail}`, partial: records, @@ -130,19 +189,19 @@ export async function runBatch( return; } if (records.malformed) { - resolve({ + settle({ kind: "crashed", reason: `Malformed NDJSON on stdout (${records.malformed} bad line(s))`, partial: records, }); return; } - resolve({ kind: "ok", records }); + settle({ kind: "ok", records }); }); // Feed the file list on stdin. The wrapper reads one path per line and // terminates when it sees EOF. - for (const p of paths) { + for (const p of stdinLines) { child.stdin.write(`${p}\n`); } child.stdin.end(); @@ -155,7 +214,7 @@ export async function runBatch( * triggers a fallback. The return value is an Array augmented with * the count so callers can read it without a second pass. */ -function parseRecords(raw: string): readonly JvmRecord[] & { malformed: number } { +export function parseRecords(raw: string): readonly JvmRecord[] & { malformed: number } { const out = [] as unknown as JvmRecord[] & { malformed: number }; out.malformed = 0; for (const line of raw.split("\n")) { @@ -175,11 +234,40 @@ function parseRecords(raw: string): readonly JvmRecord[] & { malformed: number } return out; } +const SYMBOL_KINDS = new Set([ + "program-id", + "paragraph", + "perform", + "copy", + "cics", + "data-item", + "file-descriptor", +]); + function isValidRecord(v: unknown): v is JvmRecord { if (v === null || typeof v !== "object") return false; - const rec = v as { kind?: unknown; filePath?: unknown }; + const rec = v as { + kind?: unknown; + filePath?: unknown; + name?: unknown; + startLine?: unknown; + endLine?: unknown; + message?: unknown; + }; if (typeof rec.kind !== "string" || typeof rec.filePath !== "string") return false; - return true; + if (rec.kind === "diagnostic") { + return typeof rec.message === "string"; + } + // Every non-diagnostic kind is a symbol ref; recordToElement copies + // name/startLine/endLine straight through, so a record missing any of them + // would leak undefined fields into a CobolDeepElement. Reject those as + // malformed rather than trust them. + if (!SYMBOL_KINDS.has(rec.kind as JvmRecord["kind"])) return false; + return ( + typeof rec.name === "string" && + typeof rec.startLine === "number" && + typeof rec.endLine === "number" + ); } /** diff --git a/packages/cobol-proleap/src/types.ts b/packages/cobol-proleap/src/types.ts index 6e533575..b838383b 100644 --- a/packages/cobol-proleap/src/types.ts +++ b/packages/cobol-proleap/src/types.ts @@ -58,6 +58,11 @@ export interface ParseCobolDeepOptions { * Per-batch timeout in milliseconds. Default: 60 000. */ readonly timeoutMs?: number; + /** + * Grace window in milliseconds between the SIGTERM sent on timeout and the + * SIGKILL escalation for a JVM that ignores SIGTERM. Default: 3 000. + */ + readonly killGraceMs?: number; /** * Structured log sink. Default: silent. */ diff --git a/packages/core-types/README.md b/packages/core-types/README.md index 389ad24c..f44dc7a9 100644 --- a/packages/core-types/README.md +++ b/packages/core-types/README.md @@ -6,25 +6,52 @@ imports nothing from the rest of the workspace. ## Surface +Everything ships from the single `.` entry point — there is no `/hash` +subpath. + ```ts -import type { SymbolNode, EdgeKind, RepoUri } from "@opencodehub/core-types"; -import { hashSymbol } from "@opencodehub/core-types/hash"; +import { + type GraphNode, + type RepoNode, + type RelationType, + type CodeRelation, + type NodeId, + makeNodeId, + graphHash, + canonicalJson, +} from "@opencodehub/core-types"; ``` -- **Node types** — `SymbolNode`, `FileNode`, `RepoNode`, `ProcessNode`, and - the `repo_uri` typed attribute (ADR 0012, - `packages/core-types/src/nodes.ts:524-552`). -- **Edge kinds** — the full `EdgeKind` enum and its confidence model (0.5 - heuristic, 1.0 SCIP-confirmed, 0.2 SCIP-unconfirmed). -- **`hashSymbol`** — the canonical, determinism-enforcing hash function. - Identical inputs must always produce the same output; this is the - project-wide invariant. -- **`RepoUri`** — branded string type for Sourcegraph-style URIs - (`github.com/org/repo`, `local:`). +- **Node types** — `GraphNode` is the discriminated union over the concrete + `*Node` interfaces (`FileNode`, `FunctionNode`, `ClassNode`, `RepoNode`, + `ProcessNode`, …) in `src/nodes.ts`. `RepoNode.repoUri` is a plain + `readonly string` carrying the Sourcegraph-style handle — + `github.com/org/repo`, or `local:` when no git + remote exists (`src/nodes.ts`, ADR 0012). +- **Relation types** — `RelationType` is the string-literal union of every + edge label (`CONTAINS`, `CALLS`, `DEPENDS_ON`, …); `RELATION_TYPES` is the + matching runtime array, append-only because its order feeds downstream + hashes. Edges are `CodeRelation` records, whose `confidence` field is a bare + `number` in `[0, 1]` doubling as an edge weight. +- **Identity** — `makeNodeId(kind, filePath, qualifiedName, opts)` builds the + canonical, branded `NodeId`; `makeEdgeId` does the same for `EdgeId`. + `parseNodeId` recovers the structured parts. +- **Determinism primitives** — `graphHash(graph)` is the canonical SHA-256 of + a `KnowledgeGraph`'s `{edges, nodes}` projection; identical graph content + must always produce the same digest, regardless of insertion order. It is + built on `canonicalJson` / `writeCanonicalJson` (sorted keys, streaming) and + the `sha256Hex` / `hash6` helpers. +- **Provenance** — `PROVENANCE_PREFIXES`, `SCIP_PROVENANCE_PREFIXES`, and + `LSP_PROVENANCE_PREFIXES` classify where a relation came from. Confidence + tiers (heuristic vs. SCIP-confirmed vs. SCIP-unconfirmed) are assigned by the + ingestion pipeline's confidence-demote phase, not by this package. +- **Schema version** — `SCHEMA_VERSION` plus `compareSchemaVersion`, which + classifies an indexed graph's version against the running binary + (`major-drift` / `minor-drift` / `forward-incompat` / `ok`). ## Design -- Zero runtime dependencies — safe to import from any environment including - edge runtimes. -- Types are declared with `satisfies` checks so schema drift is caught at - compile time rather than at runtime. +- Zero runtime dependencies — safe to import from any environment. +- Determinism is the project-wide invariant: `makeNodeId` and `graphHash` are + pure functions of their inputs, so two indexes of the same commit produce + byte-identical graph hashes. diff --git a/packages/core-types/src/graph.test.ts b/packages/core-types/src/graph.test.ts index 5f68fa7d..3907f609 100644 --- a/packages/core-types/src/graph.test.ts +++ b/packages/core-types/src/graph.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert/strict"; import { test } from "node:test"; import { KnowledgeGraph } from "./graph.js"; +import { graphHash } from "./graph-hash.js"; import type { NodeId } from "./id.js"; import { makeNodeId } from "./id.js"; import type { GraphNode } from "./nodes.js"; @@ -78,6 +79,51 @@ test("addEdge: different step values treated as distinct", () => { assert.equal(g.edgeCount(), 2); }); +test("addNode: equal field-count merge is insertion-order independent", () => { + const id = makeNodeId("Class", "src/a.ts", "Foo"); + // Two records for the same id, each with the same number of defined fields + // but differing values (startLine 10 vs 99). Before the canonical-JSON + // tiebreak the first writer won, so A-then-B and B-then-A kept different + // startLines and produced different graph hashes. + const a = cls(id, { startLine: 10, endLine: 42 }); + const b = cls(id, { startLine: 99, endLine: 42 }); + + const gAB = new KnowledgeGraph(); + gAB.addNode(a); + gAB.addNode(b); + const gBA = new KnowledgeGraph(); + gBA.addNode(b); + gBA.addNode(a); + + assert.equal(graphHash(gAB), graphHash(gBA)); + // The deterministic survivor is the canonically-smaller record (startLine 10). + const kept = gAB.getNode(id); + assert.equal(kept && "startLine" in kept ? kept.startLine : undefined, 10); +}); + +test("addEdge: equal-confidence merge is insertion-order independent", () => { + const from = makeNodeId("Function", "a.ts", "f"); + const to = makeNodeId("Function", "b.ts", "g"); + // Same (from, type, to, step) and same confidence, but different `reason`. + // `reason` is serialized into the graph hash, so the survivor must not depend + // on which writer arrived first. + const eAlpha = { from, to, type: "CALLS", confidence: 0.7, reason: "alpha" } as const; + const eBeta = { from, to, type: "CALLS", confidence: 0.7, reason: "beta" } as const; + + const gAB = new KnowledgeGraph(); + gAB.addEdge(eAlpha); + gAB.addEdge(eBeta); + const gBA = new KnowledgeGraph(); + gBA.addEdge(eBeta); + gBA.addEdge(eAlpha); + + assert.equal(graphHash(gAB), graphHash(gBA)); + const [only] = [...gAB.edges()]; + assert.ok(only); + // Canonically-smaller record wins: "alpha" < "beta". + assert.equal(only.reason, "alpha"); +}); + test("orderedNodes / orderedEdges: produce canonical sort", () => { const g = new KnowledgeGraph(); const a = makeNodeId("Function", "a.ts", "a"); diff --git a/packages/core-types/src/graph.ts b/packages/core-types/src/graph.ts index 5bdd1b91..afe7374f 100644 --- a/packages/core-types/src/graph.ts +++ b/packages/core-types/src/graph.ts @@ -1,4 +1,5 @@ import type { CodeRelation } from "./edges.js"; +import { canonicalJson } from "./hash.js"; import { makeEdgeId, type NodeId } from "./id.js"; import type { GraphNode } from "./nodes.js"; import { sortEdges, sortNodes } from "./ordering.js"; @@ -12,6 +13,20 @@ function definedFieldCount(node: GraphNode): number { return n; } +// Deterministic tiebreak for equal-primary merges: compare the canonical-JSON +// projection of two records and keep the byte-smaller one. canonicalJson sorts +// object keys, so this ordering is independent of field insertion order — which +// is the property addNode/addEdge need so that full vs incremental re-indexes +// (which can present the same id in different phase/scan orders) converge on the +// same winner and therefore the same graphHash. +function canonicalCompare(a: unknown, b: unknown): number { + const ca = canonicalJson(a); + const cb = canonicalJson(b); + if (ca < cb) return -1; + if (ca > cb) return 1; + return 0; +} + function edgeDedupKey(e: Pick): string { return `${e.from}\x00${e.type}\x00${e.to}\x00${e.step ?? 0}`; } @@ -26,7 +41,15 @@ export class KnowledgeGraph { this.nodeById.set(node.id, node); return; } - if (definedFieldCount(node) > definedFieldCount(existing)) { + const candidateFields = definedFieldCount(node); + const existingFields = definedFieldCount(existing); + if (candidateFields > existingFields) { + this.nodeById.set(node.id, node); + return; + } + // Equal field count: break the tie deterministically by canonical-JSON + // order so the survivor never depends on which writer arrived first. + if (candidateFields === existingFields && canonicalCompare(node, existing) < 0) { this.nodeById.set(node.id, node); } } @@ -42,6 +65,13 @@ export class KnowledgeGraph { } if (candidate.confidence > existing.confidence) { this.edgeByKey.set(key, candidate); + return; + } + // Equal confidence: `reason` is part of the graphHash-serialized payload, + // so the survivor must not depend on insertion order. Break the tie by + // canonical-JSON order of the full edge record. + if (candidate.confidence === existing.confidence && canonicalCompare(candidate, existing) < 0) { + this.edgeByKey.set(key, candidate); } } diff --git a/packages/core-types/src/id.test.ts b/packages/core-types/src/id.test.ts index f7fc11b2..cbb657b0 100644 --- a/packages/core-types/src/id.test.ts +++ b/packages/core-types/src/id.test.ts @@ -98,6 +98,25 @@ test("parseNodeId: qualified name may contain colons from file path edges", () = assert.equal(parsed.qualifiedName, "Outer::Inner"); }); +test("parseNodeId: non-callable kind keeps a #N tail as part of the name", () => { + // A Variable literally named `tmp#42` is not an arity suffix — only callable + // kinds carry `#paramCount`. The tail must survive round-trip intact. + const id = makeNodeId("Variable", "src/a.ts", "tmp#42"); + assert.equal(id, "Variable:src/a.ts:tmp#42"); + const parsed = parseNodeId(id); + assert.equal(parsed.kind, "Variable"); + assert.equal(parsed.filePath, "src/a.ts"); + assert.equal(parsed.qualifiedName, "tmp#42"); + assert.equal(parsed.parameterCount, undefined); +}); + +test("parseNodeId: callable kind still recovers a real #N arity tail", () => { + const id = makeNodeId("Function", "src/a.ts", "f", { parameterCount: 3 }); + const parsed = parseNodeId(id); + assert.equal(parsed.qualifiedName, "f"); + assert.equal(parsed.parameterCount, 3); +}); + test("makeEdgeId: deterministic concatenation form", () => { const from = "Function:a.ts:f" as NodeId; const to = "Function:b.ts:g" as NodeId; diff --git a/packages/core-types/src/id.ts b/packages/core-types/src/id.ts index 33a8b83f..d321d4d4 100644 --- a/packages/core-types/src/id.ts +++ b/packages/core-types/src/id.ts @@ -52,6 +52,20 @@ export interface ParsedNodeId { } export function parseNodeId(id: NodeId): ParsedNodeId { + // Anchor parsing to the node kind FIRST. The arity (`#N`), type-hash + // (`~<6hex>`), and const (`$const`) suffixes are only ever appended by + // makeNodeId, and the `#N` arity is reserved for callable kinds. Stripping + // them unconditionally is lossy for symbols whose qualifiedName legitimately + // contains those characters (e.g. a Variable literally named `tmp#42`), so we + // only peel a suffix off when the kind makes it a suffix makeNodeId could have + // produced. + const firstColon = (id as string).indexOf(":"); + if (firstColon === -1) { + throw new Error(`Invalid node id (missing kind separator): ${id}`); + } + const kind = (id as string).slice(0, firstColon) as NodeKind; + const isCallable = CALLABLE_KINDS.has(kind); + let rest = id as string; let isConst = false; if (rest.endsWith("$const")) { @@ -65,19 +79,16 @@ export function parseNodeId(id: NodeId): ParsedNodeId { rest = rest.slice(0, tildeIdx); } let parameterCount: number | undefined; - const hashIdx = rest.lastIndexOf("#"); - if (hashIdx !== -1) { - const maybeNum = rest.slice(hashIdx + 1); - if (/^\d+$/.test(maybeNum)) { - parameterCount = Number(maybeNum); - rest = rest.slice(0, hashIdx); + if (isCallable) { + const hashIdx = rest.lastIndexOf("#"); + if (hashIdx !== -1) { + const maybeNum = rest.slice(hashIdx + 1); + if (/^\d+$/.test(maybeNum)) { + parameterCount = Number(maybeNum); + rest = rest.slice(0, hashIdx); + } } } - const firstColon = rest.indexOf(":"); - if (firstColon === -1) { - throw new Error(`Invalid node id (missing kind separator): ${id}`); - } - const kind = rest.slice(0, firstColon) as NodeKind; const afterKind = rest.slice(firstColon + 1); const secondColon = afterKind.indexOf(":"); if (secondColon === -1) { diff --git a/packages/core-types/src/nodes.ts b/packages/core-types/src/nodes.ts index 428e7495..2314025b 100644 --- a/packages/core-types/src/nodes.ts +++ b/packages/core-types/src/nodes.ts @@ -458,12 +458,19 @@ export type FrameworkCategory = /** * Structured evidence for a single framework detection. Each entry is a - * citation — which of the 5 pipeline stages produced it, which source - * file or symbol supplied the signal, and a short human-readable detail. - * Replaces the unstructured `signals: string[]` field on v1.0 graphs. + * citation — which detection stage produced it, which source file or symbol + * supplied the signal, and a short human-readable detail. Replaces the + * unstructured `signals: string[]` field on v1.0 graphs. + * + * The wired profile-time pipeline emits stages 1 (manifest), 2 (lockfile), + * and 4 (folder/file marker). Stages 3 (config-AST) and 5 (imports) ship as + * standalone, independently-tested modules in `@opencodehub/frameworks` but + * are not yet wired — stage 5 in particular needs the materialized IMPORTS + * edges, which do not exist at profile time (profile is a leaf on `scan`). + * The `stage` union keeps 3 and 5 as reserved, forward-looking values. */ export interface Evidence { - /** Which pipeline stage produced this evidence (1=manifest, 2=lockfile, 3=config-AST, 4=folder, 5=imports). */ + /** Which detection stage produced this evidence (1=manifest, 2=lockfile, 3=config-AST, 4=folder, 5=imports). Profile-time wiring emits 1/2/4; 3 and 5 are reserved. */ readonly stage: 1 | 2 | 3 | 4 | 5; /** Source file path or symbol id that supplied the signal. */ readonly source: string; @@ -478,7 +485,7 @@ export interface FrameworkDetection { readonly version?: string; readonly confidence: "deterministic" | "heuristic" | "composite"; /** - * Structured evidence the 5-stage detection pipeline produced. Sorted + * Structured evidence the framework-detection pipeline produced. Sorted * deterministically by (stage, source, detail) for byte-stable output. */ readonly evidence: readonly Evidence[]; diff --git a/packages/core-types/src/schema-version.test.ts b/packages/core-types/src/schema-version.test.ts new file mode 100644 index 00000000..99d66eff --- /dev/null +++ b/packages/core-types/src/schema-version.test.ts @@ -0,0 +1,33 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { compareSchemaVersion, SCHEMA_VERSION } from "./schema-version.js"; + +test("compareSchemaVersion: same version is ok", () => { + assert.equal(compareSchemaVersion(SCHEMA_VERSION), "ok"); +}); + +test("compareSchemaVersion: differing major is major-drift", () => { + assert.equal(compareSchemaVersion("2.0.0"), "major-drift"); + assert.equal(compareSchemaVersion("0.9.0"), "major-drift"); +}); + +test("compareSchemaVersion: older indexed minor is minor-drift", () => { + assert.equal(compareSchemaVersion("1.1.0"), "minor-drift"); +}); + +test("compareSchemaVersion: newer indexed minor is forward-incompat", () => { + // An older binary reading a graph written by a newer-schema binary must not + // be reported as drift-free: it may be missing fields/invariants the newer + // minor introduced. + assert.equal(compareSchemaVersion("1.3.0"), "forward-incompat"); +}); + +test("compareSchemaVersion: patch differences are tolerated as ok", () => { + assert.equal(compareSchemaVersion("1.2.9"), "ok"); + assert.equal(compareSchemaVersion("1.2"), "ok"); +}); + +test("compareSchemaVersion: rejects malformed versions", () => { + assert.throws(() => compareSchemaVersion("1")); + assert.throws(() => compareSchemaVersion("x.y.z")); +}); diff --git a/packages/core-types/src/schema-version.ts b/packages/core-types/src/schema-version.ts index ed1f1ce6..73dc1f2d 100644 --- a/packages/core-types/src/schema-version.ts +++ b/packages/core-types/src/schema-version.ts @@ -1,6 +1,20 @@ export const SCHEMA_VERSION = "1.2.0" as const; -export type SchemaCompareResult = "ok" | "major-drift" | "minor-drift"; +/** + * Result of comparing an indexed graph's schema version against the running + * binary's {@link SCHEMA_VERSION}. + * + * - `ok` — same major, same minor. Patch differences are deliberately treated + * as `ok`: patch bumps are reserved for backward- and forward-compatible + * changes that never alter graph bytes or invariants. + * - `major-drift` — major versions differ; the graph must be re-indexed. + * - `minor-drift` — the indexed graph is an OLDER minor than the binary; it may + * be missing fields/invariants the newer binary expects (backward gap). + * - `forward-incompat` — the indexed graph is a NEWER minor than the binary; an + * older binary cannot assume it understands fields/invariants the newer + * schema introduced (forward gap). + */ +export type SchemaCompareResult = "ok" | "major-drift" | "minor-drift" | "forward-incompat"; interface Semver { readonly major: number; @@ -28,5 +42,6 @@ export function compareSchemaVersion(indexed: string): SchemaCompareResult { const b = parseSemver(SCHEMA_VERSION); if (a.major !== b.major) return "major-drift"; if (a.minor < b.minor) return "minor-drift"; + if (a.minor > b.minor) return "forward-incompat"; return "ok"; } diff --git a/packages/docs/README.md b/packages/docs/README.md index c22afbc3..0ea6e71b 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -26,7 +26,7 @@ Top-level sections under `src/content/docs/`: - `start-here/` — install, quick-start, first query. - `guides/` — editor integrations and task-oriented walkthroughs. -- `mcp/` — server overview, tool catalog, resources, prompts. +- `mcp/` — server overview, tool catalog, resources, and the prompts page (which documents that the prompts surface is intentionally empty — 0 prompts). - `reference/` — CLI, error codes, language matrix, configuration. - `architecture/` — monorepo map, determinism, supply chain, ADR index. - `skills/` — Claude Code skill references. @@ -34,8 +34,8 @@ Top-level sections under `src/content/docs/`: ## ADRs -Architecture decision records live at `/docs/adr/` at the repo root — 10 -files, numbered `0001-*.md` through `0010-*.md`. The Starlight site +Architecture decision records live at `/docs/adr/` at the repo root — 18 +files, numbered `0001` through `0017` (with a duplicate `0013`). The Starlight site surfaces them through an index page at `src/content/docs/architecture/adrs.md`, so readers get both the canonical source and a browsable index. diff --git a/packages/docs/astro.config.mjs b/packages/docs/astro.config.mjs index 9f7ff827..96bb099b 100644 --- a/packages/docs/astro.config.mjs +++ b/packages/docs/astro.config.mjs @@ -20,7 +20,7 @@ export default defineConfig({ starlight({ title: "OpenCodeHub", description: - "Apache-2.0 code intelligence graph + MCP server for AI coding agents. 29 tools, 15 GA languages, LadybugDB-default, WASM-default parsing, deterministic, offline-capable.", + "Apache-2.0 code intelligence graph + MCP server for AI coding agents. 30 tools, 15 GA languages, lbug graph + DuckDB temporal, WASM-only parsing, deterministic, offline-capable.", logo: { src: "./src/assets/logo.svg", replacesTitle: false, @@ -48,7 +48,7 @@ export default defineConfig({ description: "Apache-2.0 code intelligence graph + MCP server for AI coding agents. Gives agents callers, callees, processes, and blast radius in one MCP tool call — local, offline-capable, deterministic.", details: - "OpenCodeHub indexes a repository into a hybrid structural + semantic knowledge graph and exposes it over the Model Context Protocol (MCP) to AI coding agents. The MCP server registers 29 tools across five families — exploration (list_repos, query, context, impact, detect_changes, rename, sql), group / federation (group_list, group_query, group_status, group_contracts, group_cross_repo_links, group_sync), scan / findings / verdict (scan, list_findings, list_findings_delta, list_dead_code, remove_dead_code, license_audit, verdict, risk_trends), HTTP / routing (route_map, api_impact, shape_check, tool_map), and meta (project_profile, dependencies, owners, pack_codebase). The CLI binary is `codehub`. Runtime: Node 20, 22, or 24, pnpm 10, LadybugDB graph store + DuckDB temporal sibling by default (legacy single-file DuckDB layout opt-in via CODEHUB_STORE=duck), web-tree-sitter (WASM) is the only parse runtime with all 15 grammar `.wasm` blobs vendored at packages/ingestion/vendor/wasms/, 15 GA languages, SCIP indexers for TypeScript / TSX / JavaScript / Python / Go / Rust / Java / C# / C / C++ / Kotlin / Ruby. 20-scanner inventory. Apache-2.0 end to end. Repos are first-class graph nodes (`repo_uri`); the cross-repo `group_*` family fans out over named groups; AMBIGUOUS_REPO error envelope returns `choices[]` so a caller can retry deterministically.", + "OpenCodeHub indexes a repository into a hybrid structural + semantic knowledge graph and exposes it over the Model Context Protocol (MCP) to AI coding agents. The MCP server registers 30 tools across five families — exploration (list_repos, query, context, impact, detect_changes, rename, sql, signature), group / federation (group_list, group_query, group_status, group_contracts, group_cross_repo_links, group_sync), scan / findings / verdict (scan, list_findings, list_findings_delta, list_dead_code, remove_dead_code, license_audit, verdict, risk_trends), HTTP / routing (route_map, api_impact, shape_check, tool_map), and meta (project_profile, dependencies, owners, pack_codebase). The CLI binary is `codehub`. Runtime: Node 20, 22, or 24, pnpm 11, lbug graph store (graph.lbug) + DuckDB temporal sibling (temporal.duckdb), always both, no backend selector (ADR 0016), web-tree-sitter (WASM) is the only parse runtime with all 15 grammar `.wasm` blobs vendored at packages/ingestion/vendor/wasms/, 15 GA languages, SCIP indexers for TypeScript / TSX / JavaScript / Python / Go / Rust / Java / C# / C / C++ / Kotlin / Ruby. 19-scanner inventory. Apache-2.0 end to end. Repos are first-class graph nodes (`repo_uri`); the cross-repo `group_*` family fans out over named groups; AMBIGUOUS_REPO error envelope returns `choices[]` so a caller can retry deterministically.", promote: [ "start-here/**", "agents/**", @@ -80,7 +80,7 @@ export default defineConfig({ label: "agents", paths: ["agents/**", "mcp/**"], description: - "Agent-side reference: per-editor MCP setup, the 29-tool catalog, tool decision matrix, idiomatic prompts.", + "Agent-side reference: per-editor MCP setup, the 30-tool catalog, tool decision matrix, idiomatic prompts.", }, { label: "mcp", diff --git a/packages/docs/public/tool-catalog.json b/packages/docs/public/tool-catalog.json index 384d6719..33d7b1b6 100644 --- a/packages/docs/public/tool-catalog.json +++ b/packages/docs/public/tool-catalog.json @@ -1,14 +1,14 @@ { "$schema": "https://opencodehub.dev/schemas/tool-catalog-v1.json", "version": "1.0.0", - "description": "Machine-readable catalog of the 29 MCP tools the OpenCodeHub server registers. Generated to be fetched by an AI coding agent that wants the catalog without scraping the docs.", + "description": "Machine-readable catalog of the 28 MCP tools the OpenCodeHub server registers. Every tool is read-only with respect to user source — no tool edits the working tree. Generated to be fetched by an AI coding agent that wants the catalog without scraping the docs.", "server": { "name": "opencodehub", "transport": "stdio", "launch_command": "codehub mcp", "capabilities": ["tools", "resources"] }, - "tool_count": 29, + "tool_count": 28, "families": { "exploration": "High-frequency code-graph tools.", "group": "Cross-repo federation tools (require a named group).", @@ -63,22 +63,22 @@ "example": "detect_changes({scope: 'compare', compare_ref: 'origin/main'})" }, { - "name": "rename", + "name": "sql", "family": "exploration", - "description": "Coordinated multi-file symbol rename with confidence-tagged edits. Default mode is dry-run.", - "when_to_use": "Renaming a symbol used across multiple files.", - "when_not_to_use": "The rename is in a single file — let the editor handle it.", - "signature_sketch": "rename({from, to, repo?, repo_uri?, dry_run?}) -> {edits, cross_module_refs, next_steps}", - "example": "rename({from: 'oldName', to: 'newName'})" + "description": "Read-only SQL against the DuckDB temporal store (cochanges + symbol_summaries). 5-second timeout. The node/edge graph is queried via the typed tools or Cypher.", + "when_to_use": "Custom view of the temporal store (cochanges + symbol_summaries) that no other tool exposes.", + "when_not_to_use": "A typed tool (context, impact, query) already covers the question, or you need the node/edge graph (reach it via the typed tools or Cypher).", + "signature_sketch": "sql({query, repo?, repo_uri?}) -> {rows, row_count, next_steps}", + "example": "sql({query: 'SELECT * FROM cochanges LIMIT 10'})" }, { - "name": "sql", + "name": "signature", "family": "exploration", - "description": "Read-only SQL against the graph store. 5-second timeout.", - "when_to_use": "Custom view of the graph that no other tool exposes.", - "when_not_to_use": "A typed tool (context, impact, query) already covers the question.", - "signature_sketch": "sql({query, repo?, repo_uri?}) -> {rows, row_count, next_steps}", - "example": "sql({query: 'SELECT name, file_path FROM nodes WHERE kind = ? LIMIT 10', params: ['Class']})" + "description": "Symbol declaration + stubbed members: a class/interface header plus its method and property signatures with bodies elided (stub syntax per language). For a standalone function, returns a single signature.", + "when_to_use": "You want a symbol's shape without reading the whole file — saves tokens.", + "when_not_to_use": "You need callers/callees — call context instead.", + "signature_sketch": "signature({name?, uid?, file_path?, kind?, repo?, repo_uri?}) -> {target, language, member_count, members, stub, next_steps}", + "example": "signature({name: 'GraphDbStore'})" }, { "name": "group_list", @@ -170,15 +170,6 @@ "signature_sketch": "list_dead_code({repo?, repo_uri?}) -> {candidates: Array<{id, name, file_path, kind, reason}>}", "example": "list_dead_code()" }, - { - "name": "remove_dead_code", - "family": "scan", - "description": "Remove specific dead symbols from disk. Default mode is dry-run.", - "when_to_use": "After list_dead_code, with explicit targets.", - "when_not_to_use": "Speculative cleanup — always dry-run first.", - "signature_sketch": "remove_dead_code({repo?, repo_uri?, targets, dry_run?}) -> {removed, skipped, next_steps}", - "example": "remove_dead_code({targets: ['oldHelper'], dry_run: true})" - }, { "name": "license_audit", "family": "scan", diff --git a/packages/docs/src/content/docs/agents/discovery-and-resources.mdx b/packages/docs/src/content/docs/agents/discovery-and-resources.mdx index a0af99e1..1d14c4e5 100644 --- a/packages/docs/src/content/docs/agents/discovery-and-resources.mdx +++ b/packages/docs/src/content/docs/agents/discovery-and-resources.mdx @@ -57,7 +57,7 @@ submission status on the [MCP registries page](/opencodehub/agents/registries/). ## Source of truth for tool inventory -The MCP server registers 29 tools at +The MCP server registers 28 tools at [`packages/mcp/src/server.ts`](https://github.com/theagenticguy/opencodehub/blob/main/packages/mcp/src/server.ts). Grep for `register[A-Z][a-zA-Z]+Tool\(server` to see the live list. If this site or any registry disagrees with the file, the file wins. diff --git a/packages/docs/src/content/docs/agents/editors/claude-code.mdx b/packages/docs/src/content/docs/agents/editors/claude-code.mdx index a8a14d13..dffde733 100644 --- a/packages/docs/src/content/docs/agents/editors/claude-code.mdx +++ b/packages/docs/src/content/docs/agents/editors/claude-code.mdx @@ -36,8 +36,9 @@ The `.mcp.json` shape: } ``` -`codehub mcp` runs the stdio MCP server. The 29 tools register under -the `mcp__opencodehub__*` namespace. +`codehub mcp` runs the stdio MCP server. The 28 tools register under +the `mcp__opencodehub__*` namespace. Every one is read-only with +respect to your source — no tool edits the working tree. ## What the plugin ships @@ -53,7 +54,7 @@ The plugin lives at ## Subagent: `code-analyst` -The `code-analyst` subagent has access to 16 OCH MCP tools plus +The `code-analyst` subagent has access to 15 OCH MCP tools plus `Read`/`Grep`/`Glob`. Its rules of engagement: - **Exploring** — uses `query` for concept jumps and `context` for the @@ -65,8 +66,10 @@ The `code-analyst` subagent has access to 16 OCH MCP tools plus `signature` rather than paraphrasing. - **Risk** — `verdict`, `list_findings`, `list_findings_delta`, `license_audit`, `list_dead_code`. -- **Refactors** — `rename` with `dry_run: true` first, never applies - without explicit confirmation. +- **Refactor planning** — `impact` + `context` to map the blast radius + and every reference, then `detect_changes` to verify scope after you + apply the edits. The MCP surface is read-only; it plans and verifies, + it does not edit source. Every claim is grounded in a tool call. If a tool returns nothing, the subagent says so — it does not invent coverage. @@ -91,7 +94,7 @@ the table: | `opencodehub-pr-review` | "Review this PR", "What does PR #42 change?", "Is this PR safe to merge?" | | `opencodehub-exploring` | "How does X work?", "What calls this?", "Show me the auth flow." | | `opencodehub-debugging` | "Why is X failing?", "Where does this error come from?", "Trace this bug." | -| `opencodehub-refactoring` | "Rename this", "Extract this into a module", "Move this." | +| `opencodehub-refactoring` | "What breaks if I rename this?", "Map callers before I extract this module.", "Did my refactor touch anything unexpected?" (plan + verify; you apply the edits) | | `opencodehub-guide` | Reference for OpenCodeHub itself — tools, resources, schema. | Source: @@ -119,7 +122,7 @@ In a Claude Code session, ask: which OpenCodeHub tools do you see? ``` -The agent should list 29 tools, all under `mcp__opencodehub__*`. If it +The agent should list 28 tools, all under `mcp__opencodehub__*`. If it sees zero, the most common causes are: Claude Code wasn't restarted after `codehub init`, or `codehub` is not on PATH for the editor's process (try launching the editor from a shell that has `codehub` diff --git a/packages/docs/src/content/docs/agents/editors/cursor.mdx b/packages/docs/src/content/docs/agents/editors/cursor.mdx index 010ba014..52709a08 100644 --- a/packages/docs/src/content/docs/agents/editors/cursor.mdx +++ b/packages/docs/src/content/docs/agents/editors/cursor.mdx @@ -32,7 +32,7 @@ define the same server name. ``` That is the entire config. `codehub mcp` runs the stdio MCP server -and registers all 29 tools under `mcp__opencodehub__*`. +and registers all 28 tools under `mcp__opencodehub__*`. If `codehub` is not on your shell PATH (Cursor inherits the GUI app's environment, not your shell's), substitute the absolute path: @@ -55,7 +55,7 @@ Find the path with `which codehub` in your terminal. 1. Restart Cursor (the agent only loads MCP servers at startup). 2. Open the chat panel. 3. Ask: `which OpenCodeHub tools do you see?` -4. Expect 29 tools listed under `mcp__opencodehub__*`. +4. Expect 28 tools listed under `mcp__opencodehub__*`. If you see zero tools, check Cursor's MCP debug pane (Settings → MCP) for the server's stderr. The most common cause is `codehub` not being diff --git a/packages/docs/src/content/docs/agents/editors/opencode.mdx b/packages/docs/src/content/docs/agents/editors/opencode.mdx index 7fe38256..0241ff84 100644 --- a/packages/docs/src/content/docs/agents/editors/opencode.mdx +++ b/packages/docs/src/content/docs/agents/editors/opencode.mdx @@ -62,7 +62,7 @@ If you need env vars: 1. Restart OpenCode (or reload the workspace). 2. Open a chat session. 3. Ask: `which OpenCodeHub tools do you see?` -4. Expect 29 tools under `mcp__opencodehub__*`. +4. Expect 28 tools under `mcp__opencodehub__*`. OpenCode logs MCP server stderr to its dev console — open it if the server fails to register. diff --git a/packages/docs/src/content/docs/agents/editors/windsurf.mdx b/packages/docs/src/content/docs/agents/editors/windsurf.mdx index 07cdaec7..0b0ffcda 100644 --- a/packages/docs/src/content/docs/agents/editors/windsurf.mdx +++ b/packages/docs/src/content/docs/agents/editors/windsurf.mdx @@ -38,7 +38,7 @@ servers, add `codehub` as a sibling key under `mcpServers`. 1. Fully restart Windsurf — Cascade only loads MCP servers at boot. 2. Open Cascade in any project. 3. Ask: `which OpenCodeHub tools do you see?` -4. Expect 29 tools under `mcp__opencodehub__*`. +4. Expect 28 tools under `mcp__opencodehub__*`. If Cascade reports zero tools, check the MCP server status pane in Cascade's settings — failed servers list their stderr there. The diff --git a/packages/docs/src/content/docs/agents/idiomatic-prompts.mdx b/packages/docs/src/content/docs/agents/idiomatic-prompts.mdx index 5103621d..5ae5bd53 100644 --- a/packages/docs/src/content/docs/agents/idiomatic-prompts.mdx +++ b/packages/docs/src/content/docs/agents/idiomatic-prompts.mdx @@ -20,25 +20,26 @@ they were validated on. Before I rename `parseConfig` to `parseAppConfig` across this repo, audit the blast radius. Use OpenCodeHub: -1. Get the 360° context for `parseConfig`. +1. Get the 360° context for `parseConfig` — every inbound and outbound ref. 2. Compute upstream blast radius depth 3. -3. Dry-run the rename and show me the file list with edit counts. -4. Flag any dynamic-dispatch or shadowed-local risks. +3. From those two, give me the full edit list: every file:line I need to + touch, grouped by file, with a count. +4. Flag any dynamic-dispatch or shadowed-local references the graph can see + but a textual edit might miss. -Tell me LOW/MEDIUM/HIGH/CRITICAL risk and the one-line reason. -Do NOT apply the rename. I will say "apply" if I'm satisfied. +Tell me LOW/MEDIUM/HIGH/CRITICAL risk and the one-line reason. Do NOT edit +any files — OpenCodeHub is read-only; I will make the edits in my editor. ``` -**Expected tool calls:** `context` → `impact(direction: upstream, depth: 3)` -→ `rename(dry_run: true)`. +**Expected tool calls:** `context` → `impact(direction: upstream, depth: 3)`. **Expected output shape:** Risk tier on line 1; affected file count -and call-site count; bullet list of risk flags; explicit "no files -written" confirmation. +and call-site count; a per-file edit checklist; a bullet list of +dynamic-reference risk flags to check by hand. The Claude Code plugin's `code-analyst` subagent runs the same -`context` → `impact` → `rename(dry_run: true)` chain when invoked with -this prompt. +`context` → `impact` chain when invoked with this prompt, then hands +you the edit plan to apply. ## 2. Surface processes that touch the auth flow diff --git a/packages/docs/src/content/docs/agents/index.mdx b/packages/docs/src/content/docs/agents/index.mdx index b4b326ae..323caa36 100644 --- a/packages/docs/src/content/docs/agents/index.mdx +++ b/packages/docs/src/content/docs/agents/index.mdx @@ -9,7 +9,7 @@ import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components"; OpenCodeHub gives an AI coding agent a code graph it can query: callers, callees, processes, blast radius, owners, scanner findings, and a 5-tier -PR verdict — all behind 29 MCP tools served by one local binary. The +PR verdict — all behind 28 MCP tools served by one local binary. The graph is built deterministically from your repo and stored next to it. Other docs sections answer "what is OCH" and "how is it built." This @@ -40,7 +40,7 @@ codehub init # writes .mcp.json + links the Claude Code plugin codehub analyze # first index — 30s to a few minutes ``` -Restart your editor. Your agent now has 29 MCP tools, all prefixed +Restart your editor. Your agent now has 28 MCP tools, all prefixed `mcp__opencodehub__*`. See [Install](/opencodehub/agents/install/) for the full path or jump to the per-editor card below. @@ -65,7 +65,7 @@ the full path or jump to the per-editor card below. -`impact`, `rename --dry-run`, and `verdict` close all three. +`impact`, `detect_changes`, and `verdict` close all three — the agent +sees the blast radius and the affected diff *before* it writes the edit. ## What changes after `codehub init` -The agent gets 29 MCP tools at the next session start, grouped into -four families: +The agent gets 28 MCP tools at the next session start, grouped into +four families. Every tool is **read-only with respect to your source** — +they tell the agent what a change touches; the agent (or your editor) +makes the edit: - **Exploration** — `query`, `context`, `impact`, `detect_changes`, - `rename`, `sql`, `list_repos`. Concept-to-code search; per-symbol + `sql`, `list_repos`. Concept-to-code search; per-symbol callers, callees, and processes; blast-radius depth-N. - **Cross-repo groups** — `group_list`, `group_query`, `group_status`, `group_contracts`, `group_cross_repo_links`, `group_sync`. Federate the same questions across a named set of repos. - **Findings and verdicts** — `scan`, `list_findings`, - `list_findings_delta`, `list_dead_code`, `remove_dead_code`, - `license_audit`, `verdict`, `risk_trends`. Scanner output, PR - decisions, license tiers, dead code. + `list_findings_delta`, `list_dead_code`, `license_audit`, `verdict`, + `risk_trends`. Scanner output, PR decisions, license tiers, dead-code + listing. - **HTTP and routing** — `route_map`, `api_impact`, `shape_check`, `tool_map`. HTTP routes and handlers; structural drift in payloads; CLI/MCP tool surfaces. diff --git a/packages/docs/src/content/docs/architecture/adrs.md b/packages/docs/src/content/docs/architecture/adrs.md index bea6b9ed..fb2cb99c 100644 --- a/packages/docs/src/content/docs/architecture/adrs.md +++ b/packages/docs/src/content/docs/architecture/adrs.md @@ -17,9 +17,9 @@ considered, and consequences. DuckDB via `@duckdb/node-api` plus the `hnsw_acorn` community extension for filter-aware vector search and the official `fts` extension for BM25. SQLite + `sqlite-vec` was rejected because FTS5 -has no filtered-HNSW story. Superseded as the default by ADR 0011 + -ADR 0013, but DuckDB is still the temporal-store backend and the -single-file opt-in fallback. +has no filtered-HNSW story. Superseded as the graph backend by ADR +0011 + ADR 0013, and the single-file DuckDB graph layout was removed +entirely in ADR 0016; DuckDB now backs the temporal store only. [Read ADR 0001](https://github.com/theagenticguy/opencodehub/blob/main/docs/adr/0001-storage-backend.md) @@ -140,6 +140,26 @@ builds and zero GitHub fetches. Supersedes ADR 0013 (parse runtime). [Read ADR 0015](https://github.com/theagenticguy/opencodehub/blob/main/docs/adr/0015-wasm-only-parser-at-the-npm-distributed-boundary.md) +### ADR 0016 — DuckDB graph rip-out + +Remove the DuckDB graph backend, the `CODEHUB_STORE` env var, the +backend probe, and the single-file `graph.duckdb` layout. The graph +tier is always `@ladybugdb/core` (`graph.lbug`); the temporal tier is +always DuckDB (`temporal.duckdb`); both files are written on every +`analyze`, with no selector. A missing graph binding hard-fails with +`GraphDbBindingError`. The segregated `IGraphStore` / `ITemporalStore` +interfaces stay as the community-fork adapter contract. + +[Read ADR 0016](https://github.com/theagenticguy/opencodehub/blob/main/docs/adr/0016-duckdb-graph-rip.md) + +### ADR 0017 — Drop detect-secrets, tune betterleaks + +Remove `detect-secrets` from the scanner fleet in favour of +`betterleaks`, bringing the catalog to 19 scanners. Records the +tuning rationale and the secret-detection coverage tradeoff. + +[Read ADR 0017](https://github.com/theagenticguy/opencodehub/blob/main/docs/adr/0017-drop-detect-secrets-tune-betterleaks.md) + ## Superseded ### ADR 0003 — CI toolchain pins diff --git a/packages/docs/src/content/docs/architecture/determinism.md b/packages/docs/src/content/docs/architecture/determinism.md index 954b5d09..e11d44f7 100644 --- a/packages/docs/src/content/docs/architecture/determinism.md +++ b/packages/docs/src/content/docs/architecture/determinism.md @@ -33,7 +33,7 @@ Three concrete reasons: An input is: - Source tree contents at the current commit. -- Toolchain versions (Node 22 or 24, pnpm 10.x, tree-sitter grammars +- Toolchain versions (Node 22 or 24, pnpm 11.x, tree-sitter grammars pinned in `packages/ingestion/package.json`, SCIP indexer versions pinned in `.github/workflows/gym.yml` per ADR 0006). - OpenCodeHub version (the monorepo version pinned in diff --git a/packages/docs/src/content/docs/architecture/monorepo-map.md b/packages/docs/src/content/docs/architecture/monorepo-map.md index 706012c2..d41330c9 100644 --- a/packages/docs/src/content/docs/architecture/monorepo-map.md +++ b/packages/docs/src/content/docs/architecture/monorepo-map.md @@ -6,26 +6,27 @@ sidebar: --- OpenCodeHub is a pnpm workspace under `packages/*`. Seventeen -TypeScript packages plus the documentation site. The CLI is the only -binary; every other package is a library imported by `cli`, `mcp`, -`ingestion`, or `analysis`. +TypeScript library packages plus the documentation site (eighteen +package directories in all). The CLI is the only binary; every other +package is a library imported by `cli`, `mcp`, `ingestion`, or +`analysis`. ## All packages | Package | Folder | Purpose | |---|---|---| -| `@opencodehub/analysis` | `packages/analysis` | `impact`, `rename`, `detect_changes`, staleness, group cross-repo links. | +| `@opencodehub/analysis` | `packages/analysis` | `impact`, `detect_changes`, staleness, group cross-repo links. | | `@opencodehub/cli` | `packages/cli` | The `codehub` binary (analyze, setup, mcp, query, context, impact, sql, group, scan, verdict, code-pack, ...). | | `@opencodehub/cobol-proleap` | `packages/cobol-proleap` | Optional JVM ProLeap deep-parse bridge for COBOL — gated behind `--allow-build-scripts=proleap`. | | `@opencodehub/core-types` | `packages/core-types` | Shared graph schema, `LanguageId`, `RelationType`, determinism primitives. | | `@opencodehub/embedder` | `packages/embedder` | Deterministic ONNX embedder (`gte-modernbert-base`), modelId fingerprint, three-backend cascade. | | `@opencodehub/frameworks` | `packages/frameworks` | Five-stage framework detector (manifest → lockfile → config-AST → folder → import/SCIP) over a curated registry. | | `@opencodehub/ingestion` | `packages/ingestion` | The indexing pipeline (parse, resolve, scip-index, embeddings, communities, processes, summaries, ...). | -| `@opencodehub/mcp` | `packages/mcp` | The stdio MCP server, 29 tool registrations, 7 resources, the error envelope, the staleness `_meta` block. | +| `@opencodehub/mcp` | `packages/mcp` | The stdio MCP server, 28 tool registrations (all read-only with respect to user source), 7 resources, the error envelope, the staleness `_meta` block. | | `@opencodehub/pack` | `packages/pack` | Deterministic 9-item code-pack BOM (the artifact attached to every release). | | `@opencodehub/policy` | `packages/policy` | `opencodehub.policy.yaml` loader, validator, evaluator. | | `@opencodehub/sarif` | `packages/sarif` | SARIF 2.1.0 Zod schemas, merge + enrich, suppressions, baseline diffing. | -| `@opencodehub/scanners` | `packages/scanners` | Nineteen scanner wrappers (semgrep, osv-scanner, bandit, ruff, grype, vulture, pip-audit, npm-audit, biome, betterleaks, trivy, checkov, hadolint, tflint, spectral, radon, ty, clamav, och self-scan). | +| `@opencodehub/scanners` | `packages/scanners` | Nineteen scanner wrappers (semgrep, betterleaks, osv-scanner, bandit, biome, pip-audit, npm-audit, trivy, checkov, checkov-docker-compose, hadolint, tflint, spectral, ruff, grype, vulture, radon, ty, clamav). | | `@opencodehub/scip-ingest` | `packages/scip-ingest` | `.scip` protobuf reader + per-language indexer runners (TypeScript, Python, Go, Rust, Java, .NET, clang, Kotlin, Ruby). | | `@opencodehub/search` | `packages/search` | Hybrid BM25 + RRF search. | | `@opencodehub/storage` | `packages/storage` | The `IGraphStore` / `ITemporalStore` interface segregation, the LadybugDB graph adapter and DuckDB temporal adapter, and `openStore()` that composes them. | diff --git a/packages/docs/src/content/docs/architecture/overview.md b/packages/docs/src/content/docs/architecture/overview.md index c0c2a27a..43deb688 100644 --- a/packages/docs/src/content/docs/architecture/overview.md +++ b/packages/docs/src/content/docs/architecture/overview.md @@ -28,7 +28,7 @@ indexers (TypeScript, Python, Go, Rust, Java, C#, C/C++, Kotlin, Ruby) upgrade heuristic edges to compiler-grade references where available. The graph persists into LadybugDB, with DuckDB carrying the temporal sibling. Communities and -processes are precomputed. An stdio MCP server with 29 tools answers +processes are precomputed. An stdio MCP server with 28 tools answers agent queries. ## Where the data lives diff --git a/packages/docs/src/content/docs/guides/using-with-claude-code.md b/packages/docs/src/content/docs/guides/using-with-claude-code.md index de7b8457..5cd6cb93 100644 --- a/packages/docs/src/content/docs/guides/using-with-claude-code.md +++ b/packages/docs/src/content/docs/guides/using-with-claude-code.md @@ -6,7 +6,7 @@ sidebar: --- There are two ways to connect OpenCodeHub to Claude Code. The **plugin** path -adds a PreToolUse hook that auto-augments rename-class edits with `impact` and +adds a PreToolUse hook that auto-augments refactor-class edits with `impact` and `detect_changes`. The **MCP-only** path wires the server without the hook. ## Plugin (preferred) @@ -17,7 +17,7 @@ codehub setup --plugin `--plugin` installs the OpenCodeHub plugin into Claude Code. The plugin registers a PreToolUse hook that runs before any edit that looks like a -rename or a cross-file refactor. The hook calls `impact` and +cross-file refactor. The hook calls `impact` and `detect_changes`, then feeds the results back to Claude Code as inline context so the agent can adjust its plan before writing a diff. @@ -93,7 +93,7 @@ entries in `.mcp.json` are preserved. ## Next -- [MCP tools](/opencodehub/mcp/tools/) — the full catalogue of 29 tools +- [MCP tools](/opencodehub/mcp/tools/) — the full catalogue of 28 tools Claude Code will see. - [MCP overview](/opencodehub/mcp/overview/) — server name, transport, envelope conventions. diff --git a/packages/docs/src/content/docs/guides/using-with-codex.md b/packages/docs/src/content/docs/guides/using-with-codex.md index 71bdc50c..a4e2d377 100644 --- a/packages/docs/src/content/docs/guides/using-with-codex.md +++ b/packages/docs/src/content/docs/guides/using-with-codex.md @@ -65,5 +65,5 @@ MCP servers are left alone. ## Next -- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 29 tools +- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 28 tools Codex will see. diff --git a/packages/docs/src/content/docs/guides/using-with-opencode.md b/packages/docs/src/content/docs/guides/using-with-opencode.md index 9a7545e7..c1dee36f 100644 --- a/packages/docs/src/content/docs/guides/using-with-opencode.md +++ b/packages/docs/src/content/docs/guides/using-with-opencode.md @@ -76,5 +76,5 @@ MCP servers configured there are left alone. ## Next -- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 29 tools +- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 28 tools OpenCode will see. diff --git a/packages/docs/src/content/docs/guides/using-with-windsurf.md b/packages/docs/src/content/docs/guides/using-with-windsurf.md index 0fa68ddc..34bcb87e 100644 --- a/packages/docs/src/content/docs/guides/using-with-windsurf.md +++ b/packages/docs/src/content/docs/guides/using-with-windsurf.md @@ -76,5 +76,5 @@ are left alone. ## Next -- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 29 tools +- [MCP tools](/opencodehub/mcp/tools/) — the catalogue of 28 tools Windsurf will see. diff --git a/packages/docs/src/content/docs/index.mdx b/packages/docs/src/content/docs/index.mdx index 0bcd3b6f..6efec178 100644 --- a/packages/docs/src/content/docs/index.mdx +++ b/packages/docs/src/content/docs/index.mdx @@ -1,6 +1,6 @@ --- title: OpenCodeHub -description: Apache-2.0 code intelligence graph + MCP server for AI coding agents — 29 tools, 15 GA languages, deterministic, offline-capable. +description: Apache-2.0 code intelligence graph + MCP server for AI coding agents — 28 tools, 15 GA languages, deterministic, offline-capable. template: splash hero: tagline: Graph-aware impact, context, and query for an AI coding agent — local, deterministic, Apache-2.0. @@ -51,7 +51,7 @@ import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components"; diff --git a/packages/docs/src/content/docs/mcp/overview.md b/packages/docs/src/content/docs/mcp/overview.md index 7f2aac40..b1f8a092 100644 --- a/packages/docs/src/content/docs/mcp/overview.md +++ b/packages/docs/src/content/docs/mcp/overview.md @@ -16,7 +16,9 @@ can connect to over stdio. - **Capabilities:** `tools` and `resources`. The server does not advertise `prompts` — the canned-prompts surface lives as Claude Code skills shipped by `plugins/opencodehub/` instead. -- **Tool count:** 29 (registered in `packages/mcp/src/server.ts`) +- **Tool count:** 28 (registered in `packages/mcp/src/server.ts`). Every + tool is read-only with respect to user source — no tool edits the + working tree. Clients spawn the `codehub mcp` process and exchange JSON-RPC frames over its stdio pipes. Signals map to clean exits: `SIGINT` → 130, @@ -37,14 +39,14 @@ editor's native MCP config location. ## The four tool families -The 29 tools fall into four functional clusters plus a meta cluster. +The 28 tools fall into four functional clusters plus a meta cluster. The full per-tool catalog is in [MCP tools](/opencodehub/mcp/tools/). | Family | Tools | Count | |---|---|---| -| Exploration | `list_repos`, `query`, `context`, `impact`, `detect_changes`, `rename`, `sql` | 7 | +| Exploration | `list_repos`, `query`, `context`, `impact`, `detect_changes`, `sql` | 6 | | Group / federation | `group_list`, `group_query`, `group_status`, `group_contracts`, `group_cross_repo_links`, `group_sync` | 6 | -| Scan / findings / verdict | `scan`, `list_findings`, `list_findings_delta`, `list_dead_code`, `remove_dead_code`, `license_audit`, `verdict`, `risk_trends` | 8 | +| Scan / findings / verdict | `scan`, `list_findings`, `list_findings_delta`, `list_dead_code`, `license_audit`, `verdict`, `risk_trends` | 7 | | HTTP / routing | `route_map`, `api_impact`, `shape_check`, `tool_map` | 4 | | Meta | `project_profile`, `dependencies`, `owners`, `pack_codebase` | 4 | @@ -89,8 +91,9 @@ Error responses instead carry `isError: true`, ## What the server exposes -- **29 tools** — exploration, federation, scan/findings, HTTP routing, - and metadata. See [tools](/opencodehub/mcp/tools/). +- **28 tools** — exploration, federation, scan/findings, HTTP routing, + and metadata. All read-only with respect to user source. See + [tools](/opencodehub/mcp/tools/). - **7 resources** — structured views over repos, clusters, and processes. See [resources](/opencodehub/mcp/resources/). - **0 prompts** — the v1 surface is intentionally empty. Pre-baked diff --git a/packages/docs/src/content/docs/mcp/prompts.md b/packages/docs/src/content/docs/mcp/prompts.md index 2d33492b..ecffcc93 100644 --- a/packages/docs/src/content/docs/mcp/prompts.md +++ b/packages/docs/src/content/docs/mcp/prompts.md @@ -34,7 +34,7 @@ covers the playbook surface: | Impact / blast-radius analysis | `opencodehub-impact-analysis` skill + `verdict` MCP tool | | PR review | `opencodehub-pr-review` skill + `codehub-pr-description` skill | | Codebase exploration | `opencodehub-exploring` skill + `codehub-onboarding` skill | -| Dependency audit | `audit-deps` slash command + `license_audit` MCP tool | +| Dependency audit | `dependencies` MCP tool + `license_audit` MCP tool | | Route / tool map generation | `codehub-document` skill + `route_map` / `tool_map` MCP tools | Each skill is richer than a static template because it inspects graph diff --git a/packages/docs/src/content/docs/mcp/resources.md b/packages/docs/src/content/docs/mcp/resources.md index 7d73f80a..13632bdd 100644 --- a/packages/docs/src/content/docs/mcp/resources.md +++ b/packages/docs/src/content/docs/mcp/resources.md @@ -1,6 +1,6 @@ --- title: MCP resources -description: The seven MCP resources the opencodehub server publishes alongside its 29 tools. +description: The seven MCP resources the opencodehub server publishes alongside its 28 tools. sidebar: order: 30 --- diff --git a/packages/docs/src/content/docs/mcp/tools.md b/packages/docs/src/content/docs/mcp/tools.md index b03448ce..20110d72 100644 --- a/packages/docs/src/content/docs/mcp/tools.md +++ b/packages/docs/src/content/docs/mcp/tools.md @@ -1,13 +1,14 @@ --- title: MCP tools -description: All 29 MCP tools the opencodehub server registers, grouped by functional family. +description: All 28 MCP tools the opencodehub server registers, grouped by functional family. Every tool is read-only with respect to user source. sidebar: order: 20 --- -The `opencodehub` MCP server registers **29 tools**, imported and +The `opencodehub` MCP server registers **28 tools**, imported and invoked from `packages/mcp/src/server.ts`. The number is taken live -from `buildServer()` at startup. +from `buildServer()` at startup. Every tool is **read-only with respect +to user source** — no tool edits the working tree. Every per-repo tool accepts an optional `repo` argument (registry name) or `repo_uri` alias (Sourcegraph-style URI). See @@ -68,23 +69,23 @@ The high-frequency tools. Most agent loops live here. | **Inputs** | `repo?`, `repo_uri?`, `scope?: "unstaged" \| "staged" \| "all" \| "compare"` (default `all`), `compare_ref?`, `strict?` | | **Returns** | `{ symbols, files, processes, max_risk, next_steps }` | -### `rename` +### `sql` | | | |---|---| -| **Use when** | Coordinating a multi-file symbol rename. Default mode is dry-run. | -| **Avoid when** | The rename is in a single file — let the editor handle it. | -| **Inputs** | `from`, `to`, `repo?`, `repo_uri?`, `dry_run?` (default `true`) | -| **Returns** | `{ edits: [{ file_path, line, before, after, confidence }], cross_module_refs, next_steps }` | +| **Use when** | You need a custom view of the temporal store (`cochanges` + `symbol_summaries`) that no other tool exposes. Read-only. 5-second timeout. | +| **Avoid when** | A typed tool (`context`, `impact`, `query`) already covers the question, or you need the node/edge graph — that lives in `graph.lbug` and is reached via the typed tools or Cypher (ADR 0016), not this SQL path. | +| **Inputs** | `query` (required), `repo?`, `repo_uri?` | +| **Returns** | `{ rows: [...], row_count, next_steps }` | -### `sql` +### `signature` | | | |---|---| -| **Use when** | You need a custom view of the graph that no other tool exposes. Read-only. 5-second timeout. | -| **Avoid when** | A typed tool (`context`, `impact`, `query`) already covers the question. SQL is the escape hatch. | -| **Inputs** | `query` (required), `repo?`, `repo_uri?` | -| **Returns** | `{ rows: [...], row_count, next_steps }` | +| **Use when** | You want a symbol's shape without reading the whole file — a class/interface declaration plus its method and property signatures with bodies elided (stub syntax per language). For a standalone function, returns a single signature. | +| **Avoid when** | You need callers/callees — call `context` instead. | +| **Inputs** | `name?`, `uid?`, `file_path?`, `kind?`, `repo?`, `repo_uri?` (one of `name` or `uid` is required) | +| **Returns** | `{ target, language, member_count, members, stub, next_steps }` | ## Group / federation (6) @@ -140,7 +141,7 @@ form so a follow-up `AMBIGUOUS_REPO` retry can use it as input. | **Inputs** | `group` | | **Returns** | `{ group, contracts_written, cross_links_written, next_steps }` | -## Scan / findings / verdict (8) +## Scan / findings / verdict (7) `scan` is the only tool that spawns processes (`openWorldHint=true`). `verdict` exits 0/1/2/3 by tier — the canonical source of CI signal. @@ -177,14 +178,6 @@ form so a follow-up `AMBIGUOUS_REPO` retry can use it as input. | **Inputs** | `repo?`, `repo_uri?` | | **Returns** | `{ candidates: [{ id, name, file_path, kind, reason }] }` | -### `remove_dead_code` - -| | | -|---|---| -| **Use when** | Remove specific dead symbols from disk. Dry-run is the default. | -| **Inputs** | `repo?`, `repo_uri?`, `targets: string[]`, `dry_run?` | -| **Returns** | `{ removed, skipped, next_steps }` | - ### `license_audit` | | | diff --git a/packages/docs/src/content/docs/skills/codehub-code-pack.mdx b/packages/docs/src/content/docs/skills/codehub-code-pack.mdx new file mode 100644 index 00000000..73ee3809 --- /dev/null +++ b/packages/docs/src/content/docs/skills/codehub-code-pack.mdx @@ -0,0 +1,82 @@ +--- +title: "codehub-code-pack" +description: "Deterministic 9-item code-pack BOM for a repo or group — byte-identical given the same (commit, tokenizer, budget)." +--- + +import { Aside } from "@astrojs/starlight/components"; + +Standalone skill. Surfaces the `pack_codebase` MCP tool to produce a +**deterministic, 9-item Bill of Materials (BOM)** at +`/.codehub/packs//` that is byte-identical given the same +`(commit, tokenizer, budget, chonkie_version, duckdb_version, +grammar_commits)`. The pack is the durable artifact agents hand to a +long-context LLM, archive for later replay, or diff between commits to +prove invariants did not change. + + + +## Frontmatter + +```yaml +name: codehub-code-pack +argument-hint: "[] [--budget ] [--tokenizer ]" +allowed-tools: pack_codebase, list_repos, project_profile, list_findings +model: sonnet +``` + +## Single-repo process + +1. `mcp__opencodehub__list_repos` — confirm the repo is indexed. If two or + more repos are indexed and no `repo` argument was supplied, retry with a + `structuredContent.error.choices[].repo_uri` value from the + `AMBIGUOUS_REPO` envelope. +2. `mcp__opencodehub__project_profile` — confirm graph freshness; surface + any `_meta.codehub/staleness` envelope so the user knows the pack + reflects the last `codehub analyze`, not HEAD. +3. `mcp__opencodehub__list_findings` — optional, only when findings are + requested in the pack. +4. `mcp__opencodehub__pack_codebase` with the default `engine: "pack"`. + The tool resolves the output to `/.codehub/packs//` + and writes the 9 items plus `manifest.json`. +5. Report back the `packHash`, the `determinismClass`, and the absolute + output directory; name the cause when the class is `best_effort` or + `degraded`. + +## Group mode + +Run the single-repo flow per member of the named group, then emit a table +of `(repoUri, packHash, determinismClass, outDir)`. `packHash` is per-repo, +not per-group — a group pack is the union of the member BOMs. + +## The 9-item BOM + +| # | File | Determinism contract | +|---|------|----------------------| +| 1 | `manifest.json` | RFC 8785 canonical JSON; pack-hash field omitted from the preimage; CRLF normalized to LF before hashing | +| 2 | `skeleton.jsonl` | PageRank score DESC, then `id` ASC | +| 3 | `file-tree.jsonl` | `path` ASC | +| 4 | `deps.jsonl` | `(ecosystem, name, version, id)` lexicographic ASC | +| 5 | `ast-chunks.jsonl` | LF-normalized; degrades to line-split with `determinismClass: degraded` | +| 6 | `xrefs.jsonl` | community rows first, then call rows | +| 7 | `embeddings.parquet` | OPTIONAL — absent entirely when no embeddings exist | +| 8 | `findings.jsonl` | severity rank then `ruleId` ASC | +| 9 | `licenses.md` + `readme.md` | alpha-sorted dependency list + manifest-derived header | + +## Determinism class + +| Class | Meaning | +|-------|---------| +| `strict` | Same inputs → same `packHash`; the full reproducibility contract holds. | +| `best_effort` | The tokenizer is an Anthropic API tokenizer that may rotate behind the model name; other inputs stay pinned. | +| `degraded` | A primitive fallback was used (e.g. line-split chunker). Re-runs match locally but not across machines. | + +## Related + +- [pack package overview](/opencodehub/architecture/monorepo-map/) +- [MCP tools — `pack_codebase`](/opencodehub/mcp/tools/) +- [Skills index](/opencodehub/skills/) diff --git a/packages/docs/src/content/docs/skills/index.mdx b/packages/docs/src/content/docs/skills/index.mdx index 5859b5fd..ac1d30f3 100644 --- a/packages/docs/src/content/docs/skills/index.mdx +++ b/packages/docs/src/content/docs/skills/index.mdx @@ -36,6 +36,11 @@ auto-discovered by Claude Code when the plugin is installed. description="Group-only. Consumer/producer contract matrix across a repo group, with Mermaid flow." href="/opencodehub/skills/codehub-contract-map/" /> + ## How they compose @@ -46,15 +51,18 @@ flowchart LR pr[codehub-pr-description
PR body] --> verdict onb[codehub-onboarding
ONBOARDING.md] --> codegraph map[codehub-contract-map
contracts.md] --> groupcontracts[group_contracts] + pack[codehub-code-pack
9-item BOM] --> packtool[pack_codebase] verdict[verdict + impact + owners] --> codegraph[(code graph)] groupcontracts --> codegraph + packtool --> codegraph docmeta --> refresh[--refresh
staleness hook] ``` -The four skills share the OpenCodeHub MCP toolkit. `codehub-document` is -the only skill that dispatches subagents; the others are linear. All four -write to `.codehub/` by default (gitignored); `--committed` opts in to a -git-tracked path. +The five skills share the OpenCodeHub MCP toolkit. `codehub-document` is +the only skill that dispatches subagents; the others are linear. The four +documentation skills write to `.codehub/` by default (gitignored), with +`--committed` opting in to a git-tracked path; `codehub-code-pack` writes +a deterministic code-pack BOM via `pack_codebase`. ## Invocation cheatsheet @@ -66,6 +74,7 @@ git-tracked path. | "draft release notes for HEAD" | `codehub-pr-description` | | "write ONBOARDING.md" / "what should a new hire read first" | `codehub-onboarding` | | "map the contracts" / "show the contract matrix for ``" | `codehub-contract-map ` | +| "pack this repo for an LLM" / "deterministic code pack" | `codehub-code-pack` | ## Discoverability hooks diff --git a/packages/docs/src/content/docs/start-here/codehub-init.md b/packages/docs/src/content/docs/start-here/codehub-init.md index 2b587048..eecb7854 100644 --- a/packages/docs/src/content/docs/start-here/codehub-init.md +++ b/packages/docs/src/content/docs/start-here/codehub-init.md @@ -33,12 +33,6 @@ Next: run 'codehub analyze' to build the graph, then restart Claude Code. .claude/ ├── agents/ │ └── code-analyst.md # graph-grounded analysis subagent -├── commands/ # 5 slash commands -│ ├── probe.md -│ ├── verdict.md -│ ├── owners.md -│ ├── audit-deps.md -│ └── rename.md ├── hooks/ # PostToolUse / Stop scripts │ ├── augment.sh # enriches Bash/Grep/Glob with graph context │ └── docs-staleness.sh # non-blocking "/codehub-document --refresh" hint diff --git a/packages/docs/src/content/docs/start-here/first-query.md b/packages/docs/src/content/docs/start-here/first-query.md index 703cc482..9a548bce 100644 --- a/packages/docs/src/content/docs/start-here/first-query.md +++ b/packages/docs/src/content/docs/start-here/first-query.md @@ -99,7 +99,7 @@ down` restricts to dependencies (who do I call), and `--direction both` ## Next - [MCP tools overview](/opencodehub/mcp/overview/) for the full server - capabilities (29 tools across exploration, federation, scan, HTTP, + capabilities (28 tools across exploration, federation, scan, HTTP, and meta). - [Using with Claude Code](/opencodehub/guides/using-with-claude-code/) to let the agent run these tools for you. diff --git a/packages/docs/src/content/docs/start-here/install.md b/packages/docs/src/content/docs/start-here/install.md index bea7d78a..8e2929f6 100644 --- a/packages/docs/src/content/docs/start-here/install.md +++ b/packages/docs/src/content/docs/start-here/install.md @@ -33,7 +33,7 @@ pnpm -r build mise run cli:link # puts `codehub` on your PATH ``` -`mise install` activates the Node 22, pnpm 10.33.2, and Python 3.12 pins +`mise install` activates the Node 22, pnpm 11.1.0, and Python 3.12 pins from `mise.toml`. `pnpm install --frozen-lockfile` installs exactly the lockfile-pinned dependencies. `pnpm -r build` compiles every TypeScript package so the CLI entrypoint at `packages/cli/dist/index.js` is @@ -55,8 +55,8 @@ If you already manage Node and pnpm another way: from [nodejs.org](https://nodejs.org)). Every supported version uses the same `web-tree-sitter` (WASM) parse runtime — there is no native parser and no opt-in (ADR 0015). -2. Install pnpm `>=10.0.0` (`corepack enable pnpm`, or `npm install -g - pnpm@10`). +2. Install pnpm `>=11.0.0` (`corepack enable pnpm`, or `npm install -g + pnpm@11`). 3. Clone, build, and link: ```bash diff --git a/packages/docs/src/content/docs/start-here/quick-start.md b/packages/docs/src/content/docs/start-here/quick-start.md index eeae2e6f..5cd0d02d 100644 --- a/packages/docs/src/content/docs/start-here/quick-start.md +++ b/packages/docs/src/content/docs/start-here/quick-start.md @@ -117,7 +117,7 @@ codehub impact validateUser --depth 2 - [Your first query](/opencodehub/start-here/first-query/) walks through `query`, `context`, and `impact` with sample output. -- [MCP tools](/opencodehub/mcp/tools/) lists all 29 tools the server +- [MCP tools](/opencodehub/mcp/tools/) lists all 28 tools the server exposes. - [Using with Claude Code](/opencodehub/guides/using-with-claude-code/) covers the plugin path (PreToolUse hooks) and the MCP-only path. diff --git a/packages/docs/src/content/docs/start-here/what-is-opencodehub.md b/packages/docs/src/content/docs/start-here/what-is-opencodehub.md index 7a38a0ac..b899de8b 100644 --- a/packages/docs/src/content/docs/start-here/what-is-opencodehub.md +++ b/packages/docs/src/content/docs/start-here/what-is-opencodehub.md @@ -45,7 +45,7 @@ flowchart LR C -->|detect communities and flows| E[Processes and clusters] D --> F[MCP server] E --> F - F -->|29 tools| G[AI coding agent] + F -->|28 tools| G[AI coding agent] ``` Clustering, execution-flow tracing, and blast-radius analysis all happen diff --git a/packages/eval/README.md b/packages/eval/README.md new file mode 100644 index 00000000..f31fa034 --- /dev/null +++ b/packages/eval/README.md @@ -0,0 +1,7 @@ +# @opencodehub/eval — extracted + +The Python retrieval / graph-quality evaluation harness that used to live +here was extracted to the sibling `opencodehub-testbed` repository so the +production package set ships free of test-time dependencies. Any local +`.venv/`, `.pytest_cache/`, `.ruff_cache/`, or `src/` left in this folder +is untracked and gitignored — see `opencodehub-testbed` for the harness. diff --git a/packages/frameworks/README.md b/packages/frameworks/README.md index 335c869f..15e2b5fd 100644 --- a/packages/frameworks/README.md +++ b/packages/frameworks/README.md @@ -1,34 +1,74 @@ # @opencodehub/frameworks -5-stage framework detection for OpenCodeHub. Identifies the languages, -runtimes, and frameworks present in a repository without executing any -project code. +Framework detection for OpenCodeHub. Identifies the languages, runtimes, and +frameworks present in a repository without executing any project code, network +access, LLM, or subprocess — pure local file-system plus string/regex +inspection. ## Surface ```ts -import { detectFrameworks } from "@opencodehub/frameworks"; +import { + detectFrameworks, + detectFrameworksDetailed, +} from "@opencodehub/frameworks"; -const result = await detectFrameworks("/path/to/repo"); -// result.detected: FrameworkHit[] — name, confidence, evidence stage +// Both take a FrameworkDetectionInput object — never a bare path string. +const input = { + repoRoot: "/path/to/repo", + files: scannedFiles, // readonly { relPath: string }[] + manifests: ["package.json"], // manifest relPaths present in the repo + detectedLanguages: ["typescript"], // optional — gates ecosystems +}; + +// Flat list (legacy v1.0 surface): sorted framework names. +const names: readonly string[] = await detectFrameworks(input); + +// Structured (preferred): FrameworkDetection[] with variant / version / +// confidence / evidence / parentName. +const detections = await detectFrameworksDetailed(input); +// detections[i]: { name, category, confidence, evidence[], variant?, version?, parentName? } ``` -Detection runs five stages in order, stopping as soon as a stage produces -high-confidence hits: +`FrameworkDetection` and `Evidence` are re-exported from +`@opencodehub/core-types`; the catalog-facing types +(`FrameworkRule`, `ManifestKey`, `FrameworkEcosystem`, `FrameworkTier`, +`VariantDefinition`) come from this package. + +## Detection stages + +The dispatcher (`src/detector.ts`) walks the curated catalog once and merges +evidence from three stages into each `FrameworkDetection`: | Stage | Method | |---|---| -| 1. Manifest | `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, … | -| 2. Lockfile | `pnpm-lock.yaml`, `poetry.lock`, `Cargo.lock`, … | -| 3. Config AST | `next.config.*`, `vite.config.*`, `astro.config.*`, … | -| 4. Folder | `src/app/`, `pages/`, `components/`, … | -| 5. Import / SCIP | Symbol-level import analysis from the code graph | +| 1. Manifest | `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `pom.xml`, … — declared-dependency and manifest-presence fingerprints | +| 2. Lockfile version | `package-lock.json`, `pnpm-lock.yaml`, `poetry.lock`, `uv.lock`, `Cargo.lock`, … — pins the exact version, overriding the manifest's semver range | +| 4. Folder / file marker | exact-path file markers (`vite.config.ts`, …) and regex path markers (`src/app/`, `pages/`, …) | + +Two further stages ship as standalone, independently tested modules but are +not yet wired into the ingestion profile phase: + +- **Stage 3 — config AST** (`src/stages/config-ast.ts`): regex-pragmatic + inspectors for `next.config.*`, `astro.config.*`, `vite.config.*`, and + `META-INF/spring.factories`. Wiring it requires the caller to pre-read the + config-file text and pass it through. +- **Stage 5 — import / SCIP** (`src/stages/imports.ts`): walks the code + graph's `IMPORTS` edges and maps resolved root modules (`fastapi`, + `@nestjs/core`, …) to frameworks. Wiring it requires passing the + `KnowledgeGraph` through. + +Wiring these two stages would feed their findings into +`FrameworkDetection.evidence` (the `Evidence.stage` field already reserves +`3` and `5`). ## Design -- The curated framework registry lives at - `packages/frameworks/src/registry/` — add an entry there to support a - new framework. +- The curated framework catalog lives at `src/catalog.ts` — a typed + `FRAMEWORK_CATALOG` table of `FrameworkRule` entries. Add an entry there + (and, if variants matter, a resolver in `src/variant-detectors.ts`) to + support a new framework. - All file reads are bounded; the detector never shells out. -- `zod` validates registry entries at module load time so a malformed - entry fails fast rather than silently producing wrong results. +- Determinism: every entrypoint returns output sorted by name, and structured + evidence is sorted by `(stage, source, detail)` for byte-stable graphs. +- Unreadable or malformed manifests/lockfiles are skipped, never fatal. diff --git a/packages/frameworks/package.json b/packages/frameworks/package.json index 05d1c81a..1fbe32b9 100644 --- a/packages/frameworks/package.json +++ b/packages/frameworks/package.json @@ -1,7 +1,7 @@ { "name": "@opencodehub/frameworks", "version": "0.1.1", - "description": "OpenCodeHub — 5-stage framework detection (manifest → lockfile → config-AST → folder → import/SCIP) over a curated registry", + "description": "OpenCodeHub — framework detection (manifest → lockfile version → folder/file marker) over a curated catalog", "license": "Apache-2.0", "repository": { "type": "git", @@ -39,8 +39,7 @@ "dependencies": { "@iarna/toml": "2.2.5", "@opencodehub/core-types": "workspace:*", - "yaml": "2.9.0", - "zod": "4.4.3" + "yaml": "2.9.0" }, "devDependencies": { "@types/node": "25.9.1", diff --git a/packages/frameworks/src/detector.test.ts b/packages/frameworks/src/detector.test.ts index 9fb5138e..bbbebcce 100644 --- a/packages/frameworks/src/detector.test.ts +++ b/packages/frameworks/src/detector.test.ts @@ -761,3 +761,41 @@ describe("framework detection — stage 2 lockfile version override", () => { assert.equal(react?.version, "^18.0.0", "manifest range used when lockfile silent"); }); }); + +describe("framework detection — version resolves from either dependency bucket", () => { + it("vite version resolves when declared under dependencies (not devDependencies)", () => { + const input = mkInput( + ["package.json", "vite.config.ts"], + [["package.json", JSON.stringify({ dependencies: { vite: "5.0.0" } })]], + ["typescript"], + ); + const out = detectFrameworksStructured(input); + const vite = findByName(out, "vite"); + assert.ok(vite, "vite detected"); + assert.equal(vite?.version, "5.0.0", "version read from dependencies bucket"); + }); + + it("electron version resolves when declared under dependencies (not devDependencies)", () => { + const input = mkInput( + ["package.json"], + [["package.json", JSON.stringify({ dependencies: { electron: "28.0.0" } })]], + ["javascript"], + ); + const out = detectFrameworksStructured(input); + const electron = findByName(out, "electron"); + assert.ok(electron, "electron detected"); + assert.equal(electron?.version, "28.0.0", "version read from dependencies bucket"); + }); + + it("vite version still resolves from devDependencies (the declared bucket)", () => { + const input = mkInput( + ["package.json", "vite.config.ts"], + [["package.json", JSON.stringify({ devDependencies: { vite: "5.4.0" } })]], + ["typescript"], + ); + const out = detectFrameworksStructured(input); + const vite = findByName(out, "vite"); + assert.ok(vite, "vite detected"); + assert.equal(vite?.version, "5.4.0", "version read from devDependencies bucket"); + }); +}); diff --git a/packages/frameworks/src/detector.ts b/packages/frameworks/src/detector.ts index 69168d4b..46ce21fd 100644 --- a/packages/frameworks/src/detector.ts +++ b/packages/frameworks/src/detector.ts @@ -268,9 +268,33 @@ function resolveVersion( // Fallback to the manifest-declared range. const parsed = manifestJson.get(rule.versionKey.file); if (parsed === undefined || parsed === null) return undefined; - const v = getPath(parsed, rule.versionKey.path); - if (typeof v !== "string") return undefined; - return v; + // Try the declared versionKey.path first, then the sibling dependency + // bucket. Many rules declare `manifestKeys` for BOTH `dependencies.` + // and `devDependencies.` (e.g. vite, electron, jest, vitest, + // playwright) but pin `versionKey` to a single bucket. Without this + // fallback a project that declares the dep in the *other* bucket is + // detected but reports `version: undefined`. We probe both common + // buckets so the version is resolved regardless of which one carries it. + for (const candidate of versionKeyCandidates(rule.versionKey.path)) { + const v = getPath(parsed, candidate); + if (typeof v === "string") return v; + } + return undefined; +} + +/** + * Yield the declared version path plus its sibling dependency bucket. A + * path of `dependencies.vite` also tries `devDependencies.vite` and vice + * versa; paths that don't name a dependency bucket are returned as-is. + */ +function versionKeyCandidates(path: string): readonly string[] { + if (path.startsWith("dependencies.")) { + return [path, `devDependencies.${path.slice("dependencies.".length)}`]; + } + if (path.startsWith("devDependencies.")) { + return [path, `dependencies.${path.slice("devDependencies.".length)}`]; + } + return [path]; } function lastPathSegment(path: string): string | null { diff --git a/packages/frameworks/src/index.ts b/packages/frameworks/src/index.ts index 1f7b98b1..44aa829f 100644 --- a/packages/frameworks/src/index.ts +++ b/packages/frameworks/src/index.ts @@ -1,17 +1,26 @@ /** - * `@opencodehub/frameworks` — 5-stage framework detection over a curated - * 23-entry registry. + * `@opencodehub/frameworks` — framework detection over a curated catalog. * - * Stages (each emits `{name, version?, confidence, evidence[]}`): - * 1. Manifest presence (`package.json`, `pyproject.toml`, `pom.xml`, …) - * 2. Lockfile + exact versions (`package-lock.json`, `pnpm-lock.yaml`, - * `Gemfile.lock`, `poetry.lock`, `uv.lock`, `Cargo.lock`) - * 3. Config AST (`next.config.*`, `astro.config.*`, `vite.config.*`, - * `spring.factories`) - * 4. Folder convention (`app/`, `pages/`, `src/main/java/`, …) - * 5. Import / SCIP usage patterns (consumes the graph's `IMPORTS` edges) + * The dispatcher (`detector.ts`) merges three stages into each + * `FrameworkDetection` (`{name, version?, confidence, evidence[]}`): + * 1. Manifest presence + declared deps (`package.json`, `pyproject.toml`, + * `pom.xml`, …) + * 2. Lockfile exact versions, overriding manifest semver ranges + * (`package-lock.json`, `pnpm-lock.yaml`, `Gemfile.lock`, + * `poetry.lock`, `uv.lock`, `Cargo.lock`) + * 4. Folder / file-marker convention (`app/`, `pages/`, `vite.config.ts`, + * `src/main/java/`, …) * - * All stages are pure-local file-system + string/regex inspection; no + * Two further stages ship as standalone, independently tested modules but + * are not yet wired into the ingestion profile phase (their findings do not + * reach `FrameworkDetection.evidence` until a caller passes the extra + * inputs through): + * 3. Config AST (`config-ast.ts`) — `next.config.*`, `astro.config.*`, + * `vite.config.*`, `spring.factories`; needs the config-file text. + * 5. Import / SCIP (`imports.ts`) — consumes the graph's `IMPORTS` edges; + * needs the `KnowledgeGraph`. + * + * Every stage is pure-local file-system + string/regex inspection; no * network, no LLM, no subprocess. */ diff --git a/packages/ingestion/src/parse/parse-worker.test.ts b/packages/ingestion/src/parse/parse-worker.test.ts new file mode 100644 index 00000000..b437ab18 --- /dev/null +++ b/packages/ingestion/src/parse/parse-worker.test.ts @@ -0,0 +1,102 @@ +import { strict as assert } from "node:assert"; +import { Buffer } from "node:buffer"; +import { describe, it } from "node:test"; +import type { LanguageId } from "@opencodehub/core-types"; +import { MAX_FILE_BYTES, parseOne } from "./parse-worker.js"; +import type { ParseCapture, ParseTask } from "./types.js"; + +function task(content: Buffer, language: LanguageId = "typescript"): ParseTask { + return { filePath: "src/sample.ts", content, language }; +} + +describe("parseOne — oversize skip", () => { + it("skips a file over the byte cap without invoking the parser", async () => { + let parserCalled = false; + const oversize = Buffer.alloc(MAX_FILE_BYTES + 1, 0x61); // 'a' + const result = await parseOne(task(oversize), async () => { + parserCalled = true; + return []; + }); + + assert.equal(parserCalled, false, "parser must not run on an oversize file"); + assert.deepEqual(result.captures, []); + assert.equal(result.byteLength, oversize.byteLength); + assert.ok(result.warnings !== undefined && result.warnings.length === 1); + assert.match(result.warnings[0] as string, /byte cap/); + assert.match(result.warnings[0] as string, /skipping parse/); + }); + + it("parses a file exactly at the byte cap", async () => { + const captures: readonly ParseCapture[] = [ + { + tag: "definition.function", + text: "atCap", + startLine: 1, + endLine: 1, + startCol: 0, + endCol: 5, + nodeType: "function_declaration", + }, + ]; + const atCap = Buffer.alloc(MAX_FILE_BYTES, 0x61); + const result = await parseOne(task(atCap), async () => captures); + + assert.deepEqual(result.captures, captures); + // A successful parse with no skip/error attaches no warnings array. + assert.equal(result.warnings, undefined); + }); +}); + +describe("parseOne — error to warning mapping", () => { + it("maps a synchronous parse throw to an empty-captures result + warning", async () => { + const result = await parseOne(task(Buffer.from("const x = 1;\n")), () => { + throw new Error("grammar exploded"); + }); + + assert.deepEqual(result.captures, [], "captures must be empty on parse failure"); + assert.ok(result.warnings !== undefined && result.warnings.length === 1); + assert.equal(result.warnings[0], "grammar exploded"); + assert.ok(result.parseTimeMs >= 0); + }); + + it("maps a rejected parse promise to a warning rather than crashing", async () => { + const result = await parseOne(task(Buffer.from("x")), async () => { + throw new Error("async parse failure"); + }); + + assert.deepEqual(result.captures, []); + assert.equal(result.warnings?.[0], "async parse failure"); + }); + + it("stringifies a non-Error throw into the warning", async () => { + const result = await parseOne(task(Buffer.from("x")), () => { + throw "plain string failure"; + }); + + assert.equal(result.warnings?.[0], "plain string failure"); + }); +}); + +describe("parseOne — happy path", () => { + it("returns the parser captures and records byte length", async () => { + const captures: readonly ParseCapture[] = [ + { + tag: "reference.call", + text: "doThing", + startLine: 2, + endLine: 2, + startCol: 4, + endCol: 11, + nodeType: "call_expression", + }, + ]; + const content = Buffer.from("function f() {\n doThing();\n}\n"); + const result = await parseOne(task(content), async () => captures); + + assert.deepEqual(result.captures, captures); + assert.equal(result.byteLength, content.byteLength); + assert.equal(result.language, "typescript"); + assert.equal(result.filePath, "src/sample.ts"); + assert.equal(result.warnings, undefined); + }); +}); diff --git a/packages/ingestion/src/parse/parse-worker.ts b/packages/ingestion/src/parse/parse-worker.ts index 41c99290..b69689fa 100644 --- a/packages/ingestion/src/parse/parse-worker.ts +++ b/packages/ingestion/src/parse/parse-worker.ts @@ -9,8 +9,16 @@ * 3. Parses each task's buffer and maps captures to {@link ParseCapture}. * 4. Returns a {@link ParseResult} per task. * - * Per-task wall-clock timeout: 30 seconds. On timeout the task returns a - * result with empty captures and a warning rather than crashing the worker. + * Per-task timeout: 30 seconds, enforced as a SOFT async-only bound. The + * timer is a `setTimeout`-backed rejection that races the parse promise, so + * it only fires for stalls that yield the event loop (a slow grammar load, + * an awaited I/O hop). It CANNOT interrupt the synchronous body of + * `WasmParserHandle.runQuery` — `parser.parse()` and `query.matches()` run to + * completion on the worker thread, blocking the same event loop the timer + * lives on. The real guard against a pathological in-thread hang is the + * pre-parse {@link MAX_FILE_BYTES} cap, which skips oversize inputs before + * any WASM call. On timeout (or any parse error) the task returns a result + * with empty captures and a warning rather than crashing the worker. * * `web-tree-sitter` is the sole runtime as of 0.4.0. Native `tree-sitter` * was removed from the runtime install graph; grammar `.wasm` blobs are @@ -24,7 +32,8 @@ import { getUnifiedQuery } from "./unified-queries.js"; import { openWasmParser, type WasmParserHandle } from "./wasm-runtime.js"; const PER_FILE_TIMEOUT_MS = 30_000; -const MAX_FILE_BYTES = 5 * 1024 * 1024; // 5 MB +/** Pre-parse byte cap. Inputs above this are skipped before any WASM call. */ +export const MAX_FILE_BYTES = 5 * 1024 * 1024; // 5 MB // Per-worker WASM parser cache. Each worker_thread has its own module // instance so this lives per-worker. @@ -41,7 +50,16 @@ export default async function parseBatch(batch: ParseBatch): Promise { +/** + * Parse one task. `parseFn` is the WASM parse step; it is injectable so the + * oversize-skip and error-to-warning branches can be exercised in a unit + * test without standing up a real grammar. Production always uses the + * default {@link runParse}. + */ +export async function parseOne( + task: ParseTask, + parseFn: (language: LanguageId, content: Buffer) => Promise = runParse, +): Promise { const start = performance.now(); const warnings: string[] = []; // piscina's structured-clone transport converts a Buffer into a plain @@ -70,7 +88,7 @@ async function parseOne(task: ParseTask): Promise { try { const captures = await withTimeout( - runParse(task.language, content), + parseFn(task.language, content), PER_FILE_TIMEOUT_MS, `parse timed out after ${PER_FILE_TIMEOUT_MS}ms`, ); @@ -133,7 +151,12 @@ async function runWasm(language: LanguageId, source: string): Promise(p: Promise, ms: number, message: string): Promise { return new Promise((resolve, reject) => { diff --git a/packages/ingestion/src/pipeline/incremental-determinism.test.ts b/packages/ingestion/src/pipeline/incremental-determinism.test.ts index 057e7ff9..138fa7ad 100644 --- a/packages/ingestion/src/pipeline/incremental-determinism.test.ts +++ b/packages/ingestion/src/pipeline/incremental-determinism.test.ts @@ -166,6 +166,66 @@ describe("incremental-determinism", () => { } }); + it("Test D: change one entry-point file (non-empty closure) → incremental hash equals full hash", async () => { + const repo = await mkdtemp(path.join(tmpdir(), "och-inc-det-d-")); + try { + await writeFixture(repo); + // Baseline full run at commit A → prior graph snapshot. + const fullA = await runIngestion(repo, { skipGit: true }); + const prior = previousGraphFromResult(fullA); + + // Semantic change to a hot file: rewrite entry.ts with a different + // multi-step flow. Nothing imports entry.ts, so the closure is just + // { entry.ts } — well under the 30% safety valve given 20+ pad files — + // yet the Process / PROCESS_STEP structure rooted in entry.ts changes. + // This is the case the old verbatim carry-forward got wrong: it would + // replay commit A's stale processes instead of re-BFS-ing the closure. + await fs.writeFile( + path.join(repo, "entry.ts"), + [ + "import { Child } from './child.js';", + "export function handleRequest(): string {", + " const c = new Child();", + " return alpha(c);", + "}", + "function alpha(c: Child): string {", + " return beta(c);", + "}", + "function beta(c: Child): string {", + " return gamma(c);", + "}", + "function gamma(c: Child): string {", + " return c.greet();", + "}", + "", + ].join("\n"), + ); + + // Full run at commit B (the changed tree) — the determinism oracle. + const fullB = await runIngestion(repo, { skipGit: true }); + // Incremental run at commit B carrying forward commit A's graph. + const incB = await runIngestion(repo, { skipGit: true, incrementalFrom: prior }); + + assert.equal(incB.incrementalScope?.mode, "incremental", "expected incremental mode"); + assert.ok( + (incB.incrementalScope?.closureFiles.length ?? 0) > 0, + "expected a non-empty closure after a content change", + ); + assert.notEqual( + fullB.graphHash, + fullA.graphHash, + "the content change must alter the full-run hash (sanity)", + ); + assert.equal( + incB.graphHash, + fullB.graphHash, + "incremental hash drifted from full hash under a non-empty closure", + ); + } finally { + await rm(repo, { recursive: true, force: true }); + } + }); + it("Test B: touch one file with no semantic change → incremental hash equals full hash", async () => { const repo = await mkdtemp(path.join(tmpdir(), "och-inc-det-b-")); try { diff --git a/packages/ingestion/src/pipeline/orchestrator.test.ts b/packages/ingestion/src/pipeline/orchestrator.test.ts index 368bb339..8431f61a 100644 --- a/packages/ingestion/src/pipeline/orchestrator.test.ts +++ b/packages/ingestion/src/pipeline/orchestrator.test.ts @@ -5,6 +5,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { after, before, describe, it } from "node:test"; import { runIngestion } from "./orchestrator.js"; +import type { PipelineContext, PipelineOptions, PipelinePhase, PreviousGraph } from "./types.js"; describe("runIngestion (end-to-end)", () => { let repo: string; @@ -46,6 +47,9 @@ describe("runIngestion (end-to-end)", () => { "repo-node", "sbom", "structure", + // `coverage` becomes runnable once `structure` completes; the + // alphabetic tiebreak lands it before `markdown`. + "coverage", "markdown", "parse", "complexity", @@ -82,6 +86,153 @@ describe("runIngestion (end-to-end)", () => { }); }); +describe("runIngestion option normalization", () => { + let repo: string; + + before(async () => { + repo = await mkdtemp(path.join(tmpdir(), "och-opts-")); + await fs.writeFile(path.join(repo, "README.md"), "# opts\n"); + }); + + after(async () => { + await rm(repo, { recursive: true, force: true }); + }); + + it("threads every PipelineOptions field through to ctx.options", async () => { + // A probe phase captures the normalized per-phase options bag. Running it + // as the sole phase keeps the assertion fast and isolated from the real + // DAG while still exercising the orchestrator's option normalization. + let seen: PipelineOptions | undefined; + const probe: PipelinePhase = { + name: "probe", + deps: [], + run(ctx: PipelineContext): Promise { + seen = ctx.options; + return Promise.resolve(undefined); + }, + }; + + // One representative non-default value per declared PipelineOptions field, + // so a dropped field surfaces as a missing/undefined value below. + const prior: PreviousGraph = { files: [], importEdges: [], heritageEdges: [] }; + const options: Required = { + force: true, + incrementalFrom: prior, + offline: true, + verbose: true, + skipGit: true, + byteCapPerFile: 12_345, + maxTotalFiles: 678, + embeddings: true, + embeddingsVariant: "int8", + embeddingsModelDir: "/tmp/model-dir", + embeddingsGranularity: ["symbol", "file", "community"], + embeddingsWorkers: 4, + embeddingsBatchSize: 16, + sbom: true, + reproducibleSbom: false, + coverage: true, + summaries: true, + maxSummariesPerRun: 7, + summaryModel: "model-x", + strictDetectors: true, + }; + + await runIngestion(repo, { ...options, phases: [probe] }); + + assert.ok(seen !== undefined, "probe phase did not run"); + // Every declared field must survive stripPhaseKeys. This is the + // regression guard: the prior allowlist silently dropped + // embeddingsWorkers / embeddingsBatchSize / coverage / strictDetectors. + for (const key of Object.keys(options) as (keyof PipelineOptions)[]) { + assert.deepEqual( + seen[key], + options[key], + `PipelineOptions.${String(key)} must round-trip into ctx.options`, + ); + } + // The four previously dropped fields, asserted explicitly so a future + // regression names them directly. + assert.equal(seen.embeddingsWorkers, 4); + assert.equal(seen.embeddingsBatchSize, 16); + assert.equal(seen.coverage, true); + assert.equal(seen.strictDetectors, true); + }); + + it("drops orchestrator-only keys from ctx.options", async () => { + let seen: Record | undefined; + const probe: PipelinePhase = { + name: "probe", + deps: [], + run(ctx: PipelineContext): Promise { + seen = ctx.options as Record; + return Promise.resolve(undefined); + }, + }; + + await runIngestion(repo, { + skipGit: true, + phases: [probe], + onProgress: () => {}, + summaryCacheAdapter: { lookup: () => Promise.resolve(undefined) }, + embeddingHashCacheAdapter: { list: () => Promise.resolve(new Map()) }, + }); + + assert.ok(seen !== undefined); + assert.equal(seen["phases"], undefined, "phases must not leak into ctx.options"); + assert.equal(seen["onProgress"], undefined, "onProgress must not leak into ctx.options"); + }); +}); + +describe("runIngestion coverage overlay", () => { + let repo: string; + + before(async () => { + repo = await mkdtemp(path.join(tmpdir(), "och-cov-e2e-")); + await fs.mkdir(path.join(repo, "src"), { recursive: true }); + await fs.writeFile( + path.join(repo, "src", "foo.ts"), + "export function foo(): number {\n return 1;\n}\n", + ); + await fs.mkdir(path.join(repo, "coverage"), { recursive: true }); + await fs.writeFile( + path.join(repo, "coverage", "lcov.info"), + ["TN:", "SF:src/foo.ts", "DA:1,1", "DA:2,0", "end_of_record", ""].join("\n"), + ); + }); + + after(async () => { + await rm(repo, { recursive: true, force: true }); + }); + + it("annotates a File node when --coverage is enabled and an lcov report is present", async () => { + // Regression guard: the coverage phase used to be absent from + // DEFAULT_PHASES, so `coverage: true` was inert under the default + // pipeline. This drives the full DAG and asserts the File node gains + // coverage fields. + const result = await runIngestion(repo, { skipGit: true, coverage: true }); + + const coverageRan = result.stats.phases.some((p) => p.name === "coverage"); + assert.ok(coverageRan, "coverage phase must run in the default pipeline"); + + const foo = [...result.graph.nodes()].find( + (n) => n.kind === "File" && n.filePath === "src/foo.ts", + ) as { coveragePercent?: number; coveredLines?: readonly number[] } | undefined; + assert.ok(foo !== undefined, "src/foo.ts File node missing"); + assert.equal(foo.coveragePercent, 0.5, "coveragePercent must reflect the lcov report"); + assert.deepEqual(foo.coveredLines, [1], "coveredLines must reflect the lcov report"); + }); + + it("leaves File nodes unannotated when --coverage is not passed", async () => { + const result = await runIngestion(repo, { skipGit: true }); + const foo = [...result.graph.nodes()].find( + (n) => n.kind === "File" && n.filePath === "src/foo.ts", + ) as { coveragePercent?: number } | undefined; + assert.ok(foo !== undefined); + assert.equal(foo.coveragePercent, undefined, "coverage must stay off by default"); + }); +}); + describe("runIngestion (determinism with routes + ORM)", () => { let repo: string; diff --git a/packages/ingestion/src/pipeline/orchestrator.ts b/packages/ingestion/src/pipeline/orchestrator.ts index 489472e0..aee35fe5 100644 --- a/packages/ingestion/src/pipeline/orchestrator.ts +++ b/packages/ingestion/src/pipeline/orchestrator.ts @@ -36,7 +36,6 @@ import type { PipelineContext, PipelineOptions, PipelinePhase, - PreviousGraph, ProgressEvent, } from "./types.js"; @@ -260,48 +259,18 @@ function summarizePhase(r: PhaseResult): { readonly name: string; readonly durat } function stripPhaseKeys(options: RunIngestionOptions): PipelineOptions { - // Copy only the fields PipelineOptions declares — phase overrides and - // progress callbacks are orchestrator-level concerns and must not leak - // into the normalized per-phase options. - const typed: { - force?: boolean; - offline?: boolean; - verbose?: boolean; - skipGit?: boolean; - byteCapPerFile?: number; - maxTotalFiles?: number; - embeddings?: boolean; - embeddingsVariant?: "fp32" | "int8"; - embeddingsModelDir?: string; - embeddingsGranularity?: readonly ("symbol" | "file" | "community")[]; - sbom?: boolean; - reproducibleSbom?: boolean; - incrementalFrom?: PreviousGraph; - summaries?: boolean; - maxSummariesPerRun?: number; - summaryModel?: string; - } = {}; - if (options.force !== undefined) typed.force = options.force; - if (options.offline !== undefined) typed.offline = options.offline; - if (options.verbose !== undefined) typed.verbose = options.verbose; - if (options.skipGit !== undefined) typed.skipGit = options.skipGit; - if (options.byteCapPerFile !== undefined) typed.byteCapPerFile = options.byteCapPerFile; - if (options.maxTotalFiles !== undefined) typed.maxTotalFiles = options.maxTotalFiles; - if (options.embeddings !== undefined) typed.embeddings = options.embeddings; - if (options.embeddingsVariant !== undefined) typed.embeddingsVariant = options.embeddingsVariant; - if (options.sbom !== undefined) typed.sbom = options.sbom; - if (options.reproducibleSbom !== undefined) typed.reproducibleSbom = options.reproducibleSbom; - if (options.embeddingsModelDir !== undefined) { - typed.embeddingsModelDir = options.embeddingsModelDir; - } - if (options.embeddingsGranularity !== undefined) { - typed.embeddingsGranularity = options.embeddingsGranularity; - } - if (options.incrementalFrom !== undefined) typed.incrementalFrom = options.incrementalFrom; - if (options.summaries !== undefined) typed.summaries = options.summaries; - if (options.maxSummariesPerRun !== undefined) { - typed.maxSummariesPerRun = options.maxSummariesPerRun; - } - if (options.summaryModel !== undefined) typed.summaryModel = options.summaryModel; - return typed; + // Drop only the orchestrator-level keys — phase overrides, the progress + // callback, and the two cache adapters are not per-phase options — and + // spread the rest. Destructure-and-omit is the single source of truth: + // every current and future `PipelineOptions` field reaches `ctx.options` + // automatically, so the normalized bag can never silently drop a field + // the way a hand-maintained allowlist did. + const { + phases: _phases, + onProgress: _onProgress, + summaryCacheAdapter: _summaryCacheAdapter, + embeddingHashCacheAdapter: _embeddingHashCacheAdapter, + ...pipelineOptions + } = options; + return pipelineOptions; } diff --git a/packages/ingestion/src/pipeline/phases/communities.ts b/packages/ingestion/src/pipeline/phases/communities.ts index 79bc9f0a..90b9235e 100644 --- a/packages/ingestion/src/pipeline/phases/communities.ts +++ b/packages/ingestion/src/pipeline/phases/communities.ts @@ -65,23 +65,25 @@ export const communitiesPhase: PipelinePhase = { }; function runCommunities(ctx: PipelineContext): CommunitiesOutput { - // ---- : incremental carry-forward short-circuit. --------------- + // ---- Incremental carry-forward (empty-closure only). ------------------ // - // Leiden is deterministic given a fixed seed AND a fixed input graph, - // but its partition is sensitive to every edge weight — running on a - // sparsified subgraph drifts in the general case. For the determinism - // gate (`--full` vs `--incremental` at the same commit must byte-equal) - // we take the conservative path: when the incremental view is active, - // carry forward every Community node + MEMBER_OF edge from the prior - // graph verbatim, and skip Leiden entirely. The post-parse callable - // graph is byte-identical to the prior run under no-semantic-change, so - // re-running Leiden would produce the same partition anyway; skipping - // the work is a pure speedup. If the closure introduces new callables - // whose community assignment matters, the 30% safety valve in - // incremental-scope flips mode back to "full" and Leiden runs normally. + // Leiden is deterministic given a fixed seed AND a fixed input graph, but + // its partition is global — it is sensitive to every edge weight, so a + // partial recompute over a closure subgraph would drift from a full run + // (unlike cross-file/mro/processes, whose outputs are per-file or per- + // entry-point and so split cleanly into carry-forward + recompute halves). + // Carry-forward verbatim is byte-identical to a full run ONLY when the + // post-parse callable graph is unchanged, which the incremental view + // guarantees exactly when the changed-file closure is empty. When the + // closure is non-empty a single hot file may have added or removed + // callables/CALLS edges that shift the global partition, so we fall + // through and re-run Leiden on the reconstructed full graph (cross-file + // already replayed the non-closure CALLS edges), which reproduces the + // full-run partition byte-for-byte. const view = resolveIncrementalView(ctx); if ( view.active && + view.closure.size === 0 && view.previousGraph?.edges !== undefined && view.previousGraph.nodes !== undefined ) { diff --git a/packages/ingestion/src/pipeline/phases/default-set.ts b/packages/ingestion/src/pipeline/phases/default-set.ts index 2983ab80..21c61cfa 100644 --- a/packages/ingestion/src/pipeline/phases/default-set.ts +++ b/packages/ingestion/src/pipeline/phases/default-set.ts @@ -1,5 +1,5 @@ /** - * Default pipeline phase set — full 14-phase DAG. + * Default pipeline phase set — the full ingestion DAG. * * The runner treats this array as the canonical ordering source. Adding or * removing a phase is a one-line change here; the DAG validator catches @@ -7,10 +7,10 @@ * * Phase ordering (topologically): * scan → profile - * → structure → markdown → parse → complexity → routes → tools → - * orm → crossFile → mro → - * communities → dead-code → - * processes → temporal → annotate + * → structure → coverage → markdown → parse → complexity → routes → + * tools → orm → crossFile → mro → + * communities → dead-code → + * processes → temporal → annotate * * `markdown` depends on `structure` and could run alongside `parse`; the * runner serialises everything since determinism dominates the latency @@ -27,6 +27,7 @@ import { cochangePhase } from "./cochange.js"; import { communitiesPhase } from "./communities.js"; import { complexityPhase } from "./complexity.js"; import { confidenceDemotePhase } from "./confidence-demote.js"; +import { coveragePhase } from "./coverage.js"; import { crossFilePhase } from "./cross-file.js"; import { deadCodePhase } from "./dead-code.js"; import { dependenciesPhase } from "./dependencies.js"; @@ -61,6 +62,12 @@ export const DEFAULT_PHASES: readonly PipelinePhase[] = [ // from the graph by MCP tools at query time, not consumed by later phases. repoNodePhase, structurePhase, + // `coverage` depends on scan + profile + structure and annotates `File` + // nodes with coveragePercent / coveredLines from an external test-runner + // report. It is a silent no-op unless `options.coverage === true`, so the + // default pipeline carries it inertly until `codehub analyze --coverage` + // is passed. + coveragePhase, markdownPhase, parsePhase, // incremental-scope is passive at v1.0: it consumes scan output and emits diff --git a/packages/ingestion/src/pipeline/phases/processes.ts b/packages/ingestion/src/pipeline/phases/processes.ts index 8dfbb56f..538ad238 100644 --- a/packages/ingestion/src/pipeline/phases/processes.ts +++ b/packages/ingestion/src/pipeline/phases/processes.ts @@ -42,7 +42,7 @@ import type { NodeId, ProcessNode } from "@opencodehub/core-types"; import { makeNodeId } from "@opencodehub/core-types"; import type { PipelineContext, PipelinePhase } from "../types.js"; import { COMMUNITIES_PHASE_NAME } from "./communities.js"; -import { resolveIncrementalView } from "./incremental-helper.js"; +import { buildFilePathLookup, resolveIncrementalView } from "./incremental-helper.js"; import { INCREMENTAL_SCOPE_PHASE_NAME } from "./incremental-scope.js"; import { ROUTES_PHASE_NAME } from "./routes.js"; import { STRUCTURE_PHASE_NAME } from "./structure.js"; @@ -284,32 +284,55 @@ interface NodeMeta { } function runProcesses(ctx: PipelineContext): ProcessesOutput { - // ---- Incremental carry-forward short-circuit. ------------------------- + // ---- Incremental carry-forward (closure-aware). ----------------------- // - // BFS re-rooting is expensive and its outputs (Process nodes, PROCESS_STEP - // edges, ENTRY_POINT_OF edges) are deterministic functions of the post- - // parse call graph + route/tool set. When the incremental view is active - // the current call graph matches the prior run under no-semantic-change, - // so the safest byte-identical incremental path is to carry forward every - // Process node and PROCESS_STEP / ENTRY_POINT_OF edge from the prior - // graph and skip BFS altogether. The 30% safety valve keeps us honest: - // whenever the closure balloons past threshold the phase falls through - // to a fresh BFS that observes any new entry points. + // A Process is the deterministic forward-BFS closure of one entry point + // over the post-parse CALLS graph plus the Route/Tool set. When the + // incremental view is active we carry forward only processes anchored + // OUTSIDE the changed-file closure and re-BFS the ones anchored inside it, + // mirroring how cross-file and mro split carry-forward from recompute. + // + // This stays byte-identical to a full run because incremental-scope widens + // the closure along the IMPORTS graph: any entry point that could reach a + // changed file is itself pulled into the closure, so a carried-forward + // (out-of-closure) process only traverses unchanged call edges. The + // previous verbatim carry-forward ignored the closure entirely, leaving a + // stale Process / ENTRY_POINT_OF structure whenever a single hot file's + // entry points changed under the 30% safety valve. const view = resolveIncrementalView(ctx); - if ( + const carryForwardActive = view.active && view.previousGraph?.edges !== undefined && - view.previousGraph.nodes !== undefined - ) { - let carriedProcesses = 0; - let carriedSteps = 0; - for (const n of view.previousGraph.nodes) { + view.previousGraph.nodes !== undefined; + const carriedProcessIds = new Set(); + let carriedProcesses = 0; + let carriedSteps = 0; + if (carryForwardActive) { + const priorNodes = view.previousGraph?.nodes ?? []; + const priorEdges = view.previousGraph?.edges ?? []; + const filePathByNodeId = buildFilePathLookup(priorNodes); + for (const n of priorNodes) { if (n.kind !== "Process") continue; + // Re-BFS in-closure processes below; carry forward the rest verbatim. + if (view.closure.has(n.filePath)) continue; ctx.graph.addNode(n); + carriedProcessIds.add(n.id); carriedProcesses += 1; } - for (const e of view.previousGraph.edges) { - if (e.type !== "PROCESS_STEP" && e.type !== "ENTRY_POINT_OF") continue; + for (const e of priorEdges) { + if (e.type === "ENTRY_POINT_OF") { + // Route/Tool -> Process: keep only when its Process was carried. + if (!carriedProcessIds.has(e.to as string)) continue; + } else if (e.type === "PROCESS_STEP") { + // symbol -> symbol: keep only when neither endpoint sits in the + // closure, so a recomputed in-closure process owns its own steps. + const fromPath = filePathByNodeId.get(e.from as string); + const toPath = filePathByNodeId.get(e.to as string); + if (fromPath !== undefined && view.closure.has(fromPath)) continue; + if (toPath !== undefined && view.closure.has(toPath)) continue; + } else { + continue; + } ctx.graph.addEdge({ from: e.from, to: e.to, @@ -320,10 +343,6 @@ function runProcesses(ctx: PipelineContext): ProcessesOutput { }); if (e.type === "PROCESS_STEP") carriedSteps += 1; } - return { - processCount: carriedProcesses, - avgStepsPerProcess: carriedProcesses === 0 ? 0 : carriedSteps / carriedProcesses, - }; } // ---- Collect candidate nodes + adjacency. ----------------------------- @@ -435,6 +454,13 @@ function runProcesses(ctx: PipelineContext): ProcessesOutput { let totalSteps = 0; for (const ep of entryPoints) { + // In active incremental mode, entry points anchored outside the closure + // were carried forward verbatim above; re-BFS only the in-closure ones. + // The full `entryPoints` set is identical to a full run (metaById spans + // the whole carried-forward graph), so the recomputed slice plus the + // carry-forward slice reconstruct the full-run output byte-for-byte. + if (carryForwardActive && !view.closure.has(ep.meta.filePath)) continue; + const steps = bfs(ep.id, adjacency); if (steps.length < MIN_STEPS_PER_PROCESS) continue; @@ -502,9 +528,13 @@ function runProcesses(ctx: PipelineContext): ProcessesOutput { totalSteps += steps.length; } + // Fold in the carried-forward (out-of-closure) processes so the reported + // counts match a full run's totals. + const allProcesses = processCount + carriedProcesses; + const allSteps = totalSteps + carriedSteps; return { - processCount, - avgStepsPerProcess: processCount === 0 ? 0 : totalSteps / processCount, + processCount: allProcesses, + avgStepsPerProcess: allProcesses === 0 ? 0 : allSteps / allProcesses, }; } diff --git a/packages/ingestion/src/pipeline/phases/summarize.test.ts b/packages/ingestion/src/pipeline/phases/summarize.test.ts index c9a62699..f287e512 100644 --- a/packages/ingestion/src/pipeline/phases/summarize.test.ts +++ b/packages/ingestion/src/pipeline/phases/summarize.test.ts @@ -277,6 +277,28 @@ describe("summarizePhase — live summarize with cap", () => { assert.equal(row.createdAt, "2026-04-22T12:00:00.000Z"); assert.ok(row.summaryText.startsWith("purpose for ")); assert.ok((row.returnsTypeSummary ?? "").startsWith("gist for ")); + // structured_json carries the validated fields the flat columns drop: + // per-input descriptions, returns.details, and line-range citations. + // It must be present (okResult has inputs + details + citations) and + // be canonical (sorted keys) so the temporal-store content is stable. + assert.ok(row.structuredJson !== undefined, "structuredJson is persisted"); + const parsed = JSON.parse(row.structuredJson) as Record; + assert.deepEqual(Object.keys(parsed), [ + "citations", + "inputs", + "invariants", + "returns", + "side_effects", + ]); + assert.equal( + (parsed["inputs"] as { description: string }[])[0]?.description, + "the thing to process deeply", + ); + assert.equal( + (parsed["returns"] as { details: string }).details, + "The computed value after processing.", + ); + assert.equal((parsed["citations"] as unknown[]).length, 2); } }); }); diff --git a/packages/ingestion/src/pipeline/phases/summarize.ts b/packages/ingestion/src/pipeline/phases/summarize.ts index ab30f02d..5dbfe66f 100644 --- a/packages/ingestion/src/pipeline/phases/summarize.ts +++ b/packages/ingestion/src/pipeline/phases/summarize.ts @@ -52,13 +52,14 @@ import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import path from "node:path"; -import { SCIP_PROVENANCE_PREFIXES } from "@opencodehub/core-types"; +import { canonicalJson, SCIP_PROVENANCE_PREFIXES } from "@opencodehub/core-types"; import type { SymbolSummaryRow } from "@opencodehub/storage"; import { DEFAULT_MODEL_ID, SUMMARIZER_PROMPT_VERSION, type SummarizeInput, type SummarizerResult, + type SymbolSummaryT, } from "@opencodehub/summarizer"; import type { PipelineContext, PipelinePhase } from "../types.js"; import { CONFIDENCE_DEMOTE_PHASE_NAME } from "./confidence-demote.js"; @@ -321,6 +322,7 @@ async function runSummarize(ctx: PipelineContext): Promise const result = await summarizer.summarize(input); const summary = result.summary; const signatureSummary = buildSignatureSummary(summary); + const structuredJson = buildStructuredJson(summary); const row: SymbolSummaryRow = { nodeId: entry.candidate.nodeId, contentHash: entry.contentHash, @@ -329,6 +331,7 @@ async function runSummarize(ctx: PipelineContext): Promise summaryText: summary.purpose, ...(signatureSummary !== undefined ? { signatureSummary } : {}), returnsTypeSummary: summary.returns.type_summary, + ...(structuredJson !== undefined ? { structuredJson } : {}), createdAt: now().toISOString(), }; rows.push(row); @@ -470,6 +473,44 @@ function buildSignatureSummary(summary: { return summary.inputs.map((i) => `${i.name}: ${i.type}`).join(", "); } +/** + * Serialize the validated structured fields the flat `SymbolSummaryRow` + * columns drop — per-input descriptions, `returns.details`, `side_effects`, + * `invariants`, and the line-range `citations` that back a staleness + * detector. Emitted via `canonicalJson` (sorted keys) so the row is + * byte-deterministic across runs and the temporal-store content hash stays + * stable. Returns `undefined` when there is nothing beyond the flat columns + * to persist, so legacy rows keep round-tripping with `structured_json` NULL. + */ +function buildStructuredJson(summary: SymbolSummaryT): string | undefined { + const hasInputDetails = summary.inputs.some((i) => i.description.length > 0); + const hasInvariants = summary.invariants !== null && summary.invariants.length > 0; + if ( + !hasInputDetails && + summary.side_effects.length === 0 && + !hasInvariants && + summary.returns.details.length === 0 && + summary.citations.length === 0 + ) { + return undefined; + } + return canonicalJson({ + inputs: summary.inputs.map((i) => ({ + name: i.name, + type: i.type, + description: i.description, + })), + returns: { type: summary.returns.type, details: summary.returns.details }, + side_effects: summary.side_effects, + invariants: summary.invariants ?? [], + citations: summary.citations.map((c) => ({ + field_name: c.field_name, + line_start: c.line_start, + line_end: c.line_end, + })), + }); +} + /** * Lightweight cache adapter a caller can plug onto `ctx.options` to let the * phase probe existing summaries before emitting work. The `summarize` diff --git a/packages/ingestion/src/pipeline/types.ts b/packages/ingestion/src/pipeline/types.ts index 1474a66e..643e164b 100644 --- a/packages/ingestion/src/pipeline/types.ts +++ b/packages/ingestion/src/pipeline/types.ts @@ -198,8 +198,7 @@ export interface PipelineOptions { * When `true`, detectors that pattern-match on receiver identifiers * skip heuristic-only matches entirely — edges are emitted only when a * receiver's module origin was confirmed via the import graph or - * ts-morph. Exposed by the `codehub analyze --strict-detectors` flag - * (DET-O-001). + * ts-morph. Exposed by the `codehub analyze --strict-detectors` flag. */ readonly strictDetectors?: boolean; } diff --git a/packages/ingestion/src/providers/index.ts b/packages/ingestion/src/providers/index.ts index 97970cf7..17f23f83 100644 --- a/packages/ingestion/src/providers/index.ts +++ b/packages/ingestion/src/providers/index.ts @@ -38,8 +38,6 @@ export { firstWinsStrategy } from "./resolution/first-wins.js"; export type { MroStrategy } from "./resolution/mro.js"; export { getMroStrategy } from "./resolution/mro.js"; export { noneStrategy } from "./resolution/none.js"; -export type { ResolverStrategy } from "./resolution/resolver-strategy.js"; -export { defaultResolver } from "./resolution/resolver-strategy.js"; export { singleInheritanceStrategy } from "./resolution/single-inheritance.js"; export { rubyProvider } from "./ruby.js"; export { rustProvider } from "./rust.js"; diff --git a/packages/ingestion/src/providers/javascript.ts b/packages/ingestion/src/providers/javascript.ts index f8ddebdf..131a173d 100644 --- a/packages/ingestion/src/providers/javascript.ts +++ b/packages/ingestion/src/providers/javascript.ts @@ -93,8 +93,9 @@ export const javascriptProvider: LanguageProvider = { heritageEdge: "EXTENDS", inferImplicitReceiver: () => "this", isExportedIdentifier: (_name, context) => context === "top-level", - // Shares the TS-family stack-graphs backend — see typescript.ts. - resolverStrategyName: "stack-graphs", + // Reference resolution runs through the three-tier walker + // (same-file -> import-scoped -> global). SCIP edges, when present, + // overlay as the precision oracle on top of the walker's output. complexityDefinitionKinds: [ "function_declaration", "function_expression", diff --git a/packages/ingestion/src/providers/python.ts b/packages/ingestion/src/providers/python.ts index 939f6771..38a71d23 100644 --- a/packages/ingestion/src/providers/python.ts +++ b/packages/ingestion/src/providers/python.ts @@ -340,11 +340,9 @@ export const pythonProvider: LanguageProvider = { inferImplicitReceiver: () => "self", preprocessImportPath: preprocessPyImportPath, isExportedIdentifier: (name) => !name.startsWith("_"), - // Opt into the clean-room stack-graphs evaluator for Python reference - // resolution. The strategy falls back to the three-tier walker whenever - // stack-graphs can't produce an answer (missing graph cache, degraded - // rule load, depth-budget exhaustion, etc.). - resolverStrategyName: "stack-graphs", + // Reference resolution runs through the three-tier walker + // (same-file -> import-scoped -> global). SCIP edges, when present, + // overlay as the precision oracle on top of the walker's output. complexityDefinitionKinds: ["function_definition", "lambda"], halsteadOperatorKinds: [ "+", diff --git a/packages/ingestion/src/providers/resolution/python-all-filter.test.ts b/packages/ingestion/src/providers/resolution/python-all-filter.test.ts deleted file mode 100644 index 65ac9c7e..00000000 --- a/packages/ingestion/src/providers/resolution/python-all-filter.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import assert from "node:assert/strict"; -import { mkdtempSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { test } from "node:test"; -import type { ResolutionCandidate } from "./context.js"; -import { filterByPythonAll, parsePythonAll } from "./python-all-filter.js"; - -test("parsePythonAll: extracts names from a list literal", () => { - const source = `\n__all__ = ["alpha", 'beta', "gamma"]\n`; - assert.deepEqual(parsePythonAll(source), ["alpha", "beta", "gamma"]); -}); - -test("parsePythonAll: extracts names from a tuple literal", () => { - const source = `__all__ = ('alpha', 'beta')`; - assert.deepEqual(parsePythonAll(source), ["alpha", "beta"]); -}); - -test("parsePythonAll: returns null when no __all__ declared", () => { - assert.equal(parsePythonAll(`x = 1`), null); -}); - -test("filterByPythonAll: drops candidates not listed in __all__", () => { - const tmp = mkdtempSync(join(tmpdir(), "all-filter-")); - const initPath = join(tmp, "__init__.py"); - writeFileSync( - initPath, - `__all__ = ["public_func"]\n\ndef _helper(): pass\ndef public_func(): pass\n`, - ); - - const candidates: readonly ResolutionCandidate[] = [ - { targetId: "x", tier: "import-scoped", confidence: 0.9 }, - ]; - const kept = filterByPythonAll(candidates, initPath, "public_func"); - assert.equal(kept.length, 1); - const dropped = filterByPythonAll(candidates, initPath, "_helper"); - assert.equal(dropped.length, 0); -}); - -test("filterByPythonAll: passes candidates through when __all__ absent", () => { - const tmp = mkdtempSync(join(tmpdir(), "all-filter-")); - const initPath = join(tmp, "__init__.py"); - writeFileSync(initPath, `def _helper(): pass\n`); - const candidates: readonly ResolutionCandidate[] = [ - { targetId: "x", tier: "import-scoped", confidence: 0.9 }, - ]; - const kept = filterByPythonAll(candidates, initPath, "_helper"); - assert.equal(kept.length, 1); -}); - -test("filterByPythonAll: passes through when target file can't be read", () => { - const candidates: readonly ResolutionCandidate[] = [ - { targetId: "x", tier: "import-scoped", confidence: 0.9 }, - ]; - const kept = filterByPythonAll(candidates, "/nonexistent/path/__init__.py", "anything"); - assert.equal(kept.length, 1); -}); diff --git a/packages/ingestion/src/providers/resolution/python-all-filter.ts b/packages/ingestion/src/providers/resolution/python-all-filter.ts deleted file mode 100644 index 6e0c1863..00000000 --- a/packages/ingestion/src/providers/resolution/python-all-filter.ts +++ /dev/null @@ -1,66 +0,0 @@ -// __all__ post-processor for wildcard Python imports. -// -// Upstream stack-graphs does not honour `__all__` lists; its edge model -// makes every top-level def reachable from a wildcard import. When a -// resolution candidate comes in via a wildcard path and the target -// module has an `__all__` declaration, we filter candidates down to the -// listed names only. If no `__all__` is declared we pass everything through. - -import { readFileSync } from "node:fs"; -import type { ResolutionCandidate } from "./context.js"; - -/** - * Parse `__all__ = [...]` from a Python source string. - * - * Supports three syntactic forms in common use: - * __all__ = ["a", "b"] - * __all__ = ('a', 'b') - * __all__ = ["a", "b",] # trailing comma - * - * Returns `null` when no recognisable declaration exists, which the - * caller treats as "filter is disabled". - */ -export function parsePythonAll(source: string): readonly string[] | null { - // Regex matches the right-hand-side list/tuple literal. We accept single - // or double-quoted string entries; bare identifiers and f-strings are - // deliberately ignored — they aren't part of the idiomatic pattern. - const m = /__all__\s*=\s*[[(]([\s\S]*?)[\])]/m.exec(source); - if (m === null) return null; - const body = m[1] ?? ""; - const names: string[] = []; - const item = /['"]([A-Za-z_][\w]*)['"]/g; - let match: RegExpExecArray | null = item.exec(body); - while (match !== null) { - const name = match[1]; - if (name !== undefined) names.push(name); - match = item.exec(body); - } - return names; -} - -/** - * Filter resolution candidates against a target module's `__all__` list. - * - * `targetModuleInitPath` is an absolute path to the module's `__init__.py` - * (or the module file itself). If the file can't be read we pass candidates - * through — degradation is strictly friendlier than rejection. - * - * `candidateName` is the bare Python identifier being imported (already - * stripped of any `module.` prefix). - */ -export function filterByPythonAll( - candidates: readonly ResolutionCandidate[], - targetModuleInitPath: string, - candidateName: string, -): readonly ResolutionCandidate[] { - let source: string; - try { - source = readFileSync(targetModuleInitPath, "utf8"); - } catch { - return candidates; - } - const allowed = parsePythonAll(source); - if (allowed === null) return candidates; - if (!allowed.includes(candidateName)) return []; - return candidates; -} diff --git a/packages/ingestion/src/providers/resolution/resolver-strategy.ts b/packages/ingestion/src/providers/resolution/resolver-strategy.ts deleted file mode 100644 index 6cd22d05..00000000 --- a/packages/ingestion/src/providers/resolution/resolver-strategy.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { LanguageProvider } from "../types.js"; -import type { ResolutionCandidate, ResolutionQuery, SymbolIndex } from "./context.js"; -import { resolve } from "./context.js"; -import { stackGraphsPythonResolver } from "./stack-graphs-python.js"; -import { stackGraphsTsResolver } from "./stack-graphs-ts.js"; - -/** - * Seam for alternative resolution backends. - * - * The MVP ships the three-tier walker as the default. v1 added the Python - * `stack-graphs` strategy; v2 extends it to the TS family. Both per-language - * implementations register under the same public name (`"stack-graphs"`); - * the `stackGraphsRouter` below dispatches per query based on - * `provider.id`, so a language provider can opt in just by setting - * `resolverStrategyName: "stack-graphs"`. - */ -export interface ResolverStrategy { - readonly name: string; - resolve(q: ResolutionQuery, index: SymbolIndex): ResolutionCandidate[]; -} - -export const defaultResolver: ResolverStrategy = { - name: "three-tier-default", - resolve, -}; - -/** - * Dispatch to the per-language stack-graphs backend by provider id. Providers - * outside the supported set fall through to the three-tier walker. Keeping - * the dispatch here means each language can evolve its own builder/cache - * without other languages paying any cost at query time. - */ -export const stackGraphsRouter: ResolverStrategy = { - name: "stack-graphs", - resolve(q, index): ResolutionCandidate[] { - const id = q.provider.id; - if (id === "python") return stackGraphsPythonResolver.resolve(q, index); - if (id === "typescript" || id === "tsx" || id === "javascript") { - return stackGraphsTsResolver.resolve(q, index); - } - return defaultResolver.resolve(q, index); - }, -}; - -/** - * Registry of all known resolver strategies keyed by the name that a - * language provider can opt into. Keeping this as a Record keeps misuses - * visible at type level. - */ -export const RESOLVER_STRATEGIES: Readonly> = { - "three-tier-default": defaultResolver, - "stack-graphs": stackGraphsRouter, -}; - -/** - * Resolve the strategy to use for a given language provider. - * - * Providers may expose an optional `resolverStrategyName` hook. If absent - * — or the referenced strategy isn't registered — we return the default - * walker. This lets new strategies ship incrementally without a breaking - * type-level change on every provider. - */ -export function getResolver( - provider: Pick & { readonly resolverStrategyName?: string }, -): ResolverStrategy { - const requested = provider.resolverStrategyName; - if (requested === undefined) return defaultResolver; - const strategy = RESOLVER_STRATEGIES[requested]; - return strategy ?? defaultResolver; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs-python.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs-python.test.ts deleted file mode 100644 index 65f4b666..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs-python.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import assert from "node:assert/strict"; -import { test } from "node:test"; -import type { LanguageProvider } from "../types.js"; -import type { SymbolIndex } from "./context.js"; -import { getResolver, RESOLVER_STRATEGIES } from "./resolver-strategy.js"; -import type { StackGraph } from "./stack-graphs/types.js"; -import { - clearStackGraphsForTests, - registerStackGraphs, - type StackGraphsHintedQuery, - stackGraphsPythonResolver, -} from "./stack-graphs-python.js"; - -// Minimal provider-shaped stubs. We intentionally avoid importing the real -// `pythonProvider` / `typescriptProvider` constants here — those modules -// sit on top of a deep import graph (parse-phase, registry, extraction -// helpers) whose initialisation order can trip ES-module cycles when the -// providers add behavioural hooks (see/ ). The resolver -// reads `provider.id` only, so these stubs drive the same dispatch logic. -function pythonStub(): LanguageProvider { - return { - id: "python", - extensions: [".py"], - importSemantics: "namespace", - mroStrategy: "c3", - typeConfig: { structural: true, nominal: false, generics: true }, - heritageEdge: "EXTENDS", - resolverStrategyName: "stack-graphs", - extractDefinitions: () => [], - extractCalls: () => [], - extractImports: () => [], - isExported: () => false, - extractHeritage: () => [], - }; -} - -function typescriptStub(): LanguageProvider { - return { - id: "typescript", - extensions: [".ts"], - importSemantics: "named", - mroStrategy: "first-wins", - typeConfig: { structural: true, nominal: false, generics: true }, - heritageEdge: "EXTENDS", - resolverStrategyName: "stack-graphs", - extractDefinitions: () => [], - extractCalls: () => [], - extractImports: () => [], - isExported: () => false, - extractHeritage: () => [], - }; -} - -function makeIndex(overrides: Partial = {}): SymbolIndex { - return { - findInFile: () => undefined, - findInImports: () => undefined, - findGlobal: () => [], - ...overrides, - }; -} - -test("getResolver: python provider maps to stack-graphs strategy", () => { - const r = getResolver(pythonStub()); - assert.equal(r.name, "stack-graphs"); -}); - -test("getResolver: typescript provider also maps to stack-graphs strategy (v2 router)", () => { - // The router enrolls the TS family into the stack-graphs strategy. - // The same public name dispatches internally by provider.id. - const r = getResolver(typescriptStub()); - assert.equal(r.name, "stack-graphs"); -}); - -test("getResolver: providers with no opt-in fall through to three-tier default", () => { - const r = getResolver({ id: "go" }); - assert.equal(r.name, "three-tier-default"); -}); - -test("registry exposes both strategies by name", () => { - assert.ok(RESOLVER_STRATEGIES["three-tier-default"] !== undefined); - assert.ok(RESOLVER_STRATEGIES["stack-graphs"] !== undefined); -}); - -test("stack-graphs strategy falls back to three-tier when no graphs registered", () => { - clearStackGraphsForTests(); - const index = makeIndex({ - findInFile: (file, name) => (file === "a.py" && name === "foo" ? "sym:a.py#foo" : undefined), - }); - const out = stackGraphsPythonResolver.resolve( - { callerFile: "a.py", calleeName: "foo", provider: pythonStub() }, - index, - ); - // Fallback path — three-tier returns the same-file hit at 0.95. - assert.equal(out.length, 1); - assert.equal(out[0]?.targetId, "sym:a.py#foo"); - assert.equal(out[0]?.confidence, 0.95); -}); - -test("stack-graphs strategy passes non-Python queries straight to three-tier", () => { - clearStackGraphsForTests(); - const index = makeIndex({ - findInFile: (file, name) => (file === "a.ts" && name === "foo" ? "sym:a.ts#foo" : undefined), - }); - const out = stackGraphsPythonResolver.resolve( - { callerFile: "a.ts", calleeName: "foo", provider: typescriptStub() }, - index, - ); - assert.equal(out.length, 1); - assert.equal(out[0]?.tier, "same-file"); -}); - -test("stack-graphs strategy emits hit at 0.9 confidence when graph resolves", () => { - clearStackGraphsForTests(); - // Hand-roll a graph where a reference at (line 5, col 0) resolves to a - // pop node with a definitionTarget. - const file = "a.py"; - const graph: StackGraph = { - file, - rootNodeId: "root", - nodes: new Map([ - ["root", { id: "root", kind: "root", file }], - ["ref", { id: "ref", kind: "push", symbol: "bar", file }], - [ - "def", - { - id: "def", - kind: "pop", - symbol: "bar", - definitionTarget: "bar", - file, - line: 1, - }, - ], - ]), - edges: [{ source: "ref", target: "def", precedence: 0 }], - referenceIndex: new Map([["5:0", "ref"]]), - }; - registerStackGraphs(new Map([[file, graph]])); - const q: StackGraphsHintedQuery = { - callerFile: file, - calleeName: "bar", - provider: pythonStub(), - referenceLine: 5, - referenceColumn: 0, - }; - const out = stackGraphsPythonResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.9); - assert.equal(out[0]?.targetId, "a.py:1:bar"); - clearStackGraphsForTests(); -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs-python.ts b/packages/ingestion/src/providers/resolution/stack-graphs-python.ts deleted file mode 100644 index 6c1faa05..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs-python.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Python-specific ResolverStrategy backed by the clean-room stack-graphs -// evaluator. Uses the three-tier walker as a fallback whenever stack-graphs -// can't produce an answer — missing rule file, empty graph cache, parse -// errors, traversal timeout — so a Python ingest never regresses versus the -// default resolver. -// -// The strategy is registered in resolver-strategy.ts under the key -// "stack-graphs"; the python provider opts in via `resolverStrategyName`. -// -// Cross-module graph wiring is provided by the caller via `registerStackGraphs` -// — the ingestion pipeline prepares one `StackGraph` per Python file during -// parse and primes the cache before resolution begins. Tests drive this same -// API with in-memory fixtures. - -import type { ResolutionCandidate, ResolutionQuery, SymbolIndex } from "./context.js"; -import { CONFIDENCE_BY_TIER, resolve as threeTierResolve } from "./context.js"; -import type { ResolverStrategy } from "./resolver-strategy.js"; -import { resolveViaStackGraphs } from "./stack-graphs/glue.js"; -import type { ReferenceQuery, StackGraph } from "./stack-graphs/types.js"; -import { STACK_GRAPHS_HIT_CONFIDENCE } from "./stack-graphs/types.js"; - -/** - * Per-reference lookup info the ingestion pipeline attaches to the query - * when delegating to stack-graphs. The default three-tier `ResolutionQuery` - * lacks line/column, so strategies that want them advertise a sibling - * interface and the pipeline downcasts when invoking stack-graphs. - */ -export interface StackGraphsHintedQuery extends ResolutionQuery { - readonly referenceLine?: number; - readonly referenceColumn?: number; -} - -interface StackGraphStore { - readonly graphs: Map; - fallbacks: number; - stackGraphHits: number; -} - -const STORE: StackGraphStore = { - graphs: new Map(), - fallbacks: 0, - stackGraphHits: 0, -}; - -/** - * Prime the cache with per-file stack graphs. Called by the ingestion parse - * phase before resolution runs. - */ -export function registerStackGraphs(graphs: ReadonlyMap): void { - STORE.graphs.clear(); - for (const [k, v] of graphs) STORE.graphs.set(k, v); -} - -/** For tests: drop all graphs. */ -export function clearStackGraphsForTests(): void { - STORE.graphs.clear(); - STORE.fallbacks = 0; - STORE.stackGraphHits = 0; -} - -/** Stats surfaced to the pipeline for telemetry. */ -export function getStackGraphsStats(): { readonly fallbacks: number; readonly hits: number } { - return { fallbacks: STORE.fallbacks, hits: STORE.stackGraphHits }; -} - -/** Is this provider Python? Strategy no-ops on non-Python inputs. */ -function isPythonQuery(q: ResolutionQuery): boolean { - return q.provider.id === "python"; -} - -function mapTargetKeyToResolutionId(targetKey: string): string { - // targetKey is `${file}:${line}:${qualifiedName}`. We emit this as the - // resolver's `targetId`. The storage layer treats opaque ids, so any - // shape that's unique works. Using `:` as the separator mirrors the - // qualified-name convention elsewhere in the ingestion pipeline. - return targetKey; -} - -function runStackGraphs(q: StackGraphsHintedQuery): ResolutionCandidate | null { - if (STORE.graphs.size === 0) return null; - const line = q.referenceLine; - const column = q.referenceColumn; - if (line === undefined || column === undefined) return null; - - const ref: ReferenceQuery = { - file: q.callerFile, - line, - column, - name: q.calleeName, - }; - try { - const { results } = resolveViaStackGraphs(ref, STORE.graphs); - const best = results[0]; - if (best === undefined) return null; - return { - targetId: mapTargetKeyToResolutionId(best.targetKey), - tier: "import-scoped", - confidence: STACK_GRAPHS_HIT_CONFIDENCE, - }; - } catch { - return null; - } -} - -export const stackGraphsPythonResolver: ResolverStrategy = { - name: "stack-graphs", - resolve(q: ResolutionQuery, index: SymbolIndex): ResolutionCandidate[] { - if (!isPythonQuery(q)) { - return threeTierResolve(q, index); - } - const hinted = q as StackGraphsHintedQuery; - const hit = runStackGraphs(hinted); - if (hit !== null) { - STORE.stackGraphHits++; - // Stack-graphs resolutions always outrank the three-tier import-scoped - // confidence — we emit the fixed STACK_GRAPHS_HIT_CONFIDENCE score and - // leave re-ranking to the caller. - const clamped: ResolutionCandidate = { - targetId: hit.targetId, - tier: hit.tier, - confidence: Math.max(hit.confidence, CONFIDENCE_BY_TIER["import-scoped"]), - }; - return [clamped]; - } - STORE.fallbacks++; - return threeTierResolve(q, index); - }, -}; diff --git a/packages/ingestion/src/providers/resolution/stack-graphs-ts.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs-ts.test.ts deleted file mode 100644 index 6c5fc2f3..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs-ts.test.ts +++ /dev/null @@ -1,400 +0,0 @@ -import assert from "node:assert/strict"; -import { test } from "node:test"; -import type { LanguageProvider } from "../types.js"; -import type { SymbolIndex } from "./context.js"; -import { getResolver, RESOLVER_STRATEGIES } from "./resolver-strategy.js"; -import type { StackGraph } from "./stack-graphs/types.js"; -import { - buildTsStackGraph, - clearTsStackGraphsForTests, - registerTsStackGraphs, - stackGraphsTsResolver, - type TsModuleFacts, - type TsStackGraphsHintedQuery, -} from "./stack-graphs-ts.js"; - -// Minimal provider-shaped stubs. We intentionally avoid importing the real -// `typescriptProvider` / `tsxProvider` / `javascriptProvider` constants here -// — those modules sit on top of a multi-hop import graph (parse-phase, -// registry, extraction helpers) that can produce ESM initialisation cycles -// in other in-flight streams. The resolver only reads `provider.id`, so a -// tagged stub exercises the same dispatch logic with zero coupling. -function providerStub( - id: LanguageProvider["id"], -): Pick & { readonly resolverStrategyName?: string } { - return { id, resolverStrategyName: "stack-graphs" }; -} - -const TS_PROVIDER: LanguageProvider = { - id: "typescript", - extensions: [".ts"], - importSemantics: "named", - mroStrategy: "first-wins", - typeConfig: { structural: true, nominal: false, generics: true }, - heritageEdge: "EXTENDS", - resolverStrategyName: "stack-graphs", - extractDefinitions: () => [], - extractCalls: () => [], - extractImports: () => [], - isExported: () => false, - extractHeritage: () => [], -}; - -function makeIndex(overrides: Partial = {}): SymbolIndex { - return { - findInFile: () => undefined, - findInImports: () => undefined, - findGlobal: () => [], - ...overrides, - }; -} - -function makeResolveModule( - mapping: Readonly>, -): (spec: string) => string | null { - return (spec) => (Object.hasOwn(mapping, spec) ? (mapping[spec] as string) : null); -} - -// --------------------------------------------------------------------------- -// Registry / provider wiring -// --------------------------------------------------------------------------- - -test("RESOLVER_STRATEGIES exposes the stack-graphs router", () => { - assert.ok(RESOLVER_STRATEGIES["stack-graphs"] !== undefined); - assert.equal(RESOLVER_STRATEGIES["stack-graphs"]?.name, "stack-graphs"); -}); - -test("typescript/tsx/javascript provider stubs opt in to stack-graphs", () => { - for (const id of ["typescript", "tsx", "javascript"] as const) { - const r = getResolver(providerStub(id)); - assert.equal(r.name, "stack-graphs", `provider ${id} should opt in`); - } -}); - -test("providers with no opt-in fall back to three-tier default", () => { - const r = getResolver({ id: "go" }); - assert.equal(r.name, "three-tier-default"); -}); - -// --------------------------------------------------------------------------- -// Strategy fall-back semantics -// --------------------------------------------------------------------------- - -test("stack-graphs TS strategy falls back to three-tier when no graphs registered", () => { - clearTsStackGraphsForTests(); - const index = makeIndex({ - findInFile: (file, name) => - file === "consumer.ts" && name === "signIn" ? "sym:consumer.ts#signIn" : undefined, - }); - const out = stackGraphsTsResolver.resolve( - { callerFile: "consumer.ts", calleeName: "signIn", provider: TS_PROVIDER }, - index, - ); - assert.equal(out.length, 1); - assert.equal(out[0]?.targetId, "sym:consumer.ts#signIn"); - assert.equal(out[0]?.confidence, 0.95); -}); - -test("stack-graphs TS strategy passes non-TS-family queries straight through", () => { - clearTsStackGraphsForTests(); - const index = makeIndex({ - findInFile: (file, name) => - file === "mod.py" && name === "foo" ? "sym:mod.py#foo" : undefined, - }); - // Passing a non-TS id (e.g. python) — TS resolver should defer to three-tier. - const out = stackGraphsTsResolver.resolve( - { - callerFile: "mod.py", - calleeName: "foo", - provider: { ...TS_PROVIDER, id: "python" }, - }, - index, - ); - assert.equal(out.length, 1); - assert.equal(out[0]?.tier, "same-file"); -}); - -test("stack-graphs TS strategy: unresolved reference falls back to three-tier globals", () => { - clearTsStackGraphsForTests(); - // Register an empty graph for consumer so the backend runs but finds no - // reference at the queried position. - const consumerFacts: TsModuleFacts = { - file: "consumer.ts", - moduleKey: "consumer", - imports: [], - exports: [], - localDefinitions: [], - references: [], - resolveModule: () => null, - }; - const graphs = new Map([["consumer.ts", buildTsStackGraph(consumerFacts)]]); - registerTsStackGraphs(graphs); - - const index = makeIndex({ - findGlobal: (name) => (name === "orphan" ? ["sym:somewhere#orphan"] : []), - }); - const q: TsStackGraphsHintedQuery = { - callerFile: "consumer.ts", - calleeName: "orphan", - provider: TS_PROVIDER, - referenceLine: 99, - referenceColumn: 0, - }; - const out = stackGraphsTsResolver.resolve(q, index); - // Three-tier fallback → global tier at 0.5. - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.5); - clearTsStackGraphsForTests(); -}); - -// --------------------------------------------------------------------------- -// Builder + engine: named import, default, namespace, re-exports. -// --------------------------------------------------------------------------- - -function buildAndRegister(modules: readonly TsModuleFacts[]): Map { - const graphs = new Map(); - for (const m of modules) graphs.set(m.file, buildTsStackGraph(m)); - registerTsStackGraphs(graphs); - return graphs; -} - -test("named import: consumer resolves signIn at 0.9 confidence via stack-graphs", () => { - clearTsStackGraphsForTests(); - const target: TsModuleFacts = { - file: "simple-import-target.ts", - moduleKey: "simple-import-target", - imports: [], - exports: [{ kind: "named-local", name: "signIn", line: 1 }], - localDefinitions: [{ name: "signIn", line: 1 }], - references: [], - resolveModule: () => null, - }; - const consumer: TsModuleFacts = { - file: "simple-import.ts", - moduleKey: "simple-import", - imports: [ - { - kind: "named", - name: "signIn", - local: "signIn", - module: "./simple-import-target", - line: 1, - }, - ], - exports: [], - localDefinitions: [{ name: "main", line: 3 }], - references: [{ name: "signIn", line: 4, column: 2 }], - resolveModule: makeResolveModule({ - "./simple-import-target": "simple-import-target", - }), - }; - buildAndRegister([target, consumer]); - - const q: TsStackGraphsHintedQuery = { - callerFile: "simple-import.ts", - calleeName: "signIn", - provider: TS_PROVIDER, - referenceLine: 4, - referenceColumn: 2, - }; - const out = stackGraphsTsResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.9); - assert.ok( - out[0]?.targetId.includes("signIn"), - `expected targetId to include signIn, got ${out[0]?.targetId}`, - ); - clearTsStackGraphsForTests(); -}); - -test("barrel re-export: consumer → index → auth/signIn resolves at 0.9 (beats 0.5 global fallback)", () => { - clearTsStackGraphsForTests(); - const signInMod: TsModuleFacts = { - file: "auth/signIn.ts", - moduleKey: "auth/signIn", - imports: [], - exports: [{ kind: "named-local", name: "signIn", line: 1 }], - localDefinitions: [{ name: "signIn", line: 1 }], - references: [], - resolveModule: () => null, - }; - const indexMod: TsModuleFacts = { - file: "barrel/index.ts", - moduleKey: "barrel/index", - imports: [], - exports: [ - { - kind: "named-reexport", - name: "signIn", - imported: "signIn", - module: "./auth/signIn", - line: 1, - }, - ], - localDefinitions: [], - references: [], - resolveModule: makeResolveModule({ "./auth/signIn": "auth/signIn" }), - }; - const consumer: TsModuleFacts = { - file: "consumer.ts", - moduleKey: "consumer", - imports: [ - { - kind: "named", - name: "signIn", - local: "signIn", - module: "./barrel", - line: 1, - }, - ], - exports: [], - localDefinitions: [{ name: "main", line: 3 }], - references: [{ name: "signIn", line: 4, column: 2 }], - resolveModule: makeResolveModule({ "./barrel": "barrel/index" }), - }; - buildAndRegister([signInMod, indexMod, consumer]); - - const q: TsStackGraphsHintedQuery = { - callerFile: "consumer.ts", - calleeName: "signIn", - provider: TS_PROVIDER, - referenceLine: 4, - referenceColumn: 2, - }; - const out = stackGraphsTsResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1, "expected a stack-graphs hit, not fallback"); - assert.equal(out[0]?.confidence, 0.9, "barrel chain should score 0.9, not 0.5 global"); - clearTsStackGraphsForTests(); -}); - -test("star re-export: export * from './user' lets consumer resolve createUser", () => { - clearTsStackGraphsForTests(); - const userMod: TsModuleFacts = { - file: "user.ts", - moduleKey: "user", - imports: [], - exports: [{ kind: "named-local", name: "createUser", line: 1 }], - localDefinitions: [{ name: "createUser", line: 1 }], - references: [], - resolveModule: () => null, - }; - const indexMod: TsModuleFacts = { - file: "barrel-star/index.ts", - moduleKey: "barrel-star/index", - imports: [], - exports: [{ kind: "star-reexport", module: "./user", line: 1 }], - localDefinitions: [], - references: [], - resolveModule: makeResolveModule({ "./user": "user" }), - }; - const consumer: TsModuleFacts = { - file: "consumer.ts", - moduleKey: "consumer", - imports: [ - { - kind: "named", - name: "createUser", - local: "createUser", - module: "./barrel-star", - line: 1, - }, - ], - exports: [], - localDefinitions: [{ name: "main", line: 3 }], - references: [{ name: "createUser", line: 4, column: 2 }], - resolveModule: makeResolveModule({ "./barrel-star": "barrel-star/index" }), - }; - buildAndRegister([userMod, indexMod, consumer]); - - const q: TsStackGraphsHintedQuery = { - callerFile: "consumer.ts", - calleeName: "createUser", - provider: TS_PROVIDER, - referenceLine: 4, - referenceColumn: 2, - }; - const out = stackGraphsTsResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.9); - clearTsStackGraphsForTests(); -}); - -test('default export: consumer `import foo from "./m"` resolves via default slot', () => { - clearTsStackGraphsForTests(); - const target: TsModuleFacts = { - file: "default-export.ts", - moduleKey: "default-export", - imports: [], - exports: [{ kind: "default-local", target: "foo", line: 1 }], - localDefinitions: [{ name: "foo", line: 1 }], - references: [], - resolveModule: () => null, - }; - const consumer: TsModuleFacts = { - file: "default-export-consumer.ts", - moduleKey: "default-export-consumer", - imports: [{ kind: "default", local: "foo", module: "./default-export", line: 1 }], - exports: [], - localDefinitions: [{ name: "main", line: 3 }], - references: [{ name: "foo", line: 4, column: 9 }], - resolveModule: makeResolveModule({ "./default-export": "default-export" }), - }; - buildAndRegister([target, consumer]); - - const q: TsStackGraphsHintedQuery = { - callerFile: "default-export-consumer.ts", - calleeName: "foo", - provider: TS_PROVIDER, - referenceLine: 4, - referenceColumn: 9, - }; - const out = stackGraphsTsResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.9); - // The resolution terminates at the local import pop; its definitionTarget - // advertises the "default" slot of the remote module. - assert.ok( - out[0]?.targetId.includes("default"), - `expected targetId to reference default slot, got ${out[0]?.targetId}`, - ); - clearTsStackGraphsForTests(); -}); - -test("namespace import: import * as ns resolves ns reference to remote wildcard slot", () => { - clearTsStackGraphsForTests(); - const target: TsModuleFacts = { - file: "ns-target.ts", - moduleKey: "ns-target", - imports: [], - exports: [{ kind: "named-local", name: "greet", line: 1 }], - localDefinitions: [{ name: "greet", line: 1 }], - references: [], - resolveModule: () => null, - }; - const consumer: TsModuleFacts = { - file: "ns-consumer.ts", - moduleKey: "ns-consumer", - imports: [{ kind: "namespace", local: "ns", module: "./ns-target", line: 1 }], - exports: [], - localDefinitions: [{ name: "main", line: 3 }], - references: [{ name: "ns", line: 4, column: 2 }], - resolveModule: makeResolveModule({ "./ns-target": "ns-target" }), - }; - buildAndRegister([target, consumer]); - - const q: TsStackGraphsHintedQuery = { - callerFile: "ns-consumer.ts", - calleeName: "ns", - provider: TS_PROVIDER, - referenceLine: 4, - referenceColumn: 2, - }; - const out = stackGraphsTsResolver.resolve(q, makeIndex()); - assert.equal(out.length, 1); - assert.equal(out[0]?.confidence, 0.9); - assert.ok( - out[0]?.targetId.includes("ns-target"), - `expected targetId to reference ns-target, got ${out[0]?.targetId}`, - ); - clearTsStackGraphsForTests(); -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs-ts.ts b/packages/ingestion/src/providers/resolution/stack-graphs-ts.ts deleted file mode 100644 index 9eae863d..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs-ts.ts +++ /dev/null @@ -1,556 +0,0 @@ -// TypeScript / TSX / JavaScript ResolverStrategy backed by the clean-room -// stack-graphs evaluator. Mirrors `stack-graphs-python.ts` but handles the -// TS-family import/export shapes instead of Python's from/import grammar. -// -// Shapes modelled here: -// * `import { foo } from "./bar"` — named import. -// * `import foo from "./bar"` — default import (binds local). -// * `import * as ns from "./bar"` — namespace import (member push). -// * `import type { X } from "./y"` — type-only import, recorded. -// * `export { x } from "./bar"` — named re-export. -// * `export { default as foo } from "./b"` — default re-export renamed. -// * `export * from "./bar"` — barrel-star re-export. -// * `export default fn|expr` — default export pop. -// * `export function foo` — named export pop. -// -// We share `stack-graphs/partial-path-engine.ts` for the BFS; the node/edge -// shape is different from the Python builder though, so we keep our own -// builder inline rather than extending the shared Python builder. -// -// Like the Python strategy we register under the name `"stack-graphs"`. The -// router in `resolver-strategy.ts` dispatches by `provider.id`, so both -// strategies coexist under the same opt-in name. - -import type { ResolutionCandidate, ResolutionQuery, SymbolIndex } from "./context.js"; -import { CONFIDENCE_BY_TIER, resolve as threeTierResolve } from "./context.js"; -import type { ResolverStrategy } from "./resolver-strategy.js"; -import { resolveReference } from "./stack-graphs/partial-path-engine.js"; -import type { - NodeId, - ReferenceQuery, - StackGraph, - StackGraphEdge, - StackGraphNode, - StackNodeKind, -} from "./stack-graphs/types.js"; -import { STACK_GRAPHS_HIT_CONFIDENCE } from "./stack-graphs/types.js"; - -/** - * Per-reference lookup info — the ingestion pipeline attaches (line, column) - * to the query when delegating to stack-graphs. Mirrors the Python hinted - * query type. - */ -export interface TsStackGraphsHintedQuery extends ResolutionQuery { - readonly referenceLine?: number; - readonly referenceColumn?: number; -} - -interface TsStackGraphStore { - readonly graphs: Map; - fallbacks: number; - stackGraphHits: number; -} - -const TS_STORE: TsStackGraphStore = { - graphs: new Map(), - fallbacks: 0, - stackGraphHits: 0, -}; - -/** - * Prime the TS cache with per-file stack graphs. Called by the ingestion - * parse phase before resolution runs. Uses a store separate from Python's. - */ -export function registerTsStackGraphs(graphs: ReadonlyMap): void { - TS_STORE.graphs.clear(); - for (const [k, v] of graphs) TS_STORE.graphs.set(k, v); -} - -/** For tests: drop all TS graphs and reset counters. */ -export function clearTsStackGraphsForTests(): void { - TS_STORE.graphs.clear(); - TS_STORE.fallbacks = 0; - TS_STORE.stackGraphHits = 0; -} - -/** Stats surfaced to the pipeline for telemetry. */ -export function getTsStackGraphsStats(): { - readonly fallbacks: number; - readonly hits: number; -} { - return { fallbacks: TS_STORE.fallbacks, hits: TS_STORE.stackGraphHits }; -} - -/** Does this query belong to a TS-family provider? */ -function isTsFamilyQuery(q: ResolutionQuery): boolean { - const id = q.provider.id; - return id === "typescript" || id === "tsx" || id === "javascript"; -} - -function mapTargetKeyToResolutionId(targetKey: string): string { - // targetKey is `${file}:${line}:${qualifiedName}`. Opaque to callers — the - // storage layer only cares that it's unique. - return targetKey; -} - -function runStackGraphs(q: TsStackGraphsHintedQuery): ResolutionCandidate | null { - if (TS_STORE.graphs.size === 0) return null; - const line = q.referenceLine; - const column = q.referenceColumn; - if (line === undefined || column === undefined) return null; - - const graph = TS_STORE.graphs.get(q.callerFile); - if (graph === undefined) return null; - const refKey = `${line}:${column}`; - const refNodeId = graph.referenceIndex.get(refKey); - if (refNodeId === undefined) return null; - - const ref: ReferenceQuery = { - file: q.callerFile, - line, - column, - name: q.calleeName, - }; - void ref; // carried for parity with the Python strategy signature - - try { - const { results } = resolveReference(TS_STORE.graphs, q.callerFile, refNodeId); - const best = results[0]; - if (best === undefined) return null; - return { - targetId: mapTargetKeyToResolutionId(best.targetKey), - tier: "import-scoped", - confidence: STACK_GRAPHS_HIT_CONFIDENCE, - }; - } catch { - return null; - } -} - -export const stackGraphsTsResolver: ResolverStrategy = { - name: "stack-graphs", - resolve(q: ResolutionQuery, index: SymbolIndex): ResolutionCandidate[] { - if (!isTsFamilyQuery(q)) { - return threeTierResolve(q, index); - } - const hinted = q as TsStackGraphsHintedQuery; - const hit = runStackGraphs(hinted); - if (hit !== null) { - TS_STORE.stackGraphHits++; - const clamped: ResolutionCandidate = { - targetId: hit.targetId, - tier: hit.tier, - confidence: Math.max(hit.confidence, CONFIDENCE_BY_TIER["import-scoped"]), - }; - return [clamped]; - } - TS_STORE.fallbacks++; - return threeTierResolve(q, index); - }, -}; - -// --------------------------------------------------------------------------- -// Builder — TS-family source → StackGraph -// --------------------------------------------------------------------------- - -/** - * Minimal import/export IR. The pipeline's parse phase produces this shape - * from the TS tree-sitter CST; tests can also hand-author it. Keeping the - * builder driven by a plain IR (rather than a tree-sitter node adapter) - * means we don't need to fake the full SyntaxNode surface for every test - * fixture — unlike the Python builder which consumes `MinimalTsNode`. - */ -export type TsImportSpec = - | { - readonly kind: "named"; - readonly name: string; - readonly local: string; - readonly module: string; - readonly typeOnly?: boolean; - readonly line: number; - } - | { - readonly kind: "default"; - readonly local: string; - readonly module: string; - readonly typeOnly?: boolean; - readonly line: number; - } - | { - readonly kind: "namespace"; - readonly local: string; - readonly module: string; - readonly typeOnly?: boolean; - readonly line: number; - }; - -export type TsExportSpec = - | { readonly kind: "named-local"; readonly name: string; readonly line: number } - | { readonly kind: "default-local"; readonly target: string; readonly line: number } - | { - readonly kind: "named-reexport"; - readonly name: string; - readonly imported: string; - readonly module: string; - readonly line: number; - } - | { - readonly kind: "default-reexport-as"; - readonly name: string; - readonly module: string; - readonly line: number; - } - | { readonly kind: "star-reexport"; readonly module: string; readonly line: number }; - -/** - * A reference site inside the module — typically an identifier the call - * extractor already located. The builder emits one push-node per reference - * so the resolver can look up (line, column). - */ -export interface TsReferenceSite { - readonly name: string; - readonly line: number; - readonly column: number; -} - -/** - * All the per-file facts the builder needs. `moduleKey` is the canonical - * identifier used both as the `graphs` map key and as the resolution target - * for cross-file ROOT hops. Typically the absolute file path without the - * extension; callers decide the shape as long as it's consistent with - * `resolveModule`. - */ -export interface TsModuleFacts { - readonly file: string; - readonly moduleKey: string; - readonly imports: readonly TsImportSpec[]; - readonly exports: readonly TsExportSpec[]; - readonly localDefinitions: readonly { readonly name: string; readonly line: number }[]; - readonly references: readonly TsReferenceSite[]; - /** - * Resolve an import specifier (e.g. `"./bar"`) to a `moduleKey` string - * matching another `TsModuleFacts.moduleKey`. Returning `null` means the - * import target is unknown — the builder will still emit the push-chain - * but cross-file traversal won't hop. - */ - resolveModule(specifier: string): string | null; -} - -interface MutableGraph { - readonly file: string; - readonly nodes: Map; - readonly edges: StackGraphEdge[]; - readonly referenceIndex: Map; - readonly rootNodeId: NodeId; - readonly moduleScopeId: NodeId; - /** Per-symbol pops that live on the module scope (local exports). */ - readonly moduleExportPops: Map; - seq: number; -} - -function nextId(g: MutableGraph, kind: StackNodeKind): NodeId { - g.seq++; - return `${g.file}#${kind}-${g.seq}`; -} - -function addNode( - g: MutableGraph, - kind: StackNodeKind, - opts: { - readonly symbol?: string; - readonly definitionTarget?: string; - readonly line?: number; - } = {}, -): NodeId { - const id = nextId(g, kind); - const base = { id, kind, file: g.file } as const; - const node: StackGraphNode = { - ...base, - ...(opts.symbol !== undefined ? { symbol: opts.symbol } : {}), - ...(opts.definitionTarget !== undefined ? { definitionTarget: opts.definitionTarget } : {}), - ...(opts.line !== undefined ? { line: opts.line } : {}), - }; - g.nodes.set(id, node); - return id; -} - -function addEdge(g: MutableGraph, source: NodeId, target: NodeId, precedence = 0): void { - g.edges.push({ source, target, precedence }); -} - -/** - * Build a stack graph for a single TS-family module from an import/export - * IR. The graph shape: - * - * * module-scope is a `scope` node. ROOT points at it (so external - * lookups hitting this module begin there). - * * Local definitions are `pop` nodes hanging off module-scope, precedence 2. - * * Named imports create a local `pop` for the binding plus a push chain - * `[binding-name, moduleKey]` terminating at ROOT. - * * Default imports are identical to named imports with `imported = "default"`. - * * Namespace imports create a `pop` at the local name whose continuation - * chains straight to ROOT of the target module (the member push is - * generated at the reference site — see `references`). - * * Star re-exports emit a precedence-1 scope edge mirroring Python's - * wildcard handling. - * * Named re-exports create a local `pop` whose target is the remote - * module's export with the same (or renamed-from) name. - * * Default re-exports (`export { default as foo }`) create a local pop - * "foo" targeting the remote default slot. - * * Default exports create a local pop "default" pointing at the target. - */ -export function buildTsStackGraph(facts: TsModuleFacts): StackGraph { - const rootNodeId: NodeId = `${facts.file}#root`; - const moduleScopeId: NodeId = `${facts.file}#module-scope`; - const graph: MutableGraph = { - file: facts.file, - nodes: new Map(), - edges: [], - referenceIndex: new Map(), - rootNodeId, - moduleScopeId, - moduleExportPops: new Map(), - seq: 0, - }; - graph.nodes.set(rootNodeId, { id: rootNodeId, kind: "root", file: facts.file }); - graph.nodes.set(moduleScopeId, { id: moduleScopeId, kind: "scope", file: facts.file }); - // External queries enter via ROOT — ROOT → module-scope so symbol lookups - // can traverse pops attached to module-scope. - addEdge(graph, rootNodeId, moduleScopeId, 0); - - emitLocalDefinitions(graph, facts.localDefinitions); - emitImports(graph, facts); - emitExports(graph, facts); - emitReferences(graph, facts); - - return { - file: facts.file, - nodes: new Map(graph.nodes), - edges: graph.edges.slice(), - rootNodeId: graph.rootNodeId, - referenceIndex: new Map(graph.referenceIndex), - }; -} - -function emitLocalDefinitions( - g: MutableGraph, - defs: readonly { readonly name: string; readonly line: number }[], -): void { - for (const def of defs) { - // A pop node that terminates a successful lookup for `name` inside this - // module. Kept on module-scope with precedence 2 so local hits outrank - // ambient re-exports (precedence 1) when both match. - const defNode = addNode(g, "pop", { - symbol: def.name, - definitionTarget: def.name, - line: def.line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - g.moduleExportPops.set(def.name, defNode); - } -} - -function emitImports(g: MutableGraph, facts: TsModuleFacts): void { - for (const imp of facts.imports) { - const targetKey = facts.resolveModule(imp.module); - switch (imp.kind) { - case "named": - bindImport(g, imp.local, imp.name, targetKey, imp.module, imp.line); - break; - case "default": - bindImport(g, imp.local, "default", targetKey, imp.module, imp.line); - break; - case "namespace": - bindNamespaceImport(g, imp.local, targetKey, imp.module, imp.line); - break; - } - } -} - -/** - * Emit the pop + push chain for a named or default import. The pop binds - * the local name; a push chain of `[imported-name, targetKey]` terminates - * at ROOT so the traversal hops to the module graph that provides the - * definition. - */ -function bindImport( - g: MutableGraph, - local: string, - imported: string, - targetKey: string | null, - specifier: string, - line: number, -): void { - const definitionTarget = - targetKey !== null ? `${targetKey}.${imported}` : `${specifier}.${imported}`; - const defNode = addNode(g, "pop", { - symbol: local, - definitionTarget, - line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - - // Push the imported name first so after the pop we expect `[imported, targetKey]` - // on the stack; the ROOT hop then emits a push for `targetKey` (the module - // identifier) and lands on the target graph's ROOT. - const pushImported = addNode(g, "push", { symbol: imported, line }); - addEdge(g, defNode, pushImported, 0); - let cursor = pushImported; - if (targetKey !== null) { - const pushModule = addNode(g, "push", { symbol: targetKey, line }); - addEdge(g, cursor, pushModule, 0); - cursor = pushModule; - } - addEdge(g, cursor, g.rootNodeId, 0); -} - -/** - * Namespace imports (`import * as ns`) resolve `ns.member` to the target - * module's `member` export. We emit a pop for `ns` plus a push for the - * module key — references of the form `ns.x` show up as two push nodes - * at the reference site (`x` then `ns`) which pop `ns` here, push `x`, - * and hop to ROOT. - */ -function bindNamespaceImport( - g: MutableGraph, - local: string, - targetKey: string | null, - specifier: string, - line: number, -): void { - const definitionTarget = targetKey !== null ? `${targetKey}.*` : `${specifier}.*`; - const defNode = addNode(g, "pop", { - symbol: local, - definitionTarget, - line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - if (targetKey !== null) { - const pushModule = addNode(g, "push", { symbol: targetKey, line }); - addEdge(g, defNode, pushModule, 0); - addEdge(g, pushModule, g.rootNodeId, 0); - } else { - addEdge(g, defNode, g.rootNodeId, 0); - } -} - -function emitExports(g: MutableGraph, facts: TsModuleFacts): void { - for (const exp of facts.exports) { - switch (exp.kind) { - case "named-local": - // Local-only export — already represented by the local definition - // pop on module-scope. If the def wasn't registered, emit a stub so - // external lookups still terminate. - if (!g.moduleExportPops.has(exp.name)) { - const stub = addNode(g, "pop", { - symbol: exp.name, - definitionTarget: exp.name, - line: exp.line, - }); - addEdge(g, g.moduleScopeId, stub, 2); - g.moduleExportPops.set(exp.name, stub); - } - break; - case "default-local": { - const defNode = addNode(g, "pop", { - symbol: "default", - definitionTarget: exp.target, - line: exp.line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - g.moduleExportPops.set("default", defNode); - break; - } - case "named-reexport": - emitNamedReexport(g, facts, exp); - break; - case "default-reexport-as": - emitDefaultReexportAs(g, facts, exp); - break; - case "star-reexport": - emitStarReexport(g, facts, exp); - break; - } - } -} - -/** - * `export { imported as name } from "./bar"` — a local pop for `name` whose - * traversal hops through ROOT to the remote module's `imported` slot. - */ -function emitNamedReexport( - g: MutableGraph, - facts: TsModuleFacts, - exp: Extract, -): void { - const targetKey = facts.resolveModule(exp.module); - const definitionTarget = - targetKey !== null ? `${targetKey}.${exp.imported}` : `${exp.module}.${exp.imported}`; - const defNode = addNode(g, "pop", { - symbol: exp.name, - definitionTarget, - line: exp.line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - g.moduleExportPops.set(exp.name, defNode); - - const pushImported = addNode(g, "push", { symbol: exp.imported, line: exp.line }); - addEdge(g, defNode, pushImported, 0); - let cursor = pushImported; - if (targetKey !== null) { - const pushModule = addNode(g, "push", { symbol: targetKey, line: exp.line }); - addEdge(g, cursor, pushModule, 0); - cursor = pushModule; - } - addEdge(g, cursor, g.rootNodeId, 0); -} - -/** `export { default as foo } from "./bar"` — same as named-reexport of "default". */ -function emitDefaultReexportAs( - g: MutableGraph, - facts: TsModuleFacts, - exp: Extract, -): void { - emitNamedReexport(g, facts, { - kind: "named-reexport", - name: exp.name, - imported: "default", - module: exp.module, - line: exp.line, - }); -} - -/** - * `export * from "./bar"` — every export in `./bar` becomes reachable from - * this module. Mirrors Python's wildcard handling: a scope junction on - * module-scope (precedence 1 so local pops still win on overlap) plus a - * push chain that lands on the remote ROOT. - */ -function emitStarReexport( - g: MutableGraph, - facts: TsModuleFacts, - exp: Extract, -): void { - const targetKey = facts.resolveModule(exp.module); - const junction = addNode(g, "scope", { line: exp.line }); - // Precedence 1 — beaten by local definitions/named re-exports (precedence 2) - // but outranks ordinary fall-through edges. - addEdge(g, g.moduleScopeId, junction, 1); - if (targetKey !== null) { - const pushModule = addNode(g, "push", { symbol: targetKey, line: exp.line }); - addEdge(g, junction, pushModule, 0); - addEdge(g, pushModule, g.rootNodeId, 0); - } else { - addEdge(g, junction, g.rootNodeId, 0); - } -} - -function emitReferences(g: MutableGraph, facts: TsModuleFacts): void { - for (const ref of facts.references) { - const key = `${ref.line}:${ref.column}`; - if (g.referenceIndex.has(key)) continue; - const pushNode = addNode(g, "push", { symbol: ref.name, line: ref.line }); - addEdge(g, pushNode, g.moduleScopeId, 0); - g.referenceIndex.set(key, pushNode); - } -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/all-filter.py b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/all-filter.py deleted file mode 100644 index d7e235ae..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/all-filter.py +++ /dev/null @@ -1,9 +0,0 @@ -__all__ = ["public_func"] - - -def _helper(): - return 1 - - -def public_func(): - return _helper() diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/auth/signIn.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/auth/signIn.ts deleted file mode 100644 index c6bdd7db..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/auth/signIn.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function signIn(user: string): boolean { - return user.length > 0; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/index.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/index.ts deleted file mode 100644 index 791efebb..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-reexport/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { signIn } from "./auth/signIn"; diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/index.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/index.ts deleted file mode 100644 index 7616f9e5..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./user"; diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/user.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/user.ts deleted file mode 100644 index 2bbf8e93..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/barrel-star/user.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function createUser(name: string): { name: string } { - return { name }; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export-consumer.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export-consumer.ts deleted file mode 100644 index 5bcc0183..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export-consumer.ts +++ /dev/null @@ -1,5 +0,0 @@ -import foo from "./default-export"; - -export function main(): string { - return foo("world"); -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export.ts deleted file mode 100644 index 3ad7c325..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/default-export.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default function foo(name: string): string { - return `hello ${name}`; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/mock-tree.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/mock-tree.ts deleted file mode 100644 index 10ac28cb..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/mock-tree.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Lightweight mock of the tree-sitter Parser.SyntaxNode surface we consume. -// Only the handful of methods the builder calls are implemented; the rest -// throw. Fixture trees are hand-assembled in tests. - -import type { MinimalTsNode, MinimalTsTree } from "../node-edge-builder.js"; - -export interface Pos { - readonly row: number; - readonly column: number; -} - -export interface MockNodeSpec { - readonly type: string; - readonly text?: string; - readonly start?: Pos; - readonly end?: Pos; - readonly namedChildren?: readonly MockNodeSpec[]; - /** Named "fields" (accessed via childForFieldName). */ - readonly fields?: Readonly>; -} - -class MockNode implements MinimalTsNode { - private readonly _namedChildren: readonly MockNode[]; - private readonly _fields: ReadonlyMap; - - constructor(private readonly spec: MockNodeSpec) { - this._namedChildren = (spec.namedChildren ?? []).map((c) => new MockNode(c)); - const fields = new Map(); - if (spec.fields !== undefined) { - for (const [k, v] of Object.entries(spec.fields)) { - fields.set(k, new MockNode(v)); - } - } - this._fields = fields; - } - - get type(): string { - return this.spec.type; - } - get text(): string { - return this.spec.text ?? ""; - } - get startPosition(): Pos { - return this.spec.start ?? { row: 0, column: 0 }; - } - get endPosition(): Pos { - return this.spec.end ?? this.startPosition; - } - get childCount(): number { - return this._namedChildren.length; - } - child(index: number): MinimalTsNode | null { - return this._namedChildren[index] ?? null; - } - childForFieldName(name: string): MinimalTsNode | null { - return this._fields.get(name) ?? null; - } - get namedChildCount(): number { - return this._namedChildren.length; - } - namedChild(index: number): MinimalTsNode | null { - return this._namedChildren[index] ?? null; - } -} - -export function mockTree(root: MockNodeSpec): MinimalTsTree { - return { rootNode: new MockNode(root) }; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/relative-import-star.py b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/relative-import-star.py deleted file mode 100644 index 61e030cd..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/relative-import-star.py +++ /dev/null @@ -1,3 +0,0 @@ -from .auth import * - -signIn("bob") diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import-target.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import-target.ts deleted file mode 100644 index c6bdd7db..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import-target.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function signIn(user: string): boolean { - return user.length > 0; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.py b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.py deleted file mode 100644 index 5770dbe2..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.py +++ /dev/null @@ -1,3 +0,0 @@ -from foo import bar - -bar() diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.ts b/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.ts deleted file mode 100644 index e28dc373..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/__fixtures__/simple-import.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { signIn } from "./simple-import-target"; - -export function main(): void { - signIn("alice"); -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/glue.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs/glue.test.ts deleted file mode 100644 index 5a970bb1..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/glue.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; -import { test } from "node:test"; -import { type MockNodeSpec, mockTree } from "./__fixtures__/mock-tree.js"; -import { - buildAllStackGraphs, - loadRules, - resetRulesForTests, - resolveViaStackGraphs, -} from "./glue.js"; - -function fromImport( - moduleName: string, - names: readonly string[], - opts: { wildcard?: boolean } = {}, -): MockNodeSpec { - const segs = moduleName - .split(".") - .filter((s) => s.length > 0) - .map((s) => ({ type: "identifier", text: s })); - const moduleField: MockNodeSpec = - moduleName === "." - ? { - type: "relative_import", - namedChildren: [{ type: "import_prefix", text: "." }], - } - : { type: "dotted_name", text: moduleName, namedChildren: segs }; - - const children: MockNodeSpec[] = [moduleField]; - if (opts.wildcard === true) { - children.push({ type: "wildcard_import", text: "*" }); - } else { - for (const name of names) { - children.push({ - type: "dotted_name", - text: name, - namedChildren: [{ type: "identifier", text: name }], - }); - } - } - return { - type: "import_from_statement", - text: `from ${moduleName} import ${names.join(",")}`, - fields: { module_name: moduleField }, - namedChildren: children, - }; -} - -function callRef(name: string, row: number, col: number): MockNodeSpec { - return { - type: "expression_statement", - namedChildren: [ - { - type: "call", - namedChildren: [ - { - type: "identifier", - text: name, - start: { row, column: col }, - end: { row, column: col + name.length }, - }, - ], - }, - ], - }; -} - -function funcDef(name: string, body: readonly MockNodeSpec[]): MockNodeSpec { - return { - type: "function_definition", - text: `def ${name}()`, - fields: { - name: { type: "identifier", text: name }, - body: { type: "block", namedChildren: body }, - }, - namedChildren: [ - { type: "identifier", text: name }, - { type: "block", namedChildren: body }, - ], - }; -} - -// Load the real tsg rule set once per test module. -function loadRealRules(): readonly import("./types.js").TsgRule[] { - resetRulesForTests(); - const src = readFileSync( - new URL("../../../../../../vendor/stack-graphs-python/rules/stack-graphs.tsg", import.meta.url), - "utf8", - ); - return loadRules(src).rules; -} - -test("glue: simple `from foo import bar` → bar call resolves to foo.bar", () => { - const rules = loadRealRules(); - const consumer = mockTree({ - type: "module", - namedChildren: [fromImport("foo", ["bar"]), funcDef("caller", [callRef("bar", 4, 0)])], - }); - // We also need a graph for the module `foo` so the ROOT hop has a - // destination. Simulate `foo.py` containing `def bar(): ...`. - const fooModule = mockTree({ - type: "module", - namedChildren: [funcDef("bar", [])], - }); - const graphs = buildAllStackGraphs( - new Map([ - ["consumer.py", consumer], - ["foo.py", fooModule], - ]), - rules, - ); - const { results } = resolveViaStackGraphs( - { file: "consumer.py", line: 5, column: 0, name: "bar" }, - graphs, - ); - // At minimum the within-file pop should be found (the local binding of - // `bar`). The best (shortest) result targets the local pop bound by the - // from-import. - assert.ok(results.length >= 1, "expected at least one resolution"); - const targets = results.map((r) => r.targetKey); - assert.ok( - targets.some((t) => t.includes("foo.bar")), - `expected a target containing foo.bar, got ${targets.join(" | ")}`, - ); -}); - -test("glue: relative wildcard import chain resolves signIn to the child module", () => { - // Fixture replicates the re-export chain from research case 1: - // pkg/__init__.py : from .auth import * - // pkg/auth.py : from .login import signIn - // pkg/login.py : def signIn(): ... - // consumer.py : from pkg import signIn ; signIn("bob") - const rules = loadRealRules(); - const consumer = mockTree({ - type: "module", - namedChildren: [fromImport("pkg", ["signIn"]), funcDef("main", [callRef("signIn", 4, 0)])], - }); - const pkgInit = mockTree({ - type: "module", - namedChildren: [fromImport(".", ["auth"])], - }); - const auth = mockTree({ - type: "module", - namedChildren: [fromImport(".", ["signIn"])], - }); - const login = mockTree({ - type: "module", - namedChildren: [funcDef("signIn", [])], - }); - const graphs = buildAllStackGraphs( - new Map([ - ["consumer.py", consumer], - ["pkg/__init__.py", pkgInit], - ["pkg/auth.py", auth], - ["pkg/login.py", login], - ]), - rules, - ); - const { results } = resolveViaStackGraphs( - { file: "consumer.py", line: 5, column: 0, name: "signIn" }, - graphs, - ); - // Success: stack-graphs produced a resolution that mentions signIn in the - // chain (the local from-import pop, which targets `pkg.signIn`). The key - // guarantee vs the 3-tier resolver is that we resolve to something - // specific rather than a 0.5 global grep over every `signIn` in the repo. - assert.ok(results.length > 0, "expected at least one resolution"); - assert.ok( - results.some((r) => r.targetKey.includes("signIn")), - `expected a signIn target, got ${results.map((r) => r.targetKey).join(" | ")}`, - ); -}); - -test("glue: missing graph for queried file returns empty results (no throw)", () => { - const rules = loadRealRules(); - const tree = mockTree({ type: "module", namedChildren: [] }); - const graphs = buildAllStackGraphs(new Map([["other.py", tree]]), rules); - const { results } = resolveViaStackGraphs( - { file: "missing.py", line: 1, column: 0, name: "x" }, - graphs, - ); - assert.deepEqual(results, []); -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/glue.ts b/packages/ingestion/src/providers/resolution/stack-graphs/glue.ts deleted file mode 100644 index 0933a88d..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/glue.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Glue layer that wires rule-parser → builder → engine into one entry point. -// -// Exposed as a small function set so the per-language resolver strategy can -// build graphs lazily per file and hand them to the partial-path engine as -// references get resolved. Nothing here knows about the ResolverStrategy -// interface — that lives in `stack-graphs-python.ts`. - -import { buildStackGraph, type MinimalTsTree } from "./node-edge-builder.js"; -import { resolveReference } from "./partial-path-engine.js"; -import { type ParsedTsg, parseTsg } from "./rule-parser.js"; -import type { PartialPathResult, ReferenceQuery, StackGraph, TsgRule } from "./types.js"; - -/** Cache key: SHA-less; in v1 we only load the rule file once per process. */ -interface RuleCache { - parsed: ParsedTsg | null; -} - -const RULES: RuleCache = { parsed: null }; - -/** Parse the rule file text once. Safe to call repeatedly. */ -export function loadRules(source: string): ParsedTsg { - if (RULES.parsed !== null) return RULES.parsed; - RULES.parsed = parseTsg(source); - return RULES.parsed; -} - -/** For tests: clear the memoized rule cache. */ -export function resetRulesForTests(): void { - RULES.parsed = null; -} - -/** - * Build one stack-graph per file up front. Callers normally feed in every - * Python file in the index so cross-module resolution can hop through ROOT. - */ -export function buildAllStackGraphs( - files: ReadonlyMap, - rules: readonly TsgRule[], -): ReadonlyMap { - const out = new Map(); - for (const [file, tree] of files) { - out.set(file, buildStackGraph(file, tree, rules)); - } - return out; -} - -/** Resolve a single reference, returning path results and truncation info. */ -export function resolveViaStackGraphs( - query: ReferenceQuery, - graphs: ReadonlyMap, -): PartialPathResult { - const graph = graphs.get(query.file); - if (graph === undefined) return { results: [], truncated: false }; - const key = `${query.line}:${query.column}`; - const refNodeId = graph.referenceIndex.get(key); - if (refNodeId === undefined) return { results: [], truncated: false }; - return resolveReference(graphs, query.file, refNodeId); -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.test.ts deleted file mode 100644 index 0da02d68..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import assert from "node:assert/strict"; -import { test } from "node:test"; -import { type MockNodeSpec, mockTree } from "./__fixtures__/mock-tree.js"; -import { buildStackGraph } from "./node-edge-builder.js"; -import type { TsgRule } from "./types.js"; - -// Minimal rule set that authorises every Python construct our builder uses. -const AUTHORISING_RULES: readonly TsgRule[] = [ - { patterns: [{ kind: "pattern", nodeType: "module" }], actions: [] }, - { patterns: [{ kind: "pattern", nodeType: "import_statement" }], actions: [] }, - { patterns: [{ kind: "pattern", nodeType: "import_from_statement" }], actions: [] }, - { patterns: [{ kind: "pattern", nodeType: "function_definition" }], actions: [] }, - { patterns: [{ kind: "pattern", nodeType: "class_definition" }], actions: [] }, - { patterns: [{ kind: "pattern", nodeType: "identifier" }], actions: [] }, -]; - -function fromImport( - moduleName: string, - names: readonly string[], - opts: { wildcard?: boolean } = {}, -): MockNodeSpec { - const children: MockNodeSpec[] = [ - { - type: "dotted_name", - text: moduleName, - namedChildren: moduleName.split(".").map((seg) => ({ type: "identifier", text: seg })), - }, - ]; - if (opts.wildcard === true) { - children.push({ type: "wildcard_import", text: "*" }); - } else { - for (const name of names) { - children.push({ - type: "dotted_name", - text: name, - namedChildren: [{ type: "identifier", text: name }], - }); - } - } - return { - type: "import_from_statement", - text: `from ${moduleName} import ${names.join(",")}`, - fields: { - module_name: { - type: "dotted_name", - text: moduleName, - namedChildren: moduleName.split(".").map((seg) => ({ type: "identifier", text: seg })), - }, - }, - namedChildren: children, - }; -} - -function funcDef(name: string, body: readonly MockNodeSpec[] = []): MockNodeSpec { - return { - type: "function_definition", - text: `def ${name}()`, - fields: { - name: { type: "identifier", text: name }, - body: { type: "block", namedChildren: body }, - }, - namedChildren: [ - { type: "identifier", text: name }, - { type: "block", namedChildren: body }, - ], - }; -} - -test("buildStackGraph: from foo import bar produces pop for bar + push chain to ROOT", () => { - const tree = mockTree({ - type: "module", - namedChildren: [fromImport("foo", ["bar"])], - }); - const graph = buildStackGraph("mod.py", tree, AUTHORISING_RULES); - const pops = [...graph.nodes.values()].filter((n) => n.kind === "pop"); - assert.ok( - pops.some((p) => p.symbol === "bar" && p.definitionTarget === "foo.bar"), - "expected a pop node for local-bound 'bar'", - ); - const pushes = [...graph.nodes.values()].filter((n) => n.kind === "push"); - assert.ok(pushes.some((p) => p.symbol === "bar")); - assert.ok(pushes.some((p) => p.symbol === "foo")); - // Exactly one edge to ROOT. - const toRoot = graph.edges.filter((e) => e.target === graph.rootNodeId); - assert.ok(toRoot.length >= 1); -}); - -test("buildStackGraph: references inside function bodies land in the index", () => { - const tree = mockTree({ - type: "module", - namedChildren: [ - fromImport("foo", ["bar"]), - funcDef("caller", [ - { - type: "expression_statement", - namedChildren: [ - { - type: "call", - namedChildren: [ - { - type: "identifier", - text: "bar", - start: { row: 4, column: 0 }, - end: { row: 4, column: 3 }, - }, - ], - }, - ], - }, - ]), - ], - }); - const graph = buildStackGraph("mod.py", tree, AUTHORISING_RULES); - const key = "5:0"; // row 4 -> line 5 (1-indexed) - const refId = graph.referenceIndex.get(key); - assert.ok(refId !== undefined, "expected a reference node at line 5 col 0"); - const refNode = graph.nodes.get(refId); - assert.equal(refNode?.symbol, "bar"); -}); - -test("buildStackGraph: wildcard import emits precedence-1 scope edge", () => { - const tree = mockTree({ - type: "module", - namedChildren: [fromImport("auth", [], { wildcard: true })], - }); - const graph = buildStackGraph("pkg/__init__.py", tree, AUTHORISING_RULES); - const hasScope = [...graph.nodes.values()].some((n) => n.kind === "scope"); - assert.ok(hasScope); -}); - -test("buildStackGraph: unauthorised rule set returns empty graph (fallback)", () => { - const tree = mockTree({ - type: "module", - namedChildren: [fromImport("foo", ["bar"])], - }); - const graph = buildStackGraph("mod.py", tree, []); - // Only root + module-scope synthesised; no import chain. - const pops = [...graph.nodes.values()].filter((n) => n.kind === "pop"); - assert.equal(pops.length, 0); -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.ts b/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.ts deleted file mode 100644 index d51611c1..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/node-edge-builder.ts +++ /dev/null @@ -1,451 +0,0 @@ -// Python-targeted stack-graph builder. -// -// Given a tree-sitter CST for a Python file this module produces a stack -// graph whose structure mirrors the intent of the vendored Python tsg rules -// without reusing their implementation. We inspect the parsed rule file -// only to guard our visitor: if the rule file lacks a pattern for a given -// Python construct (e.g. `module`) we skip synthesising nodes for it, so -// our graph never claims more structure than the vendored rules authorise. -// -// The graph we emit captures four shapes that drive Python re-export -// resolution: -// 1. module root scope + root entry/exit -// 2. top-level definitions (function / class / const) as pop nodes -// 3. import forms (import, from-import, wildcard) producing push/pop -// chains and ROOT edges -// 4. reference sites inside function bodies (call / attribute) as push -// nodes keyed by (line, column) so the resolver can look them up -// -// We intentionally do not model function-local scope machinery or scoped -// symbols — Python's class/method resolution in OpenCodeHub is handled by -// the C3 MRO walker; stack-graphs here specifically targets cross-module -// and re-export resolution. - -import type { - NodeId, - StackGraph, - StackGraphEdge, - StackGraphNode, - StackNodeKind, - TsgRule, -} from "./types.js"; - -/** - * Minimal view of a tree-sitter node — duck-typed so we can pass either a - * real `Parser.SyntaxNode` from tree-sitter or a test fixture. Only the - * fields we use are declared; that lets fixtures omit the dozens of other - * properties. - */ -export interface MinimalTsNode { - readonly type: string; - readonly text: string; - readonly startPosition: { readonly row: number; readonly column: number }; - readonly endPosition: { readonly row: number; readonly column: number }; - readonly childCount: number; - child(index: number): MinimalTsNode | null; - childForFieldName(name: string): MinimalTsNode | null; - readonly namedChildCount: number; - namedChild(index: number): MinimalTsNode | null; -} - -export interface MinimalTsTree { - readonly rootNode: MinimalTsNode; -} - -interface MutableGraph { - readonly file: string; - readonly nodes: Map; - readonly edges: StackGraphEdge[]; - readonly referenceIndex: Map; - readonly rootNodeId: NodeId; - readonly moduleScopeId: NodeId; - seq: number; -} - -function nextId(g: MutableGraph, kind: StackNodeKind): NodeId { - g.seq++; - return `${g.file}#${kind}-${g.seq}`; -} - -function addNode( - g: MutableGraph, - kind: StackNodeKind, - opts: { - readonly symbol?: string; - readonly definitionTarget?: string; - readonly line?: number; - } = {}, -): NodeId { - const id = nextId(g, kind); - const base = { id, kind, file: g.file } as const; - const node: StackGraphNode = { - ...base, - ...(opts.symbol !== undefined ? { symbol: opts.symbol } : {}), - ...(opts.definitionTarget !== undefined ? { definitionTarget: opts.definitionTarget } : {}), - ...(opts.line !== undefined ? { line: opts.line } : {}), - }; - g.nodes.set(id, node); - return id; -} - -function addEdge(g: MutableGraph, source: NodeId, target: NodeId, precedence = 0): void { - g.edges.push({ source, target, precedence }); -} - -/** Iterate every named descendant breadth-first. Stable order. */ -function* walkNamed(root: MinimalTsNode): IterableIterator { - const queue: MinimalTsNode[] = [root]; - while (queue.length > 0) { - const cur = queue.shift(); - if (cur === undefined) continue; - yield cur; - for (let i = 0; i < cur.namedChildCount; i++) { - const c = cur.namedChild(i); - if (c !== null) queue.push(c); - } - } -} - -function positionKey(line: number, column: number): string { - return `${line}:${column}`; -} - -/** Build a graph for a single Python file. */ -export function buildStackGraph( - file: string, - tree: MinimalTsTree, - rules: readonly TsgRule[], -): StackGraph { - const rootNodeId: NodeId = `${file}#root`; - const moduleScopeId: NodeId = `${file}#module-scope`; - const graph: MutableGraph = { - file, - nodes: new Map(), - edges: [], - referenceIndex: new Map(), - rootNodeId, - moduleScopeId, - seq: 0, - }; - graph.nodes.set(rootNodeId, { id: rootNodeId, kind: "root", file }); - graph.nodes.set(moduleScopeId, { id: moduleScopeId, kind: "scope", file }); - addEdge(graph, rootNodeId, moduleScopeId, 0); - - // Gate visitors on the parsed rule set. A rule firing on `module` means - // the vendored ruleset authorises us to walk modules; missing the rule - // disables our synthesiser for that kind. - const authorised = new Set(); - for (const rule of rules) { - for (const pat of rule.patterns) authorised.add(pat.nodeType); - } - - const root = tree.rootNode; - if (!authorised.has("module") && root.type !== "module") { - // Unknown rule set — return an empty graph so callers can fall back. - return finalise(graph); - } - - visitTopLevel(root, graph, authorised); - return finalise(graph); -} - -function finalise(g: MutableGraph): StackGraph { - return { - file: g.file, - nodes: new Map(g.nodes), - edges: g.edges.slice(), - rootNodeId: g.rootNodeId, - referenceIndex: new Map(g.referenceIndex), - }; -} - -/** Module-level walk — definitions, imports, and function bodies. */ -function visitTopLevel( - moduleNode: MinimalTsNode, - g: MutableGraph, - authorised: ReadonlySet, -): void { - for (let i = 0; i < moduleNode.namedChildCount; i++) { - const child = moduleNode.namedChild(i); - if (child === null) continue; - dispatchStatement(child, g, authorised); - } -} - -function dispatchStatement( - node: MinimalTsNode, - g: MutableGraph, - authorised: ReadonlySet, -): void { - switch (node.type) { - case "import_statement": - if (authorised.has("import_statement")) handleImport(node, g); - break; - case "import_from_statement": - if (authorised.has("import_from_statement")) handleFromImport(node, g); - break; - case "function_definition": - if (authorised.has("function_definition")) handleFunctionDef(node, g, authorised); - break; - case "class_definition": - if (authorised.has("class_definition")) handleClassDef(node, g, authorised); - break; - case "expression_statement": - // Assignment targets like `__all__ = [...]` are interpreted by the - // post-processor; we don't add stack-graph nodes for them. - break; - default: { - // Recurse into decorated defs, if statements, etc., so we still reach - // function/class definitions and reference sites living inside them. - for (const named of walkNamed(node)) { - if (named === node) continue; - if ( - named.type === "function_definition" || - named.type === "class_definition" || - named.type === "import_statement" || - named.type === "import_from_statement" - ) { - dispatchStatement(named, g, authorised); - } - } - } - } -} - -function dottedNameSegments(node: MinimalTsNode): readonly string[] { - if (node.type === "identifier") return [node.text]; - if (node.type === "dotted_name") { - const out: string[] = []; - for (let i = 0; i < node.namedChildCount; i++) { - const c = node.namedChild(i); - if (c === null) continue; - if (c.type === "identifier") out.push(c.text); - } - return out; - } - return [node.text]; -} - -function handleImport(node: MinimalTsNode, g: MutableGraph): void { - // `import foo.bar [as baz]` — bind `foo` (or alias) to the module path. - for (let i = 0; i < node.namedChildCount; i++) { - const child = node.namedChild(i); - if (child === null) continue; - if (child.type === "dotted_name") { - const segments = dottedNameSegments(child); - if (segments.length === 0) continue; - const head = segments[0]; - if (head === undefined) continue; - bindModuleAlias(g, head, segments, node.startPosition.row + 1); - } else if (child.type === "aliased_import") { - const nameChild = child.childForFieldName("name"); - const aliasChild = child.childForFieldName("alias"); - if (nameChild === null) continue; - const segments = dottedNameSegments(nameChild); - const alias = aliasChild?.text ?? segments[0]; - if (alias === undefined) continue; - bindModuleAlias(g, alias, segments, node.startPosition.row + 1); - } - } -} - -function bindModuleAlias( - g: MutableGraph, - alias: string, - targetSegments: readonly string[], - line: number, -): void { - // A local definition that pops the alias symbol and points to ROOT via - // a push chain of the target segments — so when a reference later pushes - // `alias`, the path lands on ROOT with `segments` still to pop. - const defNode = addNode(g, "pop", { - symbol: alias, - definitionTarget: targetSegments.join("."), - line, - }); - addEdge(g, g.moduleScopeId, defNode, 1); - // Push chain: each segment becomes a push node in reverse order so the - // final edge lands on ROOT. - let cursor = defNode; - for (const segment of targetSegments) { - const pushNode = addNode(g, "push", { symbol: segment, line }); - addEdge(g, cursor, pushNode, 0); - cursor = pushNode; - } - addEdge(g, cursor, g.rootNodeId, 0); -} - -function handleFromImport(node: MinimalTsNode, g: MutableGraph): void { - const moduleField = node.childForFieldName("module_name"); - if (moduleField === null) return; - const modulePath = resolveModulePath(moduleField); - if (modulePath === null) return; - - const names = collectImportedNames(node); - const line = node.startPosition.row + 1; - for (const binding of names) { - if (binding.kind === "wildcard") { - // Wildcard: every symbol reachable from the target module's scope - // becomes reachable from ours. We model this with a precedence-1 - // edge from module-scope through a push chain to ROOT. - emitFromImportChain(g, null, modulePath, line, /*precedence*/ 1); - continue; - } - emitFromImportChain(g, binding, modulePath, line, /*precedence*/ 2); - } -} - -interface ImportedName { - readonly kind: "named"; - readonly local: string; - readonly imported: string; -} -interface WildcardName { - readonly kind: "wildcard"; -} - -function collectImportedNames(node: MinimalTsNode): readonly (ImportedName | WildcardName)[] { - const out: (ImportedName | WildcardName)[] = []; - for (let i = 0; i < node.namedChildCount; i++) { - const c = node.namedChild(i); - if (c === null) continue; - if (c.type === "wildcard_import") { - out.push({ kind: "wildcard" }); - continue; - } - // Skip the module_name child — it appears as a named child too. - const moduleField = node.childForFieldName("module_name"); - if (moduleField !== null && c === moduleField) continue; - - if (c.type === "dotted_name") { - const segs = dottedNameSegments(c); - const name = segs[0]; - if (name !== undefined) out.push({ kind: "named", local: name, imported: name }); - } else if (c.type === "aliased_import") { - const nameChild = c.childForFieldName("name"); - const aliasChild = c.childForFieldName("alias"); - if (nameChild === null) continue; - const imported = dottedNameSegments(nameChild)[0]; - const local = aliasChild?.text ?? imported; - if (imported !== undefined && local !== undefined) { - out.push({ kind: "named", local, imported }); - } - } - } - return out; -} - -function resolveModulePath(field: MinimalTsNode): readonly string[] | null { - if (field.type === "dotted_name") return dottedNameSegments(field); - if (field.type === "relative_import") { - // `from . import x` or `from ..pkg import y` — we flatten dots into - // leading empty segments so the resolver can interpret them later. - const segments: string[] = []; - for (let i = 0; i < field.namedChildCount; i++) { - const c = field.namedChild(i); - if (c === null) continue; - if (c.type === "import_prefix") { - for (const _ of c.text) segments.push(""); - } else if (c.type === "dotted_name") { - segments.push(...dottedNameSegments(c)); - } - } - return segments; - } - return null; -} - -function emitFromImportChain( - g: MutableGraph, - binding: ImportedName | null, - modulePath: readonly string[], - line: number, - precedence: number, -): void { - // Definition (pop) for the locally-bound name, or a scope junction for - // wildcards so every push that reaches the module scope forwards on. - const defNode = - binding === null - ? addNode(g, "scope", { line }) - : addNode(g, "pop", { - symbol: binding.local, - definitionTarget: `${modulePath.join(".")}.${binding.imported}`, - line, - }); - addEdge(g, g.moduleScopeId, defNode, precedence); - - // Emit the push chain: imported-name then each module segment in reverse. - let cursor = defNode; - if (binding !== null) { - const pushImported = addNode(g, "push", { symbol: binding.imported, line }); - addEdge(g, cursor, pushImported, 0); - cursor = pushImported; - } - for (const segment of modulePath) { - if (segment === "") continue; // skip relative-prefix placeholders - const pushSegment = addNode(g, "push", { symbol: segment, line }); - addEdge(g, cursor, pushSegment, 0); - cursor = pushSegment; - } - addEdge(g, cursor, g.rootNodeId, 0); -} - -function handleFunctionDef( - node: MinimalTsNode, - g: MutableGraph, - authorised: ReadonlySet, -): void { - const nameChild = node.childForFieldName("name"); - if (nameChild === null) return; - const line = node.startPosition.row + 1; - const defNode = addNode(g, "pop", { - symbol: nameChild.text, - definitionTarget: nameChild.text, - line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - - // Walk the body to index reference sites. - const body = node.childForFieldName("body"); - if (body !== null) indexReferences(body, g, authorised); -} - -function handleClassDef( - node: MinimalTsNode, - g: MutableGraph, - authorised: ReadonlySet, -): void { - const nameChild = node.childForFieldName("name"); - if (nameChild === null) return; - const line = node.startPosition.row + 1; - const defNode = addNode(g, "pop", { - symbol: nameChild.text, - definitionTarget: nameChild.text, - line, - }); - addEdge(g, g.moduleScopeId, defNode, 2); - const body = node.childForFieldName("body"); - if (body !== null) indexReferences(body, g, authorised); -} - -function indexReferences( - root: MinimalTsNode, - g: MutableGraph, - authorised: ReadonlySet, -): void { - for (const node of walkNamed(root)) { - if (node.type === "identifier") { - if (!authorised.has("identifier")) continue; - // Skip identifiers that are themselves names of nested defs. - // Heuristic: we index every identifier and rely on the resolver to - // filter — position-keyed lookup means false-positives are harmless. - const line = node.startPosition.row + 1; - const col = node.startPosition.column; - const key = positionKey(line, col); - if (g.referenceIndex.has(key)) continue; - const pushNode = addNode(g, "push", { symbol: node.text, line }); - addEdge(g, pushNode, g.moduleScopeId, 0); - g.referenceIndex.set(key, pushNode); - } - } -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.test.ts deleted file mode 100644 index f9ce3d35..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import assert from "node:assert/strict"; -import { test } from "node:test"; -import { resolveReference } from "./partial-path-engine.js"; -import type { NodeId, StackGraph, StackGraphNode } from "./types.js"; - -/** Minimal graph builder for tests — avoids pulling in the full builder. */ -function makeGraph( - file: string, - nodes: readonly StackGraphNode[], - edges: readonly { source: NodeId; target: NodeId; precedence?: number }[], - rootNodeId: NodeId, -): StackGraph { - return { - file, - nodes: new Map(nodes.map((n) => [n.id, n])), - edges: edges.map((e) => ({ - source: e.source, - target: e.target, - precedence: e.precedence ?? 0, - })), - rootNodeId, - referenceIndex: new Map(), - }; -} - -test("resolveReference: single-file push-pop pair empties the stack at the definition", () => { - // Simulate: reference `bar` pushes, flows through an edge to a pop node - // for `bar` whose definitionTarget is `foo.bar`. - const file = "mod.py"; - const ref: StackGraphNode = { id: "ref", kind: "push", symbol: "bar", file }; - const def: StackGraphNode = { - id: "def", - kind: "pop", - symbol: "bar", - file, - definitionTarget: "foo.bar", - line: 1, - }; - const root: StackGraphNode = { id: "root", kind: "root", file }; - const graph = makeGraph(file, [ref, def, root], [{ source: "ref", target: "def" }], "root"); - const { results } = resolveReference(new Map([[file, graph]]), file, "ref"); - assert.equal(results.length, 1); - assert.equal(results[0]?.targetNodeId, "def"); - assert.equal(results[0]?.targetKey, "mod.py:1:foo.bar"); -}); - -test("resolveReference: cross-file hop via ROOT lands on the other file's pop", () => { - // consumer.py has `ref(bar)` → root. other.py's root has a pop(bar). - const consumer: StackGraph = makeGraph( - "consumer.py", - [ - { id: "ref", kind: "push", symbol: "bar", file: "consumer.py" }, - { id: "root", kind: "root", file: "consumer.py" }, - ], - [{ source: "ref", target: "root" }], - "root", - ); - const other: StackGraph = makeGraph( - "other.py", - [ - { id: "root", kind: "root", file: "other.py" }, - { - id: "def", - kind: "pop", - symbol: "bar", - file: "other.py", - definitionTarget: "bar", - line: 2, - }, - ], - [{ source: "root", target: "def" }], - "root", - ); - const graphs = new Map([ - ["consumer.py", consumer], - ["other.py", other], - ]); - const { results } = resolveReference(graphs, "consumer.py", "ref"); - assert.equal(results.length, 1); - assert.equal(results[0]?.targetKey, "other.py:2:bar"); -}); - -test("resolveReference: mismatched pop prunes the path", () => { - const file = "mod.py"; - const ref: StackGraphNode = { id: "ref", kind: "push", symbol: "bar", file }; - const wrongPop: StackGraphNode = { - id: "wrong", - kind: "pop", - symbol: "baz", - file, - definitionTarget: "baz", - line: 1, - }; - const root: StackGraphNode = { id: "root", kind: "root", file }; - const graph = makeGraph( - file, - [ref, wrongPop, root], - [{ source: "ref", target: "wrong" }], - "root", - ); - const { results } = resolveReference(new Map([[file, graph]]), file, "ref"); - assert.equal(results.length, 0); -}); - -test("resolveReference: multiple paths ranked by shortest first", () => { - const file = "mod.py"; - const ref: StackGraphNode = { id: "ref", kind: "push", symbol: "bar", file }; - const near: StackGraphNode = { - id: "near", - kind: "pop", - symbol: "bar", - file, - definitionTarget: "near", - line: 1, - }; - const mid: StackGraphNode = { id: "mid", kind: "scope", file }; - const far: StackGraphNode = { - id: "far", - kind: "pop", - symbol: "bar", - file, - definitionTarget: "far", - line: 2, - }; - const root: StackGraphNode = { id: "root", kind: "root", file }; - const graph = makeGraph( - file, - [ref, near, mid, far, root], - [ - { source: "ref", target: "near", precedence: 1 }, - { source: "ref", target: "mid", precedence: 0 }, - { source: "mid", target: "far" }, - ], - "root", - ); - const { results } = resolveReference(new Map([[file, graph]]), file, "ref"); - assert.equal(results.length, 2); - assert.equal(results[0]?.targetKey, "mod.py:1:near", "shortest path first"); - assert.equal(results[1]?.targetKey, "mod.py:2:far"); -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.ts b/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.ts deleted file mode 100644 index 7c323c66..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/partial-path-engine.ts +++ /dev/null @@ -1,174 +0,0 @@ -// Partial-path search engine. -// -// Given one or more stack graphs (per-file) and a starting reference node, -// enumerate paths whose symbol stack eventually empties at a pop node that -// carries a `definitionTarget`. We use BFS rather than DFS so the first -// result found is also the shortest — a natural ranking signal when several -// alternative definitions exist (e.g. conditional imports). -// -// Semantics (simplified, intentionally narrower than the full model): -// * Every `push` node appends its symbol to the current stack. -// * Every `pop` node must match the top of the stack; path is pruned if -// the top differs. -// * A path terminates successfully when it lands on a pop node whose -// `definitionTarget` is set and the remaining stack is empty. -// * `root` nodes hop to every other graph's root, enabling cross-file -// resolution without having to model a global scope stack. -// -// Determinism is provided by sorting each node's outgoing edges on -// (descending precedence, ascending target id) before expansion. That gives -// us reproducible traversal order regardless of insertion order. - -import type { - NodeId, - PartialPathResult, - ResolvedDefinition, - StackGraph, - StackGraphEdge, -} from "./types.js"; -import { MAX_PARTIAL_PATH_DEPTH } from "./types.js"; - -/** Map a graph by file path so the traversal can hop across graphs. */ -export type GraphIndex = ReadonlyMap; - -/** Internal BFS state element. */ -interface Frontier { - readonly nodeId: NodeId; - readonly graphFile: string; - readonly symbolStack: readonly string[]; - readonly depth: number; - readonly visitedKey: string; -} - -function edgeSortKey(e: StackGraphEdge): string { - // Sort by descending precedence (higher first), then ascending target id - // to keep iteration deterministic across runs. - const invPrec = String(10_000 - e.precedence).padStart(6, "0"); - return `${invPrec}|${e.target}`; -} - -function outgoingEdges(graph: StackGraph, nodeId: NodeId): readonly StackGraphEdge[] { - const out: StackGraphEdge[] = []; - for (const e of graph.edges) { - if (e.source === nodeId) out.push(e); - } - return out.sort((a, b) => (edgeSortKey(a) < edgeSortKey(b) ? -1 : 1)); -} - -function makeVisitedKey(graphFile: string, nodeId: NodeId, stack: readonly string[]): string { - return `${graphFile}|${nodeId}|${stack.join(">")}`; -} - -/** - * Resolve a reference within a per-file graph. `startGraphFile` is the key - * into `graphs` at which the search begins. - */ -export function resolveReference( - graphs: GraphIndex, - startGraphFile: string, - referenceNodeId: NodeId, -): PartialPathResult { - const startGraph = graphs.get(startGraphFile); - if (startGraph === undefined) return { results: [], truncated: false }; - const startNode = startGraph.nodes.get(referenceNodeId); - if (startNode === undefined) return { results: [], truncated: false }; - - const results: ResolvedDefinition[] = []; - const visited = new Set(); - const initialStack: readonly string[] = startNode.symbol === undefined ? [] : [startNode.symbol]; - const queue: Frontier[] = []; - queue.push({ - nodeId: referenceNodeId, - graphFile: startGraphFile, - symbolStack: initialStack, - depth: 0, - visitedKey: makeVisitedKey(startGraphFile, referenceNodeId, initialStack), - }); - visited.add(queue[0]?.visitedKey ?? ""); - - let truncated = false; - - while (queue.length > 0) { - const current = queue.shift(); - if (current === undefined) continue; - if (current.depth > MAX_PARTIAL_PATH_DEPTH) { - truncated = true; - continue; - } - const graph = graphs.get(current.graphFile); - if (graph === undefined) continue; - const node = graph.nodes.get(current.nodeId); - if (node === undefined) continue; - - // Successful termination: we landed on a pop node whose own symbol - // has already been consumed via the inbound edge traversal (stack.length - // now zero) AND the node advertises a definitionTarget. - if ( - node.kind === "pop" && - node.definitionTarget !== undefined && - current.symbolStack.length === 0 - ) { - results.push({ - targetNodeId: node.id, - targetKey: `${graph.file}:${node.line ?? 0}:${node.definitionTarget}`, - pathLength: current.depth, - }); - // Don't continue from a successful terminal — any further traversal - // would leave the stack empty and then try to pop with nothing to - // pop, which is semantically invalid. - continue; - } - - // Cross-graph hop at root nodes. - if (node.kind === "root") { - for (const [otherFile, otherGraph] of graphs) { - if (otherFile === current.graphFile) continue; - const key = makeVisitedKey(otherFile, otherGraph.rootNodeId, current.symbolStack); - if (visited.has(key)) continue; - visited.add(key); - queue.push({ - nodeId: otherGraph.rootNodeId, - graphFile: otherFile, - symbolStack: current.symbolStack, - depth: current.depth + 1, - visitedKey: key, - }); - } - } - - for (const edge of outgoingEdges(graph, current.nodeId)) { - const nextNode = graph.nodes.get(edge.target); - if (nextNode === undefined) continue; - let nextStack: readonly string[] | null = current.symbolStack; - if (nextNode.kind === "push" && nextNode.symbol !== undefined) { - nextStack = [...current.symbolStack, nextNode.symbol]; - } else if (nextNode.kind === "pop" && nextNode.symbol !== undefined) { - const top = current.symbolStack[current.symbolStack.length - 1]; - if (top !== nextNode.symbol) { - // Pop mismatch — prune. - nextStack = null; - } else { - nextStack = current.symbolStack.slice(0, -1); - } - } - if (nextStack === null) continue; - const key = makeVisitedKey(current.graphFile, edge.target, nextStack); - if (visited.has(key)) continue; - visited.add(key); - queue.push({ - nodeId: edge.target, - graphFile: current.graphFile, - symbolStack: nextStack, - depth: current.depth + 1, - visitedKey: key, - }); - } - } - - // Sort by path length (shorter first) for deterministic output. - results.sort((a, b) => { - if (a.pathLength !== b.pathLength) return a.pathLength - b.pathLength; - return a.targetKey < b.targetKey ? -1 : 1; - }); - return { results, truncated }; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.test.ts b/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.test.ts deleted file mode 100644 index 5d8f98d7..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; -import { test } from "node:test"; -import { parseTsg } from "./rule-parser.js"; - -const SAMPLE_TSG = ` -;; A tiny tsg doc for the parser. -global FILE_PATH -global ROOT_PATH = "" - -attribute node_definition = node => type = "pop_symbol" - -(module) @mod { - node @mod.after_scope - node @mod.before_scope - edge @mod.before_scope -> @mod.after_scope -} - -[ - (import_statement) - (import_from_statement) -] @import { - node @import.def - attr (@import.def) pop_symbol = "." -} -`; - -test("parseTsg: extracts rules, globals, and attributes", () => { - const parsed = parseTsg(SAMPLE_TSG); - assert.equal(parsed.globals.length, 2); - assert.equal(parsed.attributeShorthands.length, 1); - assert.equal(parsed.rules.length, 2); - const first = parsed.rules[0]; - assert.ok(first !== undefined); - assert.equal(first.patterns[0]?.nodeType, "module"); - // All three actions classified non-unknown. - const kinds = first.actions.map((a) => a.kind); - assert.ok(kinds.includes("node-decl")); - assert.ok(kinds.includes("edge-decl")); -}); - -test("parseTsg: multi-pattern brackets expand to one rule with many patterns", () => { - const parsed = parseTsg(SAMPLE_TSG); - const multi = parsed.rules[1]; - assert.ok(multi !== undefined); - assert.equal(multi.patterns.length, 2); - const nodeTypes = multi.patterns.map((p) => p.nodeType).sort(); - assert.deepEqual(nodeTypes, ["import_from_statement", "import_statement"]); -}); - -test("parseTsg: real vendored Python rules parse without fatal errors", () => { - // Compiled test lives at packages/ingestion/dist/providers/resolution/ - // stack-graphs/rule-parser.test.js — six segments up lands on repo root. - const realRules = readFileSync( - new URL("../../../../../../vendor/stack-graphs-python/rules/stack-graphs.tsg", import.meta.url), - "utf8", - ); - const parsed = parseTsg(realRules); - // The file is 1377 LOC; it declares many rules. We just check we got - // non-trivial output and didn't abort early. - assert.ok(parsed.rules.length > 20, `only parsed ${parsed.rules.length} rules`); - const topLevelTypes = new Set(parsed.rules.flatMap((r) => r.patterns.map((p) => p.nodeType))); - for (const expected of [ - "module", - "import_statement", - "import_from_statement", - "function_definition", - "class_definition", - ]) { - assert.ok(topLevelTypes.has(expected), `missing expected rule for ${expected}`); - } -}); diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.ts b/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.ts deleted file mode 100644 index 1e165dbd..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/rule-parser.ts +++ /dev/null @@ -1,253 +0,0 @@ -// Rule-file parser for the vendored .tsg Python ruleset. -// -// The .tsg DSL is a mix of tree-sitter S-expression queries and a -// small imperative language for declaring nodes, edges, and attributes. -// We parse only the surface structure we need: -// * top-level rule blocks: `(pattern) @capture { actions }` -// * global declarations: `global NAME`, `global NAME = value` (recorded but -// otherwise ignored — the builder hardcodes Python's needs) -// * attribute shorthands: `attribute name = ...` (stored raw; the builder -// references them by name, not by macroexpansion) -// * actions inside blocks are shallowly tokenised into `node-decl`, -// `edge-decl`, `attr-decl`, or `unknown` so callers can surface stats. -// -// This is deliberately permissive — constructs we don't understand are -// recorded as `unknown` rather than rejected. The Python evaluator uses -// the rule file primarily as a licence-scoped manifest: our actual path -// construction lives in node-edge-builder.ts and is driven by the -// tree-sitter parse rather than a generic DSL interpreter. - -import type { TsgAction, TsgActionKind, TsgMatch, TsgRule } from "./types.js"; - -/** Strip `;;`-prefixed comments from a line. */ -function stripLineComment(line: string): string { - const idx = line.indexOf(";;"); - if (idx < 0) return line; - return line.slice(0, idx); -} - -/** Scan the opening pattern — everything inside a balanced `(...)` pair. */ -function readBalanced( - source: string, - start: number, - open: string, - close: string, -): { readonly body: string; readonly end: number } | null { - if (source[start] !== open) return null; - let depth = 0; - let inString = false; - for (let i = start; i < source.length; i++) { - const ch = source[i]; - if (ch === '"' && source[i - 1] !== "\\") { - inString = !inString; - continue; - } - if (inString) continue; - if (ch === open) depth++; - else if (ch === close) { - depth--; - if (depth === 0) { - return { body: source.slice(start + 1, i), end: i }; - } - } - } - return null; -} - -/** Extract rough tree-sitter pattern info from a parenthesised pattern body. */ -function parsePattern(body: string): TsgMatch { - // The S-expression head token names the node type. Any `@capture` after - // the closing paren is attached outside by the caller; here we only peek - // at the very first identifier so callers can group rules by target type. - const m = /^\s*([A-Za-z_][\w]*)/.exec(body); - const nodeType = m?.[1] ?? "unknown"; - return { kind: "pattern", nodeType }; -} - -/** Classify an action line into one of our coarse buckets. */ -function classifyAction(raw: string): TsgActionKind { - const trimmed = raw.trim(); - if (trimmed.startsWith("node ")) return "node-decl"; - if (trimmed.startsWith("edge ")) return "edge-decl"; - if (trimmed.startsWith("attr ") || trimmed.startsWith("attribute ")) return "attr-decl"; - return "unknown"; -} - -/** Split an action body into individual statements at top-level newlines. */ -function splitActions(body: string): readonly string[] { - // We respect braces — an action like `scan x { ... }` is one statement even - // though it spans multiple lines. This keeps the `unknown` bucket from - // exploding. - const out: string[] = []; - let buf = ""; - let braceDepth = 0; - let parenDepth = 0; - let inString = false; - - for (let i = 0; i < body.length; i++) { - const ch = body[i]; - if (ch === '"' && body[i - 1] !== "\\") inString = !inString; - if (!inString) { - if (ch === "{") braceDepth++; - else if (ch === "}") braceDepth--; - else if (ch === "(") parenDepth++; - else if (ch === ")") parenDepth--; - } - buf += ch; - if (!inString && braceDepth === 0 && parenDepth === 0 && (ch === "\n" || ch === ";")) { - const cleaned = stripLineComment(buf).trim(); - if (cleaned.length > 0) out.push(cleaned); - buf = ""; - } - } - const tail = stripLineComment(buf).trim(); - if (tail.length > 0) out.push(tail); - return out; -} - -/** Result of parsing a tsg source file. */ -export interface ParsedTsg { - readonly rules: readonly TsgRule[]; - readonly globals: readonly string[]; - readonly attributeShorthands: readonly string[]; - readonly warnings: readonly string[]; -} - -/** Parse a .tsg source string into a set of rules plus ancillary decls. */ -export function parseTsg(source: string): ParsedTsg { - const rules: TsgRule[] = []; - const globals: string[] = []; - const attributeShorthands: string[] = []; - const warnings: string[] = []; - - let i = 0; - const n = source.length; - - const skipSpaceAndComments = (): void => { - while (i < n) { - const ch = source[i]; - if (ch === undefined) break; - if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") { - i++; - continue; - } - if (ch === ";" && source[i + 1] === ";") { - while (i < n && source[i] !== "\n") i++; - continue; - } - break; - } - }; - - while (i < n) { - skipSpaceAndComments(); - if (i >= n) break; - - // Top-level keywords we track explicitly. - if (source.startsWith("global", i)) { - const end = source.indexOf("\n", i); - const line = source.slice(i, end < 0 ? n : end).trim(); - globals.push(line); - i = end < 0 ? n : end; - continue; - } - if (source.startsWith("attribute", i)) { - const end = source.indexOf("\n", i); - const line = source.slice(i, end < 0 ? n : end).trim(); - attributeShorthands.push(line); - i = end < 0 ? n : end; - continue; - } - if (source.startsWith("inherit", i)) { - const end = source.indexOf("\n", i); - i = end < 0 ? n : end; - continue; - } - - // A rule block must start with `(` or `[`. - const ch = source[i]; - if (ch !== "(" && ch !== "[") { - // Skip unrecognised tokens to the next newline. - const end = source.indexOf("\n", i); - if (end < 0) break; - i = end + 1; - continue; - } - - const patterns: TsgMatch[] = []; - if (ch === "[") { - // Multi-pattern rule: `[ pat1 pat2 ... ] @capture { ... }` - const block = readBalanced(source, i, "[", "]"); - if (block === null) { - warnings.push(`unterminated '[' at offset ${i}`); - break; - } - // Walk the bracket body picking out each top-level `(...)` pattern. - let j = 0; - while (j < block.body.length) { - const c = block.body[j]; - if (c === undefined) break; - if (c === " " || c === "\n" || c === "\t" || c === "\r") { - j++; - continue; - } - if (c === ";" && block.body[j + 1] === ";") { - while (j < block.body.length && block.body[j] !== "\n") j++; - continue; - } - if (c === "(") { - const inner = readBalanced(block.body, j, "(", ")"); - if (inner === null) break; - patterns.push(parsePattern(inner.body)); - j = inner.end + 1; - continue; - } - j++; - } - i = block.end + 1; - } else { - const pat = readBalanced(source, i, "(", ")"); - if (pat === null) { - warnings.push(`unterminated '(' at offset ${i}`); - break; - } - patterns.push(parsePattern(pat.body)); - i = pat.end + 1; - } - - // Skip to the action block `{ ... }` — the `@capture` name, if any, lives - // between the pattern and the brace; we don't need to retain it. - while (i < n && source[i] !== "{") { - if (source[i] === "\n" || source[i] === " " || source[i] === "\t" || source[i] === "@") { - i++; - continue; - } - if (source[i] === "(") { - const pred = readBalanced(source, i, "(", ")"); - if (pred === null) break; - i = pred.end + 1; - continue; - } - // Unknown chars before the block — drift forward rather than abort. - i++; - } - if (source[i] !== "{") { - warnings.push(`missing action block after pattern at offset ${i}`); - continue; - } - const action = readBalanced(source, i, "{", "}"); - if (action === null) { - warnings.push(`unterminated action block at offset ${i}`); - break; - } - const statements = splitActions(action.body); - const actions: TsgAction[] = statements.map((s) => ({ - kind: classifyAction(s), - raw: s, - })); - rules.push({ patterns, actions }); - i = action.end + 1; - } - - return { rules, globals, attributeShorthands, warnings }; -} diff --git a/packages/ingestion/src/providers/resolution/stack-graphs/types.ts b/packages/ingestion/src/providers/resolution/stack-graphs/types.ts deleted file mode 100644 index 0ce4afcd..00000000 --- a/packages/ingestion/src/providers/resolution/stack-graphs/types.ts +++ /dev/null @@ -1,116 +0,0 @@ -// Shared types for the clean-room stack-graphs evaluator. -// -// This module is a minimal, Python-scoped subset of the stack-graphs model. -// Our types are intentionally narrow. All names and semantics were chosen -// independently from any prior implementation — we model only what our -// Python rules need. - -/** - * Node roles in our simplified stack-graph. Each maps to one primitive - * attribute family in the broader stack-graphs model, but we collapse - * push-scoped / pop-scoped into `push`/`pop` here because Python's - * reference model doesn't require the scope-stack machinery for the - * re-export cases we actually resolve. - */ -export type StackNodeKind = - | "push" // reference site — pushes a symbol when traversed - | "pop" // definition site — pops a matching symbol - | "scope" // pass-through junction - | "root"; // global sink for cross-file lookup - -/** Opaque node identifier. Format: `${file}#${seq}` where seq is per-file. */ -export type NodeId = string; - -/** - * One node in our stack graph. `symbol` is populated for push/pop kinds and - * carries the Python identifier the node pushes or pops. `definitionTarget` - * is set on pop nodes that correspond to concrete definitions (e.g. the - * function / class the resolver should land on). - */ -export interface StackGraphNode { - readonly id: NodeId; - readonly kind: StackNodeKind; - readonly symbol?: string; - readonly definitionTarget?: string; - /** Source file that created this node — useful for cross-file debugging. */ - readonly file: string; - /** Optional line number in the source file (1-indexed). */ - readonly line?: number; -} - -/** A directed edge with optional precedence (higher wins at enumeration time). */ -export interface StackGraphEdge { - readonly source: NodeId; - readonly target: NodeId; - readonly precedence: number; -} - -/** Per-file stack graph plus the well-known root-node id. */ -export interface StackGraph { - readonly file: string; - readonly nodes: ReadonlyMap; - readonly edges: readonly StackGraphEdge[]; - /** Id of the module's per-file root — references escape to here. */ - readonly rootNodeId: NodeId; - /** Ids of reference-push nodes, keyed by (line, column) for lookup. */ - readonly referenceIndex: ReadonlyMap; -} - -/** - * Query input: resolve the reference at this position in this file. - * The evaluator looks up the starting push-node via `referenceIndex`. - */ -export interface ReferenceQuery { - readonly file: string; - readonly line: number; - readonly column: number; - readonly name: string; -} - -/** A successful path resolution. */ -export interface ResolvedDefinition { - readonly targetNodeId: NodeId; - /** Where the definition actually lives — typically `${file}:${line}:${name}`. */ - readonly targetKey: string; - /** Length of the path in edges; shorter paths score higher. */ - readonly pathLength: number; -} - -/** Confidence assigned when stack-graphs produces a hit. */ -export const STACK_GRAPHS_HIT_CONFIDENCE = 0.9; - -/** Budget — never enumerate a path longer than this. */ -export const MAX_PARTIAL_PATH_DEPTH = 100; - -/** - * Rule-file AST — consumed by the node/edge builder. We intentionally keep - * only the shapes our Python evaluator consults; other rule kinds - * (let/set/var/scan) are parsed into an opaque `raw` form and ignored by - * the builder. - */ -export type TsgMatch = { - readonly kind: "pattern"; - /** Tree-sitter node type name the rule fires on (e.g. `module`). */ - readonly nodeType: string; - /** Tree-sitter capture name attached to the match (e.g. `@mod`). */ - readonly capture?: string; -}; - -export type TsgActionKind = "node-decl" | "edge-decl" | "attr-decl" | "unknown"; - -export interface TsgAction { - readonly kind: TsgActionKind; - /** Raw action source text (debug only). */ - readonly raw: string; -} - -export interface TsgRule { - readonly patterns: readonly TsgMatch[]; - readonly actions: readonly TsgAction[]; -} - -/** Result returned from `resolveReference`. */ -export interface PartialPathResult { - readonly results: readonly ResolvedDefinition[]; - readonly truncated: boolean; -} diff --git a/packages/ingestion/src/providers/tsx.ts b/packages/ingestion/src/providers/tsx.ts index 0b77c76b..cf4331a6 100644 --- a/packages/ingestion/src/providers/tsx.ts +++ b/packages/ingestion/src/providers/tsx.ts @@ -25,8 +25,9 @@ export const tsxProvider: LanguageProvider = { inferImplicitReceiver: () => "this", preprocessImportPath: preprocessTsImportPath, isExportedIdentifier: (_name, context) => context === "top-level", - // Shares the TS-family stack-graphs backend — see typescript.ts. - resolverStrategyName: "stack-graphs", + // Reference resolution runs through the three-tier walker + // (same-file -> import-scoped -> global). SCIP edges, when present, + // overlay as the precision oracle on top of the walker's output. complexityDefinitionKinds: [ "function_declaration", "function_expression", diff --git a/packages/ingestion/src/providers/types.ts b/packages/ingestion/src/providers/types.ts index 9320bbd8..2a42689b 100644 --- a/packages/ingestion/src/providers/types.ts +++ b/packages/ingestion/src/providers/types.ts @@ -123,15 +123,6 @@ export interface LanguageProvider { /** Name used for the implicit receiver inside a method body. */ readonly inferImplicitReceiver?: (callerKind: string) => "self" | "this" | undefined; - /** - * Optional opt-in for an alternative reference-resolution backend. Unset - * providers use the three-tier walker (`"three-tier-default"`). Known - * values map to the registry in `resolution/resolver-strategy.ts`: - * - `"stack-graphs"` — clean-room stack-graphs evaluator (Python only). - * Unknown values silently fall back to the default. - */ - readonly resolverStrategyName?: string; - /** * Tree-sitter node-type names that start a new function/method/constructor * body for complexity counting. The complexity phase dispatches on these; diff --git a/packages/ingestion/src/providers/typescript.ts b/packages/ingestion/src/providers/typescript.ts index 3dade6ef..a8c6c410 100644 --- a/packages/ingestion/src/providers/typescript.ts +++ b/packages/ingestion/src/providers/typescript.ts @@ -27,11 +27,9 @@ export const typescriptProvider: LanguageProvider = { inferImplicitReceiver: () => "this", preprocessImportPath: preprocessTsImportPath, isExportedIdentifier: (_name, context) => context === "top-level", - // Opt into the clean-room stack-graphs evaluator. The router in - // resolver-strategy.ts dispatches this name by provider.id; the TS/TSX/JS - // backend handles barrel re-exports, default/namespace imports, and falls - // back to the three-tier walker when stack-graphs can't resolve. - resolverStrategyName: "stack-graphs", + // Reference resolution runs through the three-tier walker + // (same-file -> import-scoped -> global). SCIP edges, when present, + // overlay as the precision oracle on top of the walker's output. complexityDefinitionKinds: [ "function_declaration", "function_expression", diff --git a/packages/mcp/README.md b/packages/mcp/README.md index a0a82847..53c7a482 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -24,16 +24,18 @@ codehub mcp # spawn the stdio server ## Tools -29 tools registered in `packages/mcp/src/server.ts:151-179`. Implementation -files live under `packages/mcp/src/tools/.ts`. +28 tools registered in `packages/mcp/src/server.ts`. Implementation +files live under `packages/mcp/src/tools/.ts`. Every tool is +**read-only with respect to user source** — no tool edits the working +tree. | Group | Tools | | ----------- | ---------------------------------------------------------------------------------------------------------- | -| Discovery | `list_repos`, `query`, `context`, `route_map`, `tool_map` | -| Impact | `impact`, `api_impact`, `detect_changes`, `shape_check`, `rename` | +| Discovery | `list_repos`, `query`, `context`, `signature`, `route_map`, `tool_map` | +| Impact | `impact`, `api_impact`, `detect_changes`, `shape_check` | | Snapshot | `pack_codebase`, `project_profile`, `dependencies`, `owners`, `risk_trends` | | Findings | `scan`, `verdict`, `list_findings`, `list_findings_delta`, `license_audit` | -| Dead code | `list_dead_code`, `remove_dead_code` | +| Dead code | `list_dead_code` | | Group | `group_list`, `group_query`, `group_status`, `group_contracts`, `group_cross_repo_links`, `group_sync` | | Raw query | `sql` | diff --git a/packages/mcp/src/analysis-bridge.ts b/packages/mcp/src/analysis-bridge.ts index c40319b8..2809cd9b 100644 --- a/packages/mcp/src/analysis-bridge.ts +++ b/packages/mcp/src/analysis-bridge.ts @@ -1,8 +1,8 @@ /** * Bridge to `@opencodehub/analysis`. * - * The analysis package exposes `runImpact`, `runRename`, `runDetectChanges`, - * and `computeStaleness`, which this bridge re-exports for the tool handlers. + * The analysis package exposes `runImpact`, `runDetectChanges`, and + * `computeStaleness`, which this bridge re-exports for the tool handlers. * A slim inline-impact fallback remains as a safety net for repos where * analysis cannot resolve the target — e.g. a bare node-id with no * declaration row — so the `impact` tool always returns something actionable. @@ -11,15 +11,10 @@ import { runDetectChanges as analysisRunDetectChanges, runImpact as analysisRunImpact, - runRename as analysisRunRename, - createNodeFs, type DetectChangesQuery, type DetectChangesResult, - type FsAbstraction, type ImpactQuery, type ImpactResult, - type RenameQuery, - type RenameResult, } from "@opencodehub/analysis"; import type { IGraphStore } from "@opencodehub/storage"; @@ -28,24 +23,12 @@ export type { DetectChangesResult, ImpactQuery, ImpactResult, - RenameEdit, - RenameQuery, - RenameResult, } from "@opencodehub/analysis"; export async function callRunImpact(store: IGraphStore, q: ImpactQuery): Promise { return analysisRunImpact(store, q); } -export async function callRunRename( - store: IGraphStore, - q: RenameQuery, - repoRoot: string, - fs?: FsAbstraction, -): Promise { - return analysisRunRename(store, q, fs ?? createNodeFs(), repoRoot); -} - export async function callRunDetectChanges( store: IGraphStore, q: DetectChangesQuery, diff --git a/packages/mcp/src/connection-pool.test.ts b/packages/mcp/src/connection-pool.test.ts index daa890e1..a2211f91 100644 --- a/packages/mcp/src/connection-pool.test.ts +++ b/packages/mcp/src/connection-pool.test.ts @@ -114,3 +114,66 @@ test("acquire after shutdown throws", async () => { await pool.shutdown(); await assert.rejects(() => pool.acquire("x", "/x.duckdb"), /shut down/); }); + +test("eviction of an in-use entry defers close to the last release", async () => { + const probes = new Map>(); + const pool = new ConnectionPool({ max: 2, ttlMs: 10_000 }, async (p) => { + const probe = makeFakeStore(p); + probes.set(p, probe); + return probe.store; + }); + try { + // Hold three distinct repos in flight at once with max=2 so the LRU + // evicts the least-recently-used ("a") WHILE it is still referenced. + await pool.acquire("a", "/a.duckdb"); + await pool.acquire("b", "/b.duckdb"); + await pool.acquire("c", "/c.duckdb"); // evicts "a" (refCount 1) + + await new Promise((resolve) => setImmediate(resolve)); + // The evicted entry is still in use — it MUST NOT be closed yet, or the + // tool still holding the handle would see a closed store mid-call. + assert.equal( + probes.get("/a.duckdb")?.isClosed(), + false, + "evicted-but-in-use store must stay open until its last release", + ); + + // The last release of the evicted key runs the deferred close — this is + // the path that was previously unreachable (findEvicted returned + // undefined), leaking the handle. + await pool.release("a"); + assert.equal( + probes.get("/a.duckdb")?.isClosed(), + true, + "last release of an evicted entry must close the store", + ); + assert.equal(probes.get("/a.duckdb")?.closeCount(), 1, "store must close exactly once"); + + await pool.release("b"); + await pool.release("c"); + } finally { + await pool.shutdown(); + } +}); + +test("shutdown closes entries evicted while still in use", async () => { + const probes = new Map>(); + const pool = new ConnectionPool({ max: 2, ttlMs: 10_000 }, async (p) => { + const probe = makeFakeStore(p); + probes.set(p, probe); + return probe.store; + }); + // Overflow so "a" is evicted while refCount > 0, then shut down before any + // release. The parked side-table entry must still be drained. + await pool.acquire("a", "/a.duckdb"); + await pool.acquire("b", "/b.duckdb"); + await pool.acquire("c", "/c.duckdb"); // evicts "a" (refCount 1, parked) + + await pool.shutdown(); + assert.equal( + probes.get("/a.duckdb")?.isClosed(), + true, + "shutdown must close a still-referenced evicted entry", + ); + assert.equal(probes.get("/a.duckdb")?.closeCount(), 1, "store must close exactly once"); +}); diff --git a/packages/mcp/src/connection-pool.ts b/packages/mcp/src/connection-pool.ts index be175580..41a8420f 100644 --- a/packages/mcp/src/connection-pool.ts +++ b/packages/mcp/src/connection-pool.ts @@ -12,7 +12,9 @@ * second connection opening the same file in read-write mode. * 2. Reference counting. Release must decrement a per-entry counter; an * eviction that lands on a still-in-use entry MUST NOT close it. We - * mark it `closed` deferred and the last release actually closes. + * set `closePending` and park the entry in a side table (it has left + * the cache, so `release` can no longer find it via the cache) so the + * last release actually closes it. * 3. Idle TTL. lru-cache@11 bumps recency on every acquire, so a repo * that is actively queried never evicts; an idle repo closes after * 15 minutes. @@ -68,6 +70,15 @@ const defaultFactory: StoreFactory = async (dbPath) => { export class ConnectionPool { private readonly cache: LRUCache; private readonly inflight = new Map>(); + /** + * Entries evicted from the LRU while still in use (`refCount > 0`). Once + * `dispose` removes a key from the cache, `cache.peek` can no longer find + * it, so without this side table the deferred-close path in `release` + * would be unreachable and the still-open store would leak. We park the + * entry here, look it up from `release`, and drain it once the last + * reference is returned and the store is closed. + */ + private readonly evicted = new Map(); private readonly factory: StoreFactory; private disposed = false; @@ -89,7 +100,10 @@ export class ConnectionPool { /* swallow — best effort during eviction */ }); } else { + // Still in use: defer the close to the last `release`. Park the + // entry so `release` can find it after it leaves the cache. entry.closePending = true; + this.evicted.set(key, entry); } // Ensure we don't leak a stale inflight promise for an evicted key. this.inflight.delete(key); @@ -151,6 +165,9 @@ export class ConnectionPool { if (entry.refCount > 0) entry.refCount -= 1; if (entry.refCount === 0 && entry.closePending && !entry.closed) { entry.closed = true; + // The entry left the cache on eviction; once closed it must also leave + // the evicted side table so we don't retain the handle forever. + this.evicted.delete(repoKey); await entry.store.close().catch(() => { /* swallow */ }); @@ -172,6 +189,11 @@ export class ConnectionPool { const entries: PoolEntry[] = []; for (const entry of this.cache.values()) entries.push(entry); this.cache.clear(); // triggers dispose on remaining entries + // Include entries that were evicted while in use: they live in the + // side table, not the cache, so the loop above would miss them and + // leave their stores open. The `closed` guard below dedupes. + for (const entry of this.evicted.values()) entries.push(entry); + this.evicted.clear(); await Promise.allSettled( entries.map(async (entry) => { @@ -188,16 +210,14 @@ export class ConnectionPool { return this.cache.size; } - private findEvicted(_repoKey: string): PoolEntry | undefined { - // After dispose runs, the entry is gone from the cache; the caller - // holds no direct reference to it here. We intentionally don't store - // a secondary map — reference counting for evicted entries is tracked - // inside the entry object itself, which remains reachable via the - // store reference that the tool handler still holds. For the current - // MVP usage (single-threaded tool handlers that acquire + release in - // the same function) this branch is unreachable, so we return - // undefined and rely on the dispose path to have already closed the - // store if refCount was 0. - return undefined; + private findEvicted(repoKey: string): PoolEntry | undefined { + // After dispose runs on a still-in-use entry, the key is gone from the + // cache so `cache.peek` returns undefined. The entry was parked in the + // `evicted` side table; return it so `release` can run the deferred + // close once the last reference comes back. Concurrent tool calls can + // hold > poolMax distinct repos in flight at once, so this path is + // reachable in practice — not just the single-threaded acquire/release + // case. + return this.evicted.get(repoKey); } } diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index f23a850f..ff8fee1d 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -50,8 +50,6 @@ export { runListRepos } from "./tools/list-repos.js"; export { runOwners } from "./tools/owners.js"; export { runProjectProfile } from "./tools/project-profile.js"; export { runQuery } from "./tools/query.js"; -export { runRemoveDeadCode } from "./tools/remove-dead-code.js"; -export { runRename } from "./tools/rename.js"; export { runRiskTrends } from "./tools/risk-trends.js"; export { runRouteMap } from "./tools/route-map.js"; export { runScan } from "./tools/scan.js"; diff --git a/packages/mcp/src/server.test.ts b/packages/mcp/src/server.test.ts index b792c4b1..ba4b3186 100644 --- a/packages/mcp/src/server.test.ts +++ b/packages/mcp/src/server.test.ts @@ -42,3 +42,71 @@ test("buildServer registers zero prompts — ListPrompts returns an empty set", } }); }); + +/** + * The complete set of tool wire-names the server must register. This is the + * authoritative contract clients depend on — a tool whose `registerXxxTool` + * call is dropped from `buildServer` silently disappears from the surface + * even though its module compiles and its handler tests pass. Pinning the + * exact set here turns that class of regression into a failing assertion. + */ +const EXPECTED_TOOL_NAMES = [ + "api_impact", + "context", + "dependencies", + "detect_changes", + "group_contracts", + "group_cross_repo_links", + "group_list", + "group_query", + "group_status", + "group_sync", + "impact", + "license_audit", + "list_dead_code", + "list_findings", + "list_findings_delta", + "list_repos", + "owners", + "pack_codebase", + "project_profile", + "query", + "risk_trends", + "route_map", + "scan", + "shape_check", + "signature", + "sql", + "tool_map", + "verdict", +].sort(); + +test("buildServer registers exactly the expected read-only tool set", async () => { + await withEmptyHome(async (home) => { + const running = buildServer({ home, silentEmbedderProbe: true }); + try { + const withPrivate = running.server as unknown as { + _registeredTools?: Record; + }; + const registered = Object.keys(withPrivate._registeredTools ?? {}).sort(); + assert.deepEqual(registered, EXPECTED_TOOL_NAMES); + // `signature` was built + tested but historically never wired into the + // server; this guards against it (or any tool) silently dropping out. + assert.ok(registered.includes("signature"), "signature tool must be registered"); + // The MCP surface is read-only by rail: the source-mutating `rename` + // and `remove_dead_code` tools were removed, so no registered tool + // edits a user's source files. + assert.ok( + !registered.includes("rename"), + "source-mutating rename tool must NOT be registered", + ); + assert.ok( + !registered.includes("remove_dead_code"), + "source-mutating remove_dead_code tool must NOT be registered", + ); + assert.equal(registered.length, 28); + } finally { + await running.shutdown(); + } + }); +}); diff --git a/packages/mcp/src/server.ts b/packages/mcp/src/server.ts index 0cfa8576..68d76d18 100644 --- a/packages/mcp/src/server.ts +++ b/packages/mcp/src/server.ts @@ -45,13 +45,12 @@ import { registerOwnersTool } from "./tools/owners.js"; import { registerPackCodebaseTool } from "./tools/pack-codebase.js"; import { registerProjectProfileTool } from "./tools/project-profile.js"; import { registerQueryTool } from "./tools/query.js"; -import { registerRemoveDeadCodeTool } from "./tools/remove-dead-code.js"; -import { registerRenameTool } from "./tools/rename.js"; import { registerRiskTrendsTool } from "./tools/risk-trends.js"; import { registerRouteMapTool } from "./tools/route-map.js"; import { registerScanTool } from "./tools/scan.js"; import { registerShapeCheckTool } from "./tools/shape-check.js"; import type { ToolContext } from "./tools/shared.js"; +import { registerSignatureTool } from "./tools/signature.js"; import { registerSqlTool } from "./tools/sql.js"; import { registerToolMapTool } from "./tools/tool-map.js"; import { registerVerdictTool } from "./tools/verdict.js"; @@ -62,9 +61,9 @@ const SERVER_VERSION = "0.0.0"; const INSTRUCTIONS = [ "OpenCodeHub exposes indexed code graphs for MCP agents.", "Typical flow: call `list_repos` first to discover indexed repos, then route subsequent calls through one of those repo names.", - "Every per-repo tool (`query`, `context`, `impact`, `detect_changes`, `rename`, `sql`, `scan`, `list_findings`, `list_findings_delta`, `list_dead_code`, `remove_dead_code`, `license_audit`, `project_profile`, `dependencies`, `owners`, `risk_trends`, `verdict`) accepts an optional `repo` argument (registry name) or a `repo_uri` alias (Sourcegraph-style URI like `github.com/org/repo`, or `local:` for unpublished repos; wins when both are provided). When exactly one repo is registered, both are optional and the tool defaults to that repo. When ≥ 2 repos are registered and neither is supplied, the tool returns `AMBIGUOUS_REPO` — the structured envelope carries `structuredContent.error.choices[]` (capped at 10, with `{repo_uri, default_branch, group}`) plus `total_matches`, so a caller can retry with one of `choices[].repo_uri`.", + "Every per-repo tool (`query`, `context`, `impact`, `detect_changes`, `sql`, `scan`, `list_findings`, `list_findings_delta`, `list_dead_code`, `license_audit`, `project_profile`, `dependencies`, `owners`, `risk_trends`, `verdict`) accepts an optional `repo` argument (registry name) or a `repo_uri` alias (Sourcegraph-style URI like `github.com/org/repo`, or `local:` for unpublished repos; wins when both are provided). When exactly one repo is registered, both are optional and the tool defaults to that repo. When ≥ 2 repos are registered and neither is supplied, the tool returns `AMBIGUOUS_REPO` — the structured envelope carries `structuredContent.error.choices[]` (capped at 10, with `{repo_uri, default_branch, group}`) plus `total_matches`, so a caller can retry with one of `choices[].repo_uri`.", "Every tool response includes a `next_steps` array under structuredContent and a `_meta.codehub/staleness` entry when the index may be behind HEAD.", - "Use `query` to locate symbols, `context` for a 360-degree view, `impact` for blast radius, `detect_changes` to map a diff to flows, `rename` for coordinated renames (dry-run by default), `dependencies` for the external package list, `license_audit` for a copyleft/unknown/proprietary tier check of dependencies, `list_findings` to browse SARIF findings, `list_findings_delta` to diff the latest scan against a frozen baseline (new/fixed/unchanged/updated buckets), `scan` to run Priority-1 scanners (openWorld — spawns processes), `verdict` for a 5-tier PR decision (exit codes 0/1/2), `risk_trends` for per-community trend lines and 30-day projections, and `sql` for bespoke queries.", + "Use `query` to locate symbols, `context` for a 360-degree view, `impact` for blast radius (plan a refactor before you edit — OpenCodeHub does not edit source), `detect_changes` to map a diff to flows (verify a refactor after you apply it), `dependencies` for the external package list, `license_audit` for a copyleft/unknown/proprietary tier check of dependencies, `list_findings` to browse SARIF findings, `list_findings_delta` to diff the latest scan against a frozen baseline (new/fixed/unchanged/updated buckets), `scan` to run Priority-1 scanners (openWorld — spawns processes), `verdict` for a 5-tier PR decision (exit codes 0/1/2), `risk_trends` for per-community trend lines and 30-day projections, and `sql` for bespoke queries.", "For cross-repo work, call `group_list` to discover named repo groups, then `group_query`/`group_status` to fan out BM25 search and staleness across the group. `group_query` returns `{ group, query, results: [{ _repo, _rrf_score, ... }], per_repo, warnings }`; results are tagged with the source repo and per-repo errors surface in `per_repo[].error` + `warnings[]` (the fan-out never aborts on a single-repo failure). Use `group_sync` to materialize a cross-repo contract registry (HTTP / gRPC / topic) under `~/.codehub/groups//contracts.json`, then `group_contracts` to list the DuckDB-backed FETCHES↔Route edges together with the registry's signature-matched cross-links.", ].join(" "); @@ -154,7 +153,6 @@ export function buildServer(opts: StartServerOptions = {}): RunningServer { registerContextTool(server, ctx); registerImpactTool(server, ctx); registerDetectChangesTool(server, ctx); - registerRenameTool(server, ctx); registerSqlTool(server, ctx); registerGroupListTool(server, ctx); registerGroupQueryTool(server, ctx); @@ -169,13 +167,13 @@ export function buildServer(opts: StartServerOptions = {}): RunningServer { registerListFindingsTool(server, ctx); registerListFindingsDeltaTool(server, ctx); registerListDeadCodeTool(server, ctx); - registerRemoveDeadCodeTool(server, ctx); registerScanTool(server, ctx); registerVerdictTool(server, ctx); registerRiskTrendsTool(server, ctx); registerRouteMapTool(server, ctx); registerApiImpactTool(server, ctx); registerShapeCheckTool(server, ctx); + registerSignatureTool(server, ctx); registerToolMapTool(server, ctx); const resCtx: { home?: string; pool: ConnectionPool } = diff --git a/packages/mcp/src/tools/annotations.test.ts b/packages/mcp/src/tools/annotations.test.ts index 7bfebdf5..bdbd537f 100644 --- a/packages/mcp/src/tools/annotations.test.ts +++ b/packages/mcp/src/tools/annotations.test.ts @@ -84,49 +84,50 @@ test("every registered tool advertises all 5 annotation fields + a title", async } }); -test("destructive tools are correctly flagged", async () => { +test("no source-mutating tool is registered; non-read-only tools only write artifacts", async () => { const home = await mkdtemp(join(tmpdir(), "codehub-mcp-destructive-")); const running = buildServer({ home, silentEmbedderProbe: true }); try { const tools = enumerateTools(running.server); - // `rename` is the only v1.0 tool that mutates user files. + // RAIL: the MCP surface never edits a user's source files. The two tools + // that did (`rename`, `remove_dead_code`) were removed; assert they are + // gone so they cannot be re-added without this test failing. + assert.equal(tools["rename"], undefined, "rename (source mutator) must not be registered"); assert.equal( - tools["rename"]?.annotations?.destructiveHint, - true, - "rename must declare destructiveHint=true", - ); - assert.equal( - tools["rename"]?.annotations?.readOnlyHint, - false, - "rename must declare readOnlyHint=false", - ); - // `scan` writes .codehub/scan.sarif and spawns external scanners. - assert.equal( - tools["scan"]?.annotations?.readOnlyHint, - false, - "scan must declare readOnlyHint=false", + tools["remove_dead_code"], + undefined, + "remove_dead_code (source mutator) must not be registered", ); + // No registered tool may declare destructiveHint=true — that flag is + // reserved for source mutation, which the rail forbids. + for (const [name, def] of Object.entries(tools)) { + assert.notEqual( + def?.annotations?.destructiveHint, + true, + `${name} declares destructiveHint=true — no MCP tool may mutate user source`, + ); + } + // The surviving non-read-only tools are ARTIFACT writers (SARIF, code + // packs, contract registries under .codehub/), not source mutators. They + // are readOnlyHint=false but destructiveHint=false. + for (const name of ["scan", "pack_codebase", "group_sync"]) { + assert.equal( + tools[name]?.annotations?.readOnlyHint, + false, + `${name} writes an artifact, so readOnlyHint must be false`, + ); + assert.notEqual( + tools[name]?.annotations?.destructiveHint, + true, + `${name} writes an artifact (not user source), so destructiveHint must not be true`, + ); + } + // `scan` spawns external scanner binaries. assert.equal( tools["scan"]?.annotations?.openWorldHint, true, "scan must declare openWorldHint=true (spawns external binaries)", ); - // `remove_dead_code` deletes source ranges when apply=true. - assert.equal( - tools["remove_dead_code"]?.annotations?.destructiveHint, - true, - "remove_dead_code must declare destructiveHint=true", - ); - assert.equal( - tools["remove_dead_code"]?.annotations?.readOnlyHint, - false, - "remove_dead_code must declare readOnlyHint=false", - ); - assert.equal( - tools["remove_dead_code"]?.annotations?.openWorldHint, - false, - "remove_dead_code must declare openWorldHint=false", - ); } finally { await running.shutdown(); await rm(home, { recursive: true, force: true }); diff --git a/packages/mcp/src/tools/pack-codebase.test.ts b/packages/mcp/src/tools/pack-codebase.test.ts index 530c8a5f..b9f25254 100644 --- a/packages/mcp/src/tools/pack-codebase.test.ts +++ b/packages/mcp/src/tools/pack-codebase.test.ts @@ -213,7 +213,7 @@ test("pack_codebase honors budget+tokenizer overrides on the pack engine", async }); }); -test("pack_codebase returns a structured error when the repo cannot be resolved", async () => { +test("pack_codebase returns a NOT_FOUND envelope when the repo is not registered", async () => { await withHarness(async ({ ctx }) => { const deps: PackCodebaseDeps = { _runPackEngine: async () => ({ outDir: "x", packHash: "x", bomItemCount: 0 }), @@ -232,5 +232,84 @@ test("pack_codebase returns a structured error when the repo cannot be resolved" deps, ); assert.equal(result.isError, true); + const err = (result.structuredContent as { error: Record }).error; + // The resolver's structured code must survive — not be flattened to INTERNAL. + assert.equal(err["code"], "NOT_FOUND"); }); }); + +test("pack_codebase surfaces the AMBIGUOUS_REPO envelope when ≥ 2 repos are registered", async () => { + // Two registered repos with neither `repo` nor `repo_uri` supplied: the + // resolver throws RepoResolveError(AMBIGUOUS_REPO). pack_codebase must emit + // the structured envelope (error_code + choices + total_matches) rather than + // flattening it to INTERNAL through the catch-all. + const home = await mkdtemp(resolve(tmpdir(), "codehub-mcp-pack-ambig-")); + try { + const repoA = resolve(home, "repoA"); + const repoB = resolve(home, "repoB"); + await mkdir(repoA, { recursive: true }); + await mkdir(repoB, { recursive: true }); + const regDir = resolve(home, ".codehub"); + await mkdir(regDir, { recursive: true }); + await writeFile( + resolve(regDir, "registry.json"), + JSON.stringify({ + "github.com/org/repoA": { + name: "github.com/org/repoA", + path: repoA, + indexedAt: "2026-04-18T00:00:00Z", + nodeCount: 0, + edgeCount: 0, + }, + "github.com/org/repoB": { + name: "github.com/org/repoB", + path: repoB, + indexedAt: "2026-04-18T00:00:00Z", + nodeCount: 0, + edgeCount: 0, + }, + }), + ); + const pool = { + acquire: async () => { + throw new Error("pool.acquire should not be called by pack_codebase"); + }, + release: async () => {}, + shutdown: async () => {}, + // biome-ignore lint/suspicious/noExplicitAny: stub doesn't implement the full ConnectionPool surface + } as any as ConnectionPool; + const ctx: ToolContext = { pool, home }; + const deps: PackCodebaseDeps = { + _runPackEngine: async () => { + throw new Error("pack engine should not run when repo is ambiguous"); + }, + }; + const result = await runPackCodebase( + ctx, + { + // No repo / repo_uri — trigger the ambiguous path. + engine: "pack", + budget: DEFAULT_PACK_BUDGET, + tokenizer: DEFAULT_PACK_TOKENIZER, + style: "xml", + compress: true, + removeComments: false, + }, + deps, + ); + assert.equal(result.isError, true); + const err = (result.structuredContent as { error: Record }).error; + assert.equal(err["code"], "AMBIGUOUS_REPO"); + assert.equal(err["error_code"], "AMBIGUOUS_REPO"); + assert.equal(err["jsonrpc_code"], -32602); + assert.equal(err["total_matches"], 2); + const choices = err["choices"] as Array<{ repo_uri: string }>; + assert.equal(choices.length, 2); + assert.deepEqual(choices.map((c) => c.repo_uri).sort(), [ + "github.com/org/repoA", + "github.com/org/repoB", + ]); + } finally { + await rm(home, { recursive: true, force: true }); + } +}); diff --git a/packages/mcp/src/tools/pack-codebase.ts b/packages/mcp/src/tools/pack-codebase.ts index a399f59c..758cb33c 100644 --- a/packages/mcp/src/tools/pack-codebase.ts +++ b/packages/mcp/src/tools/pack-codebase.ts @@ -26,9 +26,9 @@ import { dirname, join } from "node:path"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { generatePack as defaultGeneratePack } from "@opencodehub/pack"; import { z } from "zod"; -import { toolErrorFromUnknown } from "../error-envelope.js"; +import { toolAmbiguousRepoError, toolError, toolErrorFromUnknown } from "../error-envelope.js"; import { withNextSteps } from "../next-step-hints.js"; -import { resolveRepo } from "../repo-resolver.js"; +import { RepoResolveError, resolveRepo } from "../repo-resolver.js"; import { fromToolResult, type ToolContext, type ToolResult, toToolResult } from "./shared.js"; const DEFAULT_REPOMIX_VERSION = "1.14.0"; @@ -135,6 +135,24 @@ export async function runPackCodebase( } return await runPackPath(entry, input, deps); } catch (err) { + // Repo-resolution failures carry a structured code (AMBIGUOUS_REPO / + // NO_INDEX / NOT_FOUND). Mirror `withStore` so pack_codebase returns the + // same envelope every other per-repo tool does — otherwise the catch-all + // below would flatten them to INTERNAL and drop the `choices[]` / + // `total_matches` / `hint` payload the AMBIGUOUS_REPO contract promises. + if (err instanceof RepoResolveError) { + if (err.code === "AMBIGUOUS_REPO" && err.ambiguous !== undefined) { + return toToolResult( + toolAmbiguousRepoError({ + message: err.message, + hint: err.hint, + choices: err.ambiguous.choices, + totalMatches: err.ambiguous.totalMatches, + }), + ); + } + return toToolResult(toolError(err.code, err.message, err.hint)); + } return toToolResult(toolErrorFromUnknown(err)); } } diff --git a/packages/mcp/src/tools/remove-dead-code.test.ts b/packages/mcp/src/tools/remove-dead-code.test.ts deleted file mode 100644 index 4aac8e4d..00000000 --- a/packages/mcp/src/tools/remove-dead-code.test.ts +++ /dev/null @@ -1,309 +0,0 @@ -// biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures -/** - * `remove_dead_code` MCP tool tests. - * - * Covers three load-bearing contracts: - * 1. `dryRun=true` (default) returns the edit plan without touching disk. - * 2. `dryRun=false + apply=true` writes the deletions through the injected - * `FsAbstraction`. - * 3. `dryRun=false` without `apply=true` is refused with INVALID_INPUT. - */ - -import { strict as assert } from "node:assert"; -import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join, resolve } from "node:path"; -import { test } from "node:test"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import type { FsAbstraction } from "@opencodehub/analysis"; -import type { - CodeRelation, - GraphNode, - KnowledgeGraph, - RelationType, -} from "@opencodehub/core-types"; -import type { - BulkLoadStats, - EmbeddingRow, - IGraphStore, - ListEdgesByTypeOptions, - ListEdgesOptions, - ListNodesOptions, - SearchQuery, - SearchResult, - StoreMeta, - TraverseQuery, - TraverseResult, - VectorQuery, - VectorResult, -} from "@opencodehub/storage"; -import { ConnectionPool } from "../connection-pool.js"; -import { type RemoveDeadCodeContext, registerRemoveDeadCodeTool } from "./remove-dead-code.js"; - -/** - * Wrap an in-memory IGraphStore-shaped fake as the composed `Store` - * (`OpenStoreResult`) that the connection pool returns. The same fake - * instance backs both `graph` and `temporal` views. - */ -function wrapAsStore(fake: unknown): import("@opencodehub/storage").Store { - return { - graph: fake as import("@opencodehub/storage").IGraphStore, - temporal: fake as import("@opencodehub/storage").ITemporalStore, - graphFile: "/in-memory/graph.lbug", - temporalFile: "/in-memory/temporal.duckdb", - close: async () => { - const closer = (fake as { close?: () => Promise }).close; - if (typeof closer === "function") await closer.call(fake); - }, - }; -} - -interface FakeNode { - readonly id: string; - readonly name: string; - readonly kind: string; - readonly filePath: string; - readonly startLine: number; - readonly endLine: number; - readonly isExported: boolean; -} - -/** - * In-memory fake of the typed-finder surface that `classifyDeadness` and - * `enrichWithEndLines` consume: `listNodes`, `listEdges`, - * `listEdgesByType`. Edges are absent from these tests (the dead-code - * path looks for inbound referrers but we only seed isolated dead - * candidates). - */ -function makeFakeStore(nodes: readonly FakeNode[]): IGraphStore { - const nodeAsGraphNode = (n: FakeNode): GraphNode => n as unknown as GraphNode; - - const api = { - open: async () => {}, - close: async () => {}, - createSchema: async () => {}, - bulkLoad: async (_g: KnowledgeGraph): Promise => ({ - nodeCount: 0, - edgeCount: 0, - durationMs: 0, - }), - upsertEmbeddings: async (_r: readonly EmbeddingRow[]): Promise => {}, - listNodes: async (opts: ListNodesOptions = {}): Promise => { - const kinds = opts.kinds; - if (kinds !== undefined && kinds.length === 0) return []; - const idsRaw = opts.ids; - if (idsRaw !== undefined && idsRaw.length === 0) return []; - const kindSet = kinds !== undefined ? new Set(kinds) : undefined; - const idSet = idsRaw !== undefined ? new Set(idsRaw) : undefined; - return nodes - .filter((n) => { - if (kindSet !== undefined && !kindSet.has(n.kind)) return false; - if (idSet !== undefined && !idSet.has(n.id)) return false; - return true; - }) - .map(nodeAsGraphNode); - }, - listEdges: async (_opts: ListEdgesOptions = {}): Promise => [], - listEdgesByType: async ( - _type: RelationType, - _opts: ListEdgesByTypeOptions = {}, - ): Promise => [], - search: async (_q: SearchQuery): Promise => [], - vectorSearch: async (_q: VectorQuery): Promise => [], - traverse: async (_q: TraverseQuery): Promise => [], - getMeta: async (): Promise => undefined, - setMeta: async (_m: StoreMeta): Promise => {}, - healthCheck: async () => ({ ok: true }), - } as unknown as IGraphStore; - return api; -} - -class FakeFs implements FsAbstraction { - readonly files: Map; - - constructor(seed: Readonly> = {}) { - this.files = new Map(Object.entries(seed)); - } - - readFile(absPath: string): Promise { - const v = this.files.get(absPath); - if (v === undefined) return Promise.reject(new Error(`ENOENT: ${absPath}`)); - return Promise.resolve(v); - } - - writeFileAtomic(absPath: string, content: string): Promise { - this.files.set(absPath, content); - return Promise.resolve(); - } -} - -interface Harness { - readonly ctx: RemoveDeadCodeContext; - readonly server: McpServer; - readonly repoPath: string; - readonly fs: FakeFs; -} - -async function withHarness( - nodes: FakeNode[], - files: Readonly>, - fn: (h: Harness) => Promise, -): Promise { - const home = await mkdtemp(resolve(tmpdir(), "codehub-mcp-rm-dead-")); - try { - const repoPath = resolve(home, "fakerepo"); - await mkdir(repoPath, { recursive: true }); - const regDir = resolve(home, ".codehub"); - await mkdir(regDir, { recursive: true }); - await writeFile( - resolve(regDir, "registry.json"), - JSON.stringify({ - fakerepo: { - name: "fakerepo", - path: repoPath, - indexedAt: "2026-04-18T00:00:00Z", - nodeCount: nodes.length, - edgeCount: 0, - lastCommit: "abc123", - }, - }), - ); - // Seed the fake filesystem with repo-absolute paths so `resolveAbs` in the - // handler produces keys the FakeFs recognises. - const seed: Record = {}; - for (const [rel, content] of Object.entries(files)) { - seed[join(repoPath, rel)] = content; - } - const fs = new FakeFs(seed); - const pool = new ConnectionPool({ max: 2, ttlMs: 60_000 }, async () => - wrapAsStore(makeFakeStore(nodes)), - ); - const ctx: RemoveDeadCodeContext = { pool, home, fsFactory: () => fs }; - const server = new McpServer( - { name: "test", version: "0.0.0" }, - { capabilities: { tools: {} } }, - ); - try { - await fn({ ctx, server, repoPath, fs }); - } finally { - await pool.shutdown(); - } - } finally { - await rm(home, { recursive: true, force: true }); - } -} - -type RegisteredTool = { handler: (args: unknown, extra: unknown) => Promise }; - -function getHandler(server: McpServer, name: string): RegisteredTool["handler"] { - // biome-ignore lint/suspicious/noExplicitAny: SDK internal field for test-only access - const map = (server as any)._registeredTools as Record; - const entry = map[name]; - assert.ok(entry, `tool not registered: ${name}`); - return entry.handler.bind(entry); -} - -const DEAD_FILE_REL = "src/dead.ts"; -const DEAD_FILE_SRC = [ - "function lonely() {", // line 1 - " return 1;", // line 2 - "}", // line 3 - "", // line 4 - "export const alive = 5;", // line 5 - "", -].join("\n"); - -function deadNodes(): FakeNode[] { - return [ - { - id: "Function:src/dead.ts:lonely", - name: "lonely", - kind: "Function", - filePath: DEAD_FILE_REL, - startLine: 1, - endLine: 3, - isExported: false, - }, - ]; -} - -test("remove_dead_code dry-run returns the edit plan without writing", async () => { - await withHarness( - deadNodes(), - { [DEAD_FILE_REL]: DEAD_FILE_SRC }, - async ({ ctx, server, repoPath, fs }) => { - registerRemoveDeadCodeTool(server, ctx); - const handler = getHandler(server, "remove_dead_code"); - - const result = await handler({ repo: "fakerepo" }, {}); - const sc = result.structuredContent as { - filesAffected: number; - totalDeletions: number; - edits: Array<{ filePath: string; startLine: number; endLine: number; content: string }>; - applied: boolean; - }; - - assert.equal(sc.applied, false); - assert.equal(sc.filesAffected, 1); - assert.equal(sc.totalDeletions, 1); - assert.equal(sc.edits.length, 1); - assert.equal(sc.edits[0]?.filePath, DEAD_FILE_REL); - assert.equal(sc.edits[0]?.startLine, 1); - assert.equal(sc.edits[0]?.endLine, 3); - assert.equal(sc.edits[0]?.content, "function lonely() {\n return 1;\n}"); - - // File on disk (well, in the fake) must still match the original source. - const absPath = join(repoPath, DEAD_FILE_REL); - assert.equal(fs.files.get(absPath), DEAD_FILE_SRC); - }, - ); -}); - -test("remove_dead_code apply=true rewrites the file with the dead range removed", async () => { - await withHarness( - deadNodes(), - { [DEAD_FILE_REL]: DEAD_FILE_SRC }, - async ({ ctx, server, repoPath, fs }) => { - registerRemoveDeadCodeTool(server, ctx); - const handler = getHandler(server, "remove_dead_code"); - - const result = await handler({ repo: "fakerepo", dryRun: false, apply: true }, {}); - const sc = result.structuredContent as { - applied: boolean; - filesAffected: number; - totalDeletions: number; - }; - assert.equal(sc.applied, true); - assert.equal(sc.filesAffected, 1); - assert.equal(sc.totalDeletions, 1); - - const rewritten = fs.files.get(join(repoPath, DEAD_FILE_REL)); - assert.ok(rewritten !== undefined); - // Lines 1-3 removed; blank line 4, the export line, and trailing newline remain. - assert.equal(rewritten, "\nexport const alive = 5;\n"); - }, - ); -}); - -test("remove_dead_code refuses dryRun=false without explicit apply=true", async () => { - await withHarness( - deadNodes(), - { [DEAD_FILE_REL]: DEAD_FILE_SRC }, - async ({ ctx, server, repoPath, fs }) => { - registerRemoveDeadCodeTool(server, ctx); - const handler = getHandler(server, "remove_dead_code"); - - const result = await handler({ repo: "fakerepo", dryRun: false }, {}); - const sc = result.structuredContent as { - error?: { code: string; message: string }; - }; - assert.equal(result.isError, true); - assert.equal(sc.error?.code, "INVALID_INPUT"); - assert.ok(sc.error?.message.includes("apply=true")); - - // Still untouched. - assert.equal(fs.files.get(join(repoPath, DEAD_FILE_REL)), DEAD_FILE_SRC); - }, - ); -}); diff --git a/packages/mcp/src/tools/remove-dead-code.ts b/packages/mcp/src/tools/remove-dead-code.ts deleted file mode 100644 index d8519a81..00000000 --- a/packages/mcp/src/tools/remove-dead-code.ts +++ /dev/null @@ -1,328 +0,0 @@ -/** - * `remove_dead_code` — delete provably-dead symbols from disk. - * - * Pipeline: - * 1. Call {@link classifyDeadness} to get the `dead` bucket (non-exported, - * no inbound referrers). - * 2. Join against `nodes.end_line` via a batched id-IN lookup so the edit - * plan carries full `[startLine, endLine]` ranges. `DeadSymbol` only - * exposes `startLine`; we avoid touching the classifier surface. - * 3. Group deletions per file, read each file via the injected - * {@link FsAbstraction}, snapshot the lines being removed, and emit an - * edit plan. - * 4. Apply (only when `apply=true` is set explicitly — even with - * dryRun=false) by rewriting the file without the deleted line ranges - * and writing atomically through `fs.writeFileAtomic`. - * - * Defaults: - * - `dryRun=true`, `apply=false` — returns the plan, writes nothing. - * - * Destructive only when `apply=true`; otherwise read-only. - */ -// biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures - -import { isAbsolute, join } from "node:path"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { - classifyDeadness, - createNodeFs, - type DeadSymbol, - type FsAbstraction, -} from "@opencodehub/analysis"; -import type { IGraphStore } from "@opencodehub/storage"; -import { z } from "zod"; -import { toolError, toolErrorFromUnknown } from "../error-envelope.js"; -import { withNextSteps } from "../next-step-hints.js"; -import { stalenessFromMeta } from "../staleness.js"; -import { - fromToolResult, - repoArgShape, - type ToolContext, - type ToolResult, - toToolResult, - withStore, -} from "./shared.js"; - -const RemoveDeadCodeInput = { - ...repoArgShape, - dryRun: z - .boolean() - .optional() - .describe("Preview-only when true (DEFAULT). Set false AND pass apply=true to write."), - filePathPattern: z - .string() - .optional() - .describe("Substring filter — only dead symbols whose file path matches are considered."), - apply: z - .boolean() - .optional() - .describe("Must be explicitly true to write to disk. Ignored (and no-op) when dryRun is true."), -}; - -/** Edit plan entry — one contiguous line range to be removed from a file. */ -export interface RemoveDeadCodeEdit { - readonly filePath: string; - readonly startLine: number; - readonly endLine: number; - /** The source lines being deleted, joined with `\n`, for audit logs. */ - readonly content: string; -} - -interface EnrichedDead extends DeadSymbol { - readonly endLine: number; -} - -/** Factory override hook: tests inject an in-memory fs. */ -export interface RemoveDeadCodeContext extends ToolContext { - readonly fsFactory?: () => FsAbstraction; -} - -interface RemoveDeadCodeArgs { - readonly repo?: string | undefined; - readonly repo_uri?: string | undefined; - readonly dryRun?: boolean | undefined; - readonly filePathPattern?: string | undefined; - readonly apply?: boolean | undefined; -} - -export async function runRemoveDeadCode( - ctx: RemoveDeadCodeContext, - args: RemoveDeadCodeArgs, -): Promise { - const dryRun = args.dryRun ?? true; - const apply = args.apply === true; - const pattern = args.filePathPattern; - - const call = await withStore(ctx, args, async (store, resolved) => { - try { - // Refuse an apply that skipped the explicit opt-in. Even when the - // caller disables dryRun, we require `apply=true` as a second - // confirmation — the tool refuses to persist deletions without an explicit opt-in. - if (!dryRun && !apply) { - return toolError( - "INVALID_INPUT", - "remove_dead_code refuses to write without an explicit apply=true flag.", - "Pass both dryRun=false and apply=true to persist deletions, or leave dryRun=true for a plan.", - ); - } - - const result = await classifyDeadness(store.graph); - const candidates = result.dead.filter( - (s) => pattern === undefined || s.filePath.includes(pattern), - ); - if (candidates.length === 0) { - return withNextSteps( - `No dead symbols matched${pattern ? ` filePath~${pattern}` : ""}. Nothing to remove.`, - { - filesAffected: 0, - totalDeletions: 0, - edits: [], - applied: false, - }, - ["call `list_dead_code` with includeUnreachableExports=true to inspect exports"], - stalenessFromMeta(resolved.meta), - ); - } - - const enriched = await enrichWithEndLines(store.graph, candidates); - const groupedByFile = groupByFile(enriched); - - const fsFactory = ctx.fsFactory ?? createNodeFs; - const fs = fsFactory(); - - const edits: RemoveDeadCodeEdit[] = []; - const readFailures: string[] = []; - for (const [filePath, syms] of groupedByFile) { - const abs = resolveAbs(resolved.repoPath, filePath); - let source: string; - try { - source = await fs.readFile(abs); - } catch (err) { - readFailures.push(`${filePath}: ${(err as Error).message}`); - continue; - } - const lines = source.split("\n"); - for (const sym of syms) { - const start = clampLine(sym.startLine, lines.length); - const end = clampLine(sym.endLine, lines.length); - if (start === 0 || end === 0 || end < start) continue; - const snippet = lines.slice(start - 1, end).join("\n"); - edits.push({ - filePath, - startLine: start, - endLine: end, - content: snippet, - }); - } - } - - const filesAffected = new Set(edits.map((e) => e.filePath)).size; - const totalDeletions = edits.length; - - let applied = false; - const writeFailures: string[] = []; - if (!dryRun && apply) { - const editsByFile = new Map(); - for (const e of edits) { - const bucket = editsByFile.get(e.filePath) ?? []; - bucket.push(e); - editsByFile.set(e.filePath, bucket); - } - for (const [filePath, fileEdits] of editsByFile) { - const abs = resolveAbs(resolved.repoPath, filePath); - let source: string; - try { - source = await fs.readFile(abs); - } catch (err) { - writeFailures.push(`${filePath}: read failed — ${(err as Error).message}`); - continue; - } - const rewritten = applyDeletions(source, fileEdits); - try { - await fs.writeFileAtomic(abs, rewritten); - } catch (err) { - writeFailures.push(`${filePath}: write failed — ${(err as Error).message}`); - } - } - applied = writeFailures.length === 0; - } - - const header = `remove_dead_code (${applied ? "applied" : dryRun ? "dry-run" : "refused"}): ${filesAffected} file(s), ${totalDeletions} deletion(s).`; - const lines: string[] = [header]; - for (const e of edits.slice(0, 50)) { - lines.push(` - ${e.filePath}:${e.startLine}-${e.endLine}`); - } - if (edits.length > 50) lines.push(` … ${edits.length - 50} more`); - if (readFailures.length > 0) { - lines.push(`Read failures (${readFailures.length}):`); - for (const f of readFailures.slice(0, 20)) lines.push(` ⚠ ${f}`); - } - if (writeFailures.length > 0) { - lines.push(`Write failures (${writeFailures.length}):`); - for (const f of writeFailures.slice(0, 20)) lines.push(` ⚠ ${f}`); - } - - const next: string[] = []; - if (dryRun && totalDeletions > 0) { - next.push("if the plan looks right, re-call with dryRun=false AND apply=true"); - } - if (applied) { - next.push("re-index the repo with `codehub analyze` so the graph reflects the removals"); - } - if (totalDeletions === 0) { - next.push("no deletions staged — confirm dead symbols exist via `list_dead_code`"); - } - - return withNextSteps( - lines.join("\n"), - { - filesAffected, - totalDeletions, - edits, - applied, - ...(readFailures.length > 0 ? { readFailures } : {}), - ...(writeFailures.length > 0 ? { writeFailures } : {}), - }, - next, - stalenessFromMeta(resolved.meta), - ); - } catch (err) { - return toolErrorFromUnknown(err); - } - }); - return toToolResult(call); -} - -export function registerRemoveDeadCodeTool(server: McpServer, ctx: RemoveDeadCodeContext): void { - server.registerTool( - "remove_dead_code", - { - title: "Remove dead symbols from disk", - description: - "Generate (and optionally apply) an edit plan that deletes every provably-dead symbol's source range. Safe by default: returns a plan without writing unless BOTH dryRun=false AND apply=true are passed.", - inputSchema: RemoveDeadCodeInput, - annotations: { - readOnlyHint: false, - destructiveHint: true, - idempotentHint: false, - openWorldHint: false, - }, - }, - async (args) => fromToolResult(await runRemoveDeadCode(ctx, args)), - ); -} - -async function enrichWithEndLines( - graph: IGraphStore, - dead: readonly DeadSymbol[], -): Promise { - if (dead.length === 0) return []; - const ids = dead.map((d) => d.id); - const partners = await graph.listNodes({ ids }); - const endById = new Map(); - for (const n of partners) { - const raw = (n as unknown as Record)["endLine"]; - const end = typeof raw === "number" && Number.isFinite(raw) ? raw : 0; - endById.set(n.id, end); - } - const out: EnrichedDead[] = []; - for (const d of dead) { - const endLine = endById.get(d.id) ?? d.startLine; - out.push({ ...d, endLine }); - } - return out; -} - -function groupByFile(syms: readonly EnrichedDead[]): Map { - const out = new Map(); - for (const s of syms) { - const bucket = out.get(s.filePath) ?? []; - bucket.push(s); - out.set(s.filePath, bucket); - } - // Sort each bucket by startLine so downstream line-slicing is predictable. - for (const [, bucket] of out) { - bucket.sort((a, b) => a.startLine - b.startLine); - } - return out; -} - -function resolveAbs(repoRoot: string, relPath: string): string { - return isAbsolute(relPath) ? relPath : join(repoRoot, relPath); -} - -function clampLine(line: number, total: number): number { - if (!Number.isFinite(line) || line <= 0) return 0; - if (line > total) return total; - return Math.floor(line); -} - -/** - * Drop each edit's `[startLine, endLine]` range from the source, processing - * ranges in descending order so earlier line numbers stay valid. Overlapping - * ranges are collapsed — we keep the union. - */ -function applyDeletions(source: string, edits: readonly RemoveDeadCodeEdit[]): string { - if (edits.length === 0) return source; - const lines = source.split("\n"); - // Merge overlapping / adjacent ranges, then delete right-to-left. - const sorted = [...edits].sort((a, b) => a.startLine - b.startLine); - const merged: { start: number; end: number }[] = []; - for (const e of sorted) { - const last = merged[merged.length - 1]; - if (last && e.startLine <= last.end + 1) { - last.end = Math.max(last.end, e.endLine); - } else { - merged.push({ start: e.startLine, end: e.endLine }); - } - } - for (let i = merged.length - 1; i >= 0; i -= 1) { - const range = merged[i]; - if (range === undefined) continue; - const startIdx = range.start - 1; - const count = range.end - range.start + 1; - if (startIdx < 0 || startIdx >= lines.length) continue; - lines.splice(startIdx, count); - } - return lines.join("\n"); -} diff --git a/packages/mcp/src/tools/rename.ts b/packages/mcp/src/tools/rename.ts deleted file mode 100644 index 4c818f44..00000000 --- a/packages/mcp/src/tools/rename.ts +++ /dev/null @@ -1,170 +0,0 @@ -/** - * `rename` — graph-coordinated symbol rename. - * - * Dry-run is the default: callers must explicitly set `dry_run: false` to - * apply edits. All file I/O is mediated by the analysis package's - * `createNodeFs()` abstraction so we do not touch disk from this module. - */ - -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { z } from "zod"; -import { callRunRename } from "../analysis-bridge.js"; -import { toolErrorFromUnknown } from "../error-envelope.js"; -import { withNextSteps } from "../next-step-hints.js"; -import { stalenessFromMeta } from "../staleness.js"; -import { - fromToolResult, - repoArgShape, - type ToolContext, - type ToolResult, - toToolResult, - withStore, -} from "./shared.js"; - -const RenameInput = { - symbol_name: z - .string() - .min(1) - .describe("The symbol to rename. Combine with `file` if the name is ambiguous."), - new_name: z - .string() - .min(1) - .describe("The new identifier. Must be a valid identifier in the target language."), - dry_run: z - .boolean() - .optional() - .describe( - "Preview-only when true (DEFAULT). Set to false explicitly to write changes to disk.", - ), - file: z - .string() - .optional() - .describe("File path suffix to narrow the rename to a specific definition."), - ...repoArgShape, -}; - -interface RenameArgs { - readonly symbol_name: string; - readonly new_name: string; - readonly dry_run?: boolean | undefined; - readonly file?: string | undefined; - readonly repo?: string | undefined; - readonly repo_uri?: string | undefined; -} - -export async function runRename(ctx: ToolContext, args: RenameArgs): Promise { - const dryRun = args.dry_run ?? true; - const call = await withStore(ctx, args, async (store, resolved) => { - try { - const q: { - symbolName: string; - newName: string; - dryRun?: boolean; - scope?: { filePath?: string }; - } = { - symbolName: args.symbol_name, - newName: args.new_name, - dryRun, - }; - if (args.file) q.scope = { filePath: args.file }; - const result = await callRunRename(store.graph, q, resolved.repoPath); - - if (result.ambiguous) { - return withNextSteps( - `"${args.symbol_name}" is ambiguous — pass \`file\` to narrow the target.${ - result.hint ? `\n${result.hint}` : "" - }`, - { - status: "rejected", - ambiguous: true, - files_affected: 0, - total_edits: 0, - graph_edits: 0, - text_edits: 0, - changes: [], - }, - ["call `context` first to pick a concrete definition"], - stalenessFromMeta(resolved.meta), - ); - } - - const graphEdits = result.edits.filter((e) => e.source === "graph").length; - const textEdits = result.edits.length - graphEdits; - const filesAffected = new Set(result.edits.map((e) => e.filePath)).size; - const status: "dry-run" | "applied" | "rejected" = result.applied ? "applied" : "dry-run"; - - const header = `Rename ${args.symbol_name} → ${args.new_name} (${status})`; - const lines: string[] = [header]; - lines.push( - `Files affected: ${filesAffected} | edits: ${result.edits.length} | graph: ${graphEdits} | text: ${textEdits}`, - ); - for (const edit of result.edits.slice(0, 50)) { - lines.push( - ` ${edit.source === "graph" ? "✓" : "?"} ${edit.filePath}:${edit.line}:${edit.column} "${edit.before}" → "${edit.after}" (conf ${edit.confidence.toFixed(2)})`, - ); - } - if (result.edits.length > 50) { - lines.push(` … ${result.edits.length - 50} more`); - } - if (result.skipped.length > 0) { - lines.push(`Skipped files (${result.skipped.length}):`); - for (const s of result.skipped.slice(0, 20)) { - lines.push(` ⚠ ${s.filePath}: ${s.reason}`); - } - } - if (result.hint) lines.push(`Hint: ${result.hint}`); - - const next: string[] = []; - if (dryRun && result.edits.length > 0) { - next.push("if the preview looks right, re-call with dry_run=false to apply the edits"); - } - if (textEdits > 0) { - next.push( - `review the ${textEdits} text-based edit(s) before applying — they are best-effort`, - ); - } - if (result.edits.length === 0) { - next.push( - "no edits planned — confirm the symbol exists via `context` and try again with `file`", - ); - } - - return withNextSteps( - lines.join("\n"), - { - status, - files_affected: filesAffected, - total_edits: result.edits.length, - graph_edits: graphEdits, - text_edits: textEdits, - changes: result.edits, - skipped: result.skipped, - }, - next, - stalenessFromMeta(resolved.meta), - ); - } catch (err) { - return toolErrorFromUnknown(err); - } - }); - return toToolResult(call); -} - -export function registerRenameTool(server: McpServer, ctx: ToolContext): void { - server.registerTool( - "rename", - { - title: "Coordinated symbol rename", - description: - "Produce a graph-coordinated rename plan. Graph-backed edits (tagged `graph`) are high-confidence; fall-back text-search edits (tagged `text`) should be reviewed before applying. DEFAULT is dry-run — the tool will not write to disk unless `dry_run: false` is passed explicitly.", - inputSchema: RenameInput, - annotations: { - readOnlyHint: false, - destructiveHint: true, - idempotentHint: false, - openWorldHint: false, - }, - }, - async (args) => fromToolResult(await runRename(ctx, args)), - ); -} diff --git a/packages/mcp/src/tools/run-smoke.test.ts b/packages/mcp/src/tools/run-smoke.test.ts index eb962162..0020ab67 100644 --- a/packages/mcp/src/tools/run-smoke.test.ts +++ b/packages/mcp/src/tools/run-smoke.test.ts @@ -49,8 +49,6 @@ import { runListRepos } from "./list-repos.js"; import { runOwners } from "./owners.js"; import { runProjectProfile } from "./project-profile.js"; import { runQuery } from "./query.js"; -import { runRemoveDeadCode } from "./remove-dead-code.js"; -import { runRename } from "./rename.js"; import { runRiskTrends } from "./risk-trends.js"; import { runRouteMap } from "./route-map.js"; import { runScan } from "./scan.js"; @@ -237,17 +235,6 @@ test("runScan returns a ToolResult shape", async () => { }); }); -test("runRename returns a ToolResult shape", async () => { - await withHarness(async (ctx) => { - const result = await runRename(ctx, { - symbol_name: "foo", - new_name: "bar", - repo: "fakerepo", - }); - assertToolResultShape(result); - }); -}); - test("runApiImpact returns a ToolResult shape", async () => { await withHarness(async (ctx) => { const result = await runApiImpact(ctx, { repo: "fakerepo" }); @@ -352,10 +339,3 @@ test("runListDeadCode returns a ToolResult shape", async () => { assertToolResultShape(result); }); }); - -test("runRemoveDeadCode returns a ToolResult shape", async () => { - await withHarness(async (ctx) => { - const result = await runRemoveDeadCode(ctx, { repo: "fakerepo" }); - assertToolResultShape(result); - }); -}); diff --git a/packages/pack/README.md b/packages/pack/README.md index 3e1f896b..fdd6a96b 100644 --- a/packages/pack/README.md +++ b/packages/pack/README.md @@ -1,3 +1,59 @@ # @opencodehub/pack -Deterministic code-pack generator producing the 9-item BOM (manifest, skeleton, file-tree, deps, ast-chunks, xrefs, embeddings-sidecar, findings, licenses). See `src/types.ts` for the contract types (`PackManifest`, `BomItem`, `PackPins`, `DeterminismClass`, `PackOpts`). +Deterministic code-pack generator. `generatePack` assembles a 9-item +"bill of materials" (BOM) for a repo plus a manifest, writing every file +into the output directory so the same inputs always produce byte-identical +bytes and the same `pack_hash`. + +## Public surface + +- `generatePack(opts, internal?)` — assemble and write the BOM + manifest. +- `buildManifest` / `serializeManifest` — manifest construction + `pack_hash`. +- Per-item builders, re-exported for direct use: `buildSkeleton`, + `buildFileTree`, `buildDeps`, `buildAstChunks`, `buildXrefs`, + `buildFindings`, `buildLicenses`, `buildReadme`, + `writeEmbeddingsSidecar`. +- Types: `PackManifest`, `BomItem`, `PackPins`, `DeterminismClass`, + `PackOpts` (see `src/types.ts`). + +## The 9-item BOM + +Eight bodies are always written; the Parquet embeddings sidecar is item 7 +and is present only when the store has embeddings. The manifest is written +last so a crash mid-run leaves an obviously-incomplete pack. + +1. `skeleton.jsonl` — symbol skeleton (functions, classes, modules). +2. `file-tree.jsonl` — file tree with framework labels. +3. `deps.jsonl` — dependency / lockfile slice with exact versions. +4. `ast-chunks.jsonl` — top-N AST-chunked files with byte offsets. +5. `xrefs.jsonl` — SCIP-grounded cross-references (communities + calls). +6. `findings.jsonl` — SARIF findings grouped by severity and rule. +7. `embeddings.parquet` — optional embeddings sidecar (absent when the + store has no embeddings). +8. `licenses.md` — aggregated dependency LICENSES by tier (BLOCK / WARN / + OK) plus a `## Notices` section carrying any `NOTICE` / `NOTICE.md` / + `NOTICES` content found at the repo root. +9. `readme.md` — this BOM's own README, interpolating the manifest and + restating the determinism contract. + +The `manifest.json` (`PackManifest`) lists every written BOM body in +`files[]` (excluding itself and `readme.md`) and carries `pack_hash`. + +## Determinism contract + +Same `(commit, tokenizer_id, budget_tokens, chonkie_version, +duckdb_version, grammar_commits)` produces a byte-identical pack and the +same `pack_hash`. All file bytes use LF line endings; CRLF and lone-CR +inputs are normalized to LF before chunking and hashing, so two repos +differing only in line-ending style produce the same `pack_hash`. + +`determinism_class` records how strong that guarantee is for a given run: + +- `strict` — every BOM file is byte-identity reproducible. +- `best_effort` — the tokenizer is a Claude / Anthropic model whose + tokenization is not guaranteed stable across versions; non-tokenizer + fields are still byte-identity. +- `degraded` — the AST chunker fell back to a line-split (e.g. tree-sitter + grammar unavailable) or the embeddings sidecar could not bind the + temporal store. The pack is still reproducible across two runs of the + same code path, but cross-environment chunks may differ. diff --git a/packages/pack/package.json b/packages/pack/package.json index 9c8560d4..2e9e87b6 100644 --- a/packages/pack/package.json +++ b/packages/pack/package.json @@ -1,7 +1,7 @@ { "name": "@opencodehub/pack", "version": "0.2.4", - "description": "OpenCodeHub — deterministic M5 9-item code-pack BOM", + "description": "OpenCodeHub — deterministic 9-item code-pack BOM", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/pack/src/ast-chunker.test.ts b/packages/pack/src/ast-chunker.test.ts index eba1359a..006d3e53 100644 --- a/packages/pack/src/ast-chunker.test.ts +++ b/packages/pack/src/ast-chunker.test.ts @@ -14,6 +14,7 @@ * degraded. * - H. File without a language goes through the line-split fallback per file * but the overall result is still strict if other files chunk OK. + * - J. Lone-CR (classic-Mac) input normalizes to LF, matching LF-only chunks. */ import { strict as assert } from "node:assert"; @@ -191,6 +192,25 @@ test("H. file without language uses the line-split fallback per file but result assert.equal(txtChunk.language, undefined); }); +test("J. lone-CR input normalizes to LF and matches the LF-only chunks", async () => { + const cr: AstChunkerOpts = { + ...BASE_OPTS, + // Classic-Mac line endings: lone CR, no LF. + files: [{ path: "x.ts", bytes: utf8("a\rb\r"), language: "typescript" }], + }; + const lf: AstChunkerOpts = { + ...BASE_OPTS, + files: [{ path: "x.ts", bytes: utf8("a\nb\n"), language: "typescript" }], + }; + const fromCr = await buildAstChunks(cr, { _loadChonkie: makeFakeLoader() }); + const fromLf = await buildAstChunks(lf, { _loadChonkie: makeFakeLoader() }); + // After lone-CR→LF the texts are byte-identical, so chunks (and therefore + // the eventual pack_hash) must match regardless of input line-ending style. + assert.equal(canonicalJson(fromCr.chunks), canonicalJson(fromLf.chunks)); + assert.equal(fromCr.chunks[0]?.startByte, 0); + assert.equal(fromCr.chunks[0]?.endByte, 4); +}); + test("I. degraded fallback emits chunks bounded by ~chunkSize*4 chars", async () => { // Build a long single-line input so the line-split has to slice mid-file. const big = "abcdefghij\n".repeat(100); // 1100 chars across 100 lines. diff --git a/packages/pack/src/ast-chunker.ts b/packages/pack/src/ast-chunker.ts index 60aa37f1..22dcb1d9 100644 --- a/packages/pack/src/ast-chunker.ts +++ b/packages/pack/src/ast-chunker.ts @@ -2,9 +2,10 @@ * BOM body item: AST-aware code chunks (item 5/9). * * Wraps `@chonkiejs/core`'s `CodeChunker`, which builds chunks from a - * tree-sitter AST (children grouped by token budget). Each input file is - * CRLF→LF normalized BEFORE chunking — two repos differing only by - * line-ending style must produce the same `pack_hash`. + * tree-sitter AST (children grouped by token budget). Each input file has + * its line endings normalized to LF (CRLF and lone CR alike) BEFORE + * chunking — two repos differing only by line-ending style must produce + * the same `pack_hash`. * * Determinism: * - Strict path: `CodeChunker.create({language})` succeeds for every @@ -282,13 +283,15 @@ function pushLineSplitChunks( } } -/** Decode raw bytes as UTF-8 and CRLF→LF normalize for line-ending byte-identity. */ +/** Decode raw bytes as UTF-8 and normalize line endings for byte-identity. */ function decodeAndNormalize(bytes: Uint8Array): string { // `fatal: false` so malformed sequences become U+FFFD instead of throwing — // the BOM is best-effort over arbitrary repo bytes; it does not validate // encoding here. const decoded = new TextDecoder("utf-8", { fatal: false }).decode(bytes); - return decoded.replace(/\r\n/g, "\n"); + // Collapse CRLF and lone CR (classic-Mac) to LF so two repos differing only + // by line-ending style chunk identically and produce the same pack_hash. + return decoded.replace(/\r\n?/g, "\n"); } /** Path ASC primary sort. */ diff --git a/packages/pack/src/licenses.test.ts b/packages/pack/src/licenses.test.ts index d2558697..4ed2e40c 100644 --- a/packages/pack/src/licenses.test.ts +++ b/packages/pack/src/licenses.test.ts @@ -10,6 +10,9 @@ * - F. No NOTICE file → empty `noticesMd`. * - G. CRLF in NOTICE content normalizes to LF. * - H. Empty graph still produces a valid markdown body with tier=OK. + * - K. NOTICE content is folded into the written `licensesMd` body. + * - L. No NOTICE file → no `## Notices` heading in `licensesMd`. + * - M. Lone-CR (classic-Mac) NOTICE content normalizes to LF. */ import { strict as assert } from "node:assert"; @@ -169,3 +172,38 @@ test("J. licensesMd ends in a single trailing newline", async () => { assert.ok(result.licensesMd.endsWith("\n")); assert.ok(!result.licensesMd.endsWith("\n\n")); }); + +test("K. NOTICE content is folded into licensesMd under a Notices heading", async () => { + const store = makeStore(DEPS_MIXED); + const reader = async (path: string) => { + if (path === "/tmp/repo/NOTICE") return "Copyright 2026 Example Corp."; + return undefined; + }; + const result = await buildLicenses({ store, repoPath: "/tmp/repo", readFile: reader }); + // The notice payload must reach the written BOM body, not just noticesMd. + assert.ok(result.licensesMd.includes("## Notices")); + assert.ok(result.licensesMd.includes("Copyright 2026 Example Corp.")); + // Single trailing newline survives the appended section. + assert.ok(result.licensesMd.endsWith("\n")); + assert.ok(!result.licensesMd.endsWith("\n\n")); +}); + +test("L. no NOTICE file → licensesMd has no Notices heading", async () => { + const store = makeStore(DEPS_MIXED); + const result = await buildLicenses({ store, repoPath: "/tmp/repo", readFile: noopReader }); + assert.ok(!result.licensesMd.includes("## Notices")); +}); + +test("M. lone-CR NOTICE content normalizes to LF", async () => { + const store = makeStore(DEPS_MIXED); + const reader = async (path: string) => { + // Classic-Mac line endings: lone CR, no LF. + if (path === "/tmp/repo/NOTICE") return "line one\rline two\r"; + return undefined; + }; + const result = await buildLicenses({ store, repoPath: "/tmp/repo", readFile: reader }); + assert.ok(!result.noticesMd.includes("\r")); + assert.ok(result.noticesMd.includes("line one\nline two")); + // Folded body is CR-free too. + assert.ok(!result.licensesMd.includes("\r")); +}); diff --git a/packages/pack/src/licenses.ts b/packages/pack/src/licenses.ts index ae256c7d..f587308a 100644 --- a/packages/pack/src/licenses.ts +++ b/packages/pack/src/licenses.ts @@ -2,14 +2,16 @@ * BOM body item: aggregated LICENSES + NOTICES (item 9 partial). * * Reads `Dependency` nodes via `IGraphStore.listNodes()`, classifies them - * via `classifyDependencies` from `@opencodehub/analysis`, and renders - * both: + * via `classifyDependencies` from `@opencodehub/analysis`, and renders: * - * - `licensesMd` — Markdown body listing every dependency by tier - * (BLOCK / WARN / OK) and a per-package section in - * `(ecosystem, name, version)` ASC order. - * - `noticesMd` — concatenated `NOTICE` / `NOTICES` / `NOTICE.md` files - * read from the repo root if any exist; empty string otherwise. + * - `licensesMd` — the full item-9 Markdown body: every dependency by + * tier (BLOCK / WARN / OK) in a per-package section in + * `(ecosystem, name, version)` ASC order, followed by a `## Notices` + * section carrying the aggregated NOTICE content when present. This + * is the payload that `generatePack` writes to `licenses.md`. + * - `noticesMd` — the same concatenated `NOTICE` / `NOTICES` / + * `NOTICE.md` content in isolation (empty string when none exist), + * returned raw for consumers that want the NOTICE section alone. * * Determinism contract: * - Dependency rows are alpha-sorted by `(ecosystem, name, version, id)` @@ -68,8 +70,13 @@ const NOTICE_FILES = ["NOTICE", "NOTICE.md", "NOTICES"] as const; export async function buildLicenses(opts: LicensesOpts): Promise { const deps = await loadDependencyRefs(opts.store); const classification = classifyDependencies(deps); - const licensesMd = renderLicensesMd(deps, classification); const noticesMd = await readNotices(opts); + // The BOM's item-9 body is "LICENSES / NOTICES": the rendered markdown + // carries both the dependency-license tier and any aggregated NOTICE + // content so the on-disk `licenses.md` is the complete payload. Callers + // serialize `licensesMd` directly; `noticesMd` is also returned raw for + // consumers that want the NOTICE section in isolation. + const licensesMd = renderLicensesMd(deps, classification, noticesMd); return { licensesMd, noticesMd, classification }; } @@ -103,11 +110,14 @@ async function loadDependencyRefs(store: IGraphStore): Promise 0) { + lines.push("## Notices"); + lines.push(""); + // `noticesMd` is already LF-only with its own trailing newline; trim it + // here so the outer trimEnd + single trailing newline stays canonical. + lines.push(noticesMd.trimEnd()); + lines.push(""); + } + // LF-only join + trailing newline so the file ends in a newline (the // POSIX-text convention that keeps `git diff` clean). return `${lines.join("\n").trimEnd()}\n`; @@ -154,8 +173,8 @@ async function readNotices(opts: LicensesOpts): Promise { if (content === undefined || content.length === 0) continue; chunks.push(`# ${filename}`); chunks.push(""); - // CRLF→LF normalize for byte-identity. - chunks.push(content.replace(/\r\n/g, "\n").trimEnd()); + // Collapse CRLF and lone CR to LF for byte-identity across line-ending styles. + chunks.push(content.replace(/\r\n?/g, "\n").trimEnd()); chunks.push(""); } if (chunks.length === 0) return ""; diff --git a/packages/pack/src/readme.ts b/packages/pack/src/readme.ts index 621a0613..127db734 100644 --- a/packages/pack/src/readme.ts +++ b/packages/pack/src/readme.ts @@ -82,7 +82,7 @@ export function buildReadme(opts: ReadmeOpts): string { ); lines.push(""); lines.push( - "All file bytes use LF line endings; CRLF inputs are normalized before hashing so two repos differing only in line-ending style produce the same `pack_hash`.", + "All file bytes use LF line endings; CRLF and lone-CR inputs are normalized before hashing so two repos differing only in line-ending style produce the same `pack_hash`.", ); lines.push(""); diff --git a/packages/policy/src/evaluate.test.ts b/packages/policy/src/evaluate.test.ts index 98bace5a..971aa51c 100644 --- a/packages/policy/src/evaluate.test.ts +++ b/packages/policy/src/evaluate.test.ts @@ -236,6 +236,93 @@ test("ownership_required: matches single-segment wildcard with '*'", () => { assert.equal(decision.violations.length, 1); }); +test("ownership_required: '**/' anchors on a segment boundary and does not over-match a partial trailing segment", () => { + const policy: Policy = { + version: 1, + rules: [ + { + type: "ownership_required", + id: "storage-dir", + paths: ["**/storage"], + require_approval_from: ["@storage-team"], + }, + ], + }; + const decision = evaluatePolicy( + policy, + emptyCtx({ + // `mystorage` ends with the literal `storage` but is a different + // segment, so `**/storage` must NOT capture it. `packages/storage` + // is a real segment match and must be gated. + touchedPaths: ["packages/mystorage", "packages/storage"], + approvals: [], + }), + ); + assert.equal(decision.status, "block"); + assert.equal(decision.violations.length, 1); + assert.match(decision.violations[0]?.reason ?? "", /"packages\/storage"/); +}); + +test("ownership_required: '**/' before a file name does not over-match a partial file segment", () => { + const policy: Policy = { + version: 1, + rules: [ + { + type: "ownership_required", + id: "test-file", + paths: ["**/test.ts"], + require_approval_from: ["@qa"], + }, + ], + }; + const decision = evaluatePolicy( + policy, + emptyCtx({ + // `mytest.ts` shares the `test.ts` suffix but is one segment, so it + // must NOT match. `src/test.ts` and a root-level `test.ts` are real + // segment matches. + touchedPaths: ["src/mytest.ts", "src/test.ts", "test.ts"], + approvals: [], + }), + ); + assert.equal(decision.status, "block"); + assert.equal(decision.violations.length, 2); + const reasons = decision.violations.map((v) => v.reason).join("\n"); + assert.match(reasons, /"src\/test\.ts"/); + assert.match(reasons, /"test\.ts"/); + assert.doesNotMatch(reasons, /mytest/); +}); + +test("ownership_required: 'packages/**/file' still matches the zero-segment case", () => { + const policy: Policy = { + version: 1, + rules: [ + { + type: "ownership_required", + id: "nested-file", + paths: ["packages/**/build.ts"], + require_approval_from: ["@maintainers"], + }, + ], + }; + const decision = evaluatePolicy( + policy, + emptyCtx({ + // The `**/` group is optional, so both the direct child and a deeply + // nested path are gated. + touchedPaths: ["packages/build.ts", "packages/a/b/build.ts", "packages/abuild.ts"], + approvals: [], + }), + ); + assert.equal(decision.status, "block"); + assert.equal(decision.violations.length, 2); + const reasons = decision.violations.map((v) => v.reason).join("\n"); + assert.match(reasons, /"packages\/build\.ts"/); + assert.match(reasons, /"packages\/a\/b\/build\.ts"/); + // `packages/abuild.ts` ends with `build.ts` but is a partial segment. + assert.doesNotMatch(reasons, /abuild/); +}); + // ---- multi-rule determinism --------------------------------------------- test("evaluatePolicy: violations are sorted by ruleId across mixed rule types", () => { diff --git a/packages/policy/src/evaluate.ts b/packages/policy/src/evaluate.ts index dfd47237..e77c7b8d 100644 --- a/packages/policy/src/evaluate.ts +++ b/packages/policy/src/evaluate.ts @@ -182,12 +182,18 @@ function globToRegex(pattern: string): RegExp { const ch = pattern[i]; if (ch === "*") { if (pattern[i + 1] === "*") { - // `**` -> match any number of characters including /. - out += ".*"; i += 2; - // Skip a trailing `/` after `**` so `packages/**/file` matches - // `packages/file` too. - if (pattern[i] === "/") i += 1; + if (pattern[i] === "/") { + // `**/` -> match zero or more whole `/`-terminated segments. The + // optional group keeps `packages/**/file` matching `packages/file` + // too, while the trailing `/` anchors on a segment boundary so + // `**/storage` does not match `mystorage`. + out += "(?:.*/)?"; + i += 1; + } else { + // bare trailing `**` -> match the rest of the path, including `/`. + out += ".*"; + } continue; } // `*` -> one path segment (no `/`). diff --git a/packages/sarif/README.md b/packages/sarif/README.md index e015ebae..e78ba483 100644 --- a/packages/sarif/README.md +++ b/packages/sarif/README.md @@ -1,28 +1,80 @@ # @opencodehub/sarif -SARIF v2.1.0 helpers for OpenCodeHub. Merges, enriches, and validates -SARIF outputs from multiple scanners into a single canonical result set. +SARIF v2.1.0 helpers for OpenCodeHub. Merges, enriches, fingerprints, +baseline-diffs, and suppresses SARIF outputs from multiple scanners into a +single canonical result set. Validation is done with `zod` (the public +shapes are pinned to SARIF version `2.1.0`); there is no JSON-schema +validator and no `ajv` dependency. ## Surface ```ts -import { mergeSarif, enrichSarif, validateSarif } from "@opencodehub/sarif"; +import { + mergeSarif, + enrichWithProperties, + enrichWithFingerprints, + diffSarif, + applyBaselineState, + loadSuppressions, + applySuppressions, + isSuppressed, +} from "@opencodehub/sarif"; +// 1. Concatenate scanner outputs into one log. const merged = mergeSarif([semgrepSarif, betterleaksSarif, osvSarif]); -const enriched = enrichSarif(merged, { repoRoot: "/path/to/repo" }); -const valid = await validateSarif(enriched); // validates against OASIS schema + +// 2. Add `opencodehub/v1` + `primaryLocationLineHash` partial fingerprints. +const fingerprinted = enrichWithFingerprints(merged); + +// 3. Attach graph-derived signals under `properties.opencodehub.*`. +const enriched = enrichWithProperties(fingerprinted, { + byResultIndex: new Map([[0, { blastRadius: 12, centrality: 0.4 }]]), + run: { enrichedAt: new Date().toISOString(), enrichmentVersion: "1" }, +}); + +// 4. Diff against a baseline and tag `result.baselineState`. +const diff = diffSarif(baselineSarif, enriched); +const tagged = applyBaselineState(enriched, baselineSarif); + +// 5. Suppress via YAML rules + inline source markers. +const { rules, warnings } = loadSuppressions(".codehub/suppressions.yaml"); +const suppressed = applySuppressions(enriched, rules, readSource); ``` -- **`mergeSarif`** — deduplicates runs by tool name, merges result arrays, - preserves all `ruleId` / `level` / `location` metadata. -- **`enrichSarif`** — adds `relatedLocations`, `fingerprints`, and - `partialFingerprints` from the OpenCodeHub graph. -- **`validateSarif`** — runs the OASIS SARIF 2.1.0 JSON schema via `ajv` - (with draft-2019-09 support). +- **`mergeSarif(logs)`** — validates each input against `SarifLogSchema`, + deep-clones, and concatenates `runs` in argument order. It does **not** + collapse runs by tool name: SARIF consumers rely on per-tool identity for + provenance, so each run keeps its own `tool.driver.name`. +- **`enrichWithFingerprints(log)`** — computes the `opencodehub/v1` content + + context-window fingerprint and the GHAS `primaryLocationLineHash` partial + fingerprint. `computeContextHash`, `computeOpenCodeHubFingerprint`, and + `computePrimaryLocationLineHash` are exported for callers that need the raw + hashers. +- **`enrichWithProperties(log, enrichments)`** — deposits OpenCodeHub signals + (blast radius, centrality, cochange score, bus factor, etc.) under + `properties.opencodehub.*`. It never mutates `ruleId`, `fingerprints`, + `partialFingerprints`, or `artifactLocation.uri` (the GHAS dedup contract). +- **`diffSarif(baseline, current, options?)`** — buckets results into + `new` / `fixed` / `unchanged` / `updated` by the `opencodehub/v1` + fingerprint, falling back to a `(ruleId, uri, startLine, startColumn)` + tuple. An optional `renameChainFor` resolver follows `git mv` continuity. +- **`applyBaselineState(current, baseline, options?)`** — returns a clone of + `current` with each result tagged with a SARIF 2.1.0 `baselineState`. +- **`loadSuppressions(path, now?)`** — reads `.codehub/suppressions.yaml`, + drops expired rules, and returns surviving rules plus non-fatal warnings. +- **`applySuppressions(log, rules, readSource?)`** — appends standard SARIF + `suppressions[]` entries for matching YAML rules and for inline + `codehub-suppress: ` comments (`//`, `#`, `/* */`, `--`, `"].join("\n"); + const htmlApplied = applySuppressions(makeLog({ startLine: 2 }), [], () => htmlSource); + const htmlSupp = firstResult(htmlApplied)["suppressions"] as { + kind: string; + justification: string; + }[]; + assert.equal(htmlSupp[0]?.kind, "inSource"); + assert.equal(htmlSupp[0]?.justification, "html comment marker"); +}); + +test("applySuppressions: location-less result is suppressed by a catch-all `**` rule", () => { + // SARIF 2.1.0 allows a Result with a ruleId but no locations[] (file-less + // secret/dependency findings). A catch-all rule must still be able to + // suppress it by ruleId; a path-scoped rule must not. + const log: SarifLog = { + version: "2.1.0", + runs: [ + { + tool: { driver: { name: "betterleaks", version: "1.0.0" } }, + results: [{ ruleId: "B101", level: "error", message: { text: "file-less finding" } }], + }, + ], + }; + + const catchAll: SuppressionRule[] = [ + { ruleId: "B101", filePathPattern: "**", reason: "accepted risk" }, + ]; + const applied = applySuppressions(log, catchAll); + const supp = firstResult(applied)["suppressions"] as { kind: string; justification: string }[]; + assert.ok(Array.isArray(supp)); + assert.equal(supp.length, 1); + assert.equal(supp[0]?.kind, "external"); + assert.equal(supp[0]?.justification, "accepted risk"); + + // A path-scoped rule must NOT leak onto the location-less finding. + const scoped: SuppressionRule[] = [ + { ruleId: "B101", filePathPattern: "tests/**", reason: "scoped" }, + ]; + const scopedApplied = applySuppressions(log, scoped); + assert.equal(firstResult(scopedApplied)["suppressions"], undefined); +}); + test("applySuppressions: preserves pre-existing suppressions[]", () => { const preExisting = [{ kind: "external" as const, justification: "vendor rule" }]; const log = makeLog({ suppressions: preExisting }); diff --git a/packages/sarif/src/suppressions.ts b/packages/sarif/src/suppressions.ts index 397ae177..4a289673 100644 --- a/packages/sarif/src/suppressions.ts +++ b/packages/sarif/src/suppressions.ts @@ -11,8 +11,14 @@ * // codehub-suppress: * # codehub-suppress: * /* codehub-suppress: *\/ - * The ruleId must match the Result.ruleId exactly; everything after - * it is the justification. + * -- codehub-suppress: + * + * The marker is honored only when it sits inside a comment — it must + * be preceded by a comment opener (`//`, `#`, `/*`, `--`, or `