diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000..a2d91f924 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,75 @@ +{ + "permissions": { + "allow": [ + "WebSearch", + "WebFetch(domain:cursor.com)", + "WebFetch(domain:composio.dev)", + "Bash(npx tsc:*)", + "Bash(claude rename:*)", + "Bash(npm run build:*)", + "Bash(npm link:*)", + "Bash(gitnexus --version:*)", + "Bash(gitnexus --help:*)", + "Bash(npm ls:*)", + "Bash(gitnexus augment:*)", + "Bash(node -e \"\nconst { augment } = await import\\(''./gitnexus/dist/core/augmentation/engine.js''\\);\ntry {\n const r = await augment\\(''setup'', process.cwd\\(\\)\\);\n console.log\\(''Result:'', r ? r.substring\\(0, 200\\) : ''null''\\);\n} catch\\(e\\) { console.error\\(''Error:'', e.message\\); }\nprocess.exit\\(0\\);\n\")", + "Bash(cmd.exe /c \"cd /d D:\\\\Projects\\\\GitnexusV2 && gitnexus augment setup\")", + "Bash(cmd.exe /c \"cd /d D:\\\\Projects\\\\GitnexusV2 && gitnexus status\")", + "Bash(gh repo clone:*)", + "Bash(claude mcp:*)", + "Bash(gh issue view:*)", + "Bash(echo:*)", + "Bash(node:*)", + "Bash(npm view:*)", + "Bash(npm version:*)", + "Bash(npm pack:*)", + "Bash(npm publish:*)", + "Bash(npx gitnexus:*)", + "mcp__gitnexus__list_repos", + "mcp__gitnexus__query", + "mcp__gitnexus__context", + "mcp__gitnexus__impact", + "Bash(git add:*)", + "Bash(Glob)", + "Bash(Bash\"\\) per new Claude Code schema\n- Rename gitnexus-hook.js → gitnexus-hook.cjs for CommonJS compatibility\n- Fix setup.ts: correct hook filename and timeout \\(8000ms instead of 10ms\\)\n- Bump to v1.1.9 and publish to npm\n\nCo-Authored-By: Claude Opus 4.6 \nEOF\n\\)\")", + "Bash(git push:*)", + "WebFetch(domain:docs.kuzudb.com)", + "WebFetch(domain:github.com)", + "WebFetch(domain:raw.githubusercontent.com)", + "WebFetch(domain:read.engineerscodex.com)", + "WebFetch(domain:towardsdatascience.com)", + "WebFetch(domain:kilo.ai)", + "WebFetch(domain:deepwiki.com)", + "WebFetch(domain:turbopuffer.com)", + "WebFetch(domain:windsurf.com)", + "WebFetch(domain:modal.com)", + "WebFetch(domain:www.augmentcode.com)", + "WebFetch(domain:www.qodo.ai)", + "WebFetch(domain:arxiv.org)", + "WebFetch(domain:cognition.ai)", + "WebFetch(domain:microsoft.github.io)", + "WebFetch(domain:github.github.com)", + "WebFetch(domain:gist.github.com)", + "WebFetch(domain:fsoft-ai4code.github.io)", + "mcp__gitnexus__cypher", + "WebFetch(domain:repomix.com)", + "WebFetch(domain:www.humanlayer.dev)", + "WebFetch(domain:agents.md)", + "WebFetch(domain:eclipsesource.com)", + "WebFetch(domain:www.usefulfunctions.co.uk)", + "WebFetch(domain:developers.googleblog.com)", + "WebFetch(domain:www.anthropic.com)", + "WebFetch(domain:www.driver.ai)", + "WebFetch(domain:blog.sshh.io)", + "WebFetch(domain:docs.qodo.ai)", + "WebFetch(domain:smartlogic.io)", + "Bash(ls:*)", + "Bash(wc:*)", + "Bash(grep:*)" + ] + }, + "enableAllProjectMcpServers": true, + "enabledMcpjsonServers": [ + "gitnexus" + ] +} diff --git a/.claude/skills/gitnexus/debugging/SKILL.md b/.claude/skills/gitnexus/debugging/SKILL.md new file mode 100644 index 000000000..3b945835b --- /dev/null +++ b/.claude/skills/gitnexus/debugging/SKILL.md @@ -0,0 +1,85 @@ +--- +name: gitnexus-debugging +description: Trace bugs through call chains using knowledge graph +--- + +# Debugging with GitNexus + +## When to Use +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. gitnexus_query({query: ""}) → Find related execution flows +2. gitnexus_context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] gitnexus_query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] gitnexus_context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] gitnexus_cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +|---------|-------------------| +| Error message | `gitnexus_query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**gitnexus_query** — find code related to error: +``` +gitnexus_query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**gitnexus_context** — full context for a suspect: +``` +gitnexus_context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**gitnexus_cypher** — custom call chain traces: +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. gitnexus_query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. gitnexus_context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/.claude/skills/gitnexus/exploring/SKILL.md b/.claude/skills/gitnexus/exploring/SKILL.md new file mode 100644 index 000000000..2214c289c --- /dev/null +++ b/.claude/skills/gitnexus/exploring/SKILL.md @@ -0,0 +1,75 @@ +--- +name: gitnexus-exploring +description: Navigate unfamiliar code using GitNexus knowledge graph +--- + +# Exploring Codebases with GitNexus + +## When to Use +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. gitnexus_query({query: ""}) → Find related execution flows +4. gitnexus_context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] gitnexus_query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] gitnexus_context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +|----------|-------------| +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**gitnexus_query** — find execution flows related to a concept: +``` +gitnexus_query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**gitnexus_context** — 360-degree view of a symbol: +``` +gitnexus_context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. gitnexus_query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. gitnexus_context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/.claude/skills/gitnexus/impact-analysis/SKILL.md b/.claude/skills/gitnexus/impact-analysis/SKILL.md new file mode 100644 index 000000000..bb5f51fcc --- /dev/null +++ b/.claude/skills/gitnexus/impact-analysis/SKILL.md @@ -0,0 +1,94 @@ +--- +name: gitnexus-impact-analysis +description: Analyze blast radius before making code changes +--- + +# Impact Analysis with GitNexus + +## When to Use +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. gitnexus_detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] gitnexus_detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +|-------|-----------|---------| +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +|----------|------| +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**gitnexus_impact** — the primary tool for symbol blast radius: +``` +gitnexus_impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**gitnexus_detect_changes** — git-diff based impact analysis: +``` +gitnexus_detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. gitnexus_impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/.claude/skills/gitnexus/refactoring/SKILL.md b/.claude/skills/gitnexus/refactoring/SKILL.md new file mode 100644 index 000000000..23f4d1130 --- /dev/null +++ b/.claude/skills/gitnexus/refactoring/SKILL.md @@ -0,0 +1,113 @@ +--- +name: gitnexus-refactoring +description: Plan safe refactors using blast radius and dependency mapping +--- + +# Refactoring with GitNexus + +## 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 + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents +2. gitnexus_query({query: "X"}) → Find execution flows involving X +3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklists + +### Rename Symbol +``` +- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits +- [ ] gitnexus_detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module +``` +- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs +- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service +``` +- [ ] gitnexus_context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**gitnexus_rename** — automated multi-file rename: +``` +gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**gitnexus_impact** — map all dependents first: +``` +gitnexus_impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**gitnexus_detect_changes** — verify your changes after refactoring: +``` +gitnexus_detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**gitnexus_cypher** — custom reference queries: +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +|-------------|------------| +| Many callers (>5) | Use gitnexus_rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | gitnexus_query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. gitnexus_detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/.claude/worktrees/determined-hofstadter b/.claude/worktrees/determined-hofstadter new file mode 160000 index 000000000..e90622aa2 --- /dev/null +++ b/.claude/worktrees/determined-hofstadter @@ -0,0 +1 @@ +Subproject commit e90622aa24c92d7a08712085d2355b9fe36cf3c3 diff --git a/.claude/worktrees/quirky-stonebraker b/.claude/worktrees/quirky-stonebraker new file mode 160000 index 000000000..e90622aa2 --- /dev/null +++ b/.claude/worktrees/quirky-stonebraker @@ -0,0 +1 @@ +Subproject commit e90622aa24c92d7a08712085d2355b9fe36cf3c3 diff --git a/.claude/worktrees/sweet-faraday b/.claude/worktrees/sweet-faraday new file mode 160000 index 000000000..44572ad0b --- /dev/null +++ b/.claude/worktrees/sweet-faraday @@ -0,0 +1 @@ +Subproject commit 44572ad0bddea03d548b5f25ace720ba4f4c1548 diff --git a/.cursor/plans/enhance_523ca41c.plan.md b/.cursor/plans/enhance_523ca41c.plan.md deleted file mode 100644 index 471e13f67..000000000 --- a/.cursor/plans/enhance_523ca41c.plan.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -name: Enhance -overview: Restructure GitNexus LLM tools to leverage clusters and processes for better code understanding. Remove unused highlight tool, add new tools (explore, overview), enhance existing tools with cluster/process context, and improve impact analysis reliability. -todos: [] ---- - -# Enhanced LLM Tools with Cluster and Process Integration - -## Summary - -Consolidate GitNexus from 6 tools to **7 focused tools** that leverage the pre-computed clusters (Communities) and processes for richer context. Remove the highlight tool, add `explore` and `overview` tools, and enhance `search` and `blastRadius` with cluster/process awareness. - -## Final Tool Set - -| Tool | Status | Purpose ||------|--------|---------|| `search` | Enhance | Hybrid search + group results by process/cluster || `grep` | Keep | Regex pattern search || `read` | Keep | Read file content || `explore` | **New** | Deep dive on one symbol, cluster, or process || `overview` | **New** | Codebase map (all clusters + all processes) || `impact` | Enhance | Rename from blastRadius, add process/cluster context, increase limits || `cypher` | Keep | Raw graph queries || `highlight` | **Remove** | No longer needed | - -## Architecture - -```mermaid -flowchart TD - subgraph tools [LLM Tools Layer] - search[search] - grep[grep] - read[read] - explore[explore] - overview[overview] - impact[impact] - cypher[cypher] - end - - subgraph graph [Knowledge Graph] - nodes[Nodes: File, Function, Class...] - communities[Community Nodes] - processes[Process Nodes] - edges[CodeRelation Edges] - memberOf[MEMBER_OF Edges] - stepIn[STEP_IN_PROCESS Edges] - end - - search --> edges - search --> communities - search --> processes - explore --> communities - explore --> processes - explore --> memberOf - explore --> stepIn - overview --> communities - overview --> processes - impact --> edges - impact --> communities - impact --> processes - cypher --> graph -``` - - - -## File Changes - -### 1. Remove Highlight Tool - -**File:** [gitnexus/src/core/llm/tools.ts](gitnexus/src/core/llm/tools.ts) - -- Delete the `highlightTool` definition (lines ~395-414) -- Remove `highlightTool` from the returned array (line ~862) -- Remove highlight marker logic from `blastRadius` output (line ~814-816) - -**File:** [gitnexus/src/core/llm/agent.ts](gitnexus/src/core/llm/agent.ts) - -- Remove highlight references from system prompt (lines 70, 77) -- Update tool list in prompt to reflect new tools - -**File:** [gitnexus/src/core/llm/types.ts](gitnexus/src/core/llm/types.ts) - -- Remove `'highlight'` from `AgentStreamChunk.type` union (line 180) -- Remove `highlightNodeIds` property (line 187-188) - -### 2. Add `explore` Tool - -**File:** [gitnexus/src/core/llm/tools.ts](gitnexus/src/core/llm/tools.ts)New tool that auto-detects target type and returns comprehensive context: - -```typescript -explore({ - target: string, // Name of symbol, cluster, or process - type?: 'symbol' | 'cluster' | 'process' // Optional, auto-detected -}) -``` - -**Functionality:** - -- For symbols: Query node, get MEMBER_OF cluster, get STEP_IN_PROCESS processes, get 1-hop connections -- For clusters: Query Community node, get members via MEMBER_OF, get processes that touch this cluster -- For processes: Query Process node, get steps via STEP_IN_PROCESS with step order, get clusters touched - -**Cypher queries needed:** - -```cypher --- Symbol cluster membership -MATCH (s {name: $name})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) -RETURN c.label, c.description - --- Symbol process participation -MATCH (s {name: $name})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) -RETURN p.label, r.step, p.stepCount - --- Process steps in order -MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process {id: $processId}) -RETURN s.name, s.filePath, r.step -ORDER BY r.step -``` - - - -### 3. Add `overview` Tool - -**File:** [gitnexus/src/core/llm/tools.ts](gitnexus/src/core/llm/tools.ts)New tool that returns codebase structure: - -```typescript -overview() // No parameters -``` - -**Functionality:** - -- Query all Community nodes with member counts -- Query all Process nodes with step counts and types -- Calculate cluster dependencies (cross-cluster CALLS) -- Identify critical paths (most connected processes) - -**Output format:** - -```javascript -CLUSTERS (N total): -| Cluster | Symbols | Cohesion | Description | -... - -PROCESSES (N total): -| Process | Steps | Type | Clusters | -... - -CRITICAL PATHS: -- LoginFlow (45 edges) -... -``` - - - -### 4. Enhance `search` Tool - -**File:** [gitnexus/src/core/llm/tools.ts](gitnexus/src/core/llm/tools.ts)Modify existing search to group results by process:**Current:** Returns flat list with 1-hop connections**Enhanced:** Groups results by process, adds cluster context**Changes:** - -- After hybrid search, query STEP_IN_PROCESS for each result -- Group results by process ID -- Sort processes by number of matching results (relevance) -- Add cluster label for each result via MEMBER_OF query -- Keep 1-hop connections as optional detail - -**New parameter:** - -```typescript -search({ - query: string, - groupByProcess?: boolean, // Default: true - limit?: number -}) -``` - - - -### 5. Enhance `impact` Tool (rename from blastRadius) - -**File:** [gitnexus/src/core/llm/tools.ts](gitnexus/src/core/llm/tools.ts)**Rename:** `blastRadiusTool` to `impactTool`**Enhancements:** - -1. Increase LIMIT clauses: 100 to 300 (depth 1), 100 to 200 (depth 2), 50 to 100 (depth 3) -2. Add affected processes section (query STEP_IN_PROCESS for all affected symbols) -3. Add affected clusters section (query MEMBER_OF for all affected symbols) -4. Add risk assessment summary -5. Surface confidence scores more prominently (group by confidence level) - -**New output sections:** - -```javascript -AFFECTED PROCESSES: -- LoginFlow - BROKEN at step 2 -- SignupFlow - BROKEN at step 1 - -AFFECTED CLUSTERS: -- Authentication (direct) -- API Routes (indirect) - -RISK: CRITICAL -- N direct callers -- N processes affected -- N clusters affected -``` - - - -### 6. Increase Process Detection Limits - -**File:** [gitnexus/src/core/ingestion/process-processor.ts](gitnexus/src/core/ingestion/process-processor.ts)Change default config (lines 27-32): - -```typescript -const DEFAULT_CONFIG: ProcessDetectionConfig = { - maxTraceDepth: 10, // Keep - maxBranching: 4, // Was 3 - maxProcesses: 75, // Was 50 - minSteps: 2, // Keep -}; -``` - - - -### 7. Update System Prompt - -**File:** [gitnexus/src/core/llm/agent.ts](gitnexus/src/core/llm/agent.ts)Update BASE_SYSTEM_PROMPT to reflect new tools: - -```javascript -## TOOLS -- **search** - Hybrid search. Results grouped by process with cluster context. -- **grep** - Regex pattern search for exact strings. -- **read** - Read file content. -- **explore** - Deep dive on a symbol, cluster, or process. Shows membership, participation, connections. -- **overview** - Codebase map showing all clusters and processes. -- **impact** - Impact analysis. Shows affected processes, clusters, and risk level. -- **cypher** - Raw Cypher queries against the graph. - -## GRAPH SCHEMA -Nodes: File, Folder, Function, Class, Interface, Method, Community, Process -Relations: CodeRelation with type: CONTAINS, DEFINES, IMPORTS, CALLS, EXTENDS, IMPLEMENTS, MEMBER_OF, STEP_IN_PROCESS -``` - - - -## Implementation Order - -1. Remove highlight tool (cleanup) -2. Increase process detection limits -3. Add overview tool (simplest new tool) -4. Add explore tool -5. Enhance impact tool \ No newline at end of file diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 000000000..397f42422 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,5 @@ +# AI Agent Rules + +Follow .gitnexus/RULES.md for all project context and coding guidelines. + +This project uses GitNexus MCP for code intelligence. See .gitnexus/RULES.md for available tools and best practices. diff --git a/.gitignore b/.gitignore index 2f3659245..2f97f68d5 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,10 @@ coverage/ .env*.local +.gitnexus + +# Assets (screenshots, images) +assets/ + +# Generated files (should not be indexed) +repomix-output* diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 000000000..83370ebfb --- /dev/null +++ b/.mcp.json @@ -0,0 +1,9 @@ +{ + "mcpServers": { + "gitnexus": { + "type": "stdio", + "command": "cmd", + "args": ["/c", "npx", "-y", "gitnexus@latest", "mcp"] + } + } +} diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 000000000..397f42422 --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,5 @@ +# AI Agent Rules + +Follow .gitnexus/RULES.md for all project context and coding guidelines. + +This project uses GitNexus MCP for code intelligence. See .gitnexus/RULES.md for available tools and best practices. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..290e07e9b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,64 @@ +# AI Agent Rules + + +# GitNexus MCP + +This project is indexed by GitNexus as **GitnexusV2** (1295 symbols, 3262 relationships, 99 execution flows). + +GitNexus provides a knowledge graph over this codebase — call chains, blast radius, execution flows, and semantic search. + +## Always Start Here + +For any task involving code understanding, debugging, impact analysis, or refactoring, you must: + +1. **Read `gitnexus://repo/{name}/context`** — codebase overview + check index freshness +2. **Match your task to a skill below** and **read that skill file** +3. **Follow the skill's workflow and checklist** + +> If step 1 warns the index is stale, run `npx gitnexus analyze` in the terminal first. + +## Skills + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/refactoring/SKILL.md` | + +## Tools Reference + +| Tool | What it gives you | +|------|-------------------| +| `query` | Process-grouped code intelligence — execution flows related to a concept | +| `context` | 360-degree symbol view — categorized refs, processes it participates in | +| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence | +| `detect_changes` | Git-diff impact — what do your current changes affect | +| `rename` | Multi-file coordinated rename with confidence-tagged edits | +| `cypher` | Raw graph queries (read `gitnexus://repo/{name}/schema` first) | +| `list_repos` | Discover indexed repos | + +## Resources Reference + +Lightweight reads (~100-500 tokens) for navigation: + +| Resource | Content | +|----------|---------| +| `gitnexus://repo/{name}/context` | Stats, staleness check | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores | +| `gitnexus://repo/{name}/cluster/{clusterName}` | Area members | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{processName}` | Step-by-step trace | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher | + +## Graph Schema + +**Nodes:** File, Function, Class, Interface, Method, Community, Process +**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"}) +RETURN caller.name, caller.filePath +``` + + diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 429c0598e..6ad457e44 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,585 +1,247 @@ -# PyBaMM Architecture - End-to-End Analysis +# GitNexus Architecture -## Executive Summary +> Auto-generated from the GitNexus knowledge graph (1021 symbols, 2552 edges, 79 execution flows). -**PyBaMM** (Python Battery Mathematical Modelling) is a comprehensive, open-source framework for modeling and simulating battery behavior. The project contains **973 files**, **4,342 functions**, and **735 classes** organized in a layered architecture optimized for modularity, extensibility, and scientific computation. +## Overview ---- +GitNexus is a graph-powered code intelligence platform that indexes codebases into a knowledge graph and exposes them via MCP (Model Context Protocol) tools for AI agents. It consists of three packages: -## 🏗️ High-Level Architecture Layers +- **`gitnexus/`** — Core CLI + MCP server (published to npm). Indexes repositories, stores graphs in KuzuDB, and serves queries. +- **`gitnexus-web/`** — Browser-based frontend with WebAssembly tree-sitter and in-browser KuzuDB. +- **`gitnexus-claude-plugin/`** / **`gitnexus-cursor-integration/`** — IDE integrations that augment AI agent tool calls with graph context. + +## Functional Areas + +The codebase is organized into 14 functional clusters detected by community analysis: + +| Module | Symbols | Cohesion | Responsibility | +|--------|---------|----------|----------------| +| **Ingestion** | 108 | 24% | Multi-phase pipeline: file walking, tree-sitter parsing, import resolution, call tracing, heritage extraction, community detection, process tracing | +| **Kuzu** | 50 | 23% | KuzuDB graph storage adapter, CSV generation, schema management, query execution | +| **Embeddings** | 46 | 35% | Embedding pipeline: text generation from symbols, ONNX model inference, vector storage | +| **Components** | 41 | 35% | Web UI React components (graph visualization, search, navigation) | +| **Local** | 38 | 15% | MCP backend: tool implementations (query, context, impact, rename), resource handlers, search (BM25 + semantic) | +| **Workers** | 38 | 27% | Web Workers for browser-side ingestion and tree-sitter parsing; Node.js worker threads for parallel parsing | +| **LLM** | 28 | 37% | LLM-based cluster enrichment, prompt building, provider abstraction | +| **CLI** | 24 | 35% | Command handlers (analyze, setup, serve, mcp), AI context file generation, IDE hook/skill installation | +| **Storage** | 22 | 32% | Repository registry, `.gitnexus/` directory management, staleness detection | +| **Services** | 14 | 31% | Shared services (config, ignore patterns, language support) | +| **Hooks** | 12 | 56% | Claude Code / Cursor hook scripts for augmenting search tools with graph context | +| **Search** | 11 | 36% | Hybrid search: BM25 keyword index + semantic vector search with reciprocal rank fusion | + +## Key Execution Flows + +### 1. CLI Analyze Pipeline + +The primary ingestion path when a user runs `npx gitnexus analyze`: ``` -┌─────────────────────────────────────────────────────────────┐ -│ USER INTERFACE & EXAMPLES │ -│ (Jupyter Notebooks, Scripts, Experiments) │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ SIMULATION & EXPERIMENT ORCHESTRATION LAYER │ -│ • Simulation - High-level simulation runner │ -│ • Experiment - Define charging/discharging cycles │ -│ • BatchStudy - Multi-parameter studies │ -│ • Callbacks - Monitor simulation progress │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ MODEL LAYER (Hierarchical) │ -├─ BaseBatteryModel (Physical domain constraints) │ -├─ Full Models: │ -│ ├─ Lithium-Ion (DFN, SPM, SPMe, MPM, MSMR, etc.) │ -│ ├─ Lead-Acid (Full, LOQS models) │ -│ ├─ Sodium-Ion (emerging battery chemistry) │ -│ └─ Equivalent Circuit Models (ECM) │ -├─ Submodels (Pluggable domain-specific components): │ -│ ├─ Particle Diffusion (kinetics in electrodes) │ -│ ├─ Electrode Kinetics (Butler-Volmer, Marcus, etc.) │ -│ ├─ Interface Chemistry (SEI growth, Li-plating, OCP) │ -│ ├─ Thermal Management (lumped, distributed 1D-3D) │ -│ ├─ Current Collector Physics │ -│ ├─ Electrolyte Transport (conductivity, diffusion) │ -│ ├─ Convection (internal circulation) │ -│ ├─ Porosity & Tortuosity (pore network) │ -│ └─ Active Material Loss (cycling degradation) │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ EXPRESSION TREE (Symbolic Computation Layer) │ -│ Directed Acyclic Graph (DAG) of mathematical expressions │ -├─ Symbol - Base class for all nodes │ -│ ├─ Variable - State vector entries │ -│ ├─ Parameter - Model parameters │ -│ ├─ Scalar/Array - Constants │ -│ ├─ StateVector - Discretized spatial domain │ -│ └─ InputParameter - Time-varying inputs │ -├─ Operators │ -│ ├─ BinaryOperators - +, -, *, /, power, etc. │ -│ ├─ UnaryOperators - exp, log, sin, cos, etc. │ -│ ├─ Concatenations - Stack vectors │ -│ └─ Broadcasts - Repeat/tile operations │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ DISCRETISATION LAYER (PDE → ODE/DAE Conversion) │ -│ Transforms continuous PDEs into discrete systems │ -├─ Discretisation - Master converter class │ -├─ Spatial Methods: │ -│ ├─ FiniteVolume - 1D/2D finite volume schemes │ -│ ├─ SpectralVolume - Spectral approach │ -│ ├─ ScikitFiniteElement - 1D unstructured meshes │ -│ ├─ ScikitFiniteElement3D- 3D tetrahedral meshes │ -│ └─ ZeroDimensionalMethod- Lumped (0D) approximations │ -├─ Meshes: │ -│ ├─ 1D Submeshes - Line domains │ -│ ├─ 2D Submeshes - Sheet domains │ -│ ├─ 3D Submeshes - Volume domains (via scikit-fem)│ -│ └─ Composite Meshes - Combined domains │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ SOLVER LAYER (DAE System Integration) │ -│ Converts discrete system → numerical solution │ -├─ Solver Interfaces: │ -│ ├─ BaseSolver - Abstract interface │ -│ ├─ ODE Solvers: │ -│ │ ├─ ScipySolver - scipy.integrate.ode │ -│ │ ├─ JAXSolver - JAX backend (jit-compiled) │ -│ │ ├─ JAXBDFSolver - JAX BDF method │ -│ │ └─ IDAKLUSolver - SUNDIALS IDA (C++ wrapper) │ -│ ├─ DAE Solvers: │ -│ │ ├─ CasadiSolver - CasADi symbolic optimization │ -│ │ ├─ IDakluJax - IDA + JAX hybrid │ -│ │ └─ AlgebraicSolver - Solve algebraic eqns only │ -│ └─ Special: │ -│ ├─ DummySolver - Testing/debugging │ -│ └─ Solution - Stores results + post-process │ -├─ Features: │ -│ ├─ Jacobian Computation - Auto diff or symbolic │ -│ ├─ Event Detection - Trigger on state changes │ -│ ├─ Callbacks - Hooks during integration │ -│ └─ Processed Variables - Post-compute derived quantities│ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ PARAMETER & DATA LAYER │ -│ Manages model coefficients and experimental data │ -├─ ParameterValues - Substitutes symbols → numbers │ -├─ Parameter Sets: │ -│ ├─ Lithium-Ion Parameter Sets (Chen2020, OKane2022, etc)│ -│ ├─ Lead-Acid Parameter Sets (Sulzer2019) │ -│ ├─ Sodium-Ion Parameter Sets (Chayambuka2022) │ -│ └─ ECM Parameter Sets (voltage model coefficients) │ -├─ Special Parameters: │ -│ ├─ ElectricalParameters - Conductivity, diffusivity │ -│ ├─ ThermalParameters - Heat capacity, conductivity │ -│ ├─ GeometricParameters - Dimensions, areas, volumes │ -│ └─ ProcessParameterData - Fit to experimental results │ -└──────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────▼──────────────────────────────────────┐ -│ VISUALIZATION & POST-PROCESSING │ -│ Analysis and interpretation of results │ -├─ Plotting Modules: │ -│ ├─ quick_plot() - 1-line quick visualization │ -│ ├─ plot() - Customizable plotting │ -│ ├─ plot_voltage_components() - Decompose voltage │ -│ ├─ plot_summary_variables() - Key metrics │ -│ ├─ plot_3d_heatmap() - 3D temperature fields │ -│ └─ plot_3d_cross_section() - 2D slices of 3D │ -├─ Dynamic Plotting: │ -│ └─ DynamicPlot - Live update during solving │ -└──────────────────────────────────────────────────────────────┘ +analyzeCommand (cli/analyze.ts) + → runPipelineFromRepo (ingestion/pipeline.ts) + → walkRepository — concurrent file I/O (32 parallel reads) + → processStructure — folder/file graph nodes + → processParsing — tree-sitter AST parsing (worker threads) + → processImports — import/require/use resolution + → processCalls — function call tracing with confidence + → processHeritage — extends/implements relationships + → processCommunities — Leiden community detection + → processProcesses — execution flow tracing + → loadGraphToKuzu — CSV export → KuzuDB bulk import + → runEmbeddingPipeline — generate + store symbol embeddings + → generateAIContextFiles — write CLAUDE.md, AGENTS.md, skills ``` ---- +### 2. MCP Server Request Flow -## 📊 Core Components Deep Dive - -### 1. **Expression Tree (Symbolic Computation)** - -**Purpose:** Represents mathematical expressions as a directed acyclic graph (DAG). - -**Key Classes:** +When an AI agent calls a GitNexus MCP tool: ``` -Symbol (Base Class) -├── Variable - Represents y(t), y_dot(t) -├── Parameter - Fixed model coefficients -├── Scalar/Array - Numerical constants -├── StateVector - Discretized spatial variables -├── InputParameter - Time-varying inputs (current, temperature) -│ -BinaryOperator -├── Addition/Subtraction -├── Multiplication/Division -├── Power -├── MatrixMultiplication -└── Equality (for algebraic equations) - -UnaryOperator -├── Exponential, Logarithm -├── Trigonometric (sin, cos, tan) -├── Sign, Absolute Value -└── Specialized (exp, log, cosh, etc.) +mcpCommand (cli/mcp.ts) + → startMCPServer (mcp/server.ts) + → callTool (mcp/local/local-backend.ts) + → ensureInitialized — lazy KuzuDB connection + embedder init + → query/context/impact — graph traversal via KuzuDB Cypher + → semanticSearch — ONNX embedding + vector similarity + → readResource (mcp/resources.ts) + → queryClusters/queryProcesses — direct graph queries ``` -**Why This Matters:** -- Enables **symbolic differentiation** (Jacobian computation) -- **Backend-agnostic**: Same expression can be evaluated as Python, CasADi, or JAX code -- Supports **automatic code generation** for performance - ---- - -### 2. **Model Hierarchy** - -**Top Level: `BaseModel`** -- Holds empty RHS and algebraic equation dictionaries -- Manages variables, parameters, boundary conditions -- Coordinates discretisation and conversion - -**Next Level: `BaseBatteryModel`** -- Enforces battery-specific physics constraints -- Implements standard lifecycle: `build_model()` → `discretise()` → `solve()` - -**Bottom Level: Concrete Models (Plug-and-Play Architecture)** - -| Model | Type | Complexity | Use Case | -|-------|------|-----------|----------| -| **SPM** | Lithium-Ion | Simplest | Quick simulations, education | -| **SPMe** | Lithium-Ion | Medium | Semi-empirical electrolyte | -| **DFN** | Lithium-Ion | Complex | High accuracy, research | -| **MSMR** | Lithium-Ion | Very Complex | Multi-scale particle size dist. | -| **MPM** | Lithium-Ion | Complex | Mesoscale particle modeling | -| **Half-Cell** | Lithium-Ion | Custom | Single electrode testing | -| **Thermal Models** | Any | Adds complexity | Temperature effects | -| **ECM (Thevenin)** | Equivalent Circuit | Simple | Real-time estimation | - -**Submodel Pattern:** -``` -Full Models = Combination of pluggable submodels - -Example: DFN Model -├── Active Material (constant or loss) -├── Particle Diffusion (negative & positive electrodes) -├── Electrode Kinetics (interface reactions) -├── Open Circuit Potential (voltage lookup) -├── SEI Growth (lithium loss) -├── Current Collector (ohmic drop) -├── Convection (internal flow) -├── Thermal (heat generation & transfer) -└── External Circuit (boundary conditions) -``` - ---- - -### 3. **Discretisation Pipeline** - -**Convert PDEs → Finite-Dimensional ODEs/DAEs** +### 3. Graph Storage Pipeline ``` -Physics-Based PDE - ↓ -[Spatial Method Selected: Finite Volume / Spectral / FEM] - ↓ -Mesh Generation (1D/2D/3D depending on model) - ↓ -Gradient/Divergence Operators Discretized - ↓ -Boundary Conditions Applied - ↓ -Expression Tree Converted (y → discretized vector) - ↓ -Final System: M*dy/dt = f(t,y) + g(t,y) = 0 [DAE form] +loadGraphToKuzu (kuzu/kuzu-adapter.ts) + → generateAllCSVs (kuzu/csv-generator.ts) + → generateFileCSV — node CSVs per label type + → escapeCSVField — UTF-8 sanitization + → KuzuDB COPY FROM — bulk CSV import + → createIndexes — property indexes for query performance ``` -**Mesh Strategy:** -- **1D**: Uniform or non-uniform grids (electrodes, separator) -- **2D**: Cartesian or polar (pouch cell cross-sections) -- **3D**: Tetrahedral (scikit-fem), complex geometries - ---- - -### 4. **Solver Pipeline** - -**Goal:** Integrate DAE system over time - -**Solver Family:** -- **ScipySolver**: Reliable, well-tested, pure Python -- **CasadiSolver**: Symbolic optimization, slow but accurate -- **IDAKLUSolver**: C++ SUNDIALS, fastest -- **JAXSolver**: JIT-compiled, GPU-capable -- **IDakluJax**: Hybrid IDA + JAX - -**Key Features:** -- **Event Detection**: Stop when voltage hits limit -- **Jacobian**: Computed symbolically or via auto-diff -- **Callbacks**: Monitor state during integration -- **Mass Matrix**: Handle DAE systems with singular mass matrices - ---- - -### 5. **Parameter System** - -**Strategy:** Keep symbolic model separate from numerical values +### 4. Embedding Pipeline ``` -Model Construction: - pybamm.Parameter("Conductivity") → generic symbol - ↓ - [Stored in expression tree] - ↓ -Before Solving: - parameter_values = pybamm.ParameterValues({ - "Conductivity": 1.23 # Numerical value - }) - parameter_values.process_model(model) - ↓ - All symbols substituted with values - ↓ - Ready to solve! +runEmbeddingPipeline (embeddings/embedding-pipeline.ts) + → generateBatchEmbeddingTexts (embeddings/text-generator.ts) + → generateEmbeddingText — symbol → natural language description + → generateFunctionText — include signature, calls, file context + → cleanContent — strip noise, truncate + → embedBatch — ONNX Runtime inference (all-MiniLM-L6-v2) + → storeEmbeddings — KuzuDB vector storage ``` -**Pre-built Parameter Sets:** -- **Lithium-Ion**: Chen2020, OKane2022, Ai2020, Ecker2015, ORegan2022 -- **Lead-Acid**: Sulzer2019 -- **Sodium-Ion**: Chayambuka2022 -- **ECM**: Thevenin model coefficients - ---- - -## 🔄 Execution Flow: From Model to Solution - -### Example: Simple SPM Simulation - -```python -import pybamm - -# Step 1: Create model -model = pybamm.lithium_ion.SPM() - -# Step 2: Define simulation -sim = pybamm.Simulation( - model, - parameter_values=pybamm.ParameterValues("Chen2020"), - solver=pybamm.IDAKLUSolver() -) - -# Step 3: Run -sim.solve([0, 3600]) # Solve 1 hour - -# Step 4: Plot -sim.plot() -``` - -**Behind the Scenes:** - -1. **Model Initialization** → Submodels concatenated -2. **Build Phase** → RHS, algebraic equations assembled -3. **Parameter Substitution** → Symbols replaced with values -4. **Discretisation** → Spatial PDE → ODE/DAE -5. **Jacobian Computation** → Auto-differentiation -6. **Solver Setup** → Initial conditions, events configured -7. **Integration Loop** → Time-stepping with callbacks -8. **Post-Processing** → Compute derived variables (impedance, etc.) -9. **Visualization** → Plot results - ---- - -## 🔗 Key Dependencies & Data Flow - -### Upstream (Inputs) -``` -Experiment (current profile) - ↓ -ParameterValues (physical constants) - ↓ -Geometry (cell dimensions) - ↓ -ModelOptions (choose submodels) - ↓ -BaseModel -``` - -### Downstream (Outputs) -``` -Discretisation - ↓ -DAE System (M*dy/dt = f(t,y)) - ↓ -Solver - ↓ -Solution object (t, y, processed_variables) - ↓ -Plotting/Analysis - ↓ -Results (voltage, capacity, temperature, etc.) -``` - ---- - -## 🌳 Hotspot Nodes (Most Connected Components) - -These are the "hubs" that everything depends on: - -| Node | Type | Connections | Role | -|------|------|-----------|------| -| `src/pybamm/__init__.py` | File | **500** | Central export hub | -| `Variable` | Class | **474** | Core state representation | -| `Scalar` | Class | **397** | Constant handling | -| `evaluate()` | Function | **344** | Expression evaluation | -| `solve()` | Function | **311** | Solver invocation | -| `BaseModel` | Class | **305** | Model parent | -| `Discretisation` | Class | **289** | Discretisation orchestration | -| `linspace()` | Function | **267** | Mesh generation | - ---- - -## 📁 Directory Structure +### 5. Web App Pipeline (Browser) ``` -src/pybamm/ -├── models/ # Model hierarchy -│ ├── base_model.py # Abstract base -│ ├── full_battery_models/ # Concrete implementations -│ │ ├── lithium_ion/ -│ │ ├── lead_acid/ -│ │ ├── sodium_ion/ -│ │ └── equivalent_circuit/ -│ └── submodels/ # Pluggable physics components -│ ├── interface/ # Electrode kinetics, SEI, OCP -│ ├── particle/ # Particle diffusion -│ ├── thermal/ # Heat transfer -│ ├── electrode/ # Ohmic drop -│ ├── convection/ # Internal flow -│ └── [more...] -│ -├── expression_tree/ # Symbolic DAG -│ ├── symbol.py # Base class -│ ├── binary_operators.py # +, -, *, / -│ ├── unary_operators.py # sin, exp, log -│ ├── operations/ # Evaluation, Jacobian, serialization -│ └── [more...] -│ -├── discretisations/ # PDE → ODE conversion -│ └── discretisation.py -│ -├── spatial_methods/ # Finite volume, spectral, FEM -│ ├── finite_volume.py -│ ├── spectral_volume.py -│ └── [more...] -│ -├── meshes/ # Grid generation -│ ├── meshes.py -│ └── [submesh types...] -│ -├── solvers/ # DAE integration -│ ├── base_solver.py -│ ├── scipy_solver.py -│ ├── casadi_solver.py -│ ├── idaklu_solver.py -│ └── [more...] -│ -├── parameters/ # Physical coefficients -│ ├── base_parameters.py -│ ├── parameter_values.py -│ ├── lithium_ion_parameters.py -│ └── input/ -│ └── parameters/ # Pre-built parameter sets -│ -├── plotting/ # Visualization -│ ├── plot.py -│ ├── quick_plot.py -│ ├── plot_voltage_components.py -│ └── [more...] -│ -├── batch_study.py # Multi-parameter studies -├── simulation.py # High-level runner -├── experiment/ # Charge/discharge cycles -└── [more...] - -tests/ -├── unit/ # Isolated component tests -└── integration/ # End-to-end tests +AppStateProvider (hooks/useAppState.tsx) + → runPipeline (workers/ingestion.worker.ts) — Web Worker + → runPipelineFromFiles (ingestion/pipeline.ts) + → createKnowledgeGraph + → processParsing — WASM tree-sitter + → processImports/Calls/Heritage + → processCommunities/Processes + → loadGraphToKuzu — in-browser KuzuDB (WASM) ``` ---- +## Architecture Diagram -## 🎯 Design Patterns +```mermaid +graph TB + subgraph CLI["CLI Layer"] + analyze["analyze command"] + setup["setup command"] + mcp_cmd["mcp command"] + serve["serve command"] + augment["augment command"] + end -### 1. **Plugin Architecture (Submodels)** -- Models are built by combining plug-and-play submodels -- Easy to swap implementations (e.g., different kinetics models) -- **Example**: Switch from Butler-Volmer to Marcus kinetics + subgraph Ingestion["Ingestion Pipeline"] + walker["Filesystem Walker
(concurrent I/O)"] + structure["Structure Processor"] + parsing["Parsing Processor
(worker threads)"] + imports["Import Processor"] + calls["Call Processor"] + heritage["Heritage Processor"] + communities["Community Detection
(Leiden)"] + processes["Process Tracing"] + end -### 2. **Expression Tree Pattern** -- Decouple symbolic math from backend -- Same expression → Python, CasADi, or JAX code -- Enables automatic differentiation + subgraph TreeSitter["Tree-Sitter"] + parser_loader["Parser Loader"] + ts_queries["Language Queries
(9 languages)"] + worker_pool["Worker Pool"] + parse_worker["Parse Workers"] + end -### 3. **Factory Pattern (Solvers)** -- `solve()` returns appropriate solver based on model type -- User doesn't need to know solver implementation details + subgraph MCP["MCP Server"] + server["MCP Server
(stdio transport)"] + tools["Tools: query, context,
impact, rename, cypher"] + resources["Resources: clusters,
processes, schema"] + backend["Local Backend"] + end -### 4. **Strategy Pattern (Spatial Methods)** -- Choose discretization strategy (FV, Spectral, FEM) at runtime -- Swap without changing model code + subgraph Storage["Storage Layer"] + kuzu["KuzuDB
(graph store)"] + csv_gen["CSV Generator"] + repo_mgr["Repo Manager
(~/.gitnexus registry)"] + end -### 5. **Template Method (Model Lifecycle)** -1. `model.build_model()` -2. `disc.discretise(model)` -3. `solver.solve(t_eval, y0)` + subgraph Search["Search Engine"] + bm25["BM25 Keyword Index"] + semantic["Semantic Search
(all-MiniLM-L6-v2)"] + embedder["ONNX Embedder"] + end ---- + subgraph Hooks["IDE Integration"] + claude_hook["Claude Code Hooks"] + cursor_hook["Cursor Hooks"] + skills["Skills
(exploring, debugging,
impact, refactoring)"] + end -## 🚀 Performance Considerations + subgraph Web["Web Frontend"] + app["React App"] + web_worker["Web Worker
(WASM ingestion)"] + components["Graph Visualization"] + end -### Bottlenecks -1. **Discretisation**: Large spatial grids → huge state vectors -2. **Jacobian Computation**: Dense matrices for implicit solvers -3. **Parameter Substitution**: Re-expression tree traversal + %% CLI → Ingestion + analyze --> walker + walker --> structure --> parsing --> imports --> calls --> heritage --> communities --> processes -### Optimizations -1. **CasADi Backend**: Symbolic optimization + JIT -2. **JAX Solver**: GPU acceleration, batched derivatives -3. **IDA Solver**: C++ wrapper, sparse Jacobian support -4. **LRU Caching**: Avoid recomputation + %% Parsing uses tree-sitter workers + parsing --> worker_pool --> parse_worker + parse_worker --> parser_loader + parse_worker --> ts_queries ---- + %% Ingestion → Storage + processes --> csv_gen --> kuzu + processes --> embedder -## 🔐 Testing Strategy + %% MCP flow + mcp_cmd --> server --> tools --> backend --> kuzu + server --> resources --> backend + backend --> bm25 + backend --> semantic --> embedder -### Unit Tests (973 files) -- Component-level validation -- Expression tree operations -- Spatial method correctness + %% CLI → Setup + setup --> repo_mgr + setup --> claude_hook + setup --> cursor_hook + setup --> skills -### Integration Tests -- Full model runs -- Solver convergence -- Different parameter sets + %% Hooks → MCP + claude_hook -.->|augments searches| tools + cursor_hook -.->|augments searches| tools + augment -.->|fast CLI path| backend -### Benchmark Tests -- Performance tracking -- Memory profiling -- Scaling analysis + %% Web + app --> web_worker --> components + web_worker --> kuzu ---- - -## 📚 Key Math Concepts - -### Governing Equations -**DAE System:** -``` -M(t,y) * dy/dt = f(t, y, u(t)) [Differential equations] -0 = g(t, y, u(t)) [Algebraic equations] + %% Serve + serve --> backend ``` -where: -- `y` = state vector (concentrations, potentials, temperature) -- `u(t)` = inputs (applied current, ambient temperature) -- `M` = mass matrix (handles singular systems) +## Data Flow Summary -### Typical Physics - -**Particle Diffusion (Fick's Law):** ``` -∂c/∂t = ∇·(D∇c) +Source Code + │ + ▼ +┌─────────────────────────────────────────┐ +│ Ingestion Pipeline (8 phases) │ +│ Files → AST → Symbols → Relationships │ +│ → Communities → Execution Flows │ +└─────────────────┬───────────────────────┘ + │ + ┌───────┴───────┐ + ▼ ▼ + ┌──────────┐ ┌────────────┐ + │ KuzuDB │ │ Embeddings │ + │ (graph) │ │ (vectors) │ + └────┬─────┘ └─────┬──────┘ + │ │ + └───────┬───────┘ + ▼ + ┌──────────────┐ + │ MCP Server │ + │ (7 tools) │ + └──────┬───────┘ + │ + ┌───────────┼───────────┐ + ▼ ▼ ▼ + Claude Cursor Other + Code Editor MCP Clients ``` -**Charge Conservation (Poisson):** -``` -∇·(σ∇φ) = i -``` - -**Energy Balance (Heat Equation):** -``` -ρCp ∂T/∂t = ∇·(k∇T) + Q_gen -``` - ---- - -## 🎓 Learning Path - -1. **Start**: Run SPM model (`pybamm.lithium_ion.SPM()`) -2. **Progress**: Modify parameter set, change solver -3. **Intermediate**: Swap submodels (DFN, thermal) -4. **Advanced**: Create custom submodel -5. **Expert**: Implement new spatial method - ---- - -## 🔮 Architecture Strengths - -✅ **Modularity**: Plug-and-play submodels -✅ **Extensibility**: Easy to add new models/solvers -✅ **Physics-First**: Expression tree mirrors actual equations -✅ **Backend-Agnostic**: Switch solvers without changing model -✅ **Scientific Quality**: Validated against experiments -✅ **Performance**: Multiple backends (Python, C++, JAX) - ---- - -## ⚠️ Architecture Tradeoffs - -⚖️ **Complexity**: Large learning curve -⚖️ **Symbolic Overhead**: DAG construction has memory cost -⚖️ **Debug Difficulty**: Multiple abstraction layers -⚖️ **Startup Time**: Model compilation + discretisation - ---- - -## 🎯 Conclusion - -PyBaMM's architecture is a **layered, modular system** optimized for: -- **Scientific fidelity** (physics-based discretisation) -- **Extensibility** (plug-and-play submodels) -- **Performance** (multiple backends) -- **Usability** (high-level simulation API) - -The design cleanly separates concerns across 7 layers, from symbolic math to numerical solvers, making it suitable for both research and production use. - ---- - -*Analysis powered by GitNexus MCP - Code Intelligence Engine* +## Supported Languages +Tree-sitter grammars are included for: **TypeScript**, **JavaScript**, **Python**, **Java**, **C**, **C++**, **C#**, **Go**, **Rust**. +## Key Design Decisions +1. **Augmentation over replacement** — Hooks enrich existing AI agent tools (Grep, Glob, Bash) with graph context rather than replacing them +2. **Native tree-sitter** — Uses N-API bindings (not WASM) in the CLI for performance; WASM in the browser +3. **Worker thread parsing** — CPU-bound tree-sitter parsing parallelized across `cpus - 1` worker threads +4. **Hybrid search** — BM25 keyword + semantic vector search combined with Reciprocal Rank Fusion for ranking +5. **LRU AST cache** — Parsed trees are cached across pipeline phases to avoid redundant re-parsing +6. **Deterministic IDs** — `generateId(label, qualifiedName)` ensures idempotent graph construction diff --git a/ARCHITECTURE_QUICK_REF.md b/ARCHITECTURE_QUICK_REF.md index f7ea6e372..52066fcb1 100644 --- a/ARCHITECTURE_QUICK_REF.md +++ b/ARCHITECTURE_QUICK_REF.md @@ -374,3 +374,11 @@ model = pybamm.lithium_ion.DFN( + + + + + + + + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..cf96c279c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ + +# GitNexus MCP + +This project is indexed by GitNexus as **GitnexusV2** (1295 symbols, 3262 relationships, 99 execution flows). + +GitNexus provides a knowledge graph over this codebase — call chains, blast radius, execution flows, and semantic search. + +## Always Start Here + +For any task involving code understanding, debugging, impact analysis, or refactoring, you must: + +1. **Read `gitnexus://repo/{name}/context`** — codebase overview + check index freshness +2. **Match your task to a skill below** and **read that skill file** +3. **Follow the skill's workflow and checklist** + +> If step 1 warns the index is stale, run `npx gitnexus analyze` in the terminal first. + +## Skills + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/refactoring/SKILL.md` | + +## Tools Reference + +| Tool | What it gives you | +|------|-------------------| +| `query` | Process-grouped code intelligence — execution flows related to a concept | +| `context` | 360-degree symbol view — categorized refs, processes it participates in | +| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence | +| `detect_changes` | Git-diff impact — what do your current changes affect | +| `rename` | Multi-file coordinated rename with confidence-tagged edits | +| `cypher` | Raw graph queries (read `gitnexus://repo/{name}/schema` first) | +| `list_repos` | Discover indexed repos | + +## Resources Reference + +Lightweight reads (~100-500 tokens) for navigation: + +| Resource | Content | +|----------|---------| +| `gitnexus://repo/{name}/context` | Stats, staleness check | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores | +| `gitnexus://repo/{name}/cluster/{clusterName}` | Area members | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{processName}` | Step-by-step trace | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher | + +## Graph Schema + +**Nodes:** File, Function, Class, Interface, Method, Community, Process +**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"}) +RETURN caller.name, caller.filePath +``` + + diff --git a/GITNEXUS_ANALYSIS.md b/GITNEXUS_ANALYSIS.md index 16d9f29d4..49254ec50 100644 --- a/GITNEXUS_ANALYSIS.md +++ b/GITNEXUS_ANALYSIS.md @@ -373,3 +373,11 @@ Expression tree traversal: + + + + + + + + diff --git a/LICENSE b/LICENSE index b7dfb94de..485af9b57 100644 --- a/LICENSE +++ b/LICENSE @@ -18,7 +18,7 @@ The licensor grants you an additional copyright license to distribute copies of You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example: -> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) +> Required Notice: Copyright Abhigyan Patwari (https://github.com/abhigyanpatwari/GitNexus) ## Changes and New Works License diff --git a/README.md b/README.md index 6eb5f3a32..87776f3af 100644 --- a/README.md +++ b/README.md @@ -1,183 +1,373 @@ -# GitNexus V2 +# GitNexus -**Zero-Server, Graph-Based Code Intelligence Engine** -Works fully in-browser through WebAssembly. (DB engine, Embeddings model, AST parsing, all happens inside browser) +**Building git for agent context.** +Indexes any codebase into a knowledge graph — every dependency, call chain, cluster, and execution flow — then exposes it through smart tools so AI agents never miss code. + +[![npm version](https://img.shields.io/npm/v/gitnexus.svg)](https://www.npmjs.com/package/gitnexus) +[![License: PolyForm Noncommercial](https://img.shields.io/badge/License-PolyForm%20Noncommercial-blue.svg)](https://polyformproject.org/licenses/noncommercial/1.0.0/) https://github.com/user-attachments/assets/abfd0300-0aae-4296-b8d3-8b72ed882433 -https://gitnexus.vercel.app -Being client sided, it costs me zero to deploy, so you can use it for free :-) (would love a ⭐ though) +> *Like DeepWiki, but deeper.* DeepWiki helps you *understand* code. GitNexus lets you *analyze* it — because a knowledge graph tracks every relationship, not just descriptions. -> *Like DeepWiki, but deeper.* 😉 - -DeepWiki helps you *understand* code. GitNexus lets you *analyze* it—because a knowledge graph tracks every dependency, call chain, and relationship. - -That's the difference between: -- "What does this function do?" → *understanding* -- "What breaks if I change this function?" → *analysis* - -**Core Innovation: Precomputed Relational Intelligence** - -Most AI coding tools give the LLM raw data and hope it figures out relationships. GitNexus **precomputes structure at index time**—clustering related code, tracing execution flows, scoring edge confidence—so tools return *decision-ready context*. This means: -- 🎯 **Reliability**: LLM can't miss context—it's already in the tool response -- ⚡ **Token efficiency**: No 10-query chains to understand one function -- 🤖 **Model democratization**: Smaller LLMs work because tools do the heavy lifting - -**Quick tech jargon:** -- **Smart Tools**: 7 graph-aware tools with built-in cluster/process context -- **Leiden Clustering**: Automatic detection of functional code communities -- **Process Detection**: Entry point tracing via BFS with framework-aware scoring -- **Confidence Scoring**: Every CALLS edge rated 0-1 (import-resolved vs fuzzy guess) -- **Hybrid Search**: BM25 + Semantic + 1-hop graph expansion via Cypher -- **Full WASM Stack**: Tree-sitter parsing + KuzuDB graph database, all in-browser -- **9 Languages**: TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust - -**What you can do:** - -| Capability | Description | -|------------|-------------| -| **Codebase-wide audits** | Find layer violations, forbidden dependencies | -| **Blast radius analysis** | See every function affected by a change (with confidence) | -| **Dead code detection** | Identify orphaned nodes with zero incoming calls | -| **Dependency tracing** | Follow import chains across the entire codebase | -| **Process exploration** | Trace execution flows from API handlers to data layer | -| **Cluster navigation** | Explore code by functional area, not just file structure | -| **AI analyses with citations** | Ask questions, analyze, get answers with `[[file:line]]` proof | - -**100% client-side.** Your code never leaves your browser. - -gitnexus_img +**TL;DR:** The **Web UI** is a quick way to chat with any repo. The **CLI + MCP** is how you make your AI agent actually reliable — it gives Cursor, Claude Code, and friends a deep architectural view of your codebase so they stop missing dependencies, breaking call chains, and shipping blind edits. Even smaller models get full architectural clarity, making it compete with goliath models. --- -## 🔍 The Problem with AI Coding Tools +## Two Ways to Use GitNexus -Tools like **Cursor**, **Claude Code**, **Cline**, **Roo Code**, and **Windsurf** are powerful—but they share a fundamental limitation: **they don't truly know your codebase structure**. +| | **CLI + MCP** | **Web UI** | +| ----------------- | -------------------------------------------------------------- | ------------------------------------------------------------ | +| **What** | Index repos locally, connect AI agents via MCP | Visual graph explorer + AI chat in browser | +| **For** | Daily development with Cursor, Claude Code, Windsurf, OpenCode | Quick exploration, demos, one-off analysis | +| **Scale** | Full repos, any size | Limited by browser memory (~5k files) | +| **Install** | `npm install -g gitnexus` | No install —[gitnexus.vercel.app](https://gitnexus.vercel.app) | +| **Storage** | KuzuDB native (fast, persistent) | KuzuDB WASM (in-memory, per session) | +| **Parsing** | Tree-sitter native bindings | Tree-sitter WASM | +| **Privacy** | Everything local, no network | Everything in-browser, no server | + +--- + +## CLI + MCP (recommended) + +The CLI indexes your repository and runs an MCP server that gives AI agents deep codebase awareness. + +### Quick Start + +```bash +# Index your repo (run from repo root) +npx gitnexus analyze +``` + +That's it. This indexes the codebase, installs agent skills, registers Claude Code hooks, and creates `AGENTS.md` / `CLAUDE.md` context files — all in one command. + +To configure MCP for your editor, run `npx gitnexus setup` once — or set it up manually below. + +### MCP Setup + +`gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once. + +### Editor Support + +| Editor | MCP | Skills | Hooks (auto-augment) | Support | +| --------------------- | --- | ------ | -------------------- | -------------- | +| **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** | +| **Cursor** | Yes | Yes | — | MCP + Skills | +| **Windsurf** | Yes | — | — | MCP | +| **OpenCode** | Yes | Yes | — | MCP + Skills | + +> **Claude Code** gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context. + +If you prefer manual configuration: + +**Claude Code** (full support — MCP + skills + hooks): + +```bash +claude mcp add gitnexus -- npx -y gitnexus@latest mcp +``` + +**Cursor** (`~/.cursor/mcp.json` — global, works for all projects): + +```json +{ + "mcpServers": { + "gitnexus": { + "command": "npx", + "args": ["-y", "gitnexus@latest", "mcp"] + } + } +} +``` + +**OpenCode** (`~/.config/opencode/config.json`): + +```json +{ + "mcp": { + "gitnexus": { + "command": "npx", + "args": ["-y", "gitnexus@latest", "mcp"] + } + } +} +``` + +### CLI Commands + +```bash +gitnexus setup # Configure MCP for your editors (one-time) +gitnexus analyze [path] # Index a repository (or update stale index) +gitnexus analyze --force # Force full re-index +gitnexus analyze --skip-embeddings # Skip embedding generation (faster) +gitnexus mcp # Start MCP server (stdio) — serves all indexed repos +gitnexus serve # Start HTTP server for web UI connection +gitnexus list # List all indexed repositories +gitnexus status # Show index status for current repo +gitnexus clean # Delete index for current repo +gitnexus clean --all --force # Delete all indexes +gitnexus wiki [path] # Generate repository wiki from knowledge graph +gitnexus wiki --model # Wiki with custom LLM model (default: gpt-4o-mini) +gitnexus wiki --base-url # Wiki with custom LLM API base URL +``` + +### What Your AI Agent Gets + +**7 tools** exposed via MCP: + +| Tool | What It Does | `repo` Param | +| ------------------ | ----------------------------------------------------------------- | -------------- | +| `list_repos` | Discover all indexed repositories | — | +| `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional | +| `context` | 360-degree symbol view — categorized refs, process participation | Optional | +| `impact` | Blast radius analysis with depth grouping and confidence | Optional | +| `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional | +| `rename` | Multi-file coordinated rename with graph + text search | Optional | +| `cypher` | Raw Cypher graph queries | Optional | + +> When only one repo is indexed, the `repo` parameter is optional. With multiple repos, specify which one: `query({query: "auth", repo: "my-app"})`. + +**Resources** for instant context: + +| Resource | Purpose | +| ----------------------------------------- | ---------------------------------------------------- | +| `gitnexus://repos` | List all indexed repositories (read this first) | +| `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools | +| `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores | +| `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{name}` | Full process trace with steps | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries | + +**2 MCP prompts** for guided workflows: + +| Prompt | What It Does | +| ----------------- | ------------------------------------------------------------------------- | +| `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level | +| `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams | + +**4 agent skills** installed to `.claude/skills/` automatically: + +- **Exploring** — Navigate unfamiliar code using the knowledge graph +- **Debugging** — Trace bugs through call chains +- **Impact Analysis** — Analyze blast radius before changes +- **Refactoring** — Plan safe refactors using dependency mapping + +--- + +## Multi-Repo MCP Architecture + +GitNexus uses a **global registry** so one MCP server can serve multiple indexed repos. No per-project MCP config needed — set it up once and it works everywhere. + +```mermaid +flowchart TD + subgraph CLI [CLI Commands] + Setup["gitnexus setup"] + Analyze["gitnexus analyze"] + Clean["gitnexus clean"] + List["gitnexus list"] + end + + subgraph Registry ["~/.gitnexus/"] + RegFile["registry.json"] + end + + subgraph Repos [Project Repos] + RepoA[".gitnexus/ in repo A"] + RepoB[".gitnexus/ in repo B"] + end + + subgraph MCP [MCP Server] + Server["server.ts"] + Backend["LocalBackend"] + Pool["Connection Pool"] + ConnA["KuzuDB conn A"] + ConnB["KuzuDB conn B"] + end + + Setup -->|"writes global MCP config"| CursorConfig["~/.cursor/mcp.json"] + Analyze -->|"registers repo"| RegFile + Analyze -->|"stores index"| RepoA + Clean -->|"unregisters repo"| RegFile + List -->|"reads"| RegFile + Server -->|"reads registry"| RegFile + Server --> Backend + Backend --> Pool + Pool -->|"lazy open"| ConnA + Pool -->|"lazy open"| ConnB + ConnA -->|"queries"| RepoA + ConnB -->|"queries"| RepoB +``` + +**How it works:** Each `gitnexus analyze` stores the index in `.gitnexus/` inside the repo (portable, gitignored) and registers a pointer in `~/.gitnexus/registry.json`. When an AI agent starts, the MCP server reads the registry and can serve any indexed repo. KuzuDB connections are opened lazily on first query and evicted after 5 minutes of inactivity (max 5 concurrent). If only one repo is indexed, the `repo` parameter is optional on all tools — agents don't need to change anything. + +--- + +## Web UI (browser-based) + +A fully client-side graph explorer and AI chat. No server, no install — your code never leaves the browser. + +**Try it now:** [gitnexus.vercel.app](https://gitnexus.vercel.app) — drag & drop a ZIP and start exploring. + +gitnexus_img + +Or run locally: + +```bash +git clone https://github.com/abhigyanpatwari/gitnexus.git +cd gitnexus/gitnexus-web +npm install +npm run dev +``` + +The web UI uses the same indexing pipeline as the CLI but runs entirely in WebAssembly (Tree-sitter WASM, KuzuDB WASM, in-browser embeddings). It's great for quick exploration but limited by browser memory for larger repos. + +--- + +## The Problem GitNexus Solves + +Tools like **Cursor**, **Claude Code**, **Cline**, **Roo Code**, and **Windsurf** are powerful — but they don't truly know your codebase structure. **What happens:** -1. AI edits `UserService.validate()` + +1. AI edits `UserService.validate()` 2. Doesn't know 47 functions depend on its return type -3. **Breaking changes ship** 💥 +3. **Breaking changes ship** -### The Solution: Precomputed Graph Intelligence +### Traditional Graph RAG vs GitNexus -Traditional Graph RAG gives the LLM raw edges and hopes it explores enough. GitNexus precomputes structure so tools return complete context in one call: +Traditional approaches give the LLM raw graph edges and hope it explores enough. GitNexus **precomputes structure at index time** — clustering, tracing, scoring — so tools return complete context in one call: ```mermaid flowchart TB - subgraph Traditional["❌ Traditional Graph RAG"] + subgraph Traditional["Traditional Graph RAG"] direction TB U1["User: What depends on UserService?"] U1 --> LLM1["LLM receives raw graph"] LLM1 --> Q1["Query 1: Find callers"] - Q1 --> R1["47 node IDs returned"] - R1 --> Q2["Query 2: What files are these?"] - Q2 --> R2["12 file paths"] - R2 --> Q3["Query 3: Filter out tests?"] - Q3 --> R3["8 production files"] - R3 --> Q4["Query 4: Which are high-risk?"] - Q4 --> THINK["LLM interprets..."] - THINK --> OUT1["Answer after 4+ queries"] + Q1 --> Q2["Query 2: What files?"] + Q2 --> Q3["Query 3: Filter tests?"] + Q3 --> Q4["Query 4: High-risk?"] + Q4 --> OUT1["Answer after 4+ queries"] end - subgraph GitNexus["✅ GitNexus Smart Tools"] + subgraph GN["GitNexus Smart Tools"] direction TB U2["User: What depends on UserService?"] U2 --> TOOL["impact UserService upstream"] TOOL --> PRECOMP["Pre-structured response: - • 8 production callers - • Grouped: Auth 3, Payment 2, API 3 - • All 90%+ confidence - • 5 in LoginFlow process"] + 8 callers, 3 clusters, all 90%+ confidence"] PRECOMP --> OUT2["Complete answer, 1 query"] end ``` -**Current state:** GitNexus is a standalone tool—a better DeepWiki that's 100% client-side with graph-powered analysis. +**Core innovation: Precomputed Relational Intelligence** -**MCP Integration:** GitNexus also runs as an MCP server (`gitnexus-mcp`) so tools like Cursor and Claude Code can query it for accurate context. - -git clone https://github.com/abhigyanpatwari/gitnexus.git -cd gitnexus -npm install -npm run dev - -Open http://localhost:5173, drag & drop a ZIP of your codebase, and start exploring. +- **Reliability** — LLM can't miss context, it's already in the tool response +- **Token efficiency** — No 10-query chains to understand one function +- **Model democratization** — Smaller LLMs work because tools do the heavy lifting --- -## 🏗️ Indexing Architecture +## How Indexing Works -Seven-phase indexing: **Structure** → **Parse** → **Imports** → **Calls** → **Heritage** → **Communities** → **Processes**. +Seven-phase pipeline that builds a complete knowledge graph: ```mermaid flowchart TD - subgraph P1["Phase 1: Extract (0-15%)"] - E1[Decompress ZIP] --> E2[Collect file paths] + subgraph P1["Phase 1: Structure (0-15%)"] + S1[Walk file tree] --> S2[Create CONTAINS edges] end - - subgraph P2["Phase 2: Structure (15-30%)"] - S1[Build folder tree] --> S2[Create CONTAINS edges] - end - - subgraph P3["Phase 3: Parse (30-55%)"] - PA1[Load Tree-sitter WASM] --> PA2[Generate ASTs] - PA2 --> PA3[Extract symbols] + + subgraph P2["Phase 2: Parse (15-40%)"] + PA1[Load Tree-sitter parsers] --> PA2[Generate ASTs] + PA2 --> PA3[Extract functions, classes, methods] PA3 --> PA4[Populate Symbol Table] end - - subgraph P4["Phase 4: Imports (55-65%)"] - I1[Find import statements] --> I2[Resolve paths] + + subgraph P3["Phase 3: Imports (40-55%)"] + I1[Find import statements] --> I2[Language-aware resolution] I2 --> I3[Create IMPORTS edges] end - - subgraph P5["Phase 5: Calls + Heritage (65-80%)"] + + subgraph P4["Phase 4: Calls + Heritage (55-75%)"] C1[Find function calls] --> C2[Resolve via Symbol Table] C2 --> C3[Create CALLS edges with confidence] C3 --> H1[Find extends/implements] H1 --> H2[Create EXTENDS/IMPLEMENTS edges] end - - subgraph P6["Phase 6: Communities (80-90%)"] + + subgraph P5["Phase 5: Communities (75-85%)"] CM1[Build CALLS graph] --> CM2[Run Leiden algorithm] CM2 --> CM3[Calculate cohesion scores] CM3 --> CM4[Generate heuristic labels] CM4 --> CM5[Create MEMBER_OF edges] end - - subgraph P7["Phase 7: Processes (90-100%)"] + + subgraph P6["Phase 6: Processes (85-95%)"] PR1[Score entry points] --> PR2[BFS trace via CALLS] PR2 --> PR3[Detect cross-community flows] PR3 --> PR4[Create STEP_IN_PROCESS edges] end - + + subgraph P7["Phase 7: Embeddings (95-100%)"] + EM1[Generate embeddings] --> EM2[Build HNSW vector index] + EM2 --> EM3[Build BM25 full-text index] + end + P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 - P7 --> DB[(KuzuDB WASM)] - DB --> READY[Graph Ready!] + P7 --> DB[(KuzuDB)] + DB --> READY[Graph Ready] ``` -### Symbol Table: Dual HashMap +### Supported Languages -Resolution strategy for function calls (produces **confidence scores**): +TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust + +### Language-Aware Import Resolution + +GitNexus doesn't just string-match import paths. It understands language-specific module systems: + +| Language | What's Resolved | +| -------------------- | ----------------------------------------------------------------------------- | +| **TypeScript** | Path aliases from `tsconfig.json` (e.g. `@/lib/auth` -> `src/lib/auth`) | +| **Rust** | Module paths (`crate::auth::validate`, `super::utils`, `self::handler`) | +| **Java** | Wildcard imports (`com.example.*`) and static imports | +| **Go** | Module paths via `go.mod`, internal package resolution | +| **C/C++** | Relative includes, system include detection | + +### Confidence Scoring on CALLS + +Every function call edge includes a trust score: + +| Confidence | Reason | Meaning | +| ---------- | ---------------------------- | ------------------------------ | +| 0.90 | `import-resolved` | Target found in imported file | +| 0.85 | `same-file` | Target defined in same file | +| 0.50 | `fuzzy-global` (1 match) | Single global match by name | +| 0.30 | `fuzzy-global` (N matches) | Multiple matches, first picked | + +The `impact` tool uses `minConfidence` to filter out guesses and return only reliable results. + +### Symbol Table: Dual HashMap ```mermaid flowchart TD CALL["Found call: validateUser"] --> CHECK1{"In Import Map?"} - CHECK1 -->|Yes| FOUND1["✅ Import-resolved (90%)"] + CHECK1 -->|Yes| FOUND1["Import-resolved (90%)"] CHECK1 -->|No| CHECK2{"In Current File?"} - CHECK2 -->|Yes| FOUND2["✅ Same-file (85%)"] + CHECK2 -->|Yes| FOUND2["Same-file (85%)"] CHECK2 -->|No| CHECK3{"Global Search"} - CHECK3 -->|1 match| FOUND3["⚠️ Fuzzy single (50%)"] - CHECK3 -->|N matches| FOUND4["⚠️ Fuzzy multiple (30%)"] + CHECK3 -->|1 match| FOUND3["Fuzzy single (50%)"] + CHECK3 -->|N matches| FOUND4["Fuzzy multiple (30%)"] CHECK3 -->|Not Found| SKIP["Skip - unresolved"] - + FOUND1 & FOUND2 & FOUND3 & FOUND4 --> EDGE["Create CALLS edge with confidence"] ``` ### Community Detection (Leiden Algorithm) -Groups related code by analyzing CALLS edge density: +Groups related code into functional clusters by analyzing CALLS edge density: ```mermaid flowchart LR @@ -191,7 +381,7 @@ flowchart LR COHESION --> MEMBER["MEMBER_OF edges"] ``` -**Why it matters:** Instead of "this function is in `/src/auth/validate.ts`", the agent knows "this function is in the **Authentication** cluster with 23 other related symbols." +Instead of "this function is in `/src/auth/validate.ts`", the agent knows "this function is in the **Authentication** cluster with 23 other related symbols." ### Process Detection (Entry Point Tracing) @@ -200,14 +390,14 @@ Finds execution flows by tracing from entry points: ```mermaid flowchart TD FUNCS[All Functions/Methods] --> SCORE["Score entry point likelihood"] - + subgraph Scoring["Entry Point Scoring"] BASE["Call ratio: callees/(callers+1)"] - EXPORT["× 2.0 if exported"] - NAME["× 1.5 if handle*/on*/Controller"] - FW["× 3.0 if in /routes/ or /handlers/"] + EXPORT["x 2.0 if exported"] + NAME["x 1.5 if handle*/on*/Controller"] + FW["x 3.0 if in /routes/ or /handlers/"] end - + SCORE --> Scoring Scoring --> TOP["Top candidates"] TOP --> BFS["BFS trace via CALLS (max 10 hops)"] @@ -216,114 +406,150 @@ flowchart TD ``` **Framework detection** boosts scoring for known patterns: + - Next.js: `/pages/`, `/app/page.tsx`, `/api/` - Express: `/routes/`, `/handlers/` - Django: `views.py`, `urls.py` - Spring: `/controllers/`, `*Controller.java` - And more for Go, Rust, C#... -### Background Embeddings - -```mermaid -flowchart LR - subgraph BG["Background (Non-blocking)"] - M1[Load snowflake-arctic-embed-xs] --> M2[Initialize WebGPU/WASM] - M2 --> E1[Batch embed nodes] - E1 --> E2[INSERT into CodeEmbedding table] - E2 --> V1[Create HNSW Vector Index] - V1 --> B1[Build BM25 Index] - end - - BG --> AI[AI Search Ready!] -``` - -User can explore the graph during embedding. AI features unlock when complete. - --- -## 📊 Graph Schema +## Graph Schema ### Node Types -| Label | Description | Key Properties | -|-------|-------------|----------------| -| `Folder` | Directory | `name`, `filePath` | -| `File` | Source file | `name`, `filePath`, `language` | -| `Function` | Function def | `name`, `filePath`, `startLine`, `endLine`, `isExported` | -| `Class` | Class def | `name`, `filePath`, `startLine`, `endLine` | -| `Interface` | Interface def | `name`, `filePath`, `startLine`, `endLine` | -| `Method` | Class method | `name`, `filePath`, `startLine`, `endLine` | -| `Community` | Functional cluster | `label`, `cohesion`, `symbolCount`, `description` | -| `Process` | Execution flow | `label`, `processType`, `stepCount`, `entryPointId` | +| Label | Description | Key Properties | +| ------------- | ------------------ | ------------------------------------------------------------------ | +| `Folder` | Directory | `name`, `filePath` | +| `File` | Source file | `name`, `filePath`, `content` | +| `Function` | Function def | `name`, `filePath`, `startLine`, `endLine`, `isExported` | +| `Class` | Class def | `name`, `filePath`, `startLine`, `endLine`, `isExported` | +| `Interface` | Interface def | `name`, `filePath`, `startLine`, `endLine`, `isExported` | +| `Method` | Class method | `name`, `filePath`, `startLine`, `endLine`, `isExported` | +| `Community` | Functional cluster | `label`, `heuristicLabel`, `cohesion`, `symbolCount` | +| `Process` | Execution flow | `label`, `processType`, `stepCount`, `entryPointId` | + +Plus language-specific nodes: `Struct`, `Enum`, `Trait`, `Impl`, `TypeAlias`, `Namespace`, `Record`, `Delegate`, `Annotation`, `Constructor`, `Template`, `Module` and more. ### Relationship Table: `CodeRelation` Single edge table with `type` property: -| Type | From | To | Properties | -|------|------|-----|------------| -| `CONTAINS` | Folder | File/Folder | — | -| `DEFINES` | File | Function/Class/etc | — | -| `IMPORTS` | File | File | — | -| `CALLS` | Function/Method | Function/Method | `confidence`, `reason` | -| `EXTENDS` | Class | Class | — | -| `IMPLEMENTS` | Class | Interface | — | -| `MEMBER_OF` | Symbol | Community | — | -| `STEP_IN_PROCESS` | Symbol | Process | `step` (1-indexed position) | - -### Confidence Scores on CALLS - -Every CALLS edge includes trust metadata: - -| Confidence | Reason | Meaning | -|------------|--------|---------| -| 0.90 | `import-resolved` | Target found in imported file | -| 0.85 | `same-file` | Target defined in same file | -| 0.50 | `fuzzy-global` (1 match) | Single global match by name | -| 0.30 | `fuzzy-global` (N matches) | Multiple matches, first picked | - -**Why it matters:** The `impact` tool filters by `minConfidence` (default 0.7) to exclude guesses. +| Type | From | To | Properties | +| ------------------- | --------------- | ------------------ | -------------------------- | +| `CONTAINS` | Folder | File/Folder | — | +| `DEFINES` | File | Function/Class/etc | — | +| `IMPORTS` | File | File | — | +| `CALLS` | Function/Method | Function/Method | `confidence`, `reason` | +| `EXTENDS` | Class | Class | — | +| `IMPLEMENTS` | Class | Interface | — | +| `MEMBER_OF` | Symbol | Community | — | +| `STEP_IN_PROCESS` | Symbol | Process | `step` (1-indexed) | --- -## 🛠️ Agent Tools Architecture +## Tool Examples -The LangChain ReAct agent has **7 tools** for code exploration. These tools **use precomputed structure** (clusters, processes, confidence) to return rich context. +### Impact Analysis -### Tool 1: `search` — Hybrid Search with Process Grouping +``` +impact({target: "UserService", direction: "upstream", minConfidence: 0.8}) -Combines **BM25** (keyword) + **Semantic** (vector) + **1-hop expansion** + **process context**: +TARGET: Class UserService (src/services/user.ts) -```mermaid -flowchart TD - Q["Query: auth middleware"] --> HYBRID["Hybrid Search (BM25 + Semantic)"] - HYBRID --> RRF["Reciprocal Rank Fusion"] - RRF --> TOP["Top K Results"] - - TOP --> ENRICH["For each result:"] - ENRICH --> HOP["1-hop connections + confidence"] - ENRICH --> CLUSTER["Cluster membership"] - ENRICH --> PROC["Process participation"] - - HOP & CLUSTER & PROC --> GROUP["Group by process"] - GROUP --> OUT["Structured output: - PROCESS: LoginFlow (3 matches) - [1] Function: validateUser (step 2/7) - Cluster: Authentication - Connections: ←[CALLS 90%] handleLogin"] +UPSTREAM (what depends on this): + Depth 1 (WILL BREAK): + handleLogin [CALLS 90%] -> src/api/auth.ts:45 + handleRegister [CALLS 90%] -> src/api/auth.ts:78 + UserController [CALLS 85%] -> src/controllers/user.ts:12 + Depth 2 (LIKELY AFFECTED): + authRouter [IMPORTS] -> src/routes/auth.ts ``` -Each result includes not just *what matches*, but *where it fits* in the codebase structure. +Options: `maxDepth`, `minConfidence`, `relationTypes` (`CALLS`, `IMPORTS`, `EXTENDS`, `IMPLEMENTS`), `includeTests` ---- +### Process-Grouped Search -### Tool 2: `cypher` — Raw Graph Queries +``` +query({query: "authentication middleware"}) -Execute Cypher directly. Supports `{{QUERY_VECTOR}}` auto-embedding: +processes: + - summary: "LoginFlow" + priority: 0.042 + symbol_count: 4 + process_type: cross_community + step_count: 7 + +process_symbols: + - name: validateUser + type: Function + filePath: src/auth/validate.ts + process_id: proc_login + step_index: 2 + +definitions: + - name: AuthConfig + type: Interface + filePath: src/types/auth.ts +``` + +### Context (360-degree Symbol View) + +``` +context({name: "validateUser"}) + +symbol: + uid: "Function:validateUser" + kind: Function + filePath: src/auth/validate.ts + startLine: 15 + +incoming: + calls: [handleLogin, handleRegister, UserController] + imports: [authRouter] + +outgoing: + calls: [checkPassword, createSession] + +processes: + - name: LoginFlow (step 2/7) + - name: RegistrationFlow (step 3/5) +``` + +### Detect Changes (Pre-Commit) + +``` +detect_changes({scope: "all"}) + +summary: + changed_count: 12 + affected_count: 3 + changed_files: 4 + risk_level: medium + +changed_symbols: [validateUser, AuthService, ...] +affected_processes: [LoginFlow, RegistrationFlow, ...] +``` + +### Rename (Multi-File) + +``` +rename({symbol_name: "validateUser", new_name: "verifyUser", dry_run: true}) + +status: success +files_affected: 5 +total_edits: 8 +graph_edits: 6 (high confidence) +text_search_edits: 2 (review carefully) +changes: [...] +``` + +### Cypher Queries ```cypher --- Find what calls auth functions in the Authentication cluster -MATCH (c:Community {label: 'Authentication'})<-[:CodeRelation {type: 'MEMBER_OF'}]-(fn) +-- Find what calls auth functions with high confidence +MATCH (c:Community {heuristicLabel: 'Authentication'})<-[:CodeRelation {type: 'MEMBER_OF'}]-(fn) MATCH (caller)-[r:CodeRelation {type: 'CALLS'}]->(fn) WHERE r.confidence > 0.8 RETURN caller.name, fn.name, r.confidence @@ -332,222 +558,87 @@ ORDER BY r.confidence DESC --- -### Tool 3: `grep` — Regex Pattern Matching +## Wiki Generation -For exact strings, error codes, TODOs: +Generate LLM-powered documentation from your knowledge graph: +```bash +# Requires an LLM API key (OPENAI_API_KEY, etc.) +gitnexus wiki + +# Use a custom model or provider +gitnexus wiki --model gpt-4o +gitnexus wiki --base-url https://api.anthropic.com/v1 + +# Force full regeneration +gitnexus wiki --force ``` -grep TODO|FIXME --fileFilter=.ts -→ src/auth/validate.ts:42: // TODO: Add rate limiting -``` + +The wiki generator reads the indexed graph structure, groups files into modules via LLM, generates per-module documentation pages, and creates an overview page — all with cross-references to the knowledge graph. --- -### Tool 4: `read` — Smart File Reader +## Tech Stack -Fuzzy path matching with suggestions if not found. +| Layer | CLI | Web | +| ------------------------- | ------------------------------------- | --------------------------------------- | +| **Runtime** | Node.js (native) | Browser (WASM) | +| **Parsing** | Tree-sitter native bindings | Tree-sitter WASM | +| **Database** | KuzuDB native | KuzuDB WASM | +| **Embeddings** | HuggingFace transformers.js (GPU/CPU) | transformers.js (WebGPU/WASM) | +| **Search** | BM25 + semantic + RRF | BM25 + semantic + RRF | +| **Agent Interface** | MCP (stdio) | LangChain ReAct agent | +| **Visualization** | — | Sigma.js + Graphology (WebGL) | +| **Frontend** | — | React 18, TypeScript, Vite, Tailwind v4 | +| **Clustering** | Graphology + Leiden | Graphology + Leiden | +| **Concurrency** | Worker threads + async | Web Workers + Comlink | --- -### Tool 5: `overview` — Codebase Map - -Returns the full structural overview in one call: - -``` -CLUSTERS (12 total): -| Cluster | Symbols | Cohesion | Description | -| Authentication| 23 | 0.82 | Login, session, JWT handling | -| Database | 18 | 0.76 | Query builders, connection pool | -... - -PROCESSES (8 total): -| Process | Steps | Type | Clusters | -| LoginFlow | 7 | cross_community | 3 | -| PaymentProcessing | 5 | intra_community | 1 | -... - -CLUSTER DEPENDENCIES: -- Authentication -> Database (12 calls) -- API -> Authentication (8 calls) -``` - ---- - -### Tool 6: `explore` — Deep Dive - -Accepts a **symbol**, **cluster**, or **process** name and returns detailed info: - -**For a symbol:** -``` -SYMBOL: Function validateUser -File: src/auth/validate.ts -Cluster: Authentication — Login and session management - -PROCESSES: -- LoginFlow (step 2/7) -- SessionRefresh (step 1/4) - -CONNECTIONS: --[CALLS 90%]-> hashPassword --[CALLS 85%]-> checkRateLimit -<-[CALLS 90%]- handleLogin -<-[CALLS 85%]- refreshSession -``` - -**For a process:** -``` -PROCESS: LoginFlow -Type: cross_community -Steps: 7 - -TRACE: -1. handleLogin (API) -2. validateUser (Authentication) -3. checkRateLimit (RateLimiting) -4. hashPassword (Authentication) -5. createSession (Authentication) -6. storeSession (Database) -7. generateToken (Authentication) - -CLUSTERS TOUCHED: API, Authentication, RateLimiting, Database -``` - ---- - -### Tool 7: `impact` — Blast Radius Analysis - -Answers "what breaks if I change X?" or "what does X depend on?": - -``` -impact UserService upstream --maxDepth=3 --minConfidence=0.8 - -TARGET: Class UserService (src/services/user.ts) - -UPSTREAM (what depends on this): -Depth 1 (direct callers): - • handleLogin [CALLS 90%] → src/api/auth.ts:45 - • handleRegister [CALLS 90%] → src/api/auth.ts:78 - • UserController [CALLS 85%] → src/controllers/user.ts:12 - -Depth 2: - • authRouter [IMPORTS] → src/routes/auth.ts - • (3 more...) - -Summary: 8 production files affected, 3 clusters touched -``` - -**Key features:** -- `upstream` = what calls this (breakage risk) -- `downstream` = what this depends on -- `minConfidence` = filter out fuzzy matches (default 0.7) -- `includeTests` = false by default - ---- - -## 💡 Key Discovery: Unified Vector + Graph - -KuzuDB supports **native vector indexing (HNSW)**, so we do semantic + graph in **one Cypher query**: - -```cypher -CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', $queryVector, 20) -YIELD node AS emb, distance -WITH emb, distance WHERE distance < 0.4 -MATCH (n:Function {id: emb.nodeId})<-[:CodeRelation {type: 'CALLS'}]-(caller) -MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) -RETURN n.name, caller.name, c.label, distance -ORDER BY distance -``` - -**Why this matters:** -- 🎯 **Single query execution** — No round-trips between systems -- 📊 **Built-in relevance ranking** — Distance IS the score -- ⚡ **No separate vector DB** — One database, one query language - ---- - -## ⚡ Technical Improvements - -### Sigma.js + WebGL -- V1: D3.js, choked at ~3k nodes -- V2: Sigma.js + GPU rendering, smooth at 10k+ - -### Dual HashMap Symbol Table -- V1: Trie (prefix tree) - clever but slow -- V2: File-scoped + Global hashmaps - **~2x speedup** - -### LRU AST Cache -- Tree-sitter ASTs live in WASM memory -- LRU cache (50 slots) with `tree.delete()` for cleanup - -### ForceAtlas2 in Web Worker -- Layout algorithm runs off main thread -- UI stays responsive during graph positioning - ---- - -## 🚧 Roadmap +## Roadmap ### Actively Building -- [ ] **LLM Cluster Enrichment** - Semantic names via LLM API -- [ ] **AST Decorator Detection** - Parse @Controller, @Get, etc. -- [ ] **Multi-Repo Support** - Analyze multiple repos together -- [ ] **External Neo4j Connection** - Use hosted graph DB +- [ ] **LLM Cluster Enrichment** — Semantic cluster names via LLM API +- [ ] **AST Decorator Detection** — Parse @Controller, @Get, etc. +- [ ] **Incremental Indexing** — Only re-index changed files -### Recently Completed ✅ +### Recently Completed -- [x] **MCP Support** - `gitnexus-mcp` package for tool integration -- [x] **Community Detection** - Leiden algorithm for functional clustering -- [x] **Process Detection** - Entry point tracing with framework awareness -- [x] **9 Language Support** - Java, C, C++, C#, Go, Rust added -- [x] **Confidence Scoring** - Trust levels on CALLS edges -- [x] **7 Smart Tools** - overview, explore, impact added -- [x] **Ollama Support** - Local LLM integration -- [x] **Blast Radius Tool** - `impact` for dependency analysis -- [x] Graph RAG Agent with streaming -- [x] Browser embeddings (snowflake-arctic-embed-xs, 22M params) -- [x] Vector index with HNSW in KuzuDB -- [x] Hybrid search (BM25 + semantic + RRF) -- [x] Grounded citations (`[[file:line]]` format) -- [x] Multiple LLM providers (OpenAI, Azure, Gemini, Anthropic, Ollama) +- [X] **Wiki Generation** — LLM-powered docs from knowledge graph (`gitnexus wiki`) +- [X] **Multi-File Rename** — Graph-aware rename with confidence tags (`rename` tool) +- [X] **Git-Diff Impact** — Pre-commit change analysis (`detect_changes` tool) +- [X] **Process-Grouped Search** — Query results grouped by execution flow (`query` tool) +- [X] **360-Degree Context** — Categorized refs + process participation (`context` tool) +- [X] **Claude Code Hooks** — Auto-augment grep/glob with graph context +- [X] **MCP Prompts** — Guided workflows for impact detection and architecture docs +- [X] **Multi-Repo MCP** — Global registry + lazy connection pool, one MCP server for all repos +- [X] **Zero-Config Setup** — `gitnexus setup` auto-configures Cursor, Claude Code, OpenCode +- [X] **Unified CLI + MCP** — `npm install -g gitnexus` for indexing and MCP server +- [X] **Language-Aware Imports** — TS path aliases, Rust modules, Java wildcards, Go packages +- [X] **Community Detection** — Leiden algorithm for functional clustering +- [X] **Process Detection** — Entry point tracing with framework awareness +- [X] **9 Language Support** — TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust +- [X] **Confidence Scoring** — Trust levels on CALLS edges (0.3-0.9) +- [X] **Blast Radius Tool** — `impact` with minConfidence, relationTypes, includeTests +- [X] **Hybrid Search** — BM25 + semantic + Reciprocal Rank Fusion +- [X] **Vector Index** — HNSW in KuzuDB for semantic search --- -## 🛠 Tech Stack +## Security & Privacy -| Layer | Technology | -|-------|------------| -| **Frontend** | React 18, TypeScript, Vite, Tailwind v4 | -| **Visualization** | Sigma.js, Graphology, ForceAtlas2 (WebGL) | -| **Parsing** | Tree-sitter WASM (9 languages) | -| **Database** | KuzuDB WASM (graph + vector HNSW) | -| **Clustering** | Graphology + Leiden (Louvain) | -| **Embeddings** | transformers.js, snowflake-arctic-embed-xs (22M) | -| **AI** | LangChain ReAct agent, streaming | -| **Concurrency** | Web Workers + Comlink | +- **CLI**: Everything runs locally on your machine. No network calls. Index stored in `.gitnexus/` (gitignored). Global registry at `~/.gitnexus/` stores only paths and metadata. +- **Web**: Everything runs in your browser. No code uploaded to any server. API keys stored in localStorage only. +- Open source — audit the code yourself. --- -## 🔐 Security & Privacy +## Acknowledgments -- All processing happens in your browser -- No code uploaded to any server -- API keys stored in localStorage only -- Open source—audit the code yourself - ---- - -## 📝 License - -MIT License - ---- - -## 🙏 Acknowledgments - -- [Tree-sitter](https://tree-sitter.github.io/) - AST parsing -- [KuzuDB](https://kuzudb.com/) - Embedded graph database with vector support -- [Sigma.js](https://www.sigmajs.org/) - WebGL graph rendering -- [transformers.js](https://huggingface.co/docs/transformers.js) - Browser ML -- [LangChain](https://langchain.com/) - Agent orchestration -- [Graphology](https://graphology.github.io/) - Graph data structures + Leiden +- [Tree-sitter](https://tree-sitter.github.io/) — AST parsing +- [KuzuDB](https://kuzudb.com/) — Embedded graph database with vector support +- [Sigma.js](https://www.sigmajs.org/) — WebGL graph rendering +- [transformers.js](https://huggingface.co/docs/transformers.js) — Browser ML +- [Graphology](https://graphology.github.io/) — Graph data structures + Leiden +- [MCP](https://modelcontextprotocol.io/) — Model Context Protocol diff --git a/eval/.env.example b/eval/.env.example new file mode 100644 index 000000000..7942d8f02 --- /dev/null +++ b/eval/.env.example @@ -0,0 +1,23 @@ +# ─── GitNexus SWE-bench Eval — API Keys ─── +# Copy this file to .env and fill in the keys you have. +# You only need keys for the models you plan to test. + +# OpenRouter (covers Claude, MiniMax, GLM, and 200+ other models) +# Get yours at: https://openrouter.ai/keys +OPENROUTER_API_KEY= + +# Anthropic (direct — optional if using OpenRouter) +# Get yours at: https://console.anthropic.com/ +ANTHROPIC_API_KEY= + +# ZhipuAI / GLM (direct — optional if using OpenRouter) +# Get yours at: https://open.bigmodel.cn/ +ZHIPUAI_API_KEY= + +# MiniMax (direct — optional if using OpenRouter) +MINIMAX_API_KEY= + +# ─── Optional ─── + +# Cost tracking: set to "ignore_errors" if litellm can't find pricing for a model +# MSWEA_COST_TRACKING=ignore_errors diff --git a/eval/.gitignore b/eval/.gitignore new file mode 100644 index 000000000..d1ac9f241 --- /dev/null +++ b/eval/.gitignore @@ -0,0 +1,16 @@ +# Evaluation results (large, should not be committed) +results/ +*.traj.json +preds.json + +# Python +__pycache__/ +*.pyc +*.egg-info/ +.eggs/ +dist/ +build/ + +# Environment +.env +.venv/ diff --git a/eval/README.md b/eval/README.md new file mode 100644 index 000000000..d268e67b0 --- /dev/null +++ b/eval/README.md @@ -0,0 +1,210 @@ +# GitNexus SWE-bench Evaluation Harness + +Evaluate whether GitNexus code intelligence improves AI agent performance on real software engineering tasks. Runs SWE-bench instances across multiple models and compares baseline (no graph) vs GitNexus-enhanced configurations. + +## What This Tests + +**Hypothesis**: Giving AI agents structural code intelligence (call graphs, execution flows, blast radius analysis) improves their ability to resolve real GitHub issues — measured by resolve rate, cost, and efficiency. + +**Evaluation modes:** + +| Mode | What the agent gets | +|------|-------------------| +| `baseline` | Standard bash tools (grep, find, cat, sed) — control group | +| `native` | Baseline + explicit GitNexus tools via eval-server (~100ms) | +| `native_augment` | Native tools + grep results automatically enriched with graph context (**recommended**) | + +> **Recommended**: Use `native_augment` mode. It mirrors the Claude Code model — the agent gets both explicit GitNexus tools (fast bash commands) AND automatic enrichment of grep results with callers, callees, and execution flows. The agent decides when to use explicit tools vs rely on enriched search output. + +**Models supported:** + +- Claude 3.5 Haiku, Claude Sonnet 4, Claude Opus 4 +- MiniMax M1 2.5 +- GLM 4.7, GLM 5 +- Any model supported by litellm (add a YAML config) + +## Prerequisites + +- Python 3.11+ +- Docker (for SWE-bench containers) +- Node.js 18+ (for GitNexus) +- API keys for your chosen models + +## Setup + +```bash +cd eval + +# Install dependencies +pip install -e . + +# Set up API keys — copy the template and fill in your keys +cp .env.example .env +# Then edit .env and paste your key(s) +``` + +All models are routed through **OpenRouter** by default, so a single `OPENROUTER_API_KEY` is all you need. To use provider APIs directly (Anthropic, ZhipuAI, etc.), edit the model YAML in `configs/models/` and set the corresponding key in `.env`. + +```bash +# Pull SWE-bench Docker images (pulled on-demand, but you can pre-pull) +docker pull swebench/sweb.eval.x86_64.django_1776_django-16527:latest +``` + +## Quick Start + +### Debug a single instance + +```bash +# Fastest way to verify everything works +python run_eval.py debug -m claude-haiku -i django__django-16527 --subset lite +``` + +### Run a single configuration + +```bash +# 5 instances, Claude Sonnet, native_augment mode (default) +python run_eval.py single -m claude-sonnet --subset lite --slice 0:5 + +# Baseline comparison (no GitNexus) +python run_eval.py single -m claude-sonnet --mode baseline --subset lite --slice 0:5 + +# Full Lite benchmark, 4 parallel workers +python run_eval.py single -m claude-sonnet --subset lite -w 4 +``` + +### Run the full matrix + +```bash +# All models x all modes +python run_eval.py matrix --subset lite -w 4 + +# Key comparison: baseline vs native_augment +python run_eval.py matrix -m claude-sonnet -m claude-haiku --modes baseline --modes native_augment --subset lite --slice 0:50 +``` + +### Analyze results + +```bash +# Summary table +python -m analysis.analyze_results results/ + +# Compare modes for a specific model +python -m analysis.analyze_results compare-modes results/ -m claude-sonnet + +# GitNexus tool usage analysis +python -m analysis.analyze_results gitnexus-usage results/ + +# Export as CSV for further analysis +python -m analysis.analyze_results summary results/ --format csv > results.csv + +# Run official SWE-bench test evaluation +python -m analysis.analyze_results summary results/ --swebench-eval +``` + +### List available configurations + +```bash +python run_eval.py list-configs +``` + +## Architecture + +``` +eval/ + run_eval.py # Main entry point (single, matrix, debug commands) + agents/ + gitnexus_agent.py # GitNexusAgent: extends DefaultAgent with augmentation + metrics + environments/ + gitnexus_docker.py # Docker env with GitNexus + eval-server + standalone tool scripts + bridge/ + gitnexus_tools.sh # Bash wrappers (legacy — now standalone scripts are installed directly) + mcp_bridge.py # Legacy MCP bridge (kept for reference) + prompts/ + system_baseline.jinja # System: persona + format rules + instance_baseline.jinja # Instance: task + workflow + system_native.jinja # System: + GitNexus tool reference + instance_native.jinja # Instance: + GitNexus debugging workflow + system_native_augment.jinja # System: + GitNexus tools + grep enrichment docs + instance_native_augment.jinja # Instance: + GitNexus workflow + risk assessment + configs/ + models/ # Per-model YAML configs + modes/ # Per-mode YAML configs (baseline, native, native_augment) + analysis/ + analyze_results.py # Post-run comparative analysis + results/ # Output directory (gitignored) +``` + +## How It Works + +### Template structure + +mini-swe-agent requires two Jinja templates: +- **system_template** → system message: persona, format rules, tool reference (static) +- **instance_template** → first user message: task, workflow, rules, examples (contains `{{task}}`) + +Each mode has a `system_{mode}.jinja` + `instance_{mode}.jinja` pair. The agent loads both automatically based on the configured mode. + +### Per-instance flow + +1. Docker container starts with SWE-bench instance (repo at specific commit) +2. **GitNexus setup**: Node.js + gitnexus installed, `gitnexus analyze` runs (or restores from cache) +3. **Eval-server starts**: `gitnexus eval-server` daemon (persistent HTTP server, keeps KuzuDB warm) +4. **Standalone tool scripts installed** in `/usr/local/bin/` — works with `subprocess.run` (no `.bashrc` needed) +5. Agent runs with the configured model + system prompt + GitNexus tools +6. Agent's patch is extracted as a git diff +7. Metrics collected: cost, tokens, tool calls, GitNexus usage, augmentation stats + +### Tool architecture + +``` +Agent → bash command → /usr/local/bin/gitnexus-query + → curl localhost:4848/tool/query (fast path: eval-server, ~100ms) + → npx gitnexus query (fallback: cold CLI, ~5-10s) +``` + +Each tool script in `/usr/local/bin/` is standalone — no sourcing, no env inheritance needed. This is critical because mini-swe-agent runs every command via `subprocess.run` in a fresh subshell. + +### Eval-server + +The eval-server is a lightweight HTTP daemon that: +- Keeps KuzuDB warm in memory (no cold start per tool call) +- Returns LLM-friendly text (not raw JSON — saves tokens) +- Includes next-step hints to guide tool chaining (query → context → impact → fix) +- Auto-shuts down after idle timeout + +### Index caching + +SWE-bench repos repeat (Django has 200+ instances at different commits). The harness caches GitNexus indexes per `(repo, commit)` hash in `~/.gitnexus-eval-cache/` to avoid redundant re-indexing. + +### Grep augmentation (native_augment mode) + +When the agent runs `grep` or `rg`, the observation is post-processed: the agent class calls `gitnexus-augment` on the search pattern and appends `[GitNexus]` annotations showing callers, callees, and execution flows for matched symbols. This mirrors the Claude Code / Cursor hook integration. + +## Adding Models + +Create a YAML file in `configs/models/`: + +```yaml +# configs/models/my-model.yaml +model: + model_name: "openrouter/provider/model-name" + cost_tracking: "ignore_errors" # if not in litellm's cost DB + model_kwargs: + max_tokens: 8192 + temperature: 0 +``` + +The model name follows [litellm conventions](https://docs.litellm.ai/docs/providers). + +## Metrics Collected + +| Metric | Description | +|--------|-------------| +| Patch Rate | % of instances where agent produced a patch | +| Resolve Rate | % of instances where patch passes tests (requires --swebench-eval) | +| Total Cost | API cost across all instances | +| Avg Cost/Instance | Cost efficiency | +| API Calls | Number of LLM calls | +| GN Tool Calls | How many GitNexus tools the agent used | +| Augment Hits | How many grep/find results got enriched | +| Augment Hit Rate | % of search commands that got useful enrichment | diff --git a/eval/__init__.py b/eval/__init__.py new file mode 100644 index 000000000..fb4a4032a --- /dev/null +++ b/eval/__init__.py @@ -0,0 +1 @@ +# GitNexus SWE-bench Evaluation Harness diff --git a/eval/agents/__init__.py b/eval/agents/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eval/agents/gitnexus_agent.py b/eval/agents/gitnexus_agent.py new file mode 100644 index 000000000..a419ba1e6 --- /dev/null +++ b/eval/agents/gitnexus_agent.py @@ -0,0 +1,209 @@ +""" +GitNexus-Enhanced Agent for SWE-bench Evaluation + +Extends mini-swe-agent's DefaultAgent with: +1. Native augment mode: GitNexus tools via eval-server + grep enrichment (recommended) +2. Native mode: GitNexus tools via eval-server only +3. Baseline mode: Pure mini-swe-agent (no GitNexus — control group) + +The agent class itself is minimal — the heavy lifting is in: +- Prompt selection (system + instance templates per mode) +- Observation post-processing (grep result augmentation) +- Metrics tracking (which tools the agent actually uses) + +Template structure (matches mini-swe-agent's expectations): + system_template → system message: persona + format rules + tool reference + instance_template → first user message: task + workflow + rules + examples +""" + +import logging +import re +import time +from enum import Enum +from pathlib import Path + +from minisweagent import Environment, Model +from minisweagent.agents.default import AgentConfig, DefaultAgent + +logger = logging.getLogger("gitnexus_agent") + +PROMPTS_DIR = Path(__file__).parent.parent / "prompts" + + +class GitNexusMode(str, Enum): + """Evaluation modes for GitNexus integration.""" + BASELINE = "baseline" # No GitNexus — pure mini-swe-agent + NATIVE = "native" # GitNexus tools via eval-server + NATIVE_AUGMENT = "native_augment" # Native tools + grep enrichment (recommended) + + +class GitNexusAgentConfig(AgentConfig): + """Extended config for GitNexus evaluation agent.""" + gitnexus_mode: GitNexusMode = GitNexusMode.BASELINE + augment_timeout: float = 5.0 + augment_min_pattern_length: int = 3 + track_gitnexus_usage: bool = True + + +class GitNexusAgent(DefaultAgent): + """ + Agent that optionally enriches its capabilities with GitNexus code intelligence. + + In BASELINE mode, behaves identically to DefaultAgent. + In NATIVE mode, GitNexus tools are available as bash commands via eval-server. + In NATIVE_AUGMENT mode, GitNexus tools + automatic grep result enrichment. + """ + + def __init__(self, model: Model, env: Environment, *, config_class: type = GitNexusAgentConfig, **kwargs): + mode = kwargs.get("gitnexus_mode", GitNexusMode.BASELINE) + if isinstance(mode, str): + mode = GitNexusMode(mode) + + # Load system template + system_file = PROMPTS_DIR / f"system_{mode.value}.jinja" + if system_file.exists() and "system_template" not in kwargs: + kwargs["system_template"] = system_file.read_text() + + # Load instance template + instance_file = PROMPTS_DIR / f"instance_{mode.value}.jinja" + if instance_file.exists() and "instance_template" not in kwargs: + kwargs["instance_template"] = instance_file.read_text() + + super().__init__(model, env, config_class=config_class, **kwargs) + self.gitnexus_mode = mode + self.gitnexus_metrics = GitNexusMetrics() + + def execute_actions(self, message: dict) -> list[dict]: + """Execute actions with optional GitNexus augmentation and tracking.""" + if self.config.track_gitnexus_usage: + self._track_tool_usage(message) + + outputs = [self.env.execute(action) for action in message.get("extra", {}).get("actions", [])] + + # Augment grep/find observations in NATIVE_AUGMENT mode + if self.gitnexus_mode == GitNexusMode.NATIVE_AUGMENT: + actions = message.get("extra", {}).get("actions", []) + for i, (action, output) in enumerate(zip(actions, outputs)): + augmented = self._maybe_augment(action, output) + if augmented: + outputs[i] = augmented + + return self.add_messages( + *self.model.format_observation_messages(message, outputs, self.get_template_vars()) + ) + + def _maybe_augment(self, action: dict, output: dict) -> dict | None: + """ + If the action is a search command (grep, find, rg, ag), augment the output + with GitNexus knowledge graph context. + """ + command = action.get("command", "") + if not command: + return None + + pattern = self._extract_search_pattern(command) + if not pattern or len(pattern) < self.config.augment_min_pattern_length: + return None + + start = time.time() + try: + augment_result = self.env.execute({ + "command": f'gitnexus-augment "{pattern}" 2>&1 || true', + "timeout": self.config.augment_timeout, + }) + elapsed = time.time() - start + self.gitnexus_metrics.augmentation_calls += 1 + self.gitnexus_metrics.augmentation_time += elapsed + + augment_text = augment_result.get("output", "").strip() + if augment_text and "[GitNexus]" in augment_text: + original_output = output.get("output", "") + output = dict(output) + output["output"] = f"{original_output}\n\n{augment_text}" + self.gitnexus_metrics.augmentation_hits += 1 + return output + except Exception as e: + logger.debug(f"Augmentation failed for pattern '{pattern}': {e}") + self.gitnexus_metrics.augmentation_errors += 1 + + return None + + @staticmethod + def _extract_search_pattern(command: str) -> str | None: + """Extract the search pattern from a grep/find/rg command.""" + patterns = [ + r'(?:grep|rg|ag)\s+(?:-[a-zA-Z]*\s+)*["\']([^"\']+)["\']', + r'(?:grep|rg|ag)\s+(?:-[a-zA-Z]*\s+)*(\S+)', + ] + + for pat in patterns: + match = re.search(pat, command) + if match: + result = match.group(1) + if result.startswith("/") or result.startswith("."): + continue + if result.startswith("-"): + continue + return result + + return None + + def _track_tool_usage(self, message: dict): + """Track which GitNexus tools the agent uses.""" + for action in message.get("extra", {}).get("actions", []): + command = action.get("command", "") + if "gitnexus-query" in command: + self.gitnexus_metrics.tool_calls["query"] += 1 + elif "gitnexus-context" in command: + self.gitnexus_metrics.tool_calls["context"] += 1 + elif "gitnexus-impact" in command: + self.gitnexus_metrics.tool_calls["impact"] += 1 + elif "gitnexus-cypher" in command: + self.gitnexus_metrics.tool_calls["cypher"] += 1 + elif "gitnexus-overview" in command: + self.gitnexus_metrics.tool_calls["overview"] += 1 + + def serialize(self, *extra_dicts) -> dict: + """Serialize with GitNexus-specific metrics.""" + gitnexus_data = { + "info": { + "gitnexus": { + "mode": self.gitnexus_mode.value, + "metrics": self.gitnexus_metrics.to_dict(), + }, + }, + } + return super().serialize(gitnexus_data, *extra_dicts) + + +class GitNexusMetrics: + """Tracks GitNexus-specific metrics during evaluation.""" + + def __init__(self): + self.tool_calls: dict[str, int] = { + "query": 0, + "context": 0, + "impact": 0, + "cypher": 0, + "overview": 0, + } + self.augmentation_calls: int = 0 + self.augmentation_hits: int = 0 + self.augmentation_errors: int = 0 + self.augmentation_time: float = 0.0 + self.index_time: float = 0.0 + + @property + def total_tool_calls(self) -> int: + return sum(self.tool_calls.values()) + + def to_dict(self) -> dict: + return { + "tool_calls": dict(self.tool_calls), + "total_tool_calls": self.total_tool_calls, + "augmentation_calls": self.augmentation_calls, + "augmentation_hits": self.augmentation_hits, + "augmentation_errors": self.augmentation_errors, + "augmentation_time_seconds": round(self.augmentation_time, 2), + "index_time_seconds": round(self.index_time, 2), + } diff --git a/eval/analysis/__init__.py b/eval/analysis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eval/analysis/analyze_results.py b/eval/analysis/analyze_results.py new file mode 100644 index 000000000..69c5f5cdb --- /dev/null +++ b/eval/analysis/analyze_results.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python3 +""" +Results Analyzer for GitNexus SWE-bench Evaluation + +Reads evaluation results and generates comparative analysis: +- Resolve rate by model x mode +- Cost comparison (total, per-instance) +- Token/API call efficiency +- GitNexus tool usage patterns +- Augmentation hit rates + +Usage: + python -m analysis.analyze_results /path/to/results + python -m analysis.analyze_results /path/to/results --format markdown + python -m analysis.analyze_results /path/to/results --swebench-eval # run actual test verification +""" + +import json +import logging +import os +import subprocess +from pathlib import Path +from typing import Any + +import typer +from rich.console import Console +from rich.table import Table + +logger = logging.getLogger("analyze_results") +console = Console() +app = typer.Typer(rich_markup_mode="rich", add_completion=False) + + +def load_run_results(results_dir: Path) -> dict[str, dict]: + """ + Load all run results from the results directory. + + Returns: {run_id: {summary, preds, instances}} + """ + runs = {} + + for run_dir in sorted(results_dir.iterdir()): + if not run_dir.is_dir(): + continue + + run_id = run_dir.name + run_data: dict[str, Any] = {"run_id": run_id, "dir": run_dir} + + # Load summary + summary_path = run_dir / "summary.json" + if summary_path.exists(): + run_data["summary"] = json.loads(summary_path.read_text()) + + # Load predictions + preds_path = run_dir / "preds.json" + if preds_path.exists(): + run_data["preds"] = json.loads(preds_path.read_text()) + + # Load individual trajectories for detailed metrics + run_data["trajectories"] = {} + for traj_dir in run_dir.iterdir(): + if not traj_dir.is_dir(): + continue + for traj_file in traj_dir.glob("*.traj.json"): + try: + traj = json.loads(traj_file.read_text()) + instance_id = traj.get("instance_id", traj_dir.name) + run_data["trajectories"][instance_id] = traj + except Exception: + pass + + if run_data.get("preds") or run_data.get("summary"): + runs[run_id] = run_data + + return runs + + +def parse_run_id(run_id: str) -> tuple[str, str]: + """Parse 'model_mode' into (model, mode).""" + # Handle multi-word model names like 'minimax-2.5' + # Modes are: baseline, mcp, augment, full + known_modes = {"baseline", "mcp", "augment", "full"} + parts = run_id.rsplit("_", 1) + if len(parts) == 2 and parts[1] in known_modes: + return parts[0], parts[1] + return run_id, "unknown" + + +def compute_metrics(run_data: dict) -> dict: + """Compute evaluation metrics for a single run.""" + preds = run_data.get("preds", {}) + summary = run_data.get("summary", {}) + trajectories = run_data.get("trajectories", {}) + + n_instances = len(preds) + n_with_patch = sum(1 for p in preds.values() if p.get("model_patch", "").strip()) + + # Cost and API call metrics from trajectories + costs = [] + api_calls = [] + gn_tool_calls = [] + gn_augment_hits = [] + gn_augment_calls = [] + + for instance_id, traj in trajectories.items(): + info = traj.get("info", {}) + model_stats = info.get("model_stats", {}) + costs.append(model_stats.get("instance_cost", 0)) + api_calls.append(model_stats.get("api_calls", 0)) + + gn = info.get("gitnexus", {}).get("metrics", {}) + if gn: + gn_tool_calls.append(gn.get("total_tool_calls", 0)) + gn_augment_hits.append(gn.get("augmentation_hits", 0)) + gn_augment_calls.append(gn.get("augmentation_calls", 0)) + + # Also try summary-level metrics + if not costs and summary: + results = summary.get("results", []) + for r in results: + costs.append(r.get("cost", 0)) + api_calls.append(r.get("n_calls", 0)) + gn = r.get("gitnexus_metrics", {}) + if gn: + gn_tool_calls.append(gn.get("total_tool_calls", 0)) + gn_augment_hits.append(gn.get("augmentation_hits", 0)) + gn_augment_calls.append(gn.get("augmentation_calls", 0)) + + total_cost = sum(costs) + total_calls = sum(api_calls) + + return { + "n_instances": n_instances, + "n_with_patch": n_with_patch, + "patch_rate": n_with_patch / max(n_instances, 1), + "total_cost": total_cost, + "avg_cost": total_cost / max(n_instances, 1), + "total_api_calls": total_calls, + "avg_api_calls": total_calls / max(n_instances, 1), + "total_gn_tool_calls": sum(gn_tool_calls), + "avg_gn_tool_calls": sum(gn_tool_calls) / max(len(gn_tool_calls), 1) if gn_tool_calls else 0, + "total_augment_hits": sum(gn_augment_hits), + "total_augment_calls": sum(gn_augment_calls), + "augment_hit_rate": sum(gn_augment_hits) / max(sum(gn_augment_calls), 1) if gn_augment_calls else 0, + } + + +def run_swebench_evaluation(results_dir: Path, run_id: str, subset: str = "lite") -> dict | None: + """ + Run the official SWE-bench evaluation on predictions. + + Requires: pip install swebench + """ + preds_path = results_dir / run_id / "preds.json" + if not preds_path.exists(): + return None + + dataset_mapping = { + "lite": "princeton-nlp/SWE-Bench_Lite", + "verified": "princeton-nlp/SWE-Bench_Verified", + "full": "princeton-nlp/SWE-Bench", + } + + try: + eval_output = results_dir / run_id / "swebench_eval" + cmd = [ + "python", "-m", "swebench.harness.run_evaluation", + "--dataset_name", dataset_mapping.get(subset, subset), + "--predictions_path", str(preds_path), + "--max_workers", "4", + "--run_id", run_id, + "--output_dir", str(eval_output), + ] + + logger.info(f"Running SWE-bench evaluation for {run_id}...") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=600) + + if result.returncode == 0: + # Parse evaluation results + report_path = eval_output / run_id / "results.json" + if report_path.exists(): + return json.loads(report_path.read_text()) + + logger.error(f"SWE-bench eval failed: {result.stderr[:500]}") + return None + + except Exception as e: + logger.error(f"SWE-bench eval error: {e}") + return None + + +# ─── CLI Commands ─────────────────────────────────────────────────────────── + + +@app.command() +def summary( + results_dir: str = typer.Argument(..., help="Path to results directory"), + format: str = typer.Option("table", "--format", help="Output format: table, markdown, json, csv"), + swebench_eval: bool = typer.Option(False, "--swebench-eval", help="Run official SWE-bench test evaluation"), + subset: str = typer.Option("lite", "--subset", help="SWE-bench subset (for --swebench-eval)"), +): + """Generate comparative analysis of evaluation results.""" + results_path = Path(results_dir) + if not results_path.exists(): + console.print(f"[red]Results directory not found: {results_path}[/red]") + raise typer.Exit(1) + + runs = load_run_results(results_path) + if not runs: + console.print("[yellow]No evaluation results found[/yellow]") + raise typer.Exit(0) + + console.print(f"\n[bold]Found {len(runs)} evaluation runs[/bold]\n") + + # Compute metrics per run + all_metrics = {} + for run_id, run_data in runs.items(): + model, mode = parse_run_id(run_id) + metrics = compute_metrics(run_data) + metrics["model"] = model + metrics["mode"] = mode + + # Optionally run SWE-bench evaluation + if swebench_eval: + eval_result = run_swebench_evaluation(results_path, run_id, subset) + if eval_result: + metrics["resolved"] = eval_result.get("resolved", 0) + metrics["resolve_rate"] = eval_result.get("resolved", 0) / max(metrics["n_instances"], 1) + + all_metrics[run_id] = metrics + + if format == "table": + _print_table(all_metrics) + elif format == "markdown": + _print_markdown(all_metrics) + elif format == "json": + console.print(json.dumps(all_metrics, indent=2)) + elif format == "csv": + _print_csv(all_metrics) + + +@app.command() +def compare_modes( + results_dir: str = typer.Argument(..., help="Path to results directory"), + model: str = typer.Option(..., "-m", "--model", help="Model to compare across modes"), +): + """Compare modes for a specific model (baseline vs mcp vs augment vs full).""" + results_path = Path(results_dir) + runs = load_run_results(results_path) + + # Filter to the specified model + model_runs = { + run_id: data for run_id, data in runs.items() + if parse_run_id(run_id)[0] == model + } + + if not model_runs: + console.print(f"[yellow]No results found for model: {model}[/yellow]") + raise typer.Exit(1) + + console.print(f"\n[bold]Mode comparison for {model}[/bold]\n") + + metrics = {} + for run_id, run_data in model_runs.items(): + _, mode = parse_run_id(run_id) + metrics[mode] = compute_metrics(run_data) + + # Print comparison table + table = Table(title=f"Mode Comparison: {model}") + table.add_column("Metric", style="bold") + for mode in ["baseline", "mcp", "augment", "full"]: + if mode in metrics: + table.add_column(mode, justify="right") + + rows = [ + ("Instances", "n_instances", "d"), + ("With Patch", "n_with_patch", "d"), + ("Patch Rate", "patch_rate", ".1%"), + ("Total Cost", "total_cost", "$.4f"), + ("Avg Cost", "avg_cost", "$.4f"), + ("Total API Calls", "total_api_calls", "d"), + ("Avg API Calls", "avg_api_calls", ".1f"), + ("GN Tool Calls", "total_gn_tool_calls", "d"), + ("Augment Hits", "total_augment_hits", "d"), + ("Augment Hit Rate", "augment_hit_rate", ".1%"), + ] + + for label, key, fmt in rows: + values = [] + for mode in ["baseline", "mcp", "augment", "full"]: + if mode in metrics: + v = metrics[mode].get(key, 0) + if fmt == ".1%": + values.append(f"{v:.1%}") + elif fmt == "$.4f": + values.append(f"${v:.4f}") + elif fmt == ".1f": + values.append(f"{v:.1f}") + else: + values.append(str(v)) + table.add_row(label, *values) + + # Add delta rows (improvement over baseline) + if "baseline" in metrics: + baseline_cost = metrics["baseline"]["avg_cost"] + baseline_calls = metrics["baseline"]["avg_api_calls"] + + table.add_section() + for mode in ["mcp", "augment", "full"]: + if mode not in metrics: + continue + mode_cost = metrics[mode]["avg_cost"] + mode_calls = metrics[mode]["avg_api_calls"] + + cost_delta = ((mode_cost - baseline_cost) / max(baseline_cost, 0.001)) * 100 + calls_delta = ((mode_calls - baseline_calls) / max(baseline_calls, 1)) * 100 + + cost_str = f"{cost_delta:+.1f}%" + calls_str = f"{calls_delta:+.1f}%" + + # Color-code: negative is good (cheaper/fewer calls) + cost_color = "green" if cost_delta < 0 else "red" + calls_color = "green" if calls_delta < 0 else "red" + + console.print(f" {mode} vs baseline: cost [{cost_color}]{cost_str}[/{cost_color}], calls [{calls_color}]{calls_str}[/{calls_color}]") + + console.print(table) + + +@app.command() +def gitnexus_usage( + results_dir: str = typer.Argument(..., help="Path to results directory"), +): + """Analyze GitNexus tool usage patterns across all runs.""" + results_path = Path(results_dir) + runs = load_run_results(results_path) + + console.print("\n[bold]GitNexus Tool Usage Analysis[/bold]\n") + + table = Table(title="Tool Usage by Run") + table.add_column("Run", style="bold") + table.add_column("query", justify="right") + table.add_column("context", justify="right") + table.add_column("impact", justify="right") + table.add_column("cypher", justify="right") + table.add_column("Total", justify="right") + table.add_column("Augment Hits", justify="right") + + for run_id, run_data in sorted(runs.items()): + _, mode = parse_run_id(run_id) + if mode == "baseline": + continue + + # Aggregate tool calls across trajectories + tool_totals: dict[str, int] = {"query": 0, "context": 0, "impact": 0, "cypher": 0, "overview": 0} + augment_hits = 0 + + for traj in run_data.get("trajectories", {}).values(): + gn = traj.get("info", {}).get("gitnexus", {}).get("metrics", {}) + for tool, count in gn.get("tool_calls", {}).items(): + tool_totals[tool] = tool_totals.get(tool, 0) + count + augment_hits += gn.get("augmentation_hits", 0) + + # Also check summary + for r in run_data.get("summary", {}).get("results", []): + gn = r.get("gitnexus_metrics", {}) + for tool, count in gn.get("tool_calls", {}).items(): + tool_totals[tool] = tool_totals.get(tool, 0) + count + augment_hits += gn.get("augmentation_hits", 0) + + total = sum(tool_totals.values()) + if total > 0 or augment_hits > 0: + table.add_row( + run_id, + str(tool_totals.get("query", 0)), + str(tool_totals.get("context", 0)), + str(tool_totals.get("impact", 0)), + str(tool_totals.get("cypher", 0)), + str(total), + str(augment_hits), + ) + + console.print(table) + + +# ─── Output Formatters ───────────────────────────────────────────────────── + + +def _print_table(all_metrics: dict): + """Print rich table summary.""" + table = Table(title="Evaluation Results") + table.add_column("Run", style="bold") + table.add_column("Model") + table.add_column("Mode") + table.add_column("N", justify="right") + table.add_column("Patched", justify="right") + table.add_column("Rate", justify="right") + table.add_column("Cost", justify="right") + table.add_column("Calls", justify="right") + table.add_column("GN Tools", justify="right") + + for run_id, m in sorted(all_metrics.items()): + resolved_str = "" + if "resolve_rate" in m: + resolved_str = f" ({m['resolve_rate']:.0%})" + + table.add_row( + run_id, + m["model"], + m["mode"], + str(m["n_instances"]), + str(m["n_with_patch"]), + f"{m['patch_rate']:.0%}{resolved_str}", + f"${m['total_cost']:.2f}", + str(m["total_api_calls"]), + str(m["total_gn_tool_calls"]) if m["total_gn_tool_calls"] > 0 else "-", + ) + + console.print(table) + + +def _print_markdown(all_metrics: dict): + """Print markdown table.""" + print("| Run | Model | Mode | N | Patched | Rate | Cost | Calls | GN Tools |") + print("|-----|-------|------|---|---------|------|------|-------|----------|") + for run_id, m in sorted(all_metrics.items()): + gn = str(m["total_gn_tool_calls"]) if m["total_gn_tool_calls"] > 0 else "-" + print(f"| {run_id} | {m['model']} | {m['mode']} | {m['n_instances']} | {m['n_with_patch']} | {m['patch_rate']:.0%} | ${m['total_cost']:.2f} | {m['total_api_calls']} | {gn} |") + + +def _print_csv(all_metrics: dict): + """Print CSV output.""" + print("run_id,model,mode,n_instances,n_with_patch,patch_rate,total_cost,avg_cost,total_api_calls,avg_api_calls,total_gn_tool_calls,total_augment_hits,augment_hit_rate") + for run_id, m in sorted(all_metrics.items()): + print( + f"{run_id},{m['model']},{m['mode']},{m['n_instances']},{m['n_with_patch']}," + f"{m['patch_rate']:.4f},{m['total_cost']:.4f},{m['avg_cost']:.4f}," + f"{m['total_api_calls']},{m['avg_api_calls']:.1f},{m['total_gn_tool_calls']}," + f"{m['total_augment_hits']},{m['augment_hit_rate']:.4f}" + ) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + app() diff --git a/eval/bridge/__init__.py b/eval/bridge/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eval/bridge/gitnexus_tools.sh b/eval/bridge/gitnexus_tools.sh new file mode 100644 index 000000000..be926d81f --- /dev/null +++ b/eval/bridge/gitnexus_tools.sh @@ -0,0 +1,155 @@ +#!/bin/bash +# GitNexus CLI tool wrappers for SWE-bench evaluation +# +# These functions call the GitNexus eval-server (HTTP daemon) for near-instant +# tool responses. The eval-server keeps KuzuDB warm in memory. +# +# If the eval-server is not running, falls back to direct CLI commands. +# +# Usage: +# gitnexus-query "how does authentication work" +# gitnexus-context "validateUser" +# gitnexus-impact "AuthService" upstream +# gitnexus-cypher "MATCH (n:Function) RETURN n.name LIMIT 10" +# gitnexus-overview + +GITNEXUS_EVAL_PORT="${GITNEXUS_EVAL_PORT:-4848}" +GITNEXUS_EVAL_URL="http://127.0.0.1:${GITNEXUS_EVAL_PORT}" + +_gitnexus_call() { + local tool="$1" + shift + local json_body="$1" + + # Try eval-server first (fastest path — KuzuDB stays warm) + local result + result=$(curl -sf -X POST "${GITNEXUS_EVAL_URL}/tool/${tool}" \ + -H "Content-Type: application/json" \ + -d "${json_body}" 2>/dev/null) + + if [ $? -eq 0 ] && [ -n "$result" ]; then + echo "$result" + return 0 + fi + + # Fallback: direct CLI (cold start, slower but always works) + case "$tool" in + query) + local q=$(echo "$json_body" | python3 -c "import sys,json; print(json.load(sys.stdin).get('query',''))" 2>/dev/null) + npx gitnexus query "$q" 2>&1 + ;; + context) + local n=$(echo "$json_body" | python3 -c "import sys,json; print(json.load(sys.stdin).get('name',''))" 2>/dev/null) + npx gitnexus context "$n" 2>&1 + ;; + impact) + local t=$(echo "$json_body" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('target',''))" 2>/dev/null) + local d=$(echo "$json_body" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('direction','upstream'))" 2>/dev/null) + npx gitnexus impact "$t" --direction "$d" 2>&1 + ;; + cypher) + local cq=$(echo "$json_body" | python3 -c "import sys,json; print(json.load(sys.stdin).get('query',''))" 2>/dev/null) + npx gitnexus cypher "$cq" 2>&1 + ;; + *) + echo "Unknown tool: $tool" >&2 + return 1 + ;; + esac +} + +gitnexus-query() { + local query="$1" + local task_context="${2:-}" + local goal="${3:-}" + + if [ -z "$query" ]; then + echo "Usage: gitnexus-query [task_context] [goal]" + echo "Search the code knowledge graph for execution flows related to a concept." + echo "" + echo "Examples:" + echo ' gitnexus-query "authentication flow"' + echo ' gitnexus-query "database connection" "fixing connection pool leak"' + return 1 + fi + + local args="{\"query\": \"$query\"" + [ -n "$task_context" ] && args="$args, \"task_context\": \"$task_context\"" + [ -n "$goal" ] && args="$args, \"goal\": \"$goal\"" + args="$args}" + + _gitnexus_call query "$args" +} + +gitnexus-context() { + local name="$1" + local file_path="${2:-}" + + if [ -z "$name" ]; then + echo "Usage: gitnexus-context [file_path]" + echo "Get a 360-degree view of a code symbol: callers, callees, processes, file location." + echo "" + echo "Examples:" + echo ' gitnexus-context "validateUser"' + echo ' gitnexus-context "AuthService" "src/auth/service.py"' + return 1 + fi + + local args="{\"name\": \"$name\"" + [ -n "$file_path" ] && args="$args, \"file_path\": \"$file_path\"" + args="$args}" + + _gitnexus_call context "$args" +} + +gitnexus-impact() { + local target="$1" + local direction="${2:-upstream}" + + if [ -z "$target" ]; then + echo "Usage: gitnexus-impact [upstream|downstream]" + echo "Analyze the blast radius of changing a code symbol." + echo "" + echo " upstream = what depends on this (what breaks if you change it)" + echo " downstream = what this depends on (what it uses)" + echo "" + echo "Examples:" + echo ' gitnexus-impact "AuthService" upstream' + echo ' gitnexus-impact "validateUser" downstream' + return 1 + fi + + _gitnexus_call impact "{\"target\": \"$target\", \"direction\": \"$direction\"}" +} + +gitnexus-cypher() { + local query="$1" + + if [ -z "$query" ]; then + echo "Usage: gitnexus-cypher " + echo "Execute a raw Cypher query against the code knowledge graph." + echo "" + echo "Schema: Nodes: File, Function, Class, Method, Interface, Community, Process" + echo "Edges via CodeRelation.type: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS" + echo "" + echo "Examples:" + echo " gitnexus-cypher 'MATCH (a)-[:CodeRelation {type: \"CALLS\"}]->(b:Function {name: \"save\"}) RETURN a.name, a.filePath'" + echo " gitnexus-cypher 'MATCH (n:Class) RETURN n.name, n.filePath LIMIT 20'" + return 1 + fi + + _gitnexus_call cypher "{\"query\": \"$query\"}" +} + +gitnexus-overview() { + echo "=== Code Knowledge Graph Overview ===" + _gitnexus_call list_repos '{}' +} + +# Export functions so they're available in subshells +export -f _gitnexus_call 2>/dev/null +export -f gitnexus-query 2>/dev/null +export -f gitnexus-context 2>/dev/null +export -f gitnexus-impact 2>/dev/null +export -f gitnexus-cypher 2>/dev/null +export -f gitnexus-overview 2>/dev/null diff --git a/eval/bridge/mcp_bridge.py b/eval/bridge/mcp_bridge.py new file mode 100644 index 000000000..1be0bde6e --- /dev/null +++ b/eval/bridge/mcp_bridge.py @@ -0,0 +1,336 @@ +""" +MCP Bridge for GitNexus + +Starts the GitNexus MCP server as a subprocess and provides a Python interface +to call MCP tools. Used by the bash wrapper scripts and the augmentation layer. + +The bridge communicates with the MCP server via stdio using the JSON-RPC protocol. +""" + +import json +import logging +import os +import subprocess +import sys +import threading +import time +from pathlib import Path +from typing import Any + +logger = logging.getLogger("mcp_bridge") + + +class MCPBridge: + """ + Manages a GitNexus MCP server subprocess and proxies tool calls to it. + + Usage: + bridge = MCPBridge(repo_path="/path/to/repo") + bridge.start() + result = bridge.call_tool("query", {"query": "authentication"}) + bridge.stop() + """ + + def __init__(self, repo_path: str | None = None): + self.repo_path = repo_path or os.getcwd() + self.process: subprocess.Popen | None = None + self._request_id = 0 + self._lock = threading.Lock() + self._started = False + + def start(self) -> bool: + """Start the GitNexus MCP server subprocess.""" + if self._started: + return True + + try: + # Find gitnexus binary + gitnexus_bin = self._find_gitnexus() + if not gitnexus_bin: + logger.error("GitNexus not found. Install with: npm install -g gitnexus") + return False + + self.process = subprocess.Popen( + [gitnexus_bin, "mcp"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=self.repo_path, + text=False, + ) + + # Send initialize request + init_result = self._send_request("initialize", { + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "gitnexus-eval", "version": "0.1.0"}, + }) + + if init_result is None: + logger.error("MCP server failed to initialize") + self.stop() + return False + + # Send initialized notification + self._send_notification("notifications/initialized", {}) + self._started = True + logger.info("MCP bridge started successfully") + return True + + except Exception as e: + logger.error(f"Failed to start MCP bridge: {e}") + self.stop() + return False + + def stop(self): + """Stop the MCP server subprocess.""" + if self.process: + try: + self.process.stdin.close() + self.process.terminate() + self.process.wait(timeout=5) + except Exception: + try: + self.process.kill() + except Exception: + pass + self.process = None + self._started = False + + def call_tool(self, tool_name: str, arguments: dict[str, Any] | None = None) -> dict[str, Any] | None: + """ + Call a GitNexus MCP tool and return the result. + + Returns the tool result content or None on error. + """ + if not self._started: + logger.error("MCP bridge not started") + return None + + result = self._send_request("tools/call", { + "name": tool_name, + "arguments": arguments or {}, + }) + + if result is None: + return None + + # Extract text content from MCP response + content = result.get("content", []) + if content and isinstance(content, list): + texts = [item.get("text", "") for item in content if item.get("type") == "text"] + return {"text": "\n".join(texts), "raw": content} + + return {"text": "", "raw": content} + + def list_tools(self) -> list[dict]: + """List available MCP tools.""" + result = self._send_request("tools/list", {}) + if result: + return result.get("tools", []) + return [] + + def read_resource(self, uri: str) -> str | None: + """Read an MCP resource by URI.""" + result = self._send_request("resources/read", {"uri": uri}) + if result: + contents = result.get("contents", []) + if contents: + return contents[0].get("text", "") + return None + + def _find_gitnexus(self) -> str | None: + """Find the gitnexus CLI binary.""" + # Check if npx is available (preferred - uses local install) + for cmd in ["npx"]: + try: + result = subprocess.run( + [cmd, "gitnexus", "--version"], + capture_output=True, text=True, timeout=15, + cwd=self.repo_path, + ) + if result.returncode == 0: + return cmd # Will use "npx gitnexus mcp" + except Exception: + continue + + # Check for global install + try: + result = subprocess.run( + ["gitnexus", "--version"], + capture_output=True, text=True, timeout=10, + ) + if result.returncode == 0: + return "gitnexus" + except Exception: + pass + + return None + + def _next_id(self) -> int: + with self._lock: + self._request_id += 1 + return self._request_id + + def _send_request(self, method: str, params: dict) -> dict | None: + """Send a JSON-RPC request and wait for response.""" + if not self.process or not self.process.stdin or not self.process.stdout: + return None + + request_id = self._next_id() + request = { + "jsonrpc": "2.0", + "id": request_id, + "method": method, + "params": params, + } + + try: + message = json.dumps(request) + # MCP uses Content-Length header framing + header = f"Content-Length: {len(message.encode('utf-8'))}\r\n\r\n" + self.process.stdin.write(header.encode("utf-8")) + self.process.stdin.write(message.encode("utf-8")) + self.process.stdin.flush() + + # Read response + response = self._read_response(timeout=30) + if response and response.get("id") == request_id: + if "error" in response: + logger.error(f"MCP error: {response['error']}") + return None + return response.get("result") + return None + + except Exception as e: + logger.error(f"MCP request failed: {e}") + return None + + def _send_notification(self, method: str, params: dict): + """Send a JSON-RPC notification (no response expected).""" + if not self.process or not self.process.stdin: + return + + notification = { + "jsonrpc": "2.0", + "method": method, + "params": params, + } + + try: + message = json.dumps(notification) + header = f"Content-Length: {len(message.encode('utf-8'))}\r\n\r\n" + self.process.stdin.write(header.encode("utf-8")) + self.process.stdin.write(message.encode("utf-8")) + self.process.stdin.flush() + except Exception as e: + logger.error(f"MCP notification failed: {e}") + + def _read_response(self, timeout: float = 30) -> dict | None: + """Read a JSON-RPC response from the MCP server.""" + if not self.process or not self.process.stdout: + return None + + start = time.time() + + try: + while time.time() - start < timeout: + # Read Content-Length header + header_line = b"" + while True: + byte = self.process.stdout.read(1) + if not byte: + return None + header_line += byte + if header_line.endswith(b"\r\n\r\n"): + break + if header_line.endswith(b"\n\n"): + break + + # Parse content length + header_str = header_line.decode("utf-8").strip() + content_length = None + for line in header_str.split("\r\n"): + if line.lower().startswith("content-length:"): + content_length = int(line.split(":")[1].strip()) + break + + if content_length is None: + continue + + # Read body + body = self.process.stdout.read(content_length) + if not body: + return None + + message = json.loads(body.decode("utf-8")) + + # Skip notifications (no id), return responses + if "id" in message: + return message + + return None + + except Exception as e: + logger.error(f"Error reading MCP response: {e}") + return None + + +class MCPToolCLI: + """ + CLI wrapper that exposes MCP tools as simple command-line calls. + Used by the bash wrapper scripts inside Docker containers. + + Usage from bash: + python -m bridge.mcp_bridge query '{"query": "authentication"}' + python -m bridge.mcp_bridge context '{"name": "validateUser"}' + """ + + def __init__(self): + self.bridge = MCPBridge() + + def run(self, tool_name: str, args_json: str = "{}") -> int: + """Run a single tool call and print the result.""" + try: + args = json.loads(args_json) + except json.JSONDecodeError: + # Try to parse as simple key=value pairs + args = self._parse_simple_args(args_json) + + if not self.bridge.start(): + print("ERROR: Failed to start GitNexus MCP bridge", file=sys.stderr) + return 1 + + try: + result = self.bridge.call_tool(tool_name, args) + if result: + print(result.get("text", "")) + return 0 + else: + print("No results", file=sys.stderr) + return 1 + finally: + self.bridge.stop() + + @staticmethod + def _parse_simple_args(args_str: str) -> dict: + """Parse 'key=value key2=value2' style arguments.""" + args = {} + for part in args_str.split(): + if "=" in part: + key, value = part.split("=", 1) + args[key] = value + return args + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python -m bridge.mcp_bridge [args_json]", file=sys.stderr) + print("Tools: query, context, impact, cypher, list_repos, detect_changes, rename", file=sys.stderr) + sys.exit(1) + + tool = sys.argv[1] + args_json = sys.argv[2] if len(sys.argv) > 2 else "{}" + + cli = MCPToolCLI() + sys.exit(cli.run(tool, args_json)) diff --git a/eval/configs/models/claude-haiku.yaml b/eval/configs/models/claude-haiku.yaml new file mode 100644 index 000000000..89746cdbd --- /dev/null +++ b/eval/configs/models/claude-haiku.yaml @@ -0,0 +1,9 @@ +# Claude 3.5 Haiku — fast, cheap, good baseline +# Via OpenRouter (set OPENROUTER_API_KEY in .env) +# To use Anthropic directly, change to: anthropic/claude-3-5-haiku-20241022 +model: + model_name: "openrouter/anthropic/claude-3.5-haiku" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 8192 + temperature: 0 diff --git a/eval/configs/models/claude-opus.yaml b/eval/configs/models/claude-opus.yaml new file mode 100644 index 000000000..3c3e114c4 --- /dev/null +++ b/eval/configs/models/claude-opus.yaml @@ -0,0 +1,9 @@ +# Claude Opus 4 — most capable, highest cost +# Via OpenRouter (set OPENROUTER_API_KEY in .env) +# To use Anthropic directly, change to: anthropic/claude-opus-4-20250514 +model: + model_name: "openrouter/anthropic/claude-opus-4" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 16384 + temperature: 0 diff --git a/eval/configs/models/claude-sonnet.yaml b/eval/configs/models/claude-sonnet.yaml new file mode 100644 index 000000000..2f8ab1493 --- /dev/null +++ b/eval/configs/models/claude-sonnet.yaml @@ -0,0 +1,9 @@ +# Claude Sonnet 4 — strong all-around model +# Via OpenRouter (set OPENROUTER_API_KEY in .env) +# To use Anthropic directly, change to: anthropic/claude-sonnet-4-20250514 +model: + model_name: "openrouter/anthropic/claude-sonnet-4" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 16384 + temperature: 0 diff --git a/eval/configs/models/glm-4.7.yaml b/eval/configs/models/glm-4.7.yaml new file mode 100644 index 000000000..8dc3111a1 --- /dev/null +++ b/eval/configs/models/glm-4.7.yaml @@ -0,0 +1,7 @@ +# GLM 4.7 — via OpenRouter (set OPENROUTER_API_KEY in .env) +model: + model_name: "openrouter/zhipuai/glm-4.7" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 8192 + temperature: 0 diff --git a/eval/configs/models/glm-5.yaml b/eval/configs/models/glm-5.yaml new file mode 100644 index 000000000..f37a162e6 --- /dev/null +++ b/eval/configs/models/glm-5.yaml @@ -0,0 +1,7 @@ +# GLM 5 — via OpenRouter (set OPENROUTER_API_KEY in .env) +model: + model_name: "openrouter/zhipuai/glm-5" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 8192 + temperature: 0 diff --git a/eval/configs/models/minimax-2.5.yaml b/eval/configs/models/minimax-2.5.yaml new file mode 100644 index 000000000..f3b43d554 --- /dev/null +++ b/eval/configs/models/minimax-2.5.yaml @@ -0,0 +1,7 @@ +# MiniMax M1 2.5 — via OpenRouter (set OPENROUTER_API_KEY in .env) +model: + model_name: "openrouter/minimax/minimax-m1-2.5" + cost_tracking: "ignore_errors" + model_kwargs: + max_tokens: 8192 + temperature: 0 diff --git a/eval/configs/modes/baseline.yaml b/eval/configs/modes/baseline.yaml new file mode 100644 index 000000000..c9c73b637 --- /dev/null +++ b/eval/configs/modes/baseline.yaml @@ -0,0 +1,9 @@ +# Baseline mode — no GitNexus, pure mini-swe-agent (control group) +agent: + agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" + gitnexus_mode: "baseline" + step_limit: 30 + cost_limit: 3.0 + +environment: + environment_class: "docker" diff --git a/eval/configs/modes/native.yaml b/eval/configs/modes/native.yaml new file mode 100644 index 000000000..573d125e7 --- /dev/null +++ b/eval/configs/modes/native.yaml @@ -0,0 +1,19 @@ +# Native mode — GitNexus tools only, no grep enrichment +# +# Explicit tools: gitnexus-query, gitnexus-context, gitnexus-impact, gitnexus-cypher +# Available as fast bash commands (~100ms via eval-server) +# +# Use this mode to isolate the value of explicit tools without grep augmentation. +agent: + agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" + gitnexus_mode: "native" + step_limit: 30 + cost_limit: 3.0 + track_gitnexus_usage: true + +environment: + environment_class: "eval.environments.gitnexus_docker.GitNexusDockerEnvironment" + enable_gitnexus: true + skip_embeddings: true + gitnexus_timeout: 120 + eval_server_port: 4848 diff --git a/eval/configs/modes/native_augment.yaml b/eval/configs/modes/native_augment.yaml new file mode 100644 index 000000000..fb9b79729 --- /dev/null +++ b/eval/configs/modes/native_augment.yaml @@ -0,0 +1,24 @@ +# Native + Augment mode — the primary evaluation mode +# +# Combines two capabilities (mirroring the Claude Code model): +# 1. Explicit GitNexus tools: gitnexus-query, gitnexus-context, gitnexus-impact, gitnexus-cypher +# Available as fast bash commands (~100ms via eval-server) +# 2. Automatic grep enrichment: grep/rg results are transparently augmented with +# [GitNexus] annotations showing callers, callees, and execution flows +# +# The agent decides when to use explicit tools vs rely on enriched grep results. +agent: + agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" + gitnexus_mode: "native_augment" + step_limit: 30 + cost_limit: 3.0 + augment_timeout: 5.0 + augment_min_pattern_length: 3 + track_gitnexus_usage: true + +environment: + environment_class: "eval.environments.gitnexus_docker.GitNexusDockerEnvironment" + enable_gitnexus: true + skip_embeddings: true + gitnexus_timeout: 120 + eval_server_port: 4848 diff --git a/eval/environments/__init__.py b/eval/environments/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eval/environments/gitnexus_docker.py b/eval/environments/gitnexus_docker.py new file mode 100644 index 000000000..95ad07bac --- /dev/null +++ b/eval/environments/gitnexus_docker.py @@ -0,0 +1,397 @@ +""" +GitNexus Docker Environment for SWE-bench Evaluation + +Extends mini-swe-agent's Docker environment to: +1. Install GitNexus (Node.js + npm + gitnexus package) +2. Run `gitnexus analyze` on the repository +3. Start the eval-server daemon (persistent HTTP server with warm KuzuDB) +4. Install standalone tool scripts in /usr/local/bin/ (works with subprocess.run) +5. Cache indexes per (repo, base_commit) to avoid re-indexing + +IMPORTANT: mini-swe-agent runs every command with subprocess.run in a fresh subshell. +This means .bashrc is NOT sourced, exported functions are NOT available, and env vars +don't persist. The tool scripts must be standalone executables in $PATH. + +Architecture: + Agent bash cmd → /usr/local/bin/gitnexus-query → curl localhost:4848/tool/query → eval-server → KuzuDB + Fallback: → npx gitnexus query (cold start, slower) + +Tool call latency: ~50-100ms via eval-server, ~5-10s via CLI fallback. +""" + +import hashlib +import json +import logging +import shutil +import time +from pathlib import Path + +from minisweagent.environments.docker import DockerEnvironment + +logger = logging.getLogger("gitnexus_docker") + +DEFAULT_CACHE_DIR = Path.home() / ".gitnexus-eval-cache" +EVAL_SERVER_PORT = 4848 + +# Standalone tool scripts installed into /usr/local/bin/ inside the container. +# Each script calls the eval-server via curl, with a CLI fallback. +# These are standalone — no sourcing, no env inheritance needed. + +TOOL_SCRIPT_QUERY = r'''#!/bin/bash +PORT="${GITNEXUS_EVAL_PORT:-__PORT__}" +query="$1"; task_ctx="${2:-}"; goal="${3:-}" +[ -z "$query" ] && echo "Usage: gitnexus-query [task_context] [goal]" && exit 1 +args="{\"query\": \"$query\"" +[ -n "$task_ctx" ] && args="$args, \"task_context\": \"$task_ctx\"" +[ -n "$goal" ] && args="$args, \"goal\": \"$goal\"" +args="$args}" +result=$(curl -sf -X POST "http://127.0.0.1:${PORT}/tool/query" -H "Content-Type: application/json" -d "$args" 2>/dev/null) +if [ $? -eq 0 ] && [ -n "$result" ]; then echo "$result"; exit 0; fi +cd /testbed && npx gitnexus query "$query" 2>&1 +''' + +TOOL_SCRIPT_CONTEXT = r'''#!/bin/bash +PORT="${GITNEXUS_EVAL_PORT:-__PORT__}" +name="$1"; file_path="${2:-}" +[ -z "$name" ] && echo "Usage: gitnexus-context [file_path]" && exit 1 +args="{\"name\": \"$name\"" +[ -n "$file_path" ] && args="$args, \"file_path\": \"$file_path\"" +args="$args}" +result=$(curl -sf -X POST "http://127.0.0.1:${PORT}/tool/context" -H "Content-Type: application/json" -d "$args" 2>/dev/null) +if [ $? -eq 0 ] && [ -n "$result" ]; then echo "$result"; exit 0; fi +cd /testbed && npx gitnexus context "$name" 2>&1 +''' + +TOOL_SCRIPT_IMPACT = r'''#!/bin/bash +PORT="${GITNEXUS_EVAL_PORT:-__PORT__}" +target="$1"; direction="${2:-upstream}" +[ -z "$target" ] && echo "Usage: gitnexus-impact [upstream|downstream]" && exit 1 +result=$(curl -sf -X POST "http://127.0.0.1:${PORT}/tool/impact" -H "Content-Type: application/json" -d "{\"target\": \"$target\", \"direction\": \"$direction\"}" 2>/dev/null) +if [ $? -eq 0 ] && [ -n "$result" ]; then echo "$result"; exit 0; fi +cd /testbed && npx gitnexus impact "$target" --direction "$direction" 2>&1 +''' + +TOOL_SCRIPT_CYPHER = r'''#!/bin/bash +PORT="${GITNEXUS_EVAL_PORT:-__PORT__}" +query="$1" +[ -z "$query" ] && echo "Usage: gitnexus-cypher " && exit 1 +result=$(curl -sf -X POST "http://127.0.0.1:${PORT}/tool/cypher" -H "Content-Type: application/json" -d "{\"query\": \"$query\"}" 2>/dev/null) +if [ $? -eq 0 ] && [ -n "$result" ]; then echo "$result"; exit 0; fi +cd /testbed && npx gitnexus cypher "$query" 2>&1 +''' + +TOOL_SCRIPT_AUGMENT = r'''#!/bin/bash +cd /testbed && npx gitnexus augment "$1" 2>&1 || true +''' + +TOOL_SCRIPT_OVERVIEW = r'''#!/bin/bash +PORT="${GITNEXUS_EVAL_PORT:-__PORT__}" +echo "=== Code Knowledge Graph Overview ===" +result=$(curl -sf -X POST "http://127.0.0.1:${PORT}/tool/list_repos" -H "Content-Type: application/json" -d "{}" 2>/dev/null) +if [ $? -eq 0 ] && [ -n "$result" ]; then echo "$result"; exit 0; fi +cd /testbed && npx gitnexus list 2>&1 +''' + + +class GitNexusDockerEnvironment(DockerEnvironment): + """ + Docker environment with GitNexus pre-installed, indexed, and eval-server running. + + Setup flow: + 1. Start Docker container (base SWE-bench image) + 2. Install Node.js + gitnexus inside the container + 3. Run `gitnexus analyze` (or restore from cache) + 4. Start `gitnexus eval-server` daemon (keeps KuzuDB warm) + 5. Install standalone tool scripts in /usr/local/bin/ + 6. Agent runs with near-instant GitNexus tool calls + """ + + def __init__( + self, + *, + enable_gitnexus: bool = True, + cache_dir: str | Path | None = None, + skip_embeddings: bool = True, + gitnexus_timeout: int = 120, + eval_server_port: int = EVAL_SERVER_PORT, + **kwargs, + ): + super().__init__(**kwargs) + self.enable_gitnexus = enable_gitnexus + self.cache_dir = Path(cache_dir) if cache_dir else DEFAULT_CACHE_DIR + self.skip_embeddings = skip_embeddings + self.gitnexus_timeout = gitnexus_timeout + self.eval_server_port = eval_server_port + self.index_time: float = 0.0 + self._gitnexus_ready = False + + def start(self) -> dict: + """Start the container and set up GitNexus.""" + result = super().start() + + if self.enable_gitnexus: + try: + self._setup_gitnexus() + except Exception as e: + logger.warning(f"GitNexus setup failed, continuing without it: {e}") + self._gitnexus_ready = False + + return result + + def _setup_gitnexus(self): + """Install and configure GitNexus in the container.""" + start = time.time() + + self._ensure_nodejs() + self._install_gitnexus() + self._index_repository() + self._start_eval_server() + self._install_tools() + + self.index_time = time.time() - start + self._gitnexus_ready = True + logger.info(f"GitNexus setup completed in {self.index_time:.1f}s") + + def _ensure_nodejs(self): + """Ensure Node.js >= 18 is available in the container.""" + check = self.execute({"command": "node --version 2>/dev/null || echo 'NOT_FOUND'"}) + output = check.get("output", "").strip() + + if "NOT_FOUND" in output: + logger.info("Installing Node.js in container...") + install_cmds = [ + "apt-get update -qq", + "apt-get install -y -qq curl ca-certificates", + "curl -fsSL https://deb.nodesource.com/setup_20.x | bash -", + "apt-get install -y -qq nodejs", + ] + for cmd in install_cmds: + result = self.execute({"command": cmd, "timeout": 60}) + if result.get("returncode", 1) != 0: + raise RuntimeError(f"Failed to install Node.js: {result.get('output', '')}") + else: + logger.info(f"Node.js already available: {output}") + + def _install_gitnexus(self): + """Install the gitnexus npm package globally.""" + check = self.execute({"command": "npx gitnexus --version 2>/dev/null || echo 'NOT_FOUND'"}) + if "NOT_FOUND" in check.get("output", ""): + logger.info("Installing gitnexus...") + result = self.execute({ + "command": "npm install -g gitnexus", + "timeout": 60, + }) + if result.get("returncode", 1) != 0: + raise RuntimeError(f"Failed to install gitnexus: {result.get('output', '')}") + + def _index_repository(self): + """Run gitnexus analyze on the repo, using cache if available.""" + repo_info = self._get_repo_info() + cache_key = self._make_cache_key(repo_info) + cache_path = self.cache_dir / cache_key + + if cache_path.exists(): + logger.info(f"Restoring GitNexus index from cache: {cache_key}") + self._restore_cache(cache_path) + return + + logger.info("Running gitnexus analyze...") + skip_flag = "--skip-embeddings" if self.skip_embeddings else "" + result = self.execute({ + "command": f"cd /testbed && npx gitnexus analyze . {skip_flag} 2>&1", + "timeout": self.gitnexus_timeout, + }) + + if result.get("returncode", 1) != 0: + output = result.get("output", "") + if "error" in output.lower() and "indexed" not in output.lower(): + raise RuntimeError(f"gitnexus analyze failed: {output[-500:]}") + + self._save_cache(cache_path, repo_info) + + def _start_eval_server(self): + """Start the GitNexus eval-server daemon in the background.""" + logger.info(f"Starting eval-server on port {self.eval_server_port}...") + + self.execute({ + "command": ( + f"nohup npx gitnexus eval-server --port {self.eval_server_port} " + f"--idle-timeout 600 " + f"> /tmp/gitnexus-eval-server.log 2>&1 &" + ), + "timeout": 5, + }) + + # Wait for the server to be ready (up to 15s for KuzuDB init) + for i in range(30): + time.sleep(0.5) + health = self.execute({ + "command": f"curl -sf http://127.0.0.1:{self.eval_server_port}/health 2>/dev/null || echo 'NOT_READY'", + "timeout": 3, + }) + output = health.get("output", "").strip() + if "NOT_READY" not in output and "ok" in output: + logger.info(f"Eval-server ready after {(i + 1) * 0.5:.1f}s") + return + + log_output = self.execute({ + "command": "cat /tmp/gitnexus-eval-server.log 2>/dev/null | tail -20", + }) + logger.warning( + f"Eval-server didn't become ready in 15s. " + f"Tools will fall back to direct CLI.\n" + f"Server log: {log_output.get('output', 'N/A')}" + ) + + def _install_tools(self): + """ + Install standalone GitNexus tool scripts in /usr/local/bin/. + + Each script is a self-contained bash script that: + 1. Calls the eval-server via curl (fast path, ~100ms) + 2. Falls back to direct CLI if eval-server is unavailable + + These are standalone executables — no sourcing, env inheritance, or .bashrc + needed. This is critical because mini-swe-agent runs every command via + subprocess.run in a fresh subshell. + + Uses heredocs with quoted delimiter to avoid all quoting/escaping issues. + """ + port = str(self.eval_server_port) + + tools = { + "gitnexus-query": TOOL_SCRIPT_QUERY, + "gitnexus-context": TOOL_SCRIPT_CONTEXT, + "gitnexus-impact": TOOL_SCRIPT_IMPACT, + "gitnexus-cypher": TOOL_SCRIPT_CYPHER, + "gitnexus-augment": TOOL_SCRIPT_AUGMENT, + "gitnexus-overview": TOOL_SCRIPT_OVERVIEW, + } + + for name, script in tools.items(): + script_content = script.replace("__PORT__", port).strip() + # Use heredoc with quoted delimiter — prevents all variable expansion and quoting issues + self.execute({ + "command": f"cat << 'GITNEXUS_SCRIPT_EOF' > /usr/local/bin/{name}\n{script_content}\nGITNEXUS_SCRIPT_EOF\nchmod +x /usr/local/bin/{name}", + "timeout": 5, + }) + + logger.info(f"Installed {len(tools)} GitNexus tool scripts in /usr/local/bin/") + + def _get_repo_info(self) -> dict: + """Get repository identity info from the container.""" + repo_result = self.execute({ + "command": "cd /testbed && basename $(git remote get-url origin 2>/dev/null || basename $(pwd)) .git" + }) + commit_result = self.execute({"command": "cd /testbed && git rev-parse HEAD 2>/dev/null || echo unknown"}) + + return { + "repo": repo_result.get("output", "unknown").strip(), + "commit": commit_result.get("output", "unknown").strip(), + } + + @staticmethod + def _make_cache_key(repo_info: dict) -> str: + """Create a deterministic cache key from repo info.""" + content = f"{repo_info['repo']}:{repo_info['commit']}" + return hashlib.sha256(content.encode()).hexdigest()[:16] + + def _save_cache(self, cache_path: Path, repo_info: dict): + """Save the GitNexus index to the host cache directory.""" + try: + cache_path.mkdir(parents=True, exist_ok=True) + + find_result = self.execute({ + "command": "find /root/.gitnexus -name 'kuzu' -type d 2>/dev/null | head -1" + }) + gitnexus_dir = find_result.get("output", "").strip() + + if gitnexus_dir: + parent = str(Path(gitnexus_dir).parent) + self.execute({ + "command": f"cd {parent} && tar czf /tmp/gitnexus-cache.tar.gz .", + "timeout": 30, + }) + + container_id = getattr(self, "_container_id", None) or getattr(self, "container_id", None) + if container_id: + import subprocess as sp + sp.run( + ["docker", "cp", f"{container_id}:/tmp/gitnexus-cache.tar.gz", + str(cache_path / "index.tar.gz")], + check=True, capture_output=True, + ) + (cache_path / "metadata.json").write_text(json.dumps(repo_info, indent=2)) + logger.info(f"Cached GitNexus index: {cache_path}") + + except Exception as e: + logger.warning(f"Failed to cache GitNexus index: {e}") + if cache_path.exists(): + shutil.rmtree(cache_path, ignore_errors=True) + + def _restore_cache(self, cache_path: Path): + """Restore a cached GitNexus index into the container.""" + try: + cache_tarball = cache_path / "index.tar.gz" + if not cache_tarball.exists(): + logger.warning("Cache tarball not found, re-indexing") + self._index_repository() + return + + container_id = getattr(self, "_container_id", None) or getattr(self, "container_id", None) + if container_id: + import subprocess as sp + + self.execute({"command": "mkdir -p /root/.gitnexus"}) + + storage_result = self.execute({ + "command": "npx gitnexus list 2>/dev/null | grep -o '/root/.gitnexus/[^ ]*' | head -1 || echo '/root/.gitnexus/repos/default'" + }) + storage_path = storage_result.get("output", "").strip() or "/root/.gitnexus/repos/default" + self.execute({"command": f"mkdir -p {storage_path}"}) + + sp.run( + ["docker", "cp", str(cache_tarball), f"{container_id}:/tmp/gitnexus-cache.tar.gz"], + check=True, capture_output=True, + ) + self.execute({ + "command": f"cd {storage_path} && tar xzf /tmp/gitnexus-cache.tar.gz", + "timeout": 30, + }) + logger.info("GitNexus index restored from cache") + + except Exception as e: + logger.warning(f"Failed to restore cache, re-indexing: {e}") + self._index_repository() + + def stop(self) -> dict: + """Stop the container, shutting down eval-server first.""" + if self._gitnexus_ready: + try: + self.execute({ + "command": f"curl -sf -X POST http://127.0.0.1:{self.eval_server_port}/shutdown 2>/dev/null || true", + "timeout": 3, + }) + except Exception: + pass + + return super().stop() + + def get_template_vars(self) -> dict: + """Add GitNexus-specific template variables.""" + base_vars = super().get_template_vars() + base_vars["gitnexus_ready"] = self._gitnexus_ready + base_vars["gitnexus_index_time"] = self.index_time + return base_vars + + def serialize(self) -> dict: + """Include GitNexus environment info in serialization.""" + base = super().serialize() + base.setdefault("info", {})["gitnexus_env"] = { + "enabled": self.enable_gitnexus, + "ready": self._gitnexus_ready, + "index_time_seconds": round(self.index_time, 2), + "skip_embeddings": self.skip_embeddings, + "eval_server_port": self.eval_server_port, + } + return base diff --git a/eval/prompts/instance_baseline.jinja b/eval/prompts/instance_baseline.jinja new file mode 100644 index 000000000..11a939ede --- /dev/null +++ b/eval/prompts/instance_baseline.jinja @@ -0,0 +1,80 @@ +Please solve this issue: {{task}} + +You can execute bash commands and edit files to implement the necessary changes. + +## Recommended Workflow + +This workflows should be done step-by-step so that you can iterate on your changes and any possible problems. + +1. Analyze the codebase by finding and reading relevant files +2. Create a script to reproduce the issue +3. Edit the source code to resolve the issue +4. Verify your fix works by running your script again +5. Test edge cases to ensure your fix is robust +6. Submit your changes and finish your work by issuing the following command: `echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT`. + Do not combine it with any other command. After this command, you cannot continue working on this task. + +## Important Rules + +1. Every response must contain exactly one action +2. The action must be enclosed in triple backticks +3. Directory or environment variable changes are not persistent. Every action is executed in a new subshell. + However, you can prefix any action with `MY_ENV_VAR=MY_VALUE cd /path/to/working/dir && ...` or write/load environment variables from files + + +{{system}} {{release}} {{version}} {{machine}} + + +## Formatting your response + +Here is an example of a correct response: + + +THOUGHT: I need to understand the structure of the repository first. Let me check what files are in the current directory to get a better understanding of the codebase. + +```mswea_bash_command +ls -la +``` + + +## Useful command examples + +### Create a new file: + +```bash +cat <<'EOF' > newfile.py +import numpy as np +hello = "world" +print(hello) +EOF +``` + +### Edit files with sed: + +{%- if system == "Darwin" -%} + +You are on MacOS. For all the below examples, you need to use `sed -i ''` instead of `sed -i`. + +{%- endif -%} + +```bash +# Replace all occurrences +sed -i 's/old_string/new_string/g' filename.py +# Replace only first occurrence +sed -i 's/old_string/new_string/' filename.py +# Replace all occurrences in lines 1-10 +sed -i '1,10s/old_string/new_string/g' filename.py +``` + +### View file content: + +```bash +# View specific lines with numbers +nl -ba filename.py | sed -n '10,20p' +``` + +### Any other command you want to run + +```bash +anything +``` diff --git a/eval/prompts/instance_native.jinja b/eval/prompts/instance_native.jinja new file mode 100644 index 000000000..0b4f14ed9 --- /dev/null +++ b/eval/prompts/instance_native.jinja @@ -0,0 +1,102 @@ +Please solve this issue: {{task}} + +You can execute bash commands and edit files to implement the necessary changes. + +## Recommended Workflow + +Work step-by-step so you can iterate on your changes and catch problems early. + +1. **Understand the issue** — read the problem statement, identify the symptom and affected area +2. **Find the relevant code** — use `gitnexus-query ""` to find execution flows, or `grep` for specific strings +3. **Understand the suspect** — use `gitnexus-context ""` to see all callers and callees, then `cat` to read the source +4. **Check blast radius** — before editing shared code, run `gitnexus-impact "" upstream` to see what depends on it +5. **Implement the fix** — make minimal, targeted changes +6. **Verify** — run relevant tests, check edge cases +7. **Submit** — issue: `echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT` + Do not combine it with any other command. After this command, you cannot continue working on this task. + +## Debugging Patterns + +| Symptom | Approach | +|---------|----------| +| Error message / exception | `gitnexus-query` for error text → `gitnexus-context` on throw sites | +| Wrong return value | `gitnexus-context` on the function → trace callees for data flow | +| Missing feature / incomplete behavior | `gitnexus-query` for feature area → find the execution flow → locate the gap | +| Need to understand callers | `gitnexus-context` — graph-complete, finds callers grep would miss | + +## Risk Assessment + +Before editing shared code, check the blast radius: + +| Impact | Risk | Action | +|--------|------|--------| +| <5 symbols at d=1 | Low | Fix with confidence | +| 5-15 symbols at d=1 | Medium | Fix carefully, run broader tests | +| >15 symbols at d=1 | High | Minimal change, run full test suite | + +## Important Rules + +1. Every response must contain exactly one action +2. The action must be enclosed in triple backticks +3. Directory or environment variable changes are not persistent. Every action is executed in a new subshell. + However, you can prefix any action with `MY_ENV_VAR=MY_VALUE cd /path/to/working/dir && ...` or write/load environment variables from files +4. Make minimal, targeted changes. Don't refactor unrelated code. +5. GitNexus tools are ~100ms. Use them when they save you multiple grep iterations. + + +{{system}} {{release}} {{version}} {{machine}} + + +## Formatting your response + +Here is an example of a correct response: + + +THOUGHT: The issue mentions a problem with form field validation. Let me search the code knowledge graph for the relevant execution flows to understand how validation works in this codebase. + +```mswea_bash_command +gitnexus-query "form field validation" +``` + + +## Useful command examples + +### Create a new file: + +```bash +cat <<'EOF' > newfile.py +import numpy as np +hello = "world" +print(hello) +EOF +``` + +### Edit files with sed: + +{%- if system == "Darwin" -%} + +You are on MacOS. For all the below examples, you need to use `sed -i ''` instead of `sed -i`. + +{%- endif -%} + +```bash +# Replace all occurrences +sed -i 's/old_string/new_string/g' filename.py +# Replace only first occurrence +sed -i 's/old_string/new_string/' filename.py +# Replace all occurrences in lines 1-10 +sed -i '1,10s/old_string/new_string/g' filename.py +``` + +### View file content: + +```bash +# View specific lines with numbers +nl -ba filename.py | sed -n '10,20p' +``` + +### Any other command you want to run + +```bash +anything +``` diff --git a/eval/prompts/instance_native_augment.jinja b/eval/prompts/instance_native_augment.jinja new file mode 100644 index 000000000..aafe1bbee --- /dev/null +++ b/eval/prompts/instance_native_augment.jinja @@ -0,0 +1,103 @@ +Please solve this issue: {{task}} + +You can execute bash commands and edit files to implement the necessary changes. + +## Recommended Workflow + +Work step-by-step so you can iterate on your changes and catch problems early. + +1. **Understand the issue** — read the problem statement, identify the symptom and affected area +2. **Find the relevant code** — use `gitnexus-query ""` to find execution flows, or `grep` for specific strings +3. **Understand the suspect** — use `gitnexus-context ""` to see all callers and callees, then `cat` to read the source +4. **Check blast radius** — before editing shared code, run `gitnexus-impact "" upstream` to see what depends on it +5. **Implement the fix** — make minimal, targeted changes +6. **Verify** — run relevant tests, check edge cases +7. **Submit** — issue: `echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT` + Do not combine it with any other command. After this command, you cannot continue working on this task. + +## Debugging Patterns + +| Symptom | Approach | +|---------|----------| +| Error message / exception | `gitnexus-query` for error text → `gitnexus-context` on throw sites | +| Wrong return value | `gitnexus-context` on the function → trace callees for data flow | +| Missing feature / incomplete behavior | `gitnexus-query` for feature area → find the execution flow → locate the gap | +| Need to understand callers | `gitnexus-context` — graph-complete, finds callers grep would miss | + +## Risk Assessment + +Before editing shared code, check the blast radius: + +| Impact | Risk | Action | +|--------|------|--------| +| <5 symbols at d=1 | Low | Fix with confidence | +| 5-15 symbols at d=1 | Medium | Fix carefully, run broader tests | +| >15 symbols at d=1 | High | Minimal change, run full test suite | + +## Important Rules + +1. Every response must contain exactly one action +2. The action must be enclosed in triple backticks +3. Directory or environment variable changes are not persistent. Every action is executed in a new subshell. + However, you can prefix any action with `MY_ENV_VAR=MY_VALUE cd /path/to/working/dir && ...` or write/load environment variables from files +4. Make minimal, targeted changes. Don't refactor unrelated code. +5. GitNexus tools are ~100ms. Use them when they save you multiple grep iterations. +6. When grep results show `[GitNexus]` enrichments, use those for navigation. + + +{{system}} {{release}} {{version}} {{machine}} + + +## Formatting your response + +Here is an example of a correct response: + + +THOUGHT: The issue mentions a problem with form field validation. Let me search the code knowledge graph for the relevant execution flows to understand how validation works in this codebase. + +```mswea_bash_command +gitnexus-query "form field validation" +``` + + +## Useful command examples + +### Create a new file: + +```bash +cat <<'EOF' > newfile.py +import numpy as np +hello = "world" +print(hello) +EOF +``` + +### Edit files with sed: + +{%- if system == "Darwin" -%} + +You are on MacOS. For all the below examples, you need to use `sed -i ''` instead of `sed -i`. + +{%- endif -%} + +```bash +# Replace all occurrences +sed -i 's/old_string/new_string/g' filename.py +# Replace only first occurrence +sed -i 's/old_string/new_string/' filename.py +# Replace all occurrences in lines 1-10 +sed -i '1,10s/old_string/new_string/g' filename.py +``` + +### View file content: + +```bash +# View specific lines with numbers +nl -ba filename.py | sed -n '10,20p' +``` + +### Any other command you want to run + +```bash +anything +``` diff --git a/eval/prompts/system_baseline.jinja b/eval/prompts/system_baseline.jinja new file mode 100644 index 000000000..45df961ef --- /dev/null +++ b/eval/prompts/system_baseline.jinja @@ -0,0 +1,15 @@ +You are a helpful assistant that can interact with a computer to solve software engineering tasks. + +Your response must contain exactly ONE bash code block with ONE command (or commands connected with && or ||). +Include a THOUGHT section before your command where you explain your reasoning process. +Format your response as shown in. + + +Your reasoning and analysis here. Explain why you want to perform the action. + +```mswea_bash_command +your_command_here +``` + + +Failure to follow these rules will cause your response to be rejected. diff --git a/eval/prompts/system_native.jinja b/eval/prompts/system_native.jinja new file mode 100644 index 000000000..6a736846f --- /dev/null +++ b/eval/prompts/system_native.jinja @@ -0,0 +1,54 @@ +You are a helpful assistant that can interact with a computer to solve software engineering tasks. + +Your response must contain exactly ONE bash code block with ONE command (or commands connected with && or ||). +Include a THOUGHT section before your command where you explain your reasoning process. +Format your response as shown in. + + +Your reasoning and analysis here. Explain why you want to perform the action. + +```mswea_bash_command +your_command_here +``` + + +Failure to follow these rules will cause your response to be rejected. + +## Code Intelligence + +You have **GitNexus** — a knowledge graph over this entire codebase. It knows every function call chain, class hierarchy, execution flow, and symbol relationship. These are fast bash commands (~100ms). Use them when useful, skip them when a simple grep suffices. + +### GitNexus Commands + +**gitnexus-query ""** — Find execution flows related to a concept. +Returns ranked execution flow traces with participating symbols and file locations. +```bash +gitnexus-query "form field validation" +``` + +**gitnexus-context "" [""]** — 360-degree view of a symbol. +Returns ALL callers, ALL callees, and execution flows. Graph-complete — finds callers that grep misses. +```bash +gitnexus-context "BoundField" "django/forms/boundfield.py" +``` + +**gitnexus-impact "" [upstream|downstream]** — Blast radius analysis. +What breaks if you change this: d=1 WILL BREAK, d=2 LIKELY AFFECTED, d=3 MAY NEED TESTING. +```bash +gitnexus-impact "BoundField" upstream +``` + +**gitnexus-cypher ""** — Raw Cypher query against the code graph. +```bash +gitnexus-cypher 'MATCH (a)-[:CodeRelation {type: "CALLS"}]->(b:Function {name: "clean"}) RETURN a.name, a.filePath' +``` + +### When to Use What + +| I need to... | Use | +|---|---| +| Understand how a feature works end-to-end | `gitnexus-query` | +| Find ALL callers of a function | `gitnexus-context` | +| Know what breaks if I change something | `gitnexus-impact` upstream | +| Find a string literal or error message | `grep` | +| Read source code | `cat` / `nl -ba` | diff --git a/eval/prompts/system_native_augment.jinja b/eval/prompts/system_native_augment.jinja new file mode 100644 index 000000000..c9379a110 --- /dev/null +++ b/eval/prompts/system_native_augment.jinja @@ -0,0 +1,56 @@ +You are a helpful assistant that can interact with a computer to solve software engineering tasks. + +Your response must contain exactly ONE bash code block with ONE command (or commands connected with && or ||). +Include a THOUGHT section before your command where you explain your reasoning process. +Format your response as shown in. + + +Your reasoning and analysis here. Explain why you want to perform the action. + +```mswea_bash_command +your_command_here +``` + + +Failure to follow these rules will cause your response to be rejected. + +## Code Intelligence + +You have **GitNexus** — a knowledge graph over this entire codebase. It knows every function call chain, class hierarchy, execution flow, and symbol relationship. These are fast bash commands (~100ms). Use them when useful, skip them when a simple grep suffices. + +Your `grep` results are also automatically enriched with `[GitNexus]` annotations showing callers, callees, and execution flows for matched symbols. Pay attention to these — they often point you to the right code without extra tool calls. + +### GitNexus Commands + +**gitnexus-query ""** — Find execution flows related to a concept. +Returns ranked execution flow traces with participating symbols and file locations. +```bash +gitnexus-query "form field validation" +``` + +**gitnexus-context "" [""]** — 360-degree view of a symbol. +Returns ALL callers, ALL callees, and execution flows. Graph-complete — finds callers that grep misses. +```bash +gitnexus-context "BoundField" "django/forms/boundfield.py" +``` + +**gitnexus-impact "" [upstream|downstream]** — Blast radius analysis. +What breaks if you change this: d=1 WILL BREAK, d=2 LIKELY AFFECTED, d=3 MAY NEED TESTING. +```bash +gitnexus-impact "BoundField" upstream +``` + +**gitnexus-cypher ""** — Raw Cypher query against the code graph. +```bash +gitnexus-cypher 'MATCH (a)-[:CodeRelation {type: "CALLS"}]->(b:Function {name: "clean"}) RETURN a.name, a.filePath' +``` + +### When to Use What + +| I need to... | Use | +|---|---| +| Understand how a feature works end-to-end | `gitnexus-query` | +| Find ALL callers of a function | `gitnexus-context` | +| Know what breaks if I change something | `gitnexus-impact` upstream | +| Find a string literal or error message | `grep` | +| Read source code | `cat` / `nl -ba` | diff --git a/eval/pyproject.toml b/eval/pyproject.toml new file mode 100644 index 000000000..ae9d2ad92 --- /dev/null +++ b/eval/pyproject.toml @@ -0,0 +1,35 @@ +[project] +name = "gitnexus-swebench-eval" +version = "0.1.0" +description = "SWE-bench evaluation harness with GitNexus code intelligence integration" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "mini-swe-agent>=2.0.0", + "litellm>=1.50.0", + "datasets>=3.0.0", + "typer>=0.12.0", + "rich>=13.0.0", + "pyyaml>=6.0", + "pandas>=2.0.0", + "tabulate>=0.9.0", + "python-dotenv>=1.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", + "ruff>=0.5.0", +] + +[project.scripts] +gitnexus-eval = "run_eval:app" +gitnexus-eval-analyze = "analysis.analyze_results:app" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.ruff] +line-length = 120 +target-version = "py311" diff --git a/eval/run_eval.py b/eval/run_eval.py new file mode 100644 index 000000000..38dc7a473 --- /dev/null +++ b/eval/run_eval.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +""" +GitNexus SWE-bench Evaluation Runner + +Main entry point for running SWE-bench evaluations with and without GitNexus. +Supports running a single configuration or a full matrix of models x modes. + +Usage: + # Single run (default: native_augment mode — GitNexus tools + grep enrichment) + python run_eval.py single -m claude-sonnet --subset lite --slice 0:5 + + # Baseline comparison (no GitNexus) + python run_eval.py single -m claude-sonnet --mode baseline --subset lite --slice 0:5 + + # Matrix run (all models x all modes) + python run_eval.py matrix --subset lite --slice 0:50 --workers 4 + + # Single instance for debugging + python run_eval.py debug -m claude-haiku -i django__django-16527 +""" + +import concurrent.futures +import json +import logging +import os +import threading +import time +import traceback +from itertools import product +from pathlib import Path +from typing import Any + +import typer +import yaml +from rich.console import Console +from rich.live import Live +from rich.table import Table + +# Load .env file from eval/ directory +_env_file = Path(__file__).parent / ".env" +if _env_file.exists(): + for line in _env_file.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + if "=" in line: + key, _, value = line.partition("=") + key, value = key.strip(), value.strip() + if value and key not in os.environ: # Don't override existing env vars + os.environ[key] = value + +logger = logging.getLogger("gitnexus_eval") +console = Console() +app = typer.Typer(rich_markup_mode="rich", add_completion=False) + +# Directory paths +EVAL_DIR = Path(__file__).parent +CONFIGS_DIR = EVAL_DIR / "configs" +MODELS_DIR = CONFIGS_DIR / "models" +MODES_DIR = CONFIGS_DIR / "modes" +DEFAULT_OUTPUT_DIR = EVAL_DIR / "results" + +# Available models and modes (discovered from config files) +AVAILABLE_MODELS = sorted([p.stem for p in MODELS_DIR.glob("*.yaml")]) +AVAILABLE_MODES = sorted([p.stem for p in MODES_DIR.glob("*.yaml")]) + +# SWE-bench dataset mapping (same as mini-swe-agent) +DATASET_MAPPING = { + "full": "princeton-nlp/SWE-Bench", + "verified": "princeton-nlp/SWE-Bench_Verified", + "lite": "princeton-nlp/SWE-Bench_Lite", +} + +_output_lock = threading.Lock() + + +def load_yaml_config(path: Path) -> dict: + """Load a YAML config file.""" + with open(path) as f: + return yaml.safe_load(f) or {} + + +def merge_configs(*configs: dict) -> dict: + """Recursively merge multiple config dicts (later values win).""" + result = {} + for config in configs: + for key, value in config.items(): + if key in result and isinstance(result[key], dict) and isinstance(value, dict): + result[key] = merge_configs(result[key], value) + else: + result[key] = value + return result + + +def build_config(model_name: str, mode_name: str) -> dict: + """Build a complete config from model + mode YAML files.""" + model_file = MODELS_DIR / f"{model_name}.yaml" + mode_file = MODES_DIR / f"{mode_name}.yaml" + + if not model_file.exists(): + raise FileNotFoundError(f"Model config not found: {model_file}") + if not mode_file.exists(): + raise FileNotFoundError(f"Mode config not found: {mode_file}") + + model_config = load_yaml_config(model_file) + mode_config = load_yaml_config(mode_file) + + return merge_configs(mode_config, model_config) + + +def load_instances(subset: str, split: str, slice_spec: str = "", filter_spec: str = "") -> list[dict]: + """Load SWE-bench instances.""" + from datasets import load_dataset + import re + + dataset_path = DATASET_MAPPING.get(subset, subset) + logger.info(f"Loading dataset: {dataset_path}, split: {split}") + instances = list(load_dataset(dataset_path, split=split)) + + if filter_spec: + instances = [i for i in instances if re.match(filter_spec, i["instance_id"])] + + if slice_spec: + values = [int(x) if x else None for x in slice_spec.split(":")] + instances = instances[slice(*values)] + + logger.info(f"Loaded {len(instances)} instances") + return instances + + +def get_swebench_docker_image(instance: dict) -> str: + """Get Docker image name for a SWE-bench instance.""" + image_name = instance.get("image_name") + if image_name is None: + iid = instance["instance_id"] + id_docker = iid.replace("__", "_1776_") + image_name = f"docker.io/swebench/sweb.eval.x86_64.{id_docker}:latest".lower() + return image_name + + +def process_instance( + instance: dict, + config: dict, + output_dir: Path, + model_name: str, + mode_name: str, +) -> dict: + """ + Process a single SWE-bench instance with the given config. + Returns result dict with instance_id, exit_status, submission, metrics. + """ + from minisweagent.models import get_model + + instance_id = instance["instance_id"] + run_id = f"{model_name}_{mode_name}" + instance_dir = output_dir / run_id / instance_id + instance_dir.mkdir(parents=True, exist_ok=True) + + result = { + "instance_id": instance_id, + "model": model_name, + "mode": mode_name, + "exit_status": None, + "submission": "", + "cost": 0.0, + "n_calls": 0, + "gitnexus_metrics": {}, + } + + agent = None + + try: + # Build model + model = get_model(config=config.get("model", {})) + + # Build environment + env_config = dict(config.get("environment", {})) + env_class_name = env_config.pop("environment_class", "docker") + + if env_class_name == "eval.environments.gitnexus_docker.GitNexusDockerEnvironment": + from eval.environments.gitnexus_docker import GitNexusDockerEnvironment + env_config["image"] = get_swebench_docker_image(instance) + env = GitNexusDockerEnvironment(**env_config) + else: + from minisweagent.environments.docker import DockerEnvironment + env = DockerEnvironment(image=get_swebench_docker_image(instance), **env_config) + + # Build agent + agent_config = dict(config.get("agent", {})) + agent_class_name = agent_config.pop("agent_class", "eval.agents.gitnexus_agent.GitNexusAgent") + + from eval.agents.gitnexus_agent import GitNexusAgent + traj_path = instance_dir / f"{instance_id}.traj.json" + agent_config["output_path"] = traj_path + agent = GitNexusAgent(model, env, **agent_config) + + # Run + logger.info(f"[{run_id}] Starting {instance_id}") + info = agent.run(instance["problem_statement"]) + + result["exit_status"] = info.get("exit_status") + result["submission"] = info.get("submission", "") + result["cost"] = agent.cost + result["n_calls"] = agent.n_calls + result["gitnexus_metrics"] = agent.gitnexus_metrics.to_dict() + + except Exception as e: + logger.error(f"[{run_id}] Error on {instance_id}: {e}") + result["exit_status"] = type(e).__name__ + result["error"] = str(e) + result["traceback"] = traceback.format_exc() + + finally: + if agent: + agent.save( + instance_dir / f"{instance_id}.traj.json", + {"instance_id": instance_id, "run_id": run_id}, + ) + + # Update predictions file + _update_preds(output_dir / run_id / "preds.json", instance_id, model_name, result) + + return result + + +def _update_preds(preds_path: Path, instance_id: str, model_name: str, result: dict): + """Thread-safe update of predictions file.""" + with _output_lock: + preds_path.parent.mkdir(parents=True, exist_ok=True) + data = {} + if preds_path.exists(): + data = json.loads(preds_path.read_text()) + data[instance_id] = { + "model_name_or_path": model_name, + "instance_id": instance_id, + "model_patch": result.get("submission", ""), + } + preds_path.write_text(json.dumps(data, indent=2)) + + +def run_configuration( + model_name: str, + mode_name: str, + instances: list[dict], + output_dir: Path, + workers: int = 1, + redo_existing: bool = False, +) -> list[dict]: + """Run a single (model, mode) configuration across all instances.""" + config = build_config(model_name, mode_name) + run_id = f"{model_name}_{mode_name}" + run_dir = output_dir / run_id + + # Skip existing instances + if not redo_existing and (run_dir / "preds.json").exists(): + existing = set(json.loads((run_dir / "preds.json").read_text()).keys()) + instances = [i for i in instances if i["instance_id"] not in existing] + if not instances: + logger.info(f"[{run_id}] All instances already completed, skipping") + return [] + + console.print(f" [bold]{run_id}[/bold]: {len(instances)} instances, {workers} workers") + + results = [] + + if workers <= 1: + for instance in instances: + result = process_instance(instance, config, output_dir, model_name, mode_name) + results.append(result) + else: + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor: + futures = { + executor.submit( + process_instance, instance, config, output_dir, model_name, mode_name + ): instance["instance_id"] + for instance in instances + } + for future in concurrent.futures.as_completed(futures): + try: + results.append(future.result()) + except Exception as e: + iid = futures[future] + logger.error(f"[{run_id}] Uncaught error for {iid}: {e}") + + # Save run summary + summary = { + "run_id": run_id, + "model": model_name, + "mode": mode_name, + "config": config, + "total_instances": len(results), + "completed": sum(1 for r in results if r["exit_status"] not in [None, "error"]), + "total_cost": sum(r.get("cost", 0) for r in results), + "total_api_calls": sum(r.get("n_calls", 0) for r in results), + "results": results, + } + (run_dir / "summary.json").mkdir(parents=True, exist_ok=True) if not run_dir.exists() else None + run_dir.mkdir(parents=True, exist_ok=True) + (run_dir / "summary.json").write_text(json.dumps(summary, indent=2, default=str)) + + return results + + +# ─── CLI Commands ─────────────────────────────────────────────────────────── + + +@app.command() +def single( + model: str = typer.Option(..., "-m", "--model", help=f"Model config name. Available: {', '.join(AVAILABLE_MODELS)}"), + mode: str = typer.Option("native_augment", "--mode", help=f"Evaluation mode. Available: {', '.join(AVAILABLE_MODES)}"), + subset: str = typer.Option("lite", "--subset", help="SWE-bench subset: lite, verified, full"), + split: str = typer.Option("dev", "--split", help="Dataset split"), + slice_spec: str = typer.Option("", "--slice", help="Slice spec (e.g., '0:5')"), + filter_spec: str = typer.Option("", "--filter", help="Filter instance IDs by regex"), + workers: int = typer.Option(1, "-w", "--workers", help="Parallel workers"), + output: str = typer.Option(str(DEFAULT_OUTPUT_DIR), "-o", "--output", help="Output directory"), + redo: bool = typer.Option(False, "--redo", help="Redo existing instances"), +): + """Run a single (model, mode) configuration on SWE-bench.""" + output_dir = Path(output) + instances = load_instances(subset, split, slice_spec, filter_spec) + + console.print(f"\n[bold]Running evaluation:[/bold] {model} + {mode}") + console.print(f" Instances: {len(instances)}") + console.print(f" Output: {output_dir}\n") + + results = run_configuration(model, mode, instances, output_dir, workers, redo) + + # Print summary + _print_summary(results, model, mode) + + +@app.command() +def matrix( + models: list[str] = typer.Option(AVAILABLE_MODELS, "-m", "--models", help="Models to evaluate (comma-separated or repeated)"), + modes: list[str] = typer.Option(AVAILABLE_MODES, "--modes", help="Modes to evaluate"), + subset: str = typer.Option("lite", "--subset", help="SWE-bench subset"), + split: str = typer.Option("dev", "--split", help="Dataset split"), + slice_spec: str = typer.Option("", "--slice", help="Slice spec"), + filter_spec: str = typer.Option("", "--filter", help="Filter instances by regex"), + workers: int = typer.Option(1, "-w", "--workers", help="Parallel workers per config"), + output: str = typer.Option(str(DEFAULT_OUTPUT_DIR), "-o", "--output", help="Output directory"), + redo: bool = typer.Option(False, "--redo", help="Redo existing instances"), +): + """Run the full evaluation matrix: all models x all modes.""" + output_dir = Path(output) + instances = load_instances(subset, split, slice_spec, filter_spec) + + combos = list(product(models, modes)) + console.print(f"\n[bold]Matrix evaluation:[/bold] {len(models)} models x {len(modes)} modes = {len(combos)} configs") + console.print(f" Models: {', '.join(models)}") + console.print(f" Modes: {', '.join(modes)}") + console.print(f" Instances per config: {len(instances)}") + console.print(f" Total runs: {len(combos) * len(instances)}") + console.print(f" Output: {output_dir}\n") + + all_results = {} + for model_name, mode_name in combos: + run_id = f"{model_name}_{mode_name}" + console.print(f"\n[bold cyan]━━━ {run_id} ━━━[/bold cyan]") + results = run_configuration(model_name, mode_name, instances, output_dir, workers, redo) + all_results[run_id] = results + + # Print comparative summary + _print_matrix_summary(all_results) + + # Save master summary + master = { + "timestamp": time.time(), + "models": models, + "modes": modes, + "subset": subset, + "n_instances": len(instances), + "runs": { + run_id: { + "total": len(results), + "cost": sum(r.get("cost", 0) for r in results), + "api_calls": sum(r.get("n_calls", 0) for r in results), + } + for run_id, results in all_results.items() + }, + } + output_dir.mkdir(parents=True, exist_ok=True) + (output_dir / "matrix_summary.json").write_text(json.dumps(master, indent=2, default=str)) + console.print(f"\n[green]Results saved to {output_dir}[/green]") + + +@app.command() +def debug( + model: str = typer.Option("claude-haiku", "-m", "--model", help="Model config name"), + mode: str = typer.Option("native_augment", "--mode", help="Evaluation mode"), + instance_id: str = typer.Option(..., "-i", "--instance", help="SWE-bench instance ID"), + subset: str = typer.Option("lite", "--subset", help="SWE-bench subset"), + split: str = typer.Option("dev", "--split"), + output: str = typer.Option(str(DEFAULT_OUTPUT_DIR / "debug"), "-o", "--output"), +): + """Debug a single SWE-bench instance.""" + from datasets import load_dataset + + dataset_path = DATASET_MAPPING.get(subset, subset) + instances = {inst["instance_id"]: inst for inst in load_dataset(dataset_path, split=split)} + + if instance_id not in instances: + console.print(f"[red]Instance '{instance_id}' not found in {subset}/{split}[/red]") + raise typer.Exit(1) + + instance = instances[instance_id] + config = build_config(model, mode) + output_dir = Path(output) + + console.print(f"\n[bold]Debug run:[/bold] {model} + {mode}") + console.print(f" Instance: {instance_id}") + console.print(f" Problem: {instance['problem_statement'][:200]}...\n") + + result = process_instance(instance, config, output_dir, model, mode) + _print_summary([result], model, mode) + + +@app.command() +def list_configs(): + """List available model and mode configurations.""" + console.print("\n[bold]Available Models:[/bold]") + for name in AVAILABLE_MODELS: + config = load_yaml_config(MODELS_DIR / f"{name}.yaml") + model_name = config.get("model", {}).get("model_name", "unknown") + console.print(f" {name:<20} {model_name}") + + console.print("\n[bold]Available Modes:[/bold]") + for name in AVAILABLE_MODES: + config = load_yaml_config(MODES_DIR / f"{name}.yaml") + gn_mode = config.get("agent", {}).get("gitnexus_mode", "baseline") + console.print(f" {name:<20} gitnexus_mode={gn_mode}") + + console.print(f"\n[bold]Matrix:[/bold] {len(AVAILABLE_MODELS)} models x {len(AVAILABLE_MODES)} modes = {len(AVAILABLE_MODELS) * len(AVAILABLE_MODES)} configurations") + + +# ─── Summary Output ──────────────────────────────────────────────────────── + + +def _print_summary(results: list[dict], model: str, mode: str): + """Print a summary table for a single run.""" + if not results: + console.print("[yellow]No results to display[/yellow]") + return + + table = Table(title=f"{model} + {mode}") + table.add_column("Metric", style="bold") + table.add_column("Value") + + total = len(results) + completed = sum(1 for r in results if r.get("submission")) + total_cost = sum(r.get("cost", 0) for r in results) + total_calls = sum(r.get("n_calls", 0) for r in results) + + table.add_row("Instances", str(total)) + table.add_row("Completed", f"{completed}/{total}") + table.add_row("Total Cost", f"${total_cost:.4f}") + table.add_row("Total API Calls", str(total_calls)) + table.add_row("Avg Cost/Instance", f"${total_cost / max(total, 1):.4f}") + table.add_row("Avg Calls/Instance", f"{total_calls / max(total, 1):.1f}") + + # GitNexus-specific metrics + gn_tool_calls = sum( + r.get("gitnexus_metrics", {}).get("total_tool_calls", 0) for r in results + ) + gn_augment_hits = sum( + r.get("gitnexus_metrics", {}).get("augmentation_hits", 0) for r in results + ) + if gn_tool_calls > 0: + table.add_row("GitNexus Tool Calls", str(gn_tool_calls)) + if gn_augment_hits > 0: + table.add_row("Augmentation Hits", str(gn_augment_hits)) + + console.print(table) + + +def _print_matrix_summary(all_results: dict[str, list[dict]]): + """Print a comparative matrix summary.""" + table = Table(title="Evaluation Matrix Summary") + table.add_column("Configuration", style="bold") + table.add_column("Instances") + table.add_column("Completed") + table.add_column("Cost") + table.add_column("API Calls") + table.add_column("GN Tools") + + for run_id, results in sorted(all_results.items()): + total = len(results) + completed = sum(1 for r in results if r.get("submission")) + cost = sum(r.get("cost", 0) for r in results) + calls = sum(r.get("n_calls", 0) for r in results) + gn_calls = sum(r.get("gitnexus_metrics", {}).get("total_tool_calls", 0) for r in results) + + table.add_row( + run_id, + str(total), + f"{completed}/{total}", + f"${cost:.2f}", + str(calls), + str(gn_calls) if gn_calls > 0 else "-", + ) + + console.print(table) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s") + app() diff --git a/gitnexus-claude-plugin/.claude-plugin/plugin.json b/gitnexus-claude-plugin/.claude-plugin/plugin.json new file mode 100644 index 000000000..0772c90df --- /dev/null +++ b/gitnexus-claude-plugin/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gitnexus", + "description": "Code intelligence powered by a knowledge graph. Provides execution flow tracing, blast radius analysis, and augmented search across your codebase.", + "version": "1.0.0", + "author": { + "name": "GitNexus" + }, + "homepage": "https://github.com/nicosxt/gitnexus", + "repository": "https://github.com/nicosxt/gitnexus" +} diff --git a/gitnexus-claude-plugin/hooks/gitnexus-hook.js b/gitnexus-claude-plugin/hooks/gitnexus-hook.js new file mode 100644 index 000000000..67c890ff5 --- /dev/null +++ b/gitnexus-claude-plugin/hooks/gitnexus-hook.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node +/** + * GitNexus Claude Code Hook + * + * PreToolUse handler — intercepts Grep/Glob/Bash searches + * and augments with graph context from the GitNexus index. + * + * NOTE: SessionStart hooks are broken on Windows (Claude Code bug). + * Session context is injected via CLAUDE.md / skills instead. + */ + +const fs = require('fs'); +const path = require('path'); +const { execFileSync } = require('child_process'); + +/** + * Read JSON input from stdin synchronously. + */ +function readInput() { + try { + const data = fs.readFileSync(0, 'utf-8'); + return JSON.parse(data); + } catch { + return {}; + } +} + +/** + * Check if a directory (or ancestor) has a .gitnexus index. + */ +function findGitNexusIndex(startDir) { + let dir = startDir || process.cwd(); + for (let i = 0; i < 5; i++) { + if (fs.existsSync(path.join(dir, '.gitnexus'))) { + return true; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return false; +} + +/** + * Extract search pattern from tool input. + */ +function extractPattern(toolName, toolInput) { + if (toolName === 'Grep') { + return toolInput.pattern || null; + } + + if (toolName === 'Glob') { + const raw = toolInput.pattern || ''; + const match = raw.match(/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/); + return match ? match[1] : null; + } + + if (toolName === 'Bash') { + const cmd = toolInput.command || ''; + if (!/\brg\b|\bgrep\b/.test(cmd)) return null; + + const tokens = cmd.split(/\s+/); + let foundCmd = false; + let skipNext = false; + const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']); + + for (const token of tokens) { + if (skipNext) { skipNext = false; continue; } + if (!foundCmd) { + if (/\brg$|\bgrep$/.test(token)) foundCmd = true; + continue; + } + if (token.startsWith('-')) { + if (flagsWithValues.has(token)) skipNext = true; + continue; + } + const cleaned = token.replace(/['"]/g, ''); + return cleaned.length >= 3 ? cleaned : null; + } + return null; + } + + return null; +} + +function main() { + try { + const input = readInput(); + const hookEvent = input.hook_event_name || ''; + + if (hookEvent !== 'PreToolUse') return; + + const cwd = input.cwd || process.cwd(); + if (!findGitNexusIndex(cwd)) return; + + const toolName = input.tool_name || ''; + const toolInput = input.tool_input || {}; + + if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Bash') return; + + const pattern = extractPattern(toolName, toolInput); + if (!pattern || pattern.length < 3) return; + + const result = execFileSync( + 'gitnexus', + ['augment', pattern], + { encoding: 'utf-8', timeout: 8000, cwd, stdio: ['pipe', 'pipe', 'pipe'] } + ); + + if (result && result.trim()) { + console.log(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + additionalContext: result.trim() + } + })); + } + } catch { + // Graceful failure + } +} + +main(); diff --git a/gitnexus-claude-plugin/hooks/hooks.json b/gitnexus-claude-plugin/hooks/hooks.json new file mode 100644 index 000000000..2e9cb49b2 --- /dev/null +++ b/gitnexus-claude-plugin/hooks/hooks.json @@ -0,0 +1,17 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Grep|Glob|Bash", + "hooks": [ + { + "type": "command", + "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/gitnexus-hook.js", + "timeout": 10, + "statusMessage": "Enriching with GitNexus graph context..." + } + ] + } + ] + } +} diff --git a/gitnexus-claude-plugin/hooks/pre-tool-use.sh b/gitnexus-claude-plugin/hooks/pre-tool-use.sh new file mode 100644 index 000000000..3c1af3bc0 --- /dev/null +++ b/gitnexus-claude-plugin/hooks/pre-tool-use.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# GitNexus PreToolUse hook for Claude Code +# Intercepts Grep/Glob/Bash searches and augments with graph context. +# Receives JSON on stdin with { tool_name, tool_input, cwd, ... } +# Returns JSON with additionalContext for graph-enriched results. + +INPUT=$(cat) + +TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null) +CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null) + +# Extract search pattern based on tool type +PATTERN="" + +case "$TOOL_NAME" in + Grep) + PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty' 2>/dev/null) + ;; + Glob) + # Glob patterns are file paths, not search terms — extract meaningful part + RAW=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty' 2>/dev/null) + # Strip glob syntax to get the meaningful name (e.g., "**/*.ts" → skip, "auth*.ts" → "auth") + PATTERN=$(echo "$RAW" | sed -n 's/.*[*\/]\([a-zA-Z][a-zA-Z0-9_-]*\).*/\1/p') + ;; + Bash) + CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null) + # Only augment grep/rg commands + if echo "$CMD" | grep -qE '\brg\b|\bgrep\b'; then + # Extract pattern from rg/grep + if echo "$CMD" | grep -qE '\brg\b'; then + PATTERN=$(echo "$CMD" | sed -n "s/.*\brg\s\+\(--[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") + elif echo "$CMD" | grep -qE '\bgrep\b'; then + PATTERN=$(echo "$CMD" | sed -n "s/.*\bgrep\s\+\(-[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") + fi + fi + ;; + *) + # Not a search tool — skip + exit 0 + ;; +esac + +# Skip if pattern too short or empty +if [ -z "$PATTERN" ] || [ ${#PATTERN} -lt 3 ]; then + exit 0 +fi + +# Check if we're in a GitNexus-indexed repo +dir="${CWD:-$PWD}" +found=false +for i in 1 2 3 4 5; do + if [ -d "$dir/.gitnexus" ]; then + found=true + break + fi + parent="$(dirname "$dir")" + [ "$parent" = "$dir" ] && break + dir="$parent" +done + +if [ "$found" = false ]; then + exit 0 +fi + +# Run gitnexus augment — must be fast (<500ms target) +RESULT=$(cd "$CWD" && npx -y gitnexus augment "$PATTERN" 2>/dev/null) + +if [ -n "$RESULT" ]; then + ESCAPED=$(echo "$RESULT" | jq -Rs .) + jq -n --argjson ctx "$ESCAPED" '{ + hookSpecificOutput: { + hookEventName: "PreToolUse", + additionalContext: $ctx + } + }' +else + exit 0 +fi diff --git a/gitnexus-claude-plugin/hooks/session-start.js b/gitnexus-claude-plugin/hooks/session-start.js new file mode 100644 index 000000000..86157d354 --- /dev/null +++ b/gitnexus-claude-plugin/hooks/session-start.js @@ -0,0 +1,41 @@ +// GitNexus SessionStart hook for Claude Code +// Fires on session startup. Stdout is injected into Claude's context. +// Checks if the current directory has a GitNexus index. + +const fs = require('fs'); +const path = require('path'); + +let dir = process.cwd(); +let found = false; +for (let i = 0; i < 5; i++) { + if (fs.existsSync(path.join(dir, '.gitnexus'))) { + found = true; + break; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; +} + +if (!found) { + process.exit(0); +} + +process.stdout.write(`## GitNexus Code Intelligence + +This codebase is indexed by GitNexus, providing a knowledge graph with execution flows, relationships, and semantic search. + +**Available MCP Tools:** +- \`query\` — Process-grouped code intelligence (execution flows related to a concept) +- \`context\` — 360-degree symbol view (categorized refs, process participation) +- \`impact\` — Blast radius analysis (what breaks if you change a symbol) +- \`detect_changes\` — Git-diff impact analysis (what do your changes affect) +- \`rename\` — Multi-file coordinated rename with confidence tags +- \`cypher\` — Raw graph queries +- \`list_repos\` — Discover indexed repos + +**Quick Start:** READ \`gitnexus://repo/{name}/context\` for codebase overview, then use \`query\` to find execution flows. + +**Resources:** \`gitnexus://repo/{name}/context\` (overview), \`/processes\` (execution flows), \`/schema\` (for Cypher) +`); +process.exit(0); diff --git a/gitnexus-claude-plugin/hooks/session-start.sh b/gitnexus-claude-plugin/hooks/session-start.sh new file mode 100644 index 000000000..8960dd376 --- /dev/null +++ b/gitnexus-claude-plugin/hooks/session-start.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# GitNexus SessionStart hook for Claude Code +# Fires on session startup. Stdout is injected into Claude's context. +# Checks if the current directory has a GitNexus index. + +dir="$PWD" +found=false +for i in 1 2 3 4 5; do + if [ -d "$dir/.gitnexus" ]; then + found=true + break + fi + parent="$(dirname "$dir")" + [ "$parent" = "$dir" ] && break + dir="$parent" +done + +if [ "$found" = false ]; then + exit 0 +fi + +# Inject GitNexus context — this stdout goes directly into Claude's context +cat << 'EOF' +## GitNexus Code Intelligence + +This codebase is indexed by GitNexus, providing a knowledge graph with execution flows, relationships, and semantic search. + +**Available MCP Tools:** +- `query` — Process-grouped code intelligence (execution flows related to a concept) +- `context` — 360-degree symbol view (categorized refs, process participation) +- `impact` — Blast radius analysis (what breaks if you change a symbol) +- `detect_changes` — Git-diff impact analysis (what do your changes affect) +- `rename` — Multi-file coordinated rename with confidence tags +- `cypher` — Raw graph queries +- `list_repos` — Discover indexed repos + +**Quick Start:** READ `gitnexus://repo/{name}/context` for codebase overview, then use `query` to find execution flows. + +**Resources:** `gitnexus://repo/{name}/context` (overview), `/processes` (execution flows), `/schema` (for Cypher) +EOF + +exit 0 diff --git a/gitnexus-claude-plugin/skills/debugging/SKILL.md b/gitnexus-claude-plugin/skills/debugging/SKILL.md new file mode 100644 index 000000000..3b945835b --- /dev/null +++ b/gitnexus-claude-plugin/skills/debugging/SKILL.md @@ -0,0 +1,85 @@ +--- +name: gitnexus-debugging +description: Trace bugs through call chains using knowledge graph +--- + +# Debugging with GitNexus + +## When to Use +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. gitnexus_query({query: ""}) → Find related execution flows +2. gitnexus_context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] gitnexus_query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] gitnexus_context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] gitnexus_cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +|---------|-------------------| +| Error message | `gitnexus_query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**gitnexus_query** — find code related to error: +``` +gitnexus_query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**gitnexus_context** — full context for a suspect: +``` +gitnexus_context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**gitnexus_cypher** — custom call chain traces: +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. gitnexus_query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. gitnexus_context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/gitnexus-claude-plugin/skills/exploring/SKILL.md b/gitnexus-claude-plugin/skills/exploring/SKILL.md new file mode 100644 index 000000000..2214c289c --- /dev/null +++ b/gitnexus-claude-plugin/skills/exploring/SKILL.md @@ -0,0 +1,75 @@ +--- +name: gitnexus-exploring +description: Navigate unfamiliar code using GitNexus knowledge graph +--- + +# Exploring Codebases with GitNexus + +## When to Use +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. gitnexus_query({query: ""}) → Find related execution flows +4. gitnexus_context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] gitnexus_query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] gitnexus_context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +|----------|-------------| +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**gitnexus_query** — find execution flows related to a concept: +``` +gitnexus_query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**gitnexus_context** — 360-degree view of a symbol: +``` +gitnexus_context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. gitnexus_query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. gitnexus_context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/gitnexus-claude-plugin/skills/impact-analysis/SKILL.md b/gitnexus-claude-plugin/skills/impact-analysis/SKILL.md new file mode 100644 index 000000000..bb5f51fcc --- /dev/null +++ b/gitnexus-claude-plugin/skills/impact-analysis/SKILL.md @@ -0,0 +1,94 @@ +--- +name: gitnexus-impact-analysis +description: Analyze blast radius before making code changes +--- + +# Impact Analysis with GitNexus + +## When to Use +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. gitnexus_detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] gitnexus_detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +|-------|-----------|---------| +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +|----------|------| +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**gitnexus_impact** — the primary tool for symbol blast radius: +``` +gitnexus_impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**gitnexus_detect_changes** — git-diff based impact analysis: +``` +gitnexus_detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. gitnexus_impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/gitnexus-claude-plugin/skills/refactoring/SKILL.md b/gitnexus-claude-plugin/skills/refactoring/SKILL.md new file mode 100644 index 000000000..23f4d1130 --- /dev/null +++ b/gitnexus-claude-plugin/skills/refactoring/SKILL.md @@ -0,0 +1,113 @@ +--- +name: gitnexus-refactoring +description: Plan safe refactors using blast radius and dependency mapping +--- + +# Refactoring with GitNexus + +## 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 + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents +2. gitnexus_query({query: "X"}) → Find execution flows involving X +3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklists + +### Rename Symbol +``` +- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits +- [ ] gitnexus_detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module +``` +- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs +- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service +``` +- [ ] gitnexus_context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**gitnexus_rename** — automated multi-file rename: +``` +gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**gitnexus_impact** — map all dependents first: +``` +gitnexus_impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**gitnexus_detect_changes** — verify your changes after refactoring: +``` +gitnexus_detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**gitnexus_cypher** — custom reference queries: +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +|-------------|------------| +| Many callers (>5) | Use gitnexus_rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | gitnexus_query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. gitnexus_detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/gitnexus-cursor-integration/hooks/augment-shell.sh b/gitnexus-cursor-integration/hooks/augment-shell.sh new file mode 100644 index 000000000..48ea185b0 --- /dev/null +++ b/gitnexus-cursor-integration/hooks/augment-shell.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# GitNexus beforeShellExecution hook for Cursor +# Receives JSON on stdin with { command, cwd, timeout } +# Returns JSON on stdout with { permission, agent_message } +# +# Extracts search pattern from grep/rg commands, runs gitnexus augment, +# and injects the enriched context via agent_message. + +INPUT=$(cat) + +COMMAND=$(echo "$INPUT" | jq -r '.command // empty' 2>/dev/null) + +if [ -z "$COMMAND" ]; then + echo '{"permission":"allow"}' + exit 0 +fi + +# Skip non-search commands +case "$COMMAND" in + cd\ *|npm\ *|yarn\ *|pnpm\ *|git\ commit*|git\ push*|git\ pull*|mkdir\ *|rm\ *|cp\ *|mv\ *|echo\ *|cat\ *) + echo '{"permission":"allow"}' + exit 0 + ;; +esac + +# Extract search pattern from rg/grep commands +PATTERN="" +if echo "$COMMAND" | grep -qE '\brg\b'; then + PATTERN=$(echo "$COMMAND" | sed -n "s/.*\brg\s\+\(--[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") +elif echo "$COMMAND" | grep -qE '\bgrep\b'; then + PATTERN=$(echo "$COMMAND" | sed -n "s/.*\bgrep\s\+\(-[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") +fi + +if [ -z "$PATTERN" ] || [ ${#PATTERN} -lt 3 ]; then + echo '{"permission":"allow"}' + exit 0 +fi + +# Run gitnexus augment +RESULT=$(npx -y gitnexus augment "$PATTERN" 2>/dev/null) + +if [ -n "$RESULT" ]; then + # Escape for JSON + ESCAPED=$(echo "$RESULT" | jq -Rs .) + echo "{\"permission\":\"allow\",\"agent_message\":$ESCAPED}" +else + echo '{"permission":"allow"}' +fi + +exit 0 diff --git a/gitnexus-cursor-integration/hooks/hooks.json b/gitnexus-cursor-integration/hooks/hooks.json new file mode 100644 index 000000000..ede0dfbf3 --- /dev/null +++ b/gitnexus-cursor-integration/hooks/hooks.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "hooks": { + "beforeShellExecution": [ + { + "command": "./hooks/augment-shell.sh", + "timeout": 5, + "matcher": "\\brg\\b|\\bgrep\\b" + } + ] + } +} diff --git a/gitnexus-cursor-integration/skills/gitnexus-debugging/SKILL.md b/gitnexus-cursor-integration/skills/gitnexus-debugging/SKILL.md new file mode 100644 index 000000000..3b945835b --- /dev/null +++ b/gitnexus-cursor-integration/skills/gitnexus-debugging/SKILL.md @@ -0,0 +1,85 @@ +--- +name: gitnexus-debugging +description: Trace bugs through call chains using knowledge graph +--- + +# Debugging with GitNexus + +## When to Use +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. gitnexus_query({query: ""}) → Find related execution flows +2. gitnexus_context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] gitnexus_query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] gitnexus_context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] gitnexus_cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +|---------|-------------------| +| Error message | `gitnexus_query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**gitnexus_query** — find code related to error: +``` +gitnexus_query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**gitnexus_context** — full context for a suspect: +``` +gitnexus_context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**gitnexus_cypher** — custom call chain traces: +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. gitnexus_query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. gitnexus_context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/gitnexus-cursor-integration/skills/gitnexus-exploring/SKILL.md b/gitnexus-cursor-integration/skills/gitnexus-exploring/SKILL.md new file mode 100644 index 000000000..2214c289c --- /dev/null +++ b/gitnexus-cursor-integration/skills/gitnexus-exploring/SKILL.md @@ -0,0 +1,75 @@ +--- +name: gitnexus-exploring +description: Navigate unfamiliar code using GitNexus knowledge graph +--- + +# Exploring Codebases with GitNexus + +## When to Use +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. gitnexus_query({query: ""}) → Find related execution flows +4. gitnexus_context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] gitnexus_query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] gitnexus_context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +|----------|-------------| +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**gitnexus_query** — find execution flows related to a concept: +``` +gitnexus_query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**gitnexus_context** — 360-degree view of a symbol: +``` +gitnexus_context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. gitnexus_query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. gitnexus_context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/gitnexus-cursor-integration/skills/gitnexus-impact-analysis/SKILL.md b/gitnexus-cursor-integration/skills/gitnexus-impact-analysis/SKILL.md new file mode 100644 index 000000000..bb5f51fcc --- /dev/null +++ b/gitnexus-cursor-integration/skills/gitnexus-impact-analysis/SKILL.md @@ -0,0 +1,94 @@ +--- +name: gitnexus-impact-analysis +description: Analyze blast radius before making code changes +--- + +# Impact Analysis with GitNexus + +## When to Use +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. gitnexus_detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] gitnexus_detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +|-------|-----------|---------| +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +|----------|------| +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**gitnexus_impact** — the primary tool for symbol blast radius: +``` +gitnexus_impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**gitnexus_detect_changes** — git-diff based impact analysis: +``` +gitnexus_detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. gitnexus_impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/gitnexus-cursor-integration/skills/gitnexus-refactoring/SKILL.md b/gitnexus-cursor-integration/skills/gitnexus-refactoring/SKILL.md new file mode 100644 index 000000000..23f4d1130 --- /dev/null +++ b/gitnexus-cursor-integration/skills/gitnexus-refactoring/SKILL.md @@ -0,0 +1,113 @@ +--- +name: gitnexus-refactoring +description: Plan safe refactors using blast radius and dependency mapping +--- + +# Refactoring with GitNexus + +## 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 + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents +2. gitnexus_query({query: "X"}) → Find execution flows involving X +3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklists + +### Rename Symbol +``` +- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits +- [ ] gitnexus_detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module +``` +- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs +- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service +``` +- [ ] gitnexus_context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**gitnexus_rename** — automated multi-file rename: +``` +gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**gitnexus_impact** — map all dependents first: +``` +gitnexus_impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**gitnexus_detect_changes** — verify your changes after refactoring: +``` +gitnexus_detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**gitnexus_cypher** — custom reference queries: +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +|-------------|------------| +| Many callers (>5) | Use gitnexus_rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | gitnexus_query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. gitnexus_detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/gitnexus-mcp/package-lock.json b/gitnexus-mcp/package-lock.json deleted file mode 100644 index 9798a4729..000000000 --- a/gitnexus-mcp/package-lock.json +++ /dev/null @@ -1,1762 +0,0 @@ -{ - "name": "gitnexus-mcp", - "version": "0.1.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "gitnexus-mcp", - "version": "0.1.1", - "license": "MIT", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.0.0", - "uuid": "^13.0.0", - "ws": "^8.16.0" - }, - "bin": { - "gitnexus-mcp": "dist/cli.js" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "@types/uuid": "^10.0.0", - "@types/ws": "^8.5.10", - "tsx": "^4.0.0", - "typescript": "^5.4.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz", - "integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==", - "license": "MIT", - "dependencies": { - "@hono/node-server": "^1.19.7", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "jose": "^6.1.1", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, - "node_modules/@types/node": { - "version": "20.19.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", - "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", - "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hono": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", - "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16.9.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/pkce-challenge": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/zod": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", - "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - } - } -} diff --git a/gitnexus-mcp/package.json b/gitnexus-mcp/package.json deleted file mode 100644 index 037571f93..000000000 --- a/gitnexus-mcp/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "gitnexus-mcp", - "version": "0.2.0", - "description": "MCP server for GitNexus code intelligence - connect Cursor, Claude, and other AI agents to your codebase", - "author": "Abhigyan Patwari", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/abhigyanpatwari/GitNexus" - }, - "keywords": [ - "mcp", - "model-context-protocol", - "code-intelligence", - "cursor", - "claude", - "ai-agent", - "gitnexus" - ], - "type": "module", - "bin": { - "gitnexus-mcp": "./dist/cli.js" - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsc", - "dev": "tsx watch src/cli.ts", - "prepublishOnly": "npm run build" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.0.0", - "uuid": "^13.0.0", - "ws": "^8.16.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "@types/uuid": "^10.0.0", - "@types/ws": "^8.5.10", - "tsx": "^4.0.0", - "typescript": "^5.4.0" - }, - "engines": { - "node": ">=18.0.0" - } -} \ No newline at end of file diff --git a/gitnexus-mcp/src/bridge/protocol.ts b/gitnexus-mcp/src/bridge/protocol.ts deleted file mode 100644 index 1d0d63fe4..000000000 --- a/gitnexus-mcp/src/bridge/protocol.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Bridge Protocol Types - * - * JSON-RPC-like protocol for communication between bridge and browser. - */ - -export interface BridgeMessage { - id: string; - type?: 'register_peer' | 'tool_call' | 'tool_result' | 'agent_info' | 'handshake' | 'handshake_ack' | 'context'; - method?: string; - params?: any; - result?: any; - error?: { - code?: number; - message: string; - }; - agentName?: string; - peerId?: string; -} - -export type ToolCallRequest = BridgeMessage & { method: string }; -export type ToolCallResponse = BridgeMessage & ({ result: any } | { error: any }); - -/** - * Check if message is a request (has method) - */ -export function isRequest(msg: BridgeMessage): msg is ToolCallRequest { - return typeof msg.method === 'string'; -} - -/** - * Check if message is a response (has result or error) - */ -export function isResponse(msg: BridgeMessage): msg is ToolCallResponse { - return 'result' in msg || 'error' in msg; -} diff --git a/gitnexus-mcp/src/bridge/websocket-server.ts b/gitnexus-mcp/src/bridge/websocket-server.ts deleted file mode 100644 index 450414941..000000000 --- a/gitnexus-mcp/src/bridge/websocket-server.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { WebSocketServer, WebSocket } from 'ws'; -import { createServer as createNetServer } from 'net'; -import { BridgeMessage, isRequest, isResponse } from './protocol.js'; -import { v4 as uuidv4 } from 'uuid'; - -/** - * Codebase context sent from the GitNexus browser app - */ -export interface CodebaseContext { - projectName: string; - stats: { - fileCount: number; - functionCount: number; - classCount: number; - interfaceCount: number; - methodCount: number; - }; - hotspots: Array<{ - name: string; - type: string; - filePath: string; - connections: number; - }>; - folderTree: string; -} - -/** - * Check if a Port is available - */ -async function isPortAvailable(port: number): Promise { - return new Promise((resolve) => { - const server = createNetServer(); - server.once('error', () => resolve(false)); - server.once('listening', () => { - server.close(); - resolve(true); - }); - server.listen(port); - }); -} - -export class WebSocketBridge { - private wss: WebSocketServer | null = null; // Used if we are the Hub - private client: WebSocket | null = null; // Used if we are a Peer (connecting to Hub), OR if we are Hub (clients connecting to us) - - // Hub State - private browserClient: WebSocket | null = null; - private peerClients: Map = new Map(); - - // Common State - private pendingRequests: Map void, reject: (err: any) => void }> = new Map(); - private requestId = 0; - private started = false; - private _context: any | null = null; // CodebaseContext - private contextListeners: Set<(context: any | null) => void> = new Set(); - private agentName: string; - private isHub = false; - private port = 54319; - - constructor(port: number = 54319, agentName?: string) { - this.port = port; - this.agentName = agentName || process.env.GITNEXUS_AGENT || this.detectAgent(); - } - - private detectAgent(): string { - if (process.env.CURSOR_SESSION_ID) return 'Cursor'; - if (process.env.CLAUDE_CODE) return 'Claude Code'; - if (process.env.WINDSURF_SESSION) return 'Windsurf'; - return 'Unknown Agent'; - } - - async start(): Promise { - const available = await isPortAvailable(this.port); - - if (available) { - return this.startAsHub(); - } else { - return this.startAsPeer(); - } - } - - // ------------------------------------------------------------------------- - // Hub Implementation (Master) - // ------------------------------------------------------------------------- - - private async startAsHub(): Promise { - console.error(`Starting as MCP Hub on port ${this.port}`); - this.isHub = true; - - return new Promise((resolve) => { - this.wss = new WebSocketServer({ port: this.port }); - - this.wss.on('connection', (ws, req) => { - // Security: Origin check could go here if req.headers.origin available - - ws.on('message', (data) => this.handleHubMessage(ws, data)); - ws.on('close', () => this.handleHubDisconnect(ws)); - ws.on('error', (err) => console.error('Hub client error:', err)); - }); - - this.wss.on('listening', () => { - this.started = true; - resolve(true); - }); - - this.wss.on('error', (err) => { - console.error('Hub server error:', err); - resolve(false); - }); - }); - } - - private handleHubMessage(ws: WebSocket, data: any) { - try { - const msg: BridgeMessage = JSON.parse(data.toString()); - - if (msg.type === 'handshake') { - // Peer verifying we are GitNexus - ws.send(JSON.stringify({ type: 'handshake_ack', id: msg.id })); - return; - } - - if (msg.type === 'register_peer') { - // Peer registering itself - const peerId = uuidv4(); - this.peerClients.set(peerId, ws); - (ws as any).peerId = peerId; - (ws as any).agentName = msg.agentName; - console.error(`Peer connected: ${msg.agentName} (${peerId})`); - - // Forward current context to new peer if available - if (this._context) { - ws.send(JSON.stringify({ type: 'context', params: this._context })); - } - return; - } - - // Handle Context updates (from Browser) - if (msg.type === 'context') { - // Browser identified itself (implicitly) - if (this.browserClient !== ws) { - if (this.browserClient) this.browserClient.close(); - this.browserClient = ws; - console.error('Browser connected to Hub'); - } - - this._context = msg.params; - this.notifyContextListeners(); - - // Broadcast context to all peers - this.broadcastToPeers(msg); - return; - } - - // Handle Tool Calls (Peer/Hub -> Browser) - if (isRequest(msg)) { - // If it came from a ws client (Peer), validation needed? - // We assume it's destined for the Browser - if (this.browserClient && this.browserClient.readyState === WebSocket.OPEN) { - // Attach agent info if missing (for UI) - if (!msg.agentName && (ws as any).agentName) { - msg.agentName = (ws as any).agentName; - } - // Attach peerId so we can route response back - if (!msg.peerId && (ws as any).peerId) { - msg.peerId = (ws as any).peerId; - } - - this.browserClient.send(JSON.stringify(msg)); - } else { - // Browser not connected, fail - if (msg.id) { - ws.send(JSON.stringify({ - id: msg.id, - error: { message: "Browser not connected. Open GitNexus." } - })); - } - } - return; - } - - // Handle Tool Results (Browser -> Peer/Hub) - if (isResponse(msg)) { - // Route to the correct peer - if (msg.peerId && this.peerClients.has(msg.peerId)) { - const peer = this.peerClients.get(msg.peerId); - if (peer?.readyState === WebSocket.OPEN) { - peer.send(JSON.stringify(msg)); - } - } else { - // It might be for Us (the Hub) - this.handleResponseLocal(msg); - } - return; - } - - } catch (e) { - console.error('Hub: Failed to parse message', e); - } - } - - private handleHubDisconnect(ws: WebSocket) { - if (ws === this.browserClient) { - console.error('Browser disconnected from Hub'); - this.browserClient = null; - this._context = null; - this.notifyContextListeners(); - } else { - const peerId = (ws as any).peerId; - if (peerId) { - this.peerClients.delete(peerId); - console.error(`Peer disconnected: ${peerId}`); - } - } - } - - private broadcastToPeers(msg: any) { - for (const client of this.peerClients.values()) { - if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify(msg)); - } - } - } - - // ------------------------------------------------------------------------- - // Peer Implementation (Spoke) - // ------------------------------------------------------------------------- - - private async startAsPeer(): Promise { - console.error(`Port ${this.port} busy. Attempting to connect as Peer...`); - - return new Promise((resolve) => { - const ws = new WebSocket(`ws://localhost:${this.port}`); - - const timeout = setTimeout(() => { - console.error('Handshake timeout. Port is busy by unknown app.'); - ws.close(); - resolve(false); - }, 1000); - - ws.on('open', () => { - // Send Handshake - ws.send(JSON.stringify({ type: 'handshake', id: 'init' })); - }); - - ws.on('message', (data) => { - try { - const msg = JSON.parse(data.toString()); - - // Handshake success? - if (msg.type === 'handshake_ack') { - clearTimeout(timeout); - console.error('Handshake successful. Joining as Peer.'); - - // Register ourselves - ws.send(JSON.stringify({ - type: 'register_peer', - agentName: this.agentName - })); - - this.client = ws; - this.started = true; - resolve(true); - return; - } - - // Normal messages from Hub - this.handlePeerMessage(msg); - - } catch (e) { - // ignore garbage - } - }); - - ws.on('error', (err) => { - console.error('Peer connection error:', err); - resolve(false); - }); - - // If connection fails immediately - ws.on('close', () => { - if (!this.started) resolve(false); - else { - this.client = null; - this._context = null; - this.notifyContextListeners(); - } - }); - }); - } - - private handlePeerMessage(msg: BridgeMessage) { - if (msg.type === 'context') { - this._context = msg.params; - this.notifyContextListeners(); - return; - } - - if (isResponse(msg)) { - this.handleResponseLocal(msg); - } - } - - // ------------------------------------------------------------------------- - // Shared / Public API - // ------------------------------------------------------------------------- - - private handleResponseLocal(msg: any) { - if (msg.id && this.pendingRequests.has(msg.id)) { - const { resolve, reject } = this.pendingRequests.get(msg.id)!; - this.pendingRequests.delete(msg.id); - - if (msg.error) { - // We'll reject the promise so caller knows - reject(new Error(msg.error.message)); - } else { - resolve(msg.result); - } - } - } - - get isConnected(): boolean { - if (this.isHub) { - return this.browserClient !== null && this.browserClient.readyState === WebSocket.OPEN; - } else { - return this.client !== null && this.client.readyState === WebSocket.OPEN; - } - } - - get context(): any { - return this._context; - } - - onContextChange(listener: (context: any) => void) { - this.contextListeners.add(listener); - return () => this.contextListeners.delete(listener); - } - - private notifyContextListeners() { - this.contextListeners.forEach((listener) => listener(this._context)); - } - - async callTool(method: string, params: any): Promise { - if (!this.isConnected) { - if (this.isHub) throw new Error('GitNexus Browser not connected.'); - else throw new Error('GitNexus Hub disonnected.'); - } - - const id = `req_${++this.requestId}`; - - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { resolve, reject }); - - const msg: BridgeMessage = { - id, - method, - params, - agentName: this.agentName, - // type is implicitly request because of method - }; - - if (this.isHub) { - // Send directly to browser - if (this.browserClient && this.browserClient.readyState === WebSocket.OPEN) { - this.browserClient.send(JSON.stringify(msg)); - } else { - this.pendingRequests.delete(id); - reject(new Error('Browser not connected')); - } - } else { - // Send to Hub (who forwards to browser) - if (this.client && this.client.readyState === WebSocket.OPEN) { - this.client.send(JSON.stringify(msg)); - } else { - this.pendingRequests.delete(id); - reject(new Error('Hub disconnected')); - } - } - - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(new Error('Request timeout')); - } - }, 30000); - }); - } - - close() { - this.wss?.close(); - this.client?.close(); - } - - disconnect() { - this.close(); - } -} diff --git a/gitnexus-mcp/src/cli.ts b/gitnexus-mcp/src/cli.ts deleted file mode 100644 index 281ecb0fb..000000000 --- a/gitnexus-mcp/src/cli.ts +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env node -/** - * GitNexus MCP CLI - * - * Bridge between external AI agents (Cursor, Claude Code, Windsurf) - * and GitNexus code intelligence running in the browser. - */ - -import { serveCommand } from './commands/serve.js'; -/** - * Minimal CLI: - * - Default: start MCP stdio server + local browser WebSocket bridge - * - Optional: `serve` alias, and `--port ` - * - * This is designed for MCP clients (Cursor/Claude/Windsurf) which spawn this - * process automatically; users should not need to run commands manually. - */ - -function parsePort(argv: string[]): string { - const portFlagIndex = argv.findIndex((a) => a === '--port' || a === '-p'); - if (portFlagIndex !== -1) { - const value = argv[portFlagIndex + 1]; - if (value) return value; - } - // Support `--port=54319` - const portEq = argv.find((a) => a.startsWith('--port=')); - if (portEq) return portEq.split('=')[1] || '54319'; - return '54319'; -} - -async function main() { - const argv = process.argv.slice(2); - const first = argv[0]; - const port = parsePort(argv); - - // Allow `gitnexus-mcp serve` for compatibility, but default to serve anyway - if (!first || first === 'serve') { - await serveCommand({ port }); - return; - } - - // Minimal help for unknown commands - if (first === '--help' || first === '-h') { - // eslint-disable-next-line no-console - console.log('gitnexus-mcp\n\nUsage:\n gitnexus-mcp [serve] [--port ]\n'); - process.exit(0); - } - - // eslint-disable-next-line no-console - console.error(`Unknown command: ${first}`); - // eslint-disable-next-line no-console - console.error('Usage: gitnexus-mcp [serve] [--port ]'); - process.exit(1); -} - -main().catch((err) => { - // eslint-disable-next-line no-console - console.error(err instanceof Error ? err.message : err); - process.exit(1); -}); diff --git a/gitnexus-mcp/src/commands/serve.ts b/gitnexus-mcp/src/commands/serve.ts deleted file mode 100644 index 524e40d60..000000000 --- a/gitnexus-mcp/src/commands/serve.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Serve Command - * - * Starts the MCP server that bridges external AI agents to GitNexus. - * - Listens on stdio for MCP protocol (from AI tools) - * - Hosts a local WebSocket bridge for the GitNexus browser app - */ - -import { startMCPServer } from '../mcp/server.js'; -import { WebSocketBridge } from '../bridge/websocket-server.js'; - -interface ServeOptions { - port: string; -} - -export async function serveCommand(options: ServeOptions) { - const port = parseInt(options.port, 10); - - // Start local WebSocket bridge (browser connects to ws://localhost:) - const client = new WebSocketBridge(port); - const started = await client.start(); - - if (!started) { - console.error(`Failed to start GitNexus browser bridge on port ${port}.`); - console.error('Another process is already using this port.'); - process.exit(1); - } - - // Start MCP server on stdio (AI tools connect here) - await startMCPServer(client); -} diff --git a/gitnexus-mcp/src/mcp/server.ts b/gitnexus-mcp/src/mcp/server.ts deleted file mode 100644 index a5c4ef7e7..000000000 --- a/gitnexus-mcp/src/mcp/server.ts +++ /dev/null @@ -1,226 +0,0 @@ -/** - * MCP Server - * - * Model Context Protocol server that runs on stdio. - * External AI tools (Cursor, Claude Code) spawn this process and - * communicate via stdin/stdout using the MCP protocol. - * - * Exposes: - * - Tools: search, cypher, blastRadius, highlight - * - Resources: codebase context (stats, hotspots, folder tree) - */ - -import { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { - CallToolRequestSchema, - ListToolsRequestSchema, - ListResourcesRequestSchema, - ReadResourceRequestSchema, -} from '@modelcontextprotocol/sdk/types.js'; -import { GITNEXUS_TOOLS } from './tools.js'; -import type { CodebaseContext } from '../bridge/websocket-server.js'; - -// Interface for anything that can call tools (DaemonClient or WebSocketBridge) -interface ToolCaller { - callTool(method: string, params: any): Promise; - disconnect?(): void; - context?: CodebaseContext | null; - onContextChange?: (listener: (context: CodebaseContext | null) => void) => () => void; -} - -/** - * Format context as markdown for the resource - */ -function formatContextAsMarkdown(context: CodebaseContext): string { - const { projectName, stats, hotspots, folderTree } = context; - - const lines: string[] = []; - - lines.push(`# GitNexus: ${projectName}`); - lines.push(''); - lines.push('This codebase is currently loaded in GitNexus. Use the tools below to explore it.'); - lines.push(''); - - // Stats - lines.push('## 📊 Statistics'); - lines.push(`- **Files**: ${stats.fileCount}`); - lines.push(`- **Functions**: ${stats.functionCount}`); - if (stats.classCount > 0) lines.push(`- **Classes**: ${stats.classCount}`); - if (stats.interfaceCount > 0) lines.push(`- **Interfaces**: ${stats.interfaceCount}`); - if (stats.methodCount > 0) lines.push(`- **Methods**: ${stats.methodCount}`); - lines.push(''); - - // Hotspots - if (hotspots.length > 0) { - lines.push('## 🔥 Hotspots (Most Connected Nodes)'); - lines.push(''); - hotspots.forEach(h => { - lines.push(`- \`${h.name}\` (${h.type}) — ${h.connections} connections — ${h.filePath}`); - }); - lines.push(''); - } - - // Folder tree - if (folderTree) { - lines.push('## 📁 Project Structure'); - lines.push('```'); - lines.push(projectName + '/'); - lines.push(folderTree); - lines.push('```'); - lines.push(''); - } - - // Usage hints - lines.push('## 🛠️ Available Tools'); - lines.push(''); - lines.push('- **search**: Semantic + keyword search across codebase'); - lines.push('- **cypher**: Execute Cypher queries on knowledge graph'); - lines.push('- **grep**: Regex pattern search in files'); - lines.push('- **read**: Read file contents'); - lines.push('- **explore**: Deep dive on symbol, cluster, or process'); - lines.push('- **overview**: Codebase map (all clusters + processes)'); - lines.push('- **impact**: Analyze change impact (upstream/downstream)'); - lines.push('- **highlight**: Visualize nodes in graph'); - lines.push(''); - lines.push('## 📝 Graph Schema'); - lines.push(''); - lines.push('**Node Types**: File, Folder, Function, Class, Interface, Method, Community, Process'); - lines.push(''); - lines.push('**Relation**: `CodeRelation` with `type` property:'); - lines.push('- CALLS, IMPORTS, EXTENDS, IMPLEMENTS, CONTAINS, DEFINES'); - lines.push('- MEMBER_OF (symbol → community), STEP_IN_PROCESS (symbol → process)'); - lines.push(''); - lines.push('**Example Cypher Queries**:'); - lines.push('```cypher'); - lines.push('MATCH (f:Function) RETURN f.name LIMIT 10'); - lines.push("MATCH (f:File)-[:CodeRelation {type: 'IMPORTS'}]->(g:File) RETURN f.name, g.name"); - lines.push("MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) RETURN c.label, count(s)"); - lines.push('```'); - - return lines.join('\n'); -} - -export async function startMCPServer(client: ToolCaller): Promise { - const server = new Server( - { - name: 'gitnexus', - version: '0.1.0', - }, - { - capabilities: { - tools: {}, - resources: {}, - }, - } - ); - - // Handle list resources request - server.setRequestHandler(ListResourcesRequestSchema, async () => { - const context = client.context; - - if (!context) { - return { resources: [] }; - } - - return { - resources: [ - { - uri: 'gitnexus://codebase/context', - name: `GitNexus: ${context.projectName}`, - description: `Codebase context for ${context.projectName} (${context.stats.fileCount} files, ${context.stats.functionCount} functions)`, - mimeType: 'text/markdown', - }, - ], - }; - }); - - // Handle read resource request - server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - const { uri } = request.params; - - if (uri === 'gitnexus://codebase/context') { - const context = client.context; - - if (!context) { - return { - contents: [ - { - uri, - mimeType: 'text/plain', - text: 'No codebase loaded. Open GitNexus in your browser and load a repository.', - }, - ], - }; - } - - return { - contents: [ - { - uri, - mimeType: 'text/markdown', - text: formatContextAsMarkdown(context), - }, - ], - }; - } - - throw new Error(`Unknown resource: ${uri}`); - }); - - // Handle list tools request - server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: GITNEXUS_TOOLS.map((tool) => ({ - name: tool.name, - description: tool.description, - inputSchema: tool.inputSchema, - })), - })); - - // Handle tool calls - server.setRequestHandler(CallToolRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - try { - // Forward the tool call to the browser via daemon - const result = await client.callTool(name, args); - - return { - content: [ - { - type: 'text', - text: typeof result === 'string' ? result : JSON.stringify(result, null, 2), - }, - ], - }; - } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return { - content: [ - { - type: 'text', - text: `Error: ${message}`, - }, - ], - isError: true, - }; - } - }); - - // Connect to stdio transport - const transport = new StdioServerTransport(); - await server.connect(transport); - - // Handle graceful shutdown - process.on('SIGINT', async () => { - client.disconnect?.(); - await server.close(); - process.exit(0); - }); - - process.on('SIGTERM', async () => { - client.disconnect?.(); - await server.close(); - process.exit(0); - }); -} diff --git a/gitnexus-mcp/src/mcp/tools.ts b/gitnexus-mcp/src/mcp/tools.ts deleted file mode 100644 index 84ab18d72..000000000 --- a/gitnexus-mcp/src/mcp/tools.ts +++ /dev/null @@ -1,225 +0,0 @@ -/** - * MCP Tool Definitions - * - * Defines the tools that GitNexus exposes to external AI agents. - * Each tool has a rich description with examples to help agents use them correctly. - */ - -export interface ToolDefinition { - name: string; - description: string; - inputSchema: { - type: 'object'; - properties: Record; - required: string[]; - }; -} - -export const GITNEXUS_TOOLS: ToolDefinition[] = [ - { - name: 'context', - description: `Get GitNexus codebase context. CALL THIS FIRST before using other tools. - -Returns: -- Project name and stats (files, functions, classes) -- Hotspots (most connected/important nodes) -- Directory structure (TOON format for token efficiency) -- Tool usage guidance - -ALWAYS call this first to understand the codebase before searching or querying.`, - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'search', - description: `Hybrid search (keyword + semantic) across the codebase. -Returns code nodes with their graph connections, grouped by process. - -WHEN TO USE: -- Finding implementations ("where is auth handled?") -- Understanding code flow ("what calls UserService?") -- Locating patterns ("find all API endpoints") - -RETURNS: Array of {name, type, filePath, code, connections[], cluster, processes[]}`, - inputSchema: { - type: 'object', - properties: { - query: { type: 'string', description: 'Natural language or keyword search query' }, - limit: { type: 'number', description: 'Max results to return', default: 10 }, - groupByProcess: { type: 'boolean', description: 'Group results by process', default: true }, - }, - required: ['query'], - }, - }, - { - name: 'cypher', - description: `Execute Cypher query against the code knowledge graph. - -SCHEMA: -- Nodes: File, Folder, Function, Class, Interface, Method, Community, Process -- Edges via CodeRelation.type: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, CONTAINS, DEFINES, MEMBER_OF, STEP_IN_PROCESS - -EXAMPLES: -• Find callers of a function: - MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b:Function {name: "validateUser"}) RETURN a.name, a.filePath - -• Find all functions in a community: - MATCH (f:Function)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community {label: "Auth"}) RETURN f.name - -• Find steps in a process: - MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process {label: "UserLogin"}) RETURN s.name, r.step ORDER BY r.step - -TIPS: -- All relationships use CodeRelation table with 'type' property -- Community = functional cluster detected by Leiden algorithm -- Process = execution flow trace from entry point to terminal`, - inputSchema: { - type: 'object', - properties: { - query: { type: 'string', description: 'Cypher query to execute' }, - }, - required: ['query'], - }, - }, - { - name: 'grep', - description: `Regex search for exact patterns in file contents. - -WHEN TO USE: -- Finding exact strings: error codes, TODOs, specific API keys -- Pattern matching: all console.log, all fetch calls -- Finding imports of specific modules - -BETTER THAN search for: exact matches, regex patterns, case-sensitive - -RETURNS: Array of {filePath, line, lineNumber, match}`, - inputSchema: { - type: 'object', - properties: { - pattern: { type: 'string', description: 'Regex pattern to search for' }, - caseSensitive: { type: 'boolean', description: 'Case-sensitive search', default: false }, - maxResults: { type: 'number', description: 'Max results to return', default: 50 }, - }, - required: ['pattern'], - }, - }, - { - name: 'read', - description: `Read file content from the codebase. - -WHEN TO USE: -- After search/grep to see full context -- To understand implementation details -- Before making changes - -ALWAYS read before concluding - don't guess from names alone. - -RETURNS: {filePath, content, language, lines}`, - inputSchema: { - type: 'object', - properties: { - filePath: { type: 'string', description: 'Path to file to read' }, - startLine: { type: 'number', description: 'Start line (optional)' }, - endLine: { type: 'number', description: 'End line (optional)' }, - }, - required: ['filePath'], - }, - }, - { - name: 'explore', - description: `Deep dive on a symbol, cluster, or process. - -TYPE: symbol | cluster | process - -For SYMBOL: Shows cluster membership, process participation, callers/callees -For CLUSTER: Shows members, cohesion score, processes touching it -For PROCESS: Shows step-by-step trace, clusters traversed, entry/terminal points - -Use after search to understand context of a specific node.`, - inputSchema: { - type: 'object', - properties: { - name: { type: 'string', description: 'Name of symbol, cluster, or process to explore' }, - type: { type: 'string', description: 'Type: symbol, cluster, or process' }, - }, - required: ['name', 'type'], - }, - }, - { - name: 'overview', - description: `Get codebase map showing all clusters and processes. - -Returns: -- All communities (clusters) with member counts and cohesion scores -- All processes with step counts and types (intra/cross-community) -- High-level architectural view - -Use to understand overall codebase structure before diving deep.`, - inputSchema: { - type: 'object', - properties: { - showProcesses: { type: 'boolean', description: 'Include process list', default: true }, - showClusters: { type: 'boolean', description: 'Include cluster list', default: true }, - limit: { type: 'number', description: 'Max items per category', default: 20 }, - }, - required: [], - }, - }, - { - name: 'impact', - description: `Analyze the impact of changing a code element. -Returns all nodes affected by modifying the target, with distance, edge type, and confidence. - -USE BEFORE making changes to understand ripple effects. - -Output includes: -- Affected processes (with step positions) -- Affected clusters (direct/indirect) -- Risk assessment (critical/high/medium/low) -- Callers/dependents grouped by depth - -EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS -Confidence: 100% = certain, <80% = fuzzy match - -Depth groups: -- d=1: WILL BREAK (direct callers/importers) -- d=2: LIKELY AFFECTED (indirect) -- d=3: MAY NEED TESTING (transitive)`, - inputSchema: { - type: 'object', - properties: { - target: { type: 'string', description: 'Name of function, class, or file to analyze' }, - direction: { type: 'string', description: 'upstream (what depends on this) or downstream (what this depends on)' }, - maxDepth: { type: 'number', description: 'Max relationship depth (default: 3)', default: 3 }, - relationTypes: { type: 'array', items: { type: 'string' }, description: 'Filter: CALLS, IMPORTS, EXTENDS, IMPLEMENTS (default: usage-based)' }, - includeTests: { type: 'boolean', description: 'Include test files (default: false)' }, - minConfidence: { type: 'number', description: 'Minimum confidence 0-1 (default: 0.7)' }, - }, - required: ['target', 'direction'], - }, - }, - { - name: 'highlight', - description: `Highlight nodes in the GitNexus graph visualization. -Use after search/analysis to show the user what you found. - -The user will see the nodes glow in the graph view. -Great for visual confirmation of your findings.`, - inputSchema: { - type: 'object', - properties: { - nodeIds: { type: 'array', items: { type: 'string' }, description: 'Array of node IDs to highlight' }, - color: { type: 'string', description: 'Highlight color (optional, default: cyan)' }, - }, - required: ['nodeIds'], - }, - }, -]; diff --git a/gitnexus-mcp/tsconfig.json b/gitnexus-mcp/tsconfig.json deleted file mode 100644 index 6f8152161..000000000 --- a/gitnexus-mcp/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "lib": [ - "ES2022" - ], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file diff --git a/gitnexus-test-setup/.gitignore b/gitnexus-test-setup/.gitignore new file mode 100644 index 000000000..c66cf2d98 --- /dev/null +++ b/gitnexus-test-setup/.gitignore @@ -0,0 +1,7 @@ + +# GitNexus AI Context +.gitnexus-rules.md +.cursorrules +.windsurfrules +CLAUDE.md +.github/copilot-instructions.md diff --git a/gitnexus-web/.gitignore b/gitnexus-web/.gitignore new file mode 100644 index 000000000..c8a733615 --- /dev/null +++ b/gitnexus-web/.gitignore @@ -0,0 +1,2 @@ +.vercel +.env*.local diff --git a/gitnexus/TODO.md b/gitnexus-web/TODO.md similarity index 96% rename from gitnexus/TODO.md rename to gitnexus-web/TODO.md index 4ffce6f43..31e5e39fe 100644 --- a/gitnexus/TODO.md +++ b/gitnexus-web/TODO.md @@ -55,9 +55,9 @@ **Goal:** Group related code into named clusters. ### 1.1 Research & Setup -- [ ] Research JS/WASM implementations of Leiden algorithm - - Options: `graphology-communities-louvain`, custom WASM port - - Constraint: Must run in browser +- [x] Implement Leiden algorithm for community detection + - Vendored from graphology-communities-leiden (unpublished npm, MIT licensed) + - Works in both browser (ESM) and Node.js (CJS) - [ ] Benchmark on sample codebases (100, 1K, 10K nodes) ### 1.2 Schema Updates @@ -324,10 +324,9 @@ interface ImpactResult { ## Technical Notes -### Leiden Algorithm Options -1. **graphology-communities-louvain** (JS, works in browser) -2. **Custom WASM port** (if performance needed) -3. **Simple Louvain** might be sufficient for V1 +### Leiden Algorithm +Implemented using vendored graphology-communities-leiden source (MIT licensed). +The Leiden algorithm guarantees well-connected communities via a refinement phase after each Louvain-style move phase. ### Schema Summary (New Additions) ``` diff --git a/gitnexus/api/proxy.ts b/gitnexus-web/api/proxy.ts similarity index 100% rename from gitnexus/api/proxy.ts rename to gitnexus-web/api/proxy.ts diff --git a/gitnexus/docs/FRAMEWORK_SUPPORT.md b/gitnexus-web/docs/FRAMEWORK_SUPPORT.md similarity index 100% rename from gitnexus/docs/FRAMEWORK_SUPPORT.md rename to gitnexus-web/docs/FRAMEWORK_SUPPORT.md diff --git a/gitnexus/index.html b/gitnexus-web/index.html similarity index 100% rename from gitnexus/index.html rename to gitnexus-web/index.html diff --git a/gitnexus-web/package-lock.json b/gitnexus-web/package-lock.json new file mode 100644 index 000000000..97942ca4d --- /dev/null +++ b/gitnexus-web/package-lock.json @@ -0,0 +1,10203 @@ +{ + "name": "gitnexus", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gitnexus", + "version": "0.0.0", + "dependencies": { + "@huggingface/transformers": "^3.0.0", + "@isomorphic-git/lightning-fs": "^4.6.2", + "@langchain/anthropic": "^1.3.10", + "@langchain/core": "^1.1.15", + "@langchain/google-genai": "^2.1.10", + "@langchain/langgraph": "^1.1.0", + "@langchain/ollama": "^1.2.0", + "@langchain/openai": "^1.2.2", + "@sigma/edge-curve": "^3.1.0", + "@tailwindcss/vite": "^4.1.18", + "axios": "^1.13.2", + "buffer": "^6.0.3", + "comlink": "^4.4.2", + "d3": "^7.9.0", + "graphology": "^0.26.0", + "graphology-indices": "^0.17.0", + "graphology-layout-force": "^0.2.4", + "graphology-layout-forceatlas2": "^0.10.1", + "graphology-layout-noverlap": "^0.4.2", + "graphology-utils": "^2.3.0", + "isomorphic-git": "^1.36.1", + "jszip": "^3.10.1", + "kuzu-wasm": "^0.11.1", + "langchain": "^1.2.10", + "lru-cache": "^11.2.4", + "lucide-react": "^0.562.0", + "mermaid": "^11.12.2", + "minisearch": "^7.2.0", + "mnemonist": "^0.39.0", + "pandemonium": "^2.4.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", + "react-zoom-pan-pinch": "^3.7.0", + "remark-gfm": "^4.0.1", + "sigma": "^3.0.2", + "tailwindcss": "^4.1.18", + "uuid": "^13.0.0", + "vite-plugin-top-level-await": "^1.6.0", + "vite-plugin-wasm": "^3.5.0", + "web-tree-sitter": "^0.20.8", + "zod": "^3.25.76" + }, + "devDependencies": { + "@babel/types": "^7.28.5", + "@types/jszip": "^3.4.0", + "@types/node": "^24.10.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@types/react-syntax-highlighter": "^15.5.13", + "@vercel/node": "^5.5.16", + "@vitejs/plugin-react": "^5.1.0", + "tree-sitter-wasms": "^0.1.13", + "typescript": "^5.4.5", + "vite": "^5.2.0", + "vite-plugin-static-copy": "^3.1.4" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.71.2", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", + "integrity": "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "license": "MIT" + }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@edge-runtime/format": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@edge-runtime/format/-/format-2.2.1.tgz", + "integrity": "sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/node-utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/node-utils/-/node-utils-2.3.0.tgz", + "integrity": "sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/ponyfill": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@edge-runtime/ponyfill/-/ponyfill-2.4.2.tgz", + "integrity": "sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/primitives": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/primitives/-/primitives-4.1.0.tgz", + "integrity": "sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/vm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/vm/-/vm-3.2.0.tgz", + "integrity": "sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@edge-runtime/primitives": "4.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.3.tgz", + "integrity": "sha512-asqfZ4GQS0hD876Uw4qiUb7Tr/V5Q+JZuo2L+BtdrD4U40QU58nIRq3ZSgAzJgT874VLjhGVacaYfrdpXtEvtA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/transformers": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-3.8.1.tgz", + "integrity": "sha512-tsTk4zVjImqdqjS8/AOZg2yNLd1z9S5v+7oUPpXaasDRwEDhB+xnglK1k5cad26lL5/ZIaeREgWWy0bs9y9pPA==", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.5.3", + "onnxruntime-node": "1.21.0", + "onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4", + "sharp": "^0.34.1" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isomorphic-git/idb-keyval": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz", + "integrity": "sha512-r8/AdpiS0/WJCNR/t/gsgL+M8NMVj/ek7s60uz3LmpCaTF2mEVlZJlB01ZzalgYzRLXwSPC92o+pdzjM7PN/pA==", + "license": "Apache-2.0" + }, + "node_modules/@isomorphic-git/lightning-fs": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@isomorphic-git/lightning-fs/-/lightning-fs-4.6.2.tgz", + "integrity": "sha512-RS/oa1UBnoUFe56bsjOEgoUUReYKQzYUlQnbERRRNv9s9KmjyWuuylPV+YgsWirR2oONKaipWYMebVQ8SAe55Q==", + "license": "MIT", + "dependencies": { + "@isomorphic-git/idb-keyval": "3.3.2", + "isomorphic-textencoder": "1.0.1", + "just-debounce-it": "1.1.0", + "just-once": "1.1.0" + }, + "bin": { + "superblocktxt": "src/superblocktxt.js" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@langchain/anthropic": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-1.3.10.tgz", + "integrity": "sha512-VXq5fsEJ4FB5XGrnoG+bfm0I7OlmYLI4jZ6cX9RasyqhGo9wcDyKw1+uEQ1H7Og7jWrTa1bfXCun76wttewJnw==", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.71.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "1.1.15" + } + }, + "node_modules/@langchain/core": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.15.tgz", + "integrity": "sha512-b8RN5DkWAmDAlMu/UpTZEluYwCLpm63PPWniRKlE8ie3KkkE7IuMQ38pf4kV1iaiI+d99BEQa2vafQHfCujsRA==", + "license": "MIT", + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": ">=0.4.0 <1.0.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "uuid": "^10.0.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/google-genai": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-2.1.10.tgz", + "integrity": "sha512-OpiBr2OUzB9Pg20mjLId+vfxJvYurc8TzbElaM/d6KE7aE8DiKCEOuQn5ZSgHTVzZV2g++lcJXw6iZlso4SORA==", + "license": "MIT", + "dependencies": { + "@google/generative-ai": "^0.24.0", + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "1.1.15" + } + }, + "node_modules/@langchain/google-genai/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@langchain/langgraph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.1.0.tgz", + "integrity": "sha512-3n1GL0ZTtr57ZwbYvbi4Th26fwiGogmpFn8OA8UXEpBM2HcpGwcv1+c8YSBJF4XRjlcCzIlXtY+DyrNsvinc6g==", + "license": "MIT", + "dependencies": { + "@langchain/langgraph-checkpoint": "^1.0.0", + "@langchain/langgraph-sdk": "~1.5.4", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": "^1.0.1", + "zod": "^3.25.32 || ^4.2.0", + "zod-to-json-schema": "^3.x" + }, + "peerDependenciesMeta": { + "zod-to-json-schema": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-checkpoint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz", + "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==", + "license": "MIT", + "dependencies": { + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": "^1.0.1" + } + }, + "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph-sdk": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.5.4.tgz", + "integrity": "sha512-eSYqG875c2qvcPwdvBwQH0niTZxt6roMGc2dAWBqCbWCUiUL0X4ftYHg2OqOelsrNE3SO6faLr/m0LIPc9hDwg==", + "license": "MIT", + "dependencies": { + "p-queue": "^9.0.1", + "p-retry": "^7.1.1", + "uuid": "^13.0.0" + }, + "peerDependencies": { + "@langchain/core": "^1.0.1", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", + "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@langchain/langgraph/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/ollama": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@langchain/ollama/-/ollama-1.2.0.tgz", + "integrity": "sha512-OinxIhssKXdDQKnQoBF4TQTMBuMMV5OcNPk4Zze8UjcaSOGngn3CAI1FVbBxl0bTG5ov61w4AoWWsUwOwiSJFw==", + "license": "MIT", + "dependencies": { + "ollama": "^0.6.3", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "^1.0.0" + } + }, + "node_modules/@langchain/ollama/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/openai": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.2.2.tgz", + "integrity": "sha512-ByGtj9nJlyL2UPR7BAxtM34g8JA0qEfDKZq7ZisLW23ju+da1ZRAKogoEqoEHHSxl5fAt2LXcydsIYx0qgCDgg==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^6.10.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "^1.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-virtual": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", + "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sigma/edge-curve": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigma/edge-curve/-/edge-curve-3.1.0.tgz", + "integrity": "sha512-OFWkfAXEsm+X8l1K4K49cC0psB0gQ+gqxKA08HG5piNPdzrDZ5gG9Gza6htZ5AirOVwd/4/uq/gPpD5En+H+3Q==", + "license": "MIT", + "peerDependencies": { + "sigma": ">=3.0.0-beta.10" + } + }, + "node_modules/@swc/core": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz", + "integrity": "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.8", + "@swc/core-darwin-x64": "1.15.8", + "@swc/core-linux-arm-gnueabihf": "1.15.8", + "@swc/core-linux-arm64-gnu": "1.15.8", + "@swc/core-linux-arm64-musl": "1.15.8", + "@swc/core-linux-x64-gnu": "1.15.8", + "@swc/core-linux-x64-musl": "1.15.8", + "@swc/core-win32-arm64-msvc": "1.15.8", + "@swc/core-win32-ia32-msvc": "1.15.8", + "@swc/core-win32-x64-msvc": "1.15.8" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz", + "integrity": "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz", + "integrity": "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz", + "integrity": "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz", + "integrity": "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz", + "integrity": "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz", + "integrity": "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz", + "integrity": "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz", + "integrity": "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz", + "integrity": "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz", + "integrity": "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@swc/wasm": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.15.8.tgz", + "integrity": "sha512-RG2BxGbbsjtddFCo1ghKH6A/BMXbY1eMBfpysV0lJMCpI4DZOjW1BNBnxvBt7YsYmlJtmy5UXIg9/4ekBTFFaQ==", + "license": "Apache-2.0" + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.11.1.tgz", + "integrity": "sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.7", + "minimatch": "^3.0.4", + "mkdirp": "^1.0.4", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jszip": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/jszip/-/jszip-3.4.0.tgz", + "integrity": "sha512-GFHqtQQP3R4NNuvZH3hNCYD0NbyBZ42bkN7kO3NDrU/SnvIZWMS8Bp38XCsRKBT5BXvgm0y1zqpZWp/ZkRzBzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jszip": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.13", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", + "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vercel/build-utils": { + "version": "13.2.11", + "resolved": "https://registry.npmjs.org/@vercel/build-utils/-/build-utils-13.2.11.tgz", + "integrity": "sha512-jbsg78iS8SLpOkLw378bBLchmzeQ+YtPnztMMuEFBORjY1G4lDxiStMacD3xp5HImCAl1wz4dNV4I8jHKd/3Tg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@vercel/error-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@vercel/error-utils/-/error-utils-2.0.3.tgz", + "integrity": "sha512-CqC01WZxbLUxoiVdh9B/poPbNpY9U+tO1N9oWHwTl5YAZxcqXmmWJ8KNMFItJCUUWdY3J3xv8LvAuQv2KZ5YdQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@vercel/nft": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.1.1.tgz", + "integrity": "sha512-mKMGa7CEUcXU75474kOeqHbtvK1kAcu4wiahhmlUenB5JbTQB8wVlDI8CyHR3rpGo0qlzoRWqcDzI41FUoBJCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^13.0.0", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@vercel/node": { + "version": "5.5.23", + "resolved": "https://registry.npmjs.org/@vercel/node/-/node-5.5.23.tgz", + "integrity": "sha512-dDJtroLF4D/H9vRMt/x/qI2bKujMOPbk6aIqRKI9WXddngjKziuHxsjcF3zEm5YXGUYDSC2lEVEFrXPbbP+hhw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@edge-runtime/node-utils": "2.3.0", + "@edge-runtime/primitives": "4.1.0", + "@edge-runtime/vm": "3.2.0", + "@types/node": "16.18.11", + "@vercel/build-utils": "13.2.11", + "@vercel/error-utils": "2.0.3", + "@vercel/nft": "1.1.1", + "@vercel/static-config": "3.1.2", + "async-listen": "3.0.0", + "cjs-module-lexer": "1.2.3", + "edge-runtime": "2.5.9", + "es-module-lexer": "1.4.1", + "esbuild": "0.27.0", + "etag": "1.8.1", + "mime-types": "2.1.35", + "node-fetch": "2.6.9", + "path-to-regexp": "6.1.0", + "path-to-regexp-updated": "npm:path-to-regexp@6.3.0", + "ts-morph": "12.0.0", + "ts-node": "10.9.1", + "typescript": "4.9.5", + "typescript5": "npm:typescript@5.9.3", + "undici": "5.28.4" + } + }, + "node_modules/@vercel/node/node_modules/@types/node": { + "version": "16.18.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vercel/node/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@vercel/static-config": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vercel/static-config/-/static-config-3.1.2.tgz", + "integrity": "sha512-2d+TXr6K30w86a+WbMbGm2W91O0UzO5VeemZYBBUJbCjk/5FLLGIi8aV6RS2+WmaRvtcqNTn2pUA7nCOK3bGcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ajv": "8.6.3", + "json-schema-to-ts": "1.6.4", + "ts-morph": "12.0.0" + } + }, + "node_modules/@vercel/static-config/node_modules/json-schema-to-ts": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-1.6.4.tgz", + "integrity": "sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.6", + "ts-toolbelt": "^6.15.5" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-listen": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.0.tgz", + "integrity": "sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.15", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz", + "integrity": "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chevrotain/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "license": "Apache-2.0" + }, + "node_modules/code-block-writer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-10.1.1.tgz", + "integrity": "sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/console-table-printer": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", + "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", + "license": "MIT", + "dependencies": { + "simple-wcswidth": "^1.1.2" + } + }, + "node_modules/convert-hrtime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-3.0.0.tgz", + "integrity": "sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/edge-runtime": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/edge-runtime/-/edge-runtime-2.5.9.tgz", + "integrity": "sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@edge-runtime/format": "2.2.1", + "@edge-runtime/ponyfill": "2.4.2", + "@edge-runtime/vm": "3.2.0", + "async-listen": "3.0.1", + "mri": "1.2.0", + "picocolors": "1.0.0", + "pretty-ms": "7.0.1", + "signal-exit": "4.0.2", + "time-span": "4.0.0" + }, + "bin": { + "edge-runtime": "dist/cli/index.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/edge-runtime/node_modules/async-listen": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.1.tgz", + "integrity": "sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/edge-runtime/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.0", + "@esbuild/android-arm": "0.27.0", + "@esbuild/android-arm64": "0.27.0", + "@esbuild/android-x64": "0.27.0", + "@esbuild/darwin-arm64": "0.27.0", + "@esbuild/darwin-x64": "0.27.0", + "@esbuild/freebsd-arm64": "0.27.0", + "@esbuild/freebsd-x64": "0.27.0", + "@esbuild/linux-arm": "0.27.0", + "@esbuild/linux-arm64": "0.27.0", + "@esbuild/linux-ia32": "0.27.0", + "@esbuild/linux-loong64": "0.27.0", + "@esbuild/linux-mips64el": "0.27.0", + "@esbuild/linux-ppc64": "0.27.0", + "@esbuild/linux-riscv64": "0.27.0", + "@esbuild/linux-s390x": "0.27.0", + "@esbuild/linux-x64": "0.27.0", + "@esbuild/netbsd-arm64": "0.27.0", + "@esbuild/netbsd-x64": "0.27.0", + "@esbuild/openbsd-arm64": "0.27.0", + "@esbuild/openbsd-x64": "0.27.0", + "@esbuild/openharmony-arm64": "0.27.0", + "@esbuild/sunos-x64": "0.27.0", + "@esbuild/win32-arm64": "0.27.0", + "@esbuild/win32-ia32": "0.27.0", + "@esbuild/win32-x64": "0.27.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "license": "Apache-2.0" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "license": "Apache-2.0" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphology": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.26.0.tgz", + "integrity": "sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0" + }, + "peerDependencies": { + "graphology-types": ">=0.24.0" + } + }, + "node_modules/graphology-indices": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/graphology-indices/-/graphology-indices-0.17.0.tgz", + "integrity": "sha512-A7RXuKQvdqSWOpn7ZVQo4S33O0vCfPBnUSf7FwE0zNCasqwZVUaCXePuWo5HBpWw68KJcwObZDHpFk6HKH6MYQ==", + "license": "MIT", + "dependencies": { + "graphology-utils": "^2.4.2", + "mnemonist": "^0.39.0" + }, + "peerDependencies": { + "graphology-types": ">=0.20.0" + } + }, + "node_modules/graphology-layout-force": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/graphology-layout-force/-/graphology-layout-force-0.2.4.tgz", + "integrity": "sha512-NYZz0YAnDkn5pkm30cvB0IScFoWGtbzJMrqaiH070dYlYJiag12Oc89dbVfaMaVR/w8DMIKxn/ix9Bqj+Umm9Q==", + "license": "MIT", + "dependencies": { + "graphology-utils": "^2.4.2" + }, + "peerDependencies": { + "graphology-types": ">=0.19.0" + } + }, + "node_modules/graphology-layout-forceatlas2": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/graphology-layout-forceatlas2/-/graphology-layout-forceatlas2-0.10.1.tgz", + "integrity": "sha512-ogzBeF1FvWzjkikrIFwxhlZXvD2+wlY54lqhsrWprcdPjopM2J9HoMweUmIgwaTvY4bUYVimpSsOdvDv1gPRFQ==", + "license": "MIT", + "dependencies": { + "graphology-utils": "^2.1.0" + }, + "peerDependencies": { + "graphology-types": ">=0.19.0" + } + }, + "node_modules/graphology-layout-noverlap": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/graphology-layout-noverlap/-/graphology-layout-noverlap-0.4.2.tgz", + "integrity": "sha512-13WwZSx96zim6l1dfZONcqLh3oqyRcjIBsqz2c2iJ3ohgs3605IDWjldH41Gnhh462xGB1j6VGmuGhZ2FKISXA==", + "license": "MIT", + "dependencies": { + "graphology-utils": "^2.3.0" + }, + "peerDependencies": { + "graphology-types": ">=0.19.0" + } + }, + "node_modules/graphology-types": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", + "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", + "license": "MIT", + "peer": true + }, + "node_modules/graphology-utils": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz", + "integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==", + "license": "MIT", + "peerDependencies": { + "graphology-types": ">=0.23.0" + } + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "license": "ISC" + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-observable": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", + "integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isomorphic-git": { + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.36.1.tgz", + "integrity": "sha512-fC8SRT8MwoaXDK8G4z5biPEbqf2WyEJUb2MJ2ftSd39/UIlsnoZxLGux+lae0poLZO4AEcx6aUVOh5bV+P8zFA==", + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^4.0.0", + "sha.js": "^2.4.12", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/isomorphic-textencoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-textencoder/-/isomorphic-textencoder-1.0.1.tgz", + "integrity": "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ==", + "license": "MIT", + "dependencies": { + "fast-text-encoding": "^1.0.0" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tiktoken": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", + "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-debounce-it": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce-it/-/just-debounce-it-1.1.0.tgz", + "integrity": "sha512-87Nnc0qZKgBZuhFZjYVjSraic0x7zwjhaTMrCKlj0QYKH6lh0KbFzVnfu6LHan03NO7J8ygjeBeD0epejn5Zcg==", + "license": "MIT" + }, + "node_modules/just-once": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-once/-/just-once-1.1.0.tgz", + "integrity": "sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==", + "license": "MIT" + }, + "node_modules/katex": { + "version": "0.16.27", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", + "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kuzu-wasm": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/kuzu-wasm/-/kuzu-wasm-0.11.3.tgz", + "integrity": "sha512-+bLOqXgYZJJ2dHJG1y9LTLyb9ZB73eLxErRZahZz2rPokfIdyLaktTJFzJH7wX39hgyukKn8QxeRNobH6gl27g==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "threads": "^1.7.0", + "tiny-worker": "^2.3.0", + "uuid": "^11.0.3" + } + }, + "node_modules/kuzu-wasm/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/langchain": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.2.10.tgz", + "integrity": "sha512-9uVxOJE/RTECvNutQfOLwH7f6R9mcq0G/IMHwA2eptDA86R/Yz2zWMz4vARVFPxPrdSJ9nJFDPAqRQlRFwdHBw==", + "license": "MIT", + "dependencies": { + "@langchain/langgraph": "^1.0.0", + "@langchain/langgraph-checkpoint": "^1.0.0", + "langsmith": ">=0.4.0 <1.0.0", + "uuid": "^10.0.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "1.1.15" + } + }, + "node_modules/langchain/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/langsmith": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.4.7.tgz", + "integrity": "sha512-Esv5g/J8wwRwbGQr10PB9+bLsNk0mWbrXc7nnEreQDhh0azbU57I7epSnT7GC4sS4EOWavhbxk+6p8PTXtreHw==", + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash-es": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", + "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lucide-react": { + "version": "0.562.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", + "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.12.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz", + "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.1", + "@mermaid-js/parser": "^0.6.3", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.18", + "dompurify": "^3.2.5", + "katex": "^0.16.22", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^16.2.1", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "license": "MIT" + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mnemonist": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", + "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.1" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "license": "MIT" + }, + "node_modules/observable-fns": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", + "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==", + "license": "MIT" + }, + "node_modules/ollama": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.6.3.tgz", + "integrity": "sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg==", + "license": "MIT", + "dependencies": { + "whatwg-fetch": "^3.6.20" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.21.0.tgz", + "integrity": "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.21.0.tgz", + "integrity": "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==", + "hasInstallScript": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "global-agent": "^3.0.0", + "onnxruntime-common": "1.21.0", + "tar": "^7.0.1" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.22.0-dev.20250409-89f8206ba4", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.22.0-dev.20250409-89f8206ba4.tgz", + "integrity": "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==", + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { + "version": "1.22.0-dev.20250409-89f8206ba4", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.22.0-dev.20250409-89f8206ba4.tgz", + "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==", + "license": "MIT" + }, + "node_modules/openai": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.16.0.tgz", + "integrity": "sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", + "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", + "license": "MIT", + "dependencies": { + "is-network-error": "^1.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/pandemonium": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/pandemonium/-/pandemonium-2.4.1.tgz", + "integrity": "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==", + "license": "MIT", + "dependencies": { + "mnemonist": "^0.39.2" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", + "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp-updated": { + "name": "path-to-regexp", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-syntax-highlighter": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", + "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^5.0.0" + }, + "engines": { + "node": ">= 16.20.2" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/react-zoom-pan-pinch": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz", + "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==", + "license": "MIT", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/refractor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", + "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^9.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sigma": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sigma/-/sigma-3.0.2.tgz", + "integrity": "sha512-/BUbeOwPGruiBOm0YQQ6ZMcLIZ6tf/W+Jcm7dxZyAX0tK3WP9/sq7/NAWBxPIxVahdGjCJoGwej0Gdrv0DxlQQ==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "graphology-utils": "^2.5.2" + } + }, + "node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-wcswidth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", + "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz", + "integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/threads": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", + "integrity": "sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==", + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0", + "debug": "^4.2.0", + "is-observable": "^2.1.0", + "observable-fns": "^0.6.1" + }, + "funding": { + "url": "https://github.com/andywer/threads.js?sponsor=1" + }, + "optionalDependencies": { + "tiny-worker": ">= 2" + } + }, + "node_modules/time-span": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-4.0.0.tgz", + "integrity": "sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tiny-worker": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", + "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", + "license": "BSD-3-Clause", + "dependencies": { + "esm": "^3.2.25" + } + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tree-sitter-wasms": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/tree-sitter-wasms/-/tree-sitter-wasms-0.1.13.tgz", + "integrity": "sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "tree-sitter-wasms": "^0.1.11" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-morph": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-12.0.0.tgz", + "integrity": "sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.11.0", + "code-block-writer": "^10.1.1" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-toolbelt": { + "version": "6.15.5", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz", + "integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript5": { + "name": "typescript", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-static-copy": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.4.tgz", + "integrity": "sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "p-map": "^7.0.3", + "picocolors": "^1.1.1", + "tinyglobby": "^0.2.15" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/vite-plugin-top-level-await": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz", + "integrity": "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==", + "license": "MIT", + "dependencies": { + "@rollup/plugin-virtual": "^3.0.2", + "@swc/core": "^1.12.14", + "@swc/wasm": "^1.12.14", + "uuid": "10.0.0" + }, + "peerDependencies": { + "vite": ">=2.8" + } + }, + "node_modules/vite-plugin-top-level-await/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite-plugin-wasm": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.5.0.tgz", + "integrity": "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ==", + "license": "MIT", + "peerDependencies": { + "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, + "node_modules/web-tree-sitter": { + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.20.8.tgz", + "integrity": "sha512-weOVgZ3aAARgdnb220GqYuh7+rZU0Ka9k9yfKtGAzEYMa6GgiCzW9JjQRJyCJakvibQW+dfjJdihjInKuuCAUQ==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/gitnexus-web/package.json b/gitnexus-web/package.json new file mode 100644 index 000000000..d66c42113 --- /dev/null +++ b/gitnexus-web/package.json @@ -0,0 +1,70 @@ +{ + "name": "gitnexus", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@huggingface/transformers": "^3.0.0", + "@isomorphic-git/lightning-fs": "^4.6.2", + "@langchain/anthropic": "^1.3.10", + "@langchain/core": "^1.1.15", + "@langchain/google-genai": "^2.1.10", + "@langchain/langgraph": "^1.1.0", + "@langchain/ollama": "^1.2.0", + "@langchain/openai": "^1.2.2", + "@sigma/edge-curve": "^3.1.0", + "@tailwindcss/vite": "^4.1.18", + "axios": "^1.13.2", + "buffer": "^6.0.3", + "comlink": "^4.4.2", + "d3": "^7.9.0", + "graphology": "^0.26.0", + "graphology-indices": "^0.17.0", + "graphology-utils": "^2.3.0", + "mnemonist": "^0.39.0", + "pandemonium": "^2.4.0", + "graphology-layout-force": "^0.2.4", + "graphology-layout-forceatlas2": "^0.10.1", + "graphology-layout-noverlap": "^0.4.2", + "isomorphic-git": "^1.36.1", + "jszip": "^3.10.1", + "kuzu-wasm": "^0.11.1", + "langchain": "^1.2.10", + "lru-cache": "^11.2.4", + "lucide-react": "^0.562.0", + "mermaid": "^11.12.2", + "minisearch": "^7.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", + "react-zoom-pan-pinch": "^3.7.0", + "remark-gfm": "^4.0.1", + "sigma": "^3.0.2", + "tailwindcss": "^4.1.18", + "uuid": "^13.0.0", + "vite-plugin-top-level-await": "^1.6.0", + "vite-plugin-wasm": "^3.5.0", + "web-tree-sitter": "^0.20.8", + "zod": "^3.25.76" + }, + "devDependencies": { + "@babel/types": "^7.28.5", + "@types/jszip": "^3.4.0", + "@types/node": "^24.10.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@types/react-syntax-highlighter": "^15.5.13", + "@vercel/node": "^5.5.16", + "@vitejs/plugin-react": "^5.1.0", + "tree-sitter-wasms": "^0.1.13", + "typescript": "^5.4.5", + "vite": "^5.2.0", + "vite-plugin-static-copy": "^3.1.4" + } +} diff --git a/gitnexus/public/wasm/c/tree-sitter-c.wasm b/gitnexus-web/public/wasm/c/tree-sitter-c.wasm similarity index 100% rename from gitnexus/public/wasm/c/tree-sitter-c.wasm rename to gitnexus-web/public/wasm/c/tree-sitter-c.wasm diff --git a/gitnexus/public/wasm/cpp/tree-sitter-cpp.wasm b/gitnexus-web/public/wasm/cpp/tree-sitter-cpp.wasm similarity index 100% rename from gitnexus/public/wasm/cpp/tree-sitter-cpp.wasm rename to gitnexus-web/public/wasm/cpp/tree-sitter-cpp.wasm diff --git a/gitnexus/public/wasm/csharp/tree-sitter-csharp.wasm b/gitnexus-web/public/wasm/csharp/tree-sitter-csharp.wasm similarity index 100% rename from gitnexus/public/wasm/csharp/tree-sitter-csharp.wasm rename to gitnexus-web/public/wasm/csharp/tree-sitter-csharp.wasm diff --git a/gitnexus/public/wasm/go/tree-sitter-go.wasm b/gitnexus-web/public/wasm/go/tree-sitter-go.wasm similarity index 100% rename from gitnexus/public/wasm/go/tree-sitter-go.wasm rename to gitnexus-web/public/wasm/go/tree-sitter-go.wasm diff --git a/gitnexus/public/wasm/java/tree-sitter-java.wasm b/gitnexus-web/public/wasm/java/tree-sitter-java.wasm similarity index 100% rename from gitnexus/public/wasm/java/tree-sitter-java.wasm rename to gitnexus-web/public/wasm/java/tree-sitter-java.wasm diff --git a/gitnexus/public/wasm/javascript/tree-sitter-javascript.wasm b/gitnexus-web/public/wasm/javascript/tree-sitter-javascript.wasm similarity index 100% rename from gitnexus/public/wasm/javascript/tree-sitter-javascript.wasm rename to gitnexus-web/public/wasm/javascript/tree-sitter-javascript.wasm diff --git a/gitnexus/public/wasm/kuzu-wasm.wasm b/gitnexus-web/public/wasm/kuzu-wasm.wasm similarity index 100% rename from gitnexus/public/wasm/kuzu-wasm.wasm rename to gitnexus-web/public/wasm/kuzu-wasm.wasm diff --git a/gitnexus/public/wasm/python/tree-sitter-python.wasm b/gitnexus-web/public/wasm/python/tree-sitter-python.wasm similarity index 100% rename from gitnexus/public/wasm/python/tree-sitter-python.wasm rename to gitnexus-web/public/wasm/python/tree-sitter-python.wasm diff --git a/gitnexus/public/wasm/rust/tree-sitter-rust.wasm b/gitnexus-web/public/wasm/rust/tree-sitter-rust.wasm similarity index 100% rename from gitnexus/public/wasm/rust/tree-sitter-rust.wasm rename to gitnexus-web/public/wasm/rust/tree-sitter-rust.wasm diff --git a/gitnexus/public/wasm/tree-sitter.wasm b/gitnexus-web/public/wasm/tree-sitter.wasm similarity index 100% rename from gitnexus/public/wasm/tree-sitter.wasm rename to gitnexus-web/public/wasm/tree-sitter.wasm diff --git a/gitnexus/public/wasm/typescript/tree-sitter-tsx.wasm b/gitnexus-web/public/wasm/typescript/tree-sitter-tsx.wasm similarity index 100% rename from gitnexus/public/wasm/typescript/tree-sitter-tsx.wasm rename to gitnexus-web/public/wasm/typescript/tree-sitter-tsx.wasm diff --git a/gitnexus/public/wasm/typescript/tree-sitter-typescript.wasm b/gitnexus-web/public/wasm/typescript/tree-sitter-typescript.wasm similarity index 100% rename from gitnexus/public/wasm/typescript/tree-sitter-typescript.wasm rename to gitnexus-web/public/wasm/typescript/tree-sitter-typescript.wasm diff --git a/gitnexus/src/App.tsx b/gitnexus-web/src/App.tsx similarity index 62% rename from gitnexus/src/App.tsx rename to gitnexus-web/src/App.tsx index 1288e502c..d609be146 100644 --- a/gitnexus/src/App.tsx +++ b/gitnexus-web/src/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useRef, useState, useEffect } from 'react'; +import { useCallback, useRef } from 'react'; import { AppStateProvider, useAppState } from './hooks/useAppState'; import { DropZone } from './components/DropZone'; import { LoadingOverlay } from './components/LoadingOverlay'; @@ -11,8 +11,6 @@ import { FileTreePanel } from './components/FileTreePanel'; import { CodeReferencesPanel } from './components/CodeReferencesPanel'; import { FileEntry } from './services/zip'; import { getActiveProviderConfig } from './core/llm/settings-service'; -import { ProviderConfig } from './core/llm/types'; -import { IntelligentClusteringModal } from './components/IntelligentClusteringModal'; const AppContent = () => { const { @@ -31,68 +29,24 @@ const AppContent = () => { refreshLLMSettings, initializeAgent, startEmbeddings, - startBackgroundEnrichment, embeddingStatus, codeReferences, selectedNode, isCodePanelOpen, - llmSettings, - updateLLMSettings, - runClusterEnrichment, } = useAppState(); - const [showClusteringModal, setShowClusteringModal] = useState(false); - - // Trigger clustering modal after ingestion if not seen yet - // DISABLED: Clustering is now in the upload flow - /* - useEffect(() => { - if (viewMode === 'exploring' && !llmSettings.hasSeenClusteringPrompt && !llmSettings.intelligentClustering) { - const timer = setTimeout(() => setShowClusteringModal(true), 2000); - return () => clearTimeout(timer); - } - }, [viewMode, llmSettings.hasSeenClusteringPrompt, llmSettings.intelligentClustering]); - */ - - const handleEnableClustering = useCallback(() => { - updateLLMSettings({ - intelligentClustering: true, - hasSeenClusteringPrompt: true, - useSameModelForClustering: true // Default to simple path - }); - setShowClusteringModal(false); - runClusterEnrichment().catch(console.error); - }, [updateLLMSettings, runClusterEnrichment]); - - const handleConfigureClustering = useCallback(() => { - updateLLMSettings({ hasSeenClusteringPrompt: true }); - setShowClusteringModal(false); - setSettingsPanelOpen(true); - }, [updateLLMSettings, setSettingsPanelOpen]); - - const handleSkipClustering = useCallback(() => { - updateLLMSettings({ hasSeenClusteringPrompt: true }); - setShowClusteringModal(false); - }, [updateLLMSettings]); - const graphCanvasRef = useRef(null); - const handleFileSelect = useCallback(async (file: File, enableSmartClustering?: boolean) => { - console.log('📥 App.handleFileSelect - param received:', enableSmartClustering, 'provider exists:', !!getActiveProviderConfig()); + const handleFileSelect = useCallback(async (file: File) => { const projectName = file.name.replace('.zip', ''); setProjectName(projectName); - // Set initial progress BEFORE entering loading mode to prevent black screen setProgress({ phase: 'extracting', percent: 0, message: 'Starting...', detail: 'Preparing to extract files' }); setViewMode('loading'); try { - // Prepare LLM config if clustering is enabled - const clusteringConfig = enableSmartClustering ? getActiveProviderConfig() ?? undefined : undefined; - console.log('✅ clusteringConfig:', !!clusteringConfig, clusteringConfig?.provider); - const result = await runPipeline(file, (progress) => { setProgress(progress); - }, clusteringConfig || undefined); + }); setGraph(result.graph); setFileContents(result.fileContents); @@ -107,16 +61,12 @@ const AppContent = () => { // Auto-start embeddings pipeline in background // Uses WebGPU if available, falls back to WASM startEmbeddings().catch((err) => { - // WebGPU not available - try WASM fallback silently if (err?.name === 'WebGPUNotAvailableError' || err?.message?.includes('WebGPU')) { startEmbeddings('wasm').catch(console.warn); } else { console.warn('Embeddings auto-start failed:', err); } }); - - // Start background cluster enrichment (if toggle was enabled) - startBackgroundEnrichment().catch(console.warn); } catch (error) { console.error('Pipeline error:', error); setProgress({ @@ -130,49 +80,36 @@ const AppContent = () => { setProgress(null); }, 3000); } - }, [setViewMode, setGraph, setFileContents, setProgress, setProjectName, runPipeline, startEmbeddings, initializeAgent, llmSettings]); + }, [setViewMode, setGraph, setFileContents, setProgress, setProjectName, runPipeline, startEmbeddings, initializeAgent]); - const handleGitClone = useCallback(async (files: FileEntry[], enableSmartClustering?: boolean) => { - // Extract project name from first file path (e.g., "owner-repo-123/src/..." -> "owner-repo") + const handleGitClone = useCallback(async (files: FileEntry[]) => { const firstPath = files[0]?.path || 'repository'; const projectName = firstPath.split('/')[0].replace(/-\d+$/, '') || 'repository'; setProjectName(projectName); - // Set initial progress BEFORE entering loading mode to prevent black screen setProgress({ phase: 'extracting', percent: 0, message: 'Starting...', detail: 'Preparing to process files' }); setViewMode('loading'); try { - // Prepare LLM config if clustering is enabled - const clusteringConfig = enableSmartClustering ? getActiveProviderConfig() ?? undefined : undefined; - const result = await runPipelineFromFiles(files, (progress) => { setProgress(progress); - }, clusteringConfig || undefined); + }); setGraph(result.graph); setFileContents(result.fileContents); setViewMode('exploring'); - // Initialize (or re-initialize) the agent AFTER a repo loads so it captures - // the current codebase context (file contents + graph tools) in the worker. if (getActiveProviderConfig()) { initializeAgent(projectName); } - // Auto-start embeddings pipeline in background - // Uses WebGPU if available, falls back to WASM startEmbeddings().catch((err) => { - // WebGPU not available - try WASM fallback silently if (err?.name === 'WebGPUNotAvailableError' || err?.message?.includes('WebGPU')) { startEmbeddings('wasm').catch(console.warn); } else { console.warn('Embeddings auto-start failed:', err); } }); - - // Start background cluster enrichment (if toggle was enabled) - startBackgroundEnrichment().catch(console.warn); } catch (error) { console.error('Pipeline error:', error); setProgress({ @@ -186,7 +123,7 @@ const AppContent = () => { setProgress(null); }, 3000); } - }, [setViewMode, setGraph, setFileContents, setProgress, setProjectName, runPipelineFromFiles, startEmbeddings, initializeAgent, runClusterEnrichment]); + }, [setViewMode, setGraph, setFileContents, setProgress, setProjectName, runPipelineFromFiles, startEmbeddings, initializeAgent]); const handleFocusNode = useCallback((nodeId: string) => { graphCanvasRef.current?.focusNode(nodeId); @@ -242,13 +179,6 @@ const AppContent = () => { onSettingsSaved={handleSettingsSaved} /> - {/* Intelligent Clustering Modal */} - ); }; diff --git a/gitnexus/src/components/CodeReferencesPanel.tsx b/gitnexus-web/src/components/CodeReferencesPanel.tsx similarity index 100% rename from gitnexus/src/components/CodeReferencesPanel.tsx rename to gitnexus-web/src/components/CodeReferencesPanel.tsx diff --git a/gitnexus/src/components/DropZone.tsx b/gitnexus-web/src/components/DropZone.tsx similarity index 81% rename from gitnexus/src/components/DropZone.tsx rename to gitnexus-web/src/components/DropZone.tsx index 0bce617a9..1749cbb24 100644 --- a/gitnexus/src/components/DropZone.tsx +++ b/gitnexus-web/src/components/DropZone.tsx @@ -1,12 +1,11 @@ -import { useState, useCallback, DragEvent, useEffect } from 'react'; -import { Upload, FileArchive, Github, Loader2, ArrowRight, Key, Eye, EyeOff, Sparkles } from 'lucide-react'; +import { useState, useCallback, DragEvent } from 'react'; +import { Upload, FileArchive, Github, Loader2, ArrowRight, Key, Eye, EyeOff } from 'lucide-react'; import { cloneRepository, parseGitHubUrl } from '../services/git-clone'; import { FileEntry } from '../services/zip'; -import { getActiveProviderConfig } from '../core/llm/settings-service'; interface DropZoneProps { - onFileSelect: (file: File, enableSmartClustering?: boolean) => void; - onGitClone?: (files: FileEntry[], enableSmartClustering?: boolean) => void; + onFileSelect: (file: File) => void; + onGitClone?: (files: FileEntry[]) => void; } export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { @@ -18,15 +17,6 @@ export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { const [isCloning, setIsCloning] = useState(false); const [cloneProgress, setCloneProgress] = useState({ phase: '', percent: 0 }); const [error, setError] = useState(null); - const [enableSmartClustering, setEnableSmartClustering] = useState(false); - const [hasLLMProvider, setHasLLMProvider] = useState(false); - - // Check if LLM provider is configured - useEffect(() => { - const config = getActiveProviderConfig(); - setHasLLMProvider(!!config); - // Keep smart clustering OFF by default, user must opt-in - }, []); const handleDragOver = useCallback((e: DragEvent) => { e.preventDefault(); @@ -49,25 +39,24 @@ export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { if (files.length > 0) { const file = files[0]; if (file.name.endsWith('.zip')) { - onFileSelect(file, enableSmartClustering); + onFileSelect(file); } else { setError('Please drop a .zip file'); } } - }, [onFileSelect, enableSmartClustering]); + }, [onFileSelect]); const handleFileInput = useCallback((e: React.ChangeEvent) => { const files = e.target.files; if (files && files.length > 0) { const file = files[0]; if (file.name.endsWith('.zip')) { - console.log('🎯 DropZone: Calling onFileSelect with enableSmartClustering:', enableSmartClustering); - onFileSelect(file, enableSmartClustering); + onFileSelect(file); } else { setError('Please select a .zip file'); } } - }, [onFileSelect, enableSmartClustering]); + }, [onFileSelect]); const handleGitClone = async () => { if (!githubUrl.trim()) { @@ -96,7 +85,7 @@ export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { setGithubToken(''); if (onGitClone) { - onGitClone(files, enableSmartClustering); + onGitClone(files); } } catch (err) { console.error('Clone failed:', err); @@ -224,41 +213,6 @@ export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { - {/* Smart Clustering Toggle - Below drop zone */} -
- -
)} diff --git a/gitnexus/src/components/EmbeddingStatus.tsx b/gitnexus-web/src/components/EmbeddingStatus.tsx similarity index 100% rename from gitnexus/src/components/EmbeddingStatus.tsx rename to gitnexus-web/src/components/EmbeddingStatus.tsx diff --git a/gitnexus/src/components/FileTreePanel.tsx b/gitnexus-web/src/components/FileTreePanel.tsx similarity index 100% rename from gitnexus/src/components/FileTreePanel.tsx rename to gitnexus-web/src/components/FileTreePanel.tsx diff --git a/gitnexus/src/components/GraphCanvas.tsx b/gitnexus-web/src/components/GraphCanvas.tsx similarity index 100% rename from gitnexus/src/components/GraphCanvas.tsx rename to gitnexus-web/src/components/GraphCanvas.tsx diff --git a/gitnexus/src/components/Header.tsx b/gitnexus-web/src/components/Header.tsx similarity index 56% rename from gitnexus/src/components/Header.tsx rename to gitnexus-web/src/components/Header.tsx index c1926842a..2fa3659fb 100644 --- a/gitnexus/src/components/Header.tsx +++ b/gitnexus-web/src/components/Header.tsx @@ -3,8 +3,6 @@ import { useAppState } from '../hooks/useAppState'; import { useState, useMemo, useRef, useEffect, useCallback } from 'react'; import { GraphNode } from '../core/graph/types'; import { EmbeddingStatus } from './EmbeddingStatus'; -import { MCPToggle } from './MCPToggle'; -import { buildCodebaseContext } from '../core/llm/context-builder'; // Color mapping for node types in search results const NODE_TYPE_COLORS: Record = { @@ -31,11 +29,6 @@ export const Header = ({ onFocusNode }: HeaderProps) => { isRightPanelOpen, rightPanelTab, setSettingsPanelOpen, - runQuery, - semanticSearch, - setHighlightedNodeIds, - fileContents, - triggerNodeAnimation, } = useAppState(); const [searchQuery, setSearchQuery] = useState(''); const [isSearchOpen, setIsSearchOpen] = useState(false); @@ -220,177 +213,6 @@ export const Header = ({ onFocusNode }: HeaderProps) => { {/* Embedding Status */} - {/* MCP Toggle for external AI agents */} - { - // Use semantic search from the app - const results = await semanticSearch(query, limit); - // Trigger pulse animation on search results - const nodeIds = results.map((r: any) => r.id).filter(Boolean); - if (nodeIds.length > 0) { - triggerNodeAnimation(nodeIds, 'pulse'); - } - return results; - }} - onCypher={async (query) => { - // Execute Cypher query - const results = await runQuery(query); - return results; - }} - onImpact={async (nodeId: string, hops = 2) => { - // Run impact analysis query - const query = ` - MATCH (start)-[*1..${hops}]-(connected) - WHERE start.id = '${nodeId}' OR start.name = '${nodeId}' - RETURN DISTINCT connected.id AS id, connected.name AS name, labels(connected) AS labels - `; - const results = await runQuery(query); - // Trigger ripple animation on impact results - const nodeIds = results.map((r: any) => r.id).filter(Boolean); - if (nodeIds.length > 0) { - triggerNodeAnimation(nodeIds, 'ripple'); - } - return results; - }} - onOverview={async () => { - // Return codebase overview: clusters + processes - const clustersQuery = ` - MATCH (c:Community) - OPTIONAL MATCH (c)<-[:CodeRelation {type: 'MEMBER_OF'}]-(m) - RETURN c.id AS id, c.label AS label, c.cohesion AS cohesion, c.description AS description, count(m) AS memberCount - ORDER BY memberCount DESC - LIMIT 50 - `; - const processesQuery = ` - MATCH (p:Process) - RETURN p.id AS id, p.label AS label, p.processType AS type, p.stepCount AS steps - ORDER BY p.stepCount DESC - LIMIT 50 - `; - const [clusters, processes] = await Promise.all([ - runQuery(clustersQuery), - runQuery(processesQuery), - ]); - return { clusters, processes }; - }} - onExplore={async (target: string, type?: 'symbol' | 'cluster' | 'process') => { - // Explore a specific target - if (type === 'cluster' || target.startsWith('comm_')) { - const query = ` - MATCH (c:Community) - WHERE c.id = '${target}' OR c.label CONTAINS '${target}' - OPTIONAL MATCH (c)<-[:CodeRelation {type: 'MEMBER_OF'}]-(m) - RETURN c.id AS id, c.label AS label, c.description AS description, collect(m.name)[0..10] AS members - LIMIT 1 - `; - return await runQuery(query); - } else if (type === 'process' || target.startsWith('proc_')) { - const query = ` - MATCH (p:Process) - WHERE p.id = '${target}' OR p.label CONTAINS '${target}' - OPTIONAL MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p) - RETURN p.id AS id, p.label AS label, p.stepCount AS steps, collect({name: s.name, step: r.step})[0..20] AS trace - LIMIT 1 - `; - return await runQuery(query); - } else { - // Symbol exploration - const query = ` - MATCH (n) - WHERE n.name = '${target}' OR n.id ENDS WITH ':${target}' - OPTIONAL MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) - OPTIONAL MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) - RETURN n.id AS id, n.name AS name, n.filePath AS filePath, label(n) AS nodeType, - c.label AS cluster, collect({process: p.label, step: r.step}) AS processes - LIMIT 1 - `; - return await runQuery(query); - } - }} - getContext={async () => { - // Build codebase context for external AI agents - if (!projectName) return null; - const context = await buildCodebaseContext(runQuery, projectName); - // Reshape to match MCP CodebaseContext format - return { - projectName: context.stats.projectName, - stats: { - fileCount: context.stats.fileCount, - functionCount: context.stats.functionCount, - classCount: context.stats.classCount, - interfaceCount: context.stats.interfaceCount, - methodCount: context.stats.methodCount, - }, - hotspots: context.hotspots, - folderTree: context.folderTree, - }; - }} - onGrep={async (pattern, caseSensitive = false, maxResults = 50) => { - // Grep across file contents - const results: Array<{ filePath: string; line: string; lineNumber: number; match: string }> = []; - const regex = new RegExp(pattern, caseSensitive ? 'g' : 'gi'); - - for (const [filePath, content] of fileContents.entries()) { - const lines = content.split('\n'); - for (let i = 0; i < lines.length && results.length < maxResults; i++) { - const line = lines[i]; - const match = line.match(regex); - if (match) { - results.push({ - filePath, - line: line.trim(), - lineNumber: i + 1, - match: match[0], - }); - } - } - if (results.length >= maxResults) break; - } - return results; - }} - onRead={async (filePath, startLine, endLine) => { - // Read file content - let content = fileContents.get(filePath); - - // Try normalized path if not found - if (!content) { - const normalizedPath = filePath.replace(/\\/g, '/'); - for (const [path, c] of fileContents.entries()) { - if (path.endsWith(normalizedPath) || normalizedPath.endsWith(path)) { - content = c; - break; - } - } - } - - if (!content) { - return { error: `File not found: ${filePath}` }; - } - - const lines = content.split('\n'); - const language = filePath.split('.').pop() || 'text'; - - // If line range specified, return only those lines - if (startLine !== undefined && endLine !== undefined) { - const slice = lines.slice(startLine - 1, endLine); - return { - filePath, - content: slice.join('\n'), - language, - lines: slice.length, - }; - } - - return { - filePath, - content, - language, - lines: lines.length, - }; - }} - /> - {/* Icon buttons */} - {/* Activity Tab */} - - {/* Processes Tab */} - {/* Activity Feed Tab */} - {activeTab === 'activity' && ( -
- -
- )} - {/* Processes Tab */} {activeTab === 'processes' && (
@@ -429,13 +410,23 @@ export const RightPanel = () => { > Clear - + {isChatLoading ? ( + + ) : ( + + )}
{!isAgentReady && !isAgentInitializing && (
diff --git a/gitnexus/src/components/SettingsPanel.tsx b/gitnexus-web/src/components/SettingsPanel.tsx similarity index 77% rename from gitnexus/src/components/SettingsPanel.tsx rename to gitnexus-web/src/components/SettingsPanel.tsx index 85b8c068c..ebc739144 100644 --- a/gitnexus/src/components/SettingsPanel.tsx +++ b/gitnexus-web/src/components/SettingsPanel.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect, useCallback } from 'react'; -import { X, Key, Server, Brain, Check, AlertCircle, Eye, EyeOff, RefreshCw, Sparkles } from 'lucide-react'; +import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; +import { X, Key, Server, Brain, Check, AlertCircle, Eye, EyeOff, RefreshCw, ChevronDown, Loader2, Search } from 'lucide-react'; import { loadSettings, saveSettings, @@ -14,6 +14,175 @@ interface SettingsPanelProps { onSettingsSaved?: () => void; } +/** + * Searchable combobox for OpenRouter model selection + */ +interface OpenRouterModelComboboxProps { + value: string; + onChange: (model: string) => void; + models: Array<{ id: string; name: string }>; + isLoading: boolean; + onLoadModels: () => void; +} + +const OpenRouterModelCombobox = ({ value, onChange, models, isLoading, onLoadModels }: OpenRouterModelComboboxProps) => { + const [isOpen, setIsOpen] = useState(false); + const [searchTerm, setSearchTerm] = useState(''); + const inputRef = useRef(null); + const containerRef = useRef(null); + + // Filter models based on search term + const filteredModels = useMemo(() => { + if (!searchTerm.trim()) return models; + const lower = searchTerm.toLowerCase(); + return models.filter(m => + m.id.toLowerCase().includes(lower) || + m.name.toLowerCase().includes(lower) + ); + }, [models, searchTerm]); + + // Find display name for current value + const displayValue = useMemo(() => { + if (!value) return ''; + const found = models.find(m => m.id === value); + return found ? found.name : value; + }, [value, models]); + + // Close dropdown when clicking outside + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setIsOpen(false); + setSearchTerm(''); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // Load models when opening + const handleOpen = () => { + setIsOpen(true); + if (models.length === 0 && !isLoading) { + onLoadModels(); + } + setTimeout(() => inputRef.current?.focus(), 10); + }; + + const handleSelect = (modelId: string) => { + onChange(modelId); + setIsOpen(false); + setSearchTerm(''); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const val = e.target.value; + setSearchTerm(val); + // Also allow direct typing of model ID + if (val && models.length === 0) { + onChange(val); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && searchTerm) { + // If exact match in filtered, select it; otherwise use raw input + const exact = filteredModels.find(m => m.id.toLowerCase() === searchTerm.toLowerCase()); + if (exact) { + handleSelect(exact.id); + } else if (filteredModels.length === 1) { + handleSelect(filteredModels[0].id); + } else { + // Allow custom model ID input + onChange(searchTerm); + setIsOpen(false); + setSearchTerm(''); + } + } else if (e.key === 'Escape') { + setIsOpen(false); + setSearchTerm(''); + } + }; + + return ( +
+ {/* Main input/button */} +
+ {isOpen ? ( + e.stopPropagation()} + /> + ) : ( + + {displayValue || 'Select or type a model...'} + + )} +
+ {isLoading && } + +
+
+ + {/* Dropdown */} + {isOpen && ( +
+ {isLoading ? ( +
+ + Loading models... +
+ ) : filteredModels.length === 0 ? ( +
+ {models.length === 0 ? ( +
+ +

Type a model ID or press Enter

+

e.g. openai/gpt-4o

+
+ ) : ( +
+

No models match "{searchTerm}"

+

Press Enter to use as custom ID

+
+ )} +
+ ) : ( +
+ {filteredModels.slice(0, 50).map(model => ( + + ))} + {filteredModels.length > 50 && ( +
+ +{filteredModels.length - 50} more • Refine your search +
+ )} +
+ )} +
+ )} +
+ ); +}; + /** * Check connection to local Ollama instance */ @@ -588,35 +757,16 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane
- + models={openRouterModels} + isLoading={isLoadingModels} + onLoadModels={loadOpenRouterModels} + />

Browse all models at{' '} -

- - Intelligent Clustering (Beta) -

- -
-
-
- -

Generate semantic names and descriptions for code clusters

-
- -
- - {settings.intelligentClustering && ( -
-
-
- -

Use the same provider configured above

-
- -
- - {!settings.useSameModelForClustering && ( -
-
-
- -
-

- Pro Tip: Use a cheaper model like GPT-4o-mini or Gemini Flash for clustering! -

-
- - {/* Simplistic Clustering Provider Config - For now just a model name override for simplicity, - or we could duplicate the provider selector. - For key simplicity in this iteration, let's just let them override the MODEL name if using the SAME provider, - or we can add a provider dropdown. - - Actually, the simplest implementation for "separate model" is just allowing them to pick a provider/model - for clustering specifically. But that replicates a lot of UI. - - Let's stick to the plan: "Use same model as agent" vs "Use different model". - If different, show a simplified provider config (just Provider + Model + Key if needed). - - For MVP, let's just assume they want to use OpenAI/Azure/Gemini with a specific model string. - */} - -
- - -
- -
- - setSettings(prev => ({ - ...prev, - clusteringProvider: { ...prev.clusteringProvider, model: e.target.value } - }))} - placeholder="e.g. gpt-4o-mini" - className="w-full px-3 py-2 bg-elevated border border-border-subtle rounded-lg text-sm text-text-primary placeholder:text-text-muted outline-none" - /> -
- -
- - setSettings(prev => ({ - ...prev, - clusteringProvider: { ...prev.clusteringProvider, apiKey: e.target.value } - }))} - placeholder="Leave blank to use main key if matching..." - className="w-full px-3 py-2 bg-elevated border border-border-subtle rounded-lg text-sm text-text-primary placeholder:text-text-muted outline-none" - /> -
- -

- Required if using a different provider than your main agent. -

-
- )} -
- )} -
-
{/* Privacy Note */}
diff --git a/gitnexus/src/components/StatusBar.tsx b/gitnexus-web/src/components/StatusBar.tsx similarity index 66% rename from gitnexus/src/components/StatusBar.tsx rename to gitnexus-web/src/components/StatusBar.tsx index 476f4ecb8..3240a008a 100644 --- a/gitnexus/src/components/StatusBar.tsx +++ b/gitnexus-web/src/components/StatusBar.tsx @@ -1,8 +1,8 @@ -import { Pause, X } from 'lucide-react'; +import { Heart } from 'lucide-react'; import { useAppState } from '../hooks/useAppState'; export const StatusBar = () => { - const { graph, progress, enrichmentProgress, cancelEnrichment } = useAppState(); + const { graph, progress } = useAppState(); const nodeCount = graph?.nodes.length ?? 0; const edgeCount = graph?.relationships.length ?? 0; @@ -23,8 +23,6 @@ export const StatusBar = () => { return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0]; })(); - const isLabeling = enrichmentProgress !== null; - return (
{/* Left - Status */} @@ -39,24 +37,6 @@ export const StatusBar = () => {
{progress.message} - ) : isLabeling ? ( - <> -
-
-
- Labeling clusters {enrichmentProgress.current}/{enrichmentProgress.total}... - - ) : (
@@ -65,6 +45,20 @@ export const StatusBar = () => { )}
+ {/* Center - Sponsor */} +
+ + Sponsor + + need to buy some API credits to run SWE-bench 😅 + + + {/* Right - Stats */}
{graph && ( diff --git a/gitnexus/src/components/ToolCallCard.tsx b/gitnexus-web/src/components/ToolCallCard.tsx similarity index 59% rename from gitnexus/src/components/ToolCallCard.tsx rename to gitnexus-web/src/components/ToolCallCard.tsx index 659a72885..e202f83d6 100644 --- a/gitnexus/src/components/ToolCallCard.tsx +++ b/gitnexus-web/src/components/ToolCallCard.tsx @@ -5,10 +5,9 @@ * Shows the tool name, status, and when expanded, the query/args and result. */ -import { useState, useCallback, useMemo } from 'react'; -import { ChevronDown, ChevronRight, Sparkles, Check, Loader2, AlertCircle, Eye, EyeOff } from 'lucide-react'; +import { useState } from 'react'; +import { ChevronDown, ChevronRight, Sparkles, Check, Loader2, AlertCircle } from 'lucide-react'; import type { ToolCallInfo } from '../core/llm/types'; -import { useAppState } from '../hooks/useAppState'; interface ToolCallCardProps { toolCall: ToolCallInfo; @@ -25,19 +24,20 @@ const formatArgs = (args: Record): string => { } // Special handling for Cypher queries - if ('query' in args && typeof args.query === 'string') { - return args.query; - } if ('cypher' in args && typeof args.cypher === 'string') { - // For execute_vector_cypher, show both the natural language query and cypher let result = ''; - if ('query' in args) { + if ('query' in args && typeof args.query === 'string') { result += `Search: "${args.query}"\n\n`; } result += args.cypher; return result; } + // Special handling for search/grep queries + if ('query' in args && typeof args.query === 'string') { + return args.query; + } + // For other tools, show as formatted JSON return JSON.stringify(args, null, 2); }; @@ -83,81 +83,23 @@ const getStatusDisplay = (status: ToolCallInfo['status']) => { */ const getToolDisplayName = (name: string): string => { const names: Record = { - // New consolidated tools + // Current 7-tool architecture 'search': '🔍 Search Code', - 'cypher': '🔍 Cypher Query', + 'cypher': '🔗 Cypher Query', 'grep': '🔎 Pattern Search', 'read': '📄 Read File', - 'highlight': '✨ Highlight in Graph', - // Legacy names (for backwards compatibility) - 'execute_cypher': '🔍 Cypher Query', - 'execute_vector_cypher': '🧠 Semantic + Graph Query', - 'highlight_in_graph': '✨ Highlight in Graph', - 'grep_code': '🔎 Pattern Search', - 'read_file': '📄 Read File', + 'overview': '🗺️ Codebase Overview', + 'explore': '🔬 Deep Dive', + 'impact': '💥 Impact Analysis', }; return names[name] || name; }; -/** - * Extract node IDs from highlight tool result - */ -const extractHighlightNodeIds = (result: string | undefined): string[] => { - if (!result) return []; - const match = result.match(/\[HIGHLIGHT_NODES:([^\]]+)\]/); - if (match) { - return match[1].split(',').map(id => id.trim()).filter(Boolean); - } - return []; -}; - export const ToolCallCard = ({ toolCall, defaultExpanded = false }: ToolCallCardProps) => { const [isExpanded, setIsExpanded] = useState(defaultExpanded); - const { highlightedNodeIds, setHighlightedNodeIds, graph } = useAppState(); const status = getStatusDisplay(toolCall.status); const formattedArgs = formatArgs(toolCall.args); - // Check if this is a highlight tool and extract node IDs - const isHighlightTool = toolCall.name === 'highlight_in_graph' || toolCall.name === 'highlight'; - const rawHighlightNodeIds = isHighlightTool ? extractHighlightNodeIds(toolCall.result) : []; - - // Resolve raw IDs to actual graph node IDs (handles partial ID matching) - const resolvedNodeIds = useMemo(() => { - if (rawHighlightNodeIds.length === 0 || !graph) return rawHighlightNodeIds; - - const graphNodeIds = graph.nodes.map(n => n.id); - const resolved: string[] = []; - - for (const rawId of rawHighlightNodeIds) { - if (graphNodeIds.includes(rawId)) { - resolved.push(rawId); - } else { - // Try partial match - find node whose ID ends with the raw ID - const found = graphNodeIds.find(gid => - gid.endsWith(rawId) || gid.endsWith(':' + rawId) - ); - if (found) resolved.push(found); - } - } - return resolved; - }, [rawHighlightNodeIds, graph]); - - // Check if these specific nodes are currently highlighted - const isHighlightActive = resolvedNodeIds.length > 0 && - resolvedNodeIds.some(id => highlightedNodeIds.has(id)); - - // Toggle highlight on/off - const toggleHighlight = useCallback((e: React.MouseEvent) => { - e.stopPropagation(); // Don't trigger expand/collapse - if (isHighlightActive) { - // Turn off - clear highlights - setHighlightedNodeIds(new Set()); - } else { - // Turn on - set these nodes as highlighted - setHighlightedNodeIds(new Set(resolvedNodeIds)); - } - }, [isHighlightActive, resolvedNodeIds, setHighlightedNodeIds]); - return (
{/* Header - always visible */} @@ -178,30 +120,6 @@ export const ToolCallCard = ({ toolCall, defaultExpanded = false }: ToolCallCard {getToolDisplayName(toolCall.name)} - {/* Highlight toggle button - only for highlight_in_graph tool with results */} - {isHighlightTool && resolvedNodeIds.length > 0 && ( - - )} - {/* Status indicator */} {status.icon} @@ -216,7 +134,7 @@ export const ToolCallCard = ({ toolCall, defaultExpanded = false }: ToolCallCard {formattedArgs && (
- {toolCall.name.includes('cypher') ? 'Query' : 'Input'} + {toolCall.name === 'cypher' ? 'Query' : 'Input'}
                 {formattedArgs}
@@ -255,4 +173,3 @@ export const ToolCallCard = ({ toolCall, defaultExpanded = false }: ToolCallCard
 };
 
 export default ToolCallCard;
-
diff --git a/gitnexus/src/components/WebGPUFallbackDialog.tsx b/gitnexus-web/src/components/WebGPUFallbackDialog.tsx
similarity index 100%
rename from gitnexus/src/components/WebGPUFallbackDialog.tsx
rename to gitnexus-web/src/components/WebGPUFallbackDialog.tsx
diff --git a/gitnexus-web/src/config/ignore-service.ts b/gitnexus-web/src/config/ignore-service.ts
new file mode 100644
index 000000000..affd83578
--- /dev/null
+++ b/gitnexus-web/src/config/ignore-service.ts
@@ -0,0 +1,239 @@
+const DEFAULT_IGNORE_LIST = new Set([
+    // Version Control
+    '.git',
+    '.svn',
+    '.hg',
+    '.bzr',
+    
+    // IDEs & Editors
+    '.idea',
+    '.vscode',
+    '.vs',
+    '.eclipse',
+    '.settings',
+    '.DS_Store',
+    'Thumbs.db',
+  
+    // Dependencies
+    'node_modules',
+    'bower_components',
+    'jspm_packages',
+    'vendor',           // PHP/Go
+    // 'packages' removed - commonly used for monorepo source code (lerna, pnpm, yarn workspaces)
+    'venv',
+    '.venv',
+    'env',
+    '.env',
+    '__pycache__',
+    '.pytest_cache',
+    '.mypy_cache',
+    'site-packages',
+    '.tox',
+    'eggs',
+    '.eggs',
+    'lib64',
+    'parts',
+    'sdist',
+    'wheels',
+  
+    // Build Outputs
+    'dist',
+    'build',
+    'out',
+    'output',
+    'bin',
+    'obj',
+    'target',           // Java/Rust
+    '.next',
+    '.nuxt',
+    '.output',
+    '.vercel',
+    '.netlify',
+    '.serverless',
+    '_build',
+    'public/build',
+    '.parcel-cache',
+    '.turbo',
+    '.svelte-kit',
+  
+    // Test & Coverage
+    'coverage',
+    '.nyc_output',
+    'htmlcov',
+    '.coverage',
+    '__tests__',        // Often just test files
+    '__mocks__',
+    '.jest',
+    
+    // Logs & Temp
+    'logs',
+    'log',
+    'tmp',
+    'temp',
+    'cache',
+    '.cache',
+    '.tmp',
+    '.temp',
+    
+    // Generated/Compiled
+    '.generated',
+    'generated',
+    'auto-generated',
+    '.terraform',
+    '.serverless',
+    
+    // Documentation (optional - might want to keep)
+    // 'docs',
+    // 'documentation',
+    
+    // Misc
+    '.husky',
+    '.github',          // GitHub config, not code
+    '.circleci',
+    '.gitlab',
+    'fixtures',         // Test fixtures
+    'snapshots',        // Jest snapshots
+    '__snapshots__',
+]);
+
+const IGNORED_EXTENSIONS = new Set([
+    // Images
+    '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.webp', '.bmp', '.tiff', '.tif',
+    '.psd', '.ai', '.sketch', '.fig', '.xd',
+    
+    // Archives
+    '.zip', '.tar', '.gz', '.rar', '.7z', '.bz2', '.xz', '.tgz',
+    
+    // Binary/Compiled
+    '.exe', '.dll', '.so', '.dylib', '.a', '.lib', '.o', '.obj',
+    '.class', '.jar', '.war', '.ear',
+    '.pyc', '.pyo', '.pyd',
+    '.beam',            // Erlang
+    '.wasm',            // WebAssembly - important!
+    '.node',            // Native Node addons
+    
+    // Documents
+    '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
+    '.odt', '.ods', '.odp',
+    
+    // Media
+    '.mp4', '.mp3', '.wav', '.mov', '.avi', '.mkv', '.flv', '.wmv',
+    '.ogg', '.webm', '.flac', '.aac', '.m4a',
+    
+    // Fonts
+    '.woff', '.woff2', '.ttf', '.eot', '.otf',
+    
+    // Databases
+    '.db', '.sqlite', '.sqlite3', '.mdb', '.accdb',
+    
+    // Minified/Bundled files
+    '.min.js', '.min.css', '.bundle.js', '.chunk.js',
+    
+    // Source maps (debug files, not source)
+    '.map',
+    
+    // Lock files (handled separately, but also here)
+    '.lock',
+    
+    // Certificates & Keys (security - don't index!)
+    '.pem', '.key', '.crt', '.cer', '.p12', '.pfx',
+    
+    // Data files (often large/binary)
+    '.csv', '.tsv', '.parquet', '.avro', '.feather',
+    '.npy', '.npz', '.pkl', '.pickle', '.h5', '.hdf5',
+    
+    // Misc binary
+    '.bin', '.dat', '.data', '.raw',
+    '.iso', '.img', '.dmg',
+]);
+
+// Files to ignore by exact name
+const IGNORED_FILES = new Set([
+    'package-lock.json',
+    'yarn.lock',
+    'pnpm-lock.yaml',
+    'composer.lock',
+    'Gemfile.lock',
+    'poetry.lock',
+    'Cargo.lock',
+    'go.sum',
+    '.gitignore',
+    '.gitattributes',
+    '.npmrc',
+    '.yarnrc',
+    '.editorconfig',
+    '.prettierrc',
+    '.prettierignore',
+    '.eslintignore',
+    '.dockerignore',
+    'Thumbs.db',
+    '.DS_Store',
+    'LICENSE',
+    'LICENSE.md',
+    'LICENSE.txt',
+    'CHANGELOG.md',
+    'CHANGELOG',
+    'CONTRIBUTING.md',
+    'CODE_OF_CONDUCT.md',
+    'SECURITY.md',
+    '.env',
+    '.env.local',
+    '.env.development',
+    '.env.production',
+    '.env.test',
+    '.env.example',
+]);
+
+
+
+export const shouldIgnorePath = (filePath: string): boolean => {
+  const normalizedPath = filePath.replace(/\\/g, '/');
+  const parts = normalizedPath.split('/');
+  const fileName = parts[parts.length - 1];
+  const fileNameLower = fileName.toLowerCase();
+
+  // Check if any path segment is in ignore list
+  for (const part of parts) {
+    if (DEFAULT_IGNORE_LIST.has(part)) {
+      return true;
+    }
+  }
+
+  // Check exact filename matches
+  if (IGNORED_FILES.has(fileName) || IGNORED_FILES.has(fileNameLower)) {
+    return true;
+  }
+
+  // Check extension
+  const lastDotIndex = fileNameLower.lastIndexOf('.');
+  if (lastDotIndex !== -1) {
+    const ext = fileNameLower.substring(lastDotIndex);
+    if (IGNORED_EXTENSIONS.has(ext)) return true;
+    
+    // Handle compound extensions like .min.js, .bundle.js
+    const secondLastDot = fileNameLower.lastIndexOf('.', lastDotIndex - 1);
+    if (secondLastDot !== -1) {
+      const compoundExt = fileNameLower.substring(secondLastDot);
+      if (IGNORED_EXTENSIONS.has(compoundExt)) return true;
+    }
+  }
+
+  // Ignore hidden files (starting with .)
+  if (fileName.startsWith('.') && fileName !== '.') {
+    // But allow some important config files
+    const allowedDotFiles = ['.env', '.gitignore']; // Already in IGNORED_FILES, so this is redundant
+    // Actually, let's NOT ignore all dot files - many are important configs
+    // Just rely on the explicit lists above
+  }
+
+  // Ignore files that look like generated/bundled code
+  if (fileNameLower.includes('.bundle.') || 
+      fileNameLower.includes('.chunk.') ||
+      fileNameLower.includes('.generated.') ||
+      fileNameLower.endsWith('.d.ts')) { // TypeScript declaration files
+    return true;
+  }
+
+  return false;
+}
+
diff --git a/gitnexus-web/src/config/supported-languages.ts b/gitnexus-web/src/config/supported-languages.ts
new file mode 100644
index 000000000..15df37c54
--- /dev/null
+++ b/gitnexus-web/src/config/supported-languages.ts
@@ -0,0 +1,14 @@
+export enum SupportedLanguages {
+    JavaScript = 'javascript',
+    TypeScript = 'typescript',
+    Python = 'python',
+    Java = 'java',
+    C = 'c',
+    CPlusPlus = 'cpp',
+    CSharp = 'csharp',
+    Go = 'go',
+    Rust = 'rust',
+    // PHP = 'php',
+    // Ruby = 'ruby',
+    // Swift = 'swift',
+}
\ No newline at end of file
diff --git a/gitnexus-web/src/core/embeddings/embedder.ts b/gitnexus-web/src/core/embeddings/embedder.ts
new file mode 100644
index 000000000..118894583
--- /dev/null
+++ b/gitnexus-web/src/core/embeddings/embedder.ts
@@ -0,0 +1,302 @@
+/**
+ * Embedder Module
+ * 
+ * Singleton factory for transformers.js embedding pipeline.
+ * Handles model loading, caching, and both single and batch embedding operations.
+ * 
+ * Uses snowflake-arctic-embed-xs by default (22M params, 384 dims, ~90MB)
+ */
+
+import { pipeline, env, type FeatureExtractionPipeline } from '@huggingface/transformers';
+import { DEFAULT_EMBEDDING_CONFIG, type EmbeddingConfig, type ModelProgress } from './types';
+
+// Module-level state for singleton pattern
+let embedderInstance: FeatureExtractionPipeline | null = null;
+let isInitializing = false;
+let initPromise: Promise | null = null;
+let currentDevice: 'webgpu' | 'wasm' | null = null;
+
+/**
+ * Progress callback type for model loading
+ */
+export type ModelProgressCallback = (progress: ModelProgress) => void;
+
+/**
+ * Custom error thrown when WebGPU is not available
+ * Allows UI to prompt user for fallback choice
+ */
+export class WebGPUNotAvailableError extends Error {
+  constructor(originalError?: Error) {
+    super('WebGPU not available in this browser');
+    this.name = 'WebGPUNotAvailableError';
+    this.cause = originalError;
+  }
+}
+
+/**
+ * Check if WebGPU is available in this browser
+ * Quick check without loading the model
+ */
+export const checkWebGPUAvailability = async (): Promise => {
+  try {
+    // Cast to any to avoid WebGPU types not being available in all TS configs
+    const nav = navigator as any;
+    if (!nav.gpu) {
+      return false;
+    }
+    const adapter = await nav.gpu.requestAdapter();
+    if (!adapter) {
+      return false;
+    }
+    // Try to get a device - this is where it usually fails
+    const device = await adapter.requestDevice();
+    device.destroy(); // Clean up
+    return true;
+  } catch {
+    return false;
+  }
+};
+
+/**
+ * Get the current device being used for inference
+ */
+export const getCurrentDevice = (): 'webgpu' | 'wasm' | null => currentDevice;
+
+/**
+ * Initialize the embedding model
+ * Uses singleton pattern - only loads once, subsequent calls return cached instance
+ * 
+ * @param onProgress - Optional callback for model download progress
+ * @param config - Optional configuration override
+ * @param forceDevice - Force a specific device (bypasses WebGPU check)
+ * @returns Promise resolving to the embedder pipeline
+ * @throws WebGPUNotAvailableError if WebGPU is requested but unavailable
+ */
+export const initEmbedder = async (
+  onProgress?: ModelProgressCallback,
+  config: Partial = {},
+  forceDevice?: 'webgpu' | 'wasm'
+): Promise => {
+  // Return existing instance if available
+  if (embedderInstance) {
+    return embedderInstance;
+  }
+
+  // If already initializing, wait for that promise
+  if (isInitializing && initPromise) {
+    return initPromise;
+  }
+
+  isInitializing = true;
+  
+  const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config };
+  const requestedDevice = forceDevice || finalConfig.device;
+
+  initPromise = (async () => {
+    try {
+      // Configure transformers.js environment
+      env.allowLocalModels = false;
+      
+      if (import.meta.env.DEV) {
+        console.log(`🧠 Loading embedding model: ${finalConfig.modelId}`);
+      }
+
+      const progressCallback = onProgress ? (data: any) => {
+        const progress: ModelProgress = {
+          status: data.status || 'progress',
+          file: data.file,
+          progress: data.progress,
+          loaded: data.loaded,
+          total: data.total,
+        };
+        onProgress(progress);
+      } : undefined;
+
+      // If WebGPU is requested (default), check availability first
+      if (requestedDevice === 'webgpu') {
+        if (import.meta.env.DEV) {
+          console.log('🔧 Checking WebGPU availability...');
+        }
+        
+        const webgpuAvailable = await checkWebGPUAvailability();
+        
+        if (!webgpuAvailable) {
+          if (import.meta.env.DEV) {
+            console.warn('⚠️ WebGPU not available');
+          }
+          isInitializing = false;
+          initPromise = null;
+          throw new WebGPUNotAvailableError();
+        }
+        
+        // Try WebGPU
+        try {
+          if (import.meta.env.DEV) {
+            console.log('🔧 Initializing WebGPU backend...');
+          }
+          
+          // Type assertion needed due to complex union types in transformers.js
+          embedderInstance = await (pipeline as any)(
+            'feature-extraction',
+            finalConfig.modelId,
+            {
+              device: 'webgpu',
+              dtype: 'fp32',
+              progress_callback: progressCallback,
+            }
+          );
+          currentDevice = 'webgpu';
+          
+          if (import.meta.env.DEV) {
+            console.log('✅ Using WebGPU backend');
+          }
+        } catch (err) {
+          if (import.meta.env.DEV) {
+            console.warn('⚠️ WebGPU initialization failed:', err);
+          }
+          isInitializing = false;
+          initPromise = null;
+          embedderInstance = null;
+          throw new WebGPUNotAvailableError(err as Error);
+        }
+      } else {
+        // WASM mode requested (user chose fallback)
+        if (import.meta.env.DEV) {
+          console.log('🔧 Initializing WASM backend (this will be slower)...');
+        }
+        
+        // Type assertion needed due to complex union types in transformers.js
+        embedderInstance = await (pipeline as any)(
+          'feature-extraction',
+          finalConfig.modelId,
+          {
+            device: 'wasm', // WASM-based CPU execution
+            dtype: 'fp32',
+            progress_callback: progressCallback,
+          }
+        );
+        currentDevice = 'wasm';
+        
+        if (import.meta.env.DEV) {
+          console.log('✅ Using WASM backend');
+        }
+      }
+
+      if (import.meta.env.DEV) {
+        console.log('✅ Embedding model loaded successfully');
+      }
+
+      return embedderInstance!;
+    } catch (error) {
+      // Re-throw WebGPUNotAvailableError as-is
+      if (error instanceof WebGPUNotAvailableError) {
+        throw error;
+      }
+      isInitializing = false;
+      initPromise = null;
+      embedderInstance = null;
+      throw error;
+    } finally {
+      isInitializing = false;
+    }
+  })();
+
+  return initPromise;
+};
+
+/**
+ * Check if the embedder is initialized and ready
+ */
+export const isEmbedderReady = (): boolean => {
+  return embedderInstance !== null;
+};
+
+/**
+ * Get the embedder instance (throws if not initialized)
+ */
+export const getEmbedder = (): FeatureExtractionPipeline => {
+  if (!embedderInstance) {
+    throw new Error('Embedder not initialized. Call initEmbedder() first.');
+  }
+  return embedderInstance;
+};
+
+/**
+ * Embed a single text string
+ * 
+ * @param text - Text to embed
+ * @returns Float32Array of embedding vector (384 dimensions)
+ */
+export const embedText = async (text: string): Promise => {
+  const embedder = getEmbedder();
+  
+  const result = await embedder(text, {
+    pooling: 'mean',
+    normalize: true,
+  });
+  
+  // Result is a Tensor, convert to Float32Array
+  return new Float32Array(result.data as ArrayLike);
+};
+
+/**
+ * Embed multiple texts in a single batch
+ * More efficient than calling embedText multiple times
+ * 
+ * @param texts - Array of texts to embed
+ * @returns Array of Float32Array embedding vectors
+ */
+export const embedBatch = async (texts: string[]): Promise => {
+  if (texts.length === 0) {
+    return [];
+  }
+
+  const embedder = getEmbedder();
+  
+  // Process batch
+  const result = await embedder(texts, {
+    pooling: 'mean',
+    normalize: true,
+  });
+  
+  // Result shape is [batch_size, dimensions]
+  // Need to split into individual vectors
+  const data = result.data as ArrayLike;
+  const dimensions = DEFAULT_EMBEDDING_CONFIG.dimensions;
+  const embeddings: Float32Array[] = [];
+  
+  for (let i = 0; i < texts.length; i++) {
+    const start = i * dimensions;
+    const end = start + dimensions;
+    embeddings.push(new Float32Array(Array.prototype.slice.call(data, start, end)));
+  }
+  
+  return embeddings;
+};
+
+/**
+ * Convert Float32Array to regular number array (for KuzuDB storage)
+ */
+export const embeddingToArray = (embedding: Float32Array): number[] => {
+  return Array.from(embedding);
+};
+
+/**
+ * Cleanup the embedder (free memory)
+ * Call this when done with embeddings
+ */
+export const disposeEmbedder = async (): Promise => {
+  if (embedderInstance) {
+    // transformers.js pipelines may have a dispose method
+    try {
+      if ('dispose' in embedderInstance && typeof embedderInstance.dispose === 'function') {
+        await embedderInstance.dispose();
+      }
+    } catch {
+      // Ignore disposal errors
+    }
+    embedderInstance = null;
+    initPromise = null;
+  }
+};
+
diff --git a/gitnexus-web/src/core/embeddings/embedding-pipeline.ts b/gitnexus-web/src/core/embeddings/embedding-pipeline.ts
new file mode 100644
index 000000000..05f8ae7ed
--- /dev/null
+++ b/gitnexus-web/src/core/embeddings/embedding-pipeline.ts
@@ -0,0 +1,399 @@
+/**
+ * Embedding Pipeline Module
+ * 
+ * Orchestrates the background embedding process:
+ * 1. Query embeddable nodes from KuzuDB
+ * 2. Generate text representations
+ * 3. Batch embed using transformers.js
+ * 4. Update KuzuDB with embeddings
+ * 5. Create vector index for semantic search
+ */
+
+import { initEmbedder, embedBatch, embedText, embeddingToArray, isEmbedderReady } from './embedder';
+import { generateBatchEmbeddingTexts, generateEmbeddingText } from './text-generator';
+import {
+  type EmbeddingProgress,
+  type EmbeddingConfig,
+  type EmbeddableNode,
+  type SemanticSearchResult,
+  type ModelProgress,
+  DEFAULT_EMBEDDING_CONFIG,
+  EMBEDDABLE_LABELS,
+} from './types';
+
+/**
+ * Progress callback type
+ */
+export type EmbeddingProgressCallback = (progress: EmbeddingProgress) => void;
+
+/**
+ * Query all embeddable nodes from KuzuDB
+ * Uses table-specific queries (File has different schema than code elements)
+ */
+const queryEmbeddableNodes = async (
+  executeQuery: (cypher: string) => Promise
+): Promise => {
+  const allNodes: EmbeddableNode[] = [];
+  
+  // Query each embeddable table with table-specific columns
+  for (const label of EMBEDDABLE_LABELS) {
+    try {
+      let query: string;
+      
+      if (label === 'File') {
+        // File nodes don't have startLine/endLine
+        query = `
+          MATCH (n:File)
+          RETURN n.id AS id, n.name AS name, 'File' AS label, 
+                 n.filePath AS filePath, n.content AS content
+        `;
+      } else {
+        // Code elements have startLine/endLine
+        query = `
+          MATCH (n:${label})
+          RETURN n.id AS id, n.name AS name, '${label}' AS label, 
+                 n.filePath AS filePath, n.content AS content,
+                 n.startLine AS startLine, n.endLine AS endLine
+        `;
+      }
+      
+      const rows = await executeQuery(query);
+      for (const row of rows) {
+        allNodes.push({
+          id: row.id ?? row[0],
+          name: row.name ?? row[1],
+          label: row.label ?? row[2],
+          filePath: row.filePath ?? row[3],
+          content: row.content ?? row[4] ?? '',
+          startLine: row.startLine ?? row[5],
+          endLine: row.endLine ?? row[6],
+        });
+      }
+    } catch (error) {
+      // Table might not exist or be empty, continue
+      if (import.meta.env.DEV) {
+        console.warn(`Query for ${label} nodes failed:`, error);
+      }
+    }
+  }
+
+  return allNodes;
+};
+
+/**
+ * Batch INSERT embeddings into separate CodeEmbedding table
+ * Using a separate lightweight table avoids copy-on-write overhead
+ * that occurs when UPDATEing nodes with large content fields
+ */
+const batchInsertEmbeddings = async (
+  executeWithReusedStatement: (
+    cypher: string,
+    paramsList: Array>
+  ) => Promise,
+  updates: Array<{ id: string; embedding: number[] }>
+): Promise => {
+  // INSERT into separate embedding table - much more memory efficient!
+  const cypher = `CREATE (e:CodeEmbedding {nodeId: $nodeId, embedding: $embedding})`;
+  const paramsList = updates.map(u => ({ nodeId: u.id, embedding: u.embedding }));
+  await executeWithReusedStatement(cypher, paramsList);
+};
+
+/**
+ * Create the vector index for semantic search
+ * Now indexes the separate CodeEmbedding table
+ */
+const createVectorIndex = async (
+  executeQuery: (cypher: string) => Promise
+): Promise => {
+  const cypher = `
+    CALL CREATE_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', 'embedding', metric := 'cosine')
+  `;
+
+  try {
+    await executeQuery(cypher);
+  } catch (error) {
+    // Index might already exist
+    if (import.meta.env.DEV) {
+      console.warn('Vector index creation warning:', error);
+    }
+  }
+};
+
+/**
+ * Run the embedding pipeline
+ * 
+ * @param executeQuery - Function to execute Cypher queries against KuzuDB
+ * @param executeWithReusedStatement - Function to execute with reused prepared statement
+ * @param onProgress - Callback for progress updates
+ * @param config - Optional configuration override
+ */
+export const runEmbeddingPipeline = async (
+  executeQuery: (cypher: string) => Promise,
+  executeWithReusedStatement: (cypher: string, paramsList: Array>) => Promise,
+  onProgress: EmbeddingProgressCallback,
+  config: Partial = {}
+): Promise => {
+  const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config };
+
+  try {
+    // Phase 1: Load embedding model
+    onProgress({
+      phase: 'loading-model',
+      percent: 0,
+      modelDownloadPercent: 0,
+    });
+
+    await initEmbedder((modelProgress: ModelProgress) => {
+      // Report model download progress
+      const downloadPercent = modelProgress.progress ?? 0;
+      onProgress({
+        phase: 'loading-model',
+        percent: Math.round(downloadPercent * 0.2), // 0-20% for model loading
+        modelDownloadPercent: downloadPercent,
+      });
+    }, finalConfig);
+
+    onProgress({
+      phase: 'loading-model',
+      percent: 20,
+      modelDownloadPercent: 100,
+    });
+
+    if (import.meta.env.DEV) {
+      console.log('🔍 Querying embeddable nodes...');
+    }
+
+    // Phase 2: Query embeddable nodes
+    const nodes = await queryEmbeddableNodes(executeQuery);
+    const totalNodes = nodes.length;
+
+    if (import.meta.env.DEV) {
+      console.log(`📊 Found ${totalNodes} embeddable nodes`);
+    }
+
+    if (totalNodes === 0) {
+      onProgress({
+        phase: 'ready',
+        percent: 100,
+        nodesProcessed: 0,
+        totalNodes: 0,
+      });
+      return;
+    }
+
+    // Phase 3: Batch embed nodes
+    const batchSize = finalConfig.batchSize;
+    const totalBatches = Math.ceil(totalNodes / batchSize);
+    let processedNodes = 0;
+
+    onProgress({
+      phase: 'embedding',
+      percent: 20,
+      nodesProcessed: 0,
+      totalNodes,
+      currentBatch: 0,
+      totalBatches,
+    });
+
+    for (let batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
+      const start = batchIndex * batchSize;
+      const end = Math.min(start + batchSize, totalNodes);
+      const batch = nodes.slice(start, end);
+
+      // Generate texts for this batch
+      const texts = generateBatchEmbeddingTexts(batch, finalConfig);
+
+      // Embed the batch
+      const embeddings = await embedBatch(texts);
+
+      // Update KuzuDB with embeddings
+      const updates = batch.map((node, i) => ({
+        id: node.id,
+        embedding: embeddingToArray(embeddings[i]),
+      }));
+
+      await batchInsertEmbeddings(executeWithReusedStatement, updates);
+
+      processedNodes += batch.length;
+
+      // Report progress (20-90% for embedding phase)
+      const embeddingProgress = 20 + ((processedNodes / totalNodes) * 70);
+      onProgress({
+        phase: 'embedding',
+        percent: Math.round(embeddingProgress),
+        nodesProcessed: processedNodes,
+        totalNodes,
+        currentBatch: batchIndex + 1,
+        totalBatches,
+      });
+    }
+
+    // Phase 4: Create vector index
+    onProgress({
+      phase: 'indexing',
+      percent: 90,
+      nodesProcessed: totalNodes,
+      totalNodes,
+    });
+
+    if (import.meta.env.DEV) {
+      console.log('📇 Creating vector index...');
+    }
+
+    await createVectorIndex(executeQuery);
+
+    // Complete
+    onProgress({
+      phase: 'ready',
+      percent: 100,
+      nodesProcessed: totalNodes,
+      totalNodes,
+    });
+
+    if (import.meta.env.DEV) {
+      console.log('✅ Embedding pipeline complete!');
+    }
+  } catch (error) {
+    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
+    
+    if (import.meta.env.DEV) {
+      console.error('❌ Embedding pipeline error:', error);
+    }
+
+    onProgress({
+      phase: 'error',
+      percent: 0,
+      error: errorMessage,
+    });
+
+    throw error;
+  }
+};
+
+/**
+ * Perform semantic search using the vector index
+ * 
+ * Uses CodeEmbedding table and queries each node table to get metadata
+ * 
+ * @param executeQuery - Function to execute Cypher queries
+ * @param query - Search query text
+ * @param k - Number of results to return (default: 10)
+ * @param maxDistance - Maximum distance threshold (default: 0.5)
+ * @returns Array of search results ordered by relevance
+ */
+export const semanticSearch = async (
+  executeQuery: (cypher: string) => Promise,
+  query: string,
+  k: number = 10,
+  maxDistance: number = 0.5
+): Promise => {
+  if (!isEmbedderReady()) {
+    throw new Error('Embedding model not initialized. Run embedding pipeline first.');
+  }
+
+  // Embed the query
+  const queryEmbedding = await embedText(query);
+  const queryVec = embeddingToArray(queryEmbedding);
+  const queryVecStr = `[${queryVec.join(',')}]`;
+
+  // Query the vector index on CodeEmbedding to get nodeIds and distances
+  const vectorQuery = `
+    CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', 
+      CAST(${queryVecStr} AS FLOAT[384]), ${k})
+    YIELD node AS emb, distance
+    WITH emb, distance
+    WHERE distance < ${maxDistance}
+    RETURN emb.nodeId AS nodeId, distance
+    ORDER BY distance
+  `;
+
+  const embResults = await executeQuery(vectorQuery);
+  
+  if (embResults.length === 0) {
+    return [];
+  }
+
+  // Get metadata for each result by querying each node table
+  const results: SemanticSearchResult[] = [];
+  
+  for (const embRow of embResults) {
+    const nodeId = embRow.nodeId ?? embRow[0];
+    const distance = embRow.distance ?? embRow[1];
+    
+    // Extract label from node ID (format: Label:path:name)
+    const labelEndIdx = nodeId.indexOf(':');
+    const label = labelEndIdx > 0 ? nodeId.substring(0, labelEndIdx) : 'Unknown';
+    
+    // Query the specific table for this node
+    // File nodes don't have startLine/endLine
+    try {
+      let nodeQuery: string;
+      if (label === 'File') {
+        nodeQuery = `
+          MATCH (n:File {id: '${nodeId.replace(/'/g, "''")}'}) 
+          RETURN n.name AS name, n.filePath AS filePath
+        `;
+      } else {
+        nodeQuery = `
+          MATCH (n:${label} {id: '${nodeId.replace(/'/g, "''")}'}) 
+          RETURN n.name AS name, n.filePath AS filePath, 
+                 n.startLine AS startLine, n.endLine AS endLine
+        `;
+      }
+      const nodeRows = await executeQuery(nodeQuery);
+      if (nodeRows.length > 0) {
+        const nodeRow = nodeRows[0];
+        results.push({
+          nodeId,
+          name: nodeRow.name ?? nodeRow[0] ?? '',
+          label,
+          filePath: nodeRow.filePath ?? nodeRow[1] ?? '',
+          distance,
+          startLine: label !== 'File' ? (nodeRow.startLine ?? nodeRow[2]) : undefined,
+          endLine: label !== 'File' ? (nodeRow.endLine ?? nodeRow[3]) : undefined,
+        });
+      }
+    } catch {
+      // Table might not exist, skip
+    }
+  }
+
+  return results;
+};
+
+/**
+ * Semantic search with graph expansion (flattened results)
+ * 
+ * Note: With multi-table schema, graph traversal is simplified.
+ * Returns semantic matches with their metadata.
+ * For full graph traversal, use execute_vector_cypher tool directly.
+ * 
+ * @param executeQuery - Function to execute Cypher queries
+ * @param query - Search query text
+ * @param k - Number of initial semantic matches (default: 5)
+ * @param _hops - Unused (kept for API compatibility).
+ * @returns Semantic matches with metadata
+ */
+export const semanticSearchWithContext = async (
+  executeQuery: (cypher: string) => Promise,
+  query: string,
+  k: number = 5,
+  _hops: number = 1
+): Promise => {
+  // For multi-table schema, just return semantic search results
+  // Graph traversal is complex with separate tables - use execute_vector_cypher instead
+  const results = await semanticSearch(executeQuery, query, k, 0.5);
+  
+  return results.map(r => ({
+    matchId: r.nodeId,
+    matchName: r.name,
+    matchLabel: r.label,
+    matchPath: r.filePath,
+    distance: r.distance,
+    connectedId: null,
+    connectedName: null,
+    connectedLabel: null,
+    relationType: null,
+  }));
+};
+
diff --git a/gitnexus-web/src/core/embeddings/index.ts b/gitnexus-web/src/core/embeddings/index.ts
new file mode 100644
index 000000000..5d384c8d5
--- /dev/null
+++ b/gitnexus-web/src/core/embeddings/index.ts
@@ -0,0 +1,11 @@
+/**
+ * Embeddings Module
+ * 
+ * Re-exports for the embedding pipeline system.
+ */
+
+export * from './types';
+export * from './embedder';
+export * from './text-generator';
+export * from './embedding-pipeline';
+
diff --git a/gitnexus-web/src/core/embeddings/text-generator.ts b/gitnexus-web/src/core/embeddings/text-generator.ts
new file mode 100644
index 000000000..36594e1a8
--- /dev/null
+++ b/gitnexus-web/src/core/embeddings/text-generator.ts
@@ -0,0 +1,235 @@
+/**
+ * Text Generator Module
+ * 
+ * Pure functions to generate embedding text from code nodes.
+ * Combines node metadata with code snippets for semantic matching.
+ */
+
+import type { EmbeddableNode, EmbeddingConfig } from './types';
+import { DEFAULT_EMBEDDING_CONFIG } from './types';
+
+/**
+ * Extract the filename from a file path
+ */
+const getFileName = (filePath: string): string => {
+  const parts = filePath.split('/');
+  return parts[parts.length - 1] || filePath;
+};
+
+/**
+ * Extract the directory path from a file path
+ */
+const getDirectory = (filePath: string): string => {
+  const parts = filePath.split('/');
+  parts.pop();
+  return parts.join('/') || '';
+};
+
+/**
+ * Truncate content to max length, preserving word boundaries
+ */
+const truncateContent = (content: string, maxLength: number): string => {
+  if (content.length <= maxLength) {
+    return content;
+  }
+  
+  // Find last space before maxLength to avoid cutting words
+  const truncated = content.slice(0, maxLength);
+  const lastSpace = truncated.lastIndexOf(' ');
+  
+  if (lastSpace > maxLength * 0.8) {
+    return truncated.slice(0, lastSpace) + '...';
+  }
+  
+  return truncated + '...';
+};
+
+/**
+ * Clean code content for embedding
+ * Removes excessive whitespace while preserving structure
+ */
+const cleanContent = (content: string): string => {
+  return content
+    // Normalize line endings
+    .replace(/\r\n/g, '\n')
+    // Remove excessive blank lines (more than 2)
+    .replace(/\n{3,}/g, '\n\n')
+    // Trim each line
+    .split('\n')
+    .map(line => line.trimEnd())
+    .join('\n')
+    .trim();
+};
+
+/**
+ * Generate embedding text for a Function node
+ */
+const generateFunctionText = (
+  node: EmbeddableNode,
+  maxSnippetLength: number
+): string => {
+  const parts: string[] = [
+    `Function: ${node.name}`,
+    `File: ${getFileName(node.filePath)}`,
+  ];
+
+  const dir = getDirectory(node.filePath);
+  if (dir) {
+    parts.push(`Directory: ${dir}`);
+  }
+
+  if (node.content) {
+    const cleanedContent = cleanContent(node.content);
+    const snippet = truncateContent(cleanedContent, maxSnippetLength);
+    parts.push('', snippet);
+  }
+
+  return parts.join('\n');
+};
+
+/**
+ * Generate embedding text for a Class node
+ */
+const generateClassText = (
+  node: EmbeddableNode,
+  maxSnippetLength: number
+): string => {
+  const parts: string[] = [
+    `Class: ${node.name}`,
+    `File: ${getFileName(node.filePath)}`,
+  ];
+
+  const dir = getDirectory(node.filePath);
+  if (dir) {
+    parts.push(`Directory: ${dir}`);
+  }
+
+  if (node.content) {
+    const cleanedContent = cleanContent(node.content);
+    const snippet = truncateContent(cleanedContent, maxSnippetLength);
+    parts.push('', snippet);
+  }
+
+  return parts.join('\n');
+};
+
+/**
+ * Generate embedding text for a Method node
+ */
+const generateMethodText = (
+  node: EmbeddableNode,
+  maxSnippetLength: number
+): string => {
+  const parts: string[] = [
+    `Method: ${node.name}`,
+    `File: ${getFileName(node.filePath)}`,
+  ];
+
+  const dir = getDirectory(node.filePath);
+  if (dir) {
+    parts.push(`Directory: ${dir}`);
+  }
+
+  if (node.content) {
+    const cleanedContent = cleanContent(node.content);
+    const snippet = truncateContent(cleanedContent, maxSnippetLength);
+    parts.push('', snippet);
+  }
+
+  return parts.join('\n');
+};
+
+/**
+ * Generate embedding text for an Interface node
+ */
+const generateInterfaceText = (
+  node: EmbeddableNode,
+  maxSnippetLength: number
+): string => {
+  const parts: string[] = [
+    `Interface: ${node.name}`,
+    `File: ${getFileName(node.filePath)}`,
+  ];
+
+  const dir = getDirectory(node.filePath);
+  if (dir) {
+    parts.push(`Directory: ${dir}`);
+  }
+
+  if (node.content) {
+    const cleanedContent = cleanContent(node.content);
+    const snippet = truncateContent(cleanedContent, maxSnippetLength);
+    parts.push('', snippet);
+  }
+
+  return parts.join('\n');
+};
+
+/**
+ * Generate embedding text for a File node
+ * Uses file name and first N characters of content
+ */
+const generateFileText = (
+  node: EmbeddableNode,
+  maxSnippetLength: number
+): string => {
+  const parts: string[] = [
+    `File: ${node.name}`,
+    `Path: ${node.filePath}`,
+  ];
+
+  if (node.content) {
+    const cleanedContent = cleanContent(node.content);
+    // For files, use a shorter snippet since they can be very long
+    const snippet = truncateContent(cleanedContent, Math.min(maxSnippetLength, 300));
+    parts.push('', snippet);
+  }
+
+  return parts.join('\n');
+};
+
+/**
+ * Generate embedding text for any embeddable node
+ * Dispatches to the appropriate generator based on node label
+ * 
+ * @param node - The node to generate text for
+ * @param config - Optional configuration for max snippet length
+ * @returns Text suitable for embedding
+ */
+export const generateEmbeddingText = (
+  node: EmbeddableNode,
+  config: Partial = {}
+): string => {
+  const maxSnippetLength = config.maxSnippetLength ?? DEFAULT_EMBEDDING_CONFIG.maxSnippetLength;
+
+  switch (node.label) {
+    case 'Function':
+      return generateFunctionText(node, maxSnippetLength);
+    case 'Class':
+      return generateClassText(node, maxSnippetLength);
+    case 'Method':
+      return generateMethodText(node, maxSnippetLength);
+    case 'Interface':
+      return generateInterfaceText(node, maxSnippetLength);
+    case 'File':
+      return generateFileText(node, maxSnippetLength);
+    default:
+      // Fallback for any other embeddable type
+      return `${node.label}: ${node.name}\nPath: ${node.filePath}`;
+  }
+};
+
+/**
+ * Generate embedding texts for a batch of nodes
+ * 
+ * @param nodes - Array of nodes to generate text for
+ * @param config - Optional configuration
+ * @returns Array of texts in the same order as input nodes
+ */
+export const generateBatchEmbeddingTexts = (
+  nodes: EmbeddableNode[],
+  config: Partial = {}
+): string[] => {
+  return nodes.map(node => generateEmbeddingText(node, config));
+};
+
diff --git a/gitnexus-web/src/core/embeddings/types.ts b/gitnexus-web/src/core/embeddings/types.ts
new file mode 100644
index 000000000..e4a04222b
--- /dev/null
+++ b/gitnexus-web/src/core/embeddings/types.ts
@@ -0,0 +1,117 @@
+/**
+ * Embedding Pipeline Types
+ * 
+ * Type definitions for the embedding generation and semantic search system.
+ */
+
+/**
+ * Node labels that should be embedded for semantic search
+ * These are code elements that benefit from semantic matching
+ */
+export const EMBEDDABLE_LABELS = [
+  'Function',
+  'Class', 
+  'Method',
+  'Interface',
+  'File',
+] as const;
+
+export type EmbeddableLabel = typeof EMBEDDABLE_LABELS[number];
+
+/**
+ * Check if a label should be embedded
+ */
+export const isEmbeddableLabel = (label: string): label is EmbeddableLabel =>
+  EMBEDDABLE_LABELS.includes(label as EmbeddableLabel);
+
+/**
+ * Embedding pipeline phases
+ */
+export type EmbeddingPhase = 
+  | 'idle'
+  | 'loading-model'
+  | 'embedding'
+  | 'indexing'
+  | 'ready'
+  | 'error';
+
+/**
+ * Progress information for the embedding pipeline
+ */
+export interface EmbeddingProgress {
+  phase: EmbeddingPhase;
+  percent: number;
+  modelDownloadPercent?: number;
+  nodesProcessed?: number;
+  totalNodes?: number;
+  currentBatch?: number;
+  totalBatches?: number;
+  error?: string;
+}
+
+/**
+ * Configuration for the embedding pipeline
+ */
+export interface EmbeddingConfig {
+  /** Model identifier for transformers.js */
+  modelId: string;
+  /** Number of nodes to embed in each batch */
+  batchSize: number;
+  /** Embedding vector dimensions */
+  dimensions: number;
+  /** Device to use for inference: 'webgpu' for GPU acceleration, 'wasm' for WASM-based CPU */
+  device: 'webgpu' | 'wasm';
+  /** Maximum characters of code snippet to include */
+  maxSnippetLength: number;
+}
+
+/**
+ * Default embedding configuration
+ * Uses snowflake-arctic-embed-xs for browser efficiency
+ * Tries WebGPU first (fast), user can choose WASM fallback if unavailable
+ */
+export const DEFAULT_EMBEDDING_CONFIG: EmbeddingConfig = {
+  modelId: 'Snowflake/snowflake-arctic-embed-xs',
+  batchSize: 16,
+  dimensions: 384,
+  device: 'webgpu', // WebGPU preferred, WASM fallback available if user chooses
+  maxSnippetLength: 500,
+};
+
+/**
+ * Result from semantic search
+ */
+export interface SemanticSearchResult {
+  nodeId: string;
+  name: string;
+  label: string;
+  filePath: string;
+  distance: number;
+  startLine?: number;
+  endLine?: number;
+}
+
+/**
+ * Node data for embedding (minimal structure from KuzuDB query)
+ */
+export interface EmbeddableNode {
+  id: string;
+  name: string;
+  label: string;
+  filePath: string;
+  content: string;
+  startLine?: number;
+  endLine?: number;
+}
+
+/**
+ * Model download progress from transformers.js
+ */
+export interface ModelProgress {
+  status: 'initiate' | 'download' | 'progress' | 'done' | 'ready';
+  file?: string;
+  progress?: number;
+  loaded?: number;
+  total?: number;
+}
+
diff --git a/gitnexus-web/src/core/graph/graph.ts b/gitnexus-web/src/core/graph/graph.ts
new file mode 100644
index 000000000..1f9653b95
--- /dev/null
+++ b/gitnexus-web/src/core/graph/graph.ts
@@ -0,0 +1,41 @@
+import { GraphNode, GraphRelationship, KnowledgeGraph } from './types'
+
+export const createKnowledgeGraph = (): KnowledgeGraph => {
+  const nodeMap = new Map();
+  const relationshipMap = new Map();
+
+  const addNode = (node: GraphNode) => {
+    if(!nodeMap.has(node.id)) {
+      nodeMap.set(node.id, node);
+    }
+  };
+
+  const addRelationship = (relationship: GraphRelationship) => {
+    if (!relationshipMap.has(relationship.id)) {
+      relationshipMap.set(relationship.id, relationship);
+    }
+  };
+
+  return{
+    get nodes(){
+      return Array.from(nodeMap.values())
+    },
+  
+    get relationships(){
+      return Array.from(relationshipMap.values())
+    },
+
+    // O(1) count getters - avoid creating arrays just for length
+    get nodeCount() {
+      return nodeMap.size;
+    },
+
+    get relationshipCount() {
+      return relationshipMap.size;
+    },
+
+    addNode,
+    addRelationship,
+
+  };
+};
\ No newline at end of file
diff --git a/gitnexus-web/src/core/graph/types.ts b/gitnexus-web/src/core/graph/types.ts
new file mode 100644
index 000000000..7bc9a5a95
--- /dev/null
+++ b/gitnexus-web/src/core/graph/types.ts
@@ -0,0 +1,86 @@
+export type NodeLabel =
+  | 'Project'
+  | 'Package'
+  | 'Module'
+  | 'Folder'
+  | 'File'
+  | 'Class'
+  | 'Function'
+  | 'Method'
+  | 'Variable'
+  | 'Interface'
+  | 'Enum'
+  | 'Decorator'
+  | 'Import'
+  | 'Type'
+  | 'CodeElement'
+  | 'Community'
+  | 'Process';
+
+
+export type NodeProperties = {
+  name: string,
+  filePath: string,
+  startLine?: number,
+  endLine?: number,
+  language?: string,
+  isExported?: boolean,
+  // Community-specific properties
+  heuristicLabel?: string,
+  cohesion?: number,
+  symbolCount?: number,
+  keywords?: string[],
+  description?: string,
+  enrichedBy?: 'heuristic' | 'llm',
+  // Process-specific properties
+  processType?: 'intra_community' | 'cross_community',
+  stepCount?: number,
+  communities?: string[],
+  entryPointId?: string,
+  terminalId?: string,
+  // Entry point scoring (computed by process detection)
+  entryPointScore?: number,
+  entryPointReason?: string,
+}
+
+export type RelationshipType = 
+  | 'CONTAINS' 
+  | 'CALLS' 
+  | 'INHERITS' 
+  | 'OVERRIDES' 
+  | 'IMPORTS'
+  | 'USES'
+  | 'DEFINES'
+  | 'DECORATES'
+  | 'IMPLEMENTS'
+  | 'EXTENDS'
+  | 'MEMBER_OF'
+  | 'STEP_IN_PROCESS'
+
+export interface GraphNode {
+  id:  string,
+  label: NodeLabel,
+  properties: NodeProperties,  
+}
+
+export interface GraphRelationship {
+  id: string,
+  sourceId: string,
+  targetId: string,
+  type: RelationshipType,
+  /** Confidence score 0-1 (1.0 = certain, lower = uncertain resolution) */
+  confidence: number,
+  /** Resolution reason: 'import-resolved', 'same-file', 'fuzzy-global', or empty for non-CALLS */
+  reason: string,
+  /** Step number for STEP_IN_PROCESS relationships (1-indexed) */
+  step?: number,
+}
+
+export interface KnowledgeGraph {
+  nodes: GraphNode[],
+  relationships: GraphRelationship[],
+  nodeCount: number,
+  relationshipCount: number,
+  addNode: (node: GraphNode) => void,
+  addRelationship: (relationship: GraphRelationship) => void,
+}
\ No newline at end of file
diff --git a/gitnexus-web/src/core/ingestion/ast-cache.ts b/gitnexus-web/src/core/ingestion/ast-cache.ts
new file mode 100644
index 000000000..61775416a
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/ast-cache.ts
@@ -0,0 +1,47 @@
+import { LRUCache } from 'lru-cache';
+import Parser from 'web-tree-sitter';
+
+// Define the interface for the Cache
+export interface ASTCache {
+  get: (filePath: string) => Parser.Tree | undefined;
+  set: (filePath: string, tree: Parser.Tree) => void;
+  clear: () => void;
+  stats: () => { size: number; maxSize: number };
+}
+
+export const createASTCache = (maxSize: number = 50): ASTCache => {
+  // Initialize the cache with a 'dispose' handler
+  // This is the magic: When an item is evicted (dropped), this runs automatically.
+  const cache = new LRUCache({
+    max: maxSize,
+    dispose: (tree) => {
+      try {
+        // CRITICAL: Free the WASM memory when the tree leaves the cache
+        tree.delete();
+      } catch (e) {
+        console.warn('Failed to delete tree from WASM memory', e);
+      }
+    }
+  });
+
+  return {
+    get: (filePath: string) => {
+      const tree = cache.get(filePath);
+      return tree; // Returns undefined if not found
+    },
+    
+    set: (filePath: string, tree: Parser.Tree) => {
+      cache.set(filePath, tree);
+    },
+    
+    clear: () => {
+      cache.clear();
+    },
+
+    stats: () => ({
+      size: cache.size,
+      maxSize: maxSize
+    })
+  };
+};
+
diff --git a/gitnexus-web/src/core/ingestion/call-processor.ts b/gitnexus-web/src/core/ingestion/call-processor.ts
new file mode 100644
index 000000000..2b71c5aaa
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/call-processor.ts
@@ -0,0 +1,314 @@
+import { KnowledgeGraph } from '../graph/types';
+import { ASTCache } from './ast-cache';
+import { SymbolTable } from './symbol-table';
+import { ImportMap } from './import-processor';
+import { loadParser, loadLanguage } from '../tree-sitter/parser-loader';
+import { LANGUAGE_QUERIES } from './tree-sitter-queries';
+import { generateId } from '../../lib/utils';
+import { getLanguageFromFilename } from './utils';
+
+/**
+ * Node types that represent function/method definitions across languages.
+ * Used to find the enclosing function for a call site.
+ */
+const FUNCTION_NODE_TYPES = new Set([
+  // TypeScript/JavaScript
+  'function_declaration',
+  'arrow_function',
+  'function_expression',
+  'method_definition',
+  'generator_function_declaration',
+  // Python
+  'function_definition',
+  // Common async variants
+  'async_function_declaration',
+  'async_arrow_function',
+  // Java
+  'method_declaration',
+  'constructor_declaration',
+  // C/C++
+  // 'function_definition' already included above
+  // Go
+  // 'method_declaration' already included from Java
+  // C#
+  'local_function_statement',
+  // Rust
+  'function_item',
+  'impl_item', // Methods inside impl blocks
+]);
+
+/**
+ * Walk up the AST from a node to find the enclosing function/method.
+ * Returns null if the call is at module/file level (top-level code).
+ */
+const findEnclosingFunction = (
+  node: any,
+  filePath: string,
+  symbolTable: SymbolTable
+): string | null => {
+  let current = node.parent;
+  
+  while (current) {
+    if (FUNCTION_NODE_TYPES.has(current.type)) {
+      // Found enclosing function - try to get its name
+      let funcName: string | null = null;
+      let label = 'Function';
+      
+      // Different node types have different name locations
+      if (current.type === 'function_declaration' || 
+          current.type === 'function_definition' ||
+          current.type === 'async_function_declaration' ||
+          current.type === 'generator_function_declaration' ||
+          current.type === 'function_item') { // Rust function
+        // Named function: function foo() {}
+        const nameNode = current.childForFieldName?.('name') || 
+                         current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier');
+        funcName = nameNode?.text;
+      } else if (current.type === 'impl_item') {
+        // Rust method inside impl block: wrapper around function_item or const_item
+        // We need to look inside for the function_item
+        const funcItem = current.children?.find((c: any) => c.type === 'function_item');
+        if (funcItem) {
+           const nameNode = funcItem.childForFieldName?.('name') || 
+                            funcItem.children?.find((c: any) => c.type === 'identifier');
+           funcName = nameNode?.text;
+           label = 'Method';
+        }
+      } else if (current.type === 'method_definition') {
+        // Method: foo() {} inside class (JS/TS)
+        const nameNode = current.childForFieldName?.('name') ||
+                         current.children?.find((c: any) => c.type === 'property_identifier');
+        funcName = nameNode?.text;
+        label = 'Method';
+      } else if (current.type === 'method_declaration') {
+        // Java method: public void foo() {}
+        const nameNode = current.childForFieldName?.('name') ||
+                         current.children?.find((c: any) => c.type === 'identifier');
+        funcName = nameNode?.text;
+        label = 'Method';
+      } else if (current.type === 'constructor_declaration') {
+        // Java constructor: public ClassName() {}
+        const nameNode = current.childForFieldName?.('name') ||
+                         current.children?.find((c: any) => c.type === 'identifier');
+        funcName = nameNode?.text;
+        label = 'Method'; // Treat constructors as methods for process detection
+      } else if (current.type === 'arrow_function' || current.type === 'function_expression') {
+        // Arrow/expression: const foo = () => {} - check parent variable declarator
+        const parent = current.parent;
+        if (parent?.type === 'variable_declarator') {
+          const nameNode = parent.childForFieldName?.('name') ||
+                           parent.children?.find((c: any) => c.type === 'identifier');
+          funcName = nameNode?.text;
+        }
+      }
+      
+      if (funcName) {
+        // Look up the function in symbol table to get its node ID
+        // Try exact match first
+        const nodeId = symbolTable.lookupExact(filePath, funcName);
+        if (nodeId) return nodeId;
+        
+        // Try construct ID manually if lookup fails (common for non-exported internal functions)
+        // Format should match what parsing-processor generates: "Function:path/to/file:funcName"
+        // Check if we already have a node with this ID in the symbol table to be safe
+        const generatedId = generateId(label, `${filePath}:${funcName}`);
+        
+        // Ideally we should verify this ID exists, but strictly speaking if we are inside it,
+        // it SHOULD exist. Returning it is better than falling back to File.
+        return generatedId;
+      }
+      
+      // Couldn't determine function name - try parent (might be nested)
+    }
+    current = current.parent;
+  }
+  
+  return null; // Top-level call (not inside any function)
+};
+
+export const processCalls = async (
+  graph: KnowledgeGraph,
+  files: { path: string; content: string }[],
+  astCache: ASTCache,
+  symbolTable: SymbolTable,
+  importMap: ImportMap,
+  onProgress?: (current: number, total: number) => void
+) => {
+  const parser = await loadParser();
+
+  for (let i = 0; i < files.length; i++) {
+    const file = files[i];
+    onProgress?.(i + 1, files.length);
+
+    // 1. Check language support first
+    const language = getLanguageFromFilename(file.path);
+    if (!language) continue;
+
+    const queryStr = LANGUAGE_QUERIES[language];
+    if (!queryStr) continue;
+
+    // 2. ALWAYS load the language before querying (parser is stateful)
+    await loadLanguage(language, file.path);
+
+    // 3. Get AST (Try Cache First)
+    let tree = astCache.get(file.path);
+    let wasReparsed = false;
+
+    if (!tree) {
+      // Cache Miss: Re-parse
+      tree = parser.parse(file.content);
+      wasReparsed = true;
+    }
+
+    let query;
+    let matches;
+    try {
+      query = parser.getLanguage().query(queryStr);
+      matches = query.matches(tree.rootNode);
+    } catch (queryError) {
+      console.warn(`Query error for ${file.path}:`, queryError);
+      if (wasReparsed) tree.delete();
+      continue;
+    }
+
+    // 3. Process each call match
+    matches.forEach(match => {
+      const captureMap: Record = {};
+      match.captures.forEach(c => captureMap[c.name] = c.node);
+
+      // Only process @call captures
+      if (!captureMap['call']) return;
+
+      const nameNode = captureMap['call.name'];
+      if (!nameNode) return;
+
+      const calledName = nameNode.text;
+
+      // Skip common built-ins and noise
+      if (isBuiltInOrNoise(calledName)) return;
+
+      // 4. Resolve the target using priority strategy (returns confidence)
+      const resolved = resolveCallTarget(
+        calledName,
+        file.path,
+        symbolTable,
+        importMap
+      );
+
+      if (!resolved) return;
+
+      // 5. Find the enclosing function (caller)
+      const callNode = captureMap['call'];
+      const enclosingFuncId = findEnclosingFunction(callNode, file.path, symbolTable);
+      
+      // Use enclosing function as source, fallback to file for top-level calls
+      const sourceId = enclosingFuncId || generateId('File', file.path);
+      
+      const relId = generateId('CALLS', `${sourceId}:${calledName}->${resolved.nodeId}`);
+
+      graph.addRelationship({
+        id: relId,
+        sourceId,
+        targetId: resolved.nodeId,
+        type: 'CALLS',
+        confidence: resolved.confidence,
+        reason: resolved.reason,
+      });
+    });
+
+    // Cleanup if re-parsed
+    if (wasReparsed) {
+      tree.delete();
+    }
+  }
+};
+
+/**
+ * Resolution result with confidence scoring
+ */
+interface ResolveResult {
+  nodeId: string;
+  confidence: number;  // 0-1: how sure are we?
+  reason: string;      // 'import-resolved' | 'same-file' | 'fuzzy-global'
+}
+
+/**
+ * Resolve a function call to its target node ID using priority strategy:
+ * A. Check imported files first (highest confidence)
+ * B. Check local file definitions
+ * C. Fuzzy global search (lowest confidence)
+ * 
+ * Returns confidence score so agents know what to trust.
+ */
+const resolveCallTarget = (
+  calledName: string,
+  currentFile: string,
+  symbolTable: SymbolTable,
+  importMap: ImportMap
+): ResolveResult | null => {
+  // Strategy A: Check imported files (HIGH confidence - we know the import chain)
+  const importedFiles = importMap.get(currentFile);
+  if (importedFiles) {
+    for (const importedFile of importedFiles) {
+      const nodeId = symbolTable.lookupExact(importedFile, calledName);
+      if (nodeId) {
+        return { nodeId, confidence: 0.9, reason: 'import-resolved' };
+      }
+    }
+  }
+
+  // Strategy B: Check local file (HIGH confidence - same file definition)
+  const localNodeId = symbolTable.lookupExact(currentFile, calledName);
+  if (localNodeId) {
+    return { nodeId: localNodeId, confidence: 0.85, reason: 'same-file' };
+  }
+
+  // Strategy C: Fuzzy global search (LOW confidence - just matching by name)
+  const fuzzyMatches = symbolTable.lookupFuzzy(calledName);
+  if (fuzzyMatches.length > 0) {
+    // Lower confidence if multiple matches exist (more ambiguous)
+    const confidence = fuzzyMatches.length === 1 ? 0.5 : 0.3;
+    return { nodeId: fuzzyMatches[0].nodeId, confidence, reason: 'fuzzy-global' };
+  }
+
+  return null;
+};
+
+/**
+ * Filter out common built-in functions and noise
+ * that shouldn't be tracked as calls
+ */
+const isBuiltInOrNoise = (name: string): boolean => {
+  const builtIns = new Set([
+    // JavaScript/TypeScript built-ins
+    'console', 'log', 'warn', 'error', 'info', 'debug',
+    'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
+    'parseInt', 'parseFloat', 'isNaN', 'isFinite',
+    'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
+    'JSON', 'parse', 'stringify',
+    'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
+    'Map', 'Set', 'WeakMap', 'WeakSet',
+    'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
+    'Math', 'Date', 'RegExp', 'Error',
+    'require', 'import', 'export',
+    'fetch', 'Response', 'Request',
+    // React hooks and common functions
+    'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
+    'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
+    'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
+    // Common array/object methods
+    'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
+    'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
+    'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
+    'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
+    'hasOwnProperty', 'toString', 'valueOf',
+    // Python built-ins
+    'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
+    'open', 'read', 'write', 'close', 'append', 'extend', 'update',
+    'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
+    'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
+  ]);
+
+  return builtIns.has(name);
+};
+
diff --git a/gitnexus-web/src/core/ingestion/cluster-enricher.ts b/gitnexus-web/src/core/ingestion/cluster-enricher.ts
new file mode 100644
index 000000000..51e00d618
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/cluster-enricher.ts
@@ -0,0 +1,245 @@
+/**
+ * Cluster Enricher
+ * 
+ * LLM-based enrichment for community clusters.
+ * Generates semantic names, keywords, and descriptions using an LLM.
+ */
+
+import { CommunityNode } from './community-processor';
+
+// ============================================================================
+// TYPES
+// ============================================================================
+
+export interface ClusterEnrichment {
+  name: string;
+  keywords: string[];
+  description: string;
+}
+
+export interface EnrichmentResult {
+  enrichments: Map;
+  tokensUsed: number;
+}
+
+export interface LLMClient {
+  generate: (prompt: string) => Promise;
+}
+
+export interface ClusterMemberInfo {
+  name: string;
+  filePath: string;
+  type: string; // 'Function' | 'Class' | 'Method' | 'Interface'
+}
+
+// ============================================================================
+// PROMPT TEMPLATE
+// ============================================================================
+
+const buildEnrichmentPrompt = (
+  members: ClusterMemberInfo[],
+  heuristicLabel: string
+): string => {
+  // Limit to first 20 members to control token usage
+  const limitedMembers = members.slice(0, 20);
+  
+  const memberList = limitedMembers
+    .map(m => `${m.name} (${m.type})`)
+    .join(', ');
+  
+  return `Analyze this code cluster and provide a semantic name and short description.
+
+Heuristic: "${heuristicLabel}"
+Members: ${memberList}${members.length > 20 ? ` (+${members.length - 20} more)` : ''}
+
+Reply with JSON only:
+{"name": "2-4 word semantic name", "description": "One sentence describing purpose"}`
+};
+
+// ============================================================================
+// PARSE LLM RESPONSE
+// ============================================================================
+
+const parseEnrichmentResponse = (
+  response: string,
+  fallbackLabel: string
+): ClusterEnrichment => {
+  try {
+    // Extract JSON from response (handles markdown code blocks)
+    const jsonMatch = response.match(/\{[\s\S]*\}/);
+    if (!jsonMatch) {
+      throw new Error('No JSON found in response');
+    }
+    
+    const parsed = JSON.parse(jsonMatch[0]);
+    
+    return {
+      name: parsed.name || fallbackLabel,
+      keywords: Array.isArray(parsed.keywords) ? parsed.keywords : [],
+      description: parsed.description || '',
+    };
+  } catch {
+    // Fallback if parsing fails
+    return {
+      name: fallbackLabel,
+      keywords: [],
+      description: '',
+    };
+  }
+};
+
+// ============================================================================
+// MAIN ENRICHMENT FUNCTION
+// ============================================================================
+
+/**
+ * Enrich clusters with LLM-generated names, keywords, and descriptions
+ * 
+ * @param communities - Community nodes to enrich
+ * @param memberMap - Map of communityId -> member info
+ * @param llmClient - LLM client for generation
+ * @param onProgress - Progress callback
+ */
+export const enrichClusters = async (
+  communities: CommunityNode[],
+  memberMap: Map,
+  llmClient: LLMClient,
+  onProgress?: (current: number, total: number) => void
+): Promise => {
+  const enrichments = new Map();
+  let tokensUsed = 0;
+  
+  for (let i = 0; i < communities.length; i++) {
+    const community = communities[i];
+    const members = memberMap.get(community.id) || [];
+    
+    onProgress?.(i + 1, communities.length);
+    
+    if (members.length === 0) {
+      // No members, use heuristic
+      enrichments.set(community.id, {
+        name: community.heuristicLabel,
+        keywords: [],
+        description: '',
+      });
+      continue;
+    }
+    
+    try {
+      const prompt = buildEnrichmentPrompt(members, community.heuristicLabel);
+      const response = await llmClient.generate(prompt);
+      
+      // Rough token estimate
+      tokensUsed += prompt.length / 4 + response.length / 4;
+      
+      const enrichment = parseEnrichmentResponse(response, community.heuristicLabel);
+      enrichments.set(community.id, enrichment);
+    } catch (error) {
+      // On error, fallback to heuristic
+      console.warn(`Failed to enrich cluster ${community.id}:`, error);
+      enrichments.set(community.id, {
+        name: community.heuristicLabel,
+        keywords: [],
+        description: '',
+      });
+    }
+  }
+  
+  return { enrichments, tokensUsed };
+};
+
+// ============================================================================
+// BATCH ENRICHMENT (more efficient)
+// ============================================================================
+
+/**
+ * Enrich multiple clusters in a single LLM call (batch mode)
+ * More efficient for token usage but requires larger context window
+ */
+export const enrichClustersBatch = async (
+  communities: CommunityNode[],
+  memberMap: Map,
+  llmClient: LLMClient,
+  batchSize: number = 5,
+  onProgress?: (current: number, total: number) => void
+): Promise => {
+  const enrichments = new Map();
+  let tokensUsed = 0;
+  
+  // Process in batches
+  for (let i = 0; i < communities.length; i += batchSize) {
+    // Report progress
+    onProgress?.(Math.min(i + batchSize, communities.length), communities.length);
+
+    const batch = communities.slice(i, i + batchSize);
+    
+    const batchPrompt = batch.map((community, idx) => {
+      const members = memberMap.get(community.id) || [];
+      const limitedMembers = members.slice(0, 15);
+      const memberList = limitedMembers
+        .map(m => `${m.name} (${m.type})`)
+        .join(', ');
+      
+      return `Cluster ${idx + 1} (id: ${community.id}):
+Heuristic: "${community.heuristicLabel}"
+Members: ${memberList}`;
+    }).join('\n\n');
+    
+    const prompt = `Analyze these code clusters and generate semantic names, keywords, and descriptions.
+
+${batchPrompt}
+
+Output JSON array:
+[
+  {"id": "comm_X", "name": "...", "keywords": [...], "description": "..."},
+  ...
+]`;
+    
+    try {
+      const response = await llmClient.generate(prompt);
+      tokensUsed += prompt.length / 4 + response.length / 4;
+      
+      // Parse batch response
+      const jsonMatch = response.match(/\[[\s\S]*\]/);
+      if (jsonMatch) {
+        const parsed = JSON.parse(jsonMatch[0]) as Array<{
+          id: string;
+          name: string;
+          keywords: string[];
+          description: string;
+        }>;
+        
+        for (const item of parsed) {
+          enrichments.set(item.id, {
+            name: item.name,
+            keywords: item.keywords || [],
+            description: item.description || '',
+          });
+        }
+      }
+    } catch (error) {
+      console.warn('Batch enrichment failed, falling back to heuristics:', error);
+      // Fallback for this batch
+      for (const community of batch) {
+        enrichments.set(community.id, {
+          name: community.heuristicLabel,
+          keywords: [],
+          description: '',
+        });
+      }
+    }
+  }
+  
+  // Fill in any missing communities
+  for (const community of communities) {
+    if (!enrichments.has(community.id)) {
+      enrichments.set(community.id, {
+        name: community.heuristicLabel,
+        keywords: [],
+        description: '',
+      });
+    }
+  }
+  
+  return { enrichments, tokensUsed };
+};
diff --git a/gitnexus-web/src/core/ingestion/community-processor.ts b/gitnexus-web/src/core/ingestion/community-processor.ts
new file mode 100644
index 000000000..5d898f1ba
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/community-processor.ts
@@ -0,0 +1,354 @@
+/**
+ * Community Detection Processor
+ * 
+ * Uses the Leiden algorithm (vendored from graphology-communities-leiden) to detect
+ * communities/clusters in the code graph based on CALLS relationships.
+ * 
+ * Communities represent groups of code that work together frequently,
+ * helping agents navigate the codebase by functional area rather than file structure.
+ */
+
+import Graph from 'graphology';
+import leiden from '../../vendor/leiden/index.js';
+import { KnowledgeGraph, NodeLabel } from '../graph/types';
+
+// ============================================================================
+// TYPES
+// ============================================================================
+
+export interface CommunityNode {
+  id: string;
+  label: string;
+  heuristicLabel: string;
+  cohesion: number;
+  symbolCount: number;
+}
+
+export interface CommunityMembership {
+  nodeId: string;
+  communityId: string;
+}
+
+export interface CommunityDetectionResult {
+  communities: CommunityNode[];
+  memberships: CommunityMembership[];
+  stats: {
+    totalCommunities: number;
+    modularity: number;
+    nodesProcessed: number;
+  };
+}
+
+// ============================================================================
+// COMMUNITY COLORS (for visualization)
+// ============================================================================
+
+export const COMMUNITY_COLORS = [
+  '#ef4444', // red
+  '#f97316', // orange
+  '#eab308', // yellow
+  '#22c55e', // green
+  '#06b6d4', // cyan
+  '#3b82f6', // blue
+  '#8b5cf6', // violet
+  '#d946ef', // fuchsia
+  '#ec4899', // pink
+  '#f43f5e', // rose
+  '#14b8a6', // teal
+  '#84cc16', // lime
+];
+
+export const getCommunityColor = (communityIndex: number): string => {
+  return COMMUNITY_COLORS[communityIndex % COMMUNITY_COLORS.length];
+};
+
+// ============================================================================
+// MAIN PROCESSOR
+// ============================================================================
+
+/**
+ * Detect communities in the knowledge graph using Leiden algorithm
+ * 
+ * This runs AFTER all relationships (CALLS, IMPORTS, etc.) have been built.
+ * It uses primarily CALLS edges to cluster code that works together.
+ */
+export const processCommunities = async (
+  knowledgeGraph: KnowledgeGraph,
+  onProgress?: (message: string, progress: number) => void
+): Promise => {
+  onProgress?.('Building graph for community detection...', 0);
+
+  // Step 1: Build a graphology graph from the knowledge graph
+  // We only include symbol nodes (Function, Class, Method) and CALLS edges
+  const graph = buildGraphologyGraph(knowledgeGraph);
+  
+  if (graph.order === 0) {
+    // No nodes to cluster
+    return {
+      communities: [],
+      memberships: [],
+      stats: { totalCommunities: 0, modularity: 0, nodesProcessed: 0 }
+    };
+  }
+
+  onProgress?.(`Running Leiden algorithm on ${graph.order} nodes...`, 30);
+
+  // Step 2: Run Leiden algorithm for community detection
+  const details = leiden.detailed(graph, {
+    resolution: 1.0,  // Default resolution, can be tuned
+    randomWalk: true,
+  });
+
+  onProgress?.(`Found ${details.count} communities...`, 60);
+
+  // Step 3: Create community nodes with heuristic labels
+  const communityNodes = createCommunityNodes(
+    details.communities as Record,
+    details.count,
+    graph,
+    knowledgeGraph
+  );
+
+  onProgress?.('Creating membership edges...', 80);
+
+  // Step 4: Create membership mappings
+  const memberships: CommunityMembership[] = [];
+  Object.entries(details.communities).forEach(([nodeId, communityNum]) => {
+    memberships.push({
+      nodeId,
+      communityId: `comm_${communityNum}`,
+    });
+  });
+
+  onProgress?.('Community detection complete!', 100);
+
+  return {
+    communities: communityNodes,
+    memberships,
+    stats: {
+      totalCommunities: details.count,
+      modularity: details.modularity,
+      nodesProcessed: graph.order,
+    }
+  };
+};
+
+// ============================================================================
+// HELPER: Build graphology graph from knowledge graph
+// ============================================================================
+
+/**
+ * Build a graphology graph containing only symbol nodes and CALLS edges
+ * This is what the Leiden algorithm will cluster
+ */
+const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph): Graph => {
+  // Use undirected graph for Leiden - it looks at edge density, not direction
+  const graph = new Graph({ type: 'undirected', allowSelfLoops: false });
+
+  // Symbol types that should be clustered
+  const symbolTypes = new Set(['Function', 'Class', 'Method', 'Interface']);
+  
+  // Add symbol nodes
+  knowledgeGraph.nodes.forEach(node => {
+    if (symbolTypes.has(node.label)) {
+      graph.addNode(node.id, {
+        name: node.properties.name,
+        filePath: node.properties.filePath,
+        type: node.label,
+      });
+    }
+  });
+
+  // Add CALLS edges (primary clustering signal)
+  // We can also include EXTENDS/IMPLEMENTS for OOP clustering
+  const clusteringRelTypes = new Set(['CALLS', 'EXTENDS', 'IMPLEMENTS']);
+  
+  knowledgeGraph.relationships.forEach(rel => {
+    if (clusteringRelTypes.has(rel.type)) {
+      // Only add edge if both nodes exist in our symbol graph
+      // Also skip self-loops (recursive calls) - not allowed in undirected graph
+      if (graph.hasNode(rel.sourceId) && graph.hasNode(rel.targetId) && rel.sourceId !== rel.targetId) {
+        // Avoid duplicate edges
+        if (!graph.hasEdge(rel.sourceId, rel.targetId)) {
+          graph.addEdge(rel.sourceId, rel.targetId);
+        }
+      }
+    }
+  });
+
+  return graph;
+};
+
+// ============================================================================
+// HELPER: Create community nodes with heuristic labels
+// ============================================================================
+
+/**
+ * Create Community nodes with auto-generated labels based on member file paths
+ */
+const createCommunityNodes = (
+  communities: Record,
+  communityCount: number,
+  graph: Graph,
+  knowledgeGraph: KnowledgeGraph
+): CommunityNode[] => {
+  // Group node IDs by community
+  const communityMembers = new Map();
+  
+  Object.entries(communities).forEach(([nodeId, commNum]) => {
+    if (!communityMembers.has(commNum)) {
+      communityMembers.set(commNum, []);
+    }
+    communityMembers.get(commNum)!.push(nodeId);
+  });
+
+  // Build node lookup for file paths
+  const nodePathMap = new Map();
+  knowledgeGraph.nodes.forEach(node => {
+    if (node.properties.filePath) {
+      nodePathMap.set(node.id, node.properties.filePath);
+    }
+  });
+
+  // Create community nodes - SKIP SINGLETONS (isolated nodes)
+  const communityNodes: CommunityNode[] = [];
+  
+  communityMembers.forEach((memberIds, commNum) => {
+    // Skip singleton communities - they're just isolated nodes
+    if (memberIds.length < 2) return;
+    
+    const heuristicLabel = generateHeuristicLabel(memberIds, nodePathMap, graph, commNum);
+    
+    communityNodes.push({
+      id: `comm_${commNum}`,
+      label: heuristicLabel,
+      heuristicLabel,
+      cohesion: calculateCohesion(memberIds, graph),
+      symbolCount: memberIds.length,
+    });
+  });
+
+  // Sort by size descending
+  communityNodes.sort((a, b) => b.symbolCount - a.symbolCount);
+
+  return communityNodes;
+};
+
+// ============================================================================
+// HELPER: Generate heuristic label from folder patterns
+// ============================================================================
+
+/**
+ * Generate a human-readable label from the most common folder name in the community
+ */
+const generateHeuristicLabel = (
+  memberIds: string[],
+  nodePathMap: Map,
+  graph: Graph,
+  commNum: number
+): string => {
+  // Collect folder names from file paths
+  const folderCounts = new Map();
+  
+  memberIds.forEach(nodeId => {
+    const filePath = nodePathMap.get(nodeId) || '';
+    const parts = filePath.split('/').filter(Boolean);
+    
+    // Get the most specific folder (parent directory)
+    if (parts.length >= 2) {
+      const folder = parts[parts.length - 2];
+      // Skip generic folder names
+      if (!['src', 'lib', 'core', 'utils', 'common', 'shared', 'helpers'].includes(folder.toLowerCase())) {
+        folderCounts.set(folder, (folderCounts.get(folder) || 0) + 1);
+      }
+    }
+  });
+
+  // Find most common folder
+  let maxCount = 0;
+  let bestFolder = '';
+  
+  folderCounts.forEach((count, folder) => {
+    if (count > maxCount) {
+      maxCount = count;
+      bestFolder = folder;
+    }
+  });
+
+  if (bestFolder) {
+    // Capitalize first letter
+    return bestFolder.charAt(0).toUpperCase() + bestFolder.slice(1);
+  }
+
+  // Fallback: use function names to detect patterns
+  const names: string[] = [];
+  memberIds.forEach(nodeId => {
+    const name = graph.getNodeAttribute(nodeId, 'name');
+    if (name) names.push(name);
+  });
+
+  // Look for common prefixes
+  if (names.length > 2) {
+    const commonPrefix = findCommonPrefix(names);
+    if (commonPrefix.length > 2) {
+      return commonPrefix.charAt(0).toUpperCase() + commonPrefix.slice(1);
+    }
+  }
+
+  // Last resort: generic name with community ID for uniqueness
+  return `Cluster_${commNum}`;
+};
+
+/**
+ * Find common prefix among strings
+ */
+const findCommonPrefix = (strings: string[]): string => {
+  if (strings.length === 0) return '';
+  
+  const sorted = strings.slice().sort();
+  const first = sorted[0];
+  const last = sorted[sorted.length - 1];
+  
+  let i = 0;
+  while (i < first.length && first[i] === last[i]) {
+    i++;
+  }
+  
+  return first.substring(0, i);
+};
+
+// ============================================================================
+// HELPER: Calculate community cohesion
+// ============================================================================
+
+/**
+ * Calculate cohesion score (0-1) based on internal edge density
+ * Higher cohesion = more internal connections relative to size
+ */
+const calculateCohesion = (memberIds: string[], graph: Graph): number => {
+  if (memberIds.length <= 1) return 1.0;
+
+  const memberSet = new Set(memberIds);
+  let internalEdges = 0;
+  
+  // Count edges within the community
+  memberIds.forEach(nodeId => {
+    if (graph.hasNode(nodeId)) {
+      graph.forEachNeighbor(nodeId, neighbor => {
+        if (memberSet.has(neighbor)) {
+          internalEdges++;
+        }
+      });
+    }
+  });
+  
+  // Each edge is counted twice (once from each end), so divide by 2
+  internalEdges = internalEdges / 2;
+  
+  // Maximum possible internal edges for n nodes: n*(n-1)/2
+  const maxPossibleEdges = (memberIds.length * (memberIds.length - 1)) / 2;
+  
+  if (maxPossibleEdges === 0) return 1.0;
+  
+  return Math.min(1.0, internalEdges / maxPossibleEdges);
+};
diff --git a/gitnexus-web/src/core/ingestion/entry-point-scoring.ts b/gitnexus-web/src/core/ingestion/entry-point-scoring.ts
new file mode 100644
index 000000000..1ef3d3ddc
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/entry-point-scoring.ts
@@ -0,0 +1,281 @@
+/**
+ * Entry Point Scoring
+ * 
+ * Calculates entry point scores for process detection based on:
+ * 1. Call ratio (existing algorithm - callees / (callers + 1))
+ * 2. Export status (exported functions get higher priority)
+ * 3. Name patterns (functions matching entry point patterns like handle*, on*, *Controller)
+ * 4. Framework detection (path-based detection for Next.js, Express, Django, etc.)
+ * 
+ * This module is language-agnostic - language-specific patterns are defined per language.
+ */
+
+import { detectFrameworkFromPath } from './framework-detection';
+
+// ============================================================================
+// NAME PATTERNS - All 9 supported languages
+// ============================================================================
+
+/**
+ * Common entry point naming patterns by language
+ * These patterns indicate functions that are likely feature entry points
+ */
+const ENTRY_POINT_PATTERNS: Record = {
+  // Universal patterns (apply to all languages)
+  '*': [
+    /^(main|init|bootstrap|start|run|setup|configure)$/i,
+    /^handle[A-Z]/,           // handleLogin, handleSubmit
+    /^on[A-Z]/,               // onClick, onSubmit
+    /Handler$/,               // RequestHandler
+    /Controller$/,            // UserController
+    /^process[A-Z]/,          // processPayment
+    /^execute[A-Z]/,          // executeQuery
+    /^perform[A-Z]/,          // performAction
+    /^dispatch[A-Z]/,         // dispatchEvent
+    /^trigger[A-Z]/,          // triggerAction
+    /^fire[A-Z]/,             // fireEvent
+    /^emit[A-Z]/,             // emitEvent
+  ],
+  
+  // JavaScript/TypeScript
+  'javascript': [
+    /^use[A-Z]/,              // React hooks (useEffect, etc.)
+  ],
+  'typescript': [
+    /^use[A-Z]/,              // React hooks
+  ],
+  
+  // Python
+  'python': [
+    /^app$/,                  // Flask/FastAPI app
+    /^(get|post|put|delete|patch)_/i,  // REST conventions
+    /^api_/,                  // API functions
+    /^view_/,                 // Django views
+  ],
+  
+  // Java
+  'java': [
+    /^do[A-Z]/,               // doGet, doPost (Servlets)
+    /^create[A-Z]/,           // Factory patterns
+    /^build[A-Z]/,            // Builder patterns
+    /Service$/,               // UserService
+  ],
+  
+  // C#
+  'csharp': [
+    /^(Get|Post|Put|Delete)/,  // ASP.NET conventions
+    /Action$/,                 // MVC actions
+    /^On[A-Z]/,               // Event handlers
+    /Async$/,                 // Async entry points
+  ],
+  
+  // Go
+  'go': [
+    /Handler$/,               // http.Handler pattern
+    /^Serve/,                 // ServeHTTP
+    /^New[A-Z]/,              // Constructor pattern (returns new instance)
+    /^Make[A-Z]/,             // Make functions
+  ],
+  
+  // Rust
+  'rust': [
+    /^(get|post|put|delete)_handler$/i,
+    /^handle_/,               // handle_request
+    /^new$/,                  // Constructor pattern
+    /^run$/,                  // run entry point
+    /^spawn/,                 // Async spawn
+  ],
+  
+  // C - explicit main() boost (critical for C programs)
+  'c': [
+    /^main$/,                 // THE entry point
+    /^init_/,                 // Initialization functions
+    /^start_/,                // Start functions
+    /^run_/,                  // Run functions
+  ],
+  
+  // C++ - same as C plus class patterns
+  'cpp': [
+    /^main$/,                 // THE entry point
+    /^init_/,
+    /^Create[A-Z]/,           // Factory patterns
+    /^Run$/,                  // Run methods
+    /^Start$/,                // Start methods
+  ],
+};
+
+// ============================================================================
+// UTILITY PATTERNS - Functions that should be penalized
+// ============================================================================
+
+/**
+ * Patterns that indicate utility/helper functions (NOT entry points)
+ * These get penalized in scoring
+ */
+const UTILITY_PATTERNS: RegExp[] = [
+  /^(get|set|is|has|can|should|will|did)[A-Z]/,  // Accessors/predicates
+  /^_/,                                            // Private by convention
+  /^(format|parse|validate|convert|transform)/i,  // Transformation utilities
+  /^(log|debug|error|warn|info)$/i,               // Logging
+  /^(to|from)[A-Z]/,                              // Conversions
+  /^(encode|decode)/i,                            // Encoding utilities
+  /^(serialize|deserialize)/i,                    // Serialization
+  /^(clone|copy|deep)/i,                          // Cloning utilities
+  /^(merge|extend|assign)/i,                      // Object utilities
+  /^(filter|map|reduce|sort|find)/i,             // Collection utilities (standalone)
+  /Helper$/,
+  /Util$/,
+  /Utils$/,
+  /^utils?$/i,
+  /^helpers?$/i,
+];
+
+// ============================================================================
+// TYPES
+// ============================================================================
+
+export interface EntryPointScoreResult {
+  score: number;
+  reasons: string[];
+}
+
+// ============================================================================
+// MAIN SCORING FUNCTION
+// ============================================================================
+
+/**
+ * Calculate an entry point score for a function/method
+ * 
+ * Higher scores indicate better entry point candidates.
+ * Score = baseScore × exportMultiplier × nameMultiplier
+ * 
+ * @param name - Function/method name
+ * @param language - Programming language
+ * @param isExported - Whether the function is exported/public
+ * @param callerCount - Number of functions that call this function
+ * @param calleeCount - Number of functions this function calls
+ * @returns Score and array of reasons explaining the score
+ */
+export function calculateEntryPointScore(
+  name: string,
+  language: string,
+  isExported: boolean,
+  callerCount: number,
+  calleeCount: number,
+  filePath: string = ''  // Optional for backwards compatibility
+): EntryPointScoreResult {
+  const reasons: string[] = [];
+  
+  // Must have outgoing calls to be an entry point (we need to trace forward)
+  if (calleeCount === 0) {
+    return { score: 0, reasons: ['no-outgoing-calls'] };
+  }
+  
+  // Base score: call ratio (existing algorithm)
+  // High ratio = calls many, called by few = likely entry point
+  const baseScore = calleeCount / (callerCount + 1);
+  reasons.push(`base:${baseScore.toFixed(2)}`);
+  
+  // Export bonus: exported/public functions are more likely entry points
+  const exportMultiplier = isExported ? 2.0 : 1.0;
+  if (isExported) {
+    reasons.push('exported');
+  }
+  
+  // Name pattern scoring
+  let nameMultiplier = 1.0;
+  
+  // Check negative patterns first (utilities get penalized)
+  if (UTILITY_PATTERNS.some(p => p.test(name))) {
+    nameMultiplier = 0.3;  // Significant penalty
+    reasons.push('utility-pattern');
+  } else {
+    // Check positive patterns
+    const universalPatterns = ENTRY_POINT_PATTERNS['*'] || [];
+    const langPatterns = ENTRY_POINT_PATTERNS[language] || [];
+    const allPatterns = [...universalPatterns, ...langPatterns];
+    
+    if (allPatterns.some(p => p.test(name))) {
+      nameMultiplier = 1.5;  // Bonus for matching entry point pattern
+      reasons.push('entry-pattern');
+    }
+  }
+  
+  // Framework detection bonus (Phase 2)
+  let frameworkMultiplier = 1.0;
+  if (filePath) {
+    const frameworkHint = detectFrameworkFromPath(filePath);
+    if (frameworkHint) {
+      frameworkMultiplier = frameworkHint.entryPointMultiplier;
+      reasons.push(`framework:${frameworkHint.reason}`);
+    }
+  }
+  
+  // Calculate final score
+  const finalScore = baseScore * exportMultiplier * nameMultiplier * frameworkMultiplier;
+  
+  return {
+    score: finalScore,
+    reasons,
+  };
+}
+
+// ============================================================================
+// HELPER FUNCTIONS
+// ============================================================================
+
+/**
+ * Check if a file path is a test file (should be excluded from entry points)
+ * Covers common test file patterns across all supported languages
+ */
+export function isTestFile(filePath: string): boolean {
+  const p = filePath.toLowerCase().replace(/\\/g, '/');
+  
+  return (
+    // JavaScript/TypeScript test patterns
+    p.includes('.test.') || 
+    p.includes('.spec.') || 
+    p.includes('__tests__/') || 
+    p.includes('__mocks__/') ||
+    // Generic test folders
+    p.includes('/test/') ||
+    p.includes('/tests/') ||
+    p.includes('/testing/') ||
+    // Python test patterns
+    p.endsWith('_test.py') ||
+    p.includes('/test_') ||
+    // Go test patterns
+    p.endsWith('_test.go') ||
+    // Java test patterns
+    p.includes('/src/test/') ||
+    // Rust test patterns (inline tests are different, but test files)
+    p.includes('/tests/') ||
+    // C# test patterns
+    p.includes('.tests/') ||
+    p.includes('tests.cs')
+  );
+}
+
+/**
+ * Check if a file path is likely a utility/helper file
+ * These might still have entry points but should be lower priority
+ */
+export function isUtilityFile(filePath: string): boolean {
+  const p = filePath.toLowerCase().replace(/\\/g, '/');
+  
+  return (
+    p.includes('/utils/') ||
+    p.includes('/util/') ||
+    p.includes('/helpers/') ||
+    p.includes('/helper/') ||
+    p.includes('/common/') ||
+    p.includes('/shared/') ||
+    p.includes('/lib/') ||
+    p.endsWith('/utils.ts') ||
+    p.endsWith('/utils.js') ||
+    p.endsWith('/helpers.ts') ||
+    p.endsWith('/helpers.js') ||
+    p.endsWith('_utils.py') ||
+    p.endsWith('_helpers.py')
+  );
+}
diff --git a/gitnexus-web/src/core/ingestion/framework-detection.ts b/gitnexus-web/src/core/ingestion/framework-detection.ts
new file mode 100644
index 000000000..d3c75ab87
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/framework-detection.ts
@@ -0,0 +1,243 @@
+/**
+ * Framework Detection
+ * 
+ * Detects frameworks from file path patterns and provides entry point multipliers.
+ * This enables framework-aware entry point scoring.
+ * 
+ * DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
+ * (no bonus, no penalty) - same behavior as before this feature.
+ */
+
+// ============================================================================
+// TYPES
+// ============================================================================
+
+export interface FrameworkHint {
+  framework: string;
+  entryPointMultiplier: number;
+  reason: string;
+}
+
+// ============================================================================
+// PATH-BASED FRAMEWORK DETECTION
+// ============================================================================
+
+/**
+ * Detect framework from file path patterns
+ * 
+ * This provides entry point multipliers based on well-known framework conventions.
+ * Returns null if no framework pattern is detected (falls back to 1.0 multiplier).
+ */
+export function detectFrameworkFromPath(filePath: string): FrameworkHint | null {
+  // Normalize path separators and ensure leading slash for consistent matching
+  let p = filePath.toLowerCase().replace(/\\/g, '/');
+  if (!p.startsWith('/')) {
+    p = '/' + p;  // Add leading slash so patterns like '/app/' match 'app/...'
+  }
+  
+  // ========== JAVASCRIPT / TYPESCRIPT FRAMEWORKS ==========
+  
+  // Next.js - Pages Router (high confidence)
+  if (p.includes('/pages/') && !p.includes('/_') && !p.includes('/api/')) {
+    if (p.endsWith('.tsx') || p.endsWith('.ts') || p.endsWith('.jsx') || p.endsWith('.js')) {
+      return { framework: 'nextjs-pages', entryPointMultiplier: 3.0, reason: 'nextjs-page' };
+    }
+  }
+  
+  // Next.js - App Router (page.tsx files)
+  if (p.includes('/app/') && (
+    p.endsWith('page.tsx') || p.endsWith('page.ts') || 
+    p.endsWith('page.jsx') || p.endsWith('page.js')
+  )) {
+    return { framework: 'nextjs-app', entryPointMultiplier: 3.0, reason: 'nextjs-app-page' };
+  }
+  
+  // Next.js - API Routes
+  if (p.includes('/pages/api/') || (p.includes('/app/') && p.includes('/api/') && p.endsWith('route.ts'))) {
+    return { framework: 'nextjs-api', entryPointMultiplier: 3.0, reason: 'nextjs-api-route' };
+  }
+  
+  // Next.js - Layout files (moderate - they're entry-ish but not the main entry)
+  if (p.includes('/app/') && (p.endsWith('layout.tsx') || p.endsWith('layout.ts'))) {
+    return { framework: 'nextjs-app', entryPointMultiplier: 2.0, reason: 'nextjs-layout' };
+  }
+  
+  // Express / Node.js routes
+  if (p.includes('/routes/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
+    return { framework: 'express', entryPointMultiplier: 2.5, reason: 'routes-folder' };
+  }
+  
+  // Generic controllers (MVC pattern)
+  if (p.includes('/controllers/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
+    return { framework: 'mvc', entryPointMultiplier: 2.5, reason: 'controllers-folder' };
+  }
+  
+  // Generic handlers
+  if (p.includes('/handlers/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
+    return { framework: 'handlers', entryPointMultiplier: 2.5, reason: 'handlers-folder' };
+  }
+  
+  // React components (lower priority - not all are entry points)
+  if ((p.includes('/components/') || p.includes('/views/')) && 
+      (p.endsWith('.tsx') || p.endsWith('.jsx'))) {
+    // Only boost if PascalCase filename (likely a component, not util)
+    const fileName = p.split('/').pop() || '';
+    if (/^[A-Z]/.test(fileName)) {
+      return { framework: 'react', entryPointMultiplier: 1.5, reason: 'react-component' };
+    }
+  }
+  
+  // ========== PYTHON FRAMEWORKS ==========
+  
+  // Django views (high confidence)
+  if (p.endsWith('views.py')) {
+    return { framework: 'django', entryPointMultiplier: 3.0, reason: 'django-views' };
+  }
+  
+  // Django URL configs
+  if (p.endsWith('urls.py')) {
+    return { framework: 'django', entryPointMultiplier: 2.0, reason: 'django-urls' };
+  }
+  
+  // FastAPI / Flask routers
+  if ((p.includes('/routers/') || p.includes('/endpoints/') || p.includes('/routes/')) && 
+      p.endsWith('.py')) {
+    return { framework: 'fastapi', entryPointMultiplier: 2.5, reason: 'api-routers' };
+  }
+  
+  // Python API folder
+  if (p.includes('/api/') && p.endsWith('.py') && !p.endsWith('__init__.py')) {
+    return { framework: 'python-api', entryPointMultiplier: 2.0, reason: 'api-folder' };
+  }
+  
+  // ========== JAVA FRAMEWORKS ==========
+  
+  // Spring Boot controllers
+  if ((p.includes('/controller/') || p.includes('/controllers/')) && p.endsWith('.java')) {
+    return { framework: 'spring', entryPointMultiplier: 3.0, reason: 'spring-controller' };
+  }
+  
+  // Spring Boot - files ending in Controller.java
+  if (p.endsWith('controller.java')) {
+    return { framework: 'spring', entryPointMultiplier: 3.0, reason: 'spring-controller-file' };
+  }
+  
+  // Java service layer (often entry points for business logic)
+  if ((p.includes('/service/') || p.includes('/services/')) && p.endsWith('.java')) {
+    return { framework: 'java-service', entryPointMultiplier: 1.8, reason: 'java-service' };
+  }
+  
+  // ========== C# / .NET FRAMEWORKS ==========
+  
+  // ASP.NET Controllers
+  if (p.includes('/controllers/') && p.endsWith('.cs')) {
+    return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-controller' };
+  }
+  
+  // ASP.NET - files ending in Controller.cs
+  if (p.endsWith('controller.cs')) {
+    return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-controller-file' };
+  }
+  
+  // Blazor pages
+  if (p.includes('/pages/') && p.endsWith('.razor')) {
+    return { framework: 'blazor', entryPointMultiplier: 2.5, reason: 'blazor-page' };
+  }
+  
+  // ========== GO FRAMEWORKS ==========
+  
+  // Go handlers
+  if ((p.includes('/handlers/') || p.includes('/handler/')) && p.endsWith('.go')) {
+    return { framework: 'go-http', entryPointMultiplier: 2.5, reason: 'go-handlers' };
+  }
+  
+  // Go routes
+  if (p.includes('/routes/') && p.endsWith('.go')) {
+    return { framework: 'go-http', entryPointMultiplier: 2.5, reason: 'go-routes' };
+  }
+  
+  // Go controllers
+  if (p.includes('/controllers/') && p.endsWith('.go')) {
+    return { framework: 'go-mvc', entryPointMultiplier: 2.5, reason: 'go-controller' };
+  }
+  
+  // Go main.go files (THE entry point)
+  if (p.endsWith('/main.go') || p.endsWith('/cmd/') && p.endsWith('.go')) {
+    return { framework: 'go', entryPointMultiplier: 3.0, reason: 'go-main' };
+  }
+  
+  // ========== RUST FRAMEWORKS ==========
+  
+  // Rust handlers/routes
+  if ((p.includes('/handlers/') || p.includes('/routes/')) && p.endsWith('.rs')) {
+    return { framework: 'rust-web', entryPointMultiplier: 2.5, reason: 'rust-handlers' };
+  }
+  
+  // Rust main.rs (THE entry point)
+  if (p.endsWith('/main.rs')) {
+    return { framework: 'rust', entryPointMultiplier: 3.0, reason: 'rust-main' };
+  }
+  
+  // Rust bin folder (executables)
+  if (p.includes('/bin/') && p.endsWith('.rs')) {
+    return { framework: 'rust', entryPointMultiplier: 2.5, reason: 'rust-bin' };
+  }
+  
+  // ========== C / C++ ==========
+  
+  // C/C++ main files
+  if (p.endsWith('/main.c') || p.endsWith('/main.cpp') || p.endsWith('/main.cc')) {
+    return { framework: 'c-cpp', entryPointMultiplier: 3.0, reason: 'c-main' };
+  }
+  
+  // C/C++ src folder entry points (if named specifically)
+  if ((p.includes('/src/') && (p.endsWith('/app.c') || p.endsWith('/app.cpp')))) {
+    return { framework: 'c-cpp', entryPointMultiplier: 2.5, reason: 'c-app' };
+  }
+  
+  // ========== GENERIC PATTERNS ==========
+  
+  // Any language: index files in API folders
+  if (p.includes('/api/') && (
+    p.endsWith('/index.ts') || p.endsWith('/index.js') || 
+    p.endsWith('/__init__.py')
+  )) {
+    return { framework: 'api', entryPointMultiplier: 1.8, reason: 'api-index' };
+  }
+  
+  // No framework detected - return null for graceful fallback (1.0 multiplier)
+  return null;
+}
+
+// ============================================================================
+// FUTURE: AST-BASED PATTERNS (for Phase 3)
+// ============================================================================
+
+/**
+ * Patterns that indicate entry points within code (for future AST-based detection)
+ * These would require parsing decorators/annotations in the code itself.
+ */
+export const FRAMEWORK_AST_PATTERNS = {
+  // JavaScript/TypeScript decorators
+  'nestjs': ['@Controller', '@Get', '@Post', '@Put', '@Delete', '@Patch'],
+  'express': ['app.get', 'app.post', 'app.put', 'app.delete', 'router.get', 'router.post'],
+  
+  // Python decorators
+  'fastapi': ['@app.get', '@app.post', '@app.put', '@app.delete', '@router.get'],
+  'flask': ['@app.route', '@blueprint.route'],
+  
+  // Java annotations
+  'spring': ['@RestController', '@Controller', '@GetMapping', '@PostMapping', '@RequestMapping'],
+  'jaxrs': ['@Path', '@GET', '@POST', '@PUT', '@DELETE'],
+  
+  // C# attributes
+  'aspnet': ['[ApiController]', '[HttpGet]', '[HttpPost]', '[Route]'],
+  
+  // Go patterns (function signatures)
+  'go-http': ['http.Handler', 'http.HandlerFunc', 'ServeHTTP'],
+  
+  // Rust macros
+  'actix': ['#[get', '#[post', '#[put', '#[delete'],
+  'axum': ['Router::new'],
+  'rocket': ['#[get', '#[post'],
+};
diff --git a/gitnexus-web/src/core/ingestion/heritage-processor.ts b/gitnexus-web/src/core/ingestion/heritage-processor.ts
new file mode 100644
index 000000000..378a3bdd1
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/heritage-processor.ts
@@ -0,0 +1,154 @@
+/**
+ * Heritage Processor
+ * 
+ * Extracts class inheritance relationships:
+ * - EXTENDS: Class extends another Class (TS, JS, Python)
+ * - IMPLEMENTS: Class implements an Interface (TS only)
+ */
+
+import { KnowledgeGraph } from '../graph/types';
+import { ASTCache } from './ast-cache';
+import { SymbolTable } from './symbol-table';
+import { loadParser, loadLanguage } from '../tree-sitter/parser-loader';
+import { LANGUAGE_QUERIES } from './tree-sitter-queries';
+import { generateId } from '../../lib/utils';
+import { getLanguageFromFilename } from './utils';
+
+export const processHeritage = async (
+  graph: KnowledgeGraph,
+  files: { path: string; content: string }[],
+  astCache: ASTCache,
+  symbolTable: SymbolTable,
+  onProgress?: (current: number, total: number) => void
+) => {
+  const parser = await loadParser();
+
+  for (let i = 0; i < files.length; i++) {
+    const file = files[i];
+    onProgress?.(i + 1, files.length);
+
+    // 1. Check language support
+    const language = getLanguageFromFilename(file.path);
+    if (!language) continue;
+
+    const queryStr = LANGUAGE_QUERIES[language];
+    if (!queryStr) continue;
+
+    // 2. Load the language
+    await loadLanguage(language, file.path);
+
+    // 3. Get AST
+    let tree = astCache.get(file.path);
+    let wasReparsed = false;
+
+    if (!tree) {
+      tree = parser.parse(file.content);
+      wasReparsed = true;
+    }
+
+    let query;
+    let matches;
+    try {
+      query = parser.getLanguage().query(queryStr);
+      matches = query.matches(tree.rootNode);
+    } catch (queryError) {
+      console.warn(`Heritage query error for ${file.path}:`, queryError);
+      if (wasReparsed) tree.delete();
+      continue;
+    }
+
+    // 4. Process heritage matches
+    matches.forEach(match => {
+      const captureMap: Record = {};
+      match.captures.forEach(c => {
+        captureMap[c.name] = c.node;
+      });
+
+      // EXTENDS: Class extends another Class
+      if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
+        const className = captureMap['heritage.class'].text;
+        const parentClassName = captureMap['heritage.extends'].text;
+
+        // Resolve both class IDs
+        const childId = symbolTable.lookupExact(file.path, className) ||
+                        symbolTable.lookupFuzzy(className)[0]?.nodeId ||
+                        generateId('Class', `${file.path}:${className}`);
+        
+        const parentId = symbolTable.lookupFuzzy(parentClassName)[0]?.nodeId ||
+                         generateId('Class', `${parentClassName}`);
+
+        if (childId && parentId && childId !== parentId) {
+          const relId = generateId('EXTENDS', `${childId}->${parentId}`);
+          
+          graph.addRelationship({
+            id: relId,
+            sourceId: childId,
+            targetId: parentId,
+            type: 'EXTENDS',
+            confidence: 1.0,
+            reason: '',
+          });
+        }
+      }
+
+      // IMPLEMENTS: Class implements Interface (TypeScript only)
+      if (captureMap['heritage.class'] && captureMap['heritage.implements']) {
+        const className = captureMap['heritage.class'].text;
+        const interfaceName = captureMap['heritage.implements'].text;
+
+        // Resolve class and interface IDs
+        const classId = symbolTable.lookupExact(file.path, className) ||
+                        symbolTable.lookupFuzzy(className)[0]?.nodeId ||
+                        generateId('Class', `${file.path}:${className}`);
+        
+        const interfaceId = symbolTable.lookupFuzzy(interfaceName)[0]?.nodeId ||
+                            generateId('Interface', `${interfaceName}`);
+
+        if (classId && interfaceId) {
+          const relId = generateId('IMPLEMENTS', `${classId}->${interfaceId}`);
+          
+          graph.addRelationship({
+            id: relId,
+            sourceId: classId,
+            targetId: interfaceId,
+            type: 'IMPLEMENTS',
+            confidence: 1.0,
+            reason: '',
+          });
+        }
+      }
+
+      // IMPLEMENTS (Rust): impl Trait for Struct
+      if (captureMap['heritage.trait'] && captureMap['heritage.class']) {
+        const structName = captureMap['heritage.class'].text;
+        const traitName = captureMap['heritage.trait'].text;
+
+        // Resolve struct and trait IDs
+        const structId = symbolTable.lookupExact(file.path, structName) ||
+                         symbolTable.lookupFuzzy(structName)[0]?.nodeId ||
+                         generateId('Struct', `${file.path}:${structName}`);
+        
+        const traitId = symbolTable.lookupFuzzy(traitName)[0]?.nodeId ||
+                        generateId('Trait', `${traitName}`);
+
+        if (structId && traitId) {
+          const relId = generateId('IMPLEMENTS', `${structId}->${traitId}`);
+          
+          graph.addRelationship({
+            id: relId,
+            sourceId: structId,
+            targetId: traitId,
+            type: 'IMPLEMENTS',
+            confidence: 1.0,
+            reason: 'trait-impl',
+          });
+        }
+      }
+    });
+
+    // Cleanup
+    if (wasReparsed) {
+      tree.delete();
+    }
+  }
+};
diff --git a/gitnexus-web/src/core/ingestion/import-processor.ts b/gitnexus-web/src/core/ingestion/import-processor.ts
new file mode 100644
index 000000000..c0cb6bd68
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/import-processor.ts
@@ -0,0 +1,236 @@
+import { KnowledgeGraph } from '../graph/types';
+import { ASTCache } from './ast-cache';
+import { loadParser, loadLanguage } from '../tree-sitter/parser-loader';
+import { LANGUAGE_QUERIES } from './tree-sitter-queries';
+import { generateId } from '../../lib/utils';
+import { getLanguageFromFilename } from './utils';
+
+// Type: Map>
+// Stores all files that a given file imports from
+export type ImportMap = Map>;
+
+export const createImportMap = (): ImportMap => new Map();
+
+// Helper: Resolve import paths (relative and absolute/package-style)
+const resolveImportPath = (
+  currentFile: string, 
+  importPath: string, 
+  allFiles: Set,
+  allFileList: string[],
+  resolveCache: Map
+): string | null => {
+  const cacheKey = `${currentFile}::${importPath}`;
+  if (resolveCache.has(cacheKey)) return resolveCache.get(cacheKey) ?? null;
+
+  // 1. Resolve '..' and '.' for relative imports
+  const currentDir = currentFile.split('/').slice(0, -1);
+  const parts = importPath.split('/');
+  
+  for (const part of parts) {
+    if (part === '.') continue;
+    if (part === '..') {
+      currentDir.pop();
+    } else {
+      currentDir.push(part);
+    }
+  }
+  
+  const basePath = currentDir.join('/');
+
+  // 2. Try extensions for all supported languages
+  const extensions = [
+    '', 
+    // TypeScript/JavaScript
+    '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js',
+    // Python
+    '.py', '/__init__.py',
+    // Java
+    '.java',
+    // C/C++
+    '.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hxx', '.hh',
+    // C#
+    '.cs',
+    // Go
+    '.go',
+    // Rust
+    '.rs', '/mod.rs'
+  ];
+  
+  if (importPath.startsWith('.')) {
+    for (const ext of extensions) {
+      const candidate = basePath + ext;
+      if (allFiles.has(candidate)) {
+        resolveCache.set(cacheKey, candidate);
+        return candidate;
+      }
+    }
+    resolveCache.set(cacheKey, null);
+    return null;
+  }
+
+  // 3. Handle absolute/package imports (Java, Go, Python, etc.)
+  if (importPath.endsWith('.*')) {
+    resolveCache.set(cacheKey, null);
+    return null;
+  }
+
+  const pathLike = importPath.includes('/')
+    ? importPath
+    : importPath.replace(/\./g, '/');
+  const pathParts = pathLike.split('/').filter(Boolean);
+
+  // Normalize all file paths to forward slashes for matching
+  const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
+
+  for (let i = 0; i < pathParts.length; i++) {
+    const suffix = pathParts.slice(i).join('/');
+    for (const ext of extensions) {
+      const suffixWithExt = suffix + ext;
+      // Require path separator before match to avoid false positives like "View.java" matching "RootView.java"
+      const suffixPattern = '/' + suffixWithExt;
+      const matchIdx = normalizedFileList.findIndex(filePath => 
+        filePath.endsWith(suffixPattern) || filePath.toLowerCase().endsWith(suffixPattern.toLowerCase())
+      );
+      if (matchIdx !== -1) {
+        const match = allFileList[matchIdx];
+        resolveCache.set(cacheKey, match);
+        return match;
+      }
+    }
+  }
+
+  // Unresolved imports (external packages, SDK imports) are expected - don't log
+  resolveCache.set(cacheKey, null);
+  return null;
+};
+
+export const processImports = async (
+  graph: KnowledgeGraph,
+  files: { path: string; content: string }[],
+  astCache: ASTCache,
+  importMap: ImportMap,
+  onProgress?: (current: number, total: number) => void
+) => {
+  // Create a Set of all file paths for fast lookup during resolution
+  const allFilePaths = new Set(files.map(f => f.path));
+  const parser = await loadParser();
+  const resolveCache = new Map();
+  const allFileList = files.map(f => f.path);
+  
+  // Track import statistics
+  let totalImportsFound = 0;
+  let totalImportsResolved = 0;
+
+  for (let i = 0; i < files.length; i++) {
+    const file = files[i];
+    onProgress?.(i + 1, files.length);
+
+    // 1. Check language support first
+    const language = getLanguageFromFilename(file.path);
+    if (!language) continue;
+    
+    const queryStr = LANGUAGE_QUERIES[language];
+    if (!queryStr) continue;
+
+    // 2. ALWAYS load the language before querying (parser is stateful)
+    await loadLanguage(language, file.path);
+
+    // 3. Get AST (Try Cache First)
+    let tree = astCache.get(file.path);
+    let wasReparsed = false;
+    
+    if (!tree) {
+      // Cache Miss: Re-parse (slower, but necessary if evicted)
+      tree = parser.parse(file.content);
+      wasReparsed = true;
+    }
+
+    let query;
+    let matches;
+    try {
+      query = parser.getLanguage().query(queryStr);
+      matches = query.matches(tree.rootNode);
+      
+      // Removed verbose Java import logging
+    } catch (queryError: any) {
+      // Detailed debug logging for query failures
+      console.group(`🔴 Query Error: ${file.path}`);
+      console.log('Language:', language);
+      console.log('Query (first 200 chars):', queryStr.substring(0, 200) + '...');
+      console.log('Error:', queryError?.message || queryError);
+      console.log('File content (first 300 chars):', file.content.substring(0, 300));
+      console.log('AST root type:', tree.rootNode?.type);
+      console.log('AST has errors:', tree.rootNode?.hasError);
+      console.groupEnd();
+      
+      if (wasReparsed) tree.delete();
+      continue;
+    }
+
+    matches.forEach(match => {
+      const captureMap: Record = {};
+      match.captures.forEach(c => captureMap[c.name] = c.node);
+
+      if (captureMap['import']) {
+        const sourceNode = captureMap['import.source'];
+        if (!sourceNode) {
+          if (import.meta.env.DEV) {
+            console.log(`⚠️ Import captured but no source node in ${file.path}`);
+          }
+          return;
+        }
+
+        // Clean path (remove quotes)
+        const rawImportPath = sourceNode.text.replace(/['"]/g, '');
+        totalImportsFound++;
+        
+        // Removed verbose per-import logging
+        
+        // Resolve to actual file in the system
+        const resolvedPath = resolveImportPath(
+          file.path,
+          rawImportPath,
+          allFilePaths,
+          allFileList,
+          resolveCache
+        );
+
+        if (resolvedPath) {
+          // A. Update Graph (File -> IMPORTS -> File)
+          const sourceId = generateId('File', file.path);
+          const targetId = generateId('File', resolvedPath);
+          const relId = generateId('IMPORTS', `${file.path}->${resolvedPath}`);
+
+          totalImportsResolved++;
+
+          graph.addRelationship({
+            id: relId,
+            sourceId,
+            targetId,
+            type: 'IMPORTS',
+            confidence: 1.0,
+            reason: '',
+          });
+
+          // B. Update Import Map (For Pass 4)
+          // Store all resolved import paths for this file
+          if (!importMap.has(file.path)) {
+            importMap.set(file.path, new Set());
+          }
+          importMap.get(file.path)!.add(resolvedPath);
+        }
+      }
+    });
+
+    // If re-parsed just for this, delete the tree to save memory
+    if (wasReparsed) {
+      tree.delete();
+    }
+  }
+  
+  if (import.meta.env.DEV) {
+    console.log(`📊 Import processing complete: ${totalImportsResolved}/${totalImportsFound} imports resolved to graph edges`);
+  }
+};
+
+
diff --git a/gitnexus-web/src/core/ingestion/parsing-processor.ts b/gitnexus-web/src/core/ingestion/parsing-processor.ts
new file mode 100644
index 000000000..807bcf581
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/parsing-processor.ts
@@ -0,0 +1,256 @@
+import { KnowledgeGraph, GraphNode, GraphRelationship } from '../graph/types';
+import { loadParser, loadLanguage } from '../tree-sitter/parser-loader';
+import { LANGUAGE_QUERIES } from './tree-sitter-queries';
+import { generateId } from '../../lib/utils';
+import { SymbolTable } from './symbol-table';
+import { ASTCache } from './ast-cache';
+import { getLanguageFromFilename } from './utils';
+
+export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
+
+// ============================================================================
+// EXPORT DETECTION - Language-specific visibility detection
+// ============================================================================
+
+/**
+ * Check if a symbol (function, class, etc.) is exported/public
+ * Handles all 9 supported languages with explicit logic
+ * 
+ * @param node - The AST node for the symbol name
+ * @param name - The symbol name
+ * @param language - The programming language
+ * @returns true if the symbol is exported/public
+ */
+const isNodeExported = (node: any, name: string, language: string): boolean => {
+  let current = node;
+  
+  switch (language) {
+    // JavaScript/TypeScript: Check for export keyword in ancestors
+    case 'javascript':
+    case 'typescript':
+      while (current) {
+        const type = current.type;
+        if (type === 'export_statement' || 
+            type === 'export_specifier' ||
+            type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
+          return true;
+        }
+        // Also check if text starts with 'export '
+        if (current.text?.startsWith('export ')) {
+          return true;
+        }
+        current = current.parent;
+      }
+      return false;
+    
+    // Python: Public if no leading underscore (convention)
+    case 'python':
+      return !name.startsWith('_');
+    
+    // Java: Check for 'public' modifier
+    // In tree-sitter Java, modifiers are siblings of the name node, not parents
+    case 'java':
+      while (current) {
+        // Check if this node or any sibling is a 'modifiers' node containing 'public'
+        if (current.parent) {
+          const parent = current.parent;
+          // Check all children of the parent for modifiers
+          for (let i = 0; i < parent.childCount; i++) {
+            const child = parent.child(i);
+            if (child?.type === 'modifiers' && child.text?.includes('public')) {
+              return true;
+            }
+          }
+          // Also check if the parent's text starts with 'public' (fallback)
+          if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
+            if (parent.text?.trimStart().startsWith('public')) {
+              return true;
+            }
+          }
+        }
+        current = current.parent;
+      }
+      return false;
+    
+    // C#: Check for 'public' modifier in ancestors
+    case 'csharp':
+      while (current) {
+        if (current.type === 'modifier' || current.type === 'modifiers') {
+          if (current.text?.includes('public')) return true;
+        }
+        current = current.parent;
+      }
+      return false;
+    
+    // Go: Uppercase first letter = exported
+    case 'go':
+      if (name.length === 0) return false;
+      const first = name[0];
+      // Must be uppercase letter (not a number or symbol)
+      return first === first.toUpperCase() && first !== first.toLowerCase();
+    
+    // Rust: Check for 'pub' visibility modifier
+    case 'rust':
+      while (current) {
+        if (current.type === 'visibility_modifier') {
+          if (current.text?.includes('pub')) return true;
+        }
+        current = current.parent;
+      }
+      return false;
+    
+    // C/C++: No native export concept at language level
+    // Entry points will be detected via name patterns (main, etc.)
+    case 'c':
+    case 'cpp':
+      return false;
+    
+    default:
+      return false;
+  }
+};
+
+export const processParsing = async (
+  graph: KnowledgeGraph, 
+  files: { path: string; content: string }[],
+  symbolTable: SymbolTable,
+  astCache: ASTCache,
+  onFileProgress?: FileProgressCallback
+) => {
+ 
+  const parser = await loadParser();
+  const total = files.length;
+
+  for (let i = 0; i < files.length; i++) {
+    const file = files[i];
+    
+    // Report progress for each file
+    onFileProgress?.(i + 1, total, file.path);
+    
+    const language = getLanguageFromFilename(file.path);
+
+    if (!language) continue;
+
+    await loadLanguage(language, file.path);
+    
+    // 3. Parse the text content into an AST
+    const tree = parser.parse(file.content);
+    
+    // Store in cache immediately (this might evict an old one)
+    astCache.set(file.path, tree);
+    
+    // 4. Get the specific query string for this language
+    const queryString = LANGUAGE_QUERIES[language];
+    if (!queryString) {
+      continue;
+    }
+
+    // 5. Run the query against the AST root node
+    // This looks for patterns like (function_declaration)
+    let query;
+    let matches;
+    try {
+      query = parser.getLanguage().query(queryString);
+      matches = query.matches(tree.rootNode);
+    } catch (queryError) {
+      console.warn(`Query error for ${file.path}:`, queryError);
+      continue;
+    }
+
+    // 6. Process every match found
+    matches.forEach(match => {
+      const captureMap: Record = {};
+      
+      match.captures.forEach(c => {
+        captureMap[c.name] = c.node;
+      });
+
+      // Skip imports here - they are handled by import-processor.ts
+      // which creates proper File -> IMPORTS -> File relationships
+      if (captureMap['import']) {
+        return;
+      }
+
+      // Skip call expressions - they are handled by call-processor.ts
+      if (captureMap['call']) {
+        return;
+      }
+
+      const nameNode = captureMap['name'];
+      if (!nameNode) return;
+
+      const nodeName = nameNode.text;
+      
+      let nodeLabel = 'CodeElement';
+      
+      // Core types
+      if (captureMap['definition.function']) nodeLabel = 'Function';
+      else if (captureMap['definition.class']) nodeLabel = 'Class';
+      else if (captureMap['definition.interface']) nodeLabel = 'Interface';
+      else if (captureMap['definition.method']) nodeLabel = 'Method';
+      // Struct types (C, C++, Go, Rust, C#)
+      else if (captureMap['definition.struct']) nodeLabel = 'Struct';
+      // Enum types
+      else if (captureMap['definition.enum']) nodeLabel = 'Enum';
+      // Namespace/Module (C++, C#, Rust)
+      else if (captureMap['definition.namespace']) nodeLabel = 'Namespace';
+      else if (captureMap['definition.module']) nodeLabel = 'Module';
+      // Rust-specific
+      else if (captureMap['definition.trait']) nodeLabel = 'Trait';
+      else if (captureMap['definition.impl']) nodeLabel = 'Impl';
+      else if (captureMap['definition.type']) nodeLabel = 'TypeAlias';
+      else if (captureMap['definition.const']) nodeLabel = 'Const';
+      else if (captureMap['definition.static']) nodeLabel = 'Static';
+      // C-specific
+      else if (captureMap['definition.typedef']) nodeLabel = 'Typedef';
+      else if (captureMap['definition.macro']) nodeLabel = 'Macro';
+      else if (captureMap['definition.union']) nodeLabel = 'Union';
+      // C#-specific
+      else if (captureMap['definition.property']) nodeLabel = 'Property';
+      else if (captureMap['definition.record']) nodeLabel = 'Record';
+      else if (captureMap['definition.delegate']) nodeLabel = 'Delegate';
+      // Java-specific
+      else if (captureMap['definition.annotation']) nodeLabel = 'Annotation';
+      else if (captureMap['definition.constructor']) nodeLabel = 'Constructor';
+      // C++ template
+      else if (captureMap['definition.template']) nodeLabel = 'Template';
+
+      const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
+      
+      const node: GraphNode = {
+        id: nodeId,
+        label: nodeLabel as any,
+        properties: {
+          name: nodeName,
+          filePath: file.path,
+          startLine: nameNode.startPosition.row,
+          endLine: nameNode.endPosition.row,
+          language: language,
+          isExported: isNodeExported(nameNode, nodeName, language),
+        }
+      };
+
+      graph.addNode(node);
+
+      // Register in Symbol Table (only definitions, not imports)
+      symbolTable.add(file.path, nodeName, nodeId, nodeLabel);
+
+      const fileId = generateId('File', file.path);
+      
+      const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
+      
+      const relationship: GraphRelationship = {
+        id: relId,
+        sourceId: fileId,
+        targetId: nodeId,
+        type: 'DEFINES',
+        confidence: 1.0,
+        reason: '',
+      };
+
+      graph.addRelationship(relationship);
+    });
+    
+    // Don't delete tree here - LRU cache handles cleanup when evicted
+  }
+};
diff --git a/gitnexus-web/src/core/ingestion/pipeline.ts b/gitnexus-web/src/core/ingestion/pipeline.ts
new file mode 100644
index 000000000..8c276b312
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/pipeline.ts
@@ -0,0 +1,304 @@
+import { createKnowledgeGraph } from '../graph/graph';
+import { extractZip, FileEntry } from '../../services/zip';
+import { processStructure } from './structure-processor';
+import { processParsing } from './parsing-processor';
+import { processImports, createImportMap } from './import-processor';
+import { processCalls } from './call-processor';
+import { processHeritage } from './heritage-processor';
+import { processCommunities, CommunityDetectionResult } from './community-processor';
+import { processProcesses, ProcessDetectionResult } from './process-processor';
+import { createSymbolTable } from './symbol-table';
+import { createASTCache } from './ast-cache';
+import { PipelineProgress, PipelineResult } from '../../types/pipeline';
+
+/**
+ * Run the ingestion pipeline from a ZIP file
+ */
+export const runIngestionPipeline = async ( file: File, onProgress: (progress: PipelineProgress) => void): Promise => {
+  // Phase 1: Extracting (0-15%)
+  onProgress({
+    phase: 'extracting',
+    percent: 0,
+    message: 'Extracting ZIP file...',
+  });
+  
+  // Fake progress for extraction (JSZip doesn't expose progress)
+  const fakeExtractionProgress = setInterval(() => {
+    onProgress({
+      phase: 'extracting',
+      percent: Math.min(14, Math.random() * 10 + 5),
+      message: 'Extracting ZIP file...',
+    });
+  }, 200);
+  
+  const files = await extractZip(file);
+  clearInterval(fakeExtractionProgress);
+  
+  // Continue with common pipeline
+  return runPipelineFromFiles(files, onProgress);
+};
+
+/**
+ * Run the ingestion pipeline from pre-extracted files (e.g., from git clone)
+ */
+export const runPipelineFromFiles = async (
+  files: FileEntry[],
+  onProgress: (progress: PipelineProgress) => void
+): Promise => {
+  const graph = createKnowledgeGraph();
+  const fileContents = new Map();
+  const symbolTable = createSymbolTable();
+  const astCache = createASTCache(50); // Keep last 50 files hot
+  const importMap = createImportMap();
+
+  // Cleanup function for error handling
+  const cleanup = () => {
+    astCache.clear();
+    symbolTable.clear();
+  };
+  
+  try {
+  // Store file contents for code panel
+  files.forEach(f => fileContents.set(f.path, f.content));
+  
+  onProgress({
+    phase: 'extracting',
+    percent: 15,
+    message: 'ZIP extracted successfully',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: 0 },
+  });
+  
+  // Phase 2: Structure (15-30%)
+  onProgress({
+    phase: 'structure',
+    percent: 15,
+    message: 'Analyzing project structure...',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: 0 },
+  });
+  
+  const filePaths = files.map(f => f.path);
+  processStructure(graph, filePaths);
+  
+  onProgress({
+    phase: 'structure',
+    percent: 30,
+    message: 'Project structure analyzed',
+    stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+  
+  // Phase 3: Parsing (30-70%)
+  onProgress({
+    phase: 'parsing',
+    percent: 30,
+    message: 'Parsing code definitions...',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+  
+  await processParsing(graph, files, symbolTable, astCache, (current, total, filePath) => {
+    const parsingProgress = 30 + ((current / total) * 40);
+    onProgress({
+      phase: 'parsing',
+      percent: Math.round(parsingProgress),
+      message: 'Parsing code definitions...',
+      detail: filePath,
+      stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
+    });
+  });
+
+
+  // Phase 4: Imports (70-82%)
+  onProgress({
+    phase: 'imports',
+    percent: 70,
+    message: 'Resolving imports...',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+
+  await processImports(graph, files, astCache, importMap, (current, total) => {
+    const importProgress = 70 + ((current / total) * 12);
+    onProgress({
+      phase: 'imports',
+      percent: Math.round(importProgress),
+      message: 'Resolving imports...',
+      stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
+    });
+  });
+  
+  // Debug: Count IMPORTS relationships
+  if (import.meta.env.DEV) {
+    const importsCount = graph.relationships.filter(r => r.type === 'IMPORTS').length;
+    console.log(`📊 Pipeline: After import phase, graph has ${importsCount} IMPORTS relationships (total: ${graph.relationshipCount})`);
+    if (importsCount > 0) {
+      const sample = graph.relationships.filter(r => r.type === 'IMPORTS').slice(0, 3);
+      sample.forEach(r => console.log(`   Sample IMPORTS: ${r.sourceId} → ${r.targetId}`));
+    }
+  }
+
+
+  // Phase 5: Calls (82-98%)
+  onProgress({
+    phase: 'calls',
+    percent: 82,
+    message: 'Tracing function calls...',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+
+  await processCalls(graph, files, astCache, symbolTable, importMap, (current, total) => {
+    const callProgress = 82 + ((current / total) * 10);
+    onProgress({
+      phase: 'calls',
+      percent: Math.round(callProgress),
+      message: 'Tracing function calls...',
+      stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
+    });
+  });
+
+  // Phase 6: Heritage - Class inheritance (92-98%)
+  onProgress({
+    phase: 'heritage',
+    percent: 92,
+    message: 'Extracting class inheritance...',
+    stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+
+  await processHeritage(graph, files, astCache, symbolTable, (current, total) => {
+    const heritageProgress = 88 + ((current / total) * 4);
+    onProgress({
+      phase: 'heritage',
+      percent: Math.round(heritageProgress),
+      message: 'Extracting class inheritance...',
+      stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
+    });
+  });
+
+  // Phase 7: Community Detection (92-98%)
+  onProgress({
+    phase: 'communities',
+    percent: 92,
+    message: 'Detecting code communities...',
+    stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+
+  const communityResult = await processCommunities(graph, (message, progress) => {
+    const communityProgress = 92 + (progress * 0.06);
+    onProgress({
+      phase: 'communities',
+      percent: Math.round(communityProgress),
+      message,
+      stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
+    });
+  });
+
+  // Log community detection results
+  if (import.meta.env.DEV) {
+    console.log(`🏘️ Community detection: ${communityResult.stats.totalCommunities} communities found (modularity: ${communityResult.stats.modularity.toFixed(3)})`);
+  }
+
+  // Add community nodes to the graph
+  communityResult.communities.forEach(comm => {
+    graph.addNode({
+      id: comm.id,
+      label: 'Community' as const,
+      properties: {
+        name: comm.label,
+        filePath: '',
+        heuristicLabel: comm.heuristicLabel,
+        cohesion: comm.cohesion,
+        symbolCount: comm.symbolCount,
+      }
+    });
+  });
+
+  // Add MEMBER_OF relationships
+  communityResult.memberships.forEach(membership => {
+    graph.addRelationship({
+      id: `${membership.nodeId}_member_of_${membership.communityId}`,
+      type: 'MEMBER_OF',
+      sourceId: membership.nodeId,
+      targetId: membership.communityId,
+      confidence: 1.0,
+      reason: 'leiden-algorithm',
+    });
+  });
+
+  // Phase 8: Process Detection (98-99%)
+  onProgress({
+    phase: 'processes',
+    percent: 98,
+    message: 'Detecting execution flows...',
+    stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
+  });
+
+  const processResult = await processProcesses(
+    graph,
+    communityResult.memberships,
+    (message, progress) => {
+      const processProgress = 98 + (progress * 0.01);
+      onProgress({
+        phase: 'processes',
+        percent: Math.round(processProgress),
+        message,
+        stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
+      });
+    }
+  );
+
+  // Log process detection results
+  if (import.meta.env.DEV) {
+    console.log(`🔄 Process detection: ${processResult.stats.totalProcesses} processes found (${processResult.stats.crossCommunityCount} cross-community)`);
+  }
+
+  // Add Process nodes to the graph
+  processResult.processes.forEach(proc => {
+    graph.addNode({
+      id: proc.id,
+      label: 'Process' as const,
+      properties: {
+        name: proc.label,
+        filePath: '',
+        heuristicLabel: proc.heuristicLabel,
+        processType: proc.processType,
+        stepCount: proc.stepCount,
+        communities: proc.communities,
+        entryPointId: proc.entryPointId,
+        terminalId: proc.terminalId,
+      }
+    });
+  });
+
+  // Add STEP_IN_PROCESS relationships
+  processResult.steps.forEach(step => {
+    graph.addRelationship({
+      id: `${step.nodeId}_step_${step.step}_${step.processId}`,
+      type: 'STEP_IN_PROCESS',
+      sourceId: step.nodeId,
+      targetId: step.processId,
+      confidence: 1.0,
+      reason: 'trace-detection',
+      step: step.step,
+    });
+  });
+
+  
+  // Phase 9: Complete (100%)
+  onProgress({
+    phase: 'complete',
+    percent: 100,
+    message: `Graph complete! ${communityResult.stats.totalCommunities} communities, ${processResult.stats.totalProcesses} processes detected.`,
+    stats: { 
+      filesProcessed: files.length, 
+      totalFiles: files.length, 
+      nodesCreated: graph.nodeCount 
+    },
+  });
+
+  // Cleanup WASM memory before returning
+  astCache.clear();
+  
+  return { graph, fileContents, communityResult, processResult };
+
+  } catch (error) {
+    cleanup();
+    throw error;
+  }
+};
diff --git a/gitnexus-web/src/core/ingestion/process-processor.ts b/gitnexus-web/src/core/ingestion/process-processor.ts
new file mode 100644
index 000000000..cf983d2e6
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/process-processor.ts
@@ -0,0 +1,409 @@
+/**
+ * Process Detection Processor
+ * 
+ * Detects execution flows (Processes) in the code graph by:
+ * 1. Finding entry points (functions with no internal callers)
+ * 2. Tracing forward via CALLS edges (BFS)
+ * 3. Grouping and deduplicating similar paths
+ * 4. Labeling with heuristic names
+ * 
+ * Processes help agents understand how features work through the codebase.
+ */
+
+import { KnowledgeGraph, GraphNode, GraphRelationship, NodeLabel } from '../graph/types';
+import { CommunityMembership } from './community-processor';
+import { calculateEntryPointScore, isTestFile } from './entry-point-scoring';
+
+// ============================================================================
+// CONFIGURATION
+// ============================================================================
+
+export interface ProcessDetectionConfig {
+  maxTraceDepth: number;      // Maximum steps to trace (default: 10)
+  maxBranching: number;       // Max branches to follow per node (default: 3)
+  maxProcesses: number;       // Maximum processes to detect (default: 50)
+  minSteps: number;           // Minimum steps for a valid process (default: 2)
+}
+
+const DEFAULT_CONFIG: ProcessDetectionConfig = {
+  maxTraceDepth: 10,
+  maxBranching: 4,
+  maxProcesses: 75,
+  minSteps: 2,
+};
+
+// ============================================================================
+// TYPES
+// ============================================================================
+
+export interface ProcessNode {
+  id: string;                    // "proc_handleLogin_createSession"
+  label: string;                 // "HandleLogin → CreateSession"
+  heuristicLabel: string;
+  processType: 'intra_community' | 'cross_community';
+  stepCount: number;
+  communities: string[];         // Community IDs touched
+  entryPointId: string;
+  terminalId: string;
+  trace: string[];               // Ordered array of node IDs
+}
+
+export interface ProcessStep {
+  nodeId: string;
+  processId: string;
+  step: number;                  // 1-indexed position in trace
+}
+
+export interface ProcessDetectionResult {
+  processes: ProcessNode[];
+  steps: ProcessStep[];
+  stats: {
+    totalProcesses: number;
+    crossCommunityCount: number;
+    avgStepCount: number;
+    entryPointsFound: number;
+  };
+}
+
+// ============================================================================
+// MAIN PROCESSOR
+// ============================================================================
+
+/**
+ * Detect processes (execution flows) in the knowledge graph
+ * 
+ * This runs AFTER community detection, using CALLS edges to trace flows.
+ */
+export const processProcesses = async (
+  knowledgeGraph: KnowledgeGraph,
+  memberships: CommunityMembership[],
+  onProgress?: (message: string, progress: number) => void,
+  config: Partial = {}
+): Promise => {
+  const cfg = { ...DEFAULT_CONFIG, ...config };
+  
+  onProgress?.('Finding entry points...', 0);
+  
+  // Build lookup maps
+  const membershipMap = new Map();
+  memberships.forEach(m => membershipMap.set(m.nodeId, m.communityId));
+  
+  const callsEdges = buildCallsGraph(knowledgeGraph);
+  const reverseCallsEdges = buildReverseCallsGraph(knowledgeGraph);
+  const nodeMap = new Map();
+  knowledgeGraph.nodes.forEach(n => nodeMap.set(n.id, n));
+  
+  // Step 1: Find entry points (functions that call others but have few callers)
+  const entryPoints = findEntryPoints(knowledgeGraph, reverseCallsEdges, callsEdges);
+  
+  onProgress?.(`Found ${entryPoints.length} entry points, tracing flows...`, 20);
+  
+  onProgress?.(`Found ${entryPoints.length} entry points, tracing flows...`, 20);
+  
+  // Step 2: Trace processes from each entry point
+  const allTraces: string[][] = [];
+  
+  for (let i = 0; i < entryPoints.length && allTraces.length < cfg.maxProcesses * 2; i++) {
+    const entryId = entryPoints[i];
+    const traces = traceFromEntryPoint(entryId, callsEdges, cfg);
+    
+    // Filter out traces that are too short
+    traces.filter(t => t.length >= cfg.minSteps).forEach(t => allTraces.push(t));
+    
+    if (i % 10 === 0) {
+      onProgress?.(`Tracing entry point ${i + 1}/${entryPoints.length}...`, 20 + (i / entryPoints.length) * 40);
+    }
+  }
+  
+  onProgress?.(`Found ${allTraces.length} traces, deduplicating...`, 60);
+  
+  // Step 3: Deduplicate similar traces
+  const uniqueTraces = deduplicateTraces(allTraces);
+  
+  // Step 4: Limit to max processes (prioritize longer traces)
+  const limitedTraces = uniqueTraces
+    .sort((a, b) => b.length - a.length)
+    .slice(0, cfg.maxProcesses);
+  
+  onProgress?.(`Creating ${limitedTraces.length} process nodes...`, 80);
+  
+  // Step 5: Create process nodes
+  const processes: ProcessNode[] = [];
+  const steps: ProcessStep[] = [];
+  
+  limitedTraces.forEach((trace, idx) => {
+    const entryPointId = trace[0];
+    const terminalId = trace[trace.length - 1];
+    
+    // Get communities touched
+    const communitiesSet = new Set();
+    trace.forEach(nodeId => {
+      const comm = membershipMap.get(nodeId);
+      if (comm) communitiesSet.add(comm);
+    });
+    const communities = Array.from(communitiesSet);
+    
+    // Determine process type
+    const processType: 'intra_community' | 'cross_community' = 
+      communities.length > 1 ? 'cross_community' : 'intra_community';
+    
+    // Generate label
+    const entryNode = nodeMap.get(entryPointId);
+    const terminalNode = nodeMap.get(terminalId);
+    const entryName = entryNode?.properties.name || 'Unknown';
+    const terminalName = terminalNode?.properties.name || 'Unknown';
+    const heuristicLabel = `${capitalize(entryName)} → ${capitalize(terminalName)}`;
+    
+    const processId = `proc_${idx}_${sanitizeId(entryName)}`;
+    
+    processes.push({
+      id: processId,
+      label: heuristicLabel,
+      heuristicLabel,
+      processType,
+      stepCount: trace.length,
+      communities,
+      entryPointId,
+      terminalId,
+      trace,
+    });
+    
+    // Create step relationships
+    trace.forEach((nodeId, stepIdx) => {
+      steps.push({
+        nodeId,
+        processId,
+        step: stepIdx + 1,  // 1-indexed
+      });
+    });
+  });
+  
+  onProgress?.('Process detection complete!', 100);
+  
+  // Calculate stats
+  const crossCommunityCount = processes.filter(p => p.processType === 'cross_community').length;
+  const avgStepCount = processes.length > 0 
+    ? processes.reduce((sum, p) => sum + p.stepCount, 0) / processes.length 
+    : 0;
+  
+  return {
+    processes,
+    steps,
+    stats: {
+      totalProcesses: processes.length,
+      crossCommunityCount,
+      avgStepCount: Math.round(avgStepCount * 10) / 10,
+      entryPointsFound: entryPoints.length,
+    },
+  };
+};
+
+// ============================================================================
+// HELPER: Build CALLS adjacency list
+// ============================================================================
+
+type AdjacencyList = Map;
+
+const buildCallsGraph = (graph: KnowledgeGraph): AdjacencyList => {
+  const adj = new Map();
+  
+  graph.relationships.forEach(rel => {
+    if (rel.type === 'CALLS') {
+      if (!adj.has(rel.sourceId)) {
+        adj.set(rel.sourceId, []);
+      }
+      adj.get(rel.sourceId)!.push(rel.targetId);
+    }
+  });
+  
+  return adj;
+};
+
+const buildReverseCallsGraph = (graph: KnowledgeGraph): AdjacencyList => {
+  const adj = new Map();
+  
+  graph.relationships.forEach(rel => {
+    if (rel.type === 'CALLS') {
+      if (!adj.has(rel.targetId)) {
+        adj.set(rel.targetId, []);
+      }
+      adj.get(rel.targetId)!.push(rel.sourceId);
+    }
+  });
+  
+  return adj;
+};
+
+/**
+ * Find functions/methods that are good entry points for tracing.
+ * 
+ * Entry points are scored based on:
+ * 1. Call ratio (calls many, called by few)
+ * 2. Export status (exported/public functions rank higher)
+ * 3. Name patterns (handle*, on*, *Controller, etc.)
+ * 
+ * Test files are excluded entirely.
+ */
+const findEntryPoints = (
+  graph: KnowledgeGraph, 
+  reverseCallsEdges: AdjacencyList,
+  callsEdges: AdjacencyList
+): string[] => {
+  const symbolTypes = new Set(['Function', 'Method']);
+  const entryPointCandidates: { 
+    id: string; 
+    score: number; 
+    reasons: string[];
+  }[] = [];
+  
+  graph.nodes.forEach(node => {
+    if (!symbolTypes.has(node.label)) return;
+    
+    const filePath = node.properties.filePath || '';
+    
+    // Skip test files entirely
+    if (isTestFile(filePath)) return;
+    
+    const callers = reverseCallsEdges.get(node.id) || [];
+    const callees = callsEdges.get(node.id) || [];
+    
+    // Must have at least 1 outgoing call to trace forward
+    if (callees.length === 0) return;
+    
+    // Calculate entry point score using new scoring system
+    const { score, reasons } = calculateEntryPointScore(
+      node.properties.name,
+      node.properties.language || 'javascript',
+      node.properties.isExported ?? false,
+      callers.length,
+      callees.length,
+      filePath  // Pass filePath for framework detection
+    );
+    
+    if (score > 0) {
+      entryPointCandidates.push({ id: node.id, score, reasons });
+    }
+  });
+  
+  // Sort by score descending and return top candidates
+  const sorted = entryPointCandidates.sort((a, b) => b.score - a.score);
+  
+  // DEBUG: Log top candidates with new scoring details
+  if (sorted.length > 0 && typeof import.meta !== 'undefined' && import.meta.env?.DEV) {
+    console.log(`[Process] Top 10 entry point candidates (new scoring):`);
+    sorted.slice(0, 10).forEach((c, i) => {
+      const node = graph.nodes.find(n => n.id === c.id);
+      const exported = node?.properties.isExported ? '✓' : '✗';
+      const shortPath = node?.properties.filePath?.split('/').slice(-2).join('/') || '';
+      console.log(`  ${i+1}. ${node?.properties.name} [exported:${exported}] (${shortPath})`);
+      console.log(`     score: ${c.score.toFixed(2)} = [${c.reasons.join(' × ')}]`);
+    });
+  }
+  
+  return sorted
+    .slice(0, 200)  // Limit to prevent explosion
+    .map(c => c.id);
+};
+
+// ============================================================================
+// HELPER: Trace from entry point (BFS)
+// ============================================================================
+
+/**
+ * Trace forward from an entry point using BFS.
+ * Returns all distinct paths up to maxDepth.
+ */
+const traceFromEntryPoint = (
+  entryId: string,
+  callsEdges: AdjacencyList,
+  config: ProcessDetectionConfig
+): string[][] => {
+  const traces: string[][] = [];
+  
+  // BFS with path tracking
+  // Each queue item: [currentNodeId, pathSoFar]
+  const queue: [string, string[]][] = [[entryId, [entryId]]];
+  const visited = new Set();
+  
+  while (queue.length > 0 && traces.length < config.maxBranching * 3) {
+    const [currentId, path] = queue.shift()!;
+    
+    // Get outgoing calls
+    const callees = callsEdges.get(currentId) || [];
+    
+    if (callees.length === 0) {
+      // Terminal node - this is a complete trace
+      if (path.length >= config.minSteps) {
+        traces.push([...path]);
+      }
+    } else if (path.length >= config.maxTraceDepth) {
+      // Max depth reached - save what we have
+      if (path.length >= config.minSteps) {
+        traces.push([...path]);
+      }
+    } else {
+      // Continue tracing - limit branching
+      const limitedCallees = callees.slice(0, config.maxBranching);
+      let addedBranch = false;
+      
+      for (const calleeId of limitedCallees) {
+        // Avoid cycles
+        if (!path.includes(calleeId)) {
+          queue.push([calleeId, [...path, calleeId]]);
+          addedBranch = true;
+        }
+      }
+      
+      // If all branches were cycles, save current path as terminal
+      if (!addedBranch && path.length >= config.minSteps) {
+        traces.push([...path]);
+      }
+    }
+  }
+  
+  return traces;
+};
+
+// ============================================================================
+// HELPER: Deduplicate traces
+// ============================================================================
+
+/**
+ * Merge traces that are subsets of other traces.
+ * Keep longer traces, remove redundant shorter ones.
+ */
+const deduplicateTraces = (traces: string[][]): string[][] => {
+  if (traces.length === 0) return [];
+  
+  // Sort by length descending
+  const sorted = [...traces].sort((a, b) => b.length - a.length);
+  const unique: string[][] = [];
+  
+  for (const trace of sorted) {
+    // Check if this trace is a subset of any already-added trace
+    const traceKey = trace.join('->');
+    const isSubset = unique.some(existing => {
+      const existingKey = existing.join('->');
+      return existingKey.includes(traceKey);
+    });
+    
+    if (!isSubset) {
+      unique.push(trace);
+    }
+  }
+  
+  return unique;
+};
+
+// ============================================================================
+// HELPER: String utilities
+// ============================================================================
+
+const capitalize = (s: string): string => {
+  if (!s) return s;
+  return s.charAt(0).toUpperCase() + s.slice(1);
+};
+
+const sanitizeId = (s: string): string => {
+  return s.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 20).toLowerCase();
+};
diff --git a/gitnexus-web/src/core/ingestion/structure-processor.ts b/gitnexus-web/src/core/ingestion/structure-processor.ts
new file mode 100644
index 000000000..c73a5837c
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/structure-processor.ts
@@ -0,0 +1,48 @@
+import { generateId } from "@/lib/utils";
+import { KnowledgeGraph, GraphNode, GraphRelationship } from "../graph/types";
+
+export const processStructure = ( graph: KnowledgeGraph, paths: string[])=>{
+    paths.forEach( path => {
+        const parts = path.split('/')
+        let currentPath = ''
+        let parentId = ''
+
+        parts.forEach( (part, index ) => {
+            const isFile = index === parts.length - 1
+            const label = isFile ? 'File' : 'Folder' 
+
+            currentPath = currentPath ? `${currentPath}/${part}` : part
+
+            const nodeId=generateId(label, currentPath)
+
+            const node: GraphNode = {
+                id: nodeId,
+                label: label,
+                properties: {
+                    name: part,
+                    filePath: currentPath
+                }
+            }
+            graph.addNode(node)
+
+            if(parentId){
+                const relId = generateId('CONTAINS', `${parentId}->${nodeId}`)
+
+                const relationship: GraphRelationship={
+                    id: relId,
+                    type: 'CONTAINS',
+                    sourceId: parentId,
+                    targetId: nodeId,
+                    confidence: 1.0,
+                    reason: '',
+                }
+
+                graph.addRelationship(relationship)
+            }
+
+            parentId = nodeId
+
+        })
+    })
+}
+
diff --git a/gitnexus-web/src/core/ingestion/symbol-table.ts b/gitnexus-web/src/core/ingestion/symbol-table.ts
new file mode 100644
index 000000000..c8c35d56f
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/symbol-table.ts
@@ -0,0 +1,80 @@
+export interface SymbolDefinition {
+  nodeId: string;
+  filePath: string;
+  type: string; // 'Function', 'Class', etc.
+}
+
+export interface SymbolTable {
+  /**
+   * Register a new symbol definition
+   */
+  add: (filePath: string, name: string, nodeId: string, type: string) => void;
+  
+  /**
+   * High Confidence: Look for a symbol specifically inside a file
+   * Returns the Node ID if found
+   */
+  lookupExact: (filePath: string, name: string) => string | undefined;
+  
+  /**
+   * Low Confidence: Look for a symbol anywhere in the project
+   * Used when imports are missing or for framework magic
+   */
+  lookupFuzzy: (name: string) => SymbolDefinition[];
+  
+  /**
+   * Debugging: See how many symbols are tracked
+   */
+  getStats: () => { fileCount: number; globalSymbolCount: number };
+  
+  /**
+   * Cleanup memory
+   */
+  clear: () => void;
+}
+
+export const createSymbolTable = (): SymbolTable => {
+  // 1. File-Specific Index (The "Good" one)
+  // Structure: FilePath -> (SymbolName -> NodeID)
+  const fileIndex = new Map>();
+
+  // 2. Global Reverse Index (The "Backup")
+  // Structure: SymbolName -> [List of Definitions]
+  const globalIndex = new Map();
+
+  const add = (filePath: string, name: string, nodeId: string, type: string) => {
+    // A. Add to File Index
+    if (!fileIndex.has(filePath)) {
+      fileIndex.set(filePath, new Map());
+    }
+    fileIndex.get(filePath)!.set(name, nodeId);
+
+    // B. Add to Global Index
+    if (!globalIndex.has(name)) {
+      globalIndex.set(name, []);
+    }
+    globalIndex.get(name)!.push({ nodeId, filePath, type });
+  };
+
+  const lookupExact = (filePath: string, name: string): string | undefined => {
+    const fileSymbols = fileIndex.get(filePath);
+    if (!fileSymbols) return undefined;
+    return fileSymbols.get(name);
+  };
+
+  const lookupFuzzy = (name: string): SymbolDefinition[] => {
+    return globalIndex.get(name) || [];
+  };
+
+  const getStats = () => ({
+    fileCount: fileIndex.size,
+    globalSymbolCount: globalIndex.size
+  });
+
+  const clear = () => {
+    fileIndex.clear();
+    globalIndex.clear();
+  };
+
+  return { add, lookupExact, lookupFuzzy, getStats, clear };
+};
\ No newline at end of file
diff --git a/gitnexus-web/src/core/ingestion/tree-sitter-queries.ts b/gitnexus-web/src/core/ingestion/tree-sitter-queries.ts
new file mode 100644
index 000000000..a931b4a40
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/tree-sitter-queries.ts
@@ -0,0 +1,331 @@
+import { SupportedLanguages } from '../../config/supported-languages';
+
+/* 
+ * Tree-sitter queries for extracting code definitions.
+ * 
+ * Note: Different grammars (typescript vs tsx vs javascript) may have
+ * slightly different node types. These queries are designed to be 
+ * compatible with the standard tree-sitter grammars.
+ */
+
+// TypeScript queries - works with tree-sitter-typescript
+export const TYPESCRIPT_QUERIES = `
+(class_declaration
+  name: (type_identifier) @name) @definition.class
+
+(interface_declaration
+  name: (type_identifier) @name) @definition.interface
+
+(function_declaration
+  name: (identifier) @name) @definition.function
+
+(method_definition
+  name: (property_identifier) @name) @definition.method
+
+(lexical_declaration
+  (variable_declarator
+    name: (identifier) @name
+    value: (arrow_function))) @definition.function
+
+(lexical_declaration
+  (variable_declarator
+    name: (identifier) @name
+    value: (function_expression))) @definition.function
+
+(export_statement
+  declaration: (lexical_declaration
+    (variable_declarator
+      name: (identifier) @name
+      value: (arrow_function)))) @definition.function
+
+(export_statement
+  declaration: (lexical_declaration
+    (variable_declarator
+      name: (identifier) @name
+      value: (function_expression)))) @definition.function
+
+(import_statement
+  source: (string) @import.source) @import
+
+(call_expression
+  function: (identifier) @call.name) @call
+
+(call_expression
+  function: (member_expression
+    property: (property_identifier) @call.name)) @call
+
+; Heritage queries - class extends
+(class_declaration
+  name: (type_identifier) @heritage.class
+  (class_heritage
+    (extends_clause
+      value: (identifier) @heritage.extends))) @heritage
+
+; Heritage queries - class implements interface
+(class_declaration
+  name: (type_identifier) @heritage.class
+  (class_heritage
+    (implements_clause
+      (type_identifier) @heritage.implements))) @heritage.impl
+`;
+
+// JavaScript queries - works with tree-sitter-javascript  
+export const JAVASCRIPT_QUERIES = `
+(class_declaration
+  name: (identifier) @name) @definition.class
+
+(function_declaration
+  name: (identifier) @name) @definition.function
+
+(method_definition
+  name: (property_identifier) @name) @definition.method
+
+(lexical_declaration
+  (variable_declarator
+    name: (identifier) @name
+    value: (arrow_function))) @definition.function
+
+(lexical_declaration
+  (variable_declarator
+    name: (identifier) @name
+    value: (function_expression))) @definition.function
+
+(export_statement
+  declaration: (lexical_declaration
+    (variable_declarator
+      name: (identifier) @name
+      value: (arrow_function)))) @definition.function
+
+(export_statement
+  declaration: (lexical_declaration
+    (variable_declarator
+      name: (identifier) @name
+      value: (function_expression)))) @definition.function
+
+(import_statement
+  source: (string) @import.source) @import
+
+(call_expression
+  function: (identifier) @call.name) @call
+
+(call_expression
+  function: (member_expression
+    property: (property_identifier) @call.name)) @call
+
+; Heritage queries - class extends (JavaScript uses different AST than TypeScript)
+; In tree-sitter-javascript, class_heritage directly contains the parent identifier
+(class_declaration
+  name: (identifier) @heritage.class
+  (class_heritage
+    (identifier) @heritage.extends)) @heritage
+`;
+
+// Python queries - works with tree-sitter-python
+export const PYTHON_QUERIES = `
+(class_definition
+  name: (identifier) @name) @definition.class
+
+(function_definition
+  name: (identifier) @name) @definition.function
+
+(import_statement
+  name: (dotted_name) @import.source) @import
+
+(import_from_statement
+  module_name: (dotted_name) @import.source) @import
+
+(call
+  function: (identifier) @call.name) @call
+
+(call
+  function: (attribute
+    attribute: (identifier) @call.name)) @call
+
+; Heritage queries - Python class inheritance
+(class_definition
+  name: (identifier) @heritage.class
+  superclasses: (argument_list
+    (identifier) @heritage.extends)) @heritage
+`;
+
+// Java queries - works with tree-sitter-java
+export const JAVA_QUERIES = `
+; Classes, Interfaces, Enums, Annotations
+(class_declaration name: (identifier) @name) @definition.class
+(interface_declaration name: (identifier) @name) @definition.interface
+(enum_declaration name: (identifier) @name) @definition.enum
+(annotation_type_declaration name: (identifier) @name) @definition.annotation
+
+; Methods & Constructors
+(method_declaration name: (identifier) @name) @definition.method
+(constructor_declaration name: (identifier) @name) @definition.constructor
+
+; Imports - capture any import declaration child as source
+(import_declaration (_) @import.source) @import
+
+; Calls
+(method_invocation name: (identifier) @call.name) @call
+(method_invocation object: (_) name: (identifier) @call.name) @call
+
+; Heritage - extends class
+(class_declaration name: (identifier) @heritage.class
+  (superclass (type_identifier) @heritage.extends)) @heritage
+
+; Heritage - implements interfaces
+(class_declaration name: (identifier) @heritage.class
+  (super_interfaces (type_list (type_identifier) @heritage.implements))) @heritage.impl
+`;
+
+// C queries - works with tree-sitter-c
+export const C_QUERIES = `
+; Functions
+(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
+(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function
+
+; Structs, Unions, Enums, Typedefs
+(struct_specifier name: (type_identifier) @name) @definition.struct
+(union_specifier name: (type_identifier) @name) @definition.union
+(enum_specifier name: (type_identifier) @name) @definition.enum
+(type_definition declarator: (type_identifier) @name) @definition.typedef
+
+; Macros
+(preproc_function_def name: (identifier) @name) @definition.macro
+(preproc_def name: (identifier) @name) @definition.macro
+
+; Includes
+(preproc_include path: (_) @import.source) @import
+
+; Calls
+(call_expression function: (identifier) @call.name) @call
+(call_expression function: (field_expression field: (field_identifier) @call.name)) @call
+`;
+
+// Go queries - works with tree-sitter-go
+export const GO_QUERIES = `
+; Functions & Methods
+(function_declaration name: (identifier) @name) @definition.function
+(method_declaration name: (field_identifier) @name) @definition.method
+
+; Types
+(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @definition.struct
+(type_declaration (type_spec name: (type_identifier) @name type: (interface_type))) @definition.interface
+(type_declaration (type_spec name: (type_identifier) @name)) @definition.type
+
+; Imports
+(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import
+(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import
+
+; Calls
+(call_expression function: (identifier) @call.name) @call
+(call_expression function: (selector_expression field: (field_identifier) @call.name)) @call
+`;
+
+// C++ queries - works with tree-sitter-cpp
+export const CPP_QUERIES = `
+; Classes, Structs, Namespaces
+(class_specifier name: (type_identifier) @name) @definition.class
+(struct_specifier name: (type_identifier) @name) @definition.struct
+(namespace_definition name: (namespace_identifier) @name) @definition.namespace
+(enum_specifier name: (type_identifier) @name) @definition.enum
+
+; Functions & Methods
+(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
+(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method
+
+; Templates
+(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template
+(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template
+
+; Includes
+(preproc_include path: (_) @import.source) @import
+
+; Calls
+(call_expression function: (identifier) @call.name) @call
+(call_expression function: (field_expression field: (field_identifier) @call.name)) @call
+(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call
+(call_expression function: (template_function name: (identifier) @call.name)) @call
+
+; Heritage
+(class_specifier name: (type_identifier) @heritage.class
+  (base_class_clause (type_identifier) @heritage.extends)) @heritage
+(class_specifier name: (type_identifier) @heritage.class
+  (base_class_clause (access_specifier) (type_identifier) @heritage.extends)) @heritage
+`;
+
+// C# queries - works with tree-sitter-c-sharp
+export const CSHARP_QUERIES = `
+; Types
+(class_declaration name: (identifier) @name) @definition.class
+(interface_declaration name: (identifier) @name) @definition.interface
+(struct_declaration name: (identifier) @name) @definition.struct
+(enum_declaration name: (identifier) @name) @definition.enum
+(record_declaration name: (identifier) @name) @definition.record
+(delegate_declaration name: (identifier) @name) @definition.delegate
+
+; Namespaces
+(namespace_declaration name: (identifier) @name) @definition.namespace
+(namespace_declaration name: (qualified_name) @name) @definition.namespace
+
+; Methods & Properties
+(method_declaration name: (identifier) @name) @definition.method
+(local_function_statement name: (identifier) @name) @definition.function
+(constructor_declaration name: (identifier) @name) @definition.constructor
+(property_declaration name: (identifier) @name) @definition.property
+
+; Using
+(using_directive (qualified_name) @import.source) @import
+(using_directive (identifier) @import.source) @import
+
+; Calls
+(invocation_expression function: (identifier) @call.name) @call
+(invocation_expression function: (member_access_expression name: (identifier) @call.name)) @call
+
+; Heritage
+(class_declaration name: (identifier) @heritage.class
+  (base_list (simple_base_type (identifier) @heritage.extends))) @heritage
+(class_declaration name: (identifier) @heritage.class
+  (base_list (simple_base_type (generic_name (identifier) @heritage.extends)))) @heritage
+`;
+
+// Rust queries - works with tree-sitter-rust
+export const RUST_QUERIES = `
+; Functions & Items
+(function_item name: (identifier) @name) @definition.function
+(struct_item name: (type_identifier) @name) @definition.struct
+(enum_item name: (type_identifier) @name) @definition.enum
+(trait_item name: (type_identifier) @name) @definition.trait
+(impl_item type: (type_identifier) @name) @definition.impl
+(mod_item name: (identifier) @name) @definition.module
+
+; Type aliases, const, static, macros
+(type_item name: (type_identifier) @name) @definition.type
+(const_item name: (identifier) @name) @definition.const
+(static_item name: (identifier) @name) @definition.static
+(macro_definition name: (identifier) @name) @definition.macro
+
+; Use statements
+(use_declaration argument: (_) @import.source) @import
+
+; Calls
+(call_expression function: (identifier) @call.name) @call
+(call_expression function: (field_expression field: (field_identifier) @call.name)) @call
+(call_expression function: (scoped_identifier name: (identifier) @call.name)) @call
+(call_expression function: (generic_function function: (identifier) @call.name)) @call
+
+; Heritage (trait implementation)
+(impl_item trait: (type_identifier) @heritage.trait type: (type_identifier) @heritage.class) @heritage
+(impl_item trait: (generic_type type: (type_identifier) @heritage.trait) type: (type_identifier) @heritage.class) @heritage
+`;
+
+export const LANGUAGE_QUERIES: Record = {
+  [SupportedLanguages.TypeScript]: TYPESCRIPT_QUERIES,
+  [SupportedLanguages.JavaScript]: JAVASCRIPT_QUERIES,
+  [SupportedLanguages.Python]: PYTHON_QUERIES,
+  [SupportedLanguages.Java]: JAVA_QUERIES,
+  [SupportedLanguages.C]: C_QUERIES,
+  [SupportedLanguages.Go]: GO_QUERIES,
+  [SupportedLanguages.CPlusPlus]: CPP_QUERIES,
+  [SupportedLanguages.CSharp]: CSHARP_QUERIES,
+  [SupportedLanguages.Rust]: RUST_QUERIES,
+};
+ 
\ No newline at end of file
diff --git a/gitnexus-web/src/core/ingestion/utils.ts b/gitnexus-web/src/core/ingestion/utils.ts
new file mode 100644
index 000000000..959eb55dc
--- /dev/null
+++ b/gitnexus-web/src/core/ingestion/utils.ts
@@ -0,0 +1,30 @@
+import { SupportedLanguages } from '../../config/supported-languages';
+
+/**
+ * Map file extension to SupportedLanguage enum
+ */
+export const getLanguageFromFilename = (filename: string): SupportedLanguages | null => {
+  // TypeScript (including TSX)
+  if (filename.endsWith('.tsx')) return SupportedLanguages.TypeScript;
+  if (filename.endsWith('.ts')) return SupportedLanguages.TypeScript;
+  // JavaScript (including JSX)
+  if (filename.endsWith('.jsx')) return SupportedLanguages.JavaScript;
+  if (filename.endsWith('.js')) return SupportedLanguages.JavaScript;
+  // Python
+  if (filename.endsWith('.py')) return SupportedLanguages.Python;
+  // Java
+  if (filename.endsWith('.java')) return SupportedLanguages.Java;
+  // C (source and headers)
+  if (filename.endsWith('.c') || filename.endsWith('.h')) return SupportedLanguages.C;
+  // C++ (all common extensions)
+  if (filename.endsWith('.cpp') || filename.endsWith('.cc') || filename.endsWith('.cxx') ||
+      filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh')) return SupportedLanguages.CPlusPlus;
+  // C#
+  if (filename.endsWith('.cs')) return SupportedLanguages.CSharp;
+  // Go
+  if (filename.endsWith('.go')) return SupportedLanguages.Go;
+  // Rust
+  if (filename.endsWith('.rs')) return SupportedLanguages.Rust;
+  return null;
+};
+
diff --git a/gitnexus-web/src/core/kuzu/csv-generator.ts b/gitnexus-web/src/core/kuzu/csv-generator.ts
new file mode 100644
index 000000000..e565399cd
--- /dev/null
+++ b/gitnexus-web/src/core/kuzu/csv-generator.ts
@@ -0,0 +1,321 @@
+/**
+ * CSV Generator for KuzuDB Hybrid Schema
+ * 
+ * Generates separate CSV files for each node table and one relation CSV.
+ * This enables efficient bulk loading via COPY FROM for hybrid schema.
+ * 
+ * RFC 4180 Compliant:
+ * - Fields containing commas, double quotes, or newlines are enclosed in double quotes
+ * - Double quotes within fields are escaped by doubling them ("")
+ * - All fields are consistently quoted for safety with code content
+ */
+
+import { KnowledgeGraph, GraphNode, NodeLabel } from '../graph/types';
+import { NODE_TABLES, NodeTableName } from './schema';
+
+// ============================================================================
+// CSV ESCAPE UTILITIES
+// ============================================================================
+
+/**
+ * Sanitize string to ensure valid UTF-8 and safe CSV content for KuzuDB
+ * Removes or replaces invalid characters that would break CSV parsing.
+ * 
+ * Critical: KuzuDB's CSV parser can misinterpret \r\n inside quoted fields.
+ * We normalize all line endings to \n only.
+ */
+const sanitizeUTF8 = (str: string): string => {
+  return str
+    .replace(/\r\n/g, '\n')          // Normalize Windows line endings first
+    .replace(/\r/g, '\n')            // Normalize remaining \r to \n
+    .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Remove control chars except \t \n
+    .replace(/[\uD800-\uDFFF]/g, '') // Remove surrogate pairs (invalid standalone)
+    .replace(/[\uFFFE\uFFFF]/g, ''); // Remove BOM and special chars
+};
+
+/**
+ * RFC 4180 compliant CSV field escaping
+ * ALWAYS wraps in double quotes for safety with code content
+ */
+const escapeCSVField = (value: string | number | undefined | null): string => {
+  if (value === undefined || value === null) {
+    return '""';
+  }
+  let str = String(value);
+  str = sanitizeUTF8(str);
+  return `"${str.replace(/"/g, '""')}"`;
+};
+
+/**
+ * Escape a numeric value (no quotes needed for numbers)
+ */
+const escapeCSVNumber = (value: number | undefined | null, defaultValue: number = -1): string => {
+  if (value === undefined || value === null) {
+    return String(defaultValue);
+  }
+  return String(value);
+};
+
+// ============================================================================
+// CONTENT EXTRACTION
+// ============================================================================
+
+/**
+ * Check if content looks like binary data
+ */
+const isBinaryContent = (content: string): boolean => {
+  if (!content || content.length === 0) return false;
+  const sample = content.slice(0, 1000);
+  let nonPrintable = 0;
+  for (let i = 0; i < sample.length; i++) {
+    const code = sample.charCodeAt(i);
+    if ((code < 9) || (code > 13 && code < 32) || code === 127) {
+      nonPrintable++;
+    }
+  }
+  return (nonPrintable / sample.length) > 0.1;
+};
+
+/**
+ * Extract code content for a node
+ */
+const extractContent = (
+  node: GraphNode,
+  fileContents: Map
+): string => {
+  const filePath = node.properties.filePath;
+  const content = fileContents.get(filePath);
+  
+  if (!content) return '';
+  if (node.label === 'Folder') return '';
+  if (isBinaryContent(content)) return '[Binary file - content not stored]';
+  
+  // For File nodes, return content (limited)
+  if (node.label === 'File') {
+    const MAX_FILE_CONTENT = 10000;
+    if (content.length > MAX_FILE_CONTENT) {
+      return content.slice(0, MAX_FILE_CONTENT) + '\n... [truncated]';
+    }
+    return content;
+  }
+  
+  // For code elements, extract the relevant lines with context
+  const startLine = node.properties.startLine;
+  const endLine = node.properties.endLine;
+  
+  if (startLine === undefined || endLine === undefined) return '';
+  
+  const lines = content.split('\n');
+  const contextLines = 2;
+  const start = Math.max(0, startLine - contextLines);
+  const end = Math.min(lines.length - 1, endLine + contextLines);
+  
+  const snippet = lines.slice(start, end + 1).join('\n');
+  const MAX_SNIPPET = 5000;
+  if (snippet.length > MAX_SNIPPET) {
+    return snippet.slice(0, MAX_SNIPPET) + '\n... [truncated]';
+  }
+  return snippet;
+};
+
+// ============================================================================
+// CSV GENERATION RESULT TYPE
+// ============================================================================
+
+export interface CSVData {
+  nodes: Map;
+  relCSV: string;  // Single relation CSV with from,to,type,confidence,reason columns
+}
+
+// ============================================================================
+// NODE CSV GENERATORS
+// ============================================================================
+
+/**
+ * Generate CSV for File nodes
+ * Headers: id,name,filePath,content
+ */
+const generateFileCSV = (nodes: GraphNode[], fileContents: Map): string => {
+  const headers = ['id', 'name', 'filePath', 'content'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const node of nodes) {
+    if (node.label !== 'File') continue;
+    const content = extractContent(node, fileContents);
+    rows.push([
+      escapeCSVField(node.id),
+      escapeCSVField(node.properties.name || ''),
+      escapeCSVField(node.properties.filePath || ''),
+      escapeCSVField(content),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+/**
+ * Generate CSV for Folder nodes
+ * Headers: id,name,filePath
+ */
+const generateFolderCSV = (nodes: GraphNode[]): string => {
+  const headers = ['id', 'name', 'filePath'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const node of nodes) {
+    if (node.label !== 'Folder') continue;
+    rows.push([
+      escapeCSVField(node.id),
+      escapeCSVField(node.properties.name || ''),
+      escapeCSVField(node.properties.filePath || ''),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+/**
+ * Generate CSV for code element nodes (Function, Class, Interface, Method, CodeElement)
+ * Headers: id,name,filePath,startLine,endLine,isExported,content
+ */
+const generateCodeElementCSV = (
+  nodes: GraphNode[],
+  label: NodeLabel,
+  fileContents: Map
+): string => {
+  const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'isExported', 'content'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const node of nodes) {
+    if (node.label !== label) continue;
+    const content = extractContent(node, fileContents);
+    rows.push([
+      escapeCSVField(node.id),
+      escapeCSVField(node.properties.name || ''),
+      escapeCSVField(node.properties.filePath || ''),
+      escapeCSVNumber(node.properties.startLine, -1),
+      escapeCSVNumber(node.properties.endLine, -1),
+      node.properties.isExported ? 'true' : 'false',
+      escapeCSVField(content),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+/**
+ * Generate CSV for Community nodes (from Leiden algorithm)
+ * Headers: id,label,heuristicLabel,keywords,description,enrichedBy,cohesion,symbolCount
+ */
+const generateCommunityCSV = (nodes: GraphNode[]): string => {
+  const headers = ['id', 'label', 'heuristicLabel', 'keywords', 'description', 'enrichedBy', 'cohesion', 'symbolCount'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const node of nodes) {
+    if (node.label !== 'Community') continue;
+    
+    // Handle keywords array - convert to KuzuDB array format
+    const keywords = (node.properties as any).keywords || [];
+    const keywordsStr = `[${keywords.map((k: string) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
+    
+    rows.push([
+      escapeCSVField(node.id),
+      escapeCSVField(node.properties.name || ''),  // label is stored in name
+      escapeCSVField(node.properties.heuristicLabel || ''),
+      keywordsStr,  // Array format for KuzuDB
+      escapeCSVField((node.properties as any).description || ''),
+      escapeCSVField((node.properties as any).enrichedBy || 'heuristic'),
+      escapeCSVNumber(node.properties.cohesion, 0),
+      escapeCSVNumber(node.properties.symbolCount, 0),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+/**
+ * Generate CSV for Process nodes
+ * Headers: id,label,heuristicLabel,processType,stepCount,communities,entryPointId,terminalId
+ */
+const generateProcessCSV = (nodes: GraphNode[]): string => {
+  const headers = ['id', 'label', 'heuristicLabel', 'processType', 'stepCount', 'communities', 'entryPointId', 'terminalId'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const node of nodes) {
+    if (node.label !== 'Process') continue;
+    
+    // Handle communities array (string[])
+    const communities = (node.properties as any).communities || [];
+    const communitiesStr = `[${communities.map((c: string) => `'${c.replace(/'/g, "''")}'`).join(',')}]`;
+    
+    rows.push([
+      escapeCSVField(node.id),
+      escapeCSVField(node.properties.name || ''), // label stores name
+      escapeCSVField((node.properties as any).heuristicLabel || ''),
+      escapeCSVField((node.properties as any).processType || ''),
+      escapeCSVNumber((node.properties as any).stepCount, 0),
+      escapeCSVField(communitiesStr), // Needs CSV escaping because it contains commas!
+      escapeCSVField((node.properties as any).entryPointId || ''),
+      escapeCSVField((node.properties as any).terminalId || ''),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+/**
+ * Generate CSV for the single CodeRelation table
+ * Headers: from,to,type,confidence,reason
+ * 
+ * confidence: 0-1 score for CALLS edges (how sure are we about the target?)
+ * reason: 'import-resolved' | 'same-file' | 'fuzzy-global' (or empty for non-CALLS)
+ */
+const generateRelationCSV = (graph: KnowledgeGraph): string => {
+  const headers = ['from', 'to', 'type', 'confidence', 'reason', 'step'];
+  const rows: string[] = [headers.join(',')];
+  
+  for (const rel of graph.relationships) {
+    rows.push([
+      escapeCSVField(rel.sourceId),
+      escapeCSVField(rel.targetId),
+      escapeCSVField(rel.type),
+      escapeCSVNumber(rel.confidence, 1.0),
+      escapeCSVField(rel.reason),
+      escapeCSVNumber((rel as any).step, 0),
+    ].join(','));
+  }
+  
+  return rows.join('\n');
+};
+
+// ============================================================================
+// MAIN CSV GENERATION FUNCTION
+// ============================================================================
+
+/**
+ * Generate all CSV data for hybrid schema bulk loading
+ * Returns Maps of node table name -> CSV content, and single relation CSV
+ */
+export const generateAllCSVs = (
+  graph: KnowledgeGraph,
+  fileContents: Map
+): CSVData => {
+  const nodes = Array.from(graph.nodes);
+  
+  // Generate node CSVs
+  const nodeCSVs = new Map();
+  nodeCSVs.set('File', generateFileCSV(nodes, fileContents));
+  nodeCSVs.set('Folder', generateFolderCSV(nodes));
+  nodeCSVs.set('Function', generateCodeElementCSV(nodes, 'Function', fileContents));
+  nodeCSVs.set('Class', generateCodeElementCSV(nodes, 'Class', fileContents));
+  nodeCSVs.set('Interface', generateCodeElementCSV(nodes, 'Interface', fileContents));
+  nodeCSVs.set('Method', generateCodeElementCSV(nodes, 'Method', fileContents));
+  nodeCSVs.set('CodeElement', generateCodeElementCSV(nodes, 'CodeElement', fileContents));
+  nodeCSVs.set('Community', generateCommunityCSV(nodes));
+  nodeCSVs.set('Process', generateProcessCSV(nodes));
+  
+  // Generate single relation CSV
+  const relCSV = generateRelationCSV(graph);
+  
+  return { nodes: nodeCSVs, relCSV };
+};
+
diff --git a/gitnexus-web/src/core/kuzu/kuzu-adapter.ts b/gitnexus-web/src/core/kuzu/kuzu-adapter.ts
new file mode 100644
index 000000000..52885b281
--- /dev/null
+++ b/gitnexus-web/src/core/kuzu/kuzu-adapter.ts
@@ -0,0 +1,527 @@
+/**
+ * KuzuDB Adapter
+ * 
+ * Manages the KuzuDB WASM instance for client-side graph database operations.
+ * Uses the "Snapshot / Bulk Load" pattern with COPY FROM for performance.
+ * 
+ * Multi-table schema: separate tables for File, Function, Class, etc.
+ */
+
+import { KnowledgeGraph } from '../graph/types';
+import { 
+  NODE_TABLES, 
+  REL_TABLE_NAME,
+  SCHEMA_QUERIES, 
+  EMBEDDING_TABLE_NAME,
+  NodeTableName,
+} from './schema';
+import { generateAllCSVs } from './csv-generator';
+
+// Holds the reference to the dynamically loaded module
+let kuzu: any = null;
+let db: any = null;
+let conn: any = null;
+
+/**
+ * Initialize KuzuDB WASM module and create in-memory database
+ */
+export const initKuzu = async () => {
+  if (conn) return { db, conn, kuzu };
+
+  try {
+    if (import.meta.env.DEV) console.log('🚀 Initializing KuzuDB...');
+
+    // 1. Dynamic Import (Fixes the "not a function" bundler issue)
+    const kuzuModule = await import('kuzu-wasm');
+    
+    // 2. Handle Vite/Webpack "default" wrapping
+    kuzu = kuzuModule.default || kuzuModule;
+
+    // 3. Initialize WASM
+    await kuzu.init();
+    
+    // 4. Create Database with 512MB buffer pool
+    const BUFFER_POOL_SIZE = 512 * 1024 * 1024; // 512MB
+    db = new kuzu.Database(':memory:', BUFFER_POOL_SIZE);
+    conn = new kuzu.Connection(db);
+    
+    if (import.meta.env.DEV) console.log('✅ KuzuDB WASM Initialized');
+
+    // 5. Initialize Schema (all node tables, then rel tables, then embedding table)
+    for (const schemaQuery of SCHEMA_QUERIES) {
+      try {
+        await conn.query(schemaQuery);
+      } catch (e) {
+        // Schema might already exist, skip
+        if (import.meta.env.DEV) {
+          console.warn('Schema creation skipped (may already exist):', e);
+        }
+      }
+    }
+    
+    if (import.meta.env.DEV) console.log('✅ KuzuDB Multi-Table Schema Created');
+
+    return { db, conn, kuzu };
+  } catch (error) {
+    if (import.meta.env.DEV) console.error('❌ KuzuDB Initialization Failed:', error);
+    throw error;
+  }
+};
+
+/**
+ * Load a KnowledgeGraph into KuzuDB using COPY FROM (bulk load)
+ * Uses batched CSV writes and COPY statements for optimal performance
+ */
+export const loadGraphToKuzu = async (
+  graph: KnowledgeGraph, 
+  fileContents: Map
+) => {
+  const { conn, kuzu } = await initKuzu();
+  
+  try {
+    if (import.meta.env.DEV) console.log(`KuzuDB: Generating CSVs for ${graph.nodeCount} nodes...`);
+    
+    // 1. Generate all CSVs (per-table)
+    const csvData = generateAllCSVs(graph, fileContents);
+    
+    const fs = kuzu.FS;
+    
+    // 2. Write all node CSVs to virtual filesystem
+    const nodeFiles: Array<{ table: NodeTableName; path: string }> = [];
+    for (const [tableName, csv] of csvData.nodes.entries()) {
+      // Skip empty CSVs (only header row)
+      if (csv.split('\n').length <= 1) continue;
+      
+      const path = `/${tableName.toLowerCase()}.csv`;
+      try { await fs.unlink(path); } catch {}
+      await fs.writeFile(path, csv);
+      nodeFiles.push({ table: tableName, path });
+    }
+    
+    // 3. Parse relation CSV and prepare for INSERT (COPY FROM doesn't work with multi-pair tables)
+    const relLines = csvData.relCSV.split('\n').slice(1).filter(line => line.trim());
+    const relCount = relLines.length;
+    
+    if (import.meta.env.DEV) {
+      console.log(`KuzuDB: Wrote ${nodeFiles.length} node CSVs, ${relCount} relations to insert`);
+    }
+    
+    // 4. COPY all node tables (must complete before rels due to FK constraints)
+    for (const { table, path } of nodeFiles) {
+      const copyQuery = getCopyQuery(table, path);
+      await conn.query(copyQuery);
+    }
+    
+    // 5. INSERT relations one by one (COPY doesn't work with multi-pair REL tables)
+    // Build a set of valid table names for fast lookup
+    const validTables = new Set(NODE_TABLES as readonly string[]);
+
+    const getNodeLabel = (nodeId: string): string => {
+      if (nodeId.startsWith('comm_')) return 'Community';
+      if (nodeId.startsWith('proc_')) return 'Process';
+      return nodeId.split(':')[0];
+    };
+
+    // All multi-language tables are created with backticks - must always reference them with backticks
+    const escapeLabel = (label: string): string => {
+      return BACKTICK_TABLES.has(label) ? `\`${label}\`` : label;
+    };
+
+    let insertedRels = 0;
+    let skippedRels = 0;
+    const skippedRelStats = new Map();
+    for (const line of relLines) {
+      try {
+        // Format: "from","to","type",confidence,"reason",step
+        const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/);
+        if (!match) continue;
+        
+        const [, fromId, toId, relType, confidenceStr, reason, stepStr] = match;
+
+        const fromLabel = getNodeLabel(fromId);
+        const toLabel = getNodeLabel(toId);
+
+        // Skip relationships where either node's label doesn't have a table in KuzuDB
+        // Querying a non-existent table causes a fatal native crash
+        if (!validTables.has(fromLabel) || !validTables.has(toLabel)) {
+          skippedRels++;
+          continue;
+        }
+
+        const confidence = parseFloat(confidenceStr) || 1.0;
+        const step = parseInt(stepStr) || 0;
+        
+        const insertQuery = `
+          MATCH (a:${escapeLabel(fromLabel)} {id: '${fromId.replace(/'/g, "''")}'}),
+                (b:${escapeLabel(toLabel)} {id: '${toId.replace(/'/g, "''")}'})
+          CREATE (a)-[:${REL_TABLE_NAME} {type: '${relType}', confidence: ${confidence}, reason: '${reason.replace(/'/g, "''")}', step: ${step}}]->(b)
+        `;
+        await conn.query(insertQuery);
+        insertedRels++;
+      } catch (err) {
+        skippedRels++;
+        const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)"/);
+        if (match) {
+          const [, fromId, toId, relType] = match;
+          const fromLabel = getNodeLabel(fromId);
+          const toLabel = getNodeLabel(toId);
+          const key = `${relType}:${fromLabel}->` + toLabel;
+          skippedRelStats.set(key, (skippedRelStats.get(key) || 0) + 1);
+          
+          if (import.meta.env.DEV) {
+            console.warn(`⚠️ Skipped: ${key} | "${fromId}" → "${toId}" | ${err instanceof Error ? err.message : String(err)}`);
+          }
+        }
+      }
+    }
+    
+    if (import.meta.env.DEV) {
+      console.log(`KuzuDB: Inserted ${insertedRels}/${relCount} relations`);
+      if (skippedRels > 0) {
+        const topSkipped = Array.from(skippedRelStats.entries())
+          .sort((a, b) => b[1] - a[1])
+          .slice(0, 10);
+        console.warn(`KuzuDB: Skipped ${skippedRels}/${relCount} relations (top by kind/pair):`, topSkipped);
+      }
+    }
+    
+    // 6. Verify results
+    let totalNodes = 0;
+    for (const tableName of NODE_TABLES) {
+      try {
+        const countRes = await conn.query(`MATCH (n:${tableName}) RETURN count(n) AS cnt`);
+        const countRow = await countRes.getNext();
+        const count = countRow ? (countRow.cnt ?? countRow[0] ?? 0) : 0;
+        totalNodes += Number(count);
+      } catch {
+        // Table might be empty, skip
+      }
+    }
+    
+    if (import.meta.env.DEV) console.log(`✅ KuzuDB Bulk Load Complete. Total nodes: ${totalNodes}, edges: ${insertedRels}`);
+
+    // 7. Cleanup CSV files
+    for (const { path } of nodeFiles) {
+      try { await fs.unlink(path); } catch {}
+    }
+
+    return { success: true, count: totalNodes };
+
+  } catch (error) {
+    if (import.meta.env.DEV) console.error('❌ KuzuDB Bulk Load Failed:', error);
+    return { success: false, count: 0 };
+  }
+};
+
+// KuzuDB default ESCAPE is '\' (backslash), but our CSV uses RFC 4180 escaping ("" for literal quotes).
+// Source code content is full of backslashes which confuse the auto-detection.
+// We MUST explicitly set ESCAPE='"' and disable auto_detect.
+const COPY_CSV_OPTS = `(HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
+
+// Multi-language table names created with backticks in CODE_ELEMENT_BASE
+const BACKTICK_TABLES = new Set([
+  'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
+  'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation',
+  'Constructor', 'Template', 'Module',
+]);
+
+const escapeTableName = (table: string): string => {
+  return BACKTICK_TABLES.has(table) ? `\`${table}\`` : table;
+};
+
+/**
+ * Get the COPY query for a node table with correct column mapping
+ */
+const getCopyQuery = (table: NodeTableName, path: string): string => {
+  const t = escapeTableName(table);
+  if (table === 'File') {
+    return `COPY ${t}(id, name, filePath, content) FROM "${path}" ${COPY_CSV_OPTS}`;
+  }
+  if (table === 'Folder') {
+    return `COPY ${t}(id, name, filePath) FROM "${path}" ${COPY_CSV_OPTS}`;
+  }
+  if (table === 'Community') {
+    return `COPY ${t}(id, label, heuristicLabel, keywords, description, enrichedBy, cohesion, symbolCount) FROM "${path}" ${COPY_CSV_OPTS}`;
+  }
+  if (table === 'Process') {
+    return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${path}" ${COPY_CSV_OPTS}`;
+  }
+  // Code element tables (Function, Class, Interface, Method, CodeElement, and multi-language)
+  return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content) FROM "${path}" ${COPY_CSV_OPTS}`;
+};
+
+/**
+ * Execute a Cypher query against the database
+ * Returns results as named objects (not tuples) for better usability
+ */
+export const executeQuery = async (cypher: string): Promise => {
+  if (!conn) {
+    await initKuzu();
+  }
+  
+  try {
+    const result = await conn.query(cypher);
+    
+    // Extract column names from RETURN clause
+    const returnMatch = cypher.match(/RETURN\s+(.+?)(?:\s+ORDER|\s+LIMIT|\s+SKIP|\s*$)/is);
+    let columnNames: string[] = [];
+    if (returnMatch) {
+      // Parse RETURN clause to get column names/aliases
+      // Handles: "a.name, b.filePath AS path, count(x) AS cnt"
+      const returnClause = returnMatch[1];
+      columnNames = returnClause.split(',').map(col => {
+        col = col.trim();
+        // Check for AS alias
+        const asMatch = col.match(/\s+AS\s+(\w+)\s*$/i);
+        if (asMatch) return asMatch[1];
+        // Check for property access like n.name
+        const propMatch = col.match(/\.(\w+)\s*$/);
+        if (propMatch) return propMatch[1];
+        // Check for function call like count(x)
+        const funcMatch = col.match(/^(\w+)\s*\(/);
+        if (funcMatch) return funcMatch[1];
+        // Just use as-is if simple identifier
+        return col.replace(/[^a-zA-Z0-9_]/g, '_');
+      });
+    }
+    
+    // Collect all rows
+    const rows: any[] = [];
+    while (await result.hasNext()) {
+      const row = await result.getNext();
+      
+      // Convert tuple to named object if we have column names and row is array
+      if (Array.isArray(row) && columnNames.length === row.length) {
+        const namedRow: Record = {};
+        for (let i = 0; i < row.length; i++) {
+          namedRow[columnNames[i]] = row[i];
+        }
+        rows.push(namedRow);
+      } else {
+        // Already an object or column count doesn't match
+        rows.push(row);
+      }
+    }
+    
+    return rows;
+  } catch (error) {
+    if (import.meta.env.DEV) console.error('Query execution failed:', error);
+    throw error;
+  }
+};
+
+/**
+ * Get database statistics
+ */
+export const getKuzuStats = async (): Promise<{ nodes: number; edges: number }> => {
+  if (!conn) {
+    return { nodes: 0, edges: 0 };
+  }
+
+  try {
+    // Count nodes across all tables
+    let totalNodes = 0;
+    for (const tableName of NODE_TABLES) {
+      try {
+        const nodeResult = await conn.query(`MATCH (n:${tableName}) RETURN count(n) AS cnt`);
+        const nodeRow = await nodeResult.getNext();
+        totalNodes += Number(nodeRow?.cnt ?? nodeRow?.[0] ?? 0);
+      } catch {
+        // Table might not exist or be empty
+      }
+    }
+    
+    // Count edges from single relation table
+    let totalEdges = 0;
+    try {
+      const edgeResult = await conn.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`);
+      const edgeRow = await edgeResult.getNext();
+      totalEdges = Number(edgeRow?.cnt ?? edgeRow?.[0] ?? 0);
+    } catch {
+      // Table might not exist or be empty
+    }
+    
+    return { nodes: totalNodes, edges: totalEdges };
+  } catch (error) {
+    if (import.meta.env.DEV) {
+      console.warn('Failed to get Kuzu stats:', error);
+    }
+    return { nodes: 0, edges: 0 };
+  }
+};
+
+/**
+ * Check if KuzuDB is initialized and has data
+ */
+export const isKuzuReady = (): boolean => {
+  return conn !== null && db !== null;
+};
+
+/**
+ * Close the database connection (cleanup)
+ */
+export const closeKuzu = async (): Promise => {
+  if (conn) {
+    try {
+      await conn.close();
+    } catch {}
+    conn = null;
+  }
+  if (db) {
+    try {
+      await db.close();
+    } catch {}
+    db = null;
+  }
+  kuzu = null;
+};
+
+/**
+ * Execute a prepared statement with parameters
+ * @param cypher - Cypher query with $param placeholders
+ * @param params - Object mapping param names to values
+ * @returns Query results
+ */
+export const executePrepared = async (
+  cypher: string,
+  params: Record
+): Promise => {
+  if (!conn) {
+    await initKuzu();
+  }
+  
+  try {
+    const stmt = await conn.prepare(cypher);
+    if (!stmt.isSuccess()) {
+      const errMsg = await stmt.getErrorMessage();
+      throw new Error(`Prepare failed: ${errMsg}`);
+    }
+    
+    const result = await conn.execute(stmt, params);
+    
+    const rows: any[] = [];
+    while (await result.hasNext()) {
+      const row = await result.getNext();
+      rows.push(row);
+    }
+    
+    await stmt.close();
+    return rows;
+  } catch (error) {
+    if (import.meta.env.DEV) console.error('Prepared query failed:', error);
+    throw error;
+  }
+};
+
+/**
+ * Execute a prepared statement with multiple parameter sets in small sub-batches
+ */
+export const executeWithReusedStatement = async (
+  cypher: string,
+  paramsList: Array>
+): Promise => {
+  if (!conn) {
+    await initKuzu();
+  }
+  
+  if (paramsList.length === 0) return;
+  
+  const SUB_BATCH_SIZE = 4;
+  
+  for (let i = 0; i < paramsList.length; i += SUB_BATCH_SIZE) {
+    const subBatch = paramsList.slice(i, i + SUB_BATCH_SIZE);
+    
+    const stmt = await conn.prepare(cypher);
+    if (!stmt.isSuccess()) {
+      const errMsg = await stmt.getErrorMessage();
+      throw new Error(`Prepare failed: ${errMsg}`);
+    }
+    
+    try {
+      for (const params of subBatch) {
+        await conn.execute(stmt, params);
+      }
+    } finally {
+      await stmt.close();
+    }
+    
+    if (i + SUB_BATCH_SIZE < paramsList.length) {
+      await new Promise(r => setTimeout(r, 0));
+    }
+  }
+};
+
+/**
+ * Test if array parameters work with prepared statements
+ */
+export const testArrayParams = async (): Promise<{ success: boolean; error?: string }> => {
+  if (!conn) {
+    await initKuzu();
+  }
+  
+  try {
+    const testEmbedding = new Array(384).fill(0).map((_, i) => i / 384);
+    
+    // Get any node ID to test with (try File first, then others)
+    let testNodeId: string | null = null;
+    for (const tableName of NODE_TABLES) {
+      try {
+        const nodeResult = await conn.query(`MATCH (n:${tableName}) RETURN n.id AS id LIMIT 1`);
+        const nodeRow = await nodeResult.getNext();
+        if (nodeRow) {
+          testNodeId = nodeRow.id ?? nodeRow[0];
+          break;
+        }
+      } catch {}
+    }
+    
+    if (!testNodeId) {
+      return { success: false, error: 'No nodes found to test with' };
+    }
+    
+    if (import.meta.env.DEV) {
+      console.log('🧪 Testing array params with node:', testNodeId);
+    }
+    
+    // First create an embedding entry
+    const createQuery = `CREATE (e:${EMBEDDING_TABLE_NAME} {nodeId: $nodeId, embedding: $embedding})`;
+    const stmt = await conn.prepare(createQuery);
+    
+    if (!stmt.isSuccess()) {
+      const errMsg = await stmt.getErrorMessage();
+      return { success: false, error: `Prepare failed: ${errMsg}` };
+    }
+    
+    await conn.execute(stmt, {
+      nodeId: testNodeId,
+      embedding: testEmbedding,
+    });
+    
+    await stmt.close();
+    
+    // Verify it was stored
+    const verifyResult = await conn.query(
+      `MATCH (e:${EMBEDDING_TABLE_NAME} {nodeId: '${testNodeId}'}) RETURN e.embedding AS emb`
+    );
+    const verifyRow = await verifyResult.getNext();
+    const storedEmb = verifyRow?.emb ?? verifyRow?.[0];
+    
+    if (storedEmb && Array.isArray(storedEmb) && storedEmb.length === 384) {
+      if (import.meta.env.DEV) {
+        console.log('✅ Array params WORK! Stored embedding length:', storedEmb.length);
+      }
+      return { success: true };
+    } else {
+      return { 
+        success: false, 
+        error: `Embedding not stored correctly. Got: ${typeof storedEmb}, length: ${storedEmb?.length}` 
+      };
+    }
+  } catch (error) {
+    const errorMsg = error instanceof Error ? error.message : String(error);
+    if (import.meta.env.DEV) {
+      console.error('❌ Array params test failed:', errorMsg);
+    }
+    return { success: false, error: errorMsg };
+  }
+};
diff --git a/gitnexus-web/src/core/kuzu/schema.ts b/gitnexus-web/src/core/kuzu/schema.ts
new file mode 100644
index 000000000..d74545c06
--- /dev/null
+++ b/gitnexus-web/src/core/kuzu/schema.ts
@@ -0,0 +1,410 @@
+/**
+ * KuzuDB Schema Definitions
+ * 
+ * Hybrid Schema:
+ * - Separate node tables for each code element type (File, Function, Class, etc.)
+ * - Single CodeRelation table with 'type' property for all relationships
+ * 
+ * This allows LLMs to write natural Cypher queries like:
+ *   MATCH (f:Function)-[r:CodeRelation {type: 'CALLS'}]->(g:Function) RETURN f, g
+ */
+
+// ============================================================================
+// NODE TABLE NAMES
+// ============================================================================
+export const NODE_TABLES = [
+  'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process',
+  // Multi-language support
+  'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
+  'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module'
+] as const;
+export type NodeTableName = typeof NODE_TABLES[number];
+
+// ============================================================================
+// RELATION TABLE
+// ============================================================================
+export const REL_TABLE_NAME = 'CodeRelation';
+
+// Valid relation types
+export const REL_TYPES = ['CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', 'MEMBER_OF', 'STEP_IN_PROCESS'] as const;
+export type RelType = typeof REL_TYPES[number];
+
+// ============================================================================
+// EMBEDDING TABLE
+// ============================================================================
+export const EMBEDDING_TABLE_NAME = 'CodeEmbedding';
+
+// ============================================================================
+// NODE TABLE SCHEMAS
+// ============================================================================
+
+export const FILE_SCHEMA = `
+CREATE NODE TABLE File (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const FOLDER_SCHEMA = `
+CREATE NODE TABLE Folder (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const FUNCTION_SCHEMA = `
+CREATE NODE TABLE Function (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  isExported BOOLEAN,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const CLASS_SCHEMA = `
+CREATE NODE TABLE Class (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  isExported BOOLEAN,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const INTERFACE_SCHEMA = `
+CREATE NODE TABLE Interface (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  isExported BOOLEAN,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const METHOD_SCHEMA = `
+CREATE NODE TABLE Method (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  isExported BOOLEAN,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const CODE_ELEMENT_SCHEMA = `
+CREATE NODE TABLE CodeElement (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  isExported BOOLEAN,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+// ============================================================================
+// COMMUNITY NODE TABLE (for Leiden algorithm clusters)
+// ============================================================================
+
+export const COMMUNITY_SCHEMA = `
+CREATE NODE TABLE Community (
+  id STRING,
+  label STRING,
+  heuristicLabel STRING,
+  keywords STRING[],
+  description STRING,
+  enrichedBy STRING,
+  cohesion DOUBLE,
+  symbolCount INT32,
+  PRIMARY KEY (id)
+)`;
+
+// ============================================================================
+// PROCESS NODE TABLE (for execution flow detection)
+// ============================================================================
+
+export const PROCESS_SCHEMA = `
+CREATE NODE TABLE Process (
+  id STRING,
+  label STRING,
+  heuristicLabel STRING,
+  processType STRING,
+  stepCount INT32,
+  communities STRING[],
+  entryPointId STRING,
+  terminalId STRING,
+  PRIMARY KEY (id)
+)`;
+
+// ============================================================================
+// MULTI-LANGUAGE NODE TABLE SCHEMAS
+// ============================================================================
+
+// Generic code element with startLine/endLine for C, C++, Rust, Go, Java, C#
+const CODE_ELEMENT_BASE = (name: string) => `
+CREATE NODE TABLE \`${name}\` (
+  id STRING,
+  name STRING,
+  filePath STRING,
+  startLine INT64,
+  endLine INT64,
+  content STRING,
+  PRIMARY KEY (id)
+)`;
+
+export const STRUCT_SCHEMA = CODE_ELEMENT_BASE('Struct');
+export const ENUM_SCHEMA = CODE_ELEMENT_BASE('Enum');
+export const MACRO_SCHEMA = CODE_ELEMENT_BASE('Macro');
+export const TYPEDEF_SCHEMA = CODE_ELEMENT_BASE('Typedef');
+export const UNION_SCHEMA = CODE_ELEMENT_BASE('Union');
+export const NAMESPACE_SCHEMA = CODE_ELEMENT_BASE('Namespace');
+export const TRAIT_SCHEMA = CODE_ELEMENT_BASE('Trait');
+export const IMPL_SCHEMA = CODE_ELEMENT_BASE('Impl');
+export const TYPE_ALIAS_SCHEMA = CODE_ELEMENT_BASE('TypeAlias');
+export const CONST_SCHEMA = CODE_ELEMENT_BASE('Const');
+export const STATIC_SCHEMA = CODE_ELEMENT_BASE('Static');
+export const PROPERTY_SCHEMA = CODE_ELEMENT_BASE('Property');
+export const RECORD_SCHEMA = CODE_ELEMENT_BASE('Record');
+export const DELEGATE_SCHEMA = CODE_ELEMENT_BASE('Delegate');
+export const ANNOTATION_SCHEMA = CODE_ELEMENT_BASE('Annotation');
+export const CONSTRUCTOR_SCHEMA = CODE_ELEMENT_BASE('Constructor');
+export const TEMPLATE_SCHEMA = CODE_ELEMENT_BASE('Template');
+export const MODULE_SCHEMA = CODE_ELEMENT_BASE('Module');
+
+// ============================================================================
+// RELATION TABLE SCHEMA
+// Single table with 'type' property - connects all node tables
+// ============================================================================
+
+export const RELATION_SCHEMA = `
+CREATE REL TABLE ${REL_TABLE_NAME} (
+  FROM File TO File,
+  FROM File TO Folder,
+  FROM File TO Function,
+  FROM File TO Class,
+  FROM File TO Interface,
+  FROM File TO Method,
+  FROM File TO CodeElement,
+  FROM File TO \`Struct\`,
+  FROM File TO \`Enum\`,
+  FROM File TO \`Macro\`,
+  FROM File TO \`Typedef\`,
+  FROM File TO \`Union\`,
+  FROM File TO \`Namespace\`,
+  FROM File TO \`Trait\`,
+  FROM File TO \`Impl\`,
+  FROM File TO \`TypeAlias\`,
+  FROM File TO \`Const\`,
+  FROM File TO \`Static\`,
+  FROM File TO \`Property\`,
+  FROM File TO \`Record\`,
+  FROM File TO \`Delegate\`,
+  FROM File TO \`Annotation\`,
+  FROM File TO \`Constructor\`,
+  FROM File TO \`Template\`,
+  FROM File TO \`Module\`,
+  FROM Folder TO Folder,
+  FROM Folder TO File,
+  FROM Function TO Function,
+  FROM Function TO Method,
+  FROM Function TO Class,
+  FROM Function TO Community,
+  FROM Function TO \`Macro\`,
+  FROM Function TO \`Struct\`,
+  FROM Function TO \`Template\`,
+  FROM Function TO \`Enum\`,
+  FROM Function TO \`Namespace\`,
+  FROM Function TO \`TypeAlias\`,
+  FROM Function TO \`Module\`,
+  FROM Function TO \`Impl\`,
+  FROM Function TO Interface,
+  FROM Function TO \`Constructor\`,
+  FROM Class TO Method,
+  FROM Class TO Function,
+  FROM Class TO Class,
+  FROM Class TO Interface,
+  FROM Class TO Community,
+  FROM Class TO \`Template\`,
+  FROM Class TO \`TypeAlias\`,
+  FROM Class TO \`Struct\`,
+  FROM Class TO \`Enum\`,
+  FROM Class TO \`Constructor\`,
+  FROM Method TO Function,
+  FROM Method TO Method,
+  FROM Method TO Class,
+  FROM Method TO Community,
+  FROM Method TO \`Template\`,
+  FROM Method TO \`Struct\`,
+  FROM Method TO \`TypeAlias\`,
+  FROM Method TO \`Enum\`,
+  FROM Method TO \`Macro\`,
+  FROM Method TO \`Namespace\`,
+  FROM Method TO \`Module\`,
+  FROM Method TO \`Impl\`,
+  FROM Method TO Interface,
+  FROM Method TO \`Constructor\`,
+  FROM \`Template\` TO \`Template\`,
+  FROM \`Template\` TO Function,
+  FROM \`Template\` TO Method,
+  FROM \`Template\` TO Class,
+  FROM \`Template\` TO \`Struct\`,
+  FROM \`Template\` TO \`TypeAlias\`,
+  FROM \`Template\` TO \`Enum\`,
+  FROM \`Template\` TO \`Macro\`,
+  FROM \`Template\` TO Interface,
+  FROM \`Template\` TO \`Constructor\`,
+  FROM \`Module\` TO \`Module\`,
+  FROM CodeElement TO Community,
+  FROM Interface TO Community,
+  FROM Interface TO Function,
+  FROM Interface TO Method,
+  FROM Interface TO Class,
+  FROM Interface TO Interface,
+  FROM Interface TO \`TypeAlias\`,
+  FROM Interface TO \`Struct\`,
+  FROM Interface TO \`Constructor\`,
+  FROM \`Struct\` TO Community,
+  FROM \`Struct\` TO \`Trait\`,
+  FROM \`Struct\` TO Function,
+  FROM \`Struct\` TO Method,
+  FROM \`Enum\` TO Community,
+  FROM \`Macro\` TO Community,
+  FROM \`Macro\` TO Function,
+  FROM \`Macro\` TO Method,
+  FROM \`Module\` TO Function,
+  FROM \`Module\` TO Method,
+  FROM \`Typedef\` TO Community,
+  FROM \`Union\` TO Community,
+  FROM \`Namespace\` TO Community,
+  FROM \`Trait\` TO Community,
+  FROM \`Impl\` TO Community,
+  FROM \`Impl\` TO \`Trait\`,
+  FROM \`TypeAlias\` TO Community,
+  FROM \`Const\` TO Community,
+  FROM \`Static\` TO Community,
+  FROM \`Property\` TO Community,
+  FROM \`Record\` TO Community,
+  FROM \`Delegate\` TO Community,
+  FROM \`Annotation\` TO Community,
+  FROM \`Constructor\` TO Community,
+  FROM \`Constructor\` TO Interface,
+  FROM \`Constructor\` TO Class,
+  FROM \`Constructor\` TO Method,
+  FROM \`Constructor\` TO Function,
+  FROM \`Constructor\` TO \`Constructor\`,
+  FROM \`Constructor\` TO \`Struct\`,
+  FROM \`Constructor\` TO \`Macro\`,
+  FROM \`Constructor\` TO \`Template\`,
+  FROM \`Constructor\` TO \`TypeAlias\`,
+  FROM \`Constructor\` TO \`Enum\`,
+  FROM \`Constructor\` TO \`Impl\`,
+  FROM \`Constructor\` TO \`Namespace\`,
+  FROM \`Template\` TO Community,
+  FROM \`Module\` TO Community,
+  FROM Function TO Process,
+  FROM Method TO Process,
+  FROM Class TO Process,
+  FROM Interface TO Process,
+  FROM \`Struct\` TO Process,
+  FROM \`Constructor\` TO Process,
+  FROM \`Module\` TO Process,
+  FROM \`Macro\` TO Process,
+  FROM \`Impl\` TO Process,
+  FROM \`Typedef\` TO Process,
+  FROM \`TypeAlias\` TO Process,
+  FROM \`Enum\` TO Process,
+  FROM \`Union\` TO Process,
+  FROM \`Namespace\` TO Process,
+  FROM \`Trait\` TO Process,
+  FROM \`Const\` TO Process,
+  FROM \`Static\` TO Process,
+  FROM \`Property\` TO Process,
+  FROM \`Record\` TO Process,
+  FROM \`Delegate\` TO Process,
+  FROM \`Annotation\` TO Process,
+  FROM \`Template\` TO Process,
+  FROM CodeElement TO Process,
+  type STRING,
+  confidence DOUBLE,
+  reason STRING,
+  step INT32
+)`;
+
+// ============================================================================
+// EMBEDDING TABLE SCHEMA
+// Separate table for vector storage to avoid copy-on-write overhead
+// ============================================================================
+
+export const EMBEDDING_SCHEMA = `
+CREATE NODE TABLE ${EMBEDDING_TABLE_NAME} (
+  nodeId STRING,
+  embedding FLOAT[384],
+  PRIMARY KEY (nodeId)
+)`;
+
+/**
+ * Create vector index for semantic search
+ * Uses HNSW (Hierarchical Navigable Small World) algorithm with cosine similarity
+ */
+export const CREATE_VECTOR_INDEX_QUERY = `
+CALL CREATE_VECTOR_INDEX('${EMBEDDING_TABLE_NAME}', 'code_embedding_idx', 'embedding', metric := 'cosine')
+`;
+
+// ============================================================================
+// ALL SCHEMA QUERIES IN ORDER
+// Node tables must be created before relationship tables that reference them
+// ============================================================================
+
+export const NODE_SCHEMA_QUERIES = [
+  FILE_SCHEMA,
+  FOLDER_SCHEMA,
+  FUNCTION_SCHEMA,
+  CLASS_SCHEMA,
+  INTERFACE_SCHEMA,
+  METHOD_SCHEMA,
+  CODE_ELEMENT_SCHEMA,
+  COMMUNITY_SCHEMA,
+  PROCESS_SCHEMA,
+  // Multi-language support
+  STRUCT_SCHEMA,
+  ENUM_SCHEMA,
+  MACRO_SCHEMA,
+  TYPEDEF_SCHEMA,
+  UNION_SCHEMA,
+  NAMESPACE_SCHEMA,
+  TRAIT_SCHEMA,
+  IMPL_SCHEMA,
+  TYPE_ALIAS_SCHEMA,
+  CONST_SCHEMA,
+  STATIC_SCHEMA,
+  PROPERTY_SCHEMA,
+  RECORD_SCHEMA,
+  DELEGATE_SCHEMA,
+  ANNOTATION_SCHEMA,
+  CONSTRUCTOR_SCHEMA,
+  TEMPLATE_SCHEMA,
+  MODULE_SCHEMA,
+];
+
+export const REL_SCHEMA_QUERIES = [
+  RELATION_SCHEMA,
+];
+
+export const SCHEMA_QUERIES = [
+  ...NODE_SCHEMA_QUERIES,
+  ...REL_SCHEMA_QUERIES,
+  EMBEDDING_SCHEMA,
+];
diff --git a/gitnexus/src/core/llm/agent.ts b/gitnexus-web/src/core/llm/agent.ts
similarity index 98%
rename from gitnexus/src/core/llm/agent.ts
rename to gitnexus-web/src/core/llm/agent.ts
index cc0c752db..6649f5742 100644
--- a/gitnexus/src/core/llm/agent.ts
+++ b/gitnexus-web/src/core/llm/agent.ts
@@ -128,14 +128,20 @@ export const createChatModel = (config: ProviderConfig): BaseChatModel => {
   switch (config.provider) {
     case 'openai': {
       const openaiConfig = config as OpenAIConfig;
+      
+      if (!openaiConfig.apiKey || openaiConfig.apiKey.trim() === '') {
+        throw new Error('OpenAI API key is required but was not provided');
+      }
+      
       return new ChatOpenAI({
-        openAIApiKey: openaiConfig.apiKey,
+        apiKey: openaiConfig.apiKey,
         modelName: openaiConfig.model,
         temperature: openaiConfig.temperature ?? 0.1,
         maxTokens: openaiConfig.maxTokens,
-        configuration: openaiConfig.baseUrl ? {
-          baseURL: openaiConfig.baseUrl,
-        } : undefined,
+        configuration: {
+          apiKey: openaiConfig.apiKey,
+          ...(openaiConfig.baseUrl ? { baseURL: openaiConfig.baseUrl } : {}),
+        },
         streaming: true,
       });
     }
diff --git a/gitnexus/src/core/llm/context-builder.ts b/gitnexus-web/src/core/llm/context-builder.ts
similarity index 100%
rename from gitnexus/src/core/llm/context-builder.ts
rename to gitnexus-web/src/core/llm/context-builder.ts
diff --git a/gitnexus/src/core/llm/index.ts b/gitnexus-web/src/core/llm/index.ts
similarity index 100%
rename from gitnexus/src/core/llm/index.ts
rename to gitnexus-web/src/core/llm/index.ts
diff --git a/gitnexus/src/core/llm/settings-service.ts b/gitnexus-web/src/core/llm/settings-service.ts
similarity index 100%
rename from gitnexus/src/core/llm/settings-service.ts
rename to gitnexus-web/src/core/llm/settings-service.ts
diff --git a/gitnexus/src/core/llm/tools.ts b/gitnexus-web/src/core/llm/tools.ts
similarity index 100%
rename from gitnexus/src/core/llm/tools.ts
rename to gitnexus-web/src/core/llm/tools.ts
diff --git a/gitnexus/src/core/llm/types.ts b/gitnexus-web/src/core/llm/types.ts
similarity index 100%
rename from gitnexus/src/core/llm/types.ts
rename to gitnexus-web/src/core/llm/types.ts
diff --git a/gitnexus-web/src/core/search/bm25-index.ts b/gitnexus-web/src/core/search/bm25-index.ts
new file mode 100644
index 000000000..4b745bd72
--- /dev/null
+++ b/gitnexus-web/src/core/search/bm25-index.ts
@@ -0,0 +1,161 @@
+/**
+ * BM25 Full-Text Search Index
+ * 
+ * Uses MiniSearch for fast keyword-based search with BM25 ranking.
+ * Complements semantic search - BM25 finds exact terms, semantic finds concepts.
+ */
+
+import MiniSearch from 'minisearch';
+
+export interface BM25Document {
+  id: string;       // File path
+  content: string;  // File content
+  name: string;     // File name (boosted in search)
+}
+
+export interface BM25SearchResult {
+  filePath: string;
+  score: number;
+  rank: number;
+}
+
+/**
+ * BM25 Index singleton
+ * Stores the MiniSearch instance and provides search methods
+ */
+let searchIndex: MiniSearch | null = null;
+let indexedDocCount = 0;
+
+/**
+ * Build the BM25 index from file contents
+ * Should be called after ingestion completes
+ * 
+ * @param fileContents - Map of file path to content
+ * @returns Number of documents indexed
+ */
+export const buildBM25Index = (fileContents: Map): number => {
+  // Create new MiniSearch instance with BM25-like scoring
+  searchIndex = new MiniSearch({
+    fields: ['content', 'name'], // Fields to index
+    storeFields: ['id'],         // Fields to return in results
+    
+    // Tokenizer: split on non-alphanumeric, camelCase, snake_case
+    tokenize: (text: string) => {
+      // Split on whitespace and punctuation
+      const tokens = text.toLowerCase().split(/[\s\-_./\\(){}[\]<>:;,!?'"]+/);
+      
+      // Also split camelCase: "getUserById" -> ["get", "user", "by", "id"]
+      const expanded: string[] = [];
+      for (const token of tokens) {
+        if (token.length === 0) continue;
+        
+        // Split camelCase
+        const camelParts = token.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase().split(' ');
+        expanded.push(...camelParts);
+        
+        // Also keep original token for exact matches
+        if (camelParts.length > 1) {
+          expanded.push(token);
+        }
+      }
+      
+      // Filter out very short tokens and common noise
+      return expanded.filter(t => t.length > 1 && !STOP_WORDS.has(t));
+    },
+  });
+  
+  // Index all files
+  const documents: BM25Document[] = [];
+  
+  for (const [filePath, content] of fileContents.entries()) {
+    // Extract filename from path
+    const name = filePath.split('/').pop() || filePath;
+    
+    documents.push({
+      id: filePath,
+      content: content,
+      name: name,
+    });
+  }
+  
+  // Batch add for efficiency
+  searchIndex.addAll(documents);
+  indexedDocCount = documents.length;
+  
+  if (import.meta.env.DEV) {
+    console.log(`📚 BM25 index built: ${indexedDocCount} documents`);
+  }
+  
+  return indexedDocCount;
+};
+
+/**
+ * Search the BM25 index
+ * 
+ * @param query - Search query (keywords)
+ * @param limit - Maximum results to return
+ * @returns Ranked search results with file paths and scores
+ */
+export const searchBM25 = (query: string, limit: number = 20): BM25SearchResult[] => {
+  if (!searchIndex) {
+    return [];
+  }
+  
+  // Search with fuzzy matching and prefix support
+  const results = searchIndex.search(query, {
+    fuzzy: 0.2,
+    prefix: true,
+    boost: { name: 2 },  // Boost file name matches
+  });
+  
+  // Limit results and add rank
+  return results.slice(0, limit).map((r, index) => ({
+    filePath: r.id,
+    score: r.score,
+    rank: index + 1,
+  }));
+};
+
+/**
+ * Check if the BM25 index is ready
+ */
+export const isBM25Ready = (): boolean => {
+  return searchIndex !== null && indexedDocCount > 0;
+};
+
+/**
+ * Get index statistics
+ */
+export const getBM25Stats = (): { documentCount: number; termCount: number } => {
+  if (!searchIndex) {
+    return { documentCount: 0, termCount: 0 };
+  }
+  
+  return {
+    documentCount: indexedDocCount,
+    termCount: searchIndex.termCount,
+  };
+};
+
+/**
+ * Clear the index (for cleanup or re-indexing)
+ */
+export const clearBM25Index = (): void => {
+  searchIndex = null;
+  indexedDocCount = 0;
+};
+
+/**
+ * Common stop words to filter out (too common to be useful)
+ */
+const STOP_WORDS = new Set([
+  // JavaScript/TypeScript keywords
+  'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while',
+  'class', 'new', 'this', 'import', 'export', 'from', 'default', 'async', 'await',
+  'try', 'catch', 'throw', 'typeof', 'instanceof', 'true', 'false', 'null', 'undefined',
+  
+  // Common English stop words
+  'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'but', 'in', 'with',
+  'to', 'of', 'it', 'be', 'as', 'by', 'that', 'for', 'are', 'was', 'were',
+]);
+
diff --git a/gitnexus-web/src/core/search/hybrid-search.ts b/gitnexus-web/src/core/search/hybrid-search.ts
new file mode 100644
index 000000000..247bb2783
--- /dev/null
+++ b/gitnexus-web/src/core/search/hybrid-search.ts
@@ -0,0 +1,149 @@
+/**
+ * Hybrid Search with Reciprocal Rank Fusion (RRF)
+ * 
+ * Combines BM25 (keyword) and semantic (embedding) search results.
+ * Uses RRF to merge rankings without needing score normalization.
+ * 
+ * This is the same approach used by Elasticsearch, Pinecone, and other
+ * production search systems.
+ */
+
+import { searchBM25, isBM25Ready, type BM25SearchResult } from './bm25-index';
+import type { SemanticSearchResult } from '../embeddings/types';
+
+/**
+ * RRF constant - standard value used in the literature
+ * Higher values give more weight to lower-ranked results
+ */
+const RRF_K = 60;
+
+export interface HybridSearchResult {
+  filePath: string;
+  score: number;           // RRF score
+  rank: number;            // Final rank
+  sources: ('bm25' | 'semantic')[];  // Which methods found this
+  
+  // Metadata from semantic search (if available)
+  nodeId?: string;
+  name?: string;
+  label?: string;
+  startLine?: number;
+  endLine?: number;
+  
+  // Original scores for debugging
+  bm25Score?: number;
+  semanticScore?: number;
+}
+
+/**
+ * Perform hybrid search combining BM25 and semantic results
+ * 
+ * @param bm25Results - Results from BM25 keyword search
+ * @param semanticResults - Results from semantic/embedding search
+ * @param limit - Maximum results to return
+ * @returns Merged and re-ranked results
+ */
+export const mergeWithRRF = (
+  bm25Results: BM25SearchResult[],
+  semanticResults: SemanticSearchResult[],
+  limit: number = 10
+): HybridSearchResult[] => {
+  const merged = new Map();
+  
+  // Process BM25 results
+  for (let i = 0; i < bm25Results.length; i++) {
+    const r = bm25Results[i];
+    const rrfScore = 1 / (RRF_K + i + 1);  // i+1 because rank starts at 1
+    
+    merged.set(r.filePath, {
+      filePath: r.filePath,
+      score: rrfScore,
+      rank: 0,  // Will be set after sorting
+      sources: ['bm25'],
+      bm25Score: r.score,
+    });
+  }
+  
+  // Process semantic results and merge
+  for (let i = 0; i < semanticResults.length; i++) {
+    const r = semanticResults[i];
+    const rrfScore = 1 / (RRF_K + i + 1);
+    
+    const existing = merged.get(r.filePath);
+    if (existing) {
+      // Found by both methods - add scores
+      existing.score += rrfScore;
+      existing.sources.push('semantic');
+      existing.semanticScore = 1 - r.distance;
+      
+      // Add semantic metadata
+      existing.nodeId = r.nodeId;
+      existing.name = r.name;
+      existing.label = r.label;
+      existing.startLine = r.startLine;
+      existing.endLine = r.endLine;
+    } else {
+      // Only found by semantic
+      merged.set(r.filePath, {
+        filePath: r.filePath,
+        score: rrfScore,
+        rank: 0,
+        sources: ['semantic'],
+        semanticScore: 1 - r.distance,
+        nodeId: r.nodeId,
+        name: r.name,
+        label: r.label,
+        startLine: r.startLine,
+        endLine: r.endLine,
+      });
+    }
+  }
+  
+  // Sort by RRF score descending
+  const sorted = Array.from(merged.values())
+    .sort((a, b) => b.score - a.score)
+    .slice(0, limit);
+  
+  // Assign final ranks
+  sorted.forEach((r, i) => {
+    r.rank = i + 1;
+  });
+  
+  return sorted;
+};
+
+/**
+ * Check if hybrid search is available
+ * Requires BM25 index to be built
+ * Note: Semantic search is optional - hybrid works with just BM25 if embeddings aren't ready
+ */
+export const isHybridSearchReady = (): boolean => {
+  return isBM25Ready();
+};
+
+/**
+ * Format hybrid results for LLM consumption
+ */
+export const formatHybridResults = (results: HybridSearchResult[]): string => {
+  if (results.length === 0) {
+    return 'No results found.';
+  }
+  
+  const formatted = results.map((r, i) => {
+    const sources = r.sources.join(' + ');
+    const location = r.startLine ? ` (lines ${r.startLine}-${r.endLine})` : '';
+    const label = r.label ? `${r.label}: ` : 'File: ';
+    const name = r.name || r.filePath.split('/').pop() || r.filePath;
+    
+    return `[${i + 1}] ${label}${name}
+    File: ${r.filePath}${location}
+    Found by: ${sources}
+    Relevance: ${r.score.toFixed(4)}`;
+  });
+  
+  return `Found ${results.length} results:\n\n${formatted.join('\n\n')}`;
+};
+
+
+
+
diff --git a/gitnexus/src/core/search/index.ts b/gitnexus-web/src/core/search/index.ts
similarity index 100%
rename from gitnexus/src/core/search/index.ts
rename to gitnexus-web/src/core/search/index.ts
diff --git a/gitnexus-web/src/core/tree-sitter/parser-loader.ts b/gitnexus-web/src/core/tree-sitter/parser-loader.ts
new file mode 100644
index 000000000..d5224ca4e
--- /dev/null
+++ b/gitnexus-web/src/core/tree-sitter/parser-loader.ts
@@ -0,0 +1,72 @@
+import Parser from 'web-tree-sitter';
+import { SupportedLanguages } from '../../config/supported-languages';
+
+let parser: Parser | null = null;
+
+// Cache the compiled Language objects to avoid fetching/compiling twice
+const languageCache = new Map();
+
+export const loadParser = async (): Promise => {
+    if (parser) return parser;
+
+    await Parser.init({
+        locateFile: (scriptName: string) => {
+            return `/wasm/${scriptName}`;
+        }
+    })
+
+    parser = new Parser();
+    return parser;
+}
+
+// Get the appropriate WASM file based on language and file extension
+const getWasmPath = (language: SupportedLanguages, filePath?: string): string => {
+    // For TypeScript, check if it's a TSX file
+    if (language === SupportedLanguages.TypeScript) {
+        if (filePath?.endsWith('.tsx')) {
+            return '/wasm/typescript/tree-sitter-tsx.wasm';
+        }
+        return '/wasm/typescript/tree-sitter-typescript.wasm';
+    }
+    
+    const languageFileMap: Record = {
+        [SupportedLanguages.JavaScript]: '/wasm/javascript/tree-sitter-javascript.wasm',
+        [SupportedLanguages.TypeScript]: '/wasm/typescript/tree-sitter-typescript.wasm',
+        [SupportedLanguages.Python]: '/wasm/python/tree-sitter-python.wasm',
+        [SupportedLanguages.Java]: '/wasm/java/tree-sitter-java.wasm',
+        [SupportedLanguages.C]: '/wasm/c/tree-sitter-c.wasm',
+        [SupportedLanguages.CPlusPlus]: '/wasm/cpp/tree-sitter-cpp.wasm',
+        [SupportedLanguages.CSharp]: '/wasm/csharp/tree-sitter-csharp.wasm',
+        [SupportedLanguages.Go]: '/wasm/go/tree-sitter-go.wasm',
+        [SupportedLanguages.Rust]: '/wasm/rust/tree-sitter-rust.wasm',
+    };
+    
+    return languageFileMap[language];
+};
+
+export const loadLanguage = async (language: SupportedLanguages, filePath?: string): Promise => {
+    if (!parser) await loadParser();
+    const wasmPath = getWasmPath(language, filePath);
+    
+    if (languageCache.has(wasmPath)) {
+        parser!.setLanguage(languageCache.get(wasmPath)!);
+        return;
+    }
+
+    if (!wasmPath) {
+        console.error(`❌ [Parser] No WASM path configured for language: ${language}`);
+        throw new Error(`Unsupported language: ${language}`);
+    }
+    
+    try {
+        const loadedLanguage = await Parser.Language.load(wasmPath);    
+        languageCache.set(wasmPath, loadedLanguage);
+        parser!.setLanguage(loadedLanguage);
+    } catch (error: unknown) {
+        const errorMessage = error instanceof Error ? error.message : String(error);
+        console.error(`❌ [Parser] Failed to load WASM grammar for ${language}`);
+        console.error(`   WASM Path: ${wasmPath}`);
+        console.error(`   Error: ${errorMessage}`);
+        throw new Error(`Failed to load grammar for ${language}: ${errorMessage}`);
+    }
+}
diff --git a/gitnexus/src/hooks/useAppState.tsx b/gitnexus-web/src/hooks/useAppState.tsx
similarity index 89%
rename from gitnexus/src/hooks/useAppState.tsx
rename to gitnexus-web/src/hooks/useAppState.tsx
index 720b92314..0242ccd92 100644
--- a/gitnexus/src/hooks/useAppState.tsx
+++ b/gitnexus-web/src/hooks/useAppState.tsx
@@ -123,9 +123,6 @@ interface AppState {
 
   // Embedding methods
   startEmbeddings: (forceDevice?: 'webgpu' | 'wasm') => Promise;
-  startBackgroundEnrichment: () => Promise;
-  cancelEnrichment: () => Promise;
-  enrichmentProgress: { current: number; total: number } | null;
   semanticSearch: (query: string, k?: number) => Promise;
   semanticSearchWithContext: (query: string, k?: number, hops?: number) => Promise;
   isEmbeddingReady: boolean;
@@ -149,9 +146,9 @@ interface AppState {
 
   // LLM methods
   refreshLLMSettings: () => void;
-  runClusterEnrichment: () => Promise;
   initializeAgent: (overrideProjectName?: string) => Promise;
   sendChatMessage: (message: string) => Promise;
+  stopChatResponse: () => void;
   clearChat: () => void;
 
   // Code References Panel
@@ -294,10 +291,6 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
   const [isCodePanelOpen, setCodePanelOpen] = useState(false);
   const [codeReferenceFocus, setCodeReferenceFocus] = useState(null);
 
-  // Cluster enrichment state
-  const [enrichmentProgress, setEnrichmentProgress] = useState<{ current: number; total: number } | null>(null);
-  const enrichmentCancelledRef = useRef(false);
-
   const normalizePath = useCallback((p: string) => {
     return p.replace(/\\/g, '/').replace(/^\.?\//, '');
   }, []);
@@ -521,63 +514,6 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
     }
   }, []);
 
-  // Background cluster enrichment
-  const startBackgroundEnrichment = useCallback(async (): Promise => {
-    const api = apiRef.current;
-    if (!api) return;
-
-    enrichmentCancelledRef.current = false;
-
-    try {
-      const result = await api.startBackgroundEnrichment(
-        Comlink.proxy((current: number, total: number) => {
-          setEnrichmentProgress({ current, total });
-          setProgress({
-            phase: 'complete',
-            percent: 100,
-            message: `Labeling clusters ${current}/${total}...`,
-          });
-        })
-      );
-
-      setEnrichmentProgress(null);
-
-      if (!result.skipped && result.enriched > 0) {
-        setProgress({
-          phase: 'complete',
-          percent: 100,
-          message: 'Smart cluster labels generated!',
-        });
-        // Clear after 3 seconds
-        setTimeout(() => setProgress(null), 3000);
-      }
-    } catch (err) {
-      console.warn('Background enrichment failed:', err);
-      setEnrichmentProgress(null);
-    }
-  }, []);
-
-  // Cancel/pause enrichment
-  const cancelEnrichment = useCallback(async (): Promise => {
-    const api = apiRef.current;
-    if (!api) return;
-
-    enrichmentCancelledRef.current = true;
-    setEnrichmentProgress(null);
-
-    try {
-      await api.cancelEnrichment();
-      setProgress({
-        phase: 'complete',
-        percent: 100,
-        message: 'LLM labeling stopped. Using heuristic labels.',
-      });
-      setTimeout(() => setProgress(null), 3000);
-    } catch (err) {
-      console.warn('Cancel enrichment failed:', err);
-    }
-  }, []);
-
   const semanticSearch = useCallback(async (
     query: string,
     k: number = 10
@@ -612,93 +548,6 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
     });
   }, []);
 
-  const runClusterEnrichment = useCallback(async () => {
-    const api = apiRef.current;
-    if (!api) {
-      setAgentError('Worker not initialized');
-      return;
-    }
-
-
-    const defaultConfig = getActiveProviderConfig();
-    const configToUse = (!llmSettings.useSameModelForClustering && llmSettings.clusteringProvider?.provider)
-      ? {
-        ...defaultConfig,
-        provider: llmSettings.clusteringProvider.provider || defaultConfig!.provider,
-        model: llmSettings.clusteringProvider.model || defaultConfig!.model,
-        apiKey: (llmSettings.clusteringProvider as any).apiKey || (defaultConfig as any).apiKey
-      } as ProviderConfig
-      : defaultConfig;
-
-    if (!configToUse) {
-      // No provider configured - open settings panel
-      setSettingsPanelOpen(true);
-      setAgentError('Please configure an LLM provider in Settings first.');
-      return;
-    }
-
-
-    try {
-      setProgress({
-        phase: 'enriching',
-        percent: 1,
-        message: 'Starting AI enrichment...',
-        stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: 0 }
-      });
-
-      const { enrichments } = await api.enrichCommunities(
-        configToUse,
-        Comlink.proxy((current, total) => {
-          setProgress(prev => prev ? ({
-            ...prev,
-            percent: Math.min(99, 5 + Math.round((current / total) * 90)),
-            message: `Enriching clusters ${current}/${total}`
-          }) : null);
-        })
-      );
-
-      // Update local graph
-      setGraph(prevGraph => {
-        if (!prevGraph) return null;
-
-        const newNodes = prevGraph.nodes.map(n => {
-          if (n.label === 'Community' && enrichments[n.id]) {
-            const e = enrichments[n.id];
-            return {
-              ...n,
-              properties: {
-                ...n.properties,
-                name: e.name,
-                keywords: e.keywords,
-                description: e.description,
-                enrichedBy: 'llm' as const
-              }
-            };
-          }
-          return n;
-        });
-        return { ...prevGraph, nodes: newNodes };
-      });
-
-      setProgress({
-        phase: 'complete',
-        percent: 100,
-        message: '✨ Smart labels generated!',
-        stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: 0 }
-      });
-
-
-      // Clear progress after 3 seconds
-      setTimeout(() => setProgress(null), 3000);
-
-    } catch (err) {
-      console.error(err);
-      const errorMsg = err instanceof Error ? err.message : String(err);
-      setAgentError('Clustering enrichment failed: ' + errorMsg);
-      setProgress(null);
-    }
-  }, [llmSettings]);
-
   const refreshLLMSettings = useCallback(() => {
     setLLMSettings(loadSettings());
   }, []);
@@ -1095,6 +944,15 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
     }
   }, [chatMessages, isAgentReady, initializeAgent, resolveFilePath, findFileNodeId, addCodeReference, clearAICodeReferences, clearAIToolHighlights, graph, embeddingStatus]);
 
+  const stopChatResponse = useCallback(() => {
+    const api = apiRef.current;
+    if (api && isChatLoading) {
+      api.stopChat();
+      setIsChatLoading(false);
+      setCurrentToolCalls([]);
+    }
+  }, [isChatLoading]);
+
   const clearChat = useCallback(() => {
     setChatMessages([]);
     setCurrentToolCalls([]);
@@ -1202,9 +1060,6 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
     embeddingStatus,
     embeddingProgress,
     startEmbeddings,
-    startBackgroundEnrichment,
-    cancelEnrichment,
-    enrichmentProgress,
     semanticSearch,
     semanticSearchWithContext,
     isEmbeddingReady: embeddingStatus === 'ready',
@@ -1224,9 +1079,9 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
     currentToolCalls,
     // LLM methods
     refreshLLMSettings,
-    runClusterEnrichment,
     initializeAgent,
     sendChatMessage,
+    stopChatResponse,
     clearChat,
     // Code References Panel
     codeReferences,
diff --git a/gitnexus/src/hooks/useSettings.ts b/gitnexus-web/src/hooks/useSettings.ts
similarity index 100%
rename from gitnexus/src/hooks/useSettings.ts
rename to gitnexus-web/src/hooks/useSettings.ts
diff --git a/gitnexus/src/hooks/useSigma.ts b/gitnexus-web/src/hooks/useSigma.ts
similarity index 100%
rename from gitnexus/src/hooks/useSigma.ts
rename to gitnexus-web/src/hooks/useSigma.ts
diff --git a/gitnexus/src/index.css b/gitnexus-web/src/index.css
similarity index 100%
rename from gitnexus/src/index.css
rename to gitnexus-web/src/index.css
diff --git a/gitnexus/src/lib/constants.ts b/gitnexus-web/src/lib/constants.ts
similarity index 100%
rename from gitnexus/src/lib/constants.ts
rename to gitnexus-web/src/lib/constants.ts
diff --git a/gitnexus/src/lib/graph-adapter.ts b/gitnexus-web/src/lib/graph-adapter.ts
similarity index 100%
rename from gitnexus/src/lib/graph-adapter.ts
rename to gitnexus-web/src/lib/graph-adapter.ts
diff --git a/gitnexus/src/lib/mermaid-generator.ts b/gitnexus-web/src/lib/mermaid-generator.ts
similarity index 100%
rename from gitnexus/src/lib/mermaid-generator.ts
rename to gitnexus-web/src/lib/mermaid-generator.ts
diff --git a/gitnexus-web/src/lib/utils.ts b/gitnexus-web/src/lib/utils.ts
new file mode 100644
index 000000000..857f9c1a8
--- /dev/null
+++ b/gitnexus-web/src/lib/utils.ts
@@ -0,0 +1,3 @@
+export const generateId = (label: string, name: string): string => {
+  return `${label}:${name}`
+}
\ No newline at end of file
diff --git a/gitnexus/src/main.tsx b/gitnexus-web/src/main.tsx
similarity index 100%
rename from gitnexus/src/main.tsx
rename to gitnexus-web/src/main.tsx
diff --git a/gitnexus/src/services/git-clone.ts b/gitnexus-web/src/services/git-clone.ts
similarity index 100%
rename from gitnexus/src/services/git-clone.ts
rename to gitnexus-web/src/services/git-clone.ts
diff --git a/gitnexus/src/services/zip.ts b/gitnexus-web/src/services/zip.ts
similarity index 100%
rename from gitnexus/src/services/zip.ts
rename to gitnexus-web/src/services/zip.ts
diff --git a/gitnexus/src/types/kuzu-wasm.d.ts b/gitnexus-web/src/types/kuzu-wasm.d.ts
similarity index 100%
rename from gitnexus/src/types/kuzu-wasm.d.ts
rename to gitnexus-web/src/types/kuzu-wasm.d.ts
diff --git a/gitnexus-web/src/types/pipeline.ts b/gitnexus-web/src/types/pipeline.ts
new file mode 100644
index 000000000..123be720b
--- /dev/null
+++ b/gitnexus-web/src/types/pipeline.ts
@@ -0,0 +1,56 @@
+import { GraphNode, GraphRelationship, KnowledgeGraph } from '../core/graph/types';
+import { CommunityDetectionResult } from '../core/ingestion/community-processor';
+import { ProcessDetectionResult } from '../core/ingestion/process-processor';
+
+export type PipelinePhase = 'idle' | 'extracting' | 'structure' | 'parsing' | 'imports' | 'calls' | 'heritage' | 'communities' | 'processes' | 'enriching' | 'complete' | 'error';
+
+export interface PipelineProgress {
+  phase: PipelinePhase;
+  percent: number;
+  message: string;
+  detail?: string;
+  stats?: {
+    filesProcessed: number;
+    totalFiles: number;
+    nodesCreated: number;
+  };
+}
+
+// Original result type (used internally in pipeline)
+export interface PipelineResult {
+  graph: KnowledgeGraph;
+  fileContents: Map;
+  communityResult?: CommunityDetectionResult;
+  processResult?: ProcessDetectionResult;
+}
+
+// Serializable version for Web Worker communication
+// Maps and functions cannot be transferred via postMessage
+export interface SerializablePipelineResult {
+  nodes: GraphNode[];
+  relationships: GraphRelationship[];
+  fileContents: Record; // Object instead of Map
+}
+
+// Helper to convert PipelineResult to serializable format
+export const serializePipelineResult = (result: PipelineResult): SerializablePipelineResult => ({
+  nodes: result.graph.nodes,
+  relationships: result.graph.relationships,
+  fileContents: Object.fromEntries(result.fileContents),
+});
+
+// Helper to reconstruct from serializable format (used in main thread)
+export const deserializePipelineResult = (
+  serialized: SerializablePipelineResult,
+  createGraph: () => KnowledgeGraph
+): PipelineResult => {
+  const graph = createGraph();
+  serialized.nodes.forEach(node => graph.addNode(node));
+  serialized.relationships.forEach(rel => graph.addRelationship(rel));
+  
+  return {
+    graph,
+    fileContents: new Map(Object.entries(serialized.fileContents)),
+  };
+};
+
diff --git a/gitnexus-web/src/vendor/leiden/index.d.ts b/gitnexus-web/src/vendor/leiden/index.d.ts
new file mode 100644
index 000000000..9248e1bf2
--- /dev/null
+++ b/gitnexus-web/src/vendor/leiden/index.d.ts
@@ -0,0 +1,35 @@
+import Graph from 'graphology';
+
+type RNGFunction = () => number;
+
+export type LeidenOptions = {
+  attributes?: {
+    community?: string;
+    weight?: string;
+  };
+  randomWalk?: boolean;
+  resolution?: number;
+  rng?: RNGFunction;
+  weighted?: boolean;
+};
+
+type LeidenMapping = { [key: string]: number };
+
+export type DetailedLeidenOutput = {
+  communities: LeidenMapping;
+  count: number;
+  deltaComputations: number;
+  dendrogram: Array;
+  modularity: number;
+  moves: Array> | Array;
+  nodesVisited: number;
+  resolution: number;
+};
+
+declare const leiden: {
+  (graph: Graph, options?: LeidenOptions): LeidenMapping;
+  assign(graph: Graph, options?: LeidenOptions): void;
+  detailed(graph: Graph, options?: LeidenOptions): DetailedLeidenOutput;
+};
+
+export default leiden;
diff --git a/gitnexus-web/src/vendor/leiden/index.js b/gitnexus-web/src/vendor/leiden/index.js
new file mode 100644
index 000000000..d05d98233
--- /dev/null
+++ b/gitnexus-web/src/vendor/leiden/index.js
@@ -0,0 +1,341 @@
+/**
+ * Graphology Leiden Algorithm
+ * ============================
+ *
+ * JavaScript implementation of the Leiden community detection
+ * algorithm for graphology.
+ *
+ * Vendored from: https://github.com/graphology/graphology/tree/master/src/communities-leiden
+ * License: MIT
+ *
+ * Converted to ESM for Vite compatibility.
+ *
+ * [Reference]
+ * Traag, V. A., et al. "From Louvain to Leiden: Guaranteeing Well-Connected
+ * Communities". Scientific Reports, vol. 9, no 1, 2019, p. 5233.
+ * https://arxiv.org/abs/1810.08473
+ */
+import resolveDefaults from 'graphology-utils/defaults';
+import isGraph from 'graphology-utils/is-graph';
+import inferType from 'graphology-utils/infer-type';
+import SparseMap from 'mnemonist/sparse-map';
+import SparseQueueSet from 'mnemonist/sparse-queue-set';
+import randomIndexModule from 'pandemonium/random-index';
+import { addWeightToCommunity, UndirectedLeidenAddenda } from './utils.js';
+
+import indices from 'graphology-indices/louvain';
+
+var createRandomIndex = randomIndexModule.createRandomIndex || randomIndexModule;
+var UndirectedLouvainIndex = indices.UndirectedLouvainIndex;
+
+var DEFAULTS = {
+ attributes: {
+ community: 'community',
+ weight: 'weight'
+ },
+ randomness: 0.01,
+ randomWalk: true,
+ resolution: 1,
+ rng: Math.random,
+ weighted: false
+};
+
+var EPSILON = 1e-10;
+
+function tieBreaker(
+ bestCommunity,
+ currentCommunity,
+ targetCommunity,
+ delta,
+ bestDelta
+) {
+ if (Math.abs(delta - bestDelta) < EPSILON) {
+ if (bestCommunity === currentCommunity) {
+ return false;
+ } else {
+ return targetCommunity > bestCommunity;
+ }
+ } else if (delta > bestDelta) {
+ return true;
+ }
+
+ return false;
+}
+
+function undirectedLeiden(detailed, graph, options) {
+ var index = new UndirectedLouvainIndex(graph, {
+ attributes: {
+ weight: options.attributes.weight
+ },
+ keepDendrogram: detailed,
+ resolution: options.resolution,
+ weighted: options.weighted
+ });
+
+ var addenda = new UndirectedLeidenAddenda(index, {
+ randomness: options.randomness,
+ rng: options.rng
+ });
+
+ var randomIndex = createRandomIndex(options.rng);
+
+ // Communities
+ var currentCommunity, targetCommunity;
+ var communities = new SparseMap(Float64Array, index.C);
+
+ // Traversal
+ var queue = new SparseQueueSet(index.C),
+ start,
+ end,
+ weight,
+ ci,
+ ri,
+ s,
+ i,
+ j,
+ l;
+
+ // Metrics
+ var degree, targetCommunityDegree;
+
+ // Moves
+ var bestCommunity, bestDelta, deltaIsBetter, delta;
+
+ // Details
+ var deltaComputations = 0,
+ nodesVisited = 0,
+ moves = [],
+ currentMoves;
+
+ while (true) {
+ l = index.C;
+
+ currentMoves = 0;
+
+ // Traversal of the graph
+ ri = options.randomWalk ? randomIndex(l) : 0;
+
+ for (s = 0; s < l; s++, ri++) {
+ i = ri % l;
+ queue.enqueue(i);
+ }
+
+ while (queue.size !== 0) {
+ i = queue.dequeue();
+ nodesVisited++;
+
+ degree = 0;
+ communities.clear();
+
+ currentCommunity = index.belongings[i];
+
+ start = index.starts[i];
+ end = index.starts[i + 1];
+
+ // Traversing neighbors
+ for (; start < end; start++) {
+ j = index.neighborhood[start];
+ weight = index.weights[start];
+
+ targetCommunity = index.belongings[j];
+
+ // Incrementing metrics
+ degree += weight;
+ addWeightToCommunity(communities, targetCommunity, weight);
+ }
+
+ // Finding best community to move to
+ bestDelta = index.fastDeltaWithOwnCommunity(
+ i,
+ degree,
+ communities.get(currentCommunity) || 0,
+ currentCommunity
+ );
+ bestCommunity = currentCommunity;
+
+ for (ci = 0; ci < communities.size; ci++) {
+ targetCommunity = communities.dense[ci];
+
+ if (targetCommunity === currentCommunity) continue;
+
+ targetCommunityDegree = communities.vals[ci];
+
+ deltaComputations++;
+
+ delta = index.fastDelta(
+ i,
+ degree,
+ targetCommunityDegree,
+ targetCommunity
+ );
+
+ deltaIsBetter = tieBreaker(
+ bestCommunity,
+ currentCommunity,
+ targetCommunity,
+ delta,
+ bestDelta
+ );
+
+ if (deltaIsBetter) {
+ bestDelta = delta;
+ bestCommunity = targetCommunity;
+ }
+ }
+
+ if (bestDelta < 0) {
+ bestCommunity = index.isolate(i, degree);
+
+ if (bestCommunity === currentCommunity) continue;
+ } else {
+ if (bestCommunity === currentCommunity) {
+ continue;
+ } else {
+ index.move(i, degree, bestCommunity);
+ }
+ }
+
+ currentMoves++;
+
+ // Adding neighbors from other communities to the queue
+ start = index.starts[i];
+ end = index.starts[i + 1];
+
+ for (; start < end; start++) {
+ j = index.neighborhood[start];
+ targetCommunity = index.belongings[j];
+
+ if (targetCommunity !== bestCommunity) queue.enqueue(j);
+ }
+ }
+
+ moves.push(currentMoves);
+
+ if (currentMoves === 0) {
+ index.zoomOut();
+ break;
+ }
+
+ if (!addenda.onlySingletons()) {
+ // We continue working on the induced graph
+ addenda.zoomOut();
+ continue;
+ }
+
+ break;
+ }
+
+ var results = {
+ index: index,
+ deltaComputations: deltaComputations,
+ nodesVisited: nodesVisited,
+ moves: moves
+ };
+
+ return results;
+}
+
+/**
+ * Function returning the communities mapping of the graph.
+ */
+function leiden(assign, detailed, graph, options) {
+ if (!isGraph(graph))
+ throw new Error(
+ 'graphology-communities-leiden: the given graph is not a valid graphology instance.'
+ );
+
+ var type = inferType(graph);
+
+ if (type === 'mixed')
+ throw new Error(
+ 'graphology-communities-leiden: cannot run the algorithm on a true mixed graph.'
+ );
+
+ if (type === 'directed')
+ throw new Error(
+ 'graphology-communities-leiden: not yet implemented for directed graphs.'
+ );
+
+ // Attributes name
+ options = resolveDefaults(options, DEFAULTS);
+
+ // Empty graph case
+ var c = 0;
+
+ if (graph.size === 0) {
+ if (assign) {
+ graph.forEachNode(function (node) {
+ graph.setNodeAttribute(node, options.attributes.communities, c++);
+ });
+
+ return;
+ }
+
+ var communities = {};
+
+ graph.forEachNode(function (node) {
+ communities[node] = c++;
+ });
+
+ if (!detailed) return communities;
+
+ return {
+ communities: communities,
+ count: graph.order,
+ deltaComputations: 0,
+ dendrogram: null,
+ level: 0,
+ modularity: NaN,
+ moves: null,
+ nodesVisited: 0,
+ resolution: options.resolution
+ };
+ }
+
+ var fn = undirectedLeiden;
+
+ var results = fn(detailed, graph, options);
+
+ var index = results.index;
+
+ // Standard output
+ if (!detailed) {
+ if (assign) {
+ index.assign(options.attributes.community);
+ return;
+ }
+
+ return index.collect();
+ }
+
+ // Detailed output
+ var output = {
+ count: index.C,
+ deltaComputations: results.deltaComputations,
+ dendrogram: index.dendrogram,
+ level: index.level,
+ modularity: index.modularity(),
+ moves: results.moves,
+ nodesVisited: results.nodesVisited,
+ resolution: options.resolution
+ };
+
+ if (assign) {
+ index.assign(options.attributes.community);
+ return output;
+ }
+
+ output.communities = index.collect();
+
+ return output;
+}
+
+/**
+ * Exporting.
+ */
+var fn = leiden.bind(null, false, false);
+fn.assign = leiden.bind(null, true, false);
+fn.detailed = leiden.bind(null, false, true);
+fn.defaults = DEFAULTS;
+
+export default fn;
diff --git a/gitnexus-web/src/vendor/leiden/utils.js b/gitnexus-web/src/vendor/leiden/utils.js
new file mode 100644
index 000000000..cca0265a7
--- /dev/null
+++ b/gitnexus-web/src/vendor/leiden/utils.js
@@ -0,0 +1,392 @@
+/**
+ * Graphology Leiden Utils
+ * ========================
+ *
+ * Miscellaneous utilities used by the Leiden algorithm.
+ *
+ * Vendored from: https://github.com/graphology/graphology/tree/master/src/communities-leiden
+ * License: MIT
+ *
+ * Converted to ESM for Vite compatibility.
+ */
+import SparseMap from 'mnemonist/sparse-map';
+import randomModule from 'pandemonium/random';
+var createRandom = randomModule.createRandom || randomModule;
+
+export function addWeightToCommunity(map, community, weight) {
+ var currentWeight = map.get(community);
+
+ if (typeof currentWeight === 'undefined') currentWeight = 0;
+
+ currentWeight += weight;
+
+ map.set(community, currentWeight);
+}
+
+export function UndirectedLeidenAddenda(index, options) {
+ options = options || {};
+
+ var rng = options.rng || Math.random;
+ var randomness = 'randomness' in options ? options.randomness : 0.01;
+
+ this.index = index;
+ this.random = createRandom(rng);
+ this.randomness = randomness;
+ this.rng = rng;
+
+ var NodesPointerArray = index.counts.constructor;
+ var WeightsArray = index.weights.constructor;
+
+ var order = index.C;
+ this.resolution = index.resolution;
+
+ // Used to group nodes by communities
+ this.B = index.C;
+ this.C = 0;
+ this.communitiesOffsets = new NodesPointerArray(order);
+ this.nodesSortedByCommunities = new NodesPointerArray(order);
+ this.communitiesBounds = new NodesPointerArray(order + 1);
+
+ // Used to merge nodes subsets
+ this.communityWeights = new WeightsArray(order);
+ this.degrees = new WeightsArray(order);
+ this.nonSingleton = new Uint8Array(order);
+ this.externalEdgeWeightPerCommunity = new WeightsArray(order);
+ this.belongings = new NodesPointerArray(order);
+ this.neighboringCommunities = new SparseMap(WeightsArray, order);
+ this.cumulativeIncrement = new Float64Array(order);
+ this.macroCommunities = null;
+}
+
+UndirectedLeidenAddenda.prototype.groupByCommunities = function () {
+ var index = this.index;
+
+ var n, i, c, b, o;
+
+ n = 0;
+ o = 0;
+
+ for (i = 0; i < index.C; i++) {
+ c = index.counts[i];
+
+ if (c !== 0) {
+ this.communitiesBounds[o++] = n;
+ n += c;
+ this.communitiesOffsets[i] = n;
+ }
+ }
+
+ this.communitiesBounds[o] = n;
+
+ o = 0;
+
+ for (i = 0; i < index.C; i++) {
+ b = index.belongings[i];
+ o = --this.communitiesOffsets[b];
+ this.nodesSortedByCommunities[o] = i;
+ }
+
+ this.B = index.C - index.U;
+ this.C = index.C;
+};
+
+UndirectedLeidenAddenda.prototype.communities = function () {
+ var communities = new Array(this.B);
+
+ var i, j, community, start, stop;
+
+ for (i = 0; i < this.B; i++) {
+ start = this.communitiesBounds[i];
+ stop = this.communitiesBounds[i + 1];
+ community = [];
+
+ for (j = start; j < stop; j++) {
+ community.push(j);
+ }
+
+ communities[i] = community;
+ }
+
+ return communities;
+};
+
+UndirectedLeidenAddenda.prototype.mergeNodesSubset = function (start, stop) {
+ var index = this.index;
+ var currentMacroCommunity =
+ index.belongings[this.nodesSortedByCommunities[start]];
+ var neighboringCommunities = this.neighboringCommunities;
+
+ var totalNodeWeight = 0;
+
+ var i, j, w;
+ var ei, el, et;
+
+ // Initializing singletons
+ for (j = start; j < stop; j++) {
+ i = this.nodesSortedByCommunities[j];
+
+ this.belongings[i] = i;
+ this.nonSingleton[i] = 0;
+ this.degrees[i] = 0;
+ totalNodeWeight += index.loops[i] / 2;
+
+ this.communityWeights[i] = index.loops[i];
+ this.externalEdgeWeightPerCommunity[i] = 0;
+
+ ei = index.starts[i];
+ el = index.starts[i + 1];
+
+ for (; ei < el; ei++) {
+ et = index.neighborhood[ei];
+ w = index.weights[ei];
+
+ this.degrees[i] += w;
+
+ if (index.belongings[et] !== currentMacroCommunity) continue;
+
+ totalNodeWeight += w;
+ this.externalEdgeWeightPerCommunity[i] += w;
+ this.communityWeights[i] += w;
+ }
+ }
+
+ var microDegrees = this.externalEdgeWeightPerCommunity.slice();
+
+ var s, ri, ci;
+ var order = stop - start;
+
+ var degree,
+ bestCommunity,
+ qualityValueIncrement,
+ maxQualityValueIncrement,
+ totalTransformedQualityValueIncrement,
+ targetCommunity,
+ targetCommunityDegree,
+ targetCommunityWeight;
+
+ var r, lo, hi, mid, chosenCommunity;
+
+ ri = this.random(start, stop - 1);
+
+ for (s = start; s < stop; s++, ri++) {
+ j = start + (ri % order);
+
+ i = this.nodesSortedByCommunities[j];
+
+ if (this.nonSingleton[i] === 1) {
+ continue;
+ }
+
+ if (
+ this.externalEdgeWeightPerCommunity[i] <
+ this.communityWeights[i] *
+ (totalNodeWeight / 2 - this.communityWeights[i]) *
+ this.resolution
+ ) {
+ continue;
+ }
+
+ this.communityWeights[i] = 0;
+ this.externalEdgeWeightPerCommunity[i] = 0;
+
+ neighboringCommunities.clear();
+ neighboringCommunities.set(i, 0);
+
+ degree = 0;
+
+ ei = index.starts[i];
+ el = index.starts[i + 1];
+
+ for (; ei < el; ei++) {
+ et = index.neighborhood[ei];
+
+ if (index.belongings[et] !== currentMacroCommunity) continue;
+
+ w = index.weights[ei];
+
+ degree += w;
+
+ addWeightToCommunity(neighboringCommunities, this.belongings[et], w);
+ }
+
+ bestCommunity = i;
+ maxQualityValueIncrement = 0;
+ totalTransformedQualityValueIncrement = 0;
+
+ for (ci = 0; ci < neighboringCommunities.size; ci++) {
+ targetCommunity = neighboringCommunities.dense[ci];
+ targetCommunityDegree = neighboringCommunities.vals[ci];
+ targetCommunityWeight = this.communityWeights[targetCommunity];
+
+ if (
+ this.externalEdgeWeightPerCommunity[targetCommunity] >=
+ targetCommunityWeight *
+ (totalNodeWeight / 2 - targetCommunityWeight) *
+ this.resolution
+ ) {
+ qualityValueIncrement =
+ targetCommunityDegree -
+ ((degree + index.loops[i]) *
+ targetCommunityWeight *
+ this.resolution) /
+ totalNodeWeight;
+
+ if (qualityValueIncrement > maxQualityValueIncrement) {
+ bestCommunity = targetCommunity;
+ maxQualityValueIncrement = qualityValueIncrement;
+ }
+
+ if (qualityValueIncrement >= 0)
+ totalTransformedQualityValueIncrement += Math.exp(
+ qualityValueIncrement / this.randomness
+ );
+ }
+
+ this.cumulativeIncrement[ci] = totalTransformedQualityValueIncrement;
+ }
+
+ if (
+ totalTransformedQualityValueIncrement < Number.MAX_VALUE &&
+ totalTransformedQualityValueIncrement < Infinity
+ ) {
+ r = totalTransformedQualityValueIncrement * this.rng();
+ lo = -1;
+ hi = neighboringCommunities.size + 1;
+
+ while (lo < hi - 1) {
+ mid = (lo + hi) >>> 1;
+
+ if (this.cumulativeIncrement[mid] >= r) hi = mid;
+ else lo = mid;
+ }
+
+ chosenCommunity = neighboringCommunities.dense[hi];
+ } else {
+ chosenCommunity = bestCommunity;
+ }
+
+ this.communityWeights[chosenCommunity] += degree + index.loops[i];
+
+ ei = index.starts[i];
+ el = index.starts[i + 1];
+
+ for (; ei < el; ei++) {
+ et = index.neighborhood[ei];
+
+ if (index.belongings[et] !== currentMacroCommunity) continue;
+
+ targetCommunity = this.belongings[et];
+
+ if (targetCommunity === chosenCommunity) {
+ this.externalEdgeWeightPerCommunity[chosenCommunity] -=
+ microDegrees[et];
+ } else {
+ this.externalEdgeWeightPerCommunity[chosenCommunity] +=
+ microDegrees[et];
+ }
+ }
+
+ if (chosenCommunity !== i) {
+ this.belongings[i] = chosenCommunity;
+ this.nonSingleton[chosenCommunity] = 1;
+ this.C--;
+ }
+ }
+
+ var microCommunities = this.neighboringCommunities;
+ microCommunities.clear();
+
+ for (j = start; j < stop; j++) {
+ i = this.nodesSortedByCommunities[j];
+ microCommunities.set(this.belongings[i], 1);
+ }
+
+ return microCommunities.dense.slice(0, microCommunities.size);
+};
+
+UndirectedLeidenAddenda.prototype.refinePartition = function () {
+ this.groupByCommunities();
+
+ this.macroCommunities = new Array(this.B);
+
+ var i, start, stop, mapping;
+
+ var bounds = this.communitiesBounds;
+
+ for (i = 0; i < this.B; i++) {
+ start = bounds[i];
+ stop = bounds[i + 1];
+
+ mapping = this.mergeNodesSubset(start, stop);
+ this.macroCommunities[i] = mapping;
+ }
+};
+
+UndirectedLeidenAddenda.prototype.split = function () {
+ var index = this.index;
+ var isolates = this.neighboringCommunities;
+
+ isolates.clear();
+
+ var i, community, isolated;
+
+ for (i = 0; i < index.C; i++) {
+ community = this.belongings[i];
+
+ if (i !== community) continue;
+
+ isolated = index.isolate(i, this.degrees[i]);
+ isolates.set(community, isolated);
+ }
+
+ for (i = 0; i < index.C; i++) {
+ community = this.belongings[i];
+
+ if (i === community) continue;
+
+ isolated = isolates.get(community);
+ index.move(i, this.degrees[i], isolated);
+ }
+
+ var j, macro;
+
+ for (i = 0; i < this.macroCommunities.length; i++) {
+ macro = this.macroCommunities[i];
+
+ for (j = 0; j < macro.length; j++) macro[j] = isolates.get(macro[j]);
+ }
+};
+
+UndirectedLeidenAddenda.prototype.zoomOut = function () {
+ var index = this.index;
+ this.refinePartition();
+ this.split();
+
+ var newLabels = index.zoomOut();
+
+ var macro, leader, follower;
+
+ var i, j;
+
+ for (i = 0; i < this.macroCommunities.length; i++) {
+ macro = this.macroCommunities[i];
+ leader = newLabels[macro[0]];
+
+ for (j = 1; j < macro.length; j++) {
+ follower = newLabels[macro[j]];
+ index.expensiveMove(follower, leader);
+ }
+ }
+};
+
+UndirectedLeidenAddenda.prototype.onlySingletons = function () {
+ var index = this.index;
+
+ var i;
+
+ for (i = 0; i < index.C; i++) {
+ if (index.counts[i] > 1) return false;
+ }
+
+ return true;
+};
diff --git a/gitnexus/src/vite-env.d.ts b/gitnexus-web/src/vite-env.d.ts
similarity index 100%
rename from gitnexus/src/vite-env.d.ts
rename to gitnexus-web/src/vite-env.d.ts
diff --git a/gitnexus/src/workers/ingestion.worker.ts b/gitnexus-web/src/workers/ingestion.worker.ts
similarity index 98%
rename from gitnexus/src/workers/ingestion.worker.ts
rename to gitnexus-web/src/workers/ingestion.worker.ts
index c5fb407f2..af50fc2ef 100644
--- a/gitnexus/src/workers/ingestion.worker.ts
+++ b/gitnexus-web/src/workers/ingestion.worker.ts
@@ -51,6 +51,9 @@ let currentGraphResult: PipelineResult | null = null;
 let pendingEnrichmentConfig: ProviderConfig | null = null;
 let enrichmentCancelled = false;
 
+// Chat cancellation flag
+let chatCancelled = false;
+
 /**
  * Worker API exposed via Comlink
  * 
@@ -570,16 +573,34 @@ const workerApi = {
       return;
     }
 
+    chatCancelled = false;
+
     try {
       for await (const chunk of streamAgentResponse(currentAgent, messages)) {
+        if (chatCancelled) {
+          onChunk({ type: 'done' });
+          break;
+        }
         onChunk(chunk);
       }
     } catch (error) {
+      if (chatCancelled) {
+        // Swallow errors from cancellation
+        onChunk({ type: 'done' });
+        return;
+      }
       const message = error instanceof Error ? error.message : String(error);
       onChunk({ type: 'error', error: message });
     }
   },
 
+  /**
+   * Stop the current chat stream
+   */
+  stopChat(): void {
+    chatCancelled = true;
+  },
+
   /**
    * Dispose of the current agent
    */
diff --git a/gitnexus/tsconfig.app.json b/gitnexus-web/tsconfig.app.json
similarity index 100%
rename from gitnexus/tsconfig.app.json
rename to gitnexus-web/tsconfig.app.json
diff --git a/gitnexus-web/tsconfig.json b/gitnexus-web/tsconfig.json
new file mode 100644
index 000000000..1ffef600d
--- /dev/null
+++ b/gitnexus-web/tsconfig.json
@@ -0,0 +1,7 @@
+{
+  "files": [],
+  "references": [
+    { "path": "./tsconfig.app.json" },
+    { "path": "./tsconfig.node.json" }
+  ]
+}
diff --git a/gitnexus/tsconfig.node.json b/gitnexus-web/tsconfig.node.json
similarity index 100%
rename from gitnexus/tsconfig.node.json
rename to gitnexus-web/tsconfig.node.json
diff --git a/gitnexus/vercel.json b/gitnexus-web/vercel.json
similarity index 100%
rename from gitnexus/vercel.json
rename to gitnexus-web/vercel.json
diff --git a/gitnexus/vite.config.ts b/gitnexus-web/vite.config.ts
similarity index 100%
rename from gitnexus/vite.config.ts
rename to gitnexus-web/vite.config.ts
diff --git a/gitnexus/.npmignore b/gitnexus/.npmignore
new file mode 100644
index 000000000..6a4cfb118
--- /dev/null
+++ b/gitnexus/.npmignore
@@ -0,0 +1,17 @@
+# Source (dist/ is the compiled output)
+src/
+tsconfig.json
+
+# Dev files
+*.ts
+!dist/**/*.d.ts
+.git/
+.gitignore
+node_modules/
+
+# Package lock (consumers use their own)
+package-lock.json
+
+# IDE
+.vscode/
+.idea/
diff --git a/gitnexus/APPROACH.md b/gitnexus/APPROACH.md
new file mode 100644
index 000000000..960ea6d40
--- /dev/null
+++ b/gitnexus/APPROACH.md
@@ -0,0 +1,339 @@
+# GitNexus: Architecture & Approach
+
+GitNexus is a code intelligence layer that builds a knowledge graph from your repository's structure — functions, classes, call relationships, execution flows — and delivers that context to AI coding agents through two complementary channels: **MCP tools** for explicit deep dives and **hook-based augmentation** that invisibly enriches every search the agent performs.
+
+## Architecture
+
+```mermaid
+graph LR
+    A[gitnexus analyze] -->|parse + index| B[(KuzuDB Graph)]
+    B --> C[MCP Server]
+    B --> D[Hook Augmentation]
+    C -->|tools + resources| E[AI Agent]
+    D -->|additionalContext on search| E
+```
+
+`gitnexus analyze` walks the repository, extracts symbols and relationships using tree-sitter, detects communities with the Leiden algorithm, traces execution flows into processes, and stores everything in a KuzuDB graph database under `.gitnexus/kuzu/`.
+
+The graph is then queryable through two paths:
+
+1. **MCP tools** — the agent calls `query`, `context`, `impact`, etc. explicitly when it needs structural understanding.
+2. **Hook augmentation** — a PreToolUse hook fires on every Grep/Glob/Bash search, runs a fast BM25 lookup against the graph, and injects related symbols, callers, callees, and execution flows as `additionalContext`. The agent never asks for this; it just appears alongside search results.
+
+## The Graph
+
+```mermaid
+erDiagram
+    File ||--o{ Function : DEFINES
+    File ||--o{ Class : DEFINES
+    File ||--o{ Interface : DEFINES
+    Class ||--o{ Method : DEFINES
+    Function ||--o{ Function : CALLS
+    Method ||--o{ Function : CALLS
+    Function ||--o{ File : IMPORTS
+    Class ||--o{ Class : EXTENDS
+    Class ||--o{ Interface : IMPLEMENTS
+    Function }o--|| Community : MEMBER_OF
+    Function }o--|| Process : STEP_IN_PROCESS
+```
+
+**Nodes** represent code symbols: `File`, `Folder`, `Function`, `Class`, `Interface`, `Method`, `CodeElement`. Language-specific nodes include `Struct`, `Enum`, `Trait`, `Impl`, etc.
+
+**Edges** use a single `CodeRelation` table with a `type` property:
+- `CALLS` — function/method invocation
+- `IMPORTS` — file/module import
+- `EXTENDS` / `IMPLEMENTS` — inheritance
+- `DEFINES` / `CONTAINS` — structural containment
+- `MEMBER_OF` — community membership
+- `STEP_IN_PROCESS` — participation in an execution flow (with `step` order)
+
+**Communities** are auto-detected functional areas (Leiden algorithm). Each community has a `heuristicLabel` (e.g., "Authentication", "Database") and a `cohesion` score measuring internal connectivity density. Same-label communities are aggregated for display; clusters with fewer than 5 symbols are filtered out.
+
+**Processes** are execution flow traces — ordered sequences of symbols that represent a call chain from entry point to terminal. Each process has a `heuristicLabel`, `processType`, and `stepCount`. A single symbol can participate in multiple processes.
+
+## MCP Tools
+
+| Tool | Purpose |
+|------|---------|
+| `list_repos` | Discover indexed repositories and their stats |
+| `query` | Search for execution flows related to a concept (hybrid BM25 + semantic, grouped by process) |
+| `context` | 360-degree view of a single symbol — callers, callees, imports, process participation |
+| `impact` | Blast radius analysis — what breaks if you change a symbol, grouped by depth |
+| `detect_changes` | Map uncommitted git changes to affected symbols and execution flows |
+| `rename` | Multi-file coordinated rename using graph refs + text search fallback |
+| `cypher` | Raw Cypher queries against the knowledge graph |
+
+### query
+
+Returns results grouped by process (execution flow), not by file. Hybrid search combines BM25 keyword matching with semantic vector search, merged via Reciprocal Rank Fusion.
+
+```json
+{
+  "processes": [
+    { "summary": "UserAuthentication", "priority": 0.033, "symbol_count": 4, "process_type": "request_handler", "step_count": 7 }
+  ],
+  "process_symbols": [
+    { "name": "validateUser", "type": "Function", "filePath": "src/auth/validator.ts", "startLine": 15, "step_index": 3 }
+  ],
+  "definitions": [
+    { "name": "AuthConfig", "type": "Interface", "filePath": "src/auth/types.ts" }
+  ]
+}
+```
+
+### context
+
+Categorized 360-degree view with disambiguation for common names.
+
+```json
+{
+  "status": "found",
+  "symbol": { "uid": "Function:src/auth/validator.ts:validateUser", "name": "validateUser", "kind": "Function", "filePath": "src/auth/validator.ts", "startLine": 15, "endLine": 42 },
+  "incoming": {
+    "calls": [
+      { "uid": "Function:src/routes/login.ts:handleLogin", "name": "handleLogin", "filePath": "src/routes/login.ts" }
+    ],
+    "imports": [
+      { "uid": "File:src/routes/login.ts", "name": "login.ts", "filePath": "src/routes/login.ts" }
+    ]
+  },
+  "outgoing": {
+    "calls": [
+      { "uid": "Function:src/db/users.ts:findByEmail", "name": "findByEmail", "filePath": "src/db/users.ts" }
+    ]
+  },
+  "processes": [
+    { "id": "proc_auth_login", "name": "UserAuthentication", "step_index": 3, "step_count": 7 }
+  ]
+}
+```
+
+### impact
+
+Blast radius grouped by depth: d=1 will break, d=2 likely affected, d=3 may need testing.
+
+```json
+{
+  "target": { "name": "validateUser", "type": "Function", "filePath": "src/auth/validator.ts" },
+  "direction": "upstream",
+  "impactedCount": 5,
+  "byDepth": {
+    "1": [{ "name": "handleLogin", "type": "Function", "filePath": "src/routes/login.ts", "relationType": "CALLS", "confidence": 1.0 }],
+    "2": [{ "name": "authRouter", "type": "Function", "filePath": "src/routes/index.ts", "relationType": "CALLS", "confidence": 1.0 }]
+  }
+}
+```
+
+### detect_changes
+
+Maps git diff to indexed symbols and traces which execution flows are impacted.
+
+```json
+{
+  "summary": { "changed_count": 3, "affected_count": 2, "changed_files": 1, "risk_level": "medium" },
+  "changed_symbols": [
+    { "name": "validateUser", "type": "Function", "filePath": "src/auth/validator.ts", "change_type": "Modified" }
+  ],
+  "affected_processes": [
+    { "name": "UserAuthentication", "process_type": "request_handler", "step_count": 7, "changed_steps": [{ "symbol": "validateUser", "step": 3 }] }
+  ]
+}
+```
+
+### rename
+
+Graph-based rename with text search fallback. Edits tagged by confidence source.
+
+```json
+{
+  "status": "success",
+  "old_name": "validateUser",
+  "new_name": "verifyUser",
+  "files_affected": 4,
+  "total_edits": 7,
+  "graph_edits": 5,
+  "text_search_edits": 2,
+  "changes": [
+    { "file_path": "src/auth/validator.ts", "edits": [{ "line": 15, "old_text": "function validateUser(", "new_text": "function verifyUser(", "confidence": "graph" }] },
+    { "file_path": "src/tests/auth.test.ts", "edits": [{ "line": 8, "old_text": "validateUser(testInput)", "new_text": "verifyUser(testInput)", "confidence": "text_search" }] }
+  ],
+  "applied": false
+}
+```
+
+## Hook Augmentation
+
+The hook intercepts every search the agent runs and silently enriches it with graph context. The agent doesn't request this — it just sees richer results.
+
+### Flow
+
+```mermaid
+sequenceDiagram
+    participant Agent
+    participant Claude Code
+    participant Hook as gitnexus-hook.js
+    participant CLI as gitnexus augment
+    participant Graph as KuzuDB
+
+    Agent->>Claude Code: Grep { pattern: "validateUser" }
+    Claude Code->>Hook: PreToolUse event (stdin JSON)
+    Hook->>Hook: Extract pattern from tool_input
+    Hook->>CLI: gitnexus augment "validateUser"
+    CLI->>Graph: BM25 search → symbol lookup → callers/callees/processes
+    Graph-->>CLI: enriched results
+    CLI-->>Hook: structured text (stdout)
+    Hook-->>Claude Code: { additionalContext: "..." }
+    Claude Code->>Agent: Grep results + additionalContext
+```
+
+### What the hook receives
+
+```json
+{
+  "hook_event_name": "PreToolUse",
+  "tool_name": "Grep",
+  "tool_input": { "pattern": "validateUser", "path": "src/" },
+  "cwd": "/home/user/project"
+}
+```
+
+### What the hook returns
+
+```json
+{
+  "hookSpecificOutput": {
+    "hookEventName": "PreToolUse",
+    "additionalContext": "[GitNexus] 3 related symbols found:\n\nvalidateUser (src/auth/validator.ts)\n  Called by: handleLogin, authMiddleware\n  Calls: findByEmail, hashPassword\n  Flows: UserAuthentication (step 3/7)\n\nauthMiddleware (src/middleware/auth.ts)\n  Called by: router.use\n  Calls: validateUser, getSession\n  Flows: RequestPipeline (step 2/5)"
+  }
+}
+```
+
+### Before and after
+
+**Without augmentation** — the agent sees only grep matches:
+
+```
+src/auth/validator.ts:15: export function validateUser(email: string, password: string) {
+src/routes/login.ts:23:   const user = await validateUser(req.body.email, req.body.password);
+src/tests/auth.test.ts:8:   const result = validateUser("test@example.com", "password123");
+```
+
+**With augmentation** — the same grep results arrive with additional context appended:
+
+```
+src/auth/validator.ts:15: export function validateUser(email: string, password: string) {
+src/routes/login.ts:23:   const user = await validateUser(req.body.email, req.body.password);
+src/tests/auth.test.ts:8:   const result = validateUser("test@example.com", "password123");
+
+[GitNexus] 3 related symbols found:
+
+validateUser (src/auth/validator.ts)
+  Called by: handleLogin, authMiddleware
+  Calls: findByEmail, hashPassword
+  Flows: UserAuthentication (step 3/7)
+
+authMiddleware (src/middleware/auth.ts)
+  Called by: router.use
+  Calls: validateUser, getSession
+  Flows: RequestPipeline (step 2/5)
+
+handleLogin (src/routes/login.ts)
+  Called by: authRouter
+  Calls: validateUser, createSession
+  Flows: UserAuthentication (step 4/7)
+```
+
+The agent now knows the call chain, related symbols it hasn't searched for yet, and which execution flows are involved — without making any explicit tool call.
+
+### Pattern extraction
+
+The hook extracts search patterns from different tool types:
+- **Grep** → uses `pattern` directly
+- **Glob** → extracts meaningful name segments from glob patterns
+- **Bash** → detects `rg`/`grep` commands and extracts the pattern argument, skipping flags
+
+Patterns shorter than 3 characters are ignored. If no `.gitnexus` index exists in the working directory ancestry, the hook exits silently.
+
+## Internal Ranking
+
+### Clusters as a hidden signal
+
+Communities (clusters) detected by the Leiden algorithm have a `cohesion` score measuring internal connectivity density. This score is used **exclusively as an internal ranking signal** — it is never exposed in tool output or augmentation text.
+
+In `query`, cluster cohesion provides a subtle ranking boost:
+```
+priority = aggregate_rrf_score + (cohesion * 0.1)
+```
+
+In augmentation, results are sorted by their cluster cohesion before formatting. The effect: symbols from tightly-connected functional areas rank higher, but the agent never sees "cluster" or "cohesion" anywhere — it just gets better-ordered results.
+
+### Processes as result grouping
+
+Processes serve as the primary grouping mechanism for `query` results. Instead of returning a flat list of symbol matches, results are organized by execution flow:
+
+1. Hybrid search finds matching symbols
+2. Each symbol is traced to its process(es) via `STEP_IN_PROCESS` edges
+3. Processes are ranked by aggregate relevance score (with cohesion boost)
+4. Results arrive grouped: "here are the execution flows related to your query, with the symbols in each"
+
+Symbols not belonging to any process fall into a `definitions` bucket (types, interfaces, standalone declarations).
+
+## Platform Integration
+
+```mermaid
+graph TD
+    subgraph "gitnexus setup"
+        S[setup.ts]
+    end
+
+    subgraph Claude Code
+        S --> CC_MCP["claude mcp add gitnexus"]
+        S --> CC_HOOKS["~/.claude/settings.json
(PreToolUse hooks)"] + S --> CC_SKILLS["~/.claude/skills/
(exploring, debugging, etc.)"] + end + + subgraph "Claude Code Plugin" + P["--plugin-dir gitnexus-claude-plugin/"] + P --> P_HOOKS["hooks/hooks.json
(PreToolUse)"] + P --> P_SKILLS["skills/
(auto-namespaced)"] + end + + subgraph Cursor + S --> CU_MCP["~/.cursor/mcp.json"] + S --> CU_SKILLS["~/.cursor/skills/"] + end + + subgraph OpenCode + S --> OC_MCP["~/.config/opencode/config.json"] + S --> OC_SKILLS["~/.config/opencode/skill/"] + end +``` + +**Two install paths for Claude Code:** + +1. **`gitnexus setup`** — copies hook scripts to `~/.claude/hooks/gitnexus/`, merges PreToolUse config into `~/.claude/settings.json`, installs skills to `~/.claude/skills/`. Works globally across all projects. + +2. **Plugin mode** (`claude --plugin-dir gitnexus-claude-plugin/`) — self-contained directory with `hooks/hooks.json` and skills. Uses `${CLAUDE_PLUGIN_ROOT}` env var for script paths. Skills are auto-namespaced (e.g., `/gitnexus:exploring`). + +Both paths install the same 4 skills: `exploring`, `debugging`, `impact-analysis`, `refactoring`. + +**Cursor** gets MCP config at `~/.cursor/mcp.json` and skills at `~/.cursor/skills/`. + +**OpenCode** gets MCP config at `~/.config/opencode/config.json` and skills at `~/.config/opencode/skill/`. + +All MCP entries point to the same server: `npx -y gitnexus@latest mcp`. + +## UX Perspective + +From the agent's point of view, GitNexus operates on two levels: + +**Invisible enrichment** — Every search the agent runs (grep for a function name, glob for a file pattern, bash with ripgrep) gets silently augmented. The agent sees its normal search results plus a `[GitNexus]` block listing related symbols, their callers/callees, and which execution flows they participate in. This happens on every search without the agent doing anything special. The effect is that the agent naturally discovers related code it wouldn't have found from text search alone. + +**Explicit tools for deep dives** — When the agent needs structural understanding (not just "find where X is used" but "what happens if I change X"), it uses the MCP tools directly: +- `query` to find execution flows related to a concept +- `context` to get the full picture of a specific symbol +- `impact` to assess blast radius before making changes +- `detect_changes` to understand what uncommitted work affects +- `rename` to safely rename across the codebase with graph-backed confidence + +The invisible layer handles the 80% case (richer search context), while the explicit tools handle the 20% (deep structural questions). The agent doesn't need to decide when to use GitNexus for search enrichment — it just happens. diff --git a/gitnexus/README.md b/gitnexus/README.md new file mode 100644 index 000000000..4acf9d36d --- /dev/null +++ b/gitnexus/README.md @@ -0,0 +1,187 @@ +# GitNexus + +**Graph-powered code intelligence for AI agents.** Index any codebase into a knowledge graph, then query it via MCP or CLI. + +Works with **Cursor**, **Claude Code**, **Windsurf**, **Cline**, **OpenCode**, and any MCP-compatible tool. + +[![npm version](https://img.shields.io/npm/v/gitnexus.svg)](https://www.npmjs.com/package/gitnexus) +[![License: PolyForm Noncommercial](https://img.shields.io/badge/License-PolyForm%20Noncommercial-blue.svg)](https://polyformproject.org/licenses/noncommercial/1.0.0/) + +--- + +## Why? + +AI coding tools don't understand your codebase structure. They edit a function without knowing 47 other functions depend on it. GitNexus fixes this by **precomputing every dependency, call chain, and relationship** into a queryable graph. + +**Three commands to give your AI agent full codebase awareness.** + +## Quick Start + +```bash +# Index your repo (run from repo root) +npx gitnexus analyze +``` + +That's it. This indexes the codebase, installs agent skills, registers Claude Code hooks, and creates `AGENTS.md` / `CLAUDE.md` context files — all in one command. + +To configure MCP for your editor, run `npx gitnexus setup` once — or set it up manually below. + +`gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once. + +### Editor Support + +| Editor | MCP | Skills | Hooks (auto-augment) | Support | +|--------|-----|--------|---------------------|---------| +| **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** | +| **Cursor** | Yes | Yes | — | MCP + Skills | +| **Windsurf** | Yes | — | — | MCP | +| **OpenCode** | Yes | Yes | — | MCP + Skills | + +> **Claude Code** gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context. + +## MCP Setup (manual) + +If you prefer to configure manually instead of using `gitnexus setup`: + +### Claude Code (full support — MCP + skills + hooks) + +```bash +claude mcp add gitnexus -- npx -y gitnexus@latest mcp +``` + +### Cursor / Windsurf + +Add to `~/.cursor/mcp.json` (global — works for all projects): + +```json +{ + "mcpServers": { + "gitnexus": { + "command": "npx", + "args": ["-y", "gitnexus@latest", "mcp"] + } + } +} +``` + +### OpenCode + +Add to `~/.config/opencode/config.json`: + +```json +{ + "mcp": { + "gitnexus": { + "command": "npx", + "args": ["-y", "gitnexus@latest", "mcp"] + } + } +} +``` + +## What It Does + +GitNexus indexes your codebase through 7 phases: + +1. **Structure** — File/folder tree +2. **Parse** — AST extraction via Tree-sitter (9 languages) +3. **Imports** — Resolve import paths (including TS path aliases, Rust modules, Java wildcards, Go packages) +4. **Calls** — Function call resolution with confidence scoring (0.3-0.9) +5. **Heritage** — Class extends/implements chains +6. **Communities** — Leiden algorithm clusters related code into functional groups +7. **Processes** — Entry point detection and execution flow tracing + +The result is a **KuzuDB graph database** stored locally in `.gitnexus/` with full-text search and semantic embeddings. + +## MCP Tools + +Your AI agent gets these tools automatically: + +| Tool | What It Does | `repo` Param | +|------|-------------|--------------| +| `list_repos` | Discover all indexed repositories | — | +| `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional | +| `context` | 360-degree symbol view — categorized refs, process participation | Optional | +| `impact` | Blast radius analysis with depth grouping and confidence | Optional | +| `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional | +| `rename` | Multi-file coordinated rename with graph + text search | Optional | +| `cypher` | Raw Cypher graph queries | Optional | + +> With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`. + +## MCP Resources + +| Resource | Purpose | +|----------|---------| +| `gitnexus://repos` | List all indexed repositories (read first) | +| `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools | +| `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores | +| `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{name}` | Full process trace with steps | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries | + +## MCP Prompts + +| Prompt | What It Does | +|--------|-------------| +| `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level | +| `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams | + +## CLI Commands + +```bash +gitnexus setup # Configure MCP for your editors (one-time) +gitnexus analyze [path] # Index a repository (or update stale index) +gitnexus analyze --force # Force full re-index +gitnexus analyze --skip-embeddings # Skip embedding generation (faster) +gitnexus mcp # Start MCP server (stdio) — serves all indexed repos +gitnexus serve # Start HTTP server for web UI +gitnexus list # List all indexed repositories +gitnexus status # Show index status for current repo +gitnexus clean # Delete index for current repo +gitnexus clean --all --force # Delete all indexes +gitnexus wiki [path] # Generate LLM-powered docs from knowledge graph +gitnexus wiki --model # Wiki with custom LLM model (default: gpt-4o-mini) +``` + +## Multi-Repo Support + +GitNexus supports indexing multiple repositories. Each `gitnexus analyze` registers the repo in a global registry (`~/.gitnexus/registry.json`). The MCP server serves all indexed repos automatically with lazy KuzuDB connections (max 5 concurrent, evicted after 5 minutes idle). + +## Supported Languages + +TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust + +## Agent Skills + +GitNexus ships with skill files that teach AI agents how to use the tools effectively: + +- **Exploring** — Navigate unfamiliar code using the knowledge graph +- **Debugging** — Trace bugs through call chains +- **Impact Analysis** — Analyze blast radius before changes +- **Refactoring** — Plan safe refactors using dependency mapping + +Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setup` (global). + +## Requirements + +- Node.js >= 18 +- Git repository (uses git for commit tracking) + +## Privacy + +- All processing happens locally on your machine +- No code is sent to any server +- Index stored in `.gitnexus/` inside your repo (gitignored) +- Global registry at `~/.gitnexus/` stores only paths and metadata + +## Web UI + +GitNexus also has a browser-based UI at [gitnexus.vercel.app](https://gitnexus.vercel.app) — 100% client-side, your code never leaves the browser. + +## License + +[PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/) + +Free for non-commercial use. Contact for commercial licensing. diff --git a/gitnexus/hooks/claude/gitnexus-hook.cjs b/gitnexus/hooks/claude/gitnexus-hook.cjs new file mode 100644 index 000000000..3b2e5f508 --- /dev/null +++ b/gitnexus/hooks/claude/gitnexus-hook.cjs @@ -0,0 +1,135 @@ +#!/usr/bin/env node +/** + * GitNexus Claude Code Hook + * + * PreToolUse handler — intercepts Grep/Glob/Bash searches + * and augments with graph context from the GitNexus index. + * + * NOTE: SessionStart hooks are broken on Windows (Claude Code bug). + * Session context is injected via CLAUDE.md / skills instead. + */ + +const fs = require('fs'); +const path = require('path'); +const { execFileSync } = require('child_process'); + +/** + * Read JSON input from stdin synchronously. + */ +function readInput() { + try { + const data = fs.readFileSync(0, 'utf-8'); + return JSON.parse(data); + } catch { + return {}; + } +} + +/** + * Check if a directory (or ancestor) has a .gitnexus index. + */ +function findGitNexusIndex(startDir) { + let dir = startDir || process.cwd(); + for (let i = 0; i < 5; i++) { + if (fs.existsSync(path.join(dir, '.gitnexus'))) { + return true; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return false; +} + +/** + * Extract search pattern from tool input. + */ +function extractPattern(toolName, toolInput) { + if (toolName === 'Grep') { + return toolInput.pattern || null; + } + + if (toolName === 'Glob') { + const raw = toolInput.pattern || ''; + const match = raw.match(/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/); + return match ? match[1] : null; + } + + if (toolName === 'Bash') { + const cmd = toolInput.command || ''; + if (!/\brg\b|\bgrep\b/.test(cmd)) return null; + + const tokens = cmd.split(/\s+/); + let foundCmd = false; + let skipNext = false; + const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']); + + for (const token of tokens) { + if (skipNext) { skipNext = false; continue; } + if (!foundCmd) { + if (/\brg$|\bgrep$/.test(token)) foundCmd = true; + continue; + } + if (token.startsWith('-')) { + if (flagsWithValues.has(token)) skipNext = true; + continue; + } + const cleaned = token.replace(/['"]/g, ''); + return cleaned.length >= 3 ? cleaned : null; + } + return null; + } + + return null; +} + +function main() { + try { + const input = readInput(); + const hookEvent = input.hook_event_name || ''; + + if (hookEvent !== 'PreToolUse') return; + + const cwd = input.cwd || process.cwd(); + if (!findGitNexusIndex(cwd)) return; + + const toolName = input.tool_name || ''; + const toolInput = input.tool_input || {}; + + if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Bash') return; + + const pattern = extractPattern(toolName, toolInput); + if (!pattern || pattern.length < 3) return; + + // Resolve CLI path relative to this hook script (same package) + // hooks/claude/gitnexus-hook.cjs → dist/cli/index.js + const cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js'); + + // augment CLI writes result to stderr (KuzuDB's native module captures + // stdout fd at OS level, making it unusable in subprocess contexts). + const { spawnSync } = require('child_process'); + let result = ''; + try { + const child = spawnSync( + process.execPath, + [cliPath, 'augment', pattern], + { encoding: 'utf-8', timeout: 8000, cwd, stdio: ['pipe', 'pipe', 'pipe'] } + ); + result = child.stderr || ''; + } catch { /* graceful failure */ } + + if (result && result.trim()) { + console.log(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + additionalContext: result.trim() + } + })); + } + } catch (err) { + // Graceful failure — log to stderr for debugging + console.error('GitNexus hook error:', err.message); + } +} + +main(); diff --git a/gitnexus/hooks/claude/pre-tool-use.sh b/gitnexus/hooks/claude/pre-tool-use.sh new file mode 100644 index 000000000..3c1af3bc0 --- /dev/null +++ b/gitnexus/hooks/claude/pre-tool-use.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# GitNexus PreToolUse hook for Claude Code +# Intercepts Grep/Glob/Bash searches and augments with graph context. +# Receives JSON on stdin with { tool_name, tool_input, cwd, ... } +# Returns JSON with additionalContext for graph-enriched results. + +INPUT=$(cat) + +TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null) +CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null) + +# Extract search pattern based on tool type +PATTERN="" + +case "$TOOL_NAME" in + Grep) + PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty' 2>/dev/null) + ;; + Glob) + # Glob patterns are file paths, not search terms — extract meaningful part + RAW=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty' 2>/dev/null) + # Strip glob syntax to get the meaningful name (e.g., "**/*.ts" → skip, "auth*.ts" → "auth") + PATTERN=$(echo "$RAW" | sed -n 's/.*[*\/]\([a-zA-Z][a-zA-Z0-9_-]*\).*/\1/p') + ;; + Bash) + CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null) + # Only augment grep/rg commands + if echo "$CMD" | grep -qE '\brg\b|\bgrep\b'; then + # Extract pattern from rg/grep + if echo "$CMD" | grep -qE '\brg\b'; then + PATTERN=$(echo "$CMD" | sed -n "s/.*\brg\s\+\(--[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") + elif echo "$CMD" | grep -qE '\bgrep\b'; then + PATTERN=$(echo "$CMD" | sed -n "s/.*\bgrep\s\+\(-[^ ]*\s\+\)*['\"]\\?\([^'\";\| >]*\\).*/\2/p") + fi + fi + ;; + *) + # Not a search tool — skip + exit 0 + ;; +esac + +# Skip if pattern too short or empty +if [ -z "$PATTERN" ] || [ ${#PATTERN} -lt 3 ]; then + exit 0 +fi + +# Check if we're in a GitNexus-indexed repo +dir="${CWD:-$PWD}" +found=false +for i in 1 2 3 4 5; do + if [ -d "$dir/.gitnexus" ]; then + found=true + break + fi + parent="$(dirname "$dir")" + [ "$parent" = "$dir" ] && break + dir="$parent" +done + +if [ "$found" = false ]; then + exit 0 +fi + +# Run gitnexus augment — must be fast (<500ms target) +RESULT=$(cd "$CWD" && npx -y gitnexus augment "$PATTERN" 2>/dev/null) + +if [ -n "$RESULT" ]; then + ESCAPED=$(echo "$RESULT" | jq -Rs .) + jq -n --argjson ctx "$ESCAPED" '{ + hookSpecificOutput: { + hookEventName: "PreToolUse", + additionalContext: $ctx + } + }' +else + exit 0 +fi diff --git a/gitnexus/hooks/claude/session-start.sh b/gitnexus/hooks/claude/session-start.sh new file mode 100644 index 000000000..8960dd376 --- /dev/null +++ b/gitnexus/hooks/claude/session-start.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# GitNexus SessionStart hook for Claude Code +# Fires on session startup. Stdout is injected into Claude's context. +# Checks if the current directory has a GitNexus index. + +dir="$PWD" +found=false +for i in 1 2 3 4 5; do + if [ -d "$dir/.gitnexus" ]; then + found=true + break + fi + parent="$(dirname "$dir")" + [ "$parent" = "$dir" ] && break + dir="$parent" +done + +if [ "$found" = false ]; then + exit 0 +fi + +# Inject GitNexus context — this stdout goes directly into Claude's context +cat << 'EOF' +## GitNexus Code Intelligence + +This codebase is indexed by GitNexus, providing a knowledge graph with execution flows, relationships, and semantic search. + +**Available MCP Tools:** +- `query` — Process-grouped code intelligence (execution flows related to a concept) +- `context` — 360-degree symbol view (categorized refs, process participation) +- `impact` — Blast radius analysis (what breaks if you change a symbol) +- `detect_changes` — Git-diff impact analysis (what do your changes affect) +- `rename` — Multi-file coordinated rename with confidence tags +- `cypher` — Raw graph queries +- `list_repos` — Discover indexed repos + +**Quick Start:** READ `gitnexus://repo/{name}/context` for codebase overview, then use `query` to find execution flows. + +**Resources:** `gitnexus://repo/{name}/context` (overview), `/processes` (execution flows), `/schema` (for Cypher) +EOF + +exit 0 diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index cf591cb30..86415b4d9 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -1,568 +1,54 @@ { "name": "gitnexus", - "version": "0.0.0", + "version": "1.1.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gitnexus", - "version": "0.0.0", + "version": "1.1.9", + "license": "PolyForm-Noncommercial-1.0.0", "dependencies": { "@huggingface/transformers": "^3.0.0", - "@isomorphic-git/lightning-fs": "^4.6.2", - "@langchain/anthropic": "^1.3.10", - "@langchain/core": "^1.1.15", - "@langchain/google-genai": "^2.1.10", - "@langchain/langgraph": "^1.1.0", - "@langchain/ollama": "^1.2.0", - "@langchain/openai": "^1.2.2", - "@sigma/edge-curve": "^3.1.0", - "@tailwindcss/vite": "^4.1.18", - "axios": "^1.13.2", - "buffer": "^6.0.3", - "comlink": "^4.4.2", - "d3": "^7.9.0", - "graphology": "^0.26.0", - "graphology-communities-louvain": "^2.0.2", - "graphology-layout-force": "^0.2.4", - "graphology-layout-forceatlas2": "^0.10.1", - "graphology-layout-noverlap": "^0.4.2", - "isomorphic-git": "^1.36.1", - "jszip": "^3.10.1", - "kuzu-wasm": "^0.11.1", - "langchain": "^1.2.10", - "lru-cache": "^11.2.4", - "lucide-react": "^0.562.0", - "mermaid": "^11.12.2", - "minisearch": "^7.2.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^10.1.0", - "react-syntax-highlighter": "^16.1.0", - "react-zoom-pan-pinch": "^3.7.0", - "remark-gfm": "^4.0.1", - "sigma": "^3.0.2", - "tailwindcss": "^4.1.18", - "uuid": "^13.0.0", - "vite-plugin-top-level-await": "^1.6.0", - "vite-plugin-wasm": "^3.5.0", - "web-tree-sitter": "^0.20.8", - "zod": "^3.25.76" + "@modelcontextprotocol/sdk": "^1.0.0", + "cli-progress": "^3.12.0", + "commander": "^12.0.0", + "cors": "^2.8.5", + "express": "^4.19.2", + "glob": "^11.0.0", + "graphology": "^0.25.4", + "graphology-indices": "^0.17.0", + "graphology-utils": "^2.3.0", + "kuzu": "^0.11.3", + "lru-cache": "^11.0.0", + "mnemonist": "^0.39.0", + "pandemonium": "^2.4.0", + "tree-sitter": "^0.21.0", + "tree-sitter-c": "^0.21.0", + "tree-sitter-c-sharp": "^0.21.0", + "tree-sitter-cpp": "^0.22.0", + "tree-sitter-go": "^0.21.0", + "tree-sitter-java": "^0.21.0", + "tree-sitter-javascript": "^0.21.0", + "tree-sitter-python": "^0.21.0", + "tree-sitter-rust": "^0.21.0", + "tree-sitter-typescript": "^0.21.0", + "typescript": "^5.4.5", + "uuid": "^13.0.0" + }, + "bin": { + "gitnexus": "dist/cli/index.js" }, "devDependencies": { - "@babel/types": "^7.28.5", - "@types/jszip": "^3.4.0", - "@types/node": "^24.10.1", - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "@types/react-syntax-highlighter": "^15.5.13", - "@vercel/node": "^5.5.16", - "@vitejs/plugin-react": "^5.1.0", - "tree-sitter-wasms": "^0.1.13", - "typescript": "^5.4.5", - "vite": "^5.2.0", - "vite-plugin-static-copy": "^3.1.4" - } - }, - "node_modules/@antfu/install-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", - "license": "MIT", - "dependencies": { - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.71.2", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", - "integrity": "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==", - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "^3.1.1" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "@types/cli-progress": "^3.11.6", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/node": "^20.0.0", + "@types/uuid": "^10.0.0", + "tsx": "^4.0.0" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", - "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", - "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.6" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@braintree/sanitize-url": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", - "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", - "license": "MIT" - }, - "node_modules/@cfworker/json-schema": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", - "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", - "license": "MIT" - }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", - "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/gast": "11.0.3", - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/@chevrotain/gast": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", - "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/@chevrotain/gast/node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/@chevrotain/regexp-to-ast": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", - "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/types": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", - "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/utils": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", - "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", - "license": "Apache-2.0" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@edge-runtime/format": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@edge-runtime/format/-/format-2.2.1.tgz", - "integrity": "sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=16" - } - }, - "node_modules/@edge-runtime/node-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/node-utils/-/node-utils-2.3.0.tgz", - "integrity": "sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=16" - } - }, - "node_modules/@edge-runtime/ponyfill": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@edge-runtime/ponyfill/-/ponyfill-2.4.2.tgz", - "integrity": "sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=16" - } - }, - "node_modules/@edge-runtime/primitives": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/primitives/-/primitives-4.1.0.tgz", - "integrity": "sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=16" - } - }, - "node_modules/@edge-runtime/vm": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/vm/-/vm-3.2.0.tgz", - "integrity": "sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@edge-runtime/primitives": "4.1.0" - }, - "engines": { - "node": ">=16" + "node": ">=18.0.0" } }, "node_modules/@emnapi/runtime": { @@ -576,9 +62,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", - "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -593,9 +79,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", - "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -610,9 +96,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", - "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -627,9 +113,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", - "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -644,9 +130,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", - "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -661,9 +147,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", - "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -678,9 +164,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", - "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -695,9 +181,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", - "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -712,9 +198,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", - "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -729,9 +215,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", - "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -746,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", - "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -763,9 +249,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", - "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -780,9 +266,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", - "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -797,9 +283,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", - "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -814,9 +300,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", - "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -831,9 +317,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", - "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -848,9 +334,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", - "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -865,9 +351,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", - "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -882,9 +368,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", - "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -899,9 +385,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", - "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -916,9 +402,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", - "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -933,9 +419,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", - "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], @@ -950,9 +436,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", - "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -967,9 +453,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", - "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -984,9 +470,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", - "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -1001,9 +487,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", - "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -1017,29 +503,22 @@ "node": ">=18" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", "license": "MIT", "engines": { - "node": ">=14" - } - }, - "node_modules/@google/generative-ai": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", - "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" } }, "node_modules/@huggingface/jinja": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.3.tgz", - "integrity": "sha512-asqfZ4GQS0hD876Uw4qiUb7Tr/V5Q+JZuo2L+BtdrD4U40QU58nIRq3ZSgAzJgT874VLjhGVacaYfrdpXtEvtA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.4.tgz", + "integrity": "sha512-VoQJywjpjy2D88Oj0BTHRuS8JCbUgoOg5t1UGgbtGh2fRia9Dx/k6Wf8FqrEWIvWK9fAkfJeeLB9fcSpCNPCpw==", "license": "MIT", "engines": { "node": ">=18" @@ -1057,23 +536,6 @@ "sharp": "^0.34.1" } }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, - "node_modules/@iconify/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", - "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^1.1.0", - "@iconify/types": "^2.0.0", - "mlly": "^1.8.0" - } - }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", @@ -1543,7 +1005,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, "license": "MIT", "engines": { "node": "20 || >=22" @@ -1553,7 +1014,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dev": true, "license": "MIT", "dependencies": { "@isaacs/balanced-match": "^4.0.1" @@ -1562,6 +1022,23 @@ "node": "20 || >=22" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -1574,390 +1051,328 @@ "node": ">=18.0.0" } }, - "node_modules/@isomorphic-git/idb-keyval": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz", - "integrity": "sha512-r8/AdpiS0/WJCNR/t/gsgL+M8NMVj/ek7s60uz3LmpCaTF2mEVlZJlB01ZzalgYzRLXwSPC92o+pdzjM7PN/pA==", - "license": "Apache-2.0" - }, - "node_modules/@isomorphic-git/lightning-fs": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@isomorphic-git/lightning-fs/-/lightning-fs-4.6.2.tgz", - "integrity": "sha512-RS/oa1UBnoUFe56bsjOEgoUUReYKQzYUlQnbERRRNv9s9KmjyWuuylPV+YgsWirR2oONKaipWYMebVQ8SAe55Q==", + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", + "integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==", "license": "MIT", "dependencies": { - "@isomorphic-git/idb-keyval": "3.3.2", - "isomorphic-textencoder": "1.0.1", - "just-debounce-it": "1.1.0", - "just-once": "1.1.0" - }, - "bin": { - "superblocktxt": "src/superblocktxt.js" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@langchain/anthropic": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-1.3.10.tgz", - "integrity": "sha512-VXq5fsEJ4FB5XGrnoG+bfm0I7OlmYLI4jZ6cX9RasyqhGo9wcDyKw1+uEQ1H7Og7jWrTa1bfXCun76wttewJnw==", - "license": "MIT", - "dependencies": { - "@anthropic-ai/sdk": "^0.71.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "1.1.15" - } - }, - "node_modules/@langchain/core": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.15.tgz", - "integrity": "sha512-b8RN5DkWAmDAlMu/UpTZEluYwCLpm63PPWniRKlE8ie3KkkE7IuMQ38pf4kV1iaiI+d99BEQa2vafQHfCujsRA==", - "license": "MIT", - "dependencies": { - "@cfworker/json-schema": "^4.0.2", - "ansi-styles": "^5.0.0", - "camelcase": "6", - "decamelize": "1.2.0", - "js-tiktoken": "^1.0.12", - "langsmith": ">=0.4.0 <1.0.0", - "mustache": "^4.2.0", - "p-queue": "^6.6.2", - "uuid": "^10.0.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@langchain/core/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@langchain/google-genai": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-2.1.10.tgz", - "integrity": "sha512-OpiBr2OUzB9Pg20mjLId+vfxJvYurc8TzbElaM/d6KE7aE8DiKCEOuQn5ZSgHTVzZV2g++lcJXw6iZlso4SORA==", - "license": "MIT", - "dependencies": { - "@google/generative-ai": "^0.24.0", - "uuid": "^11.1.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "1.1.15" - } - }, - "node_modules/@langchain/google-genai/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/@langchain/langgraph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.1.0.tgz", - "integrity": "sha512-3n1GL0ZTtr57ZwbYvbi4Th26fwiGogmpFn8OA8UXEpBM2HcpGwcv1+c8YSBJF4XRjlcCzIlXtY+DyrNsvinc6g==", - "license": "MIT", - "dependencies": { - "@langchain/langgraph-checkpoint": "^1.0.0", - "@langchain/langgraph-sdk": "~1.5.4", - "uuid": "^10.0.0" + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": "^1.0.1", - "zod": "^3.25.32 || ^4.2.0", - "zod-to-json-schema": "^3.x" + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { - "zod-to-json-schema": { + "@cfworker/json-schema": { "optional": true + }, + "zod": { + "optional": false } } }, - "node_modules/@langchain/langgraph-checkpoint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz", - "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==", + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "uuid": "^10.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" }, - "peerDependencies": { - "@langchain/core": "^1.0.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@langchain/langgraph-sdk": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.5.4.tgz", - "integrity": "sha512-eSYqG875c2qvcPwdvBwQH0niTZxt6roMGc2dAWBqCbWCUiUL0X4ftYHg2OqOelsrNE3SO6faLr/m0LIPc9hDwg==", - "license": "MIT", - "dependencies": { - "p-queue": "^9.0.1", - "p-retry": "^7.1.1", - "uuid": "^13.0.0" - }, - "peerDependencies": { - "@langchain/core": "^1.0.1", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@langchain/core": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", - "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^7.0.0" - }, "engines": { - "node": ">=20" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/langgraph/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@langchain/ollama": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@langchain/ollama/-/ollama-1.2.0.tgz", - "integrity": "sha512-OinxIhssKXdDQKnQoBF4TQTMBuMMV5OcNPk4Zze8UjcaSOGngn3CAI1FVbBxl0bTG5ov61w4AoWWsUwOwiSJFw==", - "license": "MIT", - "dependencies": { - "ollama": "^0.6.3", - "uuid": "^10.0.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.0.0" - } - }, - "node_modules/@langchain/ollama/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@langchain/openai": { + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.2.2.tgz", - "integrity": "sha512-ByGtj9nJlyL2UPR7BAxtM34g8JA0qEfDKZq7ZisLW23ju+da1ZRAKogoEqoEHHSxl5fAt2LXcydsIYx0qgCDgg==", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^6.10.0", - "zod": "^3.25.76 || ^4" - }, "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.0.0" + "node": ">=6.6.0" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" + "ms": "^2.1.3" }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@mermaid-js/parser": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", - "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "dependencies": { - "langium": "3.3.1" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 8" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 8" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/@protobufjs/aspromise": { @@ -2024,1651 +1439,244 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.53", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", - "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-virtual": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", - "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sigma/edge-curve": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigma/edge-curve/-/edge-curve-3.1.0.tgz", - "integrity": "sha512-OFWkfAXEsm+X8l1K4K49cC0psB0gQ+gqxKA08HG5piNPdzrDZ5gG9Gza6htZ5AirOVwd/4/uq/gPpD5En+H+3Q==", - "license": "MIT", - "peerDependencies": { - "sigma": ">=3.0.0-beta.10" - } - }, - "node_modules/@swc/core": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz", - "integrity": "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.25" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.8", - "@swc/core-darwin-x64": "1.15.8", - "@swc/core-linux-arm-gnueabihf": "1.15.8", - "@swc/core-linux-arm64-gnu": "1.15.8", - "@swc/core-linux-arm64-musl": "1.15.8", - "@swc/core-linux-x64-gnu": "1.15.8", - "@swc/core-linux-x64-musl": "1.15.8", - "@swc/core-win32-arm64-msvc": "1.15.8", - "@swc/core-win32-ia32-msvc": "1.15.8", - "@swc/core-win32-x64-msvc": "1.15.8" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz", - "integrity": "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz", - "integrity": "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz", - "integrity": "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz", - "integrity": "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz", - "integrity": "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz", - "integrity": "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz", - "integrity": "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz", - "integrity": "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz", - "integrity": "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz", - "integrity": "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, - "node_modules/@swc/types": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", - "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/@swc/wasm": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.15.8.tgz", - "integrity": "sha512-RG2BxGbbsjtddFCo1ghKH6A/BMXbY1eMBfpysV0lJMCpI4DZOjW1BNBnxvBt7YsYmlJtmy5UXIg9/4ekBTFFaQ==", - "license": "Apache-2.0" - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", - "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.1", - "lightningcss": "1.30.2", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.18" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", - "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-x64": "4.1.18", - "@tailwindcss/oxide-freebsd-x64": "4.1.18", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-x64-musl": "4.1.18", - "@tailwindcss/oxide-wasm32-wasi": "4.1.18", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", - "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", - "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", - "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", - "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", - "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", - "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", - "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", - "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.0", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", - "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", - "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", - "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.18", - "@tailwindcss/oxide": "4.1.18", - "tailwindcss": "4.1.18" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.11.1.tgz", - "integrity": "sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==", + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", + "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "^3.2.7", - "minimatch": "^3.0.4", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" + "@types/node": "*" } }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@types/node": "*" } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0" + "@types/node": "*" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" } }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@types/d3": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", - "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/d3-axis": "*", - "@types/d3-brush": "*", - "@types/d3-chord": "*", - "@types/d3-color": "*", - "@types/d3-contour": "*", - "@types/d3-delaunay": "*", - "@types/d3-dispatch": "*", - "@types/d3-drag": "*", - "@types/d3-dsv": "*", - "@types/d3-ease": "*", - "@types/d3-fetch": "*", - "@types/d3-force": "*", - "@types/d3-format": "*", - "@types/d3-geo": "*", - "@types/d3-hierarchy": "*", - "@types/d3-interpolate": "*", - "@types/d3-path": "*", - "@types/d3-polygon": "*", - "@types/d3-quadtree": "*", - "@types/d3-random": "*", - "@types/d3-scale": "*", - "@types/d3-scale-chromatic": "*", - "@types/d3-selection": "*", - "@types/d3-shape": "*", - "@types/d3-time": "*", - "@types/d3-time-format": "*", - "@types/d3-timer": "*", - "@types/d3-transition": "*", - "@types/d3-zoom": "*" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-axis": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", - "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-brush": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", - "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-chord": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", - "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-contour": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", - "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", - "license": "MIT" - }, - "node_modules/@types/d3-dispatch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", - "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", - "license": "MIT" - }, - "node_modules/@types/d3-drag": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", - "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-dsv": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", - "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-fetch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", - "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", - "license": "MIT", - "dependencies": { - "@types/d3-dsv": "*" - } - }, - "node_modules/@types/d3-force": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", - "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", - "license": "MIT" - }, - "node_modules/@types/d3-format": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", - "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", - "license": "MIT" - }, - "node_modules/@types/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-hierarchy": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", - "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-polygon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", - "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", - "license": "MIT" - }, - "node_modules/@types/d3-quadtree": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", - "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", - "license": "MIT" - }, - "node_modules/@types/d3-random": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", - "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", - "license": "MIT" - }, - "node_modules/@types/d3-selection": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "license": "MIT" - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-time-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", - "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/d3-transition": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-zoom": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", - "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "license": "MIT", - "dependencies": { - "@types/d3-interpolate": "*", - "@types/d3-selection": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, "license": "MIT" }, - "node_modules/@types/jszip": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@types/jszip/-/jszip-3.4.0.tgz", - "integrity": "sha512-GFHqtQQP3R4NNuvZH3hNCYD0NbyBZ42bkN7kO3NDrU/SnvIZWMS8Bp38XCsRKBT5BXvgm0y1zqpZWp/ZkRzBzg==", + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true, - "license": "MIT", - "dependencies": { - "jszip": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", - "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~6.21.0" } }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "license": "MIT" - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } + "license": "MIT" }, - "node_modules/@types/react-syntax-highlighter": { - "version": "15.5.13", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", - "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/react": "*" + "@types/node": "*" } }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, "license": "MIT", - "optional": true + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@vercel/build-utils": { - "version": "13.2.11", - "resolved": "https://registry.npmjs.org/@vercel/build-utils/-/build-utils-13.2.11.tgz", - "integrity": "sha512-jbsg78iS8SLpOkLw378bBLchmzeQ+YtPnztMMuEFBORjY1G4lDxiStMacD3xp5HImCAl1wz4dNV4I8jHKd/3Tg==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@vercel/error-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@vercel/error-utils/-/error-utils-2.0.3.tgz", - "integrity": "sha512-CqC01WZxbLUxoiVdh9B/poPbNpY9U+tO1N9oWHwTl5YAZxcqXmmWJ8KNMFItJCUUWdY3J3xv8LvAuQv2KZ5YdQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@vercel/nft": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.1.1.tgz", - "integrity": "sha512-mKMGa7CEUcXU75474kOeqHbtvK1kAcu4wiahhmlUenB5JbTQB8wVlDI8CyHR3rpGo0qlzoRWqcDzI41FUoBJCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^13.0.0", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.2", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@vercel/node": { - "version": "5.5.23", - "resolved": "https://registry.npmjs.org/@vercel/node/-/node-5.5.23.tgz", - "integrity": "sha512-dDJtroLF4D/H9vRMt/x/qI2bKujMOPbk6aIqRKI9WXddngjKziuHxsjcF3zEm5YXGUYDSC2lEVEFrXPbbP+hhw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@edge-runtime/node-utils": "2.3.0", - "@edge-runtime/primitives": "4.1.0", - "@edge-runtime/vm": "3.2.0", - "@types/node": "16.18.11", - "@vercel/build-utils": "13.2.11", - "@vercel/error-utils": "2.0.3", - "@vercel/nft": "1.1.1", - "@vercel/static-config": "3.1.2", - "async-listen": "3.0.0", - "cjs-module-lexer": "1.2.3", - "edge-runtime": "2.5.9", - "es-module-lexer": "1.4.1", - "esbuild": "0.27.0", - "etag": "1.8.1", - "mime-types": "2.1.35", - "node-fetch": "2.6.9", - "path-to-regexp": "6.1.0", - "path-to-regexp-updated": "npm:path-to-regexp@6.3.0", - "ts-morph": "12.0.0", - "ts-node": "10.9.1", - "typescript": "4.9.5", - "typescript5": "npm:typescript@5.9.3", - "undici": "5.28.4" - } - }, - "node_modules/@vercel/node/node_modules/@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", "dev": true, "license": "MIT" }, - "node_modules/@vercel/node/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/@vercel/static-config": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vercel/static-config/-/static-config-3.1.2.tgz", - "integrity": "sha512-2d+TXr6K30w86a+WbMbGm2W91O0UzO5VeemZYBBUJbCjk/5FLLGIi8aV6RS2+WmaRvtcqNTn2pUA7nCOK3bGcQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "ajv": "8.6.3", - "json-schema-to-ts": "1.6.4", - "ts-morph": "12.0.0" - } - }, - "node_modules/@vercel/static-config/node_modules/json-schema-to-ts": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-1.6.4.tgz", - "integrity": "sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==", - "dev": true, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.6", - "ts-toolbelt": "^6.15.5" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", - "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.5", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.53", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" + "node": ">= 0.6" } }, "node_modules/ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">= 8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-listen": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.0.tgz", - "integrity": "sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/async-lock": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "license": "MIT" - }, - "node_modules/async-sema": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", - "dev": true, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, "node_modules/asynckit": { @@ -3677,25 +1685,10 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -3703,74 +1696,43 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.15", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz", - "integrity": "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "file-uri-to-path": "1.0.0" + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/boolean": { @@ -3780,104 +1742,13 @@ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, "node_modules/call-bind-apply-helpers": { @@ -3909,75 +1780,92 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "string-width": "^4.2.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, - "node_modules/chalk/node_modules/ansi-styles": { + "node_modules/cli-progress/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cli-progress/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -3992,130 +1880,102 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chevrotain": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", - "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/cst-dts-gen": "11.0.3", - "@chevrotain/gast": "11.0.3", - "@chevrotain/regexp-to-ast": "11.0.3", - "@chevrotain/types": "11.0.3", - "@chevrotain/utils": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/chevrotain-allstar": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", - "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21" - }, - "peerDependencies": { - "chevrotain": "^11.0.0" - } - }, - "node_modules/chevrotain/node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cmake-js": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.4.0.tgz", + "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", + "license": "MIT", + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "cmake-js": "bin/cmake-js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", "engines": { - "node": ">=18" + "node": ">= 14.15.0" } }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true, - "license": "MIT" + "node_modules/cmake-js/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - "node_modules/clean-git-ref": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", - "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", - "license": "Apache-2.0" - }, - "node_modules/code-block-writer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-10.1.1.tgz", - "integrity": "sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==", - "dev": true, + "node_modules/cmake-js/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/color-convert": { @@ -4136,6 +1996,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4148,677 +2017,104 @@ "node": ">= 0.8" } }, - "node_modules/comlink": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", - "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", - "license": "Apache-2.0" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=18" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/console-table-printer": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", - "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", - "license": "MIT", - "dependencies": { - "simple-wcswidth": "^1.1.2" - } - }, - "node_modules/convert-hrtime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-3.0.0.tgz", - "integrity": "sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "license": "MIT", - "dependencies": { - "layout-base": "^1.0.0" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/cytoscape": { - "version": "3.33.1", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", - "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "license": "MIT", - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "license": "MIT", - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "license": "MIT", - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", - "license": "MIT" - }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "license": "ISC", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "license": "ISC", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "license": "ISC", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "license": "ISC", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "license": "ISC", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "license": "ISC", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "license": "ISC" }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dagre-d3-es": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", - "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { - "d3": "^7.9.0", - "lodash-es": "^4.17.21" + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "ms": "2.0.0" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0.0" } }, "node_modules/define-data-property": { @@ -4855,15 +2151,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4873,13 +2160,29 @@ "node": ">=0.4.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/detect-libc": { @@ -4897,44 +2200,6 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT" }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff3": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", - "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", - "license": "MIT" - }, - "node_modules/dompurify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4949,65 +2214,31 @@ "node": ">= 0.4" } }, - "node_modules/edge-runtime": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/edge-runtime/-/edge-runtime-2.5.9.tgz", - "integrity": "sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@edge-runtime/format": "2.2.1", - "@edge-runtime/ponyfill": "2.4.2", - "@edge-runtime/vm": "3.2.0", - "async-listen": "3.0.1", - "mri": "1.2.0", - "picocolors": "1.0.0", - "pretty-ms": "7.0.1", - "signal-exit": "4.0.2", - "time-span": "4.0.0" - }, - "bin": { - "edge-runtime": "dist/cli/index.js" - }, - "engines": { - "node": ">=16" - } + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, - "node_modules/edge-runtime/node_modules/async-listen": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.1.tgz", - "integrity": "sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==", - "dev": true, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { - "node": ">= 14" - } - }, - "node_modules/edge-runtime/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" + "node": ">= 0.8" } }, "node_modules/es-define-property": { @@ -5028,13 +2259,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true, - "license": "MIT" - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -5069,9 +2293,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5082,44 +2306,49 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.0", - "@esbuild/android-arm": "0.27.0", - "@esbuild/android-arm64": "0.27.0", - "@esbuild/android-x64": "0.27.0", - "@esbuild/darwin-arm64": "0.27.0", - "@esbuild/darwin-x64": "0.27.0", - "@esbuild/freebsd-arm64": "0.27.0", - "@esbuild/freebsd-x64": "0.27.0", - "@esbuild/linux-arm": "0.27.0", - "@esbuild/linux-arm64": "0.27.0", - "@esbuild/linux-ia32": "0.27.0", - "@esbuild/linux-loong64": "0.27.0", - "@esbuild/linux-mips64el": "0.27.0", - "@esbuild/linux-ppc64": "0.27.0", - "@esbuild/linux-riscv64": "0.27.0", - "@esbuild/linux-s390x": "0.27.0", - "@esbuild/linux-x64": "0.27.0", - "@esbuild/netbsd-arm64": "0.27.0", - "@esbuild/netbsd-x64": "0.27.0", - "@esbuild/openbsd-arm64": "0.27.0", - "@esbuild/openbsd-x64": "0.27.0", - "@esbuild/openharmony-arm64": "0.27.0", - "@esbuild/sunos-x64": "0.27.0", - "@esbuild/win32-arm64": "0.27.0", - "@esbuild/win32-ia32": "0.27.0", - "@esbuild/win32-x64": "0.27.0" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5132,57 +2361,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -5192,101 +2379,126 @@ "node": ">=0.8.x" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-text-encoding": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", - "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", - "license": "Apache-2.0" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" } - } + ], + "license": "BSD-3-Clause" }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, "node_modules/flatbuffers": { @@ -5315,19 +2527,20 @@ } } }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { - "is-callable": "^1.2.7" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/form-data": { @@ -5346,18 +2559,67 @@ "node": ">= 6" } }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { - "node": ">=0.4.x" + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5377,14 +2639,80 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { @@ -5424,17 +2752,35 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "node_modules/get-tsconfig": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.1.tgz", + "integrity": "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==", "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "license": "BlueOak-1.0.0", "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, "engines": { "node": "20 || >=22" }, @@ -5442,19 +2788,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", @@ -5507,32 +2840,18 @@ "license": "ISC" }, "node_modules/graphology": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.26.0.tgz", - "integrity": "sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", + "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", "license": "MIT", "dependencies": { - "events": "^3.3.0" + "events": "^3.3.0", + "obliterator": "^2.0.2" }, "peerDependencies": { "graphology-types": ">=0.24.0" } }, - "node_modules/graphology-communities-louvain": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/graphology-communities-louvain/-/graphology-communities-louvain-2.0.2.tgz", - "integrity": "sha512-zt+2hHVPYxjEquyecxWXoUoIuN/UvYzsvI7boDdMNz0rRvpESQ7+e+Ejv6wK7AThycbZXuQ6DkG8NPMCq6XwoA==", - "license": "MIT", - "dependencies": { - "graphology-indices": "^0.17.0", - "graphology-utils": "^2.4.4", - "mnemonist": "^0.39.0", - "pandemonium": "^2.4.1" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, "node_modules/graphology-indices": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/graphology-indices/-/graphology-indices-0.17.0.tgz", @@ -5546,42 +2865,6 @@ "graphology-types": ">=0.20.0" } }, - "node_modules/graphology-layout-force": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/graphology-layout-force/-/graphology-layout-force-0.2.4.tgz", - "integrity": "sha512-NYZz0YAnDkn5pkm30cvB0IScFoWGtbzJMrqaiH070dYlYJiag12Oc89dbVfaMaVR/w8DMIKxn/ix9Bqj+Umm9Q==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.4.2" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, - "node_modules/graphology-layout-forceatlas2": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/graphology-layout-forceatlas2/-/graphology-layout-forceatlas2-0.10.1.tgz", - "integrity": "sha512-ogzBeF1FvWzjkikrIFwxhlZXvD2+wlY54lqhsrWprcdPjopM2J9HoMweUmIgwaTvY4bUYVimpSsOdvDv1gPRFQ==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.1.0" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, - "node_modules/graphology-layout-noverlap": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/graphology-layout-noverlap/-/graphology-layout-noverlap-0.4.2.tgz", - "integrity": "sha512-13WwZSx96zim6l1dfZONcqLh3oqyRcjIBsqz2c2iJ3ohgs3605IDWjldH41Gnhh462xGB1j6VGmuGhZ2FKISXA==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.3.0" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, "node_modules/graphology-types": { "version": "0.24.8", "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", @@ -5604,21 +2887,6 @@ "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", "license": "ISC" }, - "node_modules/hachure-fill": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", - "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -5658,6 +2926,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5670,1030 +2944,171 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", + "peer": true, "engines": { - "node": "*" + "node": ">=16.9.0" } }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">= 14" + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.10" } }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-network-error": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", - "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-observable": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", - "integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isomorphic-git": { - "version": "1.36.1", - "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.36.1.tgz", - "integrity": "sha512-fC8SRT8MwoaXDK8G4z5biPEbqf2WyEJUb2MJ2ftSd39/UIlsnoZxLGux+lae0poLZO4AEcx6aUVOh5bV+P8zFA==", - "license": "MIT", - "dependencies": { - "async-lock": "^1.4.1", - "clean-git-ref": "^2.0.1", - "crc-32": "^1.2.0", - "diff3": "0.0.3", - "ignore": "^5.1.4", - "minimisted": "^2.0.0", - "pako": "^1.0.10", - "pify": "^4.0.1", - "readable-stream": "^4.0.0", - "sha.js": "^2.4.12", - "simple-get": "^4.0.1" - }, - "bin": { - "isogit": "cli.cjs" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/isomorphic-textencoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-textencoder/-/isomorphic-textencoder-1.0.1.tgz", - "integrity": "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ==", - "license": "MIT", - "dependencies": { - "fast-text-encoding": "^1.0.0" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tiktoken": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", - "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.5.1" - } - }, - "node_modules/js-tokens": { + "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=6" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/json-schema-to-ts": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "ts-algebra": "^2.0.0" - }, - "engines": { - "node": ">=16" + "funding": { + "url": "https://github.com/sponsors/panva" } }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "license": "ISC" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jszip/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/just-debounce-it": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/just-debounce-it/-/just-debounce-it-1.1.0.tgz", - "integrity": "sha512-87Nnc0qZKgBZuhFZjYVjSraic0x7zwjhaTMrCKlj0QYKH6lh0KbFzVnfu6LHan03NO7J8ygjeBeD0epejn5Zcg==", - "license": "MIT" - }, - "node_modules/just-once": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/just-once/-/just-once-1.1.0.tgz", - "integrity": "sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==", - "license": "MIT" - }, - "node_modules/katex": { - "version": "0.16.27", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", - "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" - }, - "node_modules/kuzu-wasm": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/kuzu-wasm/-/kuzu-wasm-0.11.3.tgz", - "integrity": "sha512-+bLOqXgYZJJ2dHJG1y9LTLyb9ZB73eLxErRZahZz2rPokfIdyLaktTJFzJH7wX39hgyukKn8QxeRNobH6gl27g==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "dependencies": { - "threads": "^1.7.0", - "tiny-worker": "^2.3.0", - "uuid": "^11.0.3" - } - }, - "node_modules/kuzu-wasm/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/langchain": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.2.10.tgz", - "integrity": "sha512-9uVxOJE/RTECvNutQfOLwH7f6R9mcq0G/IMHwA2eptDA86R/Yz2zWMz4vARVFPxPrdSJ9nJFDPAqRQlRFwdHBw==", - "license": "MIT", - "dependencies": { - "@langchain/langgraph": "^1.0.0", - "@langchain/langgraph-checkpoint": "^1.0.0", - "langsmith": ">=0.4.0 <1.0.0", - "uuid": "^10.0.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "1.1.15" - } - }, - "node_modules/langchain/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/langium": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", - "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", - "license": "MIT", - "dependencies": { - "chevrotain": "~11.0.3", - "chevrotain-allstar": "~0.3.0", - "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.0.8" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/langsmith": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.4.7.tgz", - "integrity": "sha512-Esv5g/J8wwRwbGQr10PB9+bLsNk0mWbrXc7nnEreQDhh0azbU57I7epSnT7GC4sS4EOWavhbxk+6p8PTXtreHw==", - "license": "MIT", - "dependencies": { - "@types/uuid": "^10.0.0", - "chalk": "^4.1.2", - "console-table-printer": "^2.12.1", - "p-queue": "^6.6.2", - "semver": "^7.6.3", - "uuid": "^10.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "*", - "@opentelemetry/exporter-trace-otlp-proto": "*", - "@opentelemetry/sdk-trace-base": "*", - "openai": "*" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-proto": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - }, - "openai": { - "optional": true - } - } - }, - "node_modules/langsmith/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", - "license": "MIT" - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lightningcss": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "universalify": "^2.0.0" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" + "graceful-fs": "^4.1.6" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node_modules/kuzu": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/kuzu/-/kuzu-0.11.3.tgz", + "integrity": "sha512-4+hD3Y+YMV3e0uiqTv1/GUal47D04l8qluw1WFWg8Nx3k7rLsHG1Pmq9WHIOlf1742svxQvTYQiuY6oS1qxAZA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "cmake-js": "^7.3.0", + "node-addon-api": "^6.0.0" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash-es": { - "version": "4.17.22", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", - "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", - "license": "MIT" - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, - "node_modules/lucide-react": { - "version": "0.562.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", - "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/marked": { - "version": "16.4.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", - "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -6715,927 +3130,52 @@ "node": ">= 0.4" } }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { - "node": ">=12" - }, + "node": ">= 0.6" + } + }, + "node_modules/memory-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", + "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.6" } }, - "node_modules/mermaid": { - "version": "11.12.2", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz", - "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", - "license": "MIT", - "dependencies": { - "@braintree/sanitize-url": "^7.1.1", - "@iconify/utils": "^3.0.1", - "@mermaid-js/parser": "^0.6.3", - "@types/d3": "^7.4.3", - "cytoscape": "^3.29.3", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.2.0", - "d3": "^7.9.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.13", - "dayjs": "^1.11.18", - "dompurify": "^3.2.5", - "katex": "^0.16.22", - "khroma": "^2.1.0", - "lodash-es": "^4.17.21", - "marked": "^16.2.1", - "roughjs": "^4.6.6", - "stylis": "^4.3.6", - "ts-dedent": "^2.2.0", - "uuid": "^11.1.0" - } - }, - "node_modules/mermaid/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "mime": "cli.js" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=4" } }, "node_modules/mime-db": { @@ -7659,23 +3199,10 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" @@ -7696,15 +3223,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimisted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", - "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - } - }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -7714,29 +3232,35 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minisearch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", - "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", - "license": "MIT" - }, "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "license": "MIT", "dependencies": { - "minipass": "^7.1.2" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -7745,18 +3269,6 @@ "node": ">=10" } }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" - } - }, "node_modules/mnemonist": { "version": "0.39.8", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", @@ -7766,75 +3278,37 @@ "obliterator": "^2.0.1" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", - "bin": { - "mustache": "bin/mustache" + "engines": { + "node": ">= 0.6" } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } + "node_modules/node-api-headers": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", + "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", + "license": "MIT" }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "dev": true, "license": "MIT", "bin": { "node-gyp-build": "bin.js", @@ -7842,39 +3316,43 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "dev": true, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -7890,19 +3368,16 @@ "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", "license": "MIT" }, - "node_modules/observable-fns": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", - "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==", - "license": "MIT" - }, - "node_modules/ollama": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.6.3.tgz", - "integrity": "sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg==", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { - "whatwg-fetch": "^3.6.20" + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/once": { @@ -7937,6 +3412,52 @@ "tar": "^7.0.1" } }, + "node_modules/onnxruntime-node/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/onnxruntime-node/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/onnxruntime-node/node_modules/tar": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/onnxruntime-node/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/onnxruntime-web": { "version": "1.22.0-dev.20250409-89f8206ba4", "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.22.0-dev.20250409-89f8206ba4.tgz", @@ -7957,103 +3478,11 @@ "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==", "license": "MIT" }, - "node_modules/openai": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.16.0.tgz", - "integrity": "sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==", - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", - "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", - "license": "MIT", - "dependencies": { - "is-network-error": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-manager-detector": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", - "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", - "license": "MIT" - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, "node_modules/pandemonium": { "version": "2.4.1", @@ -8064,59 +3493,28 @@ "mnemonist": "^0.39.2" } }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-data-parser": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", - "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", - "license": "MIT" + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/path-scurry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -8130,63 +3528,18 @@ } }, "node_modules/path-to-regexp": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", - "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==", - "dev": true, + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, - "node_modules/path-to-regexp-updated": { - "name": "path-to-regexp", - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "node": ">=16.20.0" } }, "node_modules/platform": { @@ -8195,109 +3548,6 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", "license": "MIT" }, - "node_modules/points-on-curve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", - "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", - "license": "MIT" - }, - "node_modules/points-on-path": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", - "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", - "license": "MIT", - "dependencies": { - "path-data-parser": "0.1.0", - "points-on-curve": "0.2.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", @@ -8322,292 +3572,135 @@ "node": ">=12.0.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { - "node": ">=6" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" + "url": "https://opencollective.com/express" } }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-syntax-highlighter": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", - "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", - "license": "MIT", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { - "@babel/runtime": "^7.28.4", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^5.0.0" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, - "engines": { - "node": ">= 16.20.2" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, - "node_modules/react-zoom-pan-pinch": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz", - "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==", - "license": "MIT", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" + "bin": { + "rc": "cli.js" } }, "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/refractor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^9.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/roarr": { @@ -8627,97 +3720,54 @@ "node": ">=8.0" } }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "license": "Unlicense" - }, - "node_modules/rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/roughjs": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", - "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", - "license": "MIT", - "dependencies": { - "hachure-fill": "^0.5.2", - "path-data-parser": "^0.1.0", - "points-on-curve": "^0.2.0", - "points-on-path": "^0.2.1" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" } }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/safe-buffer": { "version": "5.2.1", @@ -8745,15 +3795,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -8772,6 +3813,36 @@ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "license": "MIT" }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -8787,48 +3858,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/sharp": { "version": "0.34.5", @@ -8874,21 +3929,103 @@ "@img/sharp-win32-x64": "0.34.5" } }, - "node_modules/sigma": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sigma/-/sigma-3.0.2.tgz", - "integrity": "sha512-/BUbeOwPGruiBOm0YQQ6ZMcLIZ6tf/W+Jcm7dxZyAX0tK3WP9/sq7/NAWBxPIxVahdGjCJoGwej0Gdrv0DxlQQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { - "events": "^3.3.0", - "graphology-utils": "^2.5.2" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", "engines": { "node": ">=14" @@ -8897,82 +4034,21 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-wcswidth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", - "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "license": "BSD-3-Clause" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8982,300 +4058,412 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz", - "integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/threads": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", - "integrity": "sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==", - "license": "MIT", - "dependencies": { - "callsites": "^3.1.0", - "debug": "^4.2.0", - "is-observable": "^2.1.0", - "observable-fns": "^0.6.1" - }, - "funding": { - "url": "https://github.com/andywer/threads.js?sponsor=1" - }, - "optionalDependencies": { - "tiny-worker": ">= 2" - } - }, - "node_modules/time-span": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/time-span/-/time-span-4.0.0.tgz", - "integrity": "sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "convert-hrtime": "^3.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tiny-worker": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", - "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", - "license": "BSD-3-Clause", - "dependencies": { - "esm": "^3.2.25" - } - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=8" } }, - "node_modules/to-buffer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", - "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", - "license": "MIT", - "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/to-regex-range": { + "node_modules/string-width-cjs/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tree-sitter-wasms": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/tree-sitter-wasms/-/tree-sitter-wasms-0.1.13.tgz", - "integrity": "sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "tree-sitter-wasms": "^0.1.11" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-algebra": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT" - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=6.10" + "node": ">=8" } }, - "node_modules/ts-morph": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-12.0.0.tgz", - "integrity": "sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ts-morph/common": "~0.11.0", - "code-block-writer": "^10.1.1" - } + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" + "ansi-regex": "^5.0.1" }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar 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 exhorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-sitter": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz", + "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.0" + } + }, + "node_modules/tree-sitter-c": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/tree-sitter-c/-/tree-sitter-c-0.21.4.tgz", + "integrity": "sha512-IahxFIhXiY15SUlrt2upBiKSBGdOaE1fjKLK1Ik5zxqGHf6T1rvr3IJrovbsE5sXhypx7Hnmf50gshsppaIihA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.1" }, "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "tree-sitter": "^0.21.0" }, "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { + "tree_sitter": { "optional": true } } }, - "node_modules/ts-toolbelt": { - "version": "6.15.5", - "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz", - "integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==", - "dev": true, - "license": "Apache-2.0" + "node_modules/tree-sitter-c-sharp": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/tree-sitter-c-sharp/-/tree-sitter-c-sharp-0.21.3.tgz", + "integrity": "sha512-TVsl5EhmqetO/mhzDPVnMK6TPFnpNMKP0OTNuAQIprshk5Hx672ODRxoIoG5WqvUUlsnBu8J0zmn35hmJqelsA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-c-sharp/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-c/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-cpp": { + "version": "0.22.3", + "resolved": "https://registry.npmjs.org/tree-sitter-cpp/-/tree-sitter-cpp-0.22.3.tgz", + "integrity": "sha512-p7w5903L/koqTQFVDwyyX0vjioxoZu2G4zT2ZHVG8DvLQbWN6OjNAqfMsCi+WdVkfMgU+7j06hS8i3j6Q0sPNQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.1" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-cpp/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-go": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/tree-sitter-go/-/tree-sitter-go-0.21.2.tgz", + "integrity": "sha512-aMFwjsB948nWhURiIxExK8QX29JYKs96P/IfXVvluVMRJZpL04SREHsdOZHYqJr1whkb7zr3/gWHqqvlkczmvw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.1.0", + "node-gyp-build": "^4.8.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-go/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-java": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/tree-sitter-java/-/tree-sitter-java-0.21.0.tgz", + "integrity": "sha512-CKJiTo1uc3SUsgEcaZgufGx8my6dzihy8JR/JsJH40Tj3uSe2/eFLk+0q+fpbosGAyY4YiXJtEoFB2O4bS2yOw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.0" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-java/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-javascript": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/tree-sitter-javascript/-/tree-sitter-javascript-0.21.4.tgz", + "integrity": "sha512-Lrk8yahebwrwc1sWJE9xPcz1OnnqiEV7Dh5fbN6EN3wNAdu9r06HpTqLqDwUUbnG4EB46Sfk+FJFAOldfoKLOw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.1" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-javascript/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter-python": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/tree-sitter-python/-/tree-sitter-python-0.21.0.tgz", + "integrity": "sha512-IUKx7JcTVbByUx1iHGFS/QsIjx7pqwTMHL9bl/NGyhyyydbfNrpruo2C7W6V4KZrbkkCOlX8QVrCoGOFW5qecg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.1.0", + "node-gyp-build": "^4.8.0" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-python/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/tree-sitter-rust": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/tree-sitter-rust/-/tree-sitter-rust-0.21.0.tgz", + "integrity": "sha512-unVr73YLn3VC4Qa/GF0Nk+Wom6UtI526p5kz9Rn2iZSqwIFedyCZ3e0fKCEmUJLIPGrTb/cIEdu3ZUNGzfZx7A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.1.0", + "node-gyp-build": "^4.8.0" + }, + "peerDependencies": { + "tree-sitter": "^0.21.1" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-rust/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/tree-sitter-typescript": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.21.2.tgz", + "integrity": "sha512-/RyNK41ZpkA8PuPZimR6pGLvNR1p0ibRUJwwQn4qAjyyLEIQD/BNlwS3NSxWtGsAWZe9gZ44VK1mWx2+eQVldg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-typescript/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/tslib": { "version": "2.8.1", @@ -9284,6 +4472,26 @@ "license": "0BSD", "optional": true }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", @@ -9296,25 +4504,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -9324,173 +4530,35 @@ "node": ">=14.17" } }, - "node_modules/typescript5": { - "name": "typescript", - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "license": "MIT" - }, - "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.8" } }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "license": "MIT" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -9498,6 +4566,15 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/uuid": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", @@ -9511,660 +4588,169 @@ "uuid": "dist-node/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.8" } }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "isexe": "^2.0.0" }, "bin": { - "vite": "bin/vite.js" + "node-which": "bin/node-which" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/vite-plugin-static-copy": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.4.tgz", - "integrity": "sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw==", - "dev": true, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { - "chokidar": "^3.6.0", - "p-map": "^7.0.3", - "picocolors": "^1.1.1", - "tinyglobby": "^0.2.15" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/vite-plugin-top-level-await": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz", - "integrity": "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==", - "license": "MIT", - "dependencies": { - "@rollup/plugin-virtual": "^3.0.2", - "@swc/core": "^1.12.14", - "@swc/wasm": "^1.12.14", - "uuid": "10.0.0" - }, - "peerDependencies": { - "vite": ">=2.8" - } - }, - "node_modules/vite-plugin-top-level-await/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vite-plugin-wasm": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.5.0.tgz", - "integrity": "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ==", - "license": "MIT", - "peerDependencies": { - "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "license": "MIT" - }, - "node_modules/web-tree-sitter": { - "version": "0.20.8", - "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.20.8.tgz", - "integrity": "sha512-weOVgZ3aAARgdnb220GqYuh7+rZU0Ka9k9yfKtGAzEYMa6GgiCzW9JjQRJyCJakvibQW+dfjJdihjInKuuCAUQ==", - "license": "MIT" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/wrappy": { @@ -10173,42 +4759,105 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" } } } diff --git a/gitnexus/package.json b/gitnexus/package.json index 20ee8b00b..2a40f17f3 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -1,67 +1,82 @@ { "name": "gitnexus", - "private": true, - "version": "0.0.0", + "version": "1.2.6", + "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.", + "author": "Abhigyan Patwari", + "license": "PolyForm-Noncommercial-1.0.0", + "homepage": "https://github.com/abhigyanpatwari/GitNexus#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/abhigyanpatwari/GitNexus.git", + "directory": "gitnexus" + }, + "bugs": { + "url": "https://github.com/abhigyanpatwari/GitNexus/issues" + }, + "keywords": [ + "mcp", + "model-context-protocol", + "code-intelligence", + "knowledge-graph", + "cursor", + "claude", + "ai-agent", + "gitnexus", + "static-analysis", + "codebase-indexing" + ], "type": "module", + "bin": { + "gitnexus": "dist/cli/index.js" + }, + "files": [ + "dist", + "hooks", + "skills", + "vendor" + ], "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "preview": "vite preview" + "build": "tsc", + "dev": "tsx watch src/cli/index.ts", + "prepare": "npm run build" }, "dependencies": { "@huggingface/transformers": "^3.0.0", - "@isomorphic-git/lightning-fs": "^4.6.2", - "@langchain/anthropic": "^1.3.10", - "@langchain/core": "^1.1.15", - "@langchain/google-genai": "^2.1.10", - "@langchain/langgraph": "^1.1.0", - "@langchain/ollama": "^1.2.0", - "@langchain/openai": "^1.2.2", - "@sigma/edge-curve": "^3.1.0", - "@tailwindcss/vite": "^4.1.18", - "axios": "^1.13.2", - "buffer": "^6.0.3", - "comlink": "^4.4.2", - "d3": "^7.9.0", - "graphology": "^0.26.0", - "graphology-communities-louvain": "^2.0.2", - "graphology-layout-force": "^0.2.4", - "graphology-layout-forceatlas2": "^0.10.1", - "graphology-layout-noverlap": "^0.4.2", - "isomorphic-git": "^1.36.1", - "jszip": "^3.10.1", - "kuzu-wasm": "^0.11.1", - "langchain": "^1.2.10", - "lru-cache": "^11.2.4", - "lucide-react": "^0.562.0", - "mermaid": "^11.12.2", - "minisearch": "^7.2.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^10.1.0", - "react-syntax-highlighter": "^16.1.0", - "react-zoom-pan-pinch": "^3.7.0", - "remark-gfm": "^4.0.1", - "sigma": "^3.0.2", - "tailwindcss": "^4.1.18", - "uuid": "^13.0.0", - "vite-plugin-top-level-await": "^1.6.0", - "vite-plugin-wasm": "^3.5.0", - "web-tree-sitter": "^0.20.8", - "zod": "^3.25.76" + "@modelcontextprotocol/sdk": "^1.0.0", + "cli-progress": "^3.12.0", + "commander": "^12.0.0", + "cors": "^2.8.5", + "express": "^4.19.2", + "glob": "^11.0.0", + "graphology": "^0.25.4", + "graphology-indices": "^0.17.0", + "graphology-utils": "^2.3.0", + "kuzu": "^0.11.3", + "lru-cache": "^11.0.0", + "mnemonist": "^0.39.0", + "pandemonium": "^2.4.0", + "tree-sitter": "^0.21.0", + "tree-sitter-c": "^0.21.0", + "tree-sitter-c-sharp": "^0.21.0", + "tree-sitter-cpp": "^0.22.0", + "tree-sitter-go": "^0.21.0", + "tree-sitter-java": "^0.21.0", + "tree-sitter-javascript": "^0.21.0", + "tree-sitter-python": "^0.21.0", + "tree-sitter-rust": "^0.21.0", + "tree-sitter-typescript": "^0.21.0", + "typescript": "^5.4.5", + "uuid": "^13.0.0" }, "devDependencies": { - "@babel/types": "^7.28.5", - "@types/jszip": "^3.4.0", - "@types/node": "^24.10.1", - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "@types/react-syntax-highlighter": "^15.5.13", - "@vercel/node": "^5.5.16", - "@vitejs/plugin-react": "^5.1.0", - "tree-sitter-wasms": "^0.1.13", - "typescript": "^5.4.5", - "vite": "^5.2.0", - "vite-plugin-static-copy": "^3.1.4" + "@types/cli-progress": "^3.11.6", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/node": "^20.0.0", + "@types/uuid": "^10.0.0", + "tsx": "^4.0.0" + }, + "engines": { + "node": ">=18.0.0" } } diff --git a/gitnexus/skills/debugging.md b/gitnexus/skills/debugging.md new file mode 100644 index 000000000..3b945835b --- /dev/null +++ b/gitnexus/skills/debugging.md @@ -0,0 +1,85 @@ +--- +name: gitnexus-debugging +description: Trace bugs through call chains using knowledge graph +--- + +# Debugging with GitNexus + +## When to Use +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. gitnexus_query({query: ""}) → Find related execution flows +2. gitnexus_context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] gitnexus_query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] gitnexus_context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] gitnexus_cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +|---------|-------------------| +| Error message | `gitnexus_query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**gitnexus_query** — find code related to error: +``` +gitnexus_query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**gitnexus_context** — full context for a suspect: +``` +gitnexus_context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**gitnexus_cypher** — custom call chain traces: +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. gitnexus_query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. gitnexus_context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/gitnexus/skills/exploring.md b/gitnexus/skills/exploring.md new file mode 100644 index 000000000..2214c289c --- /dev/null +++ b/gitnexus/skills/exploring.md @@ -0,0 +1,75 @@ +--- +name: gitnexus-exploring +description: Navigate unfamiliar code using GitNexus knowledge graph +--- + +# Exploring Codebases with GitNexus + +## When to Use +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. gitnexus_query({query: ""}) → Find related execution flows +4. gitnexus_context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] gitnexus_query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] gitnexus_context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +|----------|-------------| +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**gitnexus_query** — find execution flows related to a concept: +``` +gitnexus_query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**gitnexus_context** — 360-degree view of a symbol: +``` +gitnexus_context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. gitnexus_query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. gitnexus_context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/gitnexus/skills/impact-analysis.md b/gitnexus/skills/impact-analysis.md new file mode 100644 index 000000000..bb5f51fcc --- /dev/null +++ b/gitnexus/skills/impact-analysis.md @@ -0,0 +1,94 @@ +--- +name: gitnexus-impact-analysis +description: Analyze blast radius before making code changes +--- + +# Impact Analysis with GitNexus + +## When to Use +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. gitnexus_detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] gitnexus_detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +|-------|-----------|---------| +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +|----------|------| +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**gitnexus_impact** — the primary tool for symbol blast radius: +``` +gitnexus_impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**gitnexus_detect_changes** — git-diff based impact analysis: +``` +gitnexus_detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. gitnexus_impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/gitnexus/skills/refactoring.md b/gitnexus/skills/refactoring.md new file mode 100644 index 000000000..23f4d1130 --- /dev/null +++ b/gitnexus/skills/refactoring.md @@ -0,0 +1,113 @@ +--- +name: gitnexus-refactoring +description: Plan safe refactors using blast radius and dependency mapping +--- + +# Refactoring with GitNexus + +## 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 + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents +2. gitnexus_query({query: "X"}) → Find execution flows involving X +3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklists + +### Rename Symbol +``` +- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits +- [ ] gitnexus_detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module +``` +- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs +- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service +``` +- [ ] gitnexus_context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**gitnexus_rename** — automated multi-file rename: +``` +gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**gitnexus_impact** — map all dependents first: +``` +gitnexus_impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**gitnexus_detect_changes** — verify your changes after refactoring: +``` +gitnexus_detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**gitnexus_cypher** — custom reference queries: +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +|-------------|------------| +| Many callers (>5) | Use gitnexus_rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | gitnexus_query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. gitnexus_detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/gitnexus/src/cli/ai-context.ts b/gitnexus/src/cli/ai-context.ts new file mode 100644 index 000000000..6f8d1ede6 --- /dev/null +++ b/gitnexus/src/cli/ai-context.ts @@ -0,0 +1,253 @@ +/** + * AI Context Generator + * + * Creates AGENTS.md and CLAUDE.md with full inline GitNexus context. + * AGENTS.md is the standard read by Cursor, Windsurf, OpenCode, Cline, etc. + * CLAUDE.md is for Claude Code which only reads that file. + */ + +import fs from 'fs/promises'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// ESM equivalent of __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +interface RepoStats { + files?: number; + nodes?: number; + edges?: number; + communities?: number; + clusters?: number; // Aggregated cluster count (what tools show) + processes?: number; +} + +const GITNEXUS_START_MARKER = ''; +const GITNEXUS_END_MARKER = ''; + +/** + * Generate the full GitNexus context content. + * + * Design principles (learned from real agent behavior): + * - AGENTS.md is the ROUTER — it tells the agent WHICH skill to read + * - Skills contain the actual workflows — AGENTS.md does NOT duplicate them + * - Bold **IMPORTANT** block + "Skills — Read First" heading — agents skip soft suggestions + * - One-line quick start (read context resource) gives agents an entry point + * - Tools/Resources sections are labeled "Reference" — agents treat them as lookup, not workflow + */ +function generateGitNexusContent(projectName: string, stats: RepoStats): string { + return `${GITNEXUS_START_MARKER} +# GitNexus MCP + +This project is indexed by GitNexus as **${projectName}** (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows). + +GitNexus provides a knowledge graph over this codebase — call chains, blast radius, execution flows, and semantic search. + +## Always Start Here + +For any task involving code understanding, debugging, impact analysis, or refactoring, you must: + +1. **Read \`gitnexus://repo/{name}/context\`** — codebase overview + check index freshness +2. **Match your task to a skill below** and **read that skill file** +3. **Follow the skill's workflow and checklist** + +> If step 1 warns the index is stale, run \`npx gitnexus analyze\` in the terminal first. + +## Skills + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/exploring/SKILL.md\` | +| Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/impact-analysis/SKILL.md\` | +| Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/debugging/SKILL.md\` | +| Rename / extract / split / refactor | \`.claude/skills/gitnexus/refactoring/SKILL.md\` | + +## Tools Reference + +| Tool | What it gives you | +|------|-------------------| +| \`query\` | Process-grouped code intelligence — execution flows related to a concept | +| \`context\` | 360-degree symbol view — categorized refs, processes it participates in | +| \`impact\` | Symbol blast radius — what breaks at depth 1/2/3 with confidence | +| \`detect_changes\` | Git-diff impact — what do your current changes affect | +| \`rename\` | Multi-file coordinated rename with confidence-tagged edits | +| \`cypher\` | Raw graph queries (read \`gitnexus://repo/{name}/schema\` first) | +| \`list_repos\` | Discover indexed repos | + +## Resources Reference + +Lightweight reads (~100-500 tokens) for navigation: + +| Resource | Content | +|----------|---------| +| \`gitnexus://repo/{name}/context\` | Stats, staleness check | +| \`gitnexus://repo/{name}/clusters\` | All functional areas with cohesion scores | +| \`gitnexus://repo/{name}/cluster/{clusterName}\` | Area members | +| \`gitnexus://repo/{name}/processes\` | All execution flows | +| \`gitnexus://repo/{name}/process/{processName}\` | Step-by-step trace | +| \`gitnexus://repo/{name}/schema\` | Graph schema for Cypher | + +## Graph Schema + +**Nodes:** File, Function, Class, Interface, Method, Community, Process +**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS + +\`\`\`cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"}) +RETURN caller.name, caller.filePath +\`\`\` + +${GITNEXUS_END_MARKER}`; +} + + +/** + * Check if a file exists + */ +async function fileExists(filePath: string): Promise { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + +/** + * Create or update GitNexus section in a file + * - If file doesn't exist: create with GitNexus content + * - If file exists without GitNexus section: append + * - If file exists with GitNexus section: replace that section + */ +async function upsertGitNexusSection( + filePath: string, + content: string +): Promise<'created' | 'updated' | 'appended'> { + const exists = await fileExists(filePath); + + if (!exists) { + await fs.writeFile(filePath, content, 'utf-8'); + return 'created'; + } + + const existingContent = await fs.readFile(filePath, 'utf-8'); + + // Check if GitNexus section already exists + const startIdx = existingContent.indexOf(GITNEXUS_START_MARKER); + const endIdx = existingContent.indexOf(GITNEXUS_END_MARKER); + + if (startIdx !== -1 && endIdx !== -1) { + // Replace existing section + const before = existingContent.substring(0, startIdx); + const after = existingContent.substring(endIdx + GITNEXUS_END_MARKER.length); + const newContent = before + content + after; + await fs.writeFile(filePath, newContent.trim() + '\n', 'utf-8'); + return 'updated'; + } + + // Append new section + const newContent = existingContent.trim() + '\n\n' + content + '\n'; + await fs.writeFile(filePath, newContent, 'utf-8'); + return 'appended'; +} + +/** + * Install GitNexus skills to .claude/skills/gitnexus/ + * Works natively with Claude Code, Cursor, and GitHub Copilot + */ +async function installSkills(repoPath: string): Promise { + const skillsDir = path.join(repoPath, '.claude', 'skills', 'gitnexus'); + const installedSkills: string[] = []; + + // Skill definitions bundled with the package + const skills = [ + { + name: 'exploring', + description: 'Navigate unfamiliar code using GitNexus knowledge graph', + }, + { + name: 'debugging', + description: 'Trace bugs through call chains using knowledge graph', + }, + { + name: 'impact-analysis', + description: 'Analyze blast radius before making code changes', + }, + { + name: 'refactoring', + description: 'Plan safe refactors using blast radius and dependency mapping', + }, + ]; + + for (const skill of skills) { + const skillDir = path.join(skillsDir, skill.name); + const skillPath = path.join(skillDir, 'SKILL.md'); + + try { + // Create skill directory + await fs.mkdir(skillDir, { recursive: true }); + + // Try to read from package skills directory + const packageSkillPath = path.join(__dirname, '..', '..', 'skills', `${skill.name}.md`); + let skillContent: string; + + try { + skillContent = await fs.readFile(packageSkillPath, 'utf-8'); + } catch { + // Fallback: generate minimal skill content + skillContent = `--- +name: gitnexus-${skill.name} +description: ${skill.description} +--- + +# ${skill.name.charAt(0).toUpperCase() + skill.name.slice(1)} + +${skill.description} + +Use GitNexus tools to accomplish this task. +`; + } + + await fs.writeFile(skillPath, skillContent, 'utf-8'); + installedSkills.push(skill.name); + } catch (err) { + // Skip on error, don't fail the whole process + console.warn(`Warning: Could not install skill ${skill.name}:`, err); + } + } + + return installedSkills; +} + +/** + * Generate AI context files after indexing + */ +export async function generateAIContextFiles( + repoPath: string, + _storagePath: string, + projectName: string, + stats: RepoStats +): Promise<{ files: string[] }> { + const content = generateGitNexusContent(projectName, stats); + const createdFiles: string[] = []; + + // Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.) + const agentsPath = path.join(repoPath, 'AGENTS.md'); + const agentsResult = await upsertGitNexusSection(agentsPath, content); + createdFiles.push(`AGENTS.md (${agentsResult})`); + + // Create CLAUDE.md (for Claude Code) + const claudePath = path.join(repoPath, 'CLAUDE.md'); + const claudeResult = await upsertGitNexusSection(claudePath, content); + createdFiles.push(`CLAUDE.md (${claudeResult})`); + + // Install skills to .claude/skills/gitnexus/ + const installedSkills = await installSkills(repoPath); + if (installedSkills.length > 0) { + createdFiles.push(`.claude/skills/gitnexus/ (${installedSkills.length} skills)`); + } + + return { files: createdFiles }; +} + diff --git a/gitnexus/src/cli/analyze.ts b/gitnexus/src/cli/analyze.ts new file mode 100644 index 000000000..7139c9a08 --- /dev/null +++ b/gitnexus/src/cli/analyze.ts @@ -0,0 +1,278 @@ +/** + * Analyze Command + * + * Indexes a repository and stores the knowledge graph in .gitnexus/ + */ + +import path from 'path'; +import cliProgress from 'cli-progress'; +import { runPipelineFromRepo } from '../core/ingestion/pipeline.js'; +import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReusedStatement, closeKuzu, createFTSIndex, loadCachedEmbeddings } from '../core/kuzu/kuzu-adapter.js'; +import { runEmbeddingPipeline } from '../core/embeddings/embedding-pipeline.js'; +import { disposeEmbedder } from '../core/embeddings/embedder.js'; +import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js'; +import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js'; +import { generateAIContextFiles } from './ai-context.js'; +import fs from 'fs/promises'; +import { registerClaudeHook } from './claude-hooks.js'; + +export interface AnalyzeOptions { + force?: boolean; + skipEmbeddings?: boolean; +} + +/** Threshold: auto-skip embeddings for repos with more nodes than this */ +const EMBEDDING_NODE_LIMIT = 50_000; + +const PHASE_LABELS: Record = { + extracting: 'Scanning files', + structure: 'Building structure', + parsing: 'Parsing code', + imports: 'Resolving imports', + calls: 'Tracing calls', + heritage: 'Extracting inheritance', + communities: 'Detecting communities', + processes: 'Detecting processes', + complete: 'Pipeline complete', + kuzu: 'Loading into KuzuDB', + fts: 'Creating search indexes', + embeddings: 'Generating embeddings', + done: 'Done', +}; + +export const analyzeCommand = async ( + inputPath?: string, + options?: AnalyzeOptions +) => { + console.log('\n GitNexus Analyzer\n'); + + let repoPath: string; + if (inputPath) { + repoPath = path.resolve(inputPath); + } else { + const gitRoot = getGitRoot(process.cwd()); + if (!gitRoot) { + console.log(' Not inside a git repository\n'); + process.exitCode = 1; + return; + } + repoPath = gitRoot; + } + + if (!isGitRepo(repoPath)) { + console.log(' Not a git repository\n'); + process.exitCode = 1; + return; + } + + const { storagePath, kuzuPath } = getStoragePaths(repoPath); + const currentCommit = getCurrentCommit(repoPath); + const existingMeta = await loadMeta(storagePath); + + if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) { + console.log(' Already up to date\n'); + return; + } + + // Single progress bar for entire pipeline + const bar = new cliProgress.SingleBar({ + format: ' {bar} {percentage}% | {phase}', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true, + barGlue: '', + autopadding: true, + clearOnComplete: false, + stopOnComplete: false, + }, cliProgress.Presets.shades_grey); + + bar.start(100, 0, { phase: 'Initializing...' }); + + const t0Global = Date.now(); + + // ── Cache embeddings from existing index before rebuild ──────────── + let cachedEmbeddingNodeIds = new Set(); + let cachedEmbeddings: Array<{ nodeId: string; embedding: number[] }> = []; + + if (existingMeta && !options?.force) { + try { + bar.update(0, { phase: 'Caching embeddings...' }); + await initKuzu(kuzuPath); + const cached = await loadCachedEmbeddings(); + cachedEmbeddingNodeIds = cached.embeddingNodeIds; + cachedEmbeddings = cached.embeddings; + await closeKuzu(); + } catch { + try { await closeKuzu(); } catch {} + } + } + + // ── Phase 1: Full Pipeline (0–60%) ───────────────────────────────── + const pipelineResult = await runPipelineFromRepo(repoPath, (progress) => { + const phaseLabel = PHASE_LABELS[progress.phase] || progress.phase; + const scaled = Math.round(progress.percent * 0.6); + bar.update(scaled, { phase: phaseLabel }); + }); + + // ── Phase 2: KuzuDB (60–85%) ────────────────────────────────────── + bar.update(60, { phase: 'Loading into KuzuDB...' }); + + await closeKuzu(); + const kuzuFiles = [kuzuPath, `${kuzuPath}.wal`, `${kuzuPath}.lock`]; + for (const f of kuzuFiles) { + try { await fs.rm(f, { recursive: true, force: true }); } catch {} + } + + const t0Kuzu = Date.now(); + await initKuzu(kuzuPath); + let kuzuMsgCount = 0; + const kuzuResult = await loadGraphToKuzu(pipelineResult.graph, pipelineResult.fileContents, storagePath, (msg) => { + kuzuMsgCount++; + const progress = Math.min(84, 60 + Math.round((kuzuMsgCount / (kuzuMsgCount + 10)) * 24)); + bar.update(progress, { phase: msg }); + }); + const kuzuTime = ((Date.now() - t0Kuzu) / 1000).toFixed(1); + const kuzuWarnings = kuzuResult.warnings; + + // ── Phase 3: FTS (85–90%) ───────────────────────────────────────── + bar.update(85, { phase: 'Creating search indexes...' }); + + const t0Fts = Date.now(); + try { + await createFTSIndex('File', 'file_fts', ['name', 'content']); + await createFTSIndex('Function', 'function_fts', ['name', 'content']); + await createFTSIndex('Class', 'class_fts', ['name', 'content']); + await createFTSIndex('Method', 'method_fts', ['name', 'content']); + await createFTSIndex('Interface', 'interface_fts', ['name', 'content']); + } catch (e: any) { + // Non-fatal — FTS is best-effort + } + const ftsTime = ((Date.now() - t0Fts) / 1000).toFixed(1); + + // ── Phase 3.5: Re-insert cached embeddings ──────────────────────── + if (cachedEmbeddings.length > 0) { + bar.update(88, { phase: `Restoring ${cachedEmbeddings.length} cached embeddings...` }); + const EMBED_BATCH = 200; + for (let i = 0; i < cachedEmbeddings.length; i += EMBED_BATCH) { + const batch = cachedEmbeddings.slice(i, i + EMBED_BATCH); + const paramsList = batch.map(e => ({ nodeId: e.nodeId, embedding: e.embedding })); + try { + await executeWithReusedStatement( + `CREATE (e:CodeEmbedding {nodeId: $nodeId, embedding: $embedding})`, + paramsList, + ); + } catch { /* some may fail if node was removed, that's fine */ } + } + } + + // ── Phase 4: Embeddings (90–98%) ────────────────────────────────── + const stats = await getKuzuStats(); + let embeddingTime = '0.0'; + let embeddingSkipped = false; + let embeddingSkipReason = ''; + + if (options?.skipEmbeddings) { + embeddingSkipped = true; + embeddingSkipReason = 'skipped (--skip-embeddings)'; + } else if (stats.nodes > EMBEDDING_NODE_LIMIT) { + embeddingSkipped = true; + embeddingSkipReason = `skipped (${stats.nodes.toLocaleString()} nodes > ${EMBEDDING_NODE_LIMIT.toLocaleString()} limit)`; + } + + if (!embeddingSkipped) { + bar.update(90, { phase: 'Loading embedding model...' }); + const t0Emb = Date.now(); + await runEmbeddingPipeline( + executeQuery, + executeWithReusedStatement, + (progress) => { + const scaled = 90 + Math.round((progress.percent / 100) * 8); + const label = progress.phase === 'loading-model' ? 'Loading embedding model...' : `Embedding ${progress.nodesProcessed || 0}/${progress.totalNodes || '?'}`; + bar.update(scaled, { phase: label }); + }, + {}, + cachedEmbeddingNodeIds.size > 0 ? cachedEmbeddingNodeIds : undefined, + ); + embeddingTime = ((Date.now() - t0Emb) / 1000).toFixed(1); + } + + // ── Phase 5: Finalize (98–100%) ─────────────────────────────────── + bar.update(98, { phase: 'Saving metadata...' }); + + const meta = { + repoPath, + lastCommit: currentCommit, + indexedAt: new Date().toISOString(), + stats: { + files: pipelineResult.fileContents.size, + nodes: stats.nodes, + edges: stats.edges, + communities: pipelineResult.communityResult?.stats.totalCommunities, + processes: pipelineResult.processResult?.stats.totalProcesses, + }, + }; + await saveMeta(storagePath, meta); + await registerRepo(repoPath, meta); + await addToGitignore(repoPath); + + const hookResult = await registerClaudeHook(); + + const projectName = path.basename(repoPath); + let aggregatedClusterCount = 0; + if (pipelineResult.communityResult?.communities) { + const groups = new Map(); + for (const c of pipelineResult.communityResult.communities) { + const label = c.heuristicLabel || c.label || 'Unknown'; + groups.set(label, (groups.get(label) || 0) + c.symbolCount); + } + aggregatedClusterCount = Array.from(groups.values()).filter(count => count >= 5).length; + } + + const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, { + files: pipelineResult.fileContents.size, + nodes: stats.nodes, + edges: stats.edges, + communities: pipelineResult.communityResult?.stats.totalCommunities, + clusters: aggregatedClusterCount, + processes: pipelineResult.processResult?.stats.totalProcesses, + }); + + await closeKuzu(); + await disposeEmbedder(); + + const totalTime = ((Date.now() - t0Global) / 1000).toFixed(1); + + bar.update(100, { phase: 'Done' }); + bar.stop(); + + // ── Summary ─────────────────────────────────────────────────────── + const embeddingsCached = cachedEmbeddings.length > 0; + console.log(`\n Repository indexed successfully (${totalTime}s)${embeddingsCached ? ` [${cachedEmbeddings.length} embeddings cached]` : ''}\n`); + console.log(` ${stats.nodes.toLocaleString()} nodes | ${stats.edges.toLocaleString()} edges | ${pipelineResult.communityResult?.stats.totalCommunities || 0} clusters | ${pipelineResult.processResult?.stats.totalProcesses || 0} flows`); + console.log(` KuzuDB ${kuzuTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`); + console.log(` ${repoPath}`); + + if (aiContext.files.length > 0) { + console.log(` Context: ${aiContext.files.join(', ')}`); + } + + if (hookResult.registered) { + console.log(` Hooks: ${hookResult.message}`); + } + + // Show warnings (missing schema pairs, etc.) after the clean output + if (kuzuWarnings.length > 0) { + console.log(`\n Warnings (${kuzuWarnings.length}):`); + for (const w of kuzuWarnings) { + console.log(` ${w}`); + } + } + + try { + await fs.access(getGlobalRegistryPath()); + } catch { + console.log('\n Tip: Run `gitnexus setup` to configure MCP for your editor.'); + } + + console.log(''); +}; diff --git a/gitnexus/src/cli/augment.ts b/gitnexus/src/cli/augment.ts new file mode 100644 index 000000000..b81d6feec --- /dev/null +++ b/gitnexus/src/cli/augment.ts @@ -0,0 +1,36 @@ +/** + * Augment CLI Command + * + * Fast-path command for platform hooks. + * Shells out from Claude Code PreToolUse / Cursor beforeShellExecution hooks. + * + * Usage: gitnexus augment + * Returns enriched text to stdout. + * + * Performance: Must cold-start fast (<500ms). + * Skips unnecessary initialization (no web server, no full DB warmup). + */ + +import { augment } from '../core/augmentation/engine.js'; + +export async function augmentCommand(pattern: string): Promise { + if (!pattern || pattern.length < 3) { + process.exit(0); + } + + try { + const result = await augment(pattern, process.cwd()); + + if (result) { + // IMPORTANT: Write to stderr, NOT stdout. + // KuzuDB's native module captures stdout fd at OS level during init, + // which makes stdout permanently broken in subprocess contexts. + // stderr is never captured, so it works reliably everywhere. + // The hook reads from the subprocess's stderr. + process.stderr.write(result + '\n'); + } + } catch { + // Graceful failure — never break the calling hook + process.exit(0); + } +} diff --git a/gitnexus/src/cli/claude-hooks.ts b/gitnexus/src/cli/claude-hooks.ts new file mode 100644 index 000000000..c81bcc752 --- /dev/null +++ b/gitnexus/src/cli/claude-hooks.ts @@ -0,0 +1,111 @@ +/** + * Claude Code Hook Registration + * + * Registers the GitNexus PreToolUse hook in ~/.claude/hooks.json + * so that grep/glob/bash calls are automatically augmented with + * knowledge graph context. + * + * Idempotent — safe to call multiple times. + */ + +import fs from 'fs/promises'; +import path from 'path'; +import os from 'os'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Get the absolute path to the gitnexus-hook.js file. + * Works for both local dev and npm-installed packages. + */ +function getHookScriptPath(): string { + // From dist/cli/claude-hooks.js → hooks/claude/gitnexus-hook.js + const packageRoot = path.resolve(__dirname, '..', '..'); + return path.join(packageRoot, 'hooks', 'claude', 'gitnexus-hook.cjs'); +} + +/** + * Register (or verify) the GitNexus hook in Claude Code's global hooks.json. + * + * - Creates ~/.claude/ and hooks.json if they don't exist + * - Preserves existing hooks from other tools + * - Skips if GitNexus hook is already registered + * + * Returns a status message for the CLI output. + */ +export async function registerClaudeHook(): Promise<{ registered: boolean; message: string }> { + const claudeDir = path.join(os.homedir(), '.claude'); + const hooksFile = path.join(claudeDir, 'hooks.json'); + const hookScript = getHookScriptPath(); + + // Check if the hook script exists + try { + await fs.access(hookScript); + } catch { + return { registered: false, message: 'Hook script not found (package may be incomplete)' }; + } + + // Build the hook command — use node + absolute path for reliability + const hookCommand = `node "${hookScript}"`; + + // Check if ~/.claude/ exists (user has Claude Code installed) + try { + await fs.access(claudeDir); + } catch { + // No Claude Code installation — skip silently + return { registered: false, message: 'Claude Code not detected (~/.claude/ not found)' }; + } + + // Read existing hooks.json or start fresh + let hooksConfig: any = {}; + try { + const existing = await fs.readFile(hooksFile, 'utf-8'); + hooksConfig = JSON.parse(existing); + } catch { + // File doesn't exist or is invalid — we'll create it + } + + // Ensure the hooks structure exists + if (!hooksConfig.hooks) { + hooksConfig.hooks = {}; + } + if (!Array.isArray(hooksConfig.hooks.PreToolUse)) { + hooksConfig.hooks.PreToolUse = []; + } + + // Check if GitNexus hook is already registered + const existingEntry = hooksConfig.hooks.PreToolUse.find((entry: any) => { + if (!entry.hooks || !Array.isArray(entry.hooks)) return false; + return entry.hooks.some((h: any) => + h.command && ( + h.command.includes('gitnexus-hook') || + h.command.includes('gitnexus augment') + ) + ); + }); + + if (existingEntry) { + return { registered: true, message: 'Claude Code hook already registered' }; + } + + // Add the GitNexus hook entry + hooksConfig.hooks.PreToolUse.push({ + matcher: { + tool_name: "Grep|Glob|Bash" + }, + hooks: [ + { + type: "command", + command: hookCommand, + timeout: 8000 + } + ] + }); + + // Write back + await fs.writeFile(hooksFile, JSON.stringify(hooksConfig, null, 2) + '\n', 'utf-8'); + + return { registered: true, message: 'Claude Code hook registered' }; +} diff --git a/gitnexus/src/cli/clean.ts b/gitnexus/src/cli/clean.ts new file mode 100644 index 000000000..edf57409d --- /dev/null +++ b/gitnexus/src/cli/clean.ts @@ -0,0 +1,66 @@ +/** + * Clean Command + * + * Removes the .gitnexus index from the current repository. + * Also unregisters it from the global registry. + */ + +import fs from 'fs/promises'; +import { findRepo, unregisterRepo, listRegisteredRepos } from '../storage/repo-manager.js'; + +export const cleanCommand = async (options?: { force?: boolean; all?: boolean }) => { + // --all flag: clean all indexed repos + if (options?.all) { + if (!options?.force) { + const entries = await listRegisteredRepos(); + if (entries.length === 0) { + console.log('No indexed repositories found.'); + return; + } + console.log(`This will delete GitNexus indexes for ${entries.length} repo(s):`); + for (const entry of entries) { + console.log(` - ${entry.name} (${entry.path})`); + } + console.log('\nRun with --force to confirm deletion.'); + return; + } + + const entries = await listRegisteredRepos(); + for (const entry of entries) { + try { + await fs.rm(entry.storagePath, { recursive: true, force: true }); + await unregisterRepo(entry.path); + console.log(`Deleted: ${entry.name} (${entry.storagePath})`); + } catch (err) { + console.error(`Failed to delete ${entry.name}:`, err); + } + } + return; + } + + // Default: clean current repo + const cwd = process.cwd(); + const repo = await findRepo(cwd); + + if (!repo) { + console.log('No indexed repository found in this directory.'); + return; + } + + const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath; + + if (!options?.force) { + console.log(`This will delete the GitNexus index for: ${repoName}`); + console.log(` Path: ${repo.storagePath}`); + console.log('\nRun with --force to confirm deletion.'); + return; + } + + try { + await fs.rm(repo.storagePath, { recursive: true, force: true }); + await unregisterRepo(repo.repoPath); + console.log(`Deleted: ${repo.storagePath}`); + } catch (err) { + console.error('Failed to delete:', err); + } +}; diff --git a/gitnexus/src/cli/eval-server.ts b/gitnexus/src/cli/eval-server.ts new file mode 100644 index 000000000..171fb0741 --- /dev/null +++ b/gitnexus/src/cli/eval-server.ts @@ -0,0 +1,430 @@ +/** + * Eval Server — Lightweight HTTP server for SWE-bench evaluation + * + * Keeps KuzuDB warm in memory so tool calls from the agent are near-instant. + * Designed to run inside Docker containers during SWE-bench evaluation. + * + * KEY DESIGN: Returns LLM-friendly text, not raw JSON. + * Raw JSON wastes tokens and is hard for models to parse. The text formatter + * converts structured results into compact, readable output that models + * can immediately act on. Next-step hints guide the agent through a + * productive tool-chaining workflow (query → context → impact → fix). + * + * Architecture: + * Agent bash cmd → curl localhost:PORT/tool/query → eval-server → LocalBackend → format → text + * + * Usage: + * gitnexus eval-server # default port 4848 + * gitnexus eval-server --port 4848 # explicit port + * gitnexus eval-server --idle-timeout 300 # auto-shutdown after 300s idle + * + * API: + * POST /tool/:name — Call a tool. Body is JSON arguments. Returns formatted text. + * GET /health — Health check. Returns {"status":"ok","repos":[...]} + * POST /shutdown — Graceful shutdown. + */ + +import http from 'http'; +import { LocalBackend } from '../mcp/local/local-backend.js'; + +export interface EvalServerOptions { + port?: string; + idleTimeout?: string; +} + +// ─── Text Formatters ────────────────────────────────────────────────── +// Convert structured JSON results into compact, LLM-friendly text. +// Design: minimize tokens, maximize actionability. + +function formatQueryResult(result: any): string { + if (result.error) return `Error: ${result.error}`; + + const lines: string[] = []; + const processes = result.processes || []; + const symbols = result.process_symbols || []; + const defs = result.definitions || []; + + if (processes.length === 0 && defs.length === 0) { + return 'No matching execution flows found. Try a different search term or use grep.'; + } + + lines.push(`Found ${processes.length} execution flow(s):\n`); + + for (let i = 0; i < processes.length; i++) { + const p = processes[i]; + lines.push(`${i + 1}. ${p.summary} (${p.step_count} steps, ${p.symbol_count} symbols)`); + + // Show symbols belonging to this process + const procSymbols = symbols.filter((s: any) => s.process_id === p.id); + for (const s of procSymbols.slice(0, 6)) { + const loc = s.startLine ? `:${s.startLine}` : ''; + lines.push(` ${s.type} ${s.name} → ${s.filePath}${loc}`); + } + if (procSymbols.length > 6) { + lines.push(` ... and ${procSymbols.length - 6} more`); + } + lines.push(''); + } + + if (defs.length > 0) { + lines.push(`Standalone definitions:`); + for (const d of defs.slice(0, 8)) { + lines.push(` ${d.type || 'Symbol'} ${d.name} → ${d.filePath || '?'}`); + } + if (defs.length > 8) lines.push(` ... and ${defs.length - 8} more`); + } + + return lines.join('\n').trim(); +} + +function formatContextResult(result: any): string { + if (result.error) return `Error: ${result.error}`; + + if (result.status === 'ambiguous') { + const lines = [`Multiple symbols named '${result.candidates?.[0]?.name || '?'}'. Disambiguate with file path:\n`]; + for (const c of result.candidates || []) { + lines.push(` ${c.kind} ${c.name} → ${c.filePath}:${c.line || '?'} (uid: ${c.uid})`); + } + lines.push(`\nRe-run: gitnexus-context "${result.candidates?.[0]?.name}" ""`); + return lines.join('\n'); + } + + const sym = result.symbol; + if (!sym) return 'Symbol not found.'; + + const lines: string[] = []; + const loc = sym.startLine ? `:${sym.startLine}-${sym.endLine}` : ''; + lines.push(`${sym.kind} ${sym.name} → ${sym.filePath}${loc}`); + lines.push(''); + + // Incoming refs (who calls/imports/extends this) + const incoming = result.incoming || {}; + const incomingCount = Object.values(incoming).reduce((sum: number, arr: any) => sum + arr.length, 0) as number; + if (incomingCount > 0) { + lines.push(`Called/imported by (${incomingCount}):`); + for (const [relType, refs] of Object.entries(incoming)) { + for (const ref of (refs as any[]).slice(0, 10)) { + lines.push(` ← [${relType}] ${ref.kind} ${ref.name} → ${ref.filePath}`); + } + } + lines.push(''); + } + + // Outgoing refs (what this calls/imports) + const outgoing = result.outgoing || {}; + const outgoingCount = Object.values(outgoing).reduce((sum: number, arr: any) => sum + arr.length, 0) as number; + if (outgoingCount > 0) { + lines.push(`Calls/imports (${outgoingCount}):`); + for (const [relType, refs] of Object.entries(outgoing)) { + for (const ref of (refs as any[]).slice(0, 10)) { + lines.push(` → [${relType}] ${ref.kind} ${ref.name} → ${ref.filePath}`); + } + } + lines.push(''); + } + + // Processes + const procs = result.processes || []; + if (procs.length > 0) { + lines.push(`Participates in ${procs.length} execution flow(s):`); + for (const p of procs) { + lines.push(` • ${p.name} (step ${p.step_index}/${p.step_count})`); + } + } + + if (sym.content) { + lines.push(''); + lines.push(`Source:`); + lines.push(sym.content); + } + + return lines.join('\n').trim(); +} + +function formatImpactResult(result: any): string { + if (result.error) return `Error: ${result.error}`; + + const target = result.target; + const direction = result.direction; + const byDepth = result.byDepth || {}; + const total = result.impactedCount || 0; + + if (total === 0) { + return `${target?.name || '?'}: No ${direction} dependencies found. This symbol appears isolated.`; + } + + const lines: string[] = []; + const dirLabel = direction === 'upstream' ? 'depends on this (will break if changed)' : 'this depends on'; + lines.push(`Blast radius for ${target?.kind || ''} ${target?.name} (${direction}): ${total} symbol(s) ${dirLabel}\n`); + + const depthLabels: Record = { + 1: 'WILL BREAK (direct)', + 2: 'LIKELY AFFECTED (indirect)', + 3: 'MAY NEED TESTING (transitive)', + }; + + for (const depth of [1, 2, 3]) { + const items = byDepth[depth]; + if (!items || items.length === 0) continue; + + lines.push(`d=${depth}: ${depthLabels[depth] || ''} (${items.length})`); + for (const item of items.slice(0, 12)) { + const conf = item.confidence < 1 ? ` (conf: ${item.confidence})` : ''; + lines.push(` ${item.type} ${item.name} → ${item.filePath} [${item.relationType}]${conf}`); + } + if (items.length > 12) { + lines.push(` ... and ${items.length - 12} more`); + } + lines.push(''); + } + + return lines.join('\n').trim(); +} + +function formatCypherResult(result: any): string { + if (result.error) return `Error: ${result.error}`; + + if (Array.isArray(result)) { + if (result.length === 0) return 'Query returned 0 rows.'; + // Format as simple table + const keys = Object.keys(result[0]); + const lines: string[] = [`${result.length} row(s):\n`]; + for (const row of result.slice(0, 30)) { + const parts = keys.map(k => `${k}: ${row[k]}`); + lines.push(` ${parts.join(' | ')}`); + } + if (result.length > 30) { + lines.push(` ... ${result.length - 30} more rows`); + } + return lines.join('\n'); + } + + return typeof result === 'string' ? result : JSON.stringify(result, null, 2); +} + +function formatDetectChangesResult(result: any): string { + if (result.error) return `Error: ${result.error}`; + + const summary = result.summary || {}; + const lines: string[] = []; + + if (summary.changed_count === 0) { + return 'No changes detected.'; + } + + lines.push(`Changes: ${summary.changed_files || 0} files, ${summary.changed_count || 0} symbols`); + lines.push(`Affected processes: ${summary.affected_count || 0}`); + lines.push(`Risk level: ${summary.risk_level || 'unknown'}\n`); + + const changed = result.changed_symbols || []; + if (changed.length > 0) { + lines.push(`Changed symbols:`); + for (const s of changed.slice(0, 15)) { + lines.push(` ${s.type} ${s.name} → ${s.filePath}`); + } + if (changed.length > 15) lines.push(` ... and ${changed.length - 15} more`); + lines.push(''); + } + + const affected = result.affected_processes || []; + if (affected.length > 0) { + lines.push(`Affected execution flows:`); + for (const p of affected.slice(0, 10)) { + const steps = (p.changed_steps || []).map((s: any) => s.symbol).join(', '); + lines.push(` • ${p.name} (${p.step_count} steps) — changed: ${steps}`); + } + } + + return lines.join('\n').trim(); +} + +function formatListReposResult(result: any): string { + if (!Array.isArray(result) || result.length === 0) { + return 'No indexed repositories.'; + } + + const lines = ['Indexed repositories:\n']; + for (const r of result) { + const stats = r.stats || {}; + lines.push(` ${r.name} — ${stats.nodes || '?'} symbols, ${stats.edges || '?'} relationships, ${stats.processes || '?'} flows`); + lines.push(` Path: ${r.path}`); + lines.push(` Indexed: ${r.indexedAt}`); + } + return lines.join('\n'); +} + +/** + * Format a tool result as compact, LLM-friendly text. + */ +function formatToolResult(toolName: string, result: any): string { + switch (toolName) { + case 'query': return formatQueryResult(result); + case 'context': return formatContextResult(result); + case 'impact': return formatImpactResult(result); + case 'cypher': return formatCypherResult(result); + case 'detect_changes': return formatDetectChangesResult(result); + case 'list_repos': return formatListReposResult(result); + default: return typeof result === 'string' ? result : JSON.stringify(result, null, 2); + } +} + +// ─── Next-Step Hints ────────────────────────────────────────────────── +// Guide the agent to the logical next tool call. +// Critical for tool chaining: query → context → impact → fix. + +function getNextStepHint(toolName: string): string { + switch (toolName) { + case 'query': + return '\n---\nNext: Pick a symbol above and run gitnexus-context "" to see all its callers, callees, and execution flows.'; + + case 'context': + return '\n---\nNext: To check what breaks if you change this, run gitnexus-impact "" upstream'; + + case 'impact': + return '\n---\nNext: Review d=1 items first (WILL BREAK). Read the source with cat to understand the code, then make your fix.'; + + case 'cypher': + return '\n---\nNext: To explore a result symbol in depth, run gitnexus-context ""'; + + case 'detect_changes': + return '\n---\nNext: Run gitnexus-context "" on high-risk changed symbols to check their callers.'; + + default: + return ''; + } +} + +// ─── Server ─────────────────────────────────────────────────────────── + +export async function evalServerCommand(options?: EvalServerOptions): Promise { + const port = parseInt(options?.port || '4848'); + const idleTimeoutSec = parseInt(options?.idleTimeout || '0'); + + const backend = new LocalBackend(); + const ok = await backend.init(); + + if (!ok) { + console.error('GitNexus eval-server: No indexed repositories found. Run: gitnexus analyze'); + process.exit(1); + } + + const repos = backend.listRepos(); + console.error(`GitNexus eval-server: ${repos.length} repo(s) loaded: ${repos.map(r => r.name).join(', ')}`); + + let idleTimer: ReturnType | null = null; + + function resetIdleTimer() { + if (idleTimeoutSec <= 0) return; + if (idleTimer) clearTimeout(idleTimer); + idleTimer = setTimeout(async () => { + console.error('GitNexus eval-server: Idle timeout reached, shutting down'); + await backend.disconnect(); + process.exit(0); + }, idleTimeoutSec * 1000); + } + + const server = http.createServer(async (req, res) => { + resetIdleTimer(); + + try { + // Health check + if (req.method === 'GET' && req.url === '/health') { + res.setHeader('Content-Type', 'application/json'); + res.writeHead(200); + res.end(JSON.stringify({ status: 'ok', repos: repos.map(r => r.name) })); + return; + } + + // Shutdown + if (req.method === 'POST' && req.url === '/shutdown') { + res.setHeader('Content-Type', 'application/json'); + res.writeHead(200); + res.end(JSON.stringify({ status: 'shutting_down' })); + setTimeout(async () => { + await backend.disconnect(); + server.close(); + process.exit(0); + }, 100); + return; + } + + // Tool calls: POST /tool/:name + const toolMatch = req.url?.match(/^\/tool\/(\w+)$/); + if (req.method === 'POST' && toolMatch) { + const toolName = toolMatch[1]; + + const body = await readBody(req); + let args: Record = {}; + if (body.trim()) { + try { + args = JSON.parse(body); + } catch { + res.setHeader('Content-Type', 'text/plain'); + res.writeHead(400); + res.end('Error: Invalid JSON body'); + return; + } + } + + // Call tool, format result as text, append next-step hint + const result = await backend.callTool(toolName, args); + const formatted = formatToolResult(toolName, result); + const hint = getNextStepHint(toolName); + + res.setHeader('Content-Type', 'text/plain'); + res.writeHead(200); + res.end(formatted + hint); + return; + } + + // 404 + res.setHeader('Content-Type', 'text/plain'); + res.writeHead(404); + res.end('Not found. Use POST /tool/:name or GET /health'); + + } catch (err: any) { + res.setHeader('Content-Type', 'text/plain'); + res.writeHead(500); + res.end(`Error: ${err.message || 'Internal error'}`); + } + }); + + server.listen(port, '127.0.0.1', () => { + console.error(`GitNexus eval-server: listening on http://127.0.0.1:${port}`); + console.error(` POST /tool/query — search execution flows`); + console.error(` POST /tool/context — 360-degree symbol view`); + console.error(` POST /tool/impact — blast radius analysis`); + console.error(` POST /tool/cypher — raw Cypher query`); + console.error(` GET /health — health check`); + console.error(` POST /shutdown — graceful shutdown`); + if (idleTimeoutSec > 0) { + console.error(` Auto-shutdown after ${idleTimeoutSec}s idle`); + } + try { + process.stdout.write(`GITNEXUS_EVAL_SERVER_READY:${port}\n`); + } catch { + // stdout may not be available + } + }); + + resetIdleTimer(); + + const shutdown = async () => { + console.error('GitNexus eval-server: shutting down...'); + await backend.disconnect(); + server.close(); + process.exit(0); + }; + + process.on('SIGINT', shutdown); + process.on('SIGTERM', shutdown); +} + +function readBody(req: http.IncomingMessage): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + req.on('data', (chunk: Buffer) => chunks.push(chunk)); + req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8'))); + req.on('error', reject); + }); +} diff --git a/gitnexus/src/cli/index.ts b/gitnexus/src/cli/index.ts new file mode 100644 index 000000000..b7be04a9f --- /dev/null +++ b/gitnexus/src/cli/index.ts @@ -0,0 +1,123 @@ +#!/usr/bin/env node +import { Command } from 'commander'; +import { analyzeCommand } from './analyze.js'; +import { serveCommand } from './serve.js'; +import { listCommand } from './list.js'; +import { statusCommand } from './status.js'; +import { mcpCommand } from './mcp.js'; +import { cleanCommand } from './clean.js'; +import { setupCommand } from './setup.js'; +import { augmentCommand } from './augment.js'; +import { wikiCommand } from './wiki.js'; +import { queryCommand, contextCommand, impactCommand, cypherCommand } from './tool.js'; +import { evalServerCommand } from './eval-server.js'; +const program = new Command(); + +program + .name('gitnexus') + .description('GitNexus local CLI and MCP server') + .version('1.2.0'); + +program + .command('setup') + .description('One-time setup: configure MCP for Cursor, Claude Code, OpenCode') + .action(setupCommand); + +program + .command('analyze [path]') + .description('Index a repository (full analysis)') + .option('-f, --force', 'Force full re-index even if up to date') + .option('--skip-embeddings', 'Skip embedding generation (faster)') + .action(analyzeCommand); + +program + .command('serve') + .description('Start local HTTP server for web UI connection') + .option('-p, --port ', 'Port number', '4747') + .action(serveCommand); + +program + .command('mcp') + .description('Start MCP server (stdio) — serves all indexed repos') + .action(mcpCommand); + +program + .command('list') + .description('List all indexed repositories') + .action(listCommand); + +program + .command('status') + .description('Show index status for current repo') + .action(statusCommand); + +program + .command('clean') + .description('Delete GitNexus index for current repo') + .option('-f, --force', 'Skip confirmation prompt') + .option('--all', 'Clean all indexed repos') + .action(cleanCommand); + +program + .command('wiki [path]') + .description('Generate repository wiki from knowledge graph') + .option('-f, --force', 'Force full regeneration even if up to date') + .option('--model ', 'LLM model name (default: minimax/minimax-m2.5)') + .option('--base-url ', 'LLM API base URL (default: OpenAI)') + .option('--api-key ', 'LLM API key (saved to ~/.gitnexus/config.json)') + .option('--concurrency ', 'Parallel LLM calls (default: 3)', '3') + .option('--gist', 'Publish wiki as a public GitHub Gist after generation') + .action(wikiCommand); + +program + .command('augment ') + .description('Augment a search pattern with knowledge graph context (used by hooks)') + .action(augmentCommand); + +// ─── Direct Tool Commands (no MCP overhead) ──────────────────────── +// These invoke LocalBackend directly for use in eval, scripts, and CI. + +program + .command('query ') + .description('Search the knowledge graph for execution flows related to a concept') + .option('-r, --repo ', 'Target repository (omit if only one indexed)') + .option('-c, --context ', 'Task context to improve ranking') + .option('-g, --goal ', 'What you want to find') + .option('-l, --limit ', 'Max processes to return (default: 5)') + .option('--content', 'Include full symbol source code') + .action(queryCommand); + +program + .command('context [name]') + .description('360-degree view of a code symbol: callers, callees, processes') + .option('-r, --repo ', 'Target repository') + .option('-u, --uid ', 'Direct symbol UID (zero-ambiguity lookup)') + .option('-f, --file ', 'File path to disambiguate common names') + .option('--content', 'Include full symbol source code') + .action(contextCommand); + +program + .command('impact ') + .description('Blast radius analysis: what breaks if you change a symbol') + .option('-d, --direction ', 'upstream (dependants) or downstream (dependencies)', 'upstream') + .option('-r, --repo ', 'Target repository') + .option('--depth ', 'Max relationship depth (default: 3)') + .option('--include-tests', 'Include test files in results') + .action(impactCommand); + +program + .command('cypher ') + .description('Execute raw Cypher query against the knowledge graph') + .option('-r, --repo ', 'Target repository') + .action(cypherCommand); + +// ─── Eval Server (persistent daemon for SWE-bench) ───────────────── + +program + .command('eval-server') + .description('Start lightweight HTTP server for fast tool calls during evaluation') + .option('-p, --port ', 'Port number', '4848') + .option('--idle-timeout ', 'Auto-shutdown after N seconds idle (0 = disabled)', '0') + .action(evalServerCommand); + +program.parse(process.argv); diff --git a/gitnexus/src/cli/list.ts b/gitnexus/src/cli/list.ts new file mode 100644 index 000000000..6f4771609 --- /dev/null +++ b/gitnexus/src/cli/list.ts @@ -0,0 +1,34 @@ +/** + * List Command + * + * Shows all indexed repositories from the global registry. + */ + +import { listRegisteredRepos } from '../storage/repo-manager.js'; + +export const listCommand = async () => { + const entries = await listRegisteredRepos({ validate: true }); + + if (entries.length === 0) { + console.log('No indexed repositories found.'); + console.log('Run `gitnexus analyze` in a git repo to index it.'); + return; + } + + console.log(`\n Indexed Repositories (${entries.length})\n`); + + for (const entry of entries) { + const indexedDate = new Date(entry.indexedAt).toLocaleString(); + const stats = entry.stats || {}; + const commitShort = entry.lastCommit?.slice(0, 7) || 'unknown'; + + console.log(` ${entry.name}`); + console.log(` Path: ${entry.path}`); + console.log(` Indexed: ${indexedDate}`); + console.log(` Commit: ${commitShort}`); + console.log(` Stats: ${stats.files ?? 0} files, ${stats.nodes ?? 0} symbols, ${stats.edges ?? 0} edges`); + if (stats.communities) console.log(` Clusters: ${stats.communities}`); + if (stats.processes) console.log(` Processes: ${stats.processes}`); + console.log(''); + } +}; diff --git a/gitnexus/src/cli/mcp.ts b/gitnexus/src/cli/mcp.ts new file mode 100644 index 000000000..be373826b --- /dev/null +++ b/gitnexus/src/cli/mcp.ts @@ -0,0 +1,53 @@ +/** + * MCP Command + * + * Starts the MCP server in standalone mode. + * Loads all indexed repos from the global registry. + * No longer depends on cwd — works from any directory. + */ + +import { startMCPServer } from '../mcp/server.js'; +import { LocalBackend } from '../mcp/local/local-backend.js'; +import { listRegisteredRepos } from '../storage/repo-manager.js'; + +export const mcpCommand = async () => { + // Prevent unhandled errors from crashing the MCP server process. + // KuzuDB lock conflicts and transient errors should degrade gracefully. + process.on('uncaughtException', (err) => { + console.error(`GitNexus MCP: uncaught exception — ${err.message}`); + }); + process.on('unhandledRejection', (reason) => { + const msg = reason instanceof Error ? reason.message : String(reason); + console.error(`GitNexus MCP: unhandled rejection — ${msg}`); + }); + + // Load all registered repos + const entries = await listRegisteredRepos({ validate: true }); + + if (entries.length === 0) { + console.error(''); + console.error(' GitNexus: No indexed repositories found.'); + console.error(''); + console.error(' To get started:'); + console.error(' 1. cd into a git repository'); + console.error(' 2. Run: gitnexus analyze'); + console.error(' 3. Restart your editor'); + console.error(''); + process.exit(1); + } + + // Initialize multi-repo backend from registry + const backend = new LocalBackend(); + const ok = await backend.init(); + + if (!ok) { + console.error('GitNexus: Failed to initialize backend from registry.'); + process.exit(1); + } + + const repoNames = backend.listRepos().map(r => r.name); + console.error(`GitNexus: MCP server starting with ${repoNames.length} repo(s): ${repoNames.join(', ')}`); + + // Start MCP server (serves all repos) + await startMCPServer(backend); +}; diff --git a/gitnexus/src/cli/serve.ts b/gitnexus/src/cli/serve.ts new file mode 100644 index 000000000..8cde8d631 --- /dev/null +++ b/gitnexus/src/cli/serve.ts @@ -0,0 +1,7 @@ +import { createServer } from '../server/api.js'; + +export const serveCommand = async (options?: { port?: string }) => { + const port = Number(options?.port ?? 4747); + await createServer(port); +}; + diff --git a/gitnexus/src/cli/setup.ts b/gitnexus/src/cli/setup.ts new file mode 100644 index 000000000..77515a49a --- /dev/null +++ b/gitnexus/src/cli/setup.ts @@ -0,0 +1,384 @@ +/** + * Setup Command + * + * One-time global MCP configuration writer. + * Detects installed AI editors and writes the appropriate MCP config + * so the GitNexus MCP server is available in all projects. + */ + +import fs from 'fs/promises'; +import path from 'path'; +import os from 'os'; +import { fileURLToPath } from 'url'; +import { getGlobalDir } from '../storage/repo-manager.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +interface SetupResult { + configured: string[]; + skipped: string[]; + errors: string[]; +} + +/** + * The MCP server entry for all editors + */ +function getMcpEntry() { + return { + command: 'npx', + args: ['-y', 'gitnexus@latest', 'mcp'], + }; +} + +/** + * Merge gitnexus entry into an existing MCP config JSON object. + * Returns the updated config. + */ +function mergeMcpConfig(existing: any): any { + if (!existing || typeof existing !== 'object') { + existing = {}; + } + if (!existing.mcpServers || typeof existing.mcpServers !== 'object') { + existing.mcpServers = {}; + } + existing.mcpServers.gitnexus = getMcpEntry(); + return existing; +} + +/** + * Try to read a JSON file, returning null if it doesn't exist or is invalid. + */ +async function readJsonFile(filePath: string): Promise { + try { + const raw = await fs.readFile(filePath, 'utf-8'); + return JSON.parse(raw); + } catch { + return null; + } +} + +/** + * Write JSON to a file, creating parent directories if needed. + */ +async function writeJsonFile(filePath: string, data: any): Promise { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8'); +} + +/** + * Check if a directory exists + */ +async function dirExists(dirPath: string): Promise { + try { + const stat = await fs.stat(dirPath); + return stat.isDirectory(); + } catch { + return false; + } +} + +// ─── Editor-specific setup ───────────────────────────────────────── + +async function setupCursor(result: SetupResult): Promise { + const cursorDir = path.join(os.homedir(), '.cursor'); + if (!(await dirExists(cursorDir))) { + result.skipped.push('Cursor (not installed)'); + return; + } + + const mcpPath = path.join(cursorDir, 'mcp.json'); + try { + const existing = await readJsonFile(mcpPath); + const updated = mergeMcpConfig(existing); + await writeJsonFile(mcpPath, updated); + result.configured.push('Cursor'); + } catch (err: any) { + result.errors.push(`Cursor: ${err.message}`); + } +} + +async function setupClaudeCode(result: SetupResult): Promise { + const claudeDir = path.join(os.homedir(), '.claude'); + const hasClaude = await dirExists(claudeDir); + + if (!hasClaude) { + result.skipped.push('Claude Code (not installed)'); + return; + } + + // Claude Code uses a JSON settings file at ~/.claude.json or claude mcp add + console.log(''); + console.log(' Claude Code detected. Run this command to add GitNexus MCP:'); + console.log(''); + console.log(' claude mcp add gitnexus -- npx -y gitnexus mcp'); + console.log(''); + result.configured.push('Claude Code (MCP manual step printed)'); +} + +/** + * Install GitNexus skills to ~/.claude/skills/ for Claude Code. + */ +async function installClaudeCodeSkills(result: SetupResult): Promise { + const claudeDir = path.join(os.homedir(), '.claude'); + if (!(await dirExists(claudeDir))) return; + + const skillsDir = path.join(claudeDir, 'skills'); + try { + const installed = await installSkillsTo(skillsDir); + if (installed.length > 0) { + result.configured.push(`Claude Code skills (${installed.length} skills → ~/.claude/skills/)`); + } + } catch (err: any) { + result.errors.push(`Claude Code skills: ${err.message}`); + } +} + +/** + * Install GitNexus hooks to ~/.claude/settings.json for Claude Code. + * Merges hook config without overwriting existing hooks. + */ +async function installClaudeCodeHooks(result: SetupResult): Promise { + const claudeDir = path.join(os.homedir(), '.claude'); + if (!(await dirExists(claudeDir))) return; + + const settingsPath = path.join(claudeDir, 'settings.json'); + + // Source hooks bundled within the gitnexus package (hooks/claude/) + const pluginHooksPath = path.join(__dirname, '..', '..', 'hooks', 'claude'); + + // Copy unified hook script to ~/.claude/hooks/gitnexus/ + const destHooksDir = path.join(claudeDir, 'hooks', 'gitnexus'); + + try { + await fs.mkdir(destHooksDir, { recursive: true }); + + const src = path.join(pluginHooksPath, 'gitnexus-hook.cjs'); + const dest = path.join(destHooksDir, 'gitnexus-hook.cjs'); + try { + const content = await fs.readFile(src, 'utf-8'); + await fs.writeFile(dest, content, 'utf-8'); + } catch { + // Script not found in source — skip + } + + const hookCmd = `node "${path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/')}"`; + + // Merge hook config into ~/.claude/settings.json + const existing = await readJsonFile(settingsPath) || {}; + if (!existing.hooks) existing.hooks = {}; + + // NOTE: SessionStart hooks are broken on Windows (Claude Code bug #23576). + // Session context is delivered via CLAUDE.md / skills instead. + + // Add PreToolUse hook if not already present + if (!existing.hooks.PreToolUse) existing.hooks.PreToolUse = []; + const hasPreToolHook = existing.hooks.PreToolUse.some( + (h: any) => h.hooks?.some((hh: any) => hh.command?.includes('gitnexus')) + ); + if (!hasPreToolHook) { + existing.hooks.PreToolUse.push({ + matcher: 'Grep|Glob|Bash', + hooks: [{ + type: 'command', + command: hookCmd, + timeout: 8000, + statusMessage: 'Enriching with GitNexus graph context...', + }], + }); + } + + await writeJsonFile(settingsPath, existing); + result.configured.push('Claude Code hooks (PreToolUse)'); + } catch (err: any) { + result.errors.push(`Claude Code hooks: ${err.message}`); + } +} + +async function setupOpenCode(result: SetupResult): Promise { + const opencodeDir = path.join(os.homedir(), '.config', 'opencode'); + if (!(await dirExists(opencodeDir))) { + result.skipped.push('OpenCode (not installed)'); + return; + } + + const configPath = path.join(opencodeDir, 'config.json'); + try { + const existing = await readJsonFile(configPath); + const config = existing || {}; + if (!config.mcp) config.mcp = {}; + config.mcp.gitnexus = getMcpEntry(); + await writeJsonFile(configPath, config); + result.configured.push('OpenCode'); + } catch (err: any) { + result.errors.push(`OpenCode: ${err.message}`); + } +} + +// ─── Skill Installation ─────────────────────────────────────────── + +const SKILL_NAMES = ['exploring', 'debugging', 'impact-analysis', 'refactoring']; + +/** + * Install GitNexus skills to a target directory. + * Each skill is installed as {targetDir}/gitnexus-{skillName}/SKILL.md + * following the Agent Skills standard (both Cursor and Claude Code). + * + * Supports two source layouts: + * - Flat file: skills/{name}.md → copied as SKILL.md + * - Directory: skills/{name}/SKILL.md → copied recursively (includes references/, etc.) + */ +async function installSkillsTo(targetDir: string): Promise { + const installed: string[] = []; + const skillsRoot = path.join(__dirname, '..', '..', 'skills'); + + for (const skillName of SKILL_NAMES) { + const skillDir = path.join(targetDir, `gitnexus-${skillName}`); + + try { + // Try directory-based skill first (skills/{name}/SKILL.md) + const dirSource = path.join(skillsRoot, skillName); + const dirSkillFile = path.join(dirSource, 'SKILL.md'); + + let isDirectory = false; + try { + const stat = await fs.stat(dirSource); + isDirectory = stat.isDirectory(); + } catch { /* not a directory */ } + + if (isDirectory) { + await copyDirRecursive(dirSource, skillDir); + installed.push(skillName); + } else { + // Fall back to flat file (skills/{name}.md) + const flatSource = path.join(skillsRoot, `${skillName}.md`); + const content = await fs.readFile(flatSource, 'utf-8'); + await fs.mkdir(skillDir, { recursive: true }); + await fs.writeFile(path.join(skillDir, 'SKILL.md'), content, 'utf-8'); + installed.push(skillName); + } + } catch { + // Source skill not found — skip + } + } + + return installed; +} + +/** + * Recursively copy a directory tree. + */ +async function copyDirRecursive(src: string, dest: string): Promise { + await fs.mkdir(dest, { recursive: true }); + const entries = await fs.readdir(src, { withFileTypes: true }); + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + await copyDirRecursive(srcPath, destPath); + } else { + await fs.copyFile(srcPath, destPath); + } + } +} + +/** + * Install global Cursor skills to ~/.cursor/skills/gitnexus/ + */ +async function installCursorSkills(result: SetupResult): Promise { + const cursorDir = path.join(os.homedir(), '.cursor'); + if (!(await dirExists(cursorDir))) return; + + const skillsDir = path.join(cursorDir, 'skills'); + try { + const installed = await installSkillsTo(skillsDir); + if (installed.length > 0) { + result.configured.push(`Cursor skills (${installed.length} skills → ~/.cursor/skills/)`); + } + } catch (err: any) { + result.errors.push(`Cursor skills: ${err.message}`); + } +} + +/** + * Install global OpenCode skills to ~/.config/opencode/skill/gitnexus/ + */ +async function installOpenCodeSkills(result: SetupResult): Promise { + const opencodeDir = path.join(os.homedir(), '.config', 'opencode'); + if (!(await dirExists(opencodeDir))) return; + + const skillsDir = path.join(opencodeDir, 'skill'); + try { + const installed = await installSkillsTo(skillsDir); + if (installed.length > 0) { + result.configured.push(`OpenCode skills (${installed.length} skills → ~/.config/opencode/skill/)`); + } + } catch (err: any) { + result.errors.push(`OpenCode skills: ${err.message}`); + } +} + +// ─── Main command ────────────────────────────────────────────────── + +export const setupCommand = async () => { + console.log(''); + console.log(' GitNexus Setup'); + console.log(' =============='); + console.log(''); + + // Ensure global directory exists + const globalDir = getGlobalDir(); + await fs.mkdir(globalDir, { recursive: true }); + + const result: SetupResult = { + configured: [], + skipped: [], + errors: [], + }; + + // Detect and configure each editor's MCP + await setupCursor(result); + await setupClaudeCode(result); + await setupOpenCode(result); + + // Install global skills for platforms that support them + await installClaudeCodeSkills(result); + await installClaudeCodeHooks(result); + await installCursorSkills(result); + await installOpenCodeSkills(result); + + // Print results + if (result.configured.length > 0) { + console.log(' Configured:'); + for (const name of result.configured) { + console.log(` + ${name}`); + } + } + + if (result.skipped.length > 0) { + console.log(''); + console.log(' Skipped:'); + for (const name of result.skipped) { + console.log(` - ${name}`); + } + } + + if (result.errors.length > 0) { + console.log(''); + console.log(' Errors:'); + for (const err of result.errors) { + console.log(` ! ${err}`); + } + } + + console.log(''); + console.log(' Summary:'); + console.log(` MCP configured for: ${result.configured.filter(c => !c.includes('skills')).join(', ') || 'none'}`); + console.log(` Skills installed to: ${result.configured.filter(c => c.includes('skills')).length > 0 ? result.configured.filter(c => c.includes('skills')).join(', ') : 'none'}`); + console.log(''); + console.log(' Next steps:'); + console.log(' 1. cd into any git repo'); + console.log(' 2. Run: gitnexus analyze'); + console.log(' 3. Open the repo in your editor — MCP is ready!'); + console.log(''); +}; diff --git a/gitnexus/src/cli/status.ts b/gitnexus/src/cli/status.ts new file mode 100644 index 000000000..314e6f062 --- /dev/null +++ b/gitnexus/src/cli/status.ts @@ -0,0 +1,33 @@ +/** + * Status Command + * + * Shows the indexing status of the current repository. + */ + +import { findRepo } from '../storage/repo-manager.js'; +import { getCurrentCommit, isGitRepo } from '../storage/git.js'; + +export const statusCommand = async () => { + const cwd = process.cwd(); + + if (!isGitRepo(cwd)) { + console.log('Not a git repository.'); + return; + } + + const repo = await findRepo(cwd); + if (!repo) { + console.log('Repository not indexed.'); + console.log('Run: gitnexus analyze'); + return; + } + + const currentCommit = getCurrentCommit(repo.repoPath); + const isUpToDate = currentCommit === repo.meta.lastCommit; + + console.log(`Repository: ${repo.repoPath}`); + console.log(`Indexed: ${new Date(repo.meta.indexedAt).toLocaleString()}`); + console.log(`Indexed commit: ${repo.meta.lastCommit?.slice(0, 7)}`); + console.log(`Current commit: ${currentCommit?.slice(0, 7)}`); + console.log(`Status: ${isUpToDate ? '✅ up-to-date' : '⚠️ stale (re-run gitnexus analyze)'}`); +}; diff --git a/gitnexus/src/cli/tool.ts b/gitnexus/src/cli/tool.ts new file mode 100644 index 000000000..41997d80b --- /dev/null +++ b/gitnexus/src/cli/tool.ts @@ -0,0 +1,120 @@ +/** + * Direct CLI Tool Commands + * + * Exposes GitNexus tools (query, context, impact, cypher) as direct CLI commands. + * Bypasses MCP entirely — invokes LocalBackend directly for minimal overhead. + * + * Usage: + * gitnexus query "authentication flow" + * gitnexus context --name "validateUser" + * gitnexus impact --target "AuthService" --direction upstream + * gitnexus cypher "MATCH (n:Function) RETURN n.name LIMIT 10" + * + * Note: Output goes to stderr because KuzuDB's native module captures stdout + * at the OS level during init. This is consistent with augment.ts. + */ + +import { LocalBackend } from '../mcp/local/local-backend.js'; + +let _backend: LocalBackend | null = null; + +async function getBackend(): Promise { + if (_backend) return _backend; + _backend = new LocalBackend(); + const ok = await _backend.init(); + if (!ok) { + console.error('GitNexus: No indexed repositories found. Run: gitnexus analyze'); + process.exit(1); + } + return _backend; +} + +function output(data: any): void { + const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2); + // stderr because KuzuDB captures stdout at OS level + process.stderr.write(text + '\n'); +} + +export async function queryCommand(queryText: string, options?: { + repo?: string; + context?: string; + goal?: string; + limit?: string; + content?: boolean; +}): Promise { + if (!queryText?.trim()) { + console.error('Usage: gitnexus query '); + process.exit(1); + } + + const backend = await getBackend(); + const result = await backend.callTool('query', { + query: queryText, + task_context: options?.context, + goal: options?.goal, + limit: options?.limit ? parseInt(options.limit) : undefined, + include_content: options?.content ?? false, + repo: options?.repo, + }); + output(result); +} + +export async function contextCommand(name: string, options?: { + repo?: string; + file?: string; + uid?: string; + content?: boolean; +}): Promise { + if (!name?.trim() && !options?.uid) { + console.error('Usage: gitnexus context [--uid ] [--file ]'); + process.exit(1); + } + + const backend = await getBackend(); + const result = await backend.callTool('context', { + name: name || undefined, + uid: options?.uid, + file_path: options?.file, + include_content: options?.content ?? false, + repo: options?.repo, + }); + output(result); +} + +export async function impactCommand(target: string, options?: { + direction?: string; + repo?: string; + depth?: string; + includeTests?: boolean; +}): Promise { + if (!target?.trim()) { + console.error('Usage: gitnexus impact [--direction upstream|downstream]'); + process.exit(1); + } + + const backend = await getBackend(); + const result = await backend.callTool('impact', { + target, + direction: options?.direction || 'upstream', + maxDepth: options?.depth ? parseInt(options.depth) : undefined, + includeTests: options?.includeTests ?? false, + repo: options?.repo, + }); + output(result); +} + +export async function cypherCommand(query: string, options?: { + repo?: string; +}): Promise { + if (!query?.trim()) { + console.error('Usage: gitnexus cypher '); + process.exit(1); + } + + const backend = await getBackend(); + const result = await backend.callTool('cypher', { + query, + repo: options?.repo, + }); + output(result); +} diff --git a/gitnexus/src/cli/wiki.ts b/gitnexus/src/cli/wiki.ts new file mode 100644 index 000000000..ac626fe80 --- /dev/null +++ b/gitnexus/src/cli/wiki.ts @@ -0,0 +1,414 @@ +/** + * Wiki Command + * + * Generates repository documentation from the knowledge graph. + * Usage: gitnexus wiki [path] [options] + */ + +import path from 'path'; +import readline from 'readline'; +import { execSync } from 'child_process'; +import cliProgress from 'cli-progress'; +import { getGitRoot, isGitRepo } from '../storage/git.js'; +import { getStoragePaths, loadMeta, loadCLIConfig, saveCLIConfig } from '../storage/repo-manager.js'; +import { WikiGenerator, type WikiOptions } from '../core/wiki/generator.js'; +import { resolveLLMConfig } from '../core/wiki/llm-client.js'; + +export interface WikiCommandOptions { + force?: boolean; + model?: string; + baseUrl?: string; + apiKey?: string; + concurrency?: string; + gist?: boolean; +} + +/** + * Prompt the user for input via stdin. + */ +function prompt(question: string, hide = false): Promise { + return new Promise((resolve) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + if (hide && process.stdin.isTTY) { + // Mask input for API keys + process.stdout.write(question); + let input = ''; + process.stdin.setRawMode(true); + process.stdin.resume(); + process.stdin.setEncoding('utf-8'); + + const onData = (char: string) => { + if (char === '\n' || char === '\r' || char === '\u0004') { + process.stdin.setRawMode(false); + process.stdin.removeListener('data', onData); + process.stdout.write('\n'); + rl.close(); + resolve(input); + } else if (char === '\u0003') { + // Ctrl+C + process.stdin.setRawMode(false); + rl.close(); + process.exit(1); + } else if (char === '\u007F' || char === '\b') { + // Backspace + if (input.length > 0) { + input = input.slice(0, -1); + process.stdout.write('\b \b'); + } + } else { + input += char; + process.stdout.write('*'); + } + }; + process.stdin.on('data', onData); + } else { + rl.question(question, (answer) => { + rl.close(); + resolve(answer.trim()); + }); + } + }); +} + +export const wikiCommand = async ( + inputPath?: string, + options?: WikiCommandOptions, +) => { + console.log('\n GitNexus Wiki Generator\n'); + + // ── Resolve repo path ─────────────────────────────────────────────── + let repoPath: string; + if (inputPath) { + repoPath = path.resolve(inputPath); + } else { + const gitRoot = getGitRoot(process.cwd()); + if (!gitRoot) { + console.log(' Error: Not inside a git repository\n'); + process.exitCode = 1; + return; + } + repoPath = gitRoot; + } + + if (!isGitRepo(repoPath)) { + console.log(' Error: Not a git repository\n'); + process.exitCode = 1; + return; + } + + // ── Check for existing index ──────────────────────────────────────── + const { storagePath, kuzuPath } = getStoragePaths(repoPath); + const meta = await loadMeta(storagePath); + + if (!meta) { + console.log(' Error: No GitNexus index found.'); + console.log(' Run `gitnexus analyze` first to index this repository.\n'); + process.exitCode = 1; + return; + } + + // ── Resolve LLM config (with interactive fallback) ───────────────── + // Save any CLI overrides immediately + if (options?.apiKey || options?.model || options?.baseUrl) { + const existing = await loadCLIConfig(); + const updates: Record = {}; + if (options.apiKey) updates.apiKey = options.apiKey; + if (options.model) updates.model = options.model; + if (options.baseUrl) updates.baseUrl = options.baseUrl; + await saveCLIConfig({ ...existing, ...updates }); + console.log(' Config saved to ~/.gitnexus/config.json\n'); + } + + const savedConfig = await loadCLIConfig(); + const hasSavedConfig = !!(savedConfig.apiKey && savedConfig.baseUrl); + const hasCLIOverrides = !!(options?.apiKey || options?.model || options?.baseUrl); + + let llmConfig = await resolveLLMConfig({ + model: options?.model, + baseUrl: options?.baseUrl, + apiKey: options?.apiKey, + }); + + // Run interactive setup if no saved config and no CLI flags provided + // (even if env vars exist — let user explicitly choose their provider) + if (!hasSavedConfig && !hasCLIOverrides) { + if (!process.stdin.isTTY) { + if (!llmConfig.apiKey) { + console.log(' Error: No LLM API key found.'); + console.log(' Set OPENAI_API_KEY or GITNEXUS_API_KEY environment variable,'); + console.log(' or pass --api-key .\n'); + process.exitCode = 1; + return; + } + // Non-interactive with env var — just use it + } else { + console.log(' No LLM configured. Let\'s set it up.\n'); + console.log(' Supports OpenAI, OpenRouter, or any OpenAI-compatible API.\n'); + + // Provider selection + console.log(' [1] OpenAI (api.openai.com)'); + console.log(' [2] OpenRouter (openrouter.ai)'); + console.log(' [3] Custom endpoint\n'); + + const choice = await prompt(' Select provider (1/2/3): '); + + let baseUrl: string; + let defaultModel: string; + + if (choice === '2') { + baseUrl = 'https://openrouter.ai/api/v1'; + defaultModel = 'minimax/minimax-m2.5'; + } else if (choice === '3') { + baseUrl = await prompt(' Base URL (e.g. http://localhost:11434/v1): '); + if (!baseUrl) { + console.log('\n No URL provided. Aborting.\n'); + process.exitCode = 1; + return; + } + defaultModel = 'gpt-4o-mini'; + } else { + baseUrl = 'https://api.openai.com/v1'; + defaultModel = 'gpt-4o-mini'; + } + + // Model + const modelInput = await prompt(` Model (default: ${defaultModel}): `); + const model = modelInput || defaultModel; + + // API key — pre-fill hint if env var exists + const envKey = process.env.GITNEXUS_API_KEY || process.env.OPENAI_API_KEY || ''; + let key: string; + if (envKey) { + const masked = envKey.slice(0, 6) + '...' + envKey.slice(-4); + const useEnv = await prompt(` Use existing env key (${masked})? (Y/n): `); + if (!useEnv || useEnv.toLowerCase() === 'y' || useEnv.toLowerCase() === 'yes') { + key = envKey; + } else { + key = await prompt(' API key: ', true); + } + } else { + key = await prompt(' API key: ', true); + } + + if (!key) { + console.log('\n No key provided. Aborting.\n'); + process.exitCode = 1; + return; + } + + // Save + await saveCLIConfig({ apiKey: key, baseUrl, model }); + console.log(' Config saved to ~/.gitnexus/config.json\n'); + + llmConfig = { ...llmConfig, apiKey: key, baseUrl, model }; + } + } + + // ── Setup progress bar with elapsed timer ────────────────────────── + const bar = new cliProgress.SingleBar({ + format: ' {bar} {percentage}% | {phase}', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true, + barGlue: '', + autopadding: true, + clearOnComplete: false, + stopOnComplete: false, + }, cliProgress.Presets.shades_grey); + + bar.start(100, 0, { phase: 'Initializing...' }); + + const t0 = Date.now(); + let lastPhase = ''; + let phaseStart = t0; + + // Tick elapsed time every second while stuck on the same phase + const elapsedTimer = setInterval(() => { + if (lastPhase) { + const elapsed = Math.round((Date.now() - phaseStart) / 1000); + if (elapsed >= 3) { + bar.update({ phase: `${lastPhase} (${elapsed}s)` }); + } + } + }, 1000); + + // ── Run generator ─────────────────────────────────────────────────── + const wikiOptions: WikiOptions = { + force: options?.force, + model: options?.model, + baseUrl: options?.baseUrl, + concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined, + }; + + const generator = new WikiGenerator( + repoPath, + storagePath, + kuzuPath, + llmConfig, + wikiOptions, + (phase, percent, detail) => { + const label = detail || phase; + if (label !== lastPhase) { + lastPhase = label; + phaseStart = Date.now(); + } + bar.update(percent, { phase: label }); + }, + ); + + try { + const result = await generator.run(); + + clearInterval(elapsedTimer); + bar.update(100, { phase: 'Done' }); + bar.stop(); + + const elapsed = ((Date.now() - t0) / 1000).toFixed(1); + + const wikiDir = path.join(storagePath, 'wiki'); + const viewerPath = path.join(wikiDir, 'index.html'); + + if (result.mode === 'up-to-date' && !options?.force) { + console.log('\n Wiki is already up to date.'); + console.log(` Viewer: ${viewerPath}\n`); + await maybePublishGist(viewerPath, options?.gist); + return; + } + + console.log(`\n Wiki generated successfully (${elapsed}s)\n`); + console.log(` Mode: ${result.mode}`); + console.log(` Pages: ${result.pagesGenerated}`); + console.log(` Output: ${wikiDir}`); + console.log(` Viewer: ${viewerPath}`); + + if (result.failedModules && result.failedModules.length > 0) { + console.log(`\n Failed modules (${result.failedModules.length}):`); + for (const mod of result.failedModules) { + console.log(` - ${mod}`); + } + console.log(' Re-run to retry failed modules (pages will be regenerated).'); + } + + console.log(''); + + await maybePublishGist(viewerPath, options?.gist); + } catch (err: any) { + clearInterval(elapsedTimer); + bar.stop(); + + if (err.message?.includes('No source files')) { + console.log(`\n ${err.message}\n`); + } else if (err.message?.includes('API key') || err.message?.includes('API error')) { + console.log(`\n LLM Error: ${err.message}\n`); + + // Offer to reconfigure on auth-related failures + const isAuthError = err.message?.includes('401') || err.message?.includes('403') + || err.message?.includes('502') || err.message?.includes('authenticate') + || err.message?.includes('Unauthorized'); + if (isAuthError && process.stdin.isTTY) { + const answer = await new Promise((resolve) => { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + rl.question(' Reconfigure LLM settings? (Y/n): ', (ans) => { rl.close(); resolve(ans.trim().toLowerCase()); }); + }); + if (!answer || answer === 'y' || answer === 'yes') { + // Clear saved config so next run triggers interactive setup + await saveCLIConfig({}); + console.log(' Config cleared. Run `gitnexus wiki` again to reconfigure.\n'); + } + } + } else { + console.log(`\n Error: ${err.message}\n`); + if (process.env.DEBUG) { + console.error(err); + } + } + process.exitCode = 1; + } +}; + +// ─── Gist Publishing ─────────────────────────────────────────────────── + +function hasGhCLI(): boolean { + try { + execSync('gh --version', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + +function publishGist(htmlPath: string): { url: string; rawUrl: string } | null { + try { + const output = execSync( + `gh gist create "${htmlPath}" --desc "Repository Wiki — generated by GitNexus" --public`, + { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }, + ).trim(); + + // gh gist create prints the gist URL as the last line + const lines = output.split('\n'); + const gistUrl = lines.find(l => l.includes('gist.github.com')) || lines[lines.length - 1]; + + if (!gistUrl || !gistUrl.includes('gist.github.com')) return null; + + // Build a raw viewer URL via gist.githack.com + // gist URL format: https://gist.github.com/{user}/{id} + const match = gistUrl.match(/gist\.github\.com\/([^/]+)\/([a-f0-9]+)/); + let rawUrl = gistUrl; + if (match) { + rawUrl = `https://gistcdn.githack.com/${match[1]}/${match[2]}/raw/index.html`; + } + + return { url: gistUrl.trim(), rawUrl }; + } catch { + return null; + } +} + +async function maybePublishGist(htmlPath: string, gistFlag?: boolean): Promise { + if (gistFlag === false) return; + + // Check that the HTML file exists + try { + const fs = await import('fs/promises'); + await fs.access(htmlPath); + } catch { + return; + } + + if (!hasGhCLI()) { + if (gistFlag) { + console.log(' GitHub CLI (gh) is not installed. Cannot publish gist.'); + console.log(' Install it: https://cli.github.com\n'); + } + return; + } + + let shouldPublish = !!gistFlag; + + if (!shouldPublish && process.stdin.isTTY) { + const answer = await new Promise((resolve) => { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + rl.question(' Publish wiki as a GitHub Gist for easy viewing? (Y/n): ', (ans) => { + rl.close(); + resolve(ans.trim().toLowerCase()); + }); + }); + shouldPublish = !answer || answer === 'y' || answer === 'yes'; + } + + if (!shouldPublish) return; + + console.log('\n Publishing to GitHub Gist...'); + const result = publishGist(htmlPath); + + if (result) { + console.log(` Gist: ${result.url}`); + console.log(` Viewer: ${result.rawUrl}\n`); + } else { + console.log(' Failed to publish gist. Make sure `gh auth login` is configured.\n'); + } +} diff --git a/gitnexus/src/components/ActivityFeed.tsx b/gitnexus/src/components/ActivityFeed.tsx deleted file mode 100644 index a802b859b..000000000 --- a/gitnexus/src/components/ActivityFeed.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Activity Feed Component - * - * Shows real-time log of external AI agent tool calls. - * Used in RightPanel as an alternative to the Chat tab. - */ - -import { useState, useEffect, useRef } from 'react'; -import { Activity, Search, Database, Terminal, Loader2, CheckCircle, XCircle, Clock, FileText, Zap, Map, Compass } from 'lucide-react'; -import { getMCPClient, type ActivityEvent } from '../core/mcp/mcp-client'; - -// Tool icons -const TOOL_ICONS: Record = { - context: Zap, - search: Search, - cypher: Database, - grep: Terminal, - read: FileText, - impact: Activity, - overview: Map, - explore: Compass, -}; - -// Tool colors -const TOOL_COLORS: Record = { - context: 'text-amber-400', - search: 'text-cyan-400', - cypher: 'text-purple-400', - grep: 'text-green-400', - read: 'text-blue-400', - impact: 'text-rose-400', - overview: 'text-indigo-400', - explore: 'text-teal-400', -}; - -export function ActivityFeed() { - const [events, setEvents] = useState([]); - const containerRef = useRef(null); - - useEffect(() => { - const client = getMCPClient(); - - // Subscribe to activity events - const unsubscribe = client.onActivity((event) => { - setEvents(prev => { - // Keep max 100 events - const next = [...prev, event]; - if (next.length > 100) { - next.shift(); - } - return next; - }); - }); - - // Get existing events - setEvents(client.getActivityLog()); - - return () => { - unsubscribe(); - }; - }, []); - - // Auto-scroll to bottom - useEffect(() => { - if (containerRef.current) { - containerRef.current.scrollTop = containerRef.current.scrollHeight; - } - }, [events]); - - const formatTime = (timestamp: number) => { - const date = new Date(timestamp); - return date.toLocaleTimeString('en-US', { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); - }; - - const formatParams = (params: any): string => { - if (!params) return ''; - // Show first key-value pairs, truncated - const entries = Object.entries(params).slice(0, 2); - return entries.map(([k, v]) => { - const val = typeof v === 'string' ? v.slice(0, 30) : JSON.stringify(v).slice(0, 30); - return `${k}: ${val}${val.length >= 30 ? '...' : ''}`; - }).join(', '); - }; - - const formatResult = (event: ActivityEvent): string => { - if (event.status === 'running') return 'Running...'; - if (event.status === 'error') return `Error: ${event.error?.slice(0, 50) || 'Unknown'}`; - - // Format result based on type - if (Array.isArray(event.result)) { - return `${event.result.length} results`; - } - if (typeof event.result === 'object' && event.result) { - const keys = Object.keys(event.result); - if (keys.includes('content')) return `${event.result.content?.length || 0} chars`; - if (keys.includes('projectName')) return event.result.projectName; - return `{${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`; - } - return String(event.result || 'Done'); - }; - - if (events.length === 0) { - return ( -
-
- 📡 -
-

- No Agent Activity -

-

- When external AI agents (Cursor, Claude Code) call GitNexus tools, - their activity will appear here in real-time. -

-

- Make sure MCP toggle is enabled in the header -

-
- ); - } - - return ( -
-
- {events.map((event) => { - const Icon = TOOL_ICONS[event.tool] || Activity; - const color = TOOL_COLORS[event.tool] || 'text-text-muted'; - - return ( -
- {/* Header row */} -
- {/* Agent color indicator */} - {event.agentColor && ( -
- )} - - {event.tool} - {event.agentName && event.agentName !== 'Unknown' && ( - - {event.agentName} - - )} - - - {formatTime(event.timestamp)} - -
- - {/* Params preview */} - {event.params && Object.keys(event.params).length > 0 && ( -
- {formatParams(event.params)} -
- )} - - {/* Status/Result */} -
- {event.status === 'running' && ( - <> - - Running... - - )} - {event.status === 'complete' && ( - <> - - {formatResult(event)} - {event.duration && ( - {event.duration}ms - )} - - )} - {event.status === 'error' && ( - <> - - {formatResult(event)} - - )} -
-
- ); - })} -
-
- ); -} diff --git a/gitnexus/src/components/IntelligentClusteringModal.tsx b/gitnexus/src/components/IntelligentClusteringModal.tsx deleted file mode 100644 index ec9ab92a2..000000000 --- a/gitnexus/src/components/IntelligentClusteringModal.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { Brain, Sparkles, X, Settings } from 'lucide-react'; - -interface IntelligentClusteringModalProps { - isOpen: boolean; - onClose: () => void; - onEnable: () => void; - onConfigure: () => void; -} - -export const IntelligentClusteringModal = ({ - isOpen, - onClose, - onEnable, - onConfigure -}: IntelligentClusteringModalProps) => { - if (!isOpen) return null; - - // Parent handles all state updates via onEnable/onConfigure/onClose - const handleEnable = () => { - onEnable(); - }; - - const handleSkip = () => { - onClose(); - }; - - - return ( -
- {/* Backdrop */} -
- - {/* Modal Content */} -
- - {/* Header with cool gradient background */} -
-
- -
- - - -
-
- -
-
- -

- Upgrade to Intelligent Clustering? -

-

- Your clusters are ready, but they could be smarter! Right now they're just named after folders. -

-
- - {/* Body */} -
- -
-

- - What you get: -

-
    -
  • - - Semantic names (e.g., "Auth System" vs "utils") -
  • -
  • - - Search keywords for better agent context -
  • -
  • - - Descriptions of what the code actually does -
  • -
-
- - {/* How it works */} -
-
-
- -
-
-

Uses Your Configured LLM

-

- Runs on your own API key. Very low token usage (~$0.01 for most codebases). -
- 💡 Tip: Use a cheaper model like GPT-4o-mini in settings! -

-
-
-
- -
- - {/* Actions */} -
- - -
- -
- -
-
- -
-
- ); -}; diff --git a/gitnexus/src/components/MCPToggle.tsx b/gitnexus/src/components/MCPToggle.tsx deleted file mode 100644 index aa05cd04d..000000000 --- a/gitnexus/src/components/MCPToggle.tsx +++ /dev/null @@ -1,300 +0,0 @@ -/** - * MCP Toggle Component - * - * Toggle for enabling MCP exposure to external AI agents (Cursor, Claude, etc.) - * Shows MCP config for setup and connection status. - */ - -import { useState, useEffect, useCallback, useRef } from 'react'; -import { Copy, Check, X, Sparkles, Zap, ExternalLink } from 'lucide-react'; -import { getMCPClient, type CodebaseContext } from '../core/mcp/mcp-client'; - -type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error'; - -interface MCPToggleProps { - onSearch?: (query: string, limit?: number) => Promise; - onCypher?: (query: string) => Promise; - onImpact?: (nodeId: string, hops?: number) => Promise; - onGrep?: (pattern: string, caseSensitive?: boolean, maxResults?: number) => Promise; - onRead?: (filePath: string, startLine?: number, endLine?: number) => Promise; - onOverview?: () => Promise; - onExplore?: (target: string, type?: 'symbol' | 'cluster' | 'process') => Promise; - showOnboardingTip?: boolean; - getContext?: () => Promise; -} - -const MCP_TIP_DISMISSED_KEY = 'gitnexus-mcp-tip-dismissed'; - -// MCP config that users copy to their AI agent -const MCP_CONFIG = `{ - "mcpServers": { - "gitnexus": { - "command": "npx", - "args": ["--prefer-online", "-y", "gitnexus-mcp@latest"] - } - } -}`; - -export function MCPToggle({ - onSearch, - onCypher, - onImpact, - onGrep, - onRead, - onOverview, - onExplore, - showOnboardingTip = false, - getContext, -}: MCPToggleProps = {}) { - const [status, setStatus] = useState('disconnected'); - const [copied, setCopied] = useState(false); - const [showPopup, setShowPopup] = useState(false); - const popupRef = useRef(null); - const [showTip, setShowTip] = useState(false); - - const isConnected = status === 'connected'; - const isConnecting = status === 'connecting'; - - // Show tip when graph becomes ready - useEffect(() => { - if (showOnboardingTip) { - const dismissed = localStorage.getItem(MCP_TIP_DISMISSED_KEY); - if (!dismissed) { - const timer = setTimeout(() => setShowTip(true), 1500); - return () => clearTimeout(timer); - } - } - }, [showOnboardingTip]); - - // Close popup when clicking outside - useEffect(() => { - if (!showPopup) return; - const handleClickOutside = (event: MouseEvent) => { - if (popupRef.current && !popupRef.current.contains(event.target as Node)) { - setShowPopup(false); - } - }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); - }, [showPopup]); - - const dismissTip = () => { - setShowTip(false); - localStorage.setItem(MCP_TIP_DISMISSED_KEY, 'true'); - }; - - const connect = useCallback(async () => { - const client = getMCPClient(); - setStatus('connecting'); - setShowTip(false); - - try { - await client.connect(); - - // Register tool handlers - if (onSearch) client.registerHandler('search', async (params) => onSearch(params.query, params.limit)); - if (onCypher) client.registerHandler('cypher', async (params) => onCypher(params.query)); - if (onImpact) client.registerHandler('impact', async (params) => onImpact(params.nodeId, params.hops)); - if (onGrep) client.registerHandler('grep', async (params) => onGrep(params.pattern, params.caseSensitive, params.maxResults)); - if (onRead) client.registerHandler('read', async (params) => onRead(params.filePath, params.startLine, params.endLine)); - if (onOverview) client.registerHandler('overview', async () => onOverview()); - if (onExplore) client.registerHandler('explore', async (params) => onExplore(params.target, params.type)); - if (getContext) client.registerHandler('context', async () => getContext()); - - setStatus('connected'); - setShowPopup(false); - localStorage.setItem(MCP_TIP_DISMISSED_KEY, 'true'); - - // Send context after connecting - if (getContext) { - try { - const context = await getContext(); - if (context) client.sendContext(context); - } catch (e) { - console.error('[MCP] Failed to send context:', e); - } - } - } catch { - setStatus('error'); - } - }, [onSearch, onCypher, onImpact, onGrep, onRead, onOverview, onExplore, getContext]); - - const disconnect = useCallback(() => { - const client = getMCPClient(); - client.disconnect(); - setStatus('disconnected'); - }, []); - - const toggle = useCallback(() => { - if (isConnected) { - disconnect(); - } else if (!isConnecting) { - connect(); - } - }, [isConnected, isConnecting, connect, disconnect]); - - const copyConfig = () => { - navigator.clipboard.writeText(MCP_CONFIG); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - // Listen for connection changes - useEffect(() => { - const client = getMCPClient(); - const unsubscribe = client.onConnectionChange((connected) => { - setStatus(connected ? 'connected' : 'disconnected'); - if (connected) setShowPopup(false); - }); - return () => { unsubscribe(); }; - }, []); - - return ( -
- {/* MCP Button */} - - - {/* Popup */} - {showPopup && ( -
- {/* Header */} -
-
-
-
- -
-
-

Connect AI Agents

-

Cursor, Claude Code, Antigravity

-
-
- -
-
- - {/* Content */} -
- {/* Step 1: Config */} -
-
- 1 - Add to your AI agent's MCP config -
-
-
-                                    {MCP_CONFIG}
-                                
- -
-
- - {/* Step 2: Connect */} -
-
- 2 - Connect browser to daemon -
- -
- - {/* Status message */} - {status === 'error' && ( -

- Daemon not running. Make sure your AI agent has started gitnexus-mcp. -

- )} - - {/* Help link */} - - Learn more - - -
-
- )} - - {/* Onboarding Tip */} - {showTip && !isConnected && !showPopup && ( -
- -
-
- -
-
-

- Connect your AI tools -

-

- Let Cursor or Claude access GitNexus code intelligence. -

- -
-
-
- )} -
- ); -} diff --git a/gitnexus/src/core/augmentation/engine.ts b/gitnexus/src/core/augmentation/engine.ts new file mode 100644 index 000000000..41ce54eb3 --- /dev/null +++ b/gitnexus/src/core/augmentation/engine.ts @@ -0,0 +1,248 @@ +/** + * Augmentation Engine + * + * Lightweight, fast-path enrichment of search patterns with knowledge graph context. + * Designed to be called from platform hooks (Claude Code PreToolUse, Cursor beforeShellExecution) + * when an agent runs grep/glob/search. + * + * Performance target: <500ms cold start, <200ms warm. + * + * Design decisions: + * - Uses only BM25 search (no semantic/embedding) for speed + * - Clusters used internally for ranking, NEVER in output + * - Output is pure relationships: callers, callees, process participation + * - Graceful failure: any error → return empty string + */ + +import path from 'path'; +import { listRegisteredRepos } from '../../storage/repo-manager.js'; + +/** + * Find the best matching repo for a given working directory. + * Matches by checking if cwd is within the repo's path. + */ +async function findRepoForCwd(cwd: string): Promise<{ + name: string; + storagePath: string; + kuzuPath: string; +} | null> { + try { + const entries = await listRegisteredRepos({ validate: true }); + const resolved = path.resolve(cwd); + + // Normalize to lowercase on Windows (drive letters can differ: D: vs d:) + const isWindows = process.platform === 'win32'; + const normalizedCwd = isWindows ? resolved.toLowerCase() : resolved; + const sep = path.sep; + + // Find the LONGEST matching repo path (most specific match wins) + let bestMatch: typeof entries[0] | null = null; + let bestLen = 0; + + for (const entry of entries) { + const repoResolved = path.resolve(entry.path); + const normalizedRepo = isWindows ? repoResolved.toLowerCase() : repoResolved; + + // Check if cwd is inside repo OR repo is inside cwd + // Must match at a path separator boundary to avoid false positives + // (e.g. /projects/gitnexusv2 should NOT match /projects/gitnexus) + let matched = false; + if (normalizedCwd === normalizedRepo) { + matched = true; + } else if (normalizedCwd.startsWith(normalizedRepo + sep)) { + matched = true; + } else if (normalizedRepo.startsWith(normalizedCwd + sep)) { + matched = true; + } + + if (matched && normalizedRepo.length > bestLen) { + bestMatch = entry; + bestLen = normalizedRepo.length; + } + } + + if (!bestMatch) return null; + + return { + name: bestMatch.name, + storagePath: bestMatch.storagePath, + kuzuPath: path.join(bestMatch.storagePath, 'kuzu'), + }; + } catch { + return null; + } +} + +/** + * Augment a search pattern with knowledge graph context. + * + * 1. BM25 search for the pattern + * 2. For top matches, fetch callers/callees/processes + * 3. Rank by internal cluster cohesion (not exposed) + * 4. Format as structured text block + * + * Returns empty string on any error (graceful failure). + */ +export async function augment(pattern: string, cwd?: string): Promise { + if (!pattern || pattern.length < 3) return ''; + + const workDir = cwd || process.cwd(); + + try { + const repo = await findRepoForCwd(workDir); + if (!repo) return ''; + + // Lazy-load kuzu adapter (skip unnecessary init) + const { initKuzu, executeQuery, isKuzuReady } = await import('../../mcp/core/kuzu-adapter.js'); + const { searchFTSFromKuzu } = await import('../search/bm25-index.js'); + + const repoId = repo.name.toLowerCase(); + + // Init KuzuDB if not already + if (!isKuzuReady(repoId)) { + await initKuzu(repoId, repo.kuzuPath); + } + + // Step 1: BM25 search (fast, no embeddings) + const bm25Results = await searchFTSFromKuzu(pattern, 10, repoId); + + if (bm25Results.length === 0) return ''; + + // Step 2: Map BM25 file results to symbols + const symbolMatches: Array<{ + nodeId: string; + name: string; + type: string; + filePath: string; + score: number; + }> = []; + + for (const result of bm25Results.slice(0, 5)) { + const escaped = result.filePath.replace(/'/g, "''"); + try { + const symbols = await executeQuery(repoId, ` + MATCH (n) WHERE n.filePath = '${escaped}' + AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}' + RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath + LIMIT 3 + `); + for (const sym of symbols) { + symbolMatches.push({ + nodeId: sym.id || sym[0], + name: sym.name || sym[1], + type: sym.type || sym[2], + filePath: sym.filePath || sym[3], + score: result.score, + }); + } + } catch { /* skip */ } + } + + if (symbolMatches.length === 0) return ''; + + // Step 3: For top matches, fetch callers/callees/processes + // Also get cluster cohesion internally for ranking + const enriched: Array<{ + name: string; + filePath: string; + callers: string[]; + callees: string[]; + processes: string[]; + cohesion: number; + }> = []; + + const seen = new Set(); + + for (const sym of symbolMatches.slice(0, 5)) { + if (seen.has(sym.nodeId)) continue; + seen.add(sym.nodeId); + + const escaped = sym.nodeId.replace(/'/g, "''"); + + // Callers + let callers: string[] = []; + try { + const rows = await executeQuery(repoId, ` + MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${escaped}'}) + RETURN caller.name AS name + LIMIT 3 + `); + callers = rows.map((r: any) => r.name || r[0]).filter(Boolean); + } catch { /* skip */ } + + // Callees + let callees: string[] = []; + try { + const rows = await executeQuery(repoId, ` + MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'CALLS'}]->(callee) + RETURN callee.name AS name + LIMIT 3 + `); + callees = rows.map((r: any) => r.name || r[0]).filter(Boolean); + } catch { /* skip */ } + + // Processes + let processes: string[] = []; + try { + const rows = await executeQuery(repoId, ` + MATCH (n {id: '${escaped}'})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) + RETURN p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount + `); + processes = rows.map((r: any) => { + const label = r.label || r[0]; + const step = r.step || r[1]; + const stepCount = r.stepCount || r[2]; + return `${label} (step ${step}/${stepCount})`; + }).filter(Boolean); + } catch { /* skip */ } + + // Cluster cohesion (internal ranking signal) + let cohesion = 0; + try { + const rows = await executeQuery(repoId, ` + MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) + RETURN c.cohesion AS cohesion + LIMIT 1 + `); + if (rows.length > 0) { + cohesion = (rows[0].cohesion ?? rows[0][0]) || 0; + } + } catch { /* skip */ } + + enriched.push({ + name: sym.name, + filePath: sym.filePath, + callers, + callees, + processes, + cohesion, + }); + } + + if (enriched.length === 0) return ''; + + // Step 4: Rank by cohesion (internal signal) and format + enriched.sort((a, b) => b.cohesion - a.cohesion); + + const lines: string[] = [`[GitNexus] ${enriched.length} related symbols found:`, '']; + + for (const item of enriched) { + lines.push(`${item.name} (${item.filePath})`); + if (item.callers.length > 0) { + lines.push(` Called by: ${item.callers.join(', ')}`); + } + if (item.callees.length > 0) { + lines.push(` Calls: ${item.callees.join(', ')}`); + } + if (item.processes.length > 0) { + lines.push(` Flows: ${item.processes.join(', ')}`); + } + lines.push(''); + } + + return lines.join('\n').trim(); + } catch { + // Graceful failure — never break the original tool + return ''; + } +} diff --git a/gitnexus/src/core/embeddings/embedder.ts b/gitnexus/src/core/embeddings/embedder.ts index 118894583..aeb084d1a 100644 --- a/gitnexus/src/core/embeddings/embedder.ts +++ b/gitnexus/src/core/embeddings/embedder.ts @@ -8,59 +8,23 @@ */ import { pipeline, env, type FeatureExtractionPipeline } from '@huggingface/transformers'; -import { DEFAULT_EMBEDDING_CONFIG, type EmbeddingConfig, type ModelProgress } from './types'; +import { DEFAULT_EMBEDDING_CONFIG, type EmbeddingConfig, type ModelProgress } from './types.js'; // Module-level state for singleton pattern let embedderInstance: FeatureExtractionPipeline | null = null; let isInitializing = false; let initPromise: Promise | null = null; -let currentDevice: 'webgpu' | 'wasm' | null = null; +let currentDevice: 'dml' | 'cuda' | 'cpu' | 'wasm' | null = null; /** * Progress callback type for model loading */ export type ModelProgressCallback = (progress: ModelProgress) => void; -/** - * Custom error thrown when WebGPU is not available - * Allows UI to prompt user for fallback choice - */ -export class WebGPUNotAvailableError extends Error { - constructor(originalError?: Error) { - super('WebGPU not available in this browser'); - this.name = 'WebGPUNotAvailableError'; - this.cause = originalError; - } -} - -/** - * Check if WebGPU is available in this browser - * Quick check without loading the model - */ -export const checkWebGPUAvailability = async (): Promise => { - try { - // Cast to any to avoid WebGPU types not being available in all TS configs - const nav = navigator as any; - if (!nav.gpu) { - return false; - } - const adapter = await nav.gpu.requestAdapter(); - if (!adapter) { - return false; - } - // Try to get a device - this is where it usually fails - const device = await adapter.requestDevice(); - device.destroy(); // Clean up - return true; - } catch { - return false; - } -}; - /** * Get the current device being used for inference */ -export const getCurrentDevice = (): 'webgpu' | 'wasm' | null => currentDevice; +export const getCurrentDevice = (): 'dml' | 'cuda' | 'cpu' | 'wasm' | null => currentDevice; /** * Initialize the embedding model @@ -68,14 +32,13 @@ export const getCurrentDevice = (): 'webgpu' | 'wasm' | null => currentDevice; * * @param onProgress - Optional callback for model download progress * @param config - Optional configuration override - * @param forceDevice - Force a specific device (bypasses WebGPU check) + * @param forceDevice - Force a specific device * @returns Promise resolving to the embedder pipeline - * @throws WebGPUNotAvailableError if WebGPU is requested but unavailable */ export const initEmbedder = async ( onProgress?: ModelProgressCallback, config: Partial = {}, - forceDevice?: 'webgpu' | 'wasm' + forceDevice?: 'dml' | 'cuda' | 'cpu' | 'wasm' ): Promise => { // Return existing instance if available if (embedderInstance) { @@ -90,14 +53,19 @@ export const initEmbedder = async ( isInitializing = true; const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config }; - const requestedDevice = forceDevice || finalConfig.device; + // On Windows, use DirectML for GPU acceleration (via DirectX12) + // CUDA is only available on Linux x64 with onnxruntime-node + const isWindows = process.platform === 'win32'; + const gpuDevice = isWindows ? 'dml' : 'cuda'; + let requestedDevice = forceDevice || (finalConfig.device === 'auto' ? gpuDevice : finalConfig.device); initPromise = (async () => { try { // Configure transformers.js environment env.allowLocalModels = false; - if (import.meta.env.DEV) { + const isDev = process.env.NODE_ENV === 'development'; + if (isDev) { console.log(`🧠 Loading embedding model: ${finalConfig.modelId}`); } @@ -112,86 +80,59 @@ export const initEmbedder = async ( onProgress(progress); } : undefined; - // If WebGPU is requested (default), check availability first - if (requestedDevice === 'webgpu') { - if (import.meta.env.DEV) { - console.log('🔧 Checking WebGPU availability...'); - } - - const webgpuAvailable = await checkWebGPUAvailability(); - - if (!webgpuAvailable) { - if (import.meta.env.DEV) { - console.warn('⚠️ WebGPU not available'); - } - isInitializing = false; - initPromise = null; - throw new WebGPUNotAvailableError(); - } - - // Try WebGPU + // Try GPU first if auto, fall back to CPU + // Windows: dml (DirectML/DirectX12), Linux: cuda + const devicesToTry: Array<'dml' | 'cuda' | 'cpu' | 'wasm'> = + (requestedDevice === 'dml' || requestedDevice === 'cuda') + ? [requestedDevice, 'cpu'] + : [requestedDevice as 'cpu' | 'wasm']; + + for (const device of devicesToTry) { try { - if (import.meta.env.DEV) { - console.log('🔧 Initializing WebGPU backend...'); + if (isDev && device === 'dml') { + console.log('🔧 Trying DirectML (DirectX12) GPU backend...'); + } else if (isDev && device === 'cuda') { + console.log('🔧 Trying CUDA GPU backend...'); + } else if (isDev && device === 'cpu') { + console.log('🔧 Using CPU backend...'); + } else if (isDev && device === 'wasm') { + console.log('🔧 Using WASM backend (slower)...'); } - - // Type assertion needed due to complex union types in transformers.js + embedderInstance = await (pipeline as any)( 'feature-extraction', finalConfig.modelId, { - device: 'webgpu', + device: device, dtype: 'fp32', progress_callback: progressCallback, } ); - currentDevice = 'webgpu'; - - if (import.meta.env.DEV) { - console.log('✅ Using WebGPU backend'); + currentDevice = device; + + if (isDev) { + const label = device === 'dml' ? 'GPU (DirectML/DirectX12)' + : device === 'cuda' ? 'GPU (CUDA)' + : device.toUpperCase(); + console.log(`✅ Using ${label} backend`); + console.log('✅ Embedding model loaded successfully'); } - } catch (err) { - if (import.meta.env.DEV) { - console.warn('⚠️ WebGPU initialization failed:', err); + + return embedderInstance!; + } catch (deviceError) { + if (isDev && (device === 'cuda' || device === 'dml')) { + const gpuType = device === 'dml' ? 'DirectML' : 'CUDA'; + console.log(`⚠️ ${gpuType} not available, falling back to CPU...`); } - isInitializing = false; - initPromise = null; - embedderInstance = null; - throw new WebGPUNotAvailableError(err as Error); - } - } else { - // WASM mode requested (user chose fallback) - if (import.meta.env.DEV) { - console.log('🔧 Initializing WASM backend (this will be slower)...'); - } - - // Type assertion needed due to complex union types in transformers.js - embedderInstance = await (pipeline as any)( - 'feature-extraction', - finalConfig.modelId, - { - device: 'wasm', // WASM-based CPU execution - dtype: 'fp32', - progress_callback: progressCallback, + // Continue to next device in list + if (device === devicesToTry[devicesToTry.length - 1]) { + throw deviceError; // Last device failed, propagate error } - ); - currentDevice = 'wasm'; - - if (import.meta.env.DEV) { - console.log('✅ Using WASM backend'); } } - if (import.meta.env.DEV) { - console.log('✅ Embedding model loaded successfully'); - } - - return embedderInstance!; + throw new Error('No suitable device found for embedding model'); } catch (error) { - // Re-throw WebGPUNotAvailableError as-is - if (error instanceof WebGPUNotAvailableError) { - throw error; - } isInitializing = false; initPromise = null; embedderInstance = null; diff --git a/gitnexus/src/core/embeddings/embedding-pipeline.ts b/gitnexus/src/core/embeddings/embedding-pipeline.ts index 05f8ae7ed..0de1fea07 100644 --- a/gitnexus/src/core/embeddings/embedding-pipeline.ts +++ b/gitnexus/src/core/embeddings/embedding-pipeline.ts @@ -9,8 +9,8 @@ * 5. Create vector index for semantic search */ -import { initEmbedder, embedBatch, embedText, embeddingToArray, isEmbedderReady } from './embedder'; -import { generateBatchEmbeddingTexts, generateEmbeddingText } from './text-generator'; +import { initEmbedder, embedBatch, embedText, embeddingToArray, isEmbedderReady } from './embedder.js'; +import { generateBatchEmbeddingTexts, generateEmbeddingText } from './text-generator.js'; import { type EmbeddingProgress, type EmbeddingConfig, @@ -19,7 +19,9 @@ import { type ModelProgress, DEFAULT_EMBEDDING_CONFIG, EMBEDDABLE_LABELS, -} from './types'; +} from './types.js'; + +const isDev = process.env.NODE_ENV === 'development'; /** * Progress callback type @@ -71,7 +73,7 @@ const queryEmbeddableNodes = async ( } } catch (error) { // Table might not exist or be empty, continue - if (import.meta.env.DEV) { + if (isDev) { console.warn(`Query for ${label} nodes failed:`, error); } } @@ -113,7 +115,7 @@ const createVectorIndex = async ( await executeQuery(cypher); } catch (error) { // Index might already exist - if (import.meta.env.DEV) { + if (isDev) { console.warn('Vector index creation warning:', error); } } @@ -126,12 +128,14 @@ const createVectorIndex = async ( * @param executeWithReusedStatement - Function to execute with reused prepared statement * @param onProgress - Callback for progress updates * @param config - Optional configuration override + * @param skipNodeIds - Optional set of node IDs that already have embeddings (incremental mode) */ export const runEmbeddingPipeline = async ( executeQuery: (cypher: string) => Promise, executeWithReusedStatement: (cypher: string, paramsList: Array>) => Promise, onProgress: EmbeddingProgressCallback, - config: Partial = {} + config: Partial = {}, + skipNodeIds?: Set, ): Promise => { const finalConfig = { ...DEFAULT_EMBEDDING_CONFIG, ...config }; @@ -144,11 +148,10 @@ export const runEmbeddingPipeline = async ( }); await initEmbedder((modelProgress: ModelProgress) => { - // Report model download progress const downloadPercent = modelProgress.progress ?? 0; onProgress({ phase: 'loading-model', - percent: Math.round(downloadPercent * 0.2), // 0-20% for model loading + percent: Math.round(downloadPercent * 0.2), modelDownloadPercent: downloadPercent, }); }, finalConfig); @@ -159,15 +162,25 @@ export const runEmbeddingPipeline = async ( modelDownloadPercent: 100, }); - if (import.meta.env.DEV) { + if (isDev) { console.log('🔍 Querying embeddable nodes...'); } // Phase 2: Query embeddable nodes - const nodes = await queryEmbeddableNodes(executeQuery); + let nodes = await queryEmbeddableNodes(executeQuery); + + // Incremental mode: filter out nodes that already have embeddings + if (skipNodeIds && skipNodeIds.size > 0) { + const beforeCount = nodes.length; + nodes = nodes.filter(n => !skipNodeIds.has(n.id)); + if (isDev) { + console.log(`📦 Incremental embeddings: ${beforeCount} total, ${skipNodeIds.size} cached, ${nodes.length} to embed`); + } + } + const totalNodes = nodes.length; - if (import.meta.env.DEV) { + if (isDev) { console.log(`📊 Found ${totalNodes} embeddable nodes`); } @@ -236,7 +249,7 @@ export const runEmbeddingPipeline = async ( totalNodes, }); - if (import.meta.env.DEV) { + if (isDev) { console.log('📇 Creating vector index...'); } @@ -250,13 +263,13 @@ export const runEmbeddingPipeline = async ( totalNodes, }); - if (import.meta.env.DEV) { + if (isDev) { console.log('✅ Embedding pipeline complete!'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - if (import.meta.env.DEV) { + if (isDev) { console.error('❌ Embedding pipeline error:', error); } diff --git a/gitnexus/src/core/embeddings/index.ts b/gitnexus/src/core/embeddings/index.ts index 5d384c8d5..4b4f10bb5 100644 --- a/gitnexus/src/core/embeddings/index.ts +++ b/gitnexus/src/core/embeddings/index.ts @@ -4,8 +4,8 @@ * Re-exports for the embedding pipeline system. */ -export * from './types'; -export * from './embedder'; -export * from './text-generator'; -export * from './embedding-pipeline'; +export * from './types.js'; +export * from './embedder.js'; +export * from './text-generator.js'; +export * from './embedding-pipeline.js'; diff --git a/gitnexus/src/core/embeddings/text-generator.ts b/gitnexus/src/core/embeddings/text-generator.ts index 36594e1a8..e3a99ff49 100644 --- a/gitnexus/src/core/embeddings/text-generator.ts +++ b/gitnexus/src/core/embeddings/text-generator.ts @@ -5,8 +5,8 @@ * Combines node metadata with code snippets for semantic matching. */ -import type { EmbeddableNode, EmbeddingConfig } from './types'; -import { DEFAULT_EMBEDDING_CONFIG } from './types'; +import type { EmbeddableNode, EmbeddingConfig } from './types.js'; +import { DEFAULT_EMBEDDING_CONFIG } from './types.js'; /** * Extract the filename from a file path diff --git a/gitnexus/src/core/embeddings/types.ts b/gitnexus/src/core/embeddings/types.ts index e4a04222b..f34572572 100644 --- a/gitnexus/src/core/embeddings/types.ts +++ b/gitnexus/src/core/embeddings/types.ts @@ -59,8 +59,8 @@ export interface EmbeddingConfig { batchSize: number; /** Embedding vector dimensions */ dimensions: number; - /** Device to use for inference: 'webgpu' for GPU acceleration, 'wasm' for WASM-based CPU */ - device: 'webgpu' | 'wasm'; + /** Device to use for inference: 'auto' tries GPU first (DirectML on Windows, CUDA on Linux), falls back to CPU */ + device: 'auto' | 'dml' | 'cuda' | 'cpu' | 'wasm'; /** Maximum characters of code snippet to include */ maxSnippetLength: number; } @@ -74,7 +74,7 @@ export const DEFAULT_EMBEDDING_CONFIG: EmbeddingConfig = { modelId: 'Snowflake/snowflake-arctic-embed-xs', batchSize: 16, dimensions: 384, - device: 'webgpu', // WebGPU preferred, WASM fallback available if user chooses + device: 'auto', maxSnippetLength: 500, }; diff --git a/gitnexus/src/core/graph/graph.ts b/gitnexus/src/core/graph/graph.ts index 1f9653b95..20643d8a2 100644 --- a/gitnexus/src/core/graph/graph.ts +++ b/gitnexus/src/core/graph/graph.ts @@ -1,4 +1,4 @@ -import { GraphNode, GraphRelationship, KnowledgeGraph } from './types' +import { GraphNode, GraphRelationship, KnowledgeGraph } from './types.js' export const createKnowledgeGraph = (): KnowledgeGraph => { const nodeMap = new Map(); @@ -16,6 +16,37 @@ export const createKnowledgeGraph = (): KnowledgeGraph => { } }; + /** + * Remove a single node and all relationships involving it + */ + const removeNode = (nodeId: string): boolean => { + if (!nodeMap.has(nodeId)) return false; + + nodeMap.delete(nodeId); + + // Remove all relationships involving this node + for (const [relId, rel] of relationshipMap) { + if (rel.sourceId === nodeId || rel.targetId === nodeId) { + relationshipMap.delete(relId); + } + } + return true; + }; + + /** + * Remove all nodes (and their relationships) belonging to a file + */ + const removeNodesByFile = (filePath: string): number => { + let removed = 0; + for (const [nodeId, node] of nodeMap) { + if (node.properties?.filePath === filePath) { + removeNode(nodeId); + removed++; + } + } + return removed; + }; + return{ get nodes(){ return Array.from(nodeMap.values()) @@ -36,6 +67,8 @@ export const createKnowledgeGraph = (): KnowledgeGraph => { addNode, addRelationship, + removeNode, + removeNodesByFile, }; -}; \ No newline at end of file +}; diff --git a/gitnexus/src/core/graph/types.ts b/gitnexus/src/core/graph/types.ts index 7bc9a5a95..ee37d94ea 100644 --- a/gitnexus/src/core/graph/types.ts +++ b/gitnexus/src/core/graph/types.ts @@ -83,4 +83,6 @@ export interface KnowledgeGraph { relationshipCount: number, addNode: (node: GraphNode) => void, addRelationship: (relationship: GraphRelationship) => void, + removeNode: (nodeId: string) => boolean, + removeNodesByFile: (filePath: string) => number, } \ No newline at end of file diff --git a/gitnexus/src/core/ingestion/ast-cache.ts b/gitnexus/src/core/ingestion/ast-cache.ts index 61775416a..0ae105120 100644 --- a/gitnexus/src/core/ingestion/ast-cache.ts +++ b/gitnexus/src/core/ingestion/ast-cache.ts @@ -1,5 +1,5 @@ import { LRUCache } from 'lru-cache'; -import Parser from 'web-tree-sitter'; +import Parser from 'tree-sitter'; // Define the interface for the Cache export interface ASTCache { @@ -16,8 +16,9 @@ export const createASTCache = (maxSize: number = 50): ASTCache => { max: maxSize, dispose: (tree) => { try { - // CRITICAL: Free the WASM memory when the tree leaves the cache - tree.delete(); + // NOTE: web-tree-sitter has tree.delete(); native tree-sitter trees are GC-managed. + // Keep this try/catch so we don't crash on either runtime. + (tree as any).delete?.(); } catch (e) { console.warn('Failed to delete tree from WASM memory', e); } diff --git a/gitnexus/src/core/ingestion/call-processor.ts b/gitnexus/src/core/ingestion/call-processor.ts index 2b71c5aaa..b3766df2b 100644 --- a/gitnexus/src/core/ingestion/call-processor.ts +++ b/gitnexus/src/core/ingestion/call-processor.ts @@ -1,11 +1,13 @@ -import { KnowledgeGraph } from '../graph/types'; -import { ASTCache } from './ast-cache'; -import { SymbolTable } from './symbol-table'; -import { ImportMap } from './import-processor'; -import { loadParser, loadLanguage } from '../tree-sitter/parser-loader'; -import { LANGUAGE_QUERIES } from './tree-sitter-queries'; -import { generateId } from '../../lib/utils'; -import { getLanguageFromFilename } from './utils'; +import { KnowledgeGraph } from '../graph/types.js'; +import { ASTCache } from './ast-cache.js'; +import { SymbolTable } from './symbol-table.js'; +import { ImportMap } from './import-processor.js'; +import Parser from 'tree-sitter'; +import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js'; +import { LANGUAGE_QUERIES } from './tree-sitter-queries.js'; +import { generateId } from '../../lib/utils.js'; +import { getLanguageFromFilename, yieldToEventLoop } from './utils.js'; +import type { ExtractedCall } from './workers/parse-worker.js'; /** * Node types that represent function/method definitions across languages. @@ -139,6 +141,7 @@ export const processCalls = async ( for (let i = 0; i < files.length; i++) { const file = files[i]; onProgress?.(i + 1, files.length); + if (i % 20 === 0) await yieldToEventLoop(); // 1. Check language support first const language = getLanguageFromFilename(file.path); @@ -156,18 +159,26 @@ export const processCalls = async ( if (!tree) { // Cache Miss: Re-parse - tree = parser.parse(file.content); + // Use larger bufferSize for files > 32KB + try { + tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + } catch (parseError) { + // Skip files that can't be parsed + continue; + } wasReparsed = true; + // Cache re-parsed tree so heritage phase gets hits + astCache.set(file.path, tree); } let query; let matches; try { - query = parser.getLanguage().query(queryStr); + const language = parser.getLanguage(); + query = new Parser.Query(language, queryStr); matches = query.matches(tree.rootNode); } catch (queryError) { console.warn(`Query error for ${file.path}:`, queryError); - if (wasReparsed) tree.delete(); continue; } @@ -216,10 +227,7 @@ export const processCalls = async ( }); }); - // Cleanup if re-parsed - if (wasReparsed) { - tree.delete(); - } + // Tree is now owned by the LRU cache — no manual delete needed } }; @@ -246,29 +254,29 @@ const resolveCallTarget = ( symbolTable: SymbolTable, importMap: ImportMap ): ResolveResult | null => { - // Strategy A: Check imported files (HIGH confidence - we know the import chain) - const importedFiles = importMap.get(currentFile); - if (importedFiles) { - for (const importedFile of importedFiles) { - const nodeId = symbolTable.lookupExact(importedFile, calledName); - if (nodeId) { - return { nodeId, confidence: 0.9, reason: 'import-resolved' }; - } - } - } - - // Strategy B: Check local file (HIGH confidence - same file definition) + // Strategy B first (cheapest — single map lookup): Check local file const localNodeId = symbolTable.lookupExact(currentFile, calledName); if (localNodeId) { return { nodeId: localNodeId, confidence: 0.85, reason: 'same-file' }; } - // Strategy C: Fuzzy global search (LOW confidence - just matching by name) - const fuzzyMatches = symbolTable.lookupFuzzy(calledName); - if (fuzzyMatches.length > 0) { - // Lower confidence if multiple matches exist (more ambiguous) - const confidence = fuzzyMatches.length === 1 ? 0.5 : 0.3; - return { nodeId: fuzzyMatches[0].nodeId, confidence, reason: 'fuzzy-global' }; + // Strategy A: Check if any definition of calledName is in an imported file + // Reversed: instead of iterating all imports and checking each, get all definitions + // and check if any is imported. O(definitions) instead of O(imports). + const allDefs = symbolTable.lookupFuzzy(calledName); + if (allDefs.length > 0) { + const importedFiles = importMap.get(currentFile); + if (importedFiles) { + for (const def of allDefs) { + if (importedFiles.has(def.filePath)) { + return { nodeId: def.nodeId, confidence: 0.9, reason: 'import-resolved' }; + } + } + } + + // Strategy C: Fuzzy global (no import match found) + const confidence = allDefs.length === 1 ? 0.5 : 0.3; + return { nodeId: allDefs[0].nodeId, confidence, reason: 'fuzzy-global' }; } return null; @@ -312,3 +320,59 @@ const isBuiltInOrNoise = (name: string): boolean => { return builtIns.has(name); }; +/** + * Fast path: resolve pre-extracted call sites from workers. + * No AST parsing — workers already extracted calledName + sourceId. + * This function only does symbol table lookups + graph mutations. + */ +export const processCallsFromExtracted = async ( + graph: KnowledgeGraph, + extractedCalls: ExtractedCall[], + symbolTable: SymbolTable, + importMap: ImportMap, + onProgress?: (current: number, total: number) => void +) => { + // Group by file for progress reporting + const byFile = new Map(); + for (const call of extractedCalls) { + let list = byFile.get(call.filePath); + if (!list) { + list = []; + byFile.set(call.filePath, list); + } + list.push(call); + } + + const totalFiles = byFile.size; + let filesProcessed = 0; + + for (const [_filePath, calls] of byFile) { + filesProcessed++; + if (filesProcessed % 100 === 0) { + onProgress?.(filesProcessed, totalFiles); + await yieldToEventLoop(); + } + + for (const call of calls) { + const resolved = resolveCallTarget( + call.calledName, + call.filePath, + symbolTable, + importMap + ); + if (!resolved) continue; + + const relId = generateId('CALLS', `${call.sourceId}:${call.calledName}->${resolved.nodeId}`); + graph.addRelationship({ + id: relId, + sourceId: call.sourceId, + targetId: resolved.nodeId, + type: 'CALLS', + confidence: resolved.confidence, + reason: resolved.reason, + }); + } + } + + onProgress?.(totalFiles, totalFiles); +}; diff --git a/gitnexus/src/core/ingestion/cluster-enricher.ts b/gitnexus/src/core/ingestion/cluster-enricher.ts index 51e00d618..0154e3bf3 100644 --- a/gitnexus/src/core/ingestion/cluster-enricher.ts +++ b/gitnexus/src/core/ingestion/cluster-enricher.ts @@ -5,7 +5,7 @@ * Generates semantic names, keywords, and descriptions using an LLM. */ -import { CommunityNode } from './community-processor'; +import { CommunityNode } from './community-processor.js'; // ============================================================================ // TYPES diff --git a/gitnexus/src/core/ingestion/community-processor.ts b/gitnexus/src/core/ingestion/community-processor.ts index 1a8901acc..369715b66 100644 --- a/gitnexus/src/core/ingestion/community-processor.ts +++ b/gitnexus/src/core/ingestion/community-processor.ts @@ -1,16 +1,28 @@ /** * Community Detection Processor * - * Uses the Leiden algorithm (via graphology-communities-louvain) to detect + * Uses the Leiden algorithm (via graphology-communities-leiden) to detect * communities/clusters in the code graph based on CALLS relationships. * * Communities represent groups of code that work together frequently, * helping agents navigate the codebase by functional area rather than file structure. */ +// NOTE: The Leiden algorithm source is vendored from graphology's repo +// (src/communities-leiden) because it was never published to npm. +// We use createRequire to load the CommonJS vendored files in ESM context. import Graph from 'graphology'; -import louvain from 'graphology-communities-louvain'; -import { KnowledgeGraph, NodeLabel } from '../graph/types'; +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import { KnowledgeGraph, NodeLabel } from '../graph/types.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +// Navigate to package root (works from both src/ and dist/) +const leidenPath = resolve(__dirname, '..', '..', '..', 'vendor', 'leiden', 'index.cjs'); +const _require = createRequire(import.meta.url); +const leiden = _require(leidenPath); // ============================================================================ // TYPES @@ -93,8 +105,8 @@ export const processCommunities = async ( onProgress?.(`Running Leiden algorithm on ${graph.order} nodes...`, 30); - // Step 2: Run Leiden (via Louvain implementation with refinement) - const details = louvain.detailed(graph, { + // Step 2: Run Leiden algorithm for community detection + const details = (leiden as any).detailed(graph, { resolution: 1.0, // Default resolution, can be tuned randomWalk: true, }); @@ -141,16 +153,28 @@ export const processCommunities = async ( * Build a graphology graph containing only symbol nodes and CALLS edges * This is what the Leiden algorithm will cluster */ -const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph): Graph => { +const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph): any => { // Use undirected graph for Leiden - it looks at edge density, not direction - const graph = new Graph({ type: 'undirected', allowSelfLoops: false }); + const graph = new (Graph as any)({ type: 'undirected', allowSelfLoops: false }); // Symbol types that should be clustered const symbolTypes = new Set(['Function', 'Class', 'Method', 'Interface']); - - // Add symbol nodes + + // First pass: collect which nodes participate in clustering edges + const clusteringRelTypes = new Set(['CALLS', 'EXTENDS', 'IMPLEMENTS']); + const connectedNodes = new Set(); + + knowledgeGraph.relationships.forEach(rel => { + if (clusteringRelTypes.has(rel.type) && rel.sourceId !== rel.targetId) { + connectedNodes.add(rel.sourceId); + connectedNodes.add(rel.targetId); + } + }); + + // Only add nodes that have at least one clustering edge + // Isolated nodes would just become singletons (skipped anyway) knowledgeGraph.nodes.forEach(node => { - if (symbolTypes.has(node.label)) { + if (symbolTypes.has(node.label) && connectedNodes.has(node.id)) { graph.addNode(node.id, { name: node.properties.name, filePath: node.properties.filePath, @@ -159,16 +183,10 @@ const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph): Graph => { } }); - // Add CALLS edges (primary clustering signal) - // We can also include EXTENDS/IMPLEMENTS for OOP clustering - const clusteringRelTypes = new Set(['CALLS', 'EXTENDS', 'IMPLEMENTS']); - + // Add edges knowledgeGraph.relationships.forEach(rel => { if (clusteringRelTypes.has(rel.type)) { - // Only add edge if both nodes exist in our symbol graph - // Also skip self-loops (recursive calls) - not allowed in undirected graph if (graph.hasNode(rel.sourceId) && graph.hasNode(rel.targetId) && rel.sourceId !== rel.targetId) { - // Avoid duplicate edges if (!graph.hasEdge(rel.sourceId, rel.targetId)) { graph.addEdge(rel.sourceId, rel.targetId); } @@ -189,7 +207,7 @@ const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph): Graph => { const createCommunityNodes = ( communities: Record, communityCount: number, - graph: Graph, + graph: any, knowledgeGraph: KnowledgeGraph ): CommunityNode[] => { // Group node IDs by community @@ -244,7 +262,7 @@ const createCommunityNodes = ( const generateHeuristicLabel = ( memberIds: string[], nodePathMap: Map, - graph: Graph, + graph: any, commNum: number ): string => { // Collect folder names from file paths @@ -322,33 +340,34 @@ const findCommonPrefix = (strings: string[]): string => { // ============================================================================ /** - * Calculate cohesion score (0-1) based on internal edge density - * Higher cohesion = more internal connections relative to size + * Estimate cohesion score (0-1) based on internal edge density. + * Uses sampling for large communities to avoid O(N^2) cost. */ -const calculateCohesion = (memberIds: string[], graph: Graph): number => { +const calculateCohesion = (memberIds: string[], graph: any): number => { if (memberIds.length <= 1) return 1.0; const memberSet = new Set(memberIds); + + // Sample up to 50 members for large communities + const SAMPLE_SIZE = 50; + const sample = memberIds.length <= SAMPLE_SIZE + ? memberIds + : memberIds.slice(0, SAMPLE_SIZE); + let internalEdges = 0; - - // Count edges within the community - memberIds.forEach(nodeId => { - if (graph.hasNode(nodeId)) { - graph.forEachNeighbor(nodeId, neighbor => { - if (memberSet.has(neighbor)) { - internalEdges++; - } - }); - } - }); - - // Each edge is counted twice (once from each end), so divide by 2 - internalEdges = internalEdges / 2; - - // Maximum possible internal edges for n nodes: n*(n-1)/2 - const maxPossibleEdges = (memberIds.length * (memberIds.length - 1)) / 2; - - if (maxPossibleEdges === 0) return 1.0; - - return Math.min(1.0, internalEdges / maxPossibleEdges); + let totalEdges = 0; + + for (const nodeId of sample) { + if (!graph.hasNode(nodeId)) continue; + graph.forEachNeighbor(nodeId, (neighbor: string) => { + totalEdges++; + if (memberSet.has(neighbor)) { + internalEdges++; + } + }); + } + + // Cohesion = fraction of edges that stay internal + if (totalEdges === 0) return 1.0; + return Math.min(1.0, internalEdges / totalEdges); }; diff --git a/gitnexus/src/core/ingestion/entry-point-scoring.ts b/gitnexus/src/core/ingestion/entry-point-scoring.ts index 1ef3d3ddc..55d0b1035 100644 --- a/gitnexus/src/core/ingestion/entry-point-scoring.ts +++ b/gitnexus/src/core/ingestion/entry-point-scoring.ts @@ -10,7 +10,7 @@ * This module is language-agnostic - language-specific patterns are defined per language. */ -import { detectFrameworkFromPath } from './framework-detection'; +import { detectFrameworkFromPath } from './framework-detection.js'; // ============================================================================ // NAME PATTERNS - All 9 supported languages diff --git a/gitnexus/src/core/ingestion/filesystem-walker.ts b/gitnexus/src/core/ingestion/filesystem-walker.ts new file mode 100644 index 000000000..bfad1574c --- /dev/null +++ b/gitnexus/src/core/ingestion/filesystem-walker.ts @@ -0,0 +1,48 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { glob } from 'glob'; +import { shouldIgnorePath } from '../../config/ignore-service.js'; + +export interface FileEntry { + path: string; + content: string; +} + +const READ_CONCURRENCY = 32; + +export const walkRepository = async ( + repoPath: string, + onProgress?: (current: number, total: number, filePath: string) => void +): Promise => { + const files = await glob('**/*', { + cwd: repoPath, + nodir: true, + dot: false, + }); + + const filtered = files.filter(file => !shouldIgnorePath(file)); + const entries: FileEntry[] = []; + let processed = 0; + + for (let start = 0; start < filtered.length; start += READ_CONCURRENCY) { + const batch = filtered.slice(start, start + READ_CONCURRENCY); + const results = await Promise.allSettled( + batch.map(relativePath => + fs.readFile(path.join(repoPath, relativePath), 'utf-8') + .then(content => ({ path: relativePath.replace(/\\/g, '/'), content })) + ) + ); + + for (const result of results) { + processed++; + if (result.status === 'fulfilled') { + entries.push(result.value); + onProgress?.(processed, filtered.length, result.value.path); + } else { + onProgress?.(processed, filtered.length, batch[results.indexOf(result)]); + } + } + } + + return entries; +}; diff --git a/gitnexus/src/core/ingestion/heritage-processor.ts b/gitnexus/src/core/ingestion/heritage-processor.ts index 378a3bdd1..dbb7bac8c 100644 --- a/gitnexus/src/core/ingestion/heritage-processor.ts +++ b/gitnexus/src/core/ingestion/heritage-processor.ts @@ -6,13 +6,15 @@ * - IMPLEMENTS: Class implements an Interface (TS only) */ -import { KnowledgeGraph } from '../graph/types'; -import { ASTCache } from './ast-cache'; -import { SymbolTable } from './symbol-table'; -import { loadParser, loadLanguage } from '../tree-sitter/parser-loader'; -import { LANGUAGE_QUERIES } from './tree-sitter-queries'; -import { generateId } from '../../lib/utils'; -import { getLanguageFromFilename } from './utils'; +import { KnowledgeGraph } from '../graph/types.js'; +import { ASTCache } from './ast-cache.js'; +import { SymbolTable } from './symbol-table.js'; +import Parser from 'tree-sitter'; +import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js'; +import { LANGUAGE_QUERIES } from './tree-sitter-queries.js'; +import { generateId } from '../../lib/utils.js'; +import { getLanguageFromFilename, yieldToEventLoop } from './utils.js'; +import type { ExtractedHeritage } from './workers/parse-worker.js'; export const processHeritage = async ( graph: KnowledgeGraph, @@ -26,6 +28,7 @@ export const processHeritage = async ( for (let i = 0; i < files.length; i++) { const file = files[i]; onProgress?.(i + 1, files.length); + if (i % 20 === 0) await yieldToEventLoop(); // 1. Check language support const language = getLanguageFromFilename(file.path); @@ -42,18 +45,26 @@ export const processHeritage = async ( let wasReparsed = false; if (!tree) { - tree = parser.parse(file.content); + // Use larger bufferSize for files > 32KB + try { + tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + } catch (parseError) { + // Skip files that can't be parsed + continue; + } wasReparsed = true; + // Cache re-parsed tree for potential future use + astCache.set(file.path, tree); } let query; let matches; try { - query = parser.getLanguage().query(queryStr); + const language = parser.getLanguage(); + query = new Parser.Query(language, queryStr); matches = query.matches(tree.rootNode); } catch (queryError) { console.warn(`Heritage query error for ${file.path}:`, queryError); - if (wasReparsed) tree.delete(); continue; } @@ -146,9 +157,86 @@ export const processHeritage = async ( } }); - // Cleanup - if (wasReparsed) { - tree.delete(); - } + // Tree is now owned by the LRU cache — no manual delete needed } }; + +/** + * Fast path: resolve pre-extracted heritage from workers. + * No AST parsing — workers already extracted className + parentName + kind. + */ +export const processHeritageFromExtracted = async ( + graph: KnowledgeGraph, + extractedHeritage: ExtractedHeritage[], + symbolTable: SymbolTable, + onProgress?: (current: number, total: number) => void +) => { + const total = extractedHeritage.length; + + for (let i = 0; i < extractedHeritage.length; i++) { + if (i % 500 === 0) { + onProgress?.(i, total); + await yieldToEventLoop(); + } + + const h = extractedHeritage[i]; + + if (h.kind === 'extends') { + const childId = symbolTable.lookupExact(h.filePath, h.className) || + symbolTable.lookupFuzzy(h.className)[0]?.nodeId || + generateId('Class', `${h.filePath}:${h.className}`); + + const parentId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId || + generateId('Class', `${h.parentName}`); + + if (childId && parentId && childId !== parentId) { + graph.addRelationship({ + id: generateId('EXTENDS', `${childId}->${parentId}`), + sourceId: childId, + targetId: parentId, + type: 'EXTENDS', + confidence: 1.0, + reason: '', + }); + } + } else if (h.kind === 'implements') { + const classId = symbolTable.lookupExact(h.filePath, h.className) || + symbolTable.lookupFuzzy(h.className)[0]?.nodeId || + generateId('Class', `${h.filePath}:${h.className}`); + + const interfaceId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId || + generateId('Interface', `${h.parentName}`); + + if (classId && interfaceId) { + graph.addRelationship({ + id: generateId('IMPLEMENTS', `${classId}->${interfaceId}`), + sourceId: classId, + targetId: interfaceId, + type: 'IMPLEMENTS', + confidence: 1.0, + reason: '', + }); + } + } else if (h.kind === 'trait-impl') { + const structId = symbolTable.lookupExact(h.filePath, h.className) || + symbolTable.lookupFuzzy(h.className)[0]?.nodeId || + generateId('Struct', `${h.filePath}:${h.className}`); + + const traitId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId || + generateId('Trait', `${h.parentName}`); + + if (structId && traitId) { + graph.addRelationship({ + id: generateId('IMPLEMENTS', `${structId}->${traitId}`), + sourceId: structId, + targetId: traitId, + type: 'IMPLEMENTS', + confidence: 1.0, + reason: 'trait-impl', + }); + } + } + } + + onProgress?.(total, total); +}; diff --git a/gitnexus/src/core/ingestion/import-processor.ts b/gitnexus/src/core/ingestion/import-processor.ts index c0cb6bd68..54b5ea070 100644 --- a/gitnexus/src/core/ingestion/import-processor.ts +++ b/gitnexus/src/core/ingestion/import-processor.ts @@ -1,9 +1,16 @@ -import { KnowledgeGraph } from '../graph/types'; -import { ASTCache } from './ast-cache'; -import { loadParser, loadLanguage } from '../tree-sitter/parser-loader'; -import { LANGUAGE_QUERIES } from './tree-sitter-queries'; -import { generateId } from '../../lib/utils'; -import { getLanguageFromFilename } from './utils'; +import fs from 'fs/promises'; +import path from 'path'; +import { KnowledgeGraph } from '../graph/types.js'; +import { ASTCache } from './ast-cache.js'; +import Parser from 'tree-sitter'; +import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js'; +import { LANGUAGE_QUERIES } from './tree-sitter-queries.js'; +import { generateId } from '../../lib/utils.js'; +import { getLanguageFromFilename, yieldToEventLoop } from './utils.js'; +import { SupportedLanguages } from '../../config/supported-languages.js'; +import type { ExtractedImport } from './workers/parse-worker.js'; + +const isDev = process.env.NODE_ENV === 'development'; // Type: Map> // Stores all files that a given file imports from @@ -11,21 +18,305 @@ export type ImportMap = Map>; export const createImportMap = (): ImportMap => new Map(); -// Helper: Resolve import paths (relative and absolute/package-style) +// ============================================================================ +// LANGUAGE-SPECIFIC CONFIG +// ============================================================================ + +/** TypeScript path alias config parsed from tsconfig.json */ +interface TsconfigPaths { + /** Map of alias prefix -> target prefix (e.g., "@/" -> "src/") */ + aliases: Map; + /** Base URL for path resolution (relative to repo root) */ + baseUrl: string; +} + +/** Go module config parsed from go.mod */ +interface GoModuleConfig { + /** Module path (e.g., "github.com/user/repo") */ + modulePath: string; +} + +/** + * Parse tsconfig.json to extract path aliases. + * Tries tsconfig.json, tsconfig.app.json, tsconfig.base.json in order. + */ +async function loadTsconfigPaths(repoRoot: string): Promise { + const candidates = ['tsconfig.json', 'tsconfig.app.json', 'tsconfig.base.json']; + + for (const filename of candidates) { + try { + const tsconfigPath = path.join(repoRoot, filename); + const raw = await fs.readFile(tsconfigPath, 'utf-8'); + // Strip JSON comments (// and /* */ style) for robustness + const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''); + const tsconfig = JSON.parse(stripped); + const compilerOptions = tsconfig.compilerOptions; + if (!compilerOptions?.paths) continue; + + const baseUrl = compilerOptions.baseUrl || '.'; + const aliases = new Map(); + + for (const [pattern, targets] of Object.entries(compilerOptions.paths)) { + if (!Array.isArray(targets) || targets.length === 0) continue; + const target = targets[0] as string; + + // Convert glob patterns: "@/*" -> "@/", "src/*" -> "src/" + const aliasPrefix = pattern.endsWith('/*') ? pattern.slice(0, -1) : pattern; + const targetPrefix = target.endsWith('/*') ? target.slice(0, -1) : target; + + aliases.set(aliasPrefix, targetPrefix); + } + + if (aliases.size > 0) { + if (isDev) { + console.log(`📦 Loaded ${aliases.size} path aliases from ${filename}`); + } + return { aliases, baseUrl }; + } + } catch { + // File doesn't exist or isn't valid JSON - try next + } + } + + return null; +} + +/** + * Parse go.mod to extract module path. + */ +async function loadGoModulePath(repoRoot: string): Promise { + try { + const goModPath = path.join(repoRoot, 'go.mod'); + const content = await fs.readFile(goModPath, 'utf-8'); + const match = content.match(/^module\s+(\S+)/m); + if (match) { + if (isDev) { + console.log(`📦 Loaded Go module path: ${match[1]}`); + } + return { modulePath: match[1] }; + } + } catch { + // No go.mod + } + return null; +} + +// ============================================================================ +// IMPORT PATH RESOLUTION +// ============================================================================ + +/** All file extensions to try during resolution */ +const EXTENSIONS = [ + '', + // TypeScript/JavaScript + '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js', + // Python + '.py', '/__init__.py', + // Java + '.java', + // C/C++ + '.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hxx', '.hh', + // C# + '.cs', + // Go + '.go', + // Rust + '.rs', '/mod.rs', +]; + +/** + * Try to match a path (with extensions) against the known file set. + * Returns the matched file path or null. + */ +function tryResolveWithExtensions( + basePath: string, + allFiles: Set, +): string | null { + for (const ext of EXTENSIONS) { + const candidate = basePath + ext; + if (allFiles.has(candidate)) return candidate; + } + return null; +} + +/** + * Build a suffix index for O(1) endsWith lookups. + * Maps every possible path suffix to its original file path. + * e.g. for "src/com/example/Foo.java": + * "Foo.java" -> "src/com/example/Foo.java" + * "example/Foo.java" -> "src/com/example/Foo.java" + * "com/example/Foo.java" -> "src/com/example/Foo.java" + * etc. + */ +export interface SuffixIndex { + /** Exact suffix lookup (case-sensitive) */ + get(suffix: string): string | undefined; + /** Case-insensitive suffix lookup */ + getInsensitive(suffix: string): string | undefined; + /** Get all files in a directory suffix */ + getFilesInDir(dirSuffix: string, extension: string): string[]; +} + +function buildSuffixIndex(normalizedFileList: string[], allFileList: string[]): SuffixIndex { + // Map: normalized suffix -> original file path + const exactMap = new Map(); + // Map: lowercase suffix -> original file path + const lowerMap = new Map(); + // Map: directory suffix -> list of file paths in that directory + const dirMap = new Map(); + + for (let i = 0; i < normalizedFileList.length; i++) { + const normalized = normalizedFileList[i]; + const original = allFileList[i]; + const parts = normalized.split('/'); + + // Index all suffixes: "a/b/c.java" -> ["c.java", "b/c.java", "a/b/c.java"] + for (let j = parts.length - 1; j >= 0; j--) { + const suffix = parts.slice(j).join('/'); + // Only store first match (longest path wins for ambiguous suffixes) + if (!exactMap.has(suffix)) { + exactMap.set(suffix, original); + } + const lower = suffix.toLowerCase(); + if (!lowerMap.has(lower)) { + lowerMap.set(lower, original); + } + } + + // Index directory membership + const lastSlash = normalized.lastIndexOf('/'); + if (lastSlash >= 0) { + // Build all directory suffixes + const dirParts = parts.slice(0, -1); + const fileName = parts[parts.length - 1]; + const ext = fileName.substring(fileName.lastIndexOf('.')); + + for (let j = dirParts.length - 1; j >= 0; j--) { + const dirSuffix = dirParts.slice(j).join('/'); + const key = `${dirSuffix}:${ext}`; + let list = dirMap.get(key); + if (!list) { + list = []; + dirMap.set(key, list); + } + list.push(original); + } + } + } + + return { + get: (suffix: string) => exactMap.get(suffix), + getInsensitive: (suffix: string) => lowerMap.get(suffix.toLowerCase()), + getFilesInDir: (dirSuffix: string, extension: string) => { + return dirMap.get(`${dirSuffix}:${extension}`) || []; + }, + }; +} + +/** + * Suffix-based resolution using index. O(1) per lookup instead of O(files). + */ +function suffixResolve( + pathParts: string[], + normalizedFileList: string[], + allFileList: string[], + index?: SuffixIndex, +): string | null { + if (index) { + for (let i = 0; i < pathParts.length; i++) { + const suffix = pathParts.slice(i).join('/'); + for (const ext of EXTENSIONS) { + const suffixWithExt = suffix + ext; + const result = index.get(suffixWithExt) || index.getInsensitive(suffixWithExt); + if (result) return result; + } + } + return null; + } + + // Fallback: linear scan (for backward compatibility) + for (let i = 0; i < pathParts.length; i++) { + const suffix = pathParts.slice(i).join('/'); + for (const ext of EXTENSIONS) { + const suffixWithExt = suffix + ext; + const suffixPattern = '/' + suffixWithExt; + const matchIdx = normalizedFileList.findIndex(filePath => + filePath.endsWith(suffixPattern) || filePath.toLowerCase().endsWith(suffixPattern.toLowerCase()) + ); + if (matchIdx !== -1) { + return allFileList[matchIdx]; + } + } + } + return null; +} + +/** + * Resolve an import path to a file path in the repository. + * + * Language-specific preprocessing is applied before the generic resolution: + * - TypeScript/JavaScript: rewrites tsconfig path aliases + * - Rust: converts crate::/super::/self:: to relative paths + * + * Java wildcards and Go package imports are handled separately in processImports + * because they resolve to multiple files. + */ const resolveImportPath = ( - currentFile: string, - importPath: string, + currentFile: string, + importPath: string, allFiles: Set, allFileList: string[], - resolveCache: Map + normalizedFileList: string[], + resolveCache: Map, + language: SupportedLanguages, + tsconfigPaths: TsconfigPaths | null, + index?: SuffixIndex, ): string | null => { const cacheKey = `${currentFile}::${importPath}`; if (resolveCache.has(cacheKey)) return resolveCache.get(cacheKey) ?? null; - // 1. Resolve '..' and '.' for relative imports + const cache = (result: string | null): string | null => { + resolveCache.set(cacheKey, result); + return result; + }; + + // ---- TypeScript/JavaScript: rewrite path aliases ---- + if ( + (language === SupportedLanguages.TypeScript || language === SupportedLanguages.JavaScript) && + tsconfigPaths && + !importPath.startsWith('.') + ) { + for (const [aliasPrefix, targetPrefix] of tsconfigPaths.aliases) { + if (importPath.startsWith(aliasPrefix)) { + const remainder = importPath.slice(aliasPrefix.length); + // Build the rewritten path relative to baseUrl + const rewritten = tsconfigPaths.baseUrl === '.' + ? targetPrefix + remainder + : tsconfigPaths.baseUrl + '/' + targetPrefix + remainder; + + // Try direct resolution from repo root + const resolved = tryResolveWithExtensions(rewritten, allFiles); + if (resolved) return cache(resolved); + + // Try suffix matching as fallback + const parts = rewritten.split('/').filter(Boolean); + const suffixResult = suffixResolve(parts, normalizedFileList, allFileList, index); + if (suffixResult) return cache(suffixResult); + } + } + } + + // ---- Rust: convert module path syntax to file paths ---- + if (language === SupportedLanguages.Rust) { + const rustResult = resolveRustImport(currentFile, importPath, allFiles); + if (rustResult) return cache(rustResult); + // Fall through to generic resolution if Rust-specific didn't match + } + + // ---- Generic relative import resolution (./ and ../) ---- const currentDir = currentFile.split('/').slice(0, -1); const parts = importPath.split('/'); - + for (const part of parts) { if (part === '.') continue; if (part === '..') { @@ -34,44 +325,18 @@ const resolveImportPath = ( currentDir.push(part); } } - + const basePath = currentDir.join('/'); - // 2. Try extensions for all supported languages - const extensions = [ - '', - // TypeScript/JavaScript - '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js', - // Python - '.py', '/__init__.py', - // Java - '.java', - // C/C++ - '.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hxx', '.hh', - // C# - '.cs', - // Go - '.go', - // Rust - '.rs', '/mod.rs' - ]; - if (importPath.startsWith('.')) { - for (const ext of extensions) { - const candidate = basePath + ext; - if (allFiles.has(candidate)) { - resolveCache.set(cacheKey, candidate); - return candidate; - } - } - resolveCache.set(cacheKey, null); - return null; + const resolved = tryResolveWithExtensions(basePath, allFiles); + return cache(resolved); } - // 3. Handle absolute/package imports (Java, Go, Python, etc.) + // ---- Generic package/absolute import resolution (suffix matching) ---- + // Java wildcards are handled in processImports, not here if (importPath.endsWith('.*')) { - resolveCache.set(cacheKey, null); - return null; + return cache(null); } const pathLike = importPath.includes('/') @@ -79,56 +344,276 @@ const resolveImportPath = ( : importPath.replace(/\./g, '/'); const pathParts = pathLike.split('/').filter(Boolean); - // Normalize all file paths to forward slashes for matching - const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/')); + const resolved = suffixResolve(pathParts, normalizedFileList, allFileList, index); + return cache(resolved); +}; - for (let i = 0; i < pathParts.length; i++) { - const suffix = pathParts.slice(i).join('/'); - for (const ext of extensions) { - const suffixWithExt = suffix + ext; - // Require path separator before match to avoid false positives like "View.java" matching "RootView.java" - const suffixPattern = '/' + suffixWithExt; - const matchIdx = normalizedFileList.findIndex(filePath => - filePath.endsWith(suffixPattern) || filePath.toLowerCase().endsWith(suffixPattern.toLowerCase()) - ); - if (matchIdx !== -1) { - const match = allFileList[matchIdx]; - resolveCache.set(cacheKey, match); - return match; +// ============================================================================ +// RUST MODULE RESOLUTION +// ============================================================================ + +/** + * Resolve Rust use-path to a file. + * Handles crate::, super::, self:: prefixes and :: path separators. + */ +function resolveRustImport( + currentFile: string, + importPath: string, + allFiles: Set, +): string | null { + let rustPath: string; + + if (importPath.startsWith('crate::')) { + // crate:: resolves from src/ directory (standard Rust layout) + rustPath = importPath.slice(7).replace(/::/g, '/'); + + // Try from src/ (standard layout) + const fromSrc = tryRustModulePath('src/' + rustPath, allFiles); + if (fromSrc) return fromSrc; + + // Try from repo root (non-standard) + const fromRoot = tryRustModulePath(rustPath, allFiles); + if (fromRoot) return fromRoot; + + return null; + } + + if (importPath.startsWith('super::')) { + // super:: = parent directory of current file's module + const currentDir = currentFile.split('/').slice(0, -1); + currentDir.pop(); // Go up one level for super:: + rustPath = importPath.slice(7).replace(/::/g, '/'); + const fullPath = [...currentDir, rustPath].join('/'); + return tryRustModulePath(fullPath, allFiles); + } + + if (importPath.startsWith('self::')) { + // self:: = current module's directory + const currentDir = currentFile.split('/').slice(0, -1); + rustPath = importPath.slice(6).replace(/::/g, '/'); + const fullPath = [...currentDir, rustPath].join('/'); + return tryRustModulePath(fullPath, allFiles); + } + + // Bare path without prefix (e.g., from a use in a nested module) + // Convert :: to / and try suffix matching + if (importPath.includes('::')) { + rustPath = importPath.replace(/::/g, '/'); + return tryRustModulePath(rustPath, allFiles); + } + + return null; +} + +/** + * Try to resolve a Rust module path to a file. + * Tries: path.rs, path/mod.rs, and with the last segment stripped + * (last segment might be a symbol name, not a module). + */ +function tryRustModulePath(modulePath: string, allFiles: Set): string | null { + // Try direct: path.rs + if (allFiles.has(modulePath + '.rs')) return modulePath + '.rs'; + // Try directory: path/mod.rs + if (allFiles.has(modulePath + '/mod.rs')) return modulePath + '/mod.rs'; + // Try path/lib.rs (for crate root) + if (allFiles.has(modulePath + '/lib.rs')) return modulePath + '/lib.rs'; + + // The last segment might be a symbol (function, struct, etc.), not a module. + // Strip it and try again. + const lastSlash = modulePath.lastIndexOf('/'); + if (lastSlash > 0) { + const parentPath = modulePath.substring(0, lastSlash); + if (allFiles.has(parentPath + '.rs')) return parentPath + '.rs'; + if (allFiles.has(parentPath + '/mod.rs')) return parentPath + '/mod.rs'; + } + + return null; +} + +// ============================================================================ +// JAVA MULTI-FILE RESOLUTION +// ============================================================================ + +/** + * Resolve a Java wildcard import (com.example.*) to all matching .java files. + * Returns an array of file paths. + */ +function resolveJavaWildcard( + importPath: string, + normalizedFileList: string[], + allFileList: string[], + index?: SuffixIndex, +): string[] { + // "com.example.util.*" -> "com/example/util" + const packagePath = importPath.slice(0, -2).replace(/\./g, '/'); + + if (index) { + // Use directory index: get all .java files in this package directory + const candidates = index.getFilesInDir(packagePath, '.java'); + // Filter to only direct children (no subdirectories) + const packageSuffix = '/' + packagePath + '/'; + return candidates.filter(f => { + const normalized = f.replace(/\\/g, '/'); + const idx = normalized.indexOf(packageSuffix); + if (idx < 0) return false; + const afterPkg = normalized.substring(idx + packageSuffix.length); + return !afterPkg.includes('/'); + }); + } + + // Fallback: linear scan + const packageSuffix = '/' + packagePath + '/'; + const matches: string[] = []; + for (let i = 0; i < normalizedFileList.length; i++) { + const normalized = normalizedFileList[i]; + if (normalized.includes(packageSuffix) && normalized.endsWith('.java')) { + const afterPackage = normalized.substring(normalized.indexOf(packageSuffix) + packageSuffix.length); + if (!afterPackage.includes('/')) { + matches.push(allFileList[i]); + } + } + } + return matches; +} + +/** + * Try to resolve a Java static import by stripping the member name. + * "com.example.Constants.VALUE" -> resolve "com.example.Constants" + */ +function resolveJavaStaticImport( + importPath: string, + normalizedFileList: string[], + allFileList: string[], + index?: SuffixIndex, +): string | null { + // Static imports look like: com.example.Constants.VALUE or com.example.Constants.* + // The last segment is a member name (field/method) if it starts with lowercase or is ALL_CAPS + const segments = importPath.split('.'); + if (segments.length < 3) return null; + + const lastSeg = segments[segments.length - 1]; + // If last segment is a wildcard or ALL_CAPS constant or starts with lowercase, strip it + if (lastSeg === '*' || /^[a-z]/.test(lastSeg) || /^[A-Z_]+$/.test(lastSeg)) { + const classPath = segments.slice(0, -1).join('/'); + const classSuffix = classPath + '.java'; + + if (index) { + return index.get(classSuffix) || index.getInsensitive(classSuffix) || null; + } + + // Fallback: linear scan + const fullSuffix = '/' + classSuffix; + for (let i = 0; i < normalizedFileList.length; i++) { + if (normalizedFileList[i].endsWith(fullSuffix) || + normalizedFileList[i].toLowerCase().endsWith(fullSuffix.toLowerCase())) { + return allFileList[i]; } } } - // Unresolved imports (external packages, SDK imports) are expected - don't log - resolveCache.set(cacheKey, null); return null; -}; +} + +// ============================================================================ +// GO PACKAGE RESOLUTION +// ============================================================================ + +/** + * Resolve a Go internal package import to all .go files in the package directory. + * Returns an array of file paths. + */ +function resolveGoPackage( + importPath: string, + goModule: GoModuleConfig, + normalizedFileList: string[], + allFileList: string[], +): string[] { + if (!importPath.startsWith(goModule.modulePath)) return []; + + // Strip module path to get relative package path + const relativePkg = importPath.slice(goModule.modulePath.length + 1); // e.g., "internal/auth" + if (!relativePkg) return []; + + const pkgSuffix = '/' + relativePkg + '/'; + const matches: string[] = []; + + for (let i = 0; i < normalizedFileList.length; i++) { + const normalized = normalizedFileList[i]; + // File must be directly in the package directory (not a subdirectory) + if (normalized.includes(pkgSuffix) && normalized.endsWith('.go') && !normalized.endsWith('_test.go')) { + const afterPkg = normalized.substring(normalized.indexOf(pkgSuffix) + pkgSuffix.length); + if (!afterPkg.includes('/')) { + matches.push(allFileList[i]); + } + } + } + + return matches; +} + +// ============================================================================ +// MAIN IMPORT PROCESSOR +// ============================================================================ export const processImports = async ( graph: KnowledgeGraph, files: { path: string; content: string }[], astCache: ASTCache, importMap: ImportMap, - onProgress?: (current: number, total: number) => void + onProgress?: (current: number, total: number) => void, + repoRoot?: string, ) => { // Create a Set of all file paths for fast lookup during resolution const allFilePaths = new Set(files.map(f => f.path)); const parser = await loadParser(); const resolveCache = new Map(); const allFileList = files.map(f => f.path); - + // Pre-compute normalized file list once (forward slashes) + const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/')); + // Build suffix index for O(1) lookups + const index = buildSuffixIndex(normalizedFileList, allFileList); + // Track import statistics let totalImportsFound = 0; let totalImportsResolved = 0; + // Load language-specific configs once before the file loop + const effectiveRoot = repoRoot || ''; + const tsconfigPaths = await loadTsconfigPaths(effectiveRoot); + const goModule = await loadGoModulePath(effectiveRoot); + + // Helper: add an IMPORTS edge + update import map + const addImportEdge = (filePath: string, resolvedPath: string) => { + const sourceId = generateId('File', filePath); + const targetId = generateId('File', resolvedPath); + const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`); + + totalImportsResolved++; + + graph.addRelationship({ + id: relId, + sourceId, + targetId, + type: 'IMPORTS', + confidence: 1.0, + reason: '', + }); + + if (!importMap.has(filePath)) { + importMap.set(filePath, new Set()); + } + importMap.get(filePath)!.add(resolvedPath); + }; + for (let i = 0; i < files.length; i++) { const file = files[i]; onProgress?.(i + 1, files.length); + if (i % 20 === 0) await yieldToEventLoop(); // 1. Check language support first const language = getLanguageFromFilename(file.path); if (!language) continue; - + const queryStr = LANGUAGE_QUERIES[language]; if (!queryStr) continue; @@ -138,32 +623,37 @@ export const processImports = async ( // 3. Get AST (Try Cache First) let tree = astCache.get(file.path); let wasReparsed = false; - + if (!tree) { - // Cache Miss: Re-parse (slower, but necessary if evicted) - tree = parser.parse(file.content); + try { + tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + } catch (parseError) { + continue; + } wasReparsed = true; + // Cache re-parsed tree so call/heritage phases get hits + astCache.set(file.path, tree); } let query; let matches; try { - query = parser.getLanguage().query(queryStr); + const lang = parser.getLanguage(); + query = new Parser.Query(lang, queryStr); matches = query.matches(tree.rootNode); - - // Removed verbose Java import logging } catch (queryError: any) { - // Detailed debug logging for query failures - console.group(`🔴 Query Error: ${file.path}`); - console.log('Language:', language); - console.log('Query (first 200 chars):', queryStr.substring(0, 200) + '...'); - console.log('Error:', queryError?.message || queryError); - console.log('File content (first 300 chars):', file.content.substring(0, 300)); - console.log('AST root type:', tree.rootNode?.type); - console.log('AST has errors:', tree.rootNode?.hasError); - console.groupEnd(); - - if (wasReparsed) tree.delete(); + if (isDev) { + console.group(`🔴 Query Error: ${file.path}`); + console.log('Language:', language); + console.log('Query (first 200 chars):', queryStr.substring(0, 200) + '...'); + console.log('Error:', queryError?.message || queryError); + console.log('File content (first 300 chars):', file.content.substring(0, 300)); + console.log('AST root type:', tree.rootNode?.type); + console.log('AST has errors:', tree.rootNode?.hasError); + console.groupEnd(); + } + + if (wasReparsed) (tree as any).delete?.(); continue; } @@ -174,63 +664,220 @@ export const processImports = async ( if (captureMap['import']) { const sourceNode = captureMap['import.source']; if (!sourceNode) { - if (import.meta.env.DEV) { + if (isDev) { console.log(`⚠️ Import captured but no source node in ${file.path}`); } return; } - // Clean path (remove quotes) - const rawImportPath = sourceNode.text.replace(/['"]/g, ''); + // Clean path (remove quotes and angle brackets for C/C++ includes) + const rawImportPath = sourceNode.text.replace(/['"<>]/g, ''); totalImportsFound++; - - // Removed verbose per-import logging - - // Resolve to actual file in the system + + // ---- Java: handle wildcards and static imports specially ---- + if (language === SupportedLanguages.Java) { + if (rawImportPath.endsWith('.*')) { + const matchedFiles = resolveJavaWildcard(rawImportPath, normalizedFileList, allFileList, index); + for (const matchedFile of matchedFiles) { + addImportEdge(file.path, matchedFile); + } + return; // skip single-file resolution + } + + // Try static import resolution (strip member name) + const staticResolved = resolveJavaStaticImport(rawImportPath, normalizedFileList, allFileList, index); + if (staticResolved) { + addImportEdge(file.path, staticResolved); + return; + } + // Fall through to normal resolution for regular Java imports + } + + // ---- Go: handle package-level imports ---- + if (language === SupportedLanguages.Go && goModule && rawImportPath.startsWith(goModule.modulePath)) { + const pkgFiles = resolveGoPackage(rawImportPath, goModule, normalizedFileList, allFileList); + if (pkgFiles.length > 0) { + for (const pkgFile of pkgFiles) { + addImportEdge(file.path, pkgFile); + } + return; // skip single-file resolution + } + // Fall through if no files found (package might be external) + } + + // ---- Standard single-file resolution ---- const resolvedPath = resolveImportPath( file.path, rawImportPath, allFilePaths, allFileList, - resolveCache + normalizedFileList, + resolveCache, + language, + tsconfigPaths, + index, ); if (resolvedPath) { - // A. Update Graph (File -> IMPORTS -> File) - const sourceId = generateId('File', file.path); - const targetId = generateId('File', resolvedPath); - const relId = generateId('IMPORTS', `${file.path}->${resolvedPath}`); - - totalImportsResolved++; - - graph.addRelationship({ - id: relId, - sourceId, - targetId, - type: 'IMPORTS', - confidence: 1.0, - reason: '', - }); - - // B. Update Import Map (For Pass 4) - // Store all resolved import paths for this file - if (!importMap.has(file.path)) { - importMap.set(file.path, new Set()); - } - importMap.get(file.path)!.add(resolvedPath); + addImportEdge(file.path, resolvedPath); } } }); - // If re-parsed just for this, delete the tree to save memory - if (wasReparsed) { - tree.delete(); - } + // Tree is now owned by the LRU cache — no manual delete needed } - - if (import.meta.env.DEV) { + + if (isDev) { console.log(`📊 Import processing complete: ${totalImportsResolved}/${totalImportsFound} imports resolved to graph edges`); } }; +// ============================================================================ +// FAST PATH: Resolve pre-extracted imports (no parsing needed) +// ============================================================================ +export const processImportsFromExtracted = async ( + graph: KnowledgeGraph, + files: { path: string; content: string }[], + extractedImports: ExtractedImport[], + importMap: ImportMap, + onProgress?: (current: number, total: number) => void, + repoRoot?: string, +) => { + const allFilePaths = new Set(files.map(f => f.path)); + const resolveCache = new Map(); + const allFileList = files.map(f => f.path); + const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/')); + // Build suffix index for O(1) lookups + const index = buildSuffixIndex(normalizedFileList, allFileList); + + let totalImportsFound = 0; + let totalImportsResolved = 0; + + const effectiveRoot = repoRoot || ''; + const tsconfigPaths = await loadTsconfigPaths(effectiveRoot); + const goModule = await loadGoModulePath(effectiveRoot); + + const addImportEdge = (filePath: string, resolvedPath: string) => { + const sourceId = generateId('File', filePath); + const targetId = generateId('File', resolvedPath); + const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`); + + totalImportsResolved++; + + graph.addRelationship({ + id: relId, + sourceId, + targetId, + type: 'IMPORTS', + confidence: 1.0, + reason: '', + }); + + if (!importMap.has(filePath)) { + importMap.set(filePath, new Set()); + } + importMap.get(filePath)!.add(resolvedPath); + }; + + // Group by file for progress reporting (users see file count, not import count) + const importsByFile = new Map(); + for (const imp of extractedImports) { + let list = importsByFile.get(imp.filePath); + if (!list) { + list = []; + importsByFile.set(imp.filePath, list); + } + list.push(imp); + } + + const totalFiles = importsByFile.size; + let filesProcessed = 0; + + // Pre-build a suffix index for O(1) suffix lookups instead of O(n) linear scans + const suffixIndex = new Map(); + for (let i = 0; i < normalizedFileList.length; i++) { + const normalized = normalizedFileList[i]; + // Index by last path segment (filename) for fast suffix matching + const lastSlash = normalized.lastIndexOf('/'); + const filename = lastSlash >= 0 ? normalized.substring(lastSlash + 1) : normalized; + let list = suffixIndex.get(filename); + if (!list) { + list = []; + suffixIndex.set(filename, list); + } + list.push(allFileList[i]); + } + + for (const [filePath, fileImports] of importsByFile) { + filesProcessed++; + if (filesProcessed % 100 === 0) { + onProgress?.(filesProcessed, totalFiles); + await yieldToEventLoop(); + } + + for (const { rawImportPath, language } of fileImports) { + totalImportsFound++; + + // Check resolve cache first + const cacheKey = `${filePath}::${rawImportPath}`; + if (resolveCache.has(cacheKey)) { + const cached = resolveCache.get(cacheKey); + if (cached) addImportEdge(filePath, cached); + continue; + } + + // Java: handle wildcards and static imports + if (language === SupportedLanguages.Java) { + if (rawImportPath.endsWith('.*')) { + const matchedFiles = resolveJavaWildcard(rawImportPath, normalizedFileList, allFileList, index); + for (const matchedFile of matchedFiles) { + addImportEdge(filePath, matchedFile); + } + continue; + } + + const staticResolved = resolveJavaStaticImport(rawImportPath, normalizedFileList, allFileList, index); + if (staticResolved) { + resolveCache.set(cacheKey, staticResolved); + addImportEdge(filePath, staticResolved); + continue; + } + } + + // Go: handle package-level imports + if (language === SupportedLanguages.Go && goModule && rawImportPath.startsWith(goModule.modulePath)) { + const pkgFiles = resolveGoPackage(rawImportPath, goModule, normalizedFileList, allFileList); + if (pkgFiles.length > 0) { + for (const pkgFile of pkgFiles) { + addImportEdge(filePath, pkgFile); + } + continue; + } + } + + // Standard resolution (has its own internal cache) + const resolvedPath = resolveImportPath( + filePath, + rawImportPath, + allFilePaths, + allFileList, + normalizedFileList, + resolveCache, + language as SupportedLanguages, + tsconfigPaths, + index, + ); + + if (resolvedPath) { + addImportEdge(filePath, resolvedPath); + } + } + } + + onProgress?.(totalFiles, totalFiles); + + if (isDev) { + console.log(`📊 Import processing (fast path): ${totalImportsResolved}/${totalImportsFound} imports resolved to graph edges`); + } +}; diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index 807bcf581..4d02e3f89 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -1,13 +1,22 @@ -import { KnowledgeGraph, GraphNode, GraphRelationship } from '../graph/types'; -import { loadParser, loadLanguage } from '../tree-sitter/parser-loader'; -import { LANGUAGE_QUERIES } from './tree-sitter-queries'; -import { generateId } from '../../lib/utils'; -import { SymbolTable } from './symbol-table'; -import { ASTCache } from './ast-cache'; -import { getLanguageFromFilename } from './utils'; +import { KnowledgeGraph, GraphNode, GraphRelationship } from '../graph/types.js'; +import Parser from 'tree-sitter'; +import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js'; +import { LANGUAGE_QUERIES } from './tree-sitter-queries.js'; +import { generateId } from '../../lib/utils.js'; +import { SymbolTable } from './symbol-table.js'; +import { ASTCache } from './ast-cache.js'; +import { getLanguageFromFilename, yieldToEventLoop } from './utils.js'; +import { WorkerPool } from './workers/worker-pool.js'; +import type { ParseWorkerResult, ParseWorkerInput, ExtractedImport, ExtractedCall, ExtractedHeritage } from './workers/parse-worker.js'; export type FileProgressCallback = (current: number, total: number, filePath: string) => void; +export interface WorkerExtractedData { + imports: ExtractedImport[]; + calls: ExtractedCall[]; + heritage: ExtractedHeritage[]; +} + // ============================================================================ // EXPORT DETECTION - Language-specific visibility detection // ============================================================================ @@ -15,7 +24,7 @@ export type FileProgressCallback = (current: number, total: number, filePath: st /** * Check if a symbol (function, class, etc.) is exported/public * Handles all 9 supported languages with explicit logic - * + * * @param node - The AST node for the symbol name * @param name - The symbol name * @param language - The programming language @@ -23,14 +32,14 @@ export type FileProgressCallback = (current: number, total: number, filePath: st */ const isNodeExported = (node: any, name: string, language: string): boolean => { let current = node; - + switch (language) { // JavaScript/TypeScript: Check for export keyword in ancestors case 'javascript': case 'typescript': while (current) { const type = current.type; - if (type === 'export_statement' || + if (type === 'export_statement' || type === 'export_specifier' || type === 'lexical_declaration' && current.parent?.type === 'export_statement') { return true; @@ -42,11 +51,11 @@ const isNodeExported = (node: any, name: string, language: string): boolean => { current = current.parent; } return false; - + // Python: Public if no leading underscore (convention) case 'python': return !name.startsWith('_'); - + // Java: Check for 'public' modifier // In tree-sitter Java, modifiers are siblings of the name node, not parents case 'java': @@ -71,7 +80,7 @@ const isNodeExported = (node: any, name: string, language: string): boolean => { current = current.parent; } return false; - + // C#: Check for 'public' modifier in ancestors case 'csharp': while (current) { @@ -81,14 +90,14 @@ const isNodeExported = (node: any, name: string, language: string): boolean => { current = current.parent; } return false; - + // Go: Uppercase first letter = exported case 'go': if (name.length === 0) return false; const first = name[0]; // Must be uppercase letter (not a number or symbol) return first === first.toUpperCase() && first !== first.toLowerCase(); - + // Rust: Check for 'pub' visibility modifier case 'rust': while (current) { @@ -98,80 +107,147 @@ const isNodeExported = (node: any, name: string, language: string): boolean => { current = current.parent; } return false; - + // C/C++: No native export concept at language level // Entry points will be detected via name patterns (main, etc.) case 'c': case 'cpp': return false; - + default: return false; } }; -export const processParsing = async ( - graph: KnowledgeGraph, +// ============================================================================ +// Worker-based parallel parsing +// ============================================================================ + +const processParsingWithWorkers = async ( + graph: KnowledgeGraph, + files: { path: string; content: string }[], + symbolTable: SymbolTable, + astCache: ASTCache, + workerPool: WorkerPool, + onFileProgress?: FileProgressCallback +): Promise => { + // Filter to parseable files only + const parseableFiles: ParseWorkerInput[] = []; + for (const file of files) { + const lang = getLanguageFromFilename(file.path); + if (lang) { + parseableFiles.push({ path: file.path, content: file.content }); + } + } + + if (parseableFiles.length === 0) return { imports: [], calls: [], heritage: [] }; + + const total = files.length; + + // Dispatch to worker pool — pool handles splitting into chunks + // Workers send progress messages during parsing so the bar updates smoothly + const chunkResults = await workerPool.dispatch( + parseableFiles, + (filesProcessed) => { + onFileProgress?.(Math.min(filesProcessed, total), total, 'Parsing...'); + } + ); + + // Merge results from all workers into graph and symbol table + const allImports: ExtractedImport[] = []; + const allCalls: ExtractedCall[] = []; + const allHeritage: ExtractedHeritage[] = []; + for (const result of chunkResults) { + for (const node of result.nodes) { + graph.addNode({ + id: node.id, + label: node.label as any, + properties: node.properties, + }); + } + + for (const rel of result.relationships) { + graph.addRelationship(rel); + } + + for (const sym of result.symbols) { + symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type); + } + + allImports.push(...result.imports); + allCalls.push(...result.calls); + allHeritage.push(...result.heritage); + } + + // Final progress + onFileProgress?.(total, total, 'done'); + return { imports: allImports, calls: allCalls, heritage: allHeritage }; +}; + +// ============================================================================ +// Sequential fallback (original implementation) +// ============================================================================ + +const processParsingSequential = async ( + graph: KnowledgeGraph, files: { path: string; content: string }[], symbolTable: SymbolTable, astCache: ASTCache, onFileProgress?: FileProgressCallback ) => { - const parser = await loadParser(); const total = files.length; for (let i = 0; i < files.length; i++) { const file = files[i]; - - // Report progress for each file + onFileProgress?.(i + 1, total, file.path); - + + if (i % 20 === 0) await yieldToEventLoop(); + const language = getLanguageFromFilename(file.path); if (!language) continue; await loadLanguage(language, file.path); - - // 3. Parse the text content into an AST - const tree = parser.parse(file.content); - - // Store in cache immediately (this might evict an old one) + + let tree; + try { + tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + } catch (parseError) { + console.warn(`Skipping unparseable file: ${file.path}`); + continue; + } + astCache.set(file.path, tree); - - // 4. Get the specific query string for this language + const queryString = LANGUAGE_QUERIES[language]; if (!queryString) { continue; } - // 5. Run the query against the AST root node - // This looks for patterns like (function_declaration) let query; let matches; try { - query = parser.getLanguage().query(queryString); + const language = parser.getLanguage(); + query = new Parser.Query(language, queryString); matches = query.matches(tree.rootNode); } catch (queryError) { console.warn(`Query error for ${file.path}:`, queryError); continue; } - // 6. Process every match found matches.forEach(match => { const captureMap: Record = {}; - + match.captures.forEach(c => { captureMap[c.name] = c.node; }); - // Skip imports here - they are handled by import-processor.ts - // which creates proper File -> IMPORTS -> File relationships if (captureMap['import']) { return; } - // Skip call expressions - they are handled by call-processor.ts if (captureMap['call']) { return; } @@ -180,43 +256,34 @@ export const processParsing = async ( if (!nameNode) return; const nodeName = nameNode.text; - + let nodeLabel = 'CodeElement'; - - // Core types + if (captureMap['definition.function']) nodeLabel = 'Function'; else if (captureMap['definition.class']) nodeLabel = 'Class'; else if (captureMap['definition.interface']) nodeLabel = 'Interface'; else if (captureMap['definition.method']) nodeLabel = 'Method'; - // Struct types (C, C++, Go, Rust, C#) else if (captureMap['definition.struct']) nodeLabel = 'Struct'; - // Enum types else if (captureMap['definition.enum']) nodeLabel = 'Enum'; - // Namespace/Module (C++, C#, Rust) else if (captureMap['definition.namespace']) nodeLabel = 'Namespace'; else if (captureMap['definition.module']) nodeLabel = 'Module'; - // Rust-specific else if (captureMap['definition.trait']) nodeLabel = 'Trait'; else if (captureMap['definition.impl']) nodeLabel = 'Impl'; else if (captureMap['definition.type']) nodeLabel = 'TypeAlias'; else if (captureMap['definition.const']) nodeLabel = 'Const'; else if (captureMap['definition.static']) nodeLabel = 'Static'; - // C-specific else if (captureMap['definition.typedef']) nodeLabel = 'Typedef'; else if (captureMap['definition.macro']) nodeLabel = 'Macro'; else if (captureMap['definition.union']) nodeLabel = 'Union'; - // C#-specific else if (captureMap['definition.property']) nodeLabel = 'Property'; else if (captureMap['definition.record']) nodeLabel = 'Record'; else if (captureMap['definition.delegate']) nodeLabel = 'Delegate'; - // Java-specific else if (captureMap['definition.annotation']) nodeLabel = 'Annotation'; else if (captureMap['definition.constructor']) nodeLabel = 'Constructor'; - // C++ template else if (captureMap['definition.template']) nodeLabel = 'Template'; const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`); - + const node: GraphNode = { id: nodeId, label: nodeLabel as any, @@ -232,13 +299,12 @@ export const processParsing = async ( graph.addNode(node); - // Register in Symbol Table (only definitions, not imports) symbolTable.add(file.path, nodeName, nodeId, nodeLabel); const fileId = generateId('File', file.path); - + const relId = generateId('DEFINES', `${fileId}->${nodeId}`); - + const relationship: GraphRelationship = { id: relId, sourceId: fileId, @@ -250,7 +316,30 @@ export const processParsing = async ( graph.addRelationship(relationship); }); - - // Don't delete tree here - LRU cache handles cleanup when evicted } }; + +// ============================================================================ +// Public API +// ============================================================================ + +export const processParsing = async ( + graph: KnowledgeGraph, + files: { path: string; content: string }[], + symbolTable: SymbolTable, + astCache: ASTCache, + onFileProgress?: FileProgressCallback, + workerPool?: WorkerPool, +): Promise => { + if (workerPool) { + try { + return await processParsingWithWorkers(graph, files, symbolTable, astCache, workerPool, onFileProgress); + } catch (err) { + console.warn('Worker pool parsing failed, falling back to sequential:', err); + } + } + + // Fallback: sequential parsing (no pre-extracted data) + await processParsingSequential(graph, files, symbolTable, astCache, onFileProgress); + return null; +}; diff --git a/gitnexus/src/core/ingestion/pipeline.ts b/gitnexus/src/core/ingestion/pipeline.ts index 8c276b312..d87aebf52 100644 --- a/gitnexus/src/core/ingestion/pipeline.ts +++ b/gitnexus/src/core/ingestion/pipeline.ts @@ -1,304 +1,334 @@ -import { createKnowledgeGraph } from '../graph/graph'; -import { extractZip, FileEntry } from '../../services/zip'; -import { processStructure } from './structure-processor'; -import { processParsing } from './parsing-processor'; -import { processImports, createImportMap } from './import-processor'; -import { processCalls } from './call-processor'; -import { processHeritage } from './heritage-processor'; -import { processCommunities, CommunityDetectionResult } from './community-processor'; -import { processProcesses, ProcessDetectionResult } from './process-processor'; -import { createSymbolTable } from './symbol-table'; -import { createASTCache } from './ast-cache'; -import { PipelineProgress, PipelineResult } from '../../types/pipeline'; +import { createKnowledgeGraph } from '../graph/graph.js'; +import { processStructure } from './structure-processor.js'; +import { processParsing } from './parsing-processor.js'; +import { processImports, processImportsFromExtracted, createImportMap } from './import-processor.js'; +import { processCalls, processCallsFromExtracted } from './call-processor.js'; +import { processHeritage, processHeritageFromExtracted } from './heritage-processor.js'; +import { processCommunities } from './community-processor.js'; +import { processProcesses } from './process-processor.js'; +import { createSymbolTable } from './symbol-table.js'; +import { createASTCache } from './ast-cache.js'; +import { PipelineProgress, PipelineResult } from '../../types/pipeline.js'; +import { walkRepository } from './filesystem-walker.js'; +import { createWorkerPool, WorkerPool } from './workers/worker-pool.js'; -/** - * Run the ingestion pipeline from a ZIP file - */ -export const runIngestionPipeline = async ( file: File, onProgress: (progress: PipelineProgress) => void): Promise => { - // Phase 1: Extracting (0-15%) - onProgress({ - phase: 'extracting', - percent: 0, - message: 'Extracting ZIP file...', - }); - - // Fake progress for extraction (JSZip doesn't expose progress) - const fakeExtractionProgress = setInterval(() => { - onProgress({ - phase: 'extracting', - percent: Math.min(14, Math.random() * 10 + 5), - message: 'Extracting ZIP file...', - }); - }, 200); - - const files = await extractZip(file); - clearInterval(fakeExtractionProgress); - - // Continue with common pipeline - return runPipelineFromFiles(files, onProgress); -}; +const isDev = process.env.NODE_ENV === 'development'; -/** - * Run the ingestion pipeline from pre-extracted files (e.g., from git clone) - */ -export const runPipelineFromFiles = async ( - files: FileEntry[], +export const runPipelineFromRepo = async ( + repoPath: string, onProgress: (progress: PipelineProgress) => void ): Promise => { const graph = createKnowledgeGraph(); const fileContents = new Map(); const symbolTable = createSymbolTable(); - const astCache = createASTCache(50); // Keep last 50 files hot + // AST cache sized after file scan — start with a placeholder, resize after we know file count + let astCache = createASTCache(50); const importMap = createImportMap(); - // Cleanup function for error handling const cleanup = () => { astCache.clear(); symbolTable.clear(); }; - + try { - // Store file contents for code panel - files.forEach(f => fileContents.set(f.path, f.content)); - - onProgress({ - phase: 'extracting', - percent: 15, - message: 'ZIP extracted successfully', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: 0 }, - }); - - // Phase 2: Structure (15-30%) - onProgress({ - phase: 'structure', - percent: 15, - message: 'Analyzing project structure...', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: 0 }, - }); - - const filePaths = files.map(f => f.path); - processStructure(graph, filePaths); - - onProgress({ - phase: 'structure', - percent: 30, - message: 'Project structure analyzed', - stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); - - // Phase 3: Parsing (30-70%) - onProgress({ - phase: 'parsing', - percent: 30, - message: 'Parsing code definitions...', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); - - await processParsing(graph, files, symbolTable, astCache, (current, total, filePath) => { - const parsingProgress = 30 + ((current / total) * 40); onProgress({ - phase: 'parsing', - percent: Math.round(parsingProgress), - message: 'Parsing code definitions...', - detail: filePath, - stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + phase: 'extracting', + percent: 0, + message: 'Scanning repository...', }); - }); - - // Phase 4: Imports (70-82%) - onProgress({ - phase: 'imports', - percent: 70, - message: 'Resolving imports...', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); - - await processImports(graph, files, astCache, importMap, (current, total) => { - const importProgress = 70 + ((current / total) * 12); - onProgress({ - phase: 'imports', - percent: Math.round(importProgress), - message: 'Resolving imports...', - stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + const files = await walkRepository(repoPath, (current, total, filePath) => { + const scanProgress = Math.round((current / total) * 15); + onProgress({ + phase: 'extracting', + percent: scanProgress, + message: 'Scanning repository...', + detail: filePath, + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); }); - }); - - // Debug: Count IMPORTS relationships - if (import.meta.env.DEV) { - const importsCount = graph.relationships.filter(r => r.type === 'IMPORTS').length; - console.log(`📊 Pipeline: After import phase, graph has ${importsCount} IMPORTS relationships (total: ${graph.relationshipCount})`); - if (importsCount > 0) { - const sample = graph.relationships.filter(r => r.type === 'IMPORTS').slice(0, 3); - sample.forEach(r => console.log(` Sample IMPORTS: ${r.sourceId} → ${r.targetId}`)); - } - } + files.forEach(f => fileContents.set(f.path, f.content)); - // Phase 5: Calls (82-98%) - onProgress({ - phase: 'calls', - percent: 82, - message: 'Tracing function calls...', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); + // Resize AST cache to fit all files — avoids re-parsing in import/call/heritage phases + astCache = createASTCache(files.length); - await processCalls(graph, files, astCache, symbolTable, importMap, (current, total) => { - const callProgress = 82 + ((current / total) * 10); onProgress({ - phase: 'calls', - percent: Math.round(callProgress), - message: 'Tracing function calls...', - stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, - }); - }); - - // Phase 6: Heritage - Class inheritance (92-98%) - onProgress({ - phase: 'heritage', - percent: 92, - message: 'Extracting class inheritance...', - stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); - - await processHeritage(graph, files, astCache, symbolTable, (current, total) => { - const heritageProgress = 88 + ((current / total) * 4); - onProgress({ - phase: 'heritage', - percent: Math.round(heritageProgress), - message: 'Extracting class inheritance...', - stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, - }); - }); - - // Phase 7: Community Detection (92-98%) - onProgress({ - phase: 'communities', - percent: 92, - message: 'Detecting code communities...', - stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); - - const communityResult = await processCommunities(graph, (message, progress) => { - const communityProgress = 92 + (progress * 0.06); - onProgress({ - phase: 'communities', - percent: Math.round(communityProgress), - message, + phase: 'extracting', + percent: 15, + message: 'Repository scanned successfully', stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, }); - }); - // Log community detection results - if (import.meta.env.DEV) { - console.log(`🏘️ Community detection: ${communityResult.stats.totalCommunities} communities found (modularity: ${communityResult.stats.modularity.toFixed(3)})`); - } - - // Add community nodes to the graph - communityResult.communities.forEach(comm => { - graph.addNode({ - id: comm.id, - label: 'Community' as const, - properties: { - name: comm.label, - filePath: '', - heuristicLabel: comm.heuristicLabel, - cohesion: comm.cohesion, - symbolCount: comm.symbolCount, - } + onProgress({ + phase: 'structure', + percent: 15, + message: 'Analyzing project structure...', + stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, }); - }); - // Add MEMBER_OF relationships - communityResult.memberships.forEach(membership => { - graph.addRelationship({ - id: `${membership.nodeId}_member_of_${membership.communityId}`, - type: 'MEMBER_OF', - sourceId: membership.nodeId, - targetId: membership.communityId, - confidence: 1.0, - reason: 'leiden-algorithm', + const filePaths = files.map(f => f.path); + processStructure(graph, filePaths); + + onProgress({ + phase: 'structure', + percent: 30, + message: 'Project structure analyzed', + stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, }); - }); - // Phase 8: Process Detection (98-99%) - onProgress({ - phase: 'processes', - percent: 98, - message: 'Detecting execution flows...', - stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, - }); + onProgress({ + phase: 'parsing', + percent: 30, + message: 'Parsing code definitions...', + stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); - const processResult = await processProcesses( - graph, - communityResult.memberships, - (message, progress) => { - const processProgress = 98 + (progress * 0.01); + // Create worker pool for parallel parsing, with graceful fallback + let workerPool: WorkerPool | undefined; + try { + const workerUrl = new URL('./workers/parse-worker.js', import.meta.url); + workerPool = createWorkerPool(workerUrl); + } catch (err) { + // Worker pool creation failed (e.g., single core) — sequential fallback + } + + let workerData: Awaited> = null; + try { + workerData = await processParsing(graph, files, symbolTable, astCache, (current, total, filePath) => { + const parsingProgress = 30 + ((current / total) * 40); + onProgress({ + phase: 'parsing', + percent: Math.round(parsingProgress), + message: 'Parsing code definitions...', + detail: filePath, + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }, workerPool); + } finally { + await workerPool?.terminate(); + } + + onProgress({ + phase: 'imports', + percent: 70, + message: 'Resolving imports...', + stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); + + if (workerData) { + // Fast path: imports already extracted by workers, just resolve paths + await processImportsFromExtracted(graph, files, workerData.imports, importMap, (current, total) => { + const importProgress = 70 + ((current / total) * 12); + onProgress({ + phase: 'imports', + percent: Math.round(importProgress), + message: 'Resolving imports...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }, repoPath); + } else { + // Fallback: full parse + resolve (sequential path) + await processImports(graph, files, astCache, importMap, (current, total) => { + const importProgress = 70 + ((current / total) * 12); + onProgress({ + phase: 'imports', + percent: Math.round(importProgress), + message: 'Resolving imports...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }, repoPath); + } + + if (isDev) { + const importsCount = graph.relationships.filter(r => r.type === 'IMPORTS').length; + console.log(`📊 Pipeline: After import phase, graph has ${importsCount} IMPORTS relationships (total: ${graph.relationshipCount})`); + } + + onProgress({ + phase: 'calls', + percent: 82, + message: 'Tracing function calls...', + stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); + + if (workerData) { + // Fast path: calls already extracted by workers, just resolve targets + await processCallsFromExtracted(graph, workerData.calls, symbolTable, importMap, (current, total) => { + const callProgress = 82 + ((current / total) * 10); + onProgress({ + phase: 'calls', + percent: Math.round(callProgress), + message: 'Tracing function calls...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }); + } else { + // Fallback: full parse + resolve (sequential path) + await processCalls(graph, files, astCache, symbolTable, importMap, (current, total) => { + const callProgress = 82 + ((current / total) * 10); + onProgress({ + phase: 'calls', + percent: Math.round(callProgress), + message: 'Tracing function calls...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }); + } + + onProgress({ + phase: 'heritage', + percent: 92, + message: 'Extracting class inheritance...', + stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); + + if (workerData) { + // Fast path: heritage already extracted by workers, just resolve symbols + await processHeritageFromExtracted(graph, workerData.heritage, symbolTable, (current, total) => { + const heritageProgress = 88 + ((current / total) * 4); + onProgress({ + phase: 'heritage', + percent: Math.round(heritageProgress), + message: 'Extracting class inheritance...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }); + } else { + // Fallback: full parse + resolve (sequential path) + await processHeritage(graph, files, astCache, symbolTable, (current, total) => { + const heritageProgress = 88 + ((current / total) * 4); + onProgress({ + phase: 'heritage', + percent: Math.round(heritageProgress), + message: 'Extracting class inheritance...', + stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount }, + }); + }); + } + + onProgress({ + phase: 'communities', + percent: 92, + message: 'Detecting code communities...', + stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); + + const communityResult = await processCommunities(graph, (message, progress) => { + const communityProgress = 92 + (progress * 0.06); onProgress({ - phase: 'processes', - percent: Math.round(processProgress), + phase: 'communities', + percent: Math.round(communityProgress), message, stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, }); + }); + + if (isDev) { + console.log(`🏘️ Community detection: ${communityResult.stats.totalCommunities} communities found (modularity: ${communityResult.stats.modularity.toFixed(3)})`); } - ); - // Log process detection results - if (import.meta.env.DEV) { - console.log(`🔄 Process detection: ${processResult.stats.totalProcesses} processes found (${processResult.stats.crossCommunityCount} cross-community)`); - } - - // Add Process nodes to the graph - processResult.processes.forEach(proc => { - graph.addNode({ - id: proc.id, - label: 'Process' as const, - properties: { - name: proc.label, - filePath: '', - heuristicLabel: proc.heuristicLabel, - processType: proc.processType, - stepCount: proc.stepCount, - communities: proc.communities, - entryPointId: proc.entryPointId, - terminalId: proc.terminalId, - } + communityResult.communities.forEach(comm => { + graph.addNode({ + id: comm.id, + label: 'Community' as const, + properties: { + name: comm.label, + filePath: '', + heuristicLabel: comm.heuristicLabel, + cohesion: comm.cohesion, + symbolCount: comm.symbolCount, + } + }); }); - }); - // Add STEP_IN_PROCESS relationships - processResult.steps.forEach(step => { - graph.addRelationship({ - id: `${step.nodeId}_step_${step.step}_${step.processId}`, - type: 'STEP_IN_PROCESS', - sourceId: step.nodeId, - targetId: step.processId, - confidence: 1.0, - reason: 'trace-detection', - step: step.step, + communityResult.memberships.forEach(membership => { + graph.addRelationship({ + id: `${membership.nodeId}_member_of_${membership.communityId}`, + type: 'MEMBER_OF', + sourceId: membership.nodeId, + targetId: membership.communityId, + confidence: 1.0, + reason: 'leiden-algorithm', + }); }); - }); - - // Phase 9: Complete (100%) - onProgress({ - phase: 'complete', - percent: 100, - message: `Graph complete! ${communityResult.stats.totalCommunities} communities, ${processResult.stats.totalProcesses} processes detected.`, - stats: { - filesProcessed: files.length, - totalFiles: files.length, - nodesCreated: graph.nodeCount - }, - }); + onProgress({ + phase: 'processes', + percent: 98, + message: 'Detecting execution flows...', + stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); - // Cleanup WASM memory before returning - astCache.clear(); - - return { graph, fileContents, communityResult, processResult }; + // Dynamic process cap based on codebase size + const symbolCount = graph.nodes.filter(n => n.label !== 'File').length; + const dynamicMaxProcesses = Math.max(20, Math.min(300, Math.round(symbolCount / 10))); + const processResult = await processProcesses( + graph, + communityResult.memberships, + (message, progress) => { + const processProgress = 98 + (progress * 0.01); + onProgress({ + phase: 'processes', + percent: Math.round(processProgress), + message, + stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount }, + }); + }, + { maxProcesses: dynamicMaxProcesses, minSteps: 3 } + ); + + if (isDev) { + console.log(`🔄 Process detection: ${processResult.stats.totalProcesses} processes found (${processResult.stats.crossCommunityCount} cross-community)`); + } + + processResult.processes.forEach(proc => { + graph.addNode({ + id: proc.id, + label: 'Process' as const, + properties: { + name: proc.label, + filePath: '', + heuristicLabel: proc.heuristicLabel, + processType: proc.processType, + stepCount: proc.stepCount, + communities: proc.communities, + entryPointId: proc.entryPointId, + terminalId: proc.terminalId, + } + }); + }); + + processResult.steps.forEach(step => { + graph.addRelationship({ + id: `${step.nodeId}_step_${step.step}_${step.processId}`, + type: 'STEP_IN_PROCESS', + sourceId: step.nodeId, + targetId: step.processId, + confidence: 1.0, + reason: 'trace-detection', + step: step.step, + }); + }); + + onProgress({ + phase: 'complete', + percent: 100, + message: `Graph complete! ${communityResult.stats.totalCommunities} communities, ${processResult.stats.totalProcesses} processes detected.`, + stats: { + filesProcessed: files.length, + totalFiles: files.length, + nodesCreated: graph.nodeCount + }, + }); + + astCache.clear(); + + return { graph, fileContents, communityResult, processResult }; } catch (error) { cleanup(); throw error; } }; + diff --git a/gitnexus/src/core/ingestion/process-processor.ts b/gitnexus/src/core/ingestion/process-processor.ts index cf983d2e6..10d3261fb 100644 --- a/gitnexus/src/core/ingestion/process-processor.ts +++ b/gitnexus/src/core/ingestion/process-processor.ts @@ -10,9 +10,11 @@ * Processes help agents understand how features work through the codebase. */ -import { KnowledgeGraph, GraphNode, GraphRelationship, NodeLabel } from '../graph/types'; -import { CommunityMembership } from './community-processor'; -import { calculateEntryPointScore, isTestFile } from './entry-point-scoring'; +import { KnowledgeGraph, GraphNode, GraphRelationship, NodeLabel } from '../graph/types.js'; +import { CommunityMembership } from './community-processor.js'; +import { calculateEntryPointScore, isTestFile } from './entry-point-scoring.js'; + +const isDev = process.env.NODE_ENV === 'development'; // ============================================================================ // CONFIGURATION @@ -29,7 +31,7 @@ const DEFAULT_CONFIG: ProcessDetectionConfig = { maxTraceDepth: 10, maxBranching: 4, maxProcesses: 75, - minSteps: 2, + minSteps: 3, // 3+ steps = genuine multi-hop flow (2-step is just "A calls B") }; // ============================================================================ @@ -117,11 +119,16 @@ export const processProcesses = async ( onProgress?.(`Found ${allTraces.length} traces, deduplicating...`, 60); - // Step 3: Deduplicate similar traces + // Step 3: Deduplicate similar traces (subset removal) const uniqueTraces = deduplicateTraces(allTraces); + // Step 3b: Deduplicate by entry+terminal pair (keep longest path per pair) + const endpointDeduped = deduplicateByEndpoints(uniqueTraces); + + onProgress?.(`Deduped ${uniqueTraces.length} → ${endpointDeduped.length} unique endpoint pairs`, 70); + // Step 4: Limit to max processes (prioritize longer traces) - const limitedTraces = uniqueTraces + const limitedTraces = endpointDeduped .sort((a, b) => b.length - a.length) .slice(0, cfg.maxProcesses); @@ -204,11 +211,18 @@ export const processProcesses = async ( type AdjacencyList = Map; +/** + * Minimum edge confidence for process tracing. + * Filters out ambiguous fuzzy-global matches (0.3) that cause + * traces to jump across unrelated code areas. + */ +const MIN_TRACE_CONFIDENCE = 0.5; + const buildCallsGraph = (graph: KnowledgeGraph): AdjacencyList => { const adj = new Map(); graph.relationships.forEach(rel => { - if (rel.type === 'CALLS') { + if (rel.type === 'CALLS' && rel.confidence >= MIN_TRACE_CONFIDENCE) { if (!adj.has(rel.sourceId)) { adj.set(rel.sourceId, []); } @@ -223,7 +237,7 @@ const buildReverseCallsGraph = (graph: KnowledgeGraph): AdjacencyList => { const adj = new Map(); graph.relationships.forEach(rel => { - if (rel.type === 'CALLS') { + if (rel.type === 'CALLS' && rel.confidence >= MIN_TRACE_CONFIDENCE) { if (!adj.has(rel.targetId)) { adj.set(rel.targetId, []); } @@ -289,7 +303,7 @@ const findEntryPoints = ( const sorted = entryPointCandidates.sort((a, b) => b.score - a.score); // DEBUG: Log top candidates with new scoring details - if (sorted.length > 0 && typeof import.meta !== 'undefined' && import.meta.env?.DEV) { + if (sorted.length > 0 && isDev) { console.log(`[Process] Top 10 entry point candidates (new scoring):`); sorted.slice(0, 10).forEach((c, i) => { const node = graph.nodes.find(n => n.id === c.id); @@ -395,6 +409,31 @@ const deduplicateTraces = (traces: string[][]): string[][] => { return unique; }; +// ============================================================================ +// HELPER: Deduplicate by entry+terminal endpoints +// ============================================================================ + +/** + * Keep only the longest trace per unique entry→terminal pair. + * Multiple paths between the same two endpoints are redundant for agents. + */ +const deduplicateByEndpoints = (traces: string[][]): string[][] => { + if (traces.length === 0) return []; + + const byEndpoints = new Map(); + // Sort longest first so the first seen per key is the longest + const sorted = [...traces].sort((a, b) => b.length - a.length); + + for (const trace of sorted) { + const key = `${trace[0]}::${trace[trace.length - 1]}`; + if (!byEndpoints.has(key)) { + byEndpoints.set(key, trace); + } + } + + return Array.from(byEndpoints.values()); +}; + // ============================================================================ // HELPER: String utilities // ============================================================================ diff --git a/gitnexus/src/core/ingestion/structure-processor.ts b/gitnexus/src/core/ingestion/structure-processor.ts index c73a5837c..de1a53e49 100644 --- a/gitnexus/src/core/ingestion/structure-processor.ts +++ b/gitnexus/src/core/ingestion/structure-processor.ts @@ -1,5 +1,5 @@ -import { generateId } from "@/lib/utils"; -import { KnowledgeGraph, GraphNode, GraphRelationship } from "../graph/types"; +import { generateId } from "../../lib/utils.js"; +import { KnowledgeGraph, GraphNode, GraphRelationship } from "../graph/types.js"; export const processStructure = ( graph: KnowledgeGraph, paths: string[])=>{ paths.forEach( path => { diff --git a/gitnexus/src/core/ingestion/tree-sitter-queries.ts b/gitnexus/src/core/ingestion/tree-sitter-queries.ts index a931b4a40..f8bcd7add 100644 --- a/gitnexus/src/core/ingestion/tree-sitter-queries.ts +++ b/gitnexus/src/core/ingestion/tree-sitter-queries.ts @@ -1,4 +1,4 @@ -import { SupportedLanguages } from '../../config/supported-languages'; +import { SupportedLanguages } from '../../config/supported-languages.js'; /* * Tree-sitter queries for extracting code definitions. diff --git a/gitnexus/src/core/ingestion/utils.ts b/gitnexus/src/core/ingestion/utils.ts index 959eb55dc..620fac7ac 100644 --- a/gitnexus/src/core/ingestion/utils.ts +++ b/gitnexus/src/core/ingestion/utils.ts @@ -1,4 +1,10 @@ -import { SupportedLanguages } from '../../config/supported-languages'; +import { SupportedLanguages } from '../../config/supported-languages.js'; + +/** + * Yield control to the event loop so spinners/progress can render. + * Call periodically in hot loops to prevent UI freezes. + */ +export const yieldToEventLoop = (): Promise => new Promise(resolve => setImmediate(resolve)); /** * Map file extension to SupportedLanguage enum diff --git a/gitnexus/src/core/ingestion/workers/parse-worker.ts b/gitnexus/src/core/ingestion/workers/parse-worker.ts new file mode 100644 index 000000000..573884f36 --- /dev/null +++ b/gitnexus/src/core/ingestion/workers/parse-worker.ts @@ -0,0 +1,535 @@ +import { parentPort } from 'node:worker_threads'; +import Parser from 'tree-sitter'; +import JavaScript from 'tree-sitter-javascript'; +import TypeScript from 'tree-sitter-typescript'; +import Python from 'tree-sitter-python'; +import Java from 'tree-sitter-java'; +import C from 'tree-sitter-c'; +import CPP from 'tree-sitter-cpp'; +import CSharp from 'tree-sitter-c-sharp'; +import Go from 'tree-sitter-go'; +import Rust from 'tree-sitter-rust'; +import { SupportedLanguages } from '../../../config/supported-languages.js'; +import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js'; +import { getLanguageFromFilename } from '../utils.js'; +import { generateId } from '../../../lib/utils.js'; + +// ============================================================================ +// Types for serializable results +// ============================================================================ + +interface ParsedNode { + id: string; + label: string; + properties: { + name: string; + filePath: string; + startLine: number; + endLine: number; + language: string; + isExported: boolean; + }; +} + +interface ParsedRelationship { + id: string; + sourceId: string; + targetId: string; + type: 'DEFINES'; + confidence: number; + reason: string; +} + +interface ParsedSymbol { + filePath: string; + name: string; + nodeId: string; + type: string; +} + +export interface ExtractedImport { + filePath: string; + rawImportPath: string; + language: string; +} + +export interface ExtractedCall { + filePath: string; + calledName: string; + /** generateId of enclosing function, or generateId('File', filePath) for top-level */ + sourceId: string; +} + +export interface ExtractedHeritage { + filePath: string; + className: string; + parentName: string; + /** 'extends' | 'implements' | 'trait-impl' */ + kind: string; +} + +export interface ParseWorkerResult { + nodes: ParsedNode[]; + relationships: ParsedRelationship[]; + symbols: ParsedSymbol[]; + imports: ExtractedImport[]; + calls: ExtractedCall[]; + heritage: ExtractedHeritage[]; + fileCount: number; +} + +export interface ParseWorkerInput { + path: string; + content: string; +} + +// ============================================================================ +// Worker-local parser + language map +// ============================================================================ + +const parser = new Parser(); + +const languageMap: Record = { + [SupportedLanguages.JavaScript]: JavaScript, + [SupportedLanguages.TypeScript]: TypeScript.typescript, + [`${SupportedLanguages.TypeScript}:tsx`]: TypeScript.tsx, + [SupportedLanguages.Python]: Python, + [SupportedLanguages.Java]: Java, + [SupportedLanguages.C]: C, + [SupportedLanguages.CPlusPlus]: CPP, + [SupportedLanguages.CSharp]: CSharp, + [SupportedLanguages.Go]: Go, + [SupportedLanguages.Rust]: Rust, +}; + +const setLanguage = (language: SupportedLanguages, filePath: string): void => { + const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx') + ? `${language}:tsx` + : language; + const lang = languageMap[key]; + if (!lang) throw new Error(`Unsupported language: ${language}`); + parser.setLanguage(lang); +}; + +// ============================================================================ +// Export detection (copied — needs AST parent traversal, can't cross threads) +// ============================================================================ + +const isNodeExported = (node: any, name: string, language: string): boolean => { + let current = node; + + switch (language) { + case 'javascript': + case 'typescript': + while (current) { + const type = current.type; + if (type === 'export_statement' || + type === 'export_specifier' || + type === 'lexical_declaration' && current.parent?.type === 'export_statement') { + return true; + } + if (current.text?.startsWith('export ')) { + return true; + } + current = current.parent; + } + return false; + + case 'python': + return !name.startsWith('_'); + + case 'java': + while (current) { + if (current.parent) { + const parent = current.parent; + for (let i = 0; i < parent.childCount; i++) { + const child = parent.child(i); + if (child?.type === 'modifiers' && child.text?.includes('public')) { + return true; + } + } + if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') { + if (parent.text?.trimStart().startsWith('public')) { + return true; + } + } + } + current = current.parent; + } + return false; + + case 'csharp': + while (current) { + if (current.type === 'modifier' || current.type === 'modifiers') { + if (current.text?.includes('public')) return true; + } + current = current.parent; + } + return false; + + case 'go': + if (name.length === 0) return false; + const first = name[0]; + return first === first.toUpperCase() && first !== first.toLowerCase(); + + case 'rust': + while (current) { + if (current.type === 'visibility_modifier') { + if (current.text?.includes('pub')) return true; + } + current = current.parent; + } + return false; + + case 'c': + case 'cpp': + return false; + + default: + return false; + } +}; + +// ============================================================================ +// Enclosing function detection (for call extraction) +// ============================================================================ + +const FUNCTION_NODE_TYPES = new Set([ + 'function_declaration', 'arrow_function', 'function_expression', + 'method_definition', 'generator_function_declaration', + 'function_definition', 'async_function_declaration', 'async_arrow_function', + 'method_declaration', 'constructor_declaration', + 'local_function_statement', 'function_item', 'impl_item', +]); + +/** Walk up AST to find enclosing function, return its generateId or null for top-level */ +const findEnclosingFunctionId = (node: any, filePath: string): string | null => { + let current = node.parent; + while (current) { + if (FUNCTION_NODE_TYPES.has(current.type)) { + let funcName: string | null = null; + let label = 'Function'; + + if (['function_declaration', 'function_definition', 'async_function_declaration', + 'generator_function_declaration', 'function_item'].includes(current.type)) { + const nameNode = current.childForFieldName?.('name') || + current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier'); + funcName = nameNode?.text; + } else if (current.type === 'impl_item') { + const funcItem = current.children?.find((c: any) => c.type === 'function_item'); + if (funcItem) { + const nameNode = funcItem.childForFieldName?.('name') || + funcItem.children?.find((c: any) => c.type === 'identifier'); + funcName = nameNode?.text; + label = 'Method'; + } + } else if (current.type === 'method_definition') { + const nameNode = current.childForFieldName?.('name') || + current.children?.find((c: any) => c.type === 'property_identifier'); + funcName = nameNode?.text; + label = 'Method'; + } else if (current.type === 'method_declaration' || current.type === 'constructor_declaration') { + const nameNode = current.childForFieldName?.('name') || + current.children?.find((c: any) => c.type === 'identifier'); + funcName = nameNode?.text; + label = 'Method'; + } else if (current.type === 'arrow_function' || current.type === 'function_expression') { + const parent = current.parent; + if (parent?.type === 'variable_declarator') { + const nameNode = parent.childForFieldName?.('name') || + parent.children?.find((c: any) => c.type === 'identifier'); + funcName = nameNode?.text; + } + } + + if (funcName) { + return generateId(label, `${filePath}:${funcName}`); + } + } + current = current.parent; + } + return null; +}; + +const BUILT_INS = new Set([ + 'console', 'log', 'warn', 'error', 'info', 'debug', + 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', + 'parseInt', 'parseFloat', 'isNaN', 'isFinite', + 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent', + 'JSON', 'parse', 'stringify', + 'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt', + 'Map', 'Set', 'WeakMap', 'WeakSet', + 'Promise', 'resolve', 'reject', 'then', 'catch', 'finally', + 'Math', 'Date', 'RegExp', 'Error', + 'require', 'import', 'export', 'fetch', 'Response', 'Request', + 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext', + 'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue', + 'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy', + 'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every', + 'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split', + 'push', 'pop', 'shift', 'unshift', 'sort', 'reverse', + 'keys', 'values', 'entries', 'assign', 'freeze', 'seal', + 'hasOwnProperty', 'toString', 'valueOf', + 'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple', + 'open', 'read', 'write', 'close', 'append', 'extend', 'update', + 'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr', + 'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs', +]); + +// ============================================================================ +// Label detection from capture map +// ============================================================================ + +const getLabelFromCaptures = (captureMap: Record): string | null => { + // Skip imports (handled separately) and calls + if (captureMap['import'] || captureMap['call']) return null; + if (!captureMap['name']) return null; + + if (captureMap['definition.function']) return 'Function'; + if (captureMap['definition.class']) return 'Class'; + if (captureMap['definition.interface']) return 'Interface'; + if (captureMap['definition.method']) return 'Method'; + if (captureMap['definition.struct']) return 'Struct'; + if (captureMap['definition.enum']) return 'Enum'; + if (captureMap['definition.namespace']) return 'Namespace'; + if (captureMap['definition.module']) return 'Module'; + if (captureMap['definition.trait']) return 'Trait'; + if (captureMap['definition.impl']) return 'Impl'; + if (captureMap['definition.type']) return 'TypeAlias'; + if (captureMap['definition.const']) return 'Const'; + if (captureMap['definition.static']) return 'Static'; + if (captureMap['definition.typedef']) return 'Typedef'; + if (captureMap['definition.macro']) return 'Macro'; + if (captureMap['definition.union']) return 'Union'; + if (captureMap['definition.property']) return 'Property'; + if (captureMap['definition.record']) return 'Record'; + if (captureMap['definition.delegate']) return 'Delegate'; + if (captureMap['definition.annotation']) return 'Annotation'; + if (captureMap['definition.constructor']) return 'Constructor'; + if (captureMap['definition.template']) return 'Template'; + return 'CodeElement'; +}; + +// ============================================================================ +// Process a batch of files +// ============================================================================ + +const processBatch = (files: ParseWorkerInput[], onProgress?: (filesProcessed: number) => void): ParseWorkerResult => { + const result: ParseWorkerResult = { + nodes: [], + relationships: [], + symbols: [], + imports: [], + calls: [], + heritage: [], + fileCount: 0, + }; + + // Group by language to minimize setLanguage calls + const byLanguage = new Map(); + for (const file of files) { + const lang = getLanguageFromFilename(file.path); + if (!lang) continue; + let list = byLanguage.get(lang); + if (!list) { + list = []; + byLanguage.set(lang, list); + } + list.push(file); + } + + let totalProcessed = 0; + let lastReported = 0; + const PROGRESS_INTERVAL = 100; // report every 100 files + + const onFileProcessed = onProgress ? () => { + totalProcessed++; + if (totalProcessed - lastReported >= PROGRESS_INTERVAL) { + lastReported = totalProcessed; + onProgress(totalProcessed); + } + } : undefined; + + for (const [language, langFiles] of byLanguage) { + const queryString = LANGUAGE_QUERIES[language]; + if (!queryString) continue; + + // Track if we need to handle tsx separately + const tsxFiles: ParseWorkerInput[] = []; + const regularFiles: ParseWorkerInput[] = []; + + if (language === SupportedLanguages.TypeScript) { + for (const f of langFiles) { + if (f.path.endsWith('.tsx')) { + tsxFiles.push(f); + } else { + regularFiles.push(f); + } + } + } else { + regularFiles.push(...langFiles); + } + + // Process regular files for this language + if (regularFiles.length > 0) { + setLanguage(language, regularFiles[0].path); + processFileGroup(regularFiles, language, queryString, result, onFileProcessed); + } + + // Process tsx files separately (different grammar) + if (tsxFiles.length > 0) { + setLanguage(language, tsxFiles[0].path); + processFileGroup(tsxFiles, language, queryString, result, onFileProcessed); + } + } + + return result; +}; + +const processFileGroup = ( + files: ParseWorkerInput[], + language: SupportedLanguages, + queryString: string, + result: ParseWorkerResult, + onFileProcessed?: () => void, +): void => { + let query: any; + try { + const lang = parser.getLanguage(); + query = new Parser.Query(lang, queryString); + } catch { + return; + } + + for (const file of files) { + let tree; + try { + tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + } catch { + continue; + } + + result.fileCount++; + onFileProcessed?.(); + + let matches; + try { + matches = query.matches(tree.rootNode); + } catch { + continue; + } + + for (const match of matches) { + const captureMap: Record = {}; + for (const c of match.captures) { + captureMap[c.name] = c.node; + } + + // Extract import paths before skipping + if (captureMap['import'] && captureMap['import.source']) { + const rawImportPath = captureMap['import.source'].text.replace(/['"<>]/g, ''); + result.imports.push({ + filePath: file.path, + rawImportPath, + language: language, + }); + continue; + } + + // Extract call sites + if (captureMap['call']) { + const callNameNode = captureMap['call.name']; + if (callNameNode) { + const calledName = callNameNode.text; + if (!BUILT_INS.has(calledName)) { + const callNode = captureMap['call']; + const sourceId = findEnclosingFunctionId(callNode, file.path) + || generateId('File', file.path); + result.calls.push({ filePath: file.path, calledName, sourceId }); + } + } + continue; + } + + // Extract heritage (extends/implements) + if (captureMap['heritage.class']) { + if (captureMap['heritage.extends']) { + result.heritage.push({ + filePath: file.path, + className: captureMap['heritage.class'].text, + parentName: captureMap['heritage.extends'].text, + kind: 'extends', + }); + } + if (captureMap['heritage.implements']) { + result.heritage.push({ + filePath: file.path, + className: captureMap['heritage.class'].text, + parentName: captureMap['heritage.implements'].text, + kind: 'implements', + }); + } + if (captureMap['heritage.trait']) { + result.heritage.push({ + filePath: file.path, + className: captureMap['heritage.class'].text, + parentName: captureMap['heritage.trait'].text, + kind: 'trait-impl', + }); + } + if (captureMap['heritage.extends'] || captureMap['heritage.implements'] || captureMap['heritage.trait']) { + continue; + } + } + + const nodeLabel = getLabelFromCaptures(captureMap); + if (!nodeLabel) continue; + + const nameNode = captureMap['name']; + const nodeName = nameNode.text; + const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`); + + result.nodes.push({ + id: nodeId, + label: nodeLabel, + properties: { + name: nodeName, + filePath: file.path, + startLine: nameNode.startPosition.row, + endLine: nameNode.endPosition.row, + language: language, + isExported: isNodeExported(nameNode, nodeName, language), + }, + }); + + result.symbols.push({ + filePath: file.path, + name: nodeName, + nodeId, + type: nodeLabel, + }); + + const fileId = generateId('File', file.path); + const relId = generateId('DEFINES', `${fileId}->${nodeId}`); + result.relationships.push({ + id: relId, + sourceId: fileId, + targetId: nodeId, + type: 'DEFINES', + confidence: 1.0, + reason: '', + }); + } + } +}; + +// ============================================================================ +// Worker message handler +// ============================================================================ + +parentPort!.on('message', (files: ParseWorkerInput[]) => { + const result = processBatch(files, (filesProcessed) => { + parentPort!.postMessage({ type: 'progress', filesProcessed }); + }); + parentPort!.postMessage({ type: 'result', data: result }); +}); diff --git a/gitnexus/src/core/ingestion/workers/worker-pool.ts b/gitnexus/src/core/ingestion/workers/worker-pool.ts new file mode 100644 index 000000000..aad23b178 --- /dev/null +++ b/gitnexus/src/core/ingestion/workers/worker-pool.ts @@ -0,0 +1,89 @@ +import { Worker } from 'node:worker_threads'; +import os from 'node:os'; + +export interface WorkerPool { + /** + * Dispatch items across workers. Items are split into chunks (one per worker), + * each worker processes its chunk, and results are concatenated back in order. + * + * @param onProgress - Called with cumulative files processed across all workers + */ + dispatch(items: TInput[], onProgress?: (filesProcessed: number) => void): Promise; + + /** + * Terminate all workers. Must be called when done. + */ + terminate(): Promise; + + /** Number of workers in the pool */ + readonly size: number; +} + +/** + * Create a pool of worker threads. + * + * @param workerUrl - URL to the worker script (use `new URL('./parse-worker.js', import.meta.url)`) + * @param poolSize - Number of workers (defaults to cpus - 1, minimum 1) + */ +export const createWorkerPool = (workerUrl: URL, poolSize?: number): WorkerPool => { + const size = poolSize ?? Math.max(1, os.cpus().length - 1); + const workers: Worker[] = []; + + for (let i = 0; i < size; i++) { + workers.push(new Worker(workerUrl)); + } + + const dispatch = (items: TInput[], onProgress?: (filesProcessed: number) => void): Promise => { + if (items.length === 0) return Promise.resolve([]); + + // Split items into one chunk per worker + const chunkSize = Math.ceil(items.length / size); + const chunks: TInput[][] = []; + for (let i = 0; i < items.length; i += chunkSize) { + chunks.push(items.slice(i, i + chunkSize)); + } + + // Track per-worker progress for cumulative reporting + const workerProgress = new Array(chunks.length).fill(0); + + // Send one chunk to each worker, collect results + const promises = chunks.map((chunk, i) => { + const worker = workers[i]; + return new Promise((resolve, reject) => { + const handler = (msg: any) => { + if (msg && msg.type === 'progress') { + // Intermediate progress from worker + workerProgress[i] = msg.filesProcessed; + if (onProgress) { + const total = workerProgress.reduce((a, b) => a + b, 0); + onProgress(total); + } + } else if (msg && msg.type === 'result') { + // Final result + worker.removeListener('message', handler); + resolve(msg.data); + } else { + // Legacy: treat any non-typed message as result (backward compat) + worker.removeListener('message', handler); + resolve(msg); + } + }; + worker.on('message', handler); + worker.once('error', (err) => { + worker.removeListener('message', handler); + reject(err); + }); + worker.postMessage(chunk); + }); + }); + + return Promise.all(promises); + }; + + const terminate = async (): Promise => { + await Promise.all(workers.map(w => w.terminate())); + workers.length = 0; + }; + + return { dispatch, terminate, size }; +}; diff --git a/gitnexus/src/core/kuzu/csv-generator.ts b/gitnexus/src/core/kuzu/csv-generator.ts index 43df569cf..266309570 100644 --- a/gitnexus/src/core/kuzu/csv-generator.ts +++ b/gitnexus/src/core/kuzu/csv-generator.ts @@ -10,20 +10,25 @@ * - All fields are consistently quoted for safety with code content */ -import { KnowledgeGraph, GraphNode, NodeLabel } from '../graph/types'; -import { NODE_TABLES, NodeTableName } from './schema'; +import { KnowledgeGraph, GraphNode, NodeLabel } from '../graph/types.js'; +import { NODE_TABLES, NodeTableName } from './schema.js'; // ============================================================================ // CSV ESCAPE UTILITIES // ============================================================================ /** - * Sanitize string to ensure valid UTF-8 - * Removes or replaces invalid characters that would break CSV parsing + * Sanitize string to ensure valid UTF-8 and safe CSV content for KuzuDB + * Removes or replaces invalid characters that would break CSV parsing. + * + * Critical: KuzuDB's native CSV parser on Windows can misinterpret \r\n + * inside quoted fields. We normalize all line endings to \n only. */ const sanitizeUTF8 = (str: string): string => { return str - .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Remove control chars except \t \n \r + .replace(/\r\n/g, '\n') // Normalize Windows line endings first + .replace(/\r/g, '\n') // Normalize remaining \r to \n + .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Remove control chars except \t \n .replace(/[\uD800-\uDFFF]/g, '') // Remove surrogate pairs (invalid standalone) .replace(/[\uFFFE\uFFFF]/g, ''); // Remove BOM and special chars }; @@ -133,9 +138,14 @@ export interface CSVData { const generateFileCSV = (nodes: GraphNode[], fileContents: Map): string => { const headers = ['id', 'name', 'filePath', 'content']; const rows: string[] = [headers.join(',')]; + const seenIds = new Set(); for (const node of nodes) { if (node.label !== 'File') continue; + // Skip duplicates + if (seenIds.has(node.id)) continue; + seenIds.add(node.id); + const content = extractContent(node, fileContents); rows.push([ escapeCSVField(node.id), @@ -170,14 +180,14 @@ const generateFolderCSV = (nodes: GraphNode[]): string => { /** * Generate CSV for code element nodes (Function, Class, Interface, Method, CodeElement) - * Headers: id,name,filePath,startLine,endLine,content + * Headers: id,name,filePath,startLine,endLine,isExported,content */ const generateCodeElementCSV = ( nodes: GraphNode[], label: NodeLabel, fileContents: Map ): string => { - const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'content']; + const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'isExported', 'content']; const rows: string[] = [headers.join(',')]; for (const node of nodes) { @@ -189,6 +199,7 @@ const generateCodeElementCSV = ( escapeCSVField(node.properties.filePath || ''), escapeCSVNumber(node.properties.startLine, -1), escapeCSVNumber(node.properties.endLine, -1), + node.properties.isExported ? 'true' : 'false', escapeCSVField(content), ].join(',')); } diff --git a/gitnexus/src/core/kuzu/kuzu-adapter.ts b/gitnexus/src/core/kuzu/kuzu-adapter.ts index c16e2edf3..9988b56ed 100644 --- a/gitnexus/src/core/kuzu/kuzu-adapter.ts +++ b/gitnexus/src/core/kuzu/kuzu-adapter.ts @@ -1,358 +1,517 @@ -/** - * KuzuDB Adapter - * - * Manages the KuzuDB WASM instance for client-side graph database operations. - * Uses the "Snapshot / Bulk Load" pattern with COPY FROM for performance. - * - * Multi-table schema: separate tables for File, Function, Class, etc. - */ - -import { KnowledgeGraph } from '../graph/types'; -import { - NODE_TABLES, +import fs from 'fs/promises'; +import path from 'path'; +import kuzu from 'kuzu'; +import { KnowledgeGraph } from '../graph/types.js'; +import { + NODE_TABLES, REL_TABLE_NAME, - SCHEMA_QUERIES, + SCHEMA_QUERIES, EMBEDDING_TABLE_NAME, NodeTableName, -} from './schema'; -import { generateAllCSVs } from './csv-generator'; +} from './schema.js'; +import { generateAllCSVs } from './csv-generator.js'; -// Holds the reference to the dynamically loaded module -let kuzu: any = null; -let db: any = null; -let conn: any = null; +let db: kuzu.Database | null = null; +let conn: kuzu.Connection | null = null; -/** - * Initialize KuzuDB WASM module and create in-memory database - */ -export const initKuzu = async () => { - if (conn) return { db, conn, kuzu }; +const normalizeCopyPath = (filePath: string): string => filePath.replace(/\\/g, '/'); +export const initKuzu = async (dbPath: string) => { + if (conn) return { db, conn }; + + // kuzu v0.11 stores the database as a single file (not a directory). + // If the path already exists, it must be a valid kuzu database file. + // Remove stale empty directories or files from older versions. try { - if (import.meta.env.DEV) console.log('🚀 Initializing KuzuDB...'); - - // 1. Dynamic Import (Fixes the "not a function" bundler issue) - const kuzuModule = await import('kuzu-wasm'); - - // 2. Handle Vite/Webpack "default" wrapping - kuzu = kuzuModule.default || kuzuModule; - - // 3. Initialize WASM - await kuzu.init(); - - // 4. Create Database with 512MB buffer pool - const BUFFER_POOL_SIZE = 512 * 1024 * 1024; // 512MB - db = new kuzu.Database(':memory:', BUFFER_POOL_SIZE); - conn = new kuzu.Connection(db); - - if (import.meta.env.DEV) console.log('✅ KuzuDB WASM Initialized'); - - // 5. Initialize Schema (all node tables, then rel tables, then embedding table) - for (const schemaQuery of SCHEMA_QUERIES) { - try { - await conn.query(schemaQuery); - } catch (e) { - // Schema might already exist, skip - if (import.meta.env.DEV) { - console.warn('Schema creation skipped (may already exist):', e); - } + const stat = await fs.stat(dbPath); + if (stat.isDirectory()) { + // Old-style directory database or empty leftover - remove it + const files = await fs.readdir(dbPath); + if (files.length === 0) { + await fs.rmdir(dbPath); + } else { + // Non-empty directory from older kuzu version - remove entire directory + await fs.rm(dbPath, { recursive: true, force: true }); } } - - if (import.meta.env.DEV) console.log('✅ KuzuDB Multi-Table Schema Created'); - - return { db, conn, kuzu }; - } catch (error) { - if (import.meta.env.DEV) console.error('❌ KuzuDB Initialization Failed:', error); - throw error; + // If it's a file, assume it's an existing kuzu database - kuzu will open it + } catch { + // Path doesn't exist, which is what kuzu wants for a new database } + + // Ensure parent directory exists + const parentDir = path.dirname(dbPath); + await fs.mkdir(parentDir, { recursive: true }); + + db = new kuzu.Database(dbPath); + conn = new kuzu.Connection(db); + + for (const schemaQuery of SCHEMA_QUERIES) { + try { + await conn.query(schemaQuery); + } catch (err) { + // Only ignore "already exists" errors - log everything else + const msg = err instanceof Error ? err.message : String(err); + if (!msg.includes('already exists')) { + console.warn(`⚠️ Schema creation warning: ${msg.slice(0, 120)}`); + } + } + } + + return { db, conn }; }; -/** - * Load a KnowledgeGraph into KuzuDB using COPY FROM (bulk load) - * Uses batched CSV writes and COPY statements for optimal performance - */ +export type KuzuProgressCallback = (message: string) => void; + export const loadGraphToKuzu = async ( - graph: KnowledgeGraph, - fileContents: Map + graph: KnowledgeGraph, + fileContents: Map, + storagePath: string, + onProgress?: KuzuProgressCallback ) => { - const { conn, kuzu } = await initKuzu(); - - try { - if (import.meta.env.DEV) console.log(`KuzuDB: Generating CSVs for ${graph.nodeCount} nodes...`); - - // 1. Generate all CSVs (per-table) - const csvData = generateAllCSVs(graph, fileContents); - - const fs = kuzu.FS; - - // 2. Write all node CSVs to virtual filesystem - const nodeFiles: Array<{ table: NodeTableName; path: string }> = []; - for (const [tableName, csv] of csvData.nodes.entries()) { - // Skip empty CSVs (only header row) - if (csv.split('\n').length <= 1) continue; - - const path = `/${tableName.toLowerCase()}.csv`; - try { await fs.unlink(path); } catch {} - await fs.writeFile(path, csv); - nodeFiles.push({ table: tableName, path }); + if (!conn) { + throw new Error('KuzuDB not initialized. Call initKuzu first.'); + } + + const log = onProgress || (() => {}); + + const csvData = generateAllCSVs(graph, fileContents); + const csvDir = path.join(storagePath, 'csv'); + await fs.mkdir(csvDir, { recursive: true }); + + log('Generating CSVs...'); + + const nodeFiles: Array<{ table: NodeTableName; path: string; rows: number }> = []; + for (const [tableName, csv] of csvData.nodes.entries()) { + const rowCount = csv.split('\n').length - 1; + if (rowCount <= 0) continue; + const filePath = path.join(csvDir, `${tableName.toLowerCase()}.csv`); + await fs.writeFile(filePath, csv, 'utf-8'); + nodeFiles.push({ table: tableName, path: filePath, rows: rowCount }); + } + + // Write relationship CSV to disk for bulk COPY + const relCsvPath = path.join(csvDir, 'relations.csv'); + const validTables = new Set(NODE_TABLES as readonly string[]); + const getNodeLabel = (nodeId: string): string => { + if (nodeId.startsWith('comm_')) return 'Community'; + if (nodeId.startsWith('proc_')) return 'Process'; + return nodeId.split(':')[0]; + }; + + const relLines = csvData.relCSV.split('\n'); + const relHeader = relLines[0]; + const validRelLines = [relHeader]; + let skippedRels = 0; + for (let i = 1; i < relLines.length; i++) { + const line = relLines[i]; + if (!line.trim()) continue; + const match = line.match(/"([^"]*)","([^"]*)"/); + if (!match) { skippedRels++; continue; } + const fromLabel = getNodeLabel(match[1]); + const toLabel = getNodeLabel(match[2]); + if (!validTables.has(fromLabel) || !validTables.has(toLabel)) { + skippedRels++; + continue; } - - // 3. Parse relation CSV and prepare for INSERT (COPY FROM doesn't work with multi-pair tables) - const relLines = csvData.relCSV.split('\n').slice(1).filter(line => line.trim()); - const relCount = relLines.length; - - if (import.meta.env.DEV) { - console.log(`KuzuDB: Wrote ${nodeFiles.length} node CSVs, ${relCount} relations to insert`); - } - - // 4. COPY all node tables (must complete before rels due to FK constraints) - for (const { table, path } of nodeFiles) { - const copyQuery = getCopyQuery(table, path); + validRelLines.push(line); + } + await fs.writeFile(relCsvPath, validRelLines.join('\n'), 'utf-8'); + + // Bulk COPY all node CSVs + const totalSteps = nodeFiles.length + 1; // +1 for relationships + let stepsDone = 0; + + for (const { table, path: filePath, rows } of nodeFiles) { + stepsDone++; + log(`Loading nodes ${stepsDone}/${totalSteps}: ${table} (${rows.toLocaleString()} rows)`); + + const normalizedPath = normalizeCopyPath(filePath); + const copyQuery = getCopyQuery(table, normalizedPath); + + try { await conn.query(copyQuery); - } - - // 5. INSERT relations one by one (COPY doesn't work with multi-pair REL tables) - // Parse CSV format: "from","to","type",confidence,"reason" - let insertedRels = 0; - let skippedRels = 0; - const skippedRelStats = new Map(); - for (const line of relLines) { + } catch (err) { try { - // Parse CSV - handle quoted fields and numeric confidence - // Parse CSV - handle quoted fields and numeric confidence - // Format: "from","to","type",confidence,"reason",step - // Note: step is unquoted numeric - const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/); - if (!match) continue; - - const [, fromId, toId, relType, confidenceStr, reason, stepStr] = match; - const confidence = parseFloat(confidenceStr) || 1.0; - const step = parseInt(stepStr) || 0; - - // Extract labels from node IDs - // Community nodes have IDs like "comm_14" (no colon) - // Other nodes have IDs like "Label:path:name" - const getNodeLabel = (nodeId: string): string => { - if (nodeId.startsWith('comm_')) { - return 'Community'; - } - if (nodeId.startsWith('proc_')) { - return 'Process'; - } - return nodeId.split(':')[0]; - }; - - // Reserved Cypher keywords need backtick escaping - const RESERVED_LABELS = ['Macro', 'Enum', 'Union', 'Const', 'Module', 'Struct']; - const escapeLabel = (label: string): string => { - return RESERVED_LABELS.includes(label) ? `\`${label}\`` : label; - }; - - const fromLabel = escapeLabel(getNodeLabel(fromId)); - const toLabel = escapeLabel(getNodeLabel(toId)); - - // INSERT with explicit node matching (including confidence and reason) - const insertQuery = ` - MATCH (a:${fromLabel} {id: '${fromId.replace(/'/g, "''")}'}), - (b:${toLabel} {id: '${toId.replace(/'/g, "''")}'}) - CREATE (a)-[:${REL_TABLE_NAME} {type: '${relType}', confidence: ${confidence}, reason: '${reason.replace(/'/g, "''")}', step: ${step}}]->(b) - `; - await conn.query(insertQuery); - insertedRels++; + const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)'); + await conn.query(retryQuery); + } catch (retryErr) { + const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr); + throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}`); + } + } + } + + // Bulk COPY relationships — split by FROM→TO label pair (KuzuDB requires it) + const insertedRels = validRelLines.length - 1; + const warnings: string[] = []; + if (insertedRels > 0) { + const relsByPair = new Map(); + for (let i = 1; i < validRelLines.length; i++) { + const line = validRelLines[i]; + const match = line.match(/"([^"]*)","([^"]*)"/); + if (!match) continue; + const fromLabel = getNodeLabel(match[1]); + const toLabel = getNodeLabel(match[2]); + const pairKey = `${fromLabel}|${toLabel}`; + let list = relsByPair.get(pairKey); + if (!list) { list = []; relsByPair.set(pairKey, list); } + list.push(line); + } + + log(`Loading edges: ${insertedRels.toLocaleString()} across ${relsByPair.size} types`); + + let pairIdx = 0; + let failedPairEdges = 0; + const failedPairLines: string[] = []; + + for (const [pairKey, lines] of relsByPair) { + pairIdx++; + const [fromLabel, toLabel] = pairKey.split('|'); + const pairCsvPath = path.join(csvDir, `rel_${fromLabel}_${toLabel}.csv`); + await fs.writeFile(pairCsvPath, relHeader + '\n' + lines.join('\n'), 'utf-8'); + const normalizedPath = normalizeCopyPath(pairCsvPath); + const copyQuery = `COPY ${REL_TABLE_NAME} FROM "${normalizedPath}" (from="${fromLabel}", to="${toLabel}", HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`; + + if (pairIdx % 5 === 0 || lines.length > 1000) { + log(`Loading edges: ${pairIdx}/${relsByPair.size} types (${fromLabel} -> ${toLabel})`); + } + + try { + await conn.query(copyQuery); } catch (err) { - // Skip failed insertions (nodes might not exist, or relation pair not allowed by schema) - skippedRels++; - const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)"/); - if (match) { - const [, fromId, toId, relType] = match; - const getNodeLabel = (nodeId: string): string => { - if (nodeId.startsWith('comm_')) return 'Community'; - if (nodeId.startsWith('proc_')) return 'Process'; - return nodeId.split(':')[0]; - }; - const fromLabel = getNodeLabel(fromId); - const toLabel = getNodeLabel(toId); - const key = `${relType}:${fromLabel}->` + toLabel; - skippedRelStats.set(key, (skippedRelStats.get(key) || 0) + 1); - - // Log each skipped relation - if (import.meta.env.DEV) { - console.warn(`⚠️ Skipped: ${key} | "${fromId}" → "${toId}" | ${err instanceof Error ? err.message : String(err)}`); - } + try { + const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)'); + await conn.query(retryQuery); + } catch (retryErr) { + const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr); + warnings.push(`${fromLabel}->${toLabel} (${lines.length} edges): ${retryMsg.slice(0, 80)}`); + failedPairEdges += lines.length; + failedPairLines.push(...lines); } } - } - - if (import.meta.env.DEV) { - console.log(`KuzuDB: Inserted ${insertedRels}/${relCount} relations`); - if (skippedRels > 0) { - const topSkipped = Array.from(skippedRelStats.entries()) - .sort((a, b) => b[1] - a[1]) - .slice(0, 10); - console.warn(`KuzuDB: Skipped ${skippedRels}/${relCount} relations (top by kind/pair):`, topSkipped); - } - } - - // 6. Verify results - let totalNodes = 0; - for (const tableName of NODE_TABLES) { - try { - const countRes = await conn.query(`MATCH (n:${tableName}) RETURN count(n) AS cnt`); - const countRow = await countRes.getNext(); - const count = countRow ? (countRow.cnt ?? countRow[0] ?? 0) : 0; - totalNodes += Number(count); - } catch { - // Table might be empty, skip - } - } - - if (import.meta.env.DEV) console.log(`✅ KuzuDB Bulk Load Complete. Total nodes: ${totalNodes}, edges: ${insertedRels}`); - - // 7. Cleanup CSV files - for (const { path } of nodeFiles) { - try { await fs.unlink(path); } catch {} + try { await fs.unlink(pairCsvPath); } catch {} } - return { success: true, count: totalNodes }; + if (failedPairLines.length > 0) { + log(`Inserting ${failedPairEdges} edges individually (missing schema pairs)`); + await fallbackRelationshipInserts([relHeader, ...failedPairLines], validTables, getNodeLabel); + } + } - } catch (error) { - if (import.meta.env.DEV) console.error('❌ KuzuDB Bulk Load Failed:', error); - return { success: false, count: 0 }; + // Cleanup all CSVs + try { await fs.unlink(relCsvPath); } catch {} + for (const { path: filePath } of nodeFiles) { + try { await fs.unlink(filePath); } catch {} + } + try { + const remaining = await fs.readdir(csvDir); + for (const f of remaining) { + try { await fs.unlink(path.join(csvDir, f)); } catch {} + } + } catch {} + try { await fs.rmdir(csvDir); } catch {} + + return { success: true, insertedRels, skippedRels, warnings }; +}; + +// KuzuDB default ESCAPE is '\' (backslash), but our CSV uses RFC 4180 escaping ("" for literal quotes). +// Source code content is full of backslashes which confuse the auto-detection. +// We MUST explicitly set ESCAPE='"' to use RFC 4180 escaping, and disable auto_detect to prevent +// KuzuDB from overriding our settings based on sample rows. +const COPY_CSV_OPTS = `(HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`; + +// Multi-language table names that were created with backticks in CODE_ELEMENT_BASE +// and must always be referenced with backticks in queries +const BACKTICK_TABLES = new Set([ + 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl', + 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', + 'Constructor', 'Template', 'Module', +]); + +const escapeTableName = (table: string): string => { + return BACKTICK_TABLES.has(table) ? `\`${table}\`` : table; +}; + +/** Fallback: insert relationships one-by-one if COPY fails */ +const fallbackRelationshipInserts = async ( + validRelLines: string[], + validTables: Set, + getNodeLabel: (id: string) => string +) => { + if (!conn) return; + const escapeLabel = (label: string): string => { + return BACKTICK_TABLES.has(label) ? `\`${label}\`` : label; + }; + + for (let i = 1; i < validRelLines.length; i++) { + const line = validRelLines[i]; + try { + const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/); + if (!match) continue; + const [, fromId, toId, relType, confidenceStr, reason, stepStr] = match; + const fromLabel = getNodeLabel(fromId); + const toLabel = getNodeLabel(toId); + if (!validTables.has(fromLabel) || !validTables.has(toLabel)) continue; + + const confidence = parseFloat(confidenceStr) || 1.0; + const step = parseInt(stepStr) || 0; + + await conn.query(` + MATCH (a:${escapeLabel(fromLabel)} {id: '${fromId.replace(/'/g, "''")}' }), + (b:${escapeLabel(toLabel)} {id: '${toId.replace(/'/g, "''")}' }) + CREATE (a)-[:${REL_TABLE_NAME} {type: '${relType}', confidence: ${confidence}, reason: '${reason.replace(/'/g, "''")}', step: ${step}}]->(b) + `); + } catch { + // skip + } } }; -/** - * Get the COPY query for a node table with correct column mapping - */ -const getCopyQuery = (table: NodeTableName, path: string): string => { - // File and Folder have different columns than code elements +const getCopyQuery = (table: NodeTableName, filePath: string): string => { + const t = escapeTableName(table); if (table === 'File') { - return `COPY File(id, name, filePath, content) FROM "${path}" (HEADER=true, PARALLEL=false)`; + return `COPY ${t}(id, name, filePath, content) FROM "${filePath}" ${COPY_CSV_OPTS}`; } if (table === 'Folder') { - return `COPY Folder(id, name, filePath) FROM "${path}" (HEADER=true, PARALLEL=false)`; + return `COPY ${t}(id, name, filePath) FROM "${filePath}" ${COPY_CSV_OPTS}`; } if (table === 'Community') { - return `COPY Community(id, label, heuristicLabel, keywords, description, enrichedBy, cohesion, symbolCount) FROM "${path}" (HEADER=true, PARALLEL=false)`; + return `COPY ${t}(id, label, heuristicLabel, keywords, description, enrichedBy, cohesion, symbolCount) FROM "${filePath}" ${COPY_CSV_OPTS}`; } if (table === 'Process') { - return `COPY Process(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${path}" (HEADER=true, PARALLEL=false)`; + return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`; } - // All code element tables: Function, Class, Interface, Method, CodeElement - return `COPY ${table}(id, name, filePath, startLine, endLine, content) FROM "${path}" (HEADER=true, PARALLEL=false)`; + // Code element tables (Function, Class, Interface, Method, CodeElement, and multi-language) + return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content) FROM "${filePath}" ${COPY_CSV_OPTS}`; }; /** - * Execute a Cypher query against the database - * Returns results as named objects (not tuples) for better usability + * Insert a single node to KuzuDB + * @param label - Node type (File, Function, Class, etc.) + * @param properties - Node properties + * @param dbPath - Path to KuzuDB database (optional if already initialized) */ -export const executeQuery = async (cypher: string): Promise => { - if (!conn) { - await initKuzu(); +export const insertNodeToKuzu = async ( + label: string, + properties: Record, + dbPath?: string +): Promise => { + // Use provided dbPath or fall back to module-level db + const targetDbPath = dbPath || (db ? undefined : null); + if (!targetDbPath && !db) { + throw new Error('KuzuDB not initialized. Provide dbPath or call initKuzu first.'); } + + try { + const escapeValue = (v: any): string => { + if (v === null || v === undefined) return 'NULL'; + if (typeof v === 'number') return String(v); + // Escape backslashes first (for Windows paths), then single quotes + return `'${String(v).replace(/\\/g, '\\\\').replace(/'/g, "''")}'`; + }; + + // Build INSERT query based on node type + let query: string; + + if (label === 'File') { + query = `CREATE (n:File {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, content: ${escapeValue(properties.content || '')}})`; + } else if (label === 'Folder') { + query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`; + } else { + // Function, Class, Method, Interface, etc. - standard code element schema + query = `CREATE (n:${label} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${escapeValue(properties.content || '')}})`; + } + + // Use per-query connection if dbPath provided (avoids lock conflicts) + if (targetDbPath) { + const tempDb = new kuzu.Database(targetDbPath); + const tempConn = new kuzu.Connection(tempDb); + try { + await tempConn.query(query); + return true; + } finally { + try { await tempConn.close(); } catch {} + try { await tempDb.close(); } catch {} + } + } else if (conn) { + // Use existing persistent connection (when called from analyze) + await conn.query(query); + return true; + } + + return false; + } catch (e: any) { + // Node may already exist or other error + console.error(`Failed to insert ${label} node:`, e.message); + return false; + } +}; + +/** + * Batch insert multiple nodes to KuzuDB using a single connection + * @param nodes - Array of {label, properties} to insert + * @param dbPath - Path to KuzuDB database + * @returns Object with success count and error count + */ +export const batchInsertNodesToKuzu = async ( + nodes: Array<{ label: string; properties: Record }>, + dbPath: string +): Promise<{ inserted: number; failed: number }> => { + if (nodes.length === 0) return { inserted: 0, failed: 0 }; + + const escapeValue = (v: any): string => { + if (v === null || v === undefined) return 'NULL'; + if (typeof v === 'number') return String(v); + // Escape backslashes first (for Windows paths), then single quotes + return `'${String(v).replace(/\\/g, '\\\\').replace(/'/g, "''")}'`; + }; + + // Open a single connection for all inserts + const tempDb = new kuzu.Database(dbPath); + const tempConn = new kuzu.Connection(tempDb); + + let inserted = 0; + let failed = 0; try { - const result = await conn.query(cypher); - - // Extract column names from RETURN clause - const returnMatch = cypher.match(/RETURN\s+(.+?)(?:\s+ORDER|\s+LIMIT|\s+SKIP|\s*$)/is); - let columnNames: string[] = []; - if (returnMatch) { - // Parse RETURN clause to get column names/aliases - // Handles: "a.name, b.filePath AS path, count(x) AS cnt" - const returnClause = returnMatch[1]; - columnNames = returnClause.split(',').map(col => { - col = col.trim(); - // Check for AS alias - const asMatch = col.match(/\s+AS\s+(\w+)\s*$/i); - if (asMatch) return asMatch[1]; - // Check for property access like n.name - const propMatch = col.match(/\.(\w+)\s*$/); - if (propMatch) return propMatch[1]; - // Check for function call like count(x) - const funcMatch = col.match(/^(\w+)\s*\(/); - if (funcMatch) return funcMatch[1]; - // Just use as-is if simple identifier - return col.replace(/[^a-zA-Z0-9_]/g, '_'); - }); - } - - // Collect all rows - const rows: any[] = []; - while (await result.hasNext()) { - const row = await result.getNext(); - - // Convert tuple to named object if we have column names and row is array - if (Array.isArray(row) && columnNames.length === row.length) { - const namedRow: Record = {}; - for (let i = 0; i < row.length; i++) { - namedRow[columnNames[i]] = row[i]; - } - rows.push(namedRow); - } else { - // Already an object or column count doesn't match - rows.push(row); - } - } - - return rows; - } catch (error) { - if (import.meta.env.DEV) console.error('Query execution failed:', error); - throw error; - } -}; - -/** - * Get database statistics - */ -export const getKuzuStats = async (): Promise<{ nodes: number; edges: number }> => { - if (!conn) { - return { nodes: 0, edges: 0 }; - } - - try { - // Count nodes across all tables - let totalNodes = 0; - for (const tableName of NODE_TABLES) { + for (const { label, properties } of nodes) { try { - const nodeResult = await conn.query(`MATCH (n:${tableName}) RETURN count(n) AS cnt`); - const nodeRow = await nodeResult.getNext(); - totalNodes += Number(nodeRow?.cnt ?? nodeRow?.[0] ?? 0); - } catch { - // Table might not exist or be empty + let query: string; + + // Use MERGE instead of CREATE for upsert behavior (handles duplicates gracefully) + if (label === 'File') { + query = `MERGE (n:File {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.content = ${escapeValue(properties.content || '')}`; + } else if (label === 'Folder') { + query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`; + } else { + query = `MERGE (n:${label} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${escapeValue(properties.content || '')}`; + } + + await tempConn.query(query); + inserted++; + } catch (e: any) { + // Don't console.error here - it corrupts MCP JSON-RPC on stderr + failed++; } } - - // Count edges from single relation table - let totalEdges = 0; + } finally { + try { await tempConn.close(); } catch {} + try { await tempDb.close(); } catch {} + } + + return { inserted, failed }; +}; + +export const executeQuery = async (cypher: string): Promise => { + if (!conn) { + throw new Error('KuzuDB not initialized. Call initKuzu first.'); + } + + const queryResult = await conn.query(cypher); + // kuzu v0.11 uses getAll() instead of hasNext()/getNext() + // Query returns QueryResult for single queries, QueryResult[] for multi-statement + const result = Array.isArray(queryResult) ? queryResult[0] : queryResult; + const rows = await result.getAll(); + return rows; +}; + +export const executeWithReusedStatement = async ( + cypher: string, + paramsList: Array> +): Promise => { + if (!conn) { + throw new Error('KuzuDB not initialized. Call initKuzu first.'); + } + if (paramsList.length === 0) return; + + const SUB_BATCH_SIZE = 4; + for (let i = 0; i < paramsList.length; i += SUB_BATCH_SIZE) { + const subBatch = paramsList.slice(i, i + SUB_BATCH_SIZE); + const stmt = await conn.prepare(cypher); + if (!stmt.isSuccess()) { + const errMsg = await stmt.getErrorMessage(); + throw new Error(`Prepare failed: ${errMsg}`); + } try { - const edgeResult = await conn.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`); - const edgeRow = await edgeResult.getNext(); - totalEdges = Number(edgeRow?.cnt ?? edgeRow?.[0] ?? 0); - } catch { - // Table might not exist or be empty + for (const params of subBatch) { + await conn.execute(stmt, params); + } + } catch (e) { + // Log the error and continue with next batch + console.warn('Batch execution error:', e); } - - return { nodes: totalNodes, edges: totalEdges }; - } catch (error) { - if (import.meta.env.DEV) { - console.warn('Failed to get Kuzu stats:', error); - } - return { nodes: 0, edges: 0 }; + // Note: kuzu 0.8.2 PreparedStatement doesn't require explicit close() } }; -/** - * Check if KuzuDB is initialized and has data - */ -export const isKuzuReady = (): boolean => { - return conn !== null && db !== null; +export const getKuzuStats = async (): Promise<{ nodes: number; edges: number }> => { + if (!conn) return { nodes: 0, edges: 0 }; + + let totalNodes = 0; + for (const tableName of NODE_TABLES) { + try { + const queryResult = await conn.query(`MATCH (n:${tableName}) RETURN count(n) AS cnt`); + const nodeResult = Array.isArray(queryResult) ? queryResult[0] : queryResult; + const nodeRows = await nodeResult.getAll(); + if (nodeRows.length > 0) { + totalNodes += Number(nodeRows[0]?.cnt ?? nodeRows[0]?.[0] ?? 0); + } + } catch { + // ignore + } + } + + let totalEdges = 0; + try { + const queryResult = await conn.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`); + const edgeResult = Array.isArray(queryResult) ? queryResult[0] : queryResult; + const edgeRows = await edgeResult.getAll(); + if (edgeRows.length > 0) { + totalEdges = Number(edgeRows[0]?.cnt ?? edgeRows[0]?.[0] ?? 0); + } + } catch { + // ignore + } + + return { nodes: totalNodes, edges: totalEdges }; }; /** - * Close the database connection (cleanup) + * Load cached embeddings from KuzuDB before a rebuild. + * Returns all embedding vectors so they can be re-inserted after the graph is reloaded, + * avoiding expensive re-embedding of unchanged nodes. */ +export const loadCachedEmbeddings = async (): Promise<{ + embeddingNodeIds: Set; + embeddings: Array<{ nodeId: string; embedding: number[] }>; +}> => { + if (!conn) { + return { embeddingNodeIds: new Set(), embeddings: [] }; + } + + const embeddingNodeIds = new Set(); + const embeddings: Array<{ nodeId: string; embedding: number[] }> = []; + try { + const rows = await conn.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN e.nodeId AS nodeId, e.embedding AS embedding`); + const result = Array.isArray(rows) ? rows[0] : rows; + for (const row of await result.getAll()) { + const nodeId = String(row.nodeId ?? row[0] ?? ''); + if (!nodeId) continue; + embeddingNodeIds.add(nodeId); + const embedding = row.embedding ?? row[1]; + if (embedding) { + embeddings.push({ + nodeId, + embedding: Array.isArray(embedding) ? embedding.map(Number) : Array.from(embedding as any).map(Number), + }); + } + } + } catch { /* embedding table may not exist */ } + + return { embeddingNodeIds, embeddings }; +}; + export const closeKuzu = async (): Promise => { if (conn) { try { @@ -366,155 +525,203 @@ export const closeKuzu = async (): Promise => { } catch {} db = null; } - kuzu = null; }; +export const isKuzuReady = (): boolean => conn !== null && db !== null; + /** - * Execute a prepared statement with parameters - * @param cypher - Cypher query with $param placeholders - * @param params - Object mapping param names to values - * @returns Query results + * Delete all nodes (and their relationships) for a specific file from KuzuDB + * @param filePath - The file path to delete nodes for + * @param dbPath - Optional path to KuzuDB for per-query connection + * @returns Object with counts of deleted nodes */ -export const executePrepared = async ( - cypher: string, - params: Record -): Promise => { - if (!conn) { - await initKuzu(); +export const deleteNodesForFile = async (filePath: string, dbPath?: string): Promise<{ deletedNodes: number }> => { + const usePerQuery = !!dbPath; + + // Set up connection (either use existing or create per-query) + let tempDb: kuzu.Database | null = null; + let tempConn: kuzu.Connection | null = null; + let targetConn: kuzu.Connection | null = conn; + + if (usePerQuery) { + tempDb = new kuzu.Database(dbPath); + tempConn = new kuzu.Connection(tempDb); + targetConn = tempConn; + } else if (!conn) { + throw new Error('KuzuDB not initialized. Provide dbPath or call initKuzu first.'); } try { - const stmt = await conn.prepare(cypher); - if (!stmt.isSuccess()) { - const errMsg = await stmt.getErrorMessage(); - throw new Error(`Prepare failed: ${errMsg}`); + let deletedNodes = 0; + const escapedPath = filePath.replace(/'/g, "''"); + + // Delete nodes from each table that has filePath + // DETACH DELETE removes the node and all its relationships + for (const tableName of NODE_TABLES) { + // Skip tables that don't have filePath (Community, Process) + if (tableName === 'Community' || tableName === 'Process') continue; + + try { + // First count how many we'll delete + const countResult = await targetConn!.query( + `MATCH (n:${tableName}) WHERE n.filePath = '${escapedPath}' RETURN count(n) AS cnt` + ); + const result = Array.isArray(countResult) ? countResult[0] : countResult; + const rows = await result.getAll(); + const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0); + + if (count > 0) { + // Delete nodes (and implicitly their relationships via DETACH) + await targetConn!.query( + `MATCH (n:${tableName}) WHERE n.filePath = '${escapedPath}' DETACH DELETE n` + ); + deletedNodes += count; + } + } catch (e) { + // Some tables may not support this query, skip + } } - const result = await conn.execute(stmt, params); - - const rows: any[] = []; - while (await result.hasNext()) { - const row = await result.getNext(); - rows.push(row); + // Also delete any embeddings for nodes in this file + try { + await targetConn!.query( + `MATCH (e:${EMBEDDING_TABLE_NAME}) WHERE e.nodeId STARTS WITH '${escapedPath}' DELETE e` + ); + } catch { + // Embedding table may not exist or nodeId format may differ } - await stmt.close(); - return rows; - } catch (error) { - if (import.meta.env.DEV) console.error('Prepared query failed:', error); - throw error; + return { deletedNodes }; + } finally { + // Close per-query connection if used + if (tempConn) { + try { await tempConn.close(); } catch {} + } + if (tempDb) { + try { await tempDb.close(); } catch {} + } + } +}; + +export const getEmbeddingTableName = (): string => EMBEDDING_TABLE_NAME; + +// ============================================================================ +// Full-Text Search (FTS) Functions +// ============================================================================ + +/** + * Load the FTS extension (required before using FTS functions) + */ +export const loadFTSExtension = async (): Promise => { + if (!conn) { + throw new Error('KuzuDB not initialized. Call initKuzu first.'); + } + try { + await conn.query('INSTALL fts'); + await conn.query('LOAD EXTENSION fts'); + } catch { + // Extension may already be loaded } }; /** - * Execute a prepared statement with multiple parameter sets in small sub-batches + * Create a full-text search index on a table + * @param tableName - The node table name (e.g., 'File', 'CodeSymbol') + * @param indexName - Name for the FTS index + * @param properties - List of properties to index (e.g., ['name', 'code']) + * @param stemmer - Stemming algorithm (default: 'porter') */ -export const executeWithReusedStatement = async ( - cypher: string, - paramsList: Array> +export const createFTSIndex = async ( + tableName: string, + indexName: string, + properties: string[], + stemmer: string = 'porter' ): Promise => { if (!conn) { - await initKuzu(); + throw new Error('KuzuDB not initialized. Call initKuzu first.'); } - if (paramsList.length === 0) return; + await loadFTSExtension(); - const SUB_BATCH_SIZE = 4; + const propList = properties.map(p => `'${p}'`).join(', '); + const query = `CALL CREATE_FTS_INDEX('${tableName}', '${indexName}', [${propList}], stemmer := '${stemmer}')`; - for (let i = 0; i < paramsList.length; i += SUB_BATCH_SIZE) { - const subBatch = paramsList.slice(i, i + SUB_BATCH_SIZE); - - const stmt = await conn.prepare(cypher); - if (!stmt.isSuccess()) { - const errMsg = await stmt.getErrorMessage(); - throw new Error(`Prepare failed: ${errMsg}`); - } - - try { - for (const params of subBatch) { - await conn.execute(stmt, params); - } - } finally { - await stmt.close(); - } - - if (i + SUB_BATCH_SIZE < paramsList.length) { - await new Promise(r => setTimeout(r, 0)); + try { + await conn.query(query); + } catch (e: any) { + // Index may already exist + if (!e.message?.includes('already exists')) { + throw e; } } }; /** - * Test if array parameters work with prepared statements + * Query a full-text search index + * @param tableName - The node table name + * @param indexName - FTS index name + * @param query - Search query string + * @param limit - Maximum results + * @param conjunctive - If true, all terms must match (AND); if false, any term matches (OR) + * @returns Array of { node properties, score } */ -export const testArrayParams = async (): Promise<{ success: boolean; error?: string }> => { +export const queryFTS = async ( + tableName: string, + indexName: string, + query: string, + limit: number = 20, + conjunctive: boolean = false +): Promise> => { if (!conn) { - await initKuzu(); + throw new Error('KuzuDB not initialized. Call initKuzu first.'); + } + + // Escape single quotes in query + const escapedQuery = query.replace(/'/g, "''"); + + const cypher = ` + CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', '${escapedQuery}', conjunctive := ${conjunctive}) + RETURN node, score + ORDER BY score DESC + LIMIT ${limit} + `; + + try { + const queryResult = await conn.query(cypher); + const result = Array.isArray(queryResult) ? queryResult[0] : queryResult; + const rows = await result.getAll(); + + return rows.map((row: any) => { + const node = row.node || row[0] || {}; + const score = row.score ?? row[1] ?? 0; + return { + nodeId: node.nodeId || node.id || '', + name: node.name || '', + filePath: node.filePath || '', + score: typeof score === 'number' ? score : parseFloat(score) || 0, + ...node, + }; + }); + } catch (e: any) { + // Return empty if index doesn't exist yet + if (e.message?.includes('does not exist')) { + return []; + } + throw e; + } +}; + +/** + * Drop an FTS index + */ +export const dropFTSIndex = async (tableName: string, indexName: string): Promise => { + if (!conn) { + throw new Error('KuzuDB not initialized. Call initKuzu first.'); } try { - const testEmbedding = new Array(384).fill(0).map((_, i) => i / 384); - - // Get any node ID to test with (try File first, then others) - let testNodeId: string | null = null; - for (const tableName of NODE_TABLES) { - try { - const nodeResult = await conn.query(`MATCH (n:${tableName}) RETURN n.id AS id LIMIT 1`); - const nodeRow = await nodeResult.getNext(); - if (nodeRow) { - testNodeId = nodeRow.id ?? nodeRow[0]; - break; - } - } catch {} - } - - if (!testNodeId) { - return { success: false, error: 'No nodes found to test with' }; - } - - if (import.meta.env.DEV) { - console.log('🧪 Testing array params with node:', testNodeId); - } - - // First create an embedding entry - const createQuery = `CREATE (e:${EMBEDDING_TABLE_NAME} {nodeId: $nodeId, embedding: $embedding})`; - const stmt = await conn.prepare(createQuery); - - if (!stmt.isSuccess()) { - const errMsg = await stmt.getErrorMessage(); - return { success: false, error: `Prepare failed: ${errMsg}` }; - } - - await conn.execute(stmt, { - nodeId: testNodeId, - embedding: testEmbedding, - }); - - await stmt.close(); - - // Verify it was stored - const verifyResult = await conn.query( - `MATCH (e:${EMBEDDING_TABLE_NAME} {nodeId: '${testNodeId}'}) RETURN e.embedding AS emb` - ); - const verifyRow = await verifyResult.getNext(); - const storedEmb = verifyRow?.emb ?? verifyRow?.[0]; - - if (storedEmb && Array.isArray(storedEmb) && storedEmb.length === 384) { - if (import.meta.env.DEV) { - console.log('✅ Array params WORK! Stored embedding length:', storedEmb.length); - } - return { success: true }; - } else { - return { - success: false, - error: `Embedding not stored correctly. Got: ${typeof storedEmb}, length: ${storedEmb?.length}` - }; - } - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - if (import.meta.env.DEV) { - console.error('❌ Array params test failed:', errorMsg); - } - return { success: false, error: errorMsg }; + await conn.query(`CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`); + } catch { + // Index may not exist } }; diff --git a/gitnexus/src/core/kuzu/schema.ts b/gitnexus/src/core/kuzu/schema.ts index 6c20b4bd5..c0f3b394a 100644 --- a/gitnexus/src/core/kuzu/schema.ts +++ b/gitnexus/src/core/kuzu/schema.ts @@ -62,6 +62,7 @@ CREATE NODE TABLE Function ( filePath STRING, startLine INT64, endLine INT64, + isExported BOOLEAN, content STRING, PRIMARY KEY (id) )`; @@ -73,6 +74,7 @@ CREATE NODE TABLE Class ( filePath STRING, startLine INT64, endLine INT64, + isExported BOOLEAN, content STRING, PRIMARY KEY (id) )`; @@ -84,6 +86,7 @@ CREATE NODE TABLE Interface ( filePath STRING, startLine INT64, endLine INT64, + isExported BOOLEAN, content STRING, PRIMARY KEY (id) )`; @@ -95,6 +98,7 @@ CREATE NODE TABLE Method ( filePath STRING, startLine INT64, endLine INT64, + isExported BOOLEAN, content STRING, PRIMARY KEY (id) )`; @@ -106,6 +110,7 @@ CREATE NODE TABLE CodeElement ( filePath STRING, startLine INT64, endLine INT64, + isExported BOOLEAN, content STRING, PRIMARY KEY (id) )`; @@ -196,20 +201,20 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM File TO \`Struct\`, FROM File TO \`Enum\`, FROM File TO \`Macro\`, - FROM File TO Typedef, + FROM File TO \`Typedef\`, FROM File TO \`Union\`, - FROM File TO Namespace, - FROM File TO Trait, - FROM File TO Impl, - FROM File TO TypeAlias, + FROM File TO \`Namespace\`, + FROM File TO \`Trait\`, + FROM File TO \`Impl\`, + FROM File TO \`TypeAlias\`, FROM File TO \`Const\`, - FROM File TO Static, - FROM File TO Property, - FROM File TO Record, - FROM File TO Delegate, - FROM File TO Annotation, - FROM File TO Constructor, - FROM File TO Template, + FROM File TO \`Static\`, + FROM File TO \`Property\`, + FROM File TO \`Record\`, + FROM File TO \`Delegate\`, + FROM File TO \`Annotation\`, + FROM File TO \`Constructor\`, + FROM File TO \`Template\`, FROM File TO \`Module\`, FROM Folder TO Folder, FROM Folder TO File, @@ -219,48 +224,49 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM Function TO Community, FROM Function TO \`Macro\`, FROM Function TO \`Struct\`, - FROM Function TO Template, + FROM Function TO \`Template\`, FROM Function TO \`Enum\`, - FROM Function TO Namespace, - FROM Function TO TypeAlias, + FROM Function TO \`Namespace\`, + FROM Function TO \`TypeAlias\`, FROM Function TO \`Module\`, - FROM Function TO Impl, + FROM Function TO \`Impl\`, FROM Function TO Interface, - FROM Function TO Constructor, + FROM Function TO \`Constructor\`, FROM Class TO Method, FROM Class TO Function, FROM Class TO Class, FROM Class TO Interface, FROM Class TO Community, - FROM Class TO Template, - FROM Class TO TypeAlias, + FROM Class TO \`Template\`, + FROM Class TO \`TypeAlias\`, FROM Class TO \`Struct\`, FROM Class TO \`Enum\`, - FROM Class TO Constructor, + FROM Class TO \`Annotation\`, + FROM Class TO \`Constructor\`, FROM Method TO Function, FROM Method TO Method, FROM Method TO Class, FROM Method TO Community, - FROM Method TO Template, + FROM Method TO \`Template\`, FROM Method TO \`Struct\`, - FROM Method TO TypeAlias, + FROM Method TO \`TypeAlias\`, FROM Method TO \`Enum\`, FROM Method TO \`Macro\`, - FROM Method TO Namespace, + FROM Method TO \`Namespace\`, FROM Method TO \`Module\`, - FROM Method TO Impl, + FROM Method TO \`Impl\`, FROM Method TO Interface, - FROM Method TO Constructor, - FROM Template TO Template, - FROM Template TO Function, - FROM Template TO Method, - FROM Template TO Class, - FROM Template TO \`Struct\`, - FROM Template TO TypeAlias, - FROM Template TO \`Enum\`, - FROM Template TO \`Macro\`, - FROM Template TO Interface, - FROM Template TO Constructor, + FROM Method TO \`Constructor\`, + FROM \`Template\` TO \`Template\`, + FROM \`Template\` TO Function, + FROM \`Template\` TO Method, + FROM \`Template\` TO Class, + FROM \`Template\` TO \`Struct\`, + FROM \`Template\` TO \`TypeAlias\`, + FROM \`Template\` TO \`Enum\`, + FROM \`Template\` TO \`Macro\`, + FROM \`Template\` TO Interface, + FROM \`Template\` TO \`Constructor\`, FROM \`Module\` TO \`Module\`, FROM CodeElement TO Community, FROM Interface TO Community, @@ -268,11 +274,11 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM Interface TO Method, FROM Interface TO Class, FROM Interface TO Interface, - FROM Interface TO TypeAlias, + FROM Interface TO \`TypeAlias\`, FROM Interface TO \`Struct\`, - FROM Interface TO Constructor, + FROM Interface TO \`Constructor\`, FROM \`Struct\` TO Community, - FROM \`Struct\` TO Trait, + FROM \`Struct\` TO \`Trait\`, FROM \`Struct\` TO Function, FROM \`Struct\` TO Method, FROM \`Enum\` TO Community, @@ -281,56 +287,58 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM \`Macro\` TO Method, FROM \`Module\` TO Function, FROM \`Module\` TO Method, - FROM Typedef TO Community, + FROM \`Typedef\` TO Community, FROM \`Union\` TO Community, - FROM Namespace TO Community, - FROM Trait TO Community, - FROM Impl TO Community, - FROM Impl TO Trait, - FROM TypeAlias TO Community, + FROM \`Namespace\` TO Community, + FROM \`Trait\` TO Community, + FROM \`Impl\` TO Community, + FROM \`Impl\` TO \`Trait\`, + FROM \`TypeAlias\` TO Community, FROM \`Const\` TO Community, - FROM Static TO Community, - FROM Property TO Community, - FROM Record TO Community, - FROM Delegate TO Community, - FROM Annotation TO Community, - FROM Constructor TO Community, - FROM Constructor TO Interface, - FROM Constructor TO Class, - FROM Constructor TO Method, - FROM Constructor TO Function, - FROM Constructor TO Constructor, - FROM Constructor TO \`Struct\`, - FROM Constructor TO \`Macro\`, - FROM Constructor TO Template, - FROM Constructor TO TypeAlias, - FROM Constructor TO \`Enum\`, - FROM Constructor TO Impl, - FROM Constructor TO Namespace, - FROM Template TO Community, + FROM \`Static\` TO Community, + FROM \`Property\` TO Community, + FROM \`Record\` TO Community, + FROM \`Delegate\` TO Community, + FROM \`Annotation\` TO Community, + FROM \`Constructor\` TO Community, + FROM \`Constructor\` TO Interface, + FROM \`Constructor\` TO Class, + FROM \`Constructor\` TO Method, + FROM \`Constructor\` TO Function, + FROM \`Constructor\` TO \`Constructor\`, + FROM \`Constructor\` TO \`Struct\`, + FROM \`Constructor\` TO \`Macro\`, + FROM \`Constructor\` TO \`Template\`, + FROM \`Constructor\` TO \`TypeAlias\`, + FROM \`Constructor\` TO \`Enum\`, + FROM \`Constructor\` TO \`Annotation\`, + FROM \`Constructor\` TO \`Impl\`, + FROM \`Constructor\` TO \`Namespace\`, + FROM \`Constructor\` TO \`Module\`, + FROM \`Template\` TO Community, FROM \`Module\` TO Community, FROM Function TO Process, FROM Method TO Process, FROM Class TO Process, FROM Interface TO Process, FROM \`Struct\` TO Process, - FROM Constructor TO Process, + FROM \`Constructor\` TO Process, FROM \`Module\` TO Process, FROM \`Macro\` TO Process, - FROM Impl TO Process, - FROM Typedef TO Process, - FROM TypeAlias TO Process, + FROM \`Impl\` TO Process, + FROM \`Typedef\` TO Process, + FROM \`TypeAlias\` TO Process, FROM \`Enum\` TO Process, FROM \`Union\` TO Process, - FROM Namespace TO Process, - FROM Trait TO Process, + FROM \`Namespace\` TO Process, + FROM \`Trait\` TO Process, FROM \`Const\` TO Process, - FROM Static TO Process, - FROM Property TO Process, - FROM Record TO Process, - FROM Delegate TO Process, - FROM Annotation TO Process, - FROM Template TO Process, + FROM \`Static\` TO Process, + FROM \`Property\` TO Process, + FROM \`Record\` TO Process, + FROM \`Delegate\` TO Process, + FROM \`Annotation\` TO Process, + FROM \`Template\` TO Process, FROM CodeElement TO Process, type STRING, confidence DOUBLE, diff --git a/gitnexus/src/core/mcp/mcp-client.ts b/gitnexus/src/core/mcp/mcp-client.ts deleted file mode 100644 index a546331f9..000000000 --- a/gitnexus/src/core/mcp/mcp-client.ts +++ /dev/null @@ -1,341 +0,0 @@ -/** - * MCP Browser Client - * - * WebSocket client that connects to the gitnexus-mcp daemon. - * - Sends codebase context (stats, hotspots, folder tree) on connect - * - Receives tool calls from external AI agents and executes them - * - Emits activity events for real-time monitoring - */ - -/** - * Agent color mapping for multi-agent support - */ -export const AGENT_COLORS: Record = { - 'cursor': '#a855f7', // Purple - 'claude': '#3b82f6', // Blue - 'claude-code': '#3b82f6', // Blue - 'windsurf': '#22c55e', // Green - 'codeium': '#22c55e', // Green (Windsurf) - 'unknown': '#6b7280', // Gray -}; - -export function getAgentColor(agentName: string): string { - const normalized = agentName.toLowerCase().trim(); - for (const [key, color] of Object.entries(AGENT_COLORS)) { - if (normalized.includes(key)) { - return color; - } - } - return AGENT_COLORS.unknown; -} - -export interface ConnectedAgent { - name: string; - color: string; -} - -export interface MCPMessage { - id: string; - type?: 'context' | 'tool_call' | 'tool_result' | 'agent_info'; - method?: string; - params?: Record; - result?: any; - error?: { message: string }; - agentName?: string; -} - -/** - * Codebase context to send to daemon - */ -export interface CodebaseContext { - projectName: string; - stats: { - fileCount: number; - functionCount: number; - classCount: number; - interfaceCount: number; - methodCount: number; - }; - hotspots: Array<{ - name: string; - type: string; - filePath: string; - connections: number; - }>; - folderTree: string; -} - -/** - * Activity event for real-time monitoring - */ -export interface ActivityEvent { - id: string; - tool: string; - params: Record; - status: 'running' | 'complete' | 'error'; - result?: any; - error?: string; - timestamp: number; - duration?: number; - agentName?: string; - agentColor?: string; -} - -type ToolHandler = (params: Record) => Promise; -type ActivityListener = (event: ActivityEvent) => void; - -export class MCPBrowserClient { - private ws: WebSocket | null = null; - private handlers: Map = new Map(); - private connectionListeners: Set<(connected: boolean) => void> = new Set(); - private activityListeners: Set = new Set(); - private activityLog: ActivityEvent[] = []; - private pendingContext: CodebaseContext | null = null; - private _connectedAgent: ConnectedAgent | null = null; - - constructor(private port = 54319) {} - - /** - * Connect to the MCP daemon - */ - async connect(): Promise { - return new Promise((resolve, reject) => { - try { - this.ws = new WebSocket(`ws://localhost:${this.port}`); - - this.ws.onopen = () => { - console.log('[MCP] Connected to daemon'); - this.notifyConnectionListeners(true); - - // Send pending context if available - if (this.pendingContext) { - this.sendContext(this.pendingContext); - } - - resolve(); - }; - - this.ws.onerror = () => { - this.notifyConnectionListeners(false); - reject(new Error('Failed to connect to MCP bridge')); - }; - - this.ws.onmessage = (event) => { - try { - const msg: MCPMessage = JSON.parse(event.data); - this.handleMessage(msg); - } catch (error) { - console.error('[MCP] Failed to parse message:', error); - } - }; - - this.ws.onclose = () => { - this.ws = null; - this.notifyConnectionListeners(false); - }; - } catch (error) { - reject(error); - } - }); - } - - /** - * Send codebase context to daemon - * Call this whenever context changes (new repo loaded, etc.) - */ - sendContext(context: CodebaseContext) { - this.pendingContext = context; - - if (this.ws?.readyState === WebSocket.OPEN) { - const msg = { - id: `ctx_${Date.now()}`, - type: 'context', - params: context, - }; - this.ws.send(JSON.stringify(msg)); - console.log('[MCP] Sent context:', context.projectName); - } - } - - /** - * Handle incoming messages from daemon - */ - private async handleMessage(msg: MCPMessage) { - // Handle agent info updates - if (msg.type === 'agent_info' && msg.agentName) { - this._connectedAgent = { - name: msg.agentName, - color: getAgentColor(msg.agentName), - }; - console.log('[MCP] Agent connected:', this._connectedAgent); - return; - } - - // This is a tool call request from an external agent - if (msg.method && msg.id) { - const handler = this.handlers.get(msg.method); - const startTime = Date.now(); - - // Get agent info from message or use connected agent - const agentName = msg.agentName || this._connectedAgent?.name || 'Unknown'; - const agentColor = getAgentColor(agentName); - - // Create activity event with agent info - const activityEvent: ActivityEvent = { - id: msg.id, - tool: msg.method, - params: msg.params || {}, - status: 'running', - timestamp: startTime, - agentName, - agentColor, - }; - this.logActivity(activityEvent); - - if (handler) { - try { - const result = await handler(msg.params || {}); - this.send({ id: msg.id, result }); - - // Update activity with success - this.updateActivity(msg.id, { - status: 'complete', - result, - duration: Date.now() - startTime, - }); - } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error'; - this.send({ id: msg.id, error: { message } }); - - // Update activity with error - this.updateActivity(msg.id, { - status: 'error', - error: message, - duration: Date.now() - startTime, - }); - } - } else { - this.send({ - id: msg.id, - error: { message: `Unknown tool: ${msg.method}` } - }); - - // Update activity with error - this.updateActivity(msg.id, { - status: 'error', - error: `Unknown tool: ${msg.method}`, - duration: Date.now() - startTime, - }); - } - } - } - - /** - * Log an activity event - */ - private logActivity(event: ActivityEvent) { - this.activityLog.push(event); - // Keep max 100 events - if (this.activityLog.length > 100) { - this.activityLog.shift(); - } - this.notifyActivityListeners(event); - } - - /** - * Update an existing activity event - */ - private updateActivity(id: string, updates: Partial) { - const event = this.activityLog.find(e => e.id === id); - if (event) { - Object.assign(event, updates); - this.notifyActivityListeners(event); - } - } - - /** - * Send a message to the daemon - */ - private send(msg: MCPMessage) { - if (this.ws?.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(msg)); - } - } - - /** - * Register a handler for a tool - */ - registerHandler(method: string, handler: ToolHandler) { - this.handlers.set(method, handler); - } - - /** - * Listen for connection state changes - */ - onConnectionChange(listener: (connected: boolean) => void) { - this.connectionListeners.add(listener); - return () => this.connectionListeners.delete(listener); - } - - private notifyConnectionListeners(connected: boolean) { - this.connectionListeners.forEach(listener => listener(connected)); - } - - /** - * Listen for activity events - */ - onActivity(listener: ActivityListener) { - this.activityListeners.add(listener); - return () => this.activityListeners.delete(listener); - } - - private notifyActivityListeners(event: ActivityEvent) { - this.activityListeners.forEach(listener => listener(event)); - } - - /** - * Get the activity log - */ - getActivityLog(): ActivityEvent[] { - return [...this.activityLog]; - } - - /** - * Clear the activity log - */ - clearActivityLog() { - this.activityLog = []; - } - - /** - * Check if connected - */ - get isConnected(): boolean { - return this.ws?.readyState === WebSocket.OPEN; - } - - /** - * Get the connected agent info - */ - get connectedAgent(): ConnectedAgent | null { - return this._connectedAgent; - } - - /** - * Disconnect from daemon - */ - disconnect() { - this.ws?.close(); - this.ws = null; - } -} - -// Singleton instance -let mcpClientInstance: MCPBrowserClient | null = null; - -export function getMCPClient(): MCPBrowserClient { - if (!mcpClientInstance) { - mcpClientInstance = new MCPBrowserClient(); - } - return mcpClientInstance; -} diff --git a/gitnexus/src/core/search/bm25-index.ts b/gitnexus/src/core/search/bm25-index.ts index 4b745bd72..d20ef1b80 100644 --- a/gitnexus/src/core/search/bm25-index.ts +++ b/gitnexus/src/core/search/bm25-index.ts @@ -1,17 +1,11 @@ /** - * BM25 Full-Text Search Index + * Full-Text Search via KuzuDB FTS * - * Uses MiniSearch for fast keyword-based search with BM25 ranking. - * Complements semantic search - BM25 finds exact terms, semantic finds concepts. + * Uses KuzuDB's built-in full-text search indexes for keyword-based search. + * Always reads from the database (no cached state to drift). */ -import MiniSearch from 'minisearch'; - -export interface BM25Document { - id: string; // File path - content: string; // File content - name: string; // File name (boosted in search) -} +import { queryFTS } from '../kuzu/kuzu-adapter.js'; export interface BM25SearchResult { filePath: string; @@ -20,142 +14,100 @@ export interface BM25SearchResult { } /** - * BM25 Index singleton - * Stores the MiniSearch instance and provides search methods + * Execute a single FTS query via a custom executor (for MCP connection pool). + * Returns the same shape as core queryFTS. */ -let searchIndex: MiniSearch | null = null; -let indexedDocCount = 0; - -/** - * Build the BM25 index from file contents - * Should be called after ingestion completes - * - * @param fileContents - Map of file path to content - * @returns Number of documents indexed - */ -export const buildBM25Index = (fileContents: Map): number => { - // Create new MiniSearch instance with BM25-like scoring - searchIndex = new MiniSearch({ - fields: ['content', 'name'], // Fields to index - storeFields: ['id'], // Fields to return in results - - // Tokenizer: split on non-alphanumeric, camelCase, snake_case - tokenize: (text: string) => { - // Split on whitespace and punctuation - const tokens = text.toLowerCase().split(/[\s\-_./\\(){}[\]<>:;,!?'"]+/); - - // Also split camelCase: "getUserById" -> ["get", "user", "by", "id"] - const expanded: string[] = []; - for (const token of tokens) { - if (token.length === 0) continue; - - // Split camelCase - const camelParts = token.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase().split(' '); - expanded.push(...camelParts); - - // Also keep original token for exact matches - if (camelParts.length > 1) { - expanded.push(token); - } - } - - // Filter out very short tokens and common noise - return expanded.filter(t => t.length > 1 && !STOP_WORDS.has(t)); - }, - }); - - // Index all files - const documents: BM25Document[] = []; - - for (const [filePath, content] of fileContents.entries()) { - // Extract filename from path - const name = filePath.split('/').pop() || filePath; - - documents.push({ - id: filePath, - content: content, - name: name, +async function queryFTSViaExecutor( + executor: (cypher: string) => Promise, + tableName: string, + indexName: string, + query: string, + limit: number, +): Promise> { + const escapedQuery = query.replace(/'/g, "''"); + const cypher = ` + CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', '${escapedQuery}', conjunctive := false) + RETURN node, score + ORDER BY score DESC + LIMIT ${limit} + `; + try { + const rows = await executor(cypher); + return rows.map((row: any) => { + const node = row.node || row[0] || {}; + const score = row.score ?? row[1] ?? 0; + return { + filePath: node.filePath || '', + score: typeof score === 'number' ? score : parseFloat(score) || 0, + }; }); - } - - // Batch add for efficiency - searchIndex.addAll(documents); - indexedDocCount = documents.length; - - if (import.meta.env.DEV) { - console.log(`📚 BM25 index built: ${indexedDocCount} documents`); - } - - return indexedDocCount; -}; - -/** - * Search the BM25 index - * - * @param query - Search query (keywords) - * @param limit - Maximum results to return - * @returns Ranked search results with file paths and scores - */ -export const searchBM25 = (query: string, limit: number = 20): BM25SearchResult[] => { - if (!searchIndex) { + } catch { return []; } +} + +/** + * Search using KuzuDB's built-in FTS (always fresh, reads from disk) + * + * Queries multiple node tables (File, Function, Class, Method) in parallel + * and merges results by filePath, summing scores for the same file. + * + * @param query - Search query string + * @param limit - Maximum results + * @param repoId - If provided, queries will be routed via the MCP connection pool + * @returns Ranked search results from FTS indexes + */ +export const searchFTSFromKuzu = async (query: string, limit: number = 20, repoId?: string): Promise => { + let fileResults: any[], functionResults: any[], classResults: any[], methodResults: any[], interfaceResults: any[]; + + if (repoId) { + // Use MCP connection pool via dynamic import + // IMPORTANT: KuzuDB uses a single connection per repo — queries must be sequential + // to avoid deadlocking. Do NOT use Promise.all here. + const { executeQuery } = await import('../../mcp/core/kuzu-adapter.js'); + const executor = (cypher: string) => executeQuery(repoId, cypher); + fileResults = await queryFTSViaExecutor(executor, 'File', 'file_fts', query, limit); + functionResults = await queryFTSViaExecutor(executor, 'Function', 'function_fts', query, limit); + classResults = await queryFTSViaExecutor(executor, 'Class', 'class_fts', query, limit); + methodResults = await queryFTSViaExecutor(executor, 'Method', 'method_fts', query, limit); + interfaceResults = await queryFTSViaExecutor(executor, 'Interface', 'interface_fts', query, limit); + } else { + // Use core kuzu adapter (CLI / pipeline context) — also sequential for safety + fileResults = await queryFTS('File', 'file_fts', query, limit, false).catch(() => []); + functionResults = await queryFTS('Function', 'function_fts', query, limit, false).catch(() => []); + classResults = await queryFTS('Class', 'class_fts', query, limit, false).catch(() => []); + methodResults = await queryFTS('Method', 'method_fts', query, limit, false).catch(() => []); + interfaceResults = await queryFTS('Interface', 'interface_fts', query, limit, false).catch(() => []); + } - // Search with fuzzy matching and prefix support - const results = searchIndex.search(query, { - fuzzy: 0.2, - prefix: true, - boost: { name: 2 }, // Boost file name matches - }); + // Merge results by filePath, summing scores for same file + const merged = new Map(); - // Limit results and add rank - return results.slice(0, limit).map((r, index) => ({ - filePath: r.id, + const addResults = (results: any[]) => { + for (const r of results) { + const existing = merged.get(r.filePath); + if (existing) { + existing.score += r.score; + } else { + merged.set(r.filePath, { filePath: r.filePath, score: r.score }); + } + } + }; + + addResults(fileResults); + addResults(functionResults); + addResults(classResults); + addResults(methodResults); + addResults(interfaceResults); + + // Sort by score descending and add rank + const sorted = Array.from(merged.values()) + .sort((a, b) => b.score - a.score) + .slice(0, limit); + + return sorted.map((r, index) => ({ + filePath: r.filePath, score: r.score, rank: index + 1, })); }; - -/** - * Check if the BM25 index is ready - */ -export const isBM25Ready = (): boolean => { - return searchIndex !== null && indexedDocCount > 0; -}; - -/** - * Get index statistics - */ -export const getBM25Stats = (): { documentCount: number; termCount: number } => { - if (!searchIndex) { - return { documentCount: 0, termCount: 0 }; - } - - return { - documentCount: indexedDocCount, - termCount: searchIndex.termCount, - }; -}; - -/** - * Clear the index (for cleanup or re-indexing) - */ -export const clearBM25Index = (): void => { - searchIndex = null; - indexedDocCount = 0; -}; - -/** - * Common stop words to filter out (too common to be useful) - */ -const STOP_WORDS = new Set([ - // JavaScript/TypeScript keywords - 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', - 'class', 'new', 'this', 'import', 'export', 'from', 'default', 'async', 'await', - 'try', 'catch', 'throw', 'typeof', 'instanceof', 'true', 'false', 'null', 'undefined', - - // Common English stop words - 'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'but', 'in', 'with', - 'to', 'of', 'it', 'be', 'as', 'by', 'that', 'for', 'are', 'was', 'were', -]); - diff --git a/gitnexus/src/core/search/hybrid-search.ts b/gitnexus/src/core/search/hybrid-search.ts index 247bb2783..16d3bc42d 100644 --- a/gitnexus/src/core/search/hybrid-search.ts +++ b/gitnexus/src/core/search/hybrid-search.ts @@ -8,8 +8,8 @@ * production search systems. */ -import { searchBM25, isBM25Ready, type BM25SearchResult } from './bm25-index'; -import type { SemanticSearchResult } from '../embeddings/types'; +import { searchFTSFromKuzu, type BM25SearchResult } from './bm25-index.js'; +import type { SemanticSearchResult } from '../embeddings/types.js'; /** * RRF constant - standard value used in the literature @@ -114,11 +114,11 @@ export const mergeWithRRF = ( /** * Check if hybrid search is available - * Requires BM25 index to be built - * Note: Semantic search is optional - hybrid works with just BM25 if embeddings aren't ready + * KuzuDB FTS is always available once the database is initialized. + * Semantic search is optional - hybrid works with just FTS if embeddings aren't ready. */ export const isHybridSearchReady = (): boolean => { - return isBM25Ready(); + return true; // FTS is always available via KuzuDB when DB is open }; /** @@ -144,6 +144,19 @@ export const formatHybridResults = (results: HybridSearchResult[]): string => { return `Found ${results.length} results:\n\n${formatted.join('\n\n')}`; }; - - - +/** + * Execute BM25 + semantic search and merge with RRF. + * Uses KuzuDB FTS for always-fresh BM25 results (no cached data). + * The semanticSearch function is injected to keep this module environment-agnostic. + */ +export const hybridSearch = async ( + query: string, + limit: number, + executeQuery: (cypher: string) => Promise, + semanticSearch: (executeQuery: (cypher: string) => Promise, query: string, k?: number) => Promise +): Promise => { + // Use KuzuDB FTS for always-fresh BM25 results + const bm25Results = await searchFTSFromKuzu(query, limit); + const semanticResults = await semanticSearch(executeQuery, query, limit); + return mergeWithRRF(bm25Results, semanticResults, limit); +}; diff --git a/gitnexus/src/core/tree-sitter/parser-loader.ts b/gitnexus/src/core/tree-sitter/parser-loader.ts index d5224ca4e..cdca3003d 100644 --- a/gitnexus/src/core/tree-sitter/parser-loader.ts +++ b/gitnexus/src/core/tree-sitter/parser-loader.ts @@ -1,72 +1,45 @@ -import Parser from 'web-tree-sitter'; -import { SupportedLanguages } from '../../config/supported-languages'; +import Parser from 'tree-sitter'; +import JavaScript from 'tree-sitter-javascript'; +import TypeScript from 'tree-sitter-typescript'; +import Python from 'tree-sitter-python'; +import Java from 'tree-sitter-java'; +import C from 'tree-sitter-c'; +import CPP from 'tree-sitter-cpp'; +import CSharp from 'tree-sitter-c-sharp'; +import Go from 'tree-sitter-go'; +import Rust from 'tree-sitter-rust'; +import { SupportedLanguages } from '../../config/supported-languages.js'; let parser: Parser | null = null; -// Cache the compiled Language objects to avoid fetching/compiling twice -const languageCache = new Map(); +const languageMap: Record = { + [SupportedLanguages.JavaScript]: JavaScript, + [SupportedLanguages.TypeScript]: TypeScript.typescript, + [`${SupportedLanguages.TypeScript}:tsx`]: TypeScript.tsx, + [SupportedLanguages.Python]: Python, + [SupportedLanguages.Java]: Java, + [SupportedLanguages.C]: C, + [SupportedLanguages.CPlusPlus]: CPP, + [SupportedLanguages.CSharp]: CSharp, + [SupportedLanguages.Go]: Go, + [SupportedLanguages.Rust]: Rust, +}; export const loadParser = async (): Promise => { - if (parser) return parser; - - await Parser.init({ - locateFile: (scriptName: string) => { - return `/wasm/${scriptName}`; - } - }) - - parser = new Parser(); - return parser; -} - -// Get the appropriate WASM file based on language and file extension -const getWasmPath = (language: SupportedLanguages, filePath?: string): string => { - // For TypeScript, check if it's a TSX file - if (language === SupportedLanguages.TypeScript) { - if (filePath?.endsWith('.tsx')) { - return '/wasm/typescript/tree-sitter-tsx.wasm'; - } - return '/wasm/typescript/tree-sitter-typescript.wasm'; - } - - const languageFileMap: Record = { - [SupportedLanguages.JavaScript]: '/wasm/javascript/tree-sitter-javascript.wasm', - [SupportedLanguages.TypeScript]: '/wasm/typescript/tree-sitter-typescript.wasm', - [SupportedLanguages.Python]: '/wasm/python/tree-sitter-python.wasm', - [SupportedLanguages.Java]: '/wasm/java/tree-sitter-java.wasm', - [SupportedLanguages.C]: '/wasm/c/tree-sitter-c.wasm', - [SupportedLanguages.CPlusPlus]: '/wasm/cpp/tree-sitter-cpp.wasm', - [SupportedLanguages.CSharp]: '/wasm/csharp/tree-sitter-csharp.wasm', - [SupportedLanguages.Go]: '/wasm/go/tree-sitter-go.wasm', - [SupportedLanguages.Rust]: '/wasm/rust/tree-sitter-rust.wasm', - }; - - return languageFileMap[language]; + if (parser) return parser; + parser = new Parser(); + return parser; }; export const loadLanguage = async (language: SupportedLanguages, filePath?: string): Promise => { - if (!parser) await loadParser(); - const wasmPath = getWasmPath(language, filePath); - - if (languageCache.has(wasmPath)) { - parser!.setLanguage(languageCache.get(wasmPath)!); - return; - } + if (!parser) await loadParser(); + const key = language === SupportedLanguages.TypeScript && filePath?.endsWith('.tsx') + ? `${language}:tsx` + : language; - if (!wasmPath) { - console.error(`❌ [Parser] No WASM path configured for language: ${language}`); - throw new Error(`Unsupported language: ${language}`); - } - - try { - const loadedLanguage = await Parser.Language.load(wasmPath); - languageCache.set(wasmPath, loadedLanguage); - parser!.setLanguage(loadedLanguage); - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error(`❌ [Parser] Failed to load WASM grammar for ${language}`); - console.error(` WASM Path: ${wasmPath}`); - console.error(` Error: ${errorMessage}`); - throw new Error(`Failed to load grammar for ${language}: ${errorMessage}`); - } -} + const lang = languageMap[key]; + if (!lang) { + throw new Error(`Unsupported language: ${language}`); + } + parser!.setLanguage(lang); +}; diff --git a/gitnexus/src/core/wiki/generator.ts b/gitnexus/src/core/wiki/generator.ts new file mode 100644 index 000000000..29a16a541 --- /dev/null +++ b/gitnexus/src/core/wiki/generator.ts @@ -0,0 +1,953 @@ +/** + * Wiki Generator + * + * Orchestrates the full wiki generation pipeline: + * Phase 0: Validate prerequisites + gather graph structure + * Phase 1: Build module tree (one LLM call) + * Phase 2: Generate module pages (one LLM call per module, bottom-up) + * Phase 3: Generate overview page + * + * Supports incremental updates via git diff + module-file mapping. + */ + +import fs from 'fs/promises'; +import path from 'path'; +import { execSync } from 'child_process'; + +import { + initWikiDb, + closeWikiDb, + getFilesWithExports, + getAllFiles, + getInterFileCallEdges, + getIntraModuleCallEdges, + getInterModuleCallEdges, + getProcessesForFiles, + getAllProcesses, + getInterModuleEdgesForOverview, + type FileWithExports, +} from './graph-queries.js'; +import { generateHTMLViewer } from './html-viewer.js'; + +import { + callLLM, + estimateTokens, + type LLMConfig, + type CallLLMOptions, +} from './llm-client.js'; + +import { + GROUPING_SYSTEM_PROMPT, + GROUPING_USER_PROMPT, + MODULE_SYSTEM_PROMPT, + MODULE_USER_PROMPT, + PARENT_SYSTEM_PROMPT, + PARENT_USER_PROMPT, + OVERVIEW_SYSTEM_PROMPT, + OVERVIEW_USER_PROMPT, + fillTemplate, + formatFileListForGrouping, + formatDirectoryTree, + formatCallEdges, + formatProcesses, +} from './prompts.js'; + +import { shouldIgnorePath } from '../../config/ignore-service.js'; + +// ─── Types ──────────────────────────────────────────────────────────── + +export interface WikiOptions { + force?: boolean; + model?: string; + baseUrl?: string; + apiKey?: string; + maxTokensPerModule?: number; + concurrency?: number; +} + +export interface WikiMeta { + fromCommit: string; + generatedAt: string; + model: string; + moduleFiles: Record; + moduleTree: ModuleTreeNode[]; +} + +export interface ModuleTreeNode { + name: string; + slug: string; + files: string[]; + children?: ModuleTreeNode[]; +} + +export type ProgressCallback = (phase: string, percent: number, detail?: string) => void; + +// ─── Constants ──────────────────────────────────────────────────────── + +const DEFAULT_MAX_TOKENS_PER_MODULE = 30_000; +const WIKI_DIR = 'wiki'; + +// ─── Generator Class ────────────────────────────────────────────────── + +export class WikiGenerator { + private repoPath: string; + private storagePath: string; + private wikiDir: string; + private kuzuPath: string; + private llmConfig: LLMConfig; + private maxTokensPerModule: number; + private concurrency: number; + private options: WikiOptions; + private onProgress: ProgressCallback; + private failedModules: string[] = []; + + constructor( + repoPath: string, + storagePath: string, + kuzuPath: string, + llmConfig: LLMConfig, + options: WikiOptions = {}, + onProgress?: ProgressCallback, + ) { + this.repoPath = repoPath; + this.storagePath = storagePath; + this.wikiDir = path.join(storagePath, WIKI_DIR); + this.kuzuPath = kuzuPath; + this.options = options; + this.llmConfig = llmConfig; + this.maxTokensPerModule = options.maxTokensPerModule ?? DEFAULT_MAX_TOKENS_PER_MODULE; + this.concurrency = options.concurrency ?? 3; + const progressFn = onProgress || (() => {}); + this.onProgress = (phase, percent, detail) => { + if (percent > 0) this.lastPercent = percent; + progressFn(phase, percent, detail); + }; + } + + private lastPercent = 0; + + /** + * Create streaming options that report LLM progress to the progress bar. + * Uses the last known percent so streaming doesn't reset the bar backwards. + */ + private streamOpts(label: string, fixedPercent?: number): CallLLMOptions { + return { + onChunk: (chars: number) => { + const tokens = Math.round(chars / 4); + const pct = fixedPercent ?? this.lastPercent; + this.onProgress('stream', pct, `${label} (${tokens} tok)`); + }, + }; + } + + /** + * Main entry point. Runs the full pipeline or incremental update. + */ + async run(): Promise<{ pagesGenerated: number; mode: 'full' | 'incremental' | 'up-to-date'; failedModules: string[] }> { + await fs.mkdir(this.wikiDir, { recursive: true }); + + const existingMeta = await this.loadWikiMeta(); + const currentCommit = this.getCurrentCommit(); + const forceMode = this.options.force; + + // Up-to-date check (skip if --force) + if (!forceMode && existingMeta && existingMeta.fromCommit === currentCommit) { + // Still regenerate the HTML viewer in case it's missing + await this.ensureHTMLViewer(); + return { pagesGenerated: 0, mode: 'up-to-date', failedModules: [] }; + } + + // Force mode: delete snapshot to force full re-grouping + if (forceMode) { + try { await fs.unlink(path.join(this.wikiDir, 'first_module_tree.json')); } catch {} + // Delete existing module pages so they get regenerated + const existingFiles = await fs.readdir(this.wikiDir).catch(() => [] as string[]); + for (const f of existingFiles) { + if (f.endsWith('.md')) { + try { await fs.unlink(path.join(this.wikiDir, f)); } catch {} + } + } + } + + // Init graph + this.onProgress('init', 2, 'Connecting to knowledge graph...'); + await initWikiDb(this.kuzuPath); + + let result: { pagesGenerated: number; mode: 'full' | 'incremental' | 'up-to-date'; failedModules: string[] }; + try { + if (!forceMode && existingMeta && existingMeta.fromCommit) { + result = await this.incrementalUpdate(existingMeta, currentCommit); + } else { + result = await this.fullGeneration(currentCommit); + } + } finally { + await closeWikiDb(); + } + + // Always generate the HTML viewer after wiki content changes + await this.ensureHTMLViewer(); + + return result; + } + + // ─── HTML Viewer ───────────────────────────────────────────────────── + + private async ensureHTMLViewer(): Promise { + // Only generate if there are markdown pages to bundle + const dirEntries = await fs.readdir(this.wikiDir).catch(() => [] as string[]); + const hasMd = dirEntries.some(f => f.endsWith('.md')); + if (!hasMd) return; + + this.onProgress('html', 98, 'Building HTML viewer...'); + const repoName = path.basename(this.repoPath); + await generateHTMLViewer(this.wikiDir, repoName); + } + + // ─── Full Generation ──────────────────────────────────────────────── + + private async fullGeneration(currentCommit: string): Promise<{ pagesGenerated: number; mode: 'full'; failedModules: string[] }> { + let pagesGenerated = 0; + + // Phase 0: Gather structure + this.onProgress('gather', 5, 'Querying graph for file structure...'); + const filesWithExports = await getFilesWithExports(); + const allFiles = await getAllFiles(); + + // Filter to source files only + const sourceFiles = allFiles.filter(f => !shouldIgnorePath(f)); + if (sourceFiles.length === 0) { + throw new Error('No source files found in the knowledge graph. Nothing to document.'); + } + + // Build enriched file list (merge exports into all source files) + const exportMap = new Map(filesWithExports.map(f => [f.filePath, f])); + const enrichedFiles: FileWithExports[] = sourceFiles.map(fp => { + return exportMap.get(fp) || { filePath: fp, symbols: [] }; + }); + + this.onProgress('gather', 10, `Found ${sourceFiles.length} source files`); + + // Phase 1: Build module tree + const moduleTree = await this.buildModuleTree(enrichedFiles); + pagesGenerated = 0; + + // Phase 2: Generate module pages (parallel with concurrency limit) + const totalModules = this.countModules(moduleTree); + let modulesProcessed = 0; + + const reportProgress = (moduleName?: string) => { + modulesProcessed++; + const percent = 30 + Math.round((modulesProcessed / totalModules) * 55); + const detail = moduleName + ? `${modulesProcessed}/${totalModules} — ${moduleName}` + : `${modulesProcessed}/${totalModules} modules`; + this.onProgress('modules', percent, detail); + }; + + // Flatten tree into layers: leaves first, then parents + // Leaves can run in parallel; parents must wait for their children + const { leaves, parents } = this.flattenModuleTree(moduleTree); + + // Process all leaf modules in parallel + pagesGenerated += await this.runParallel(leaves, async (node) => { + const pagePath = path.join(this.wikiDir, `${node.slug}.md`); + if (await this.fileExists(pagePath)) { + reportProgress(node.name); + return 0; + } + try { + await this.generateLeafPage(node); + reportProgress(node.name); + return 1; + } catch (err: any) { + this.failedModules.push(node.name); + reportProgress(`Failed: ${node.name}`); + return 0; + } + }); + + // Process parent modules sequentially (they depend on child docs) + for (const node of parents) { + const pagePath = path.join(this.wikiDir, `${node.slug}.md`); + if (await this.fileExists(pagePath)) { + reportProgress(node.name); + continue; + } + try { + await this.generateParentPage(node); + pagesGenerated++; + reportProgress(node.name); + } catch (err: any) { + this.failedModules.push(node.name); + reportProgress(`Failed: ${node.name}`); + } + } + + // Phase 3: Generate overview + this.onProgress('overview', 88, 'Generating overview page...'); + await this.generateOverview(moduleTree); + pagesGenerated++; + + // Save metadata + this.onProgress('finalize', 95, 'Saving metadata...'); + const moduleFiles = this.extractModuleFiles(moduleTree); + await this.saveModuleTree(moduleTree); + await this.saveWikiMeta({ + fromCommit: currentCommit, + generatedAt: new Date().toISOString(), + model: this.llmConfig.model, + moduleFiles, + moduleTree, + }); + + this.onProgress('done', 100, 'Wiki generation complete'); + return { pagesGenerated, mode: 'full', failedModules: [...this.failedModules] }; + } + + // ─── Phase 1: Build Module Tree ──────────────────────────────────── + + private async buildModuleTree(files: FileWithExports[]): Promise { + // Check for existing immutable snapshot (resumability) + const snapshotPath = path.join(this.wikiDir, 'first_module_tree.json'); + try { + const existing = await fs.readFile(snapshotPath, 'utf-8'); + const parsed = JSON.parse(existing); + if (Array.isArray(parsed) && parsed.length > 0) { + this.onProgress('grouping', 25, 'Using existing module tree (resuming)'); + return parsed; + } + } catch { + // No snapshot, generate new + } + + this.onProgress('grouping', 15, 'Grouping files into modules (LLM)...'); + + const fileList = formatFileListForGrouping(files); + const dirTree = formatDirectoryTree(files.map(f => f.filePath)); + + const prompt = fillTemplate(GROUPING_USER_PROMPT, { + FILE_LIST: fileList, + DIRECTORY_TREE: dirTree, + }); + + const response = await callLLM( + prompt, this.llmConfig, GROUPING_SYSTEM_PROMPT, + this.streamOpts('Grouping files', 15), + ); + const grouping = this.parseGroupingResponse(response.content, files); + + // Convert to tree nodes + const tree: ModuleTreeNode[] = []; + for (const [moduleName, modulePaths] of Object.entries(grouping)) { + const slug = this.slugify(moduleName); + const node: ModuleTreeNode = { name: moduleName, slug, files: modulePaths }; + + // Token budget check — split if too large + const totalTokens = await this.estimateModuleTokens(modulePaths); + if (totalTokens > this.maxTokensPerModule && modulePaths.length > 3) { + node.children = this.splitBySubdirectory(moduleName, modulePaths); + node.files = []; // Parent doesn't own files directly when split + } + + tree.push(node); + } + + // Save immutable snapshot for resumability + await fs.writeFile(snapshotPath, JSON.stringify(tree, null, 2), 'utf-8'); + this.onProgress('grouping', 28, `Created ${tree.length} modules`); + + return tree; + } + + /** + * Parse LLM grouping response. Validates all files are assigned. + */ + private parseGroupingResponse( + content: string, + files: FileWithExports[], + ): Record { + // Extract JSON from response (handle markdown fences) + let jsonStr = content.trim(); + const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/); + if (fenceMatch) { + jsonStr = fenceMatch[1].trim(); + } + + let parsed: Record; + try { + parsed = JSON.parse(jsonStr); + } catch { + // Fallback: group by top-level directory + return this.fallbackGrouping(files); + } + + if (typeof parsed !== 'object' || Array.isArray(parsed)) { + return this.fallbackGrouping(files); + } + + // Validate — ensure all files are assigned + const allFilePaths = new Set(files.map(f => f.filePath)); + const assignedFiles = new Set(); + const validGrouping: Record = {}; + + for (const [mod, paths] of Object.entries(parsed)) { + if (!Array.isArray(paths)) continue; + const validPaths = paths.filter(p => { + if (allFilePaths.has(p) && !assignedFiles.has(p)) { + assignedFiles.add(p); + return true; + } + return false; + }); + if (validPaths.length > 0) { + validGrouping[mod] = validPaths; + } + } + + // Assign unassigned files to a "Miscellaneous" module + const unassigned = files + .map(f => f.filePath) + .filter(fp => !assignedFiles.has(fp)); + if (unassigned.length > 0) { + validGrouping['Other'] = unassigned; + } + + return Object.keys(validGrouping).length > 0 + ? validGrouping + : this.fallbackGrouping(files); + } + + /** + * Fallback grouping by top-level directory when LLM parsing fails. + */ + private fallbackGrouping(files: FileWithExports[]): Record { + const groups = new Map(); + for (const f of files) { + const parts = f.filePath.replace(/\\/g, '/').split('/'); + const topDir = parts.length > 1 ? parts[0] : 'Root'; + let group = groups.get(topDir); + if (!group) { group = []; groups.set(topDir, group); } + group.push(f.filePath); + } + return Object.fromEntries(groups); + } + + /** + * Split a large module into sub-modules by subdirectory. + */ + private splitBySubdirectory(moduleName: string, files: string[]): ModuleTreeNode[] { + const subGroups = new Map(); + for (const fp of files) { + const parts = fp.replace(/\\/g, '/').split('/'); + // Use the deepest common-ish directory + const subDir = parts.length > 2 ? parts.slice(0, 2).join('/') : parts[0]; + let group = subGroups.get(subDir); + if (!group) { group = []; subGroups.set(subDir, group); } + group.push(fp); + } + + return Array.from(subGroups.entries()).map(([subDir, subFiles]) => ({ + name: `${moduleName} — ${path.basename(subDir)}`, + slug: this.slugify(`${moduleName}-${path.basename(subDir)}`), + files: subFiles, + })); + } + + // ─── Phase 2: Generate Module Pages ───────────────────────────────── + + /** + * Generate a leaf module page from source code + graph data. + */ + private async generateLeafPage(node: ModuleTreeNode): Promise { + const filePaths = node.files; + + // Read source files from disk + const sourceCode = await this.readSourceFiles(filePaths); + + // Token budget check — if too large, summarize in batches + const totalTokens = estimateTokens(sourceCode); + let finalSourceCode = sourceCode; + if (totalTokens > this.maxTokensPerModule) { + finalSourceCode = this.truncateSource(sourceCode, this.maxTokensPerModule); + } + + // Get graph data + const [intraCalls, interCalls, processes] = await Promise.all([ + getIntraModuleCallEdges(filePaths), + getInterModuleCallEdges(filePaths), + getProcessesForFiles(filePaths, 5), + ]); + + const prompt = fillTemplate(MODULE_USER_PROMPT, { + MODULE_NAME: node.name, + SOURCE_CODE: finalSourceCode, + INTRA_CALLS: formatCallEdges(intraCalls), + OUTGOING_CALLS: formatCallEdges(interCalls.outgoing), + INCOMING_CALLS: formatCallEdges(interCalls.incoming), + PROCESSES: formatProcesses(processes), + }); + + const response = await callLLM( + prompt, this.llmConfig, MODULE_SYSTEM_PROMPT, + this.streamOpts(node.name), + ); + + // Write page with front matter + const pageContent = `# ${node.name}\n\n${response.content}`; + await fs.writeFile(path.join(this.wikiDir, `${node.slug}.md`), pageContent, 'utf-8'); + } + + /** + * Generate a parent module page from children's documentation. + */ + private async generateParentPage(node: ModuleTreeNode): Promise { + if (!node.children || node.children.length === 0) return; + + // Read children's overview sections + const childDocs: string[] = []; + for (const child of node.children) { + const childPage = path.join(this.wikiDir, `${child.slug}.md`); + try { + const content = await fs.readFile(childPage, 'utf-8'); + // Extract overview section (first ~500 chars or up to "### Architecture") + const overviewEnd = content.indexOf('### Architecture'); + const overview = overviewEnd > 0 ? content.slice(0, overviewEnd).trim() : content.slice(0, 800).trim(); + childDocs.push(`#### ${child.name}\n${overview}`); + } catch { + childDocs.push(`#### ${child.name}\n(Documentation not yet generated)`); + } + } + + // Get cross-child call edges + const allChildFiles = node.children.flatMap(c => c.files); + const crossCalls = await getIntraModuleCallEdges(allChildFiles); + const processes = await getProcessesForFiles(allChildFiles, 3); + + const prompt = fillTemplate(PARENT_USER_PROMPT, { + MODULE_NAME: node.name, + CHILDREN_DOCS: childDocs.join('\n\n'), + CROSS_MODULE_CALLS: formatCallEdges(crossCalls), + CROSS_PROCESSES: formatProcesses(processes), + }); + + const response = await callLLM( + prompt, this.llmConfig, PARENT_SYSTEM_PROMPT, + this.streamOpts(node.name), + ); + + const pageContent = `# ${node.name}\n\n${response.content}`; + await fs.writeFile(path.join(this.wikiDir, `${node.slug}.md`), pageContent, 'utf-8'); + } + + // ─── Phase 3: Generate Overview ───────────────────────────────────── + + private async generateOverview(moduleTree: ModuleTreeNode[]): Promise { + // Read module overview sections + const moduleSummaries: string[] = []; + for (const node of moduleTree) { + const pagePath = path.join(this.wikiDir, `${node.slug}.md`); + try { + const content = await fs.readFile(pagePath, 'utf-8'); + const overviewEnd = content.indexOf('### Architecture'); + const overview = overviewEnd > 0 ? content.slice(0, overviewEnd).trim() : content.slice(0, 600).trim(); + moduleSummaries.push(`#### ${node.name}\n${overview}`); + } catch { + moduleSummaries.push(`#### ${node.name}\n(Documentation pending)`); + } + } + + // Get inter-module edges for architecture diagram + const moduleFiles = this.extractModuleFiles(moduleTree); + const moduleEdges = await getInterModuleEdgesForOverview(moduleFiles); + + // Get top processes for key workflows + const topProcesses = await getAllProcesses(5); + + // Read project config + const projectInfo = await this.readProjectInfo(); + + const edgesText = moduleEdges.length > 0 + ? moduleEdges.map(e => `${e.from} → ${e.to} (${e.count} calls)`).join('\n') + : 'No inter-module call edges detected'; + + const prompt = fillTemplate(OVERVIEW_USER_PROMPT, { + PROJECT_INFO: projectInfo, + MODULE_SUMMARIES: moduleSummaries.join('\n\n'), + MODULE_EDGES: edgesText, + TOP_PROCESSES: formatProcesses(topProcesses), + }); + + const response = await callLLM( + prompt, this.llmConfig, OVERVIEW_SYSTEM_PROMPT, + this.streamOpts('Generating overview', 88), + ); + + const pageContent = `# ${path.basename(this.repoPath)} — Wiki\n\n${response.content}`; + await fs.writeFile(path.join(this.wikiDir, 'overview.md'), pageContent, 'utf-8'); + } + + // ─── Incremental Updates ──────────────────────────────────────────── + + private async incrementalUpdate( + existingMeta: WikiMeta, + currentCommit: string, + ): Promise<{ pagesGenerated: number; mode: 'incremental'; failedModules: string[] }> { + this.onProgress('incremental', 5, 'Detecting changes...'); + + // Get changed files since last generation + const changedFiles = this.getChangedFiles(existingMeta.fromCommit, currentCommit); + if (changedFiles.length === 0) { + // No file changes but commit differs (e.g. merge commit) + await this.saveWikiMeta({ + ...existingMeta, + fromCommit: currentCommit, + generatedAt: new Date().toISOString(), + }); + return { pagesGenerated: 0, mode: 'incremental', failedModules: [] }; + } + + this.onProgress('incremental', 10, `${changedFiles.length} files changed`); + + // Determine affected modules + const affectedModules = new Set(); + const newFiles: string[] = []; + + for (const fp of changedFiles) { + let found = false; + for (const [mod, files] of Object.entries(existingMeta.moduleFiles)) { + if (files.includes(fp)) { + affectedModules.add(mod); + found = true; + break; + } + } + if (!found && !shouldIgnorePath(fp)) { + newFiles.push(fp); + } + } + + // If significant new files exist, re-run full grouping + if (newFiles.length > 5) { + this.onProgress('incremental', 15, 'Significant new files detected, running full generation...'); + // Delete old snapshot to force re-grouping + try { await fs.unlink(path.join(this.wikiDir, 'first_module_tree.json')); } catch {} + const fullResult = await this.fullGeneration(currentCommit); + return { ...fullResult, mode: 'incremental' }; + } + + // Add new files to nearest module or "Other" + if (newFiles.length > 0) { + if (!existingMeta.moduleFiles['Other']) { + existingMeta.moduleFiles['Other'] = []; + } + existingMeta.moduleFiles['Other'].push(...newFiles); + affectedModules.add('Other'); + } + + // Regenerate affected module pages (parallel) + let pagesGenerated = 0; + const moduleTree = existingMeta.moduleTree; + const affectedArray = Array.from(affectedModules); + + this.onProgress('incremental', 20, `Regenerating ${affectedArray.length} module(s)...`); + + const affectedNodes: ModuleTreeNode[] = []; + for (const mod of affectedArray) { + const modSlug = this.slugify(mod); + const node = this.findNodeBySlug(moduleTree, modSlug); + if (node) { + try { await fs.unlink(path.join(this.wikiDir, `${node.slug}.md`)); } catch {} + affectedNodes.push(node); + } + } + + let incProcessed = 0; + pagesGenerated += await this.runParallel(affectedNodes, async (node) => { + try { + if (node.children && node.children.length > 0) { + await this.generateParentPage(node); + } else { + await this.generateLeafPage(node); + } + incProcessed++; + const percent = 20 + Math.round((incProcessed / affectedNodes.length) * 60); + this.onProgress('incremental', percent, `${incProcessed}/${affectedNodes.length} — ${node.name}`); + return 1; + } catch (err: any) { + this.failedModules.push(node.name); + incProcessed++; + return 0; + } + }); + + // Regenerate overview if any pages changed + if (pagesGenerated > 0) { + this.onProgress('incremental', 85, 'Updating overview...'); + await this.generateOverview(moduleTree); + pagesGenerated++; + } + + // Save updated metadata + this.onProgress('incremental', 95, 'Saving metadata...'); + await this.saveWikiMeta({ + ...existingMeta, + fromCommit: currentCommit, + generatedAt: new Date().toISOString(), + model: this.llmConfig.model, + }); + + this.onProgress('done', 100, 'Incremental update complete'); + return { pagesGenerated, mode: 'incremental', failedModules: [...this.failedModules] }; + } + + // ─── Helpers ──────────────────────────────────────────────────────── + + private getCurrentCommit(): string { + try { + return execSync('git rev-parse HEAD', { cwd: this.repoPath }).toString().trim(); + } catch { + return ''; + } + } + + private getChangedFiles(fromCommit: string, toCommit: string): string[] { + try { + const output = execSync( + `git diff ${fromCommit}..${toCommit} --name-only`, + { cwd: this.repoPath }, + ).toString().trim(); + return output ? output.split('\n').filter(Boolean) : []; + } catch { + return []; + } + } + + private async readSourceFiles(filePaths: string[]): Promise { + const parts: string[] = []; + for (const fp of filePaths) { + const fullPath = path.join(this.repoPath, fp); + try { + const content = await fs.readFile(fullPath, 'utf-8'); + parts.push(`\n--- ${fp} ---\n${content}`); + } catch { + parts.push(`\n--- ${fp} ---\n(file not readable)`); + } + } + return parts.join('\n'); + } + + private truncateSource(source: string, maxTokens: number): string { + // Rough truncation: keep first maxTokens*4 chars and add notice + const maxChars = maxTokens * 4; + if (source.length <= maxChars) return source; + return source.slice(0, maxChars) + '\n\n... (source truncated for context window limits)'; + } + + private async estimateModuleTokens(filePaths: string[]): Promise { + let total = 0; + for (const fp of filePaths) { + try { + const content = await fs.readFile(path.join(this.repoPath, fp), 'utf-8'); + total += estimateTokens(content); + } catch { + // File not readable, skip + } + } + return total; + } + + private async readProjectInfo(): Promise { + const candidates = ['package.json', 'Cargo.toml', 'pyproject.toml', 'go.mod', 'pom.xml', 'build.gradle']; + const lines: string[] = [`Project: ${path.basename(this.repoPath)}`]; + + for (const file of candidates) { + const fullPath = path.join(this.repoPath, file); + try { + const content = await fs.readFile(fullPath, 'utf-8'); + if (file === 'package.json') { + const pkg = JSON.parse(content); + if (pkg.name) lines.push(`Name: ${pkg.name}`); + if (pkg.description) lines.push(`Description: ${pkg.description}`); + if (pkg.scripts) lines.push(`Scripts: ${Object.keys(pkg.scripts).join(', ')}`); + } else { + // Include first 500 chars of other config files + lines.push(`\n${file}:\n${content.slice(0, 500)}`); + } + break; // Use first config found + } catch { + continue; + } + } + + // Read README excerpt + for (const readme of ['README.md', 'readme.md', 'README.txt']) { + try { + const content = await fs.readFile(path.join(this.repoPath, readme), 'utf-8'); + lines.push(`\nREADME excerpt:\n${content.slice(0, 1000)}`); + break; + } catch { + continue; + } + } + + return lines.join('\n'); + } + + private extractModuleFiles(tree: ModuleTreeNode[]): Record { + const result: Record = {}; + for (const node of tree) { + if (node.children && node.children.length > 0) { + result[node.name] = node.children.flatMap(c => c.files); + for (const child of node.children) { + result[child.name] = child.files; + } + } else { + result[node.name] = node.files; + } + } + return result; + } + + private countModules(tree: ModuleTreeNode[]): number { + let count = 0; + for (const node of tree) { + count++; + if (node.children) { + count += node.children.length; + } + } + return count; + } + + /** + * Flatten the module tree into leaf nodes and parent nodes. + * Leaves can be processed in parallel; parents must wait for children. + */ + private flattenModuleTree(tree: ModuleTreeNode[]): { leaves: ModuleTreeNode[]; parents: ModuleTreeNode[] } { + const leaves: ModuleTreeNode[] = []; + const parents: ModuleTreeNode[] = []; + + for (const node of tree) { + if (node.children && node.children.length > 0) { + for (const child of node.children) { + leaves.push(child); + } + parents.push(node); + } else { + leaves.push(node); + } + } + + return { leaves, parents }; + } + + /** + * Run async tasks in parallel with a concurrency limit and adaptive rate limiting. + * If a 429 rate limit is hit, concurrency is temporarily reduced. + */ + private async runParallel( + items: T[], + fn: (item: T) => Promise, + ): Promise { + let total = 0; + let activeConcurrency = this.concurrency; + let running = 0; + let idx = 0; + + return new Promise((resolve, reject) => { + const next = () => { + while (running < activeConcurrency && idx < items.length) { + const item = items[idx++]; + running++; + + fn(item) + .then((count) => { + total += count; + running--; + if (idx >= items.length && running === 0) { + resolve(total); + } else { + next(); + } + }) + .catch((err) => { + running--; + // On rate limit, reduce concurrency temporarily + if (err.message?.includes('429')) { + activeConcurrency = Math.max(1, activeConcurrency - 1); + this.onProgress('modules', this.lastPercent, `Rate limited — concurrency → ${activeConcurrency}`); + // Re-queue the item + idx--; + setTimeout(next, 5000); + } else { + if (idx >= items.length && running === 0) { + resolve(total); + } else { + next(); + } + } + }); + } + }; + + if (items.length === 0) { + resolve(0); + } else { + next(); + } + }); + } + + private findNodeBySlug(tree: ModuleTreeNode[], slug: string): ModuleTreeNode | null { + for (const node of tree) { + if (node.slug === slug) return node; + if (node.children) { + const found = this.findNodeBySlug(node.children, slug); + if (found) return found; + } + } + return null; + } + + private slugify(name: string): string { + return name + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 60); + } + + private async fileExists(fp: string): Promise { + try { + await fs.access(fp); + return true; + } catch { + return false; + } + } + + private async loadWikiMeta(): Promise { + try { + const raw = await fs.readFile(path.join(this.wikiDir, 'meta.json'), 'utf-8'); + return JSON.parse(raw) as WikiMeta; + } catch { + return null; + } + } + + private async saveWikiMeta(meta: WikiMeta): Promise { + await fs.writeFile( + path.join(this.wikiDir, 'meta.json'), + JSON.stringify(meta, null, 2), + 'utf-8', + ); + } + + private async saveModuleTree(tree: ModuleTreeNode[]): Promise { + await fs.writeFile( + path.join(this.wikiDir, 'module_tree.json'), + JSON.stringify(tree, null, 2), + 'utf-8', + ); + } +} diff --git a/gitnexus/src/core/wiki/graph-queries.ts b/gitnexus/src/core/wiki/graph-queries.ts new file mode 100644 index 000000000..f32161e16 --- /dev/null +++ b/gitnexus/src/core/wiki/graph-queries.ts @@ -0,0 +1,300 @@ +/** + * Graph Queries for Wiki Generation + * + * Encapsulated Cypher queries against the GitNexus knowledge graph. + * Uses the MCP-style pooled kuzu-adapter for connection management. + */ + +import { initKuzu, executeQuery, closeKuzu } from '../../mcp/core/kuzu-adapter.js'; + +const REPO_ID = '__wiki__'; + +export interface FileWithExports { + filePath: string; + symbols: Array<{ name: string; type: string }>; +} + +export interface CallEdge { + fromFile: string; + fromName: string; + toFile: string; + toName: string; +} + +export interface ProcessInfo { + id: string; + label: string; + type: string; + stepCount: number; + steps: Array<{ + step: number; + name: string; + filePath: string; + type: string; + }>; +} + +/** + * Initialize the KuzuDB connection for wiki generation. + */ +export async function initWikiDb(kuzuPath: string): Promise { + await initKuzu(REPO_ID, kuzuPath); +} + +/** + * Close the KuzuDB connection. + */ +export async function closeWikiDb(): Promise { + await closeKuzu(REPO_ID); +} + +/** + * Get all source files with their exported symbol names and types. + */ +export async function getFilesWithExports(): Promise { + const rows = await executeQuery(REPO_ID, ` + MATCH (f:File)-[:CodeRelation {type: 'DEFINES'}]->(n) + WHERE n.isExported = true + RETURN f.filePath AS filePath, n.name AS name, labels(n)[0] AS type + ORDER BY f.filePath + `); + + const fileMap = new Map(); + for (const row of rows) { + const fp = row.filePath || row[0]; + const name = row.name || row[1]; + const type = row.type || row[2]; + + let entry = fileMap.get(fp); + if (!entry) { + entry = { filePath: fp, symbols: [] }; + fileMap.set(fp, entry); + } + entry.symbols.push({ name, type }); + } + + return Array.from(fileMap.values()); +} + +/** + * Get all files tracked in the graph (including those with no exports). + */ +export async function getAllFiles(): Promise { + const rows = await executeQuery(REPO_ID, ` + MATCH (f:File) + RETURN f.filePath AS filePath + ORDER BY f.filePath + `); + return rows.map(r => r.filePath || r[0]); +} + +/** + * Get inter-file call edges (calls between different files). + */ +export async function getInterFileCallEdges(): Promise { + const rows = await executeQuery(REPO_ID, ` + MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b) + WHERE a.filePath <> b.filePath + RETURN DISTINCT a.filePath AS fromFile, a.name AS fromName, + b.filePath AS toFile, b.name AS toName + `); + + return rows.map(r => ({ + fromFile: r.fromFile || r[0], + fromName: r.fromName || r[1], + toFile: r.toFile || r[2], + toName: r.toName || r[3], + })); +} + +/** + * Get call edges between files within a specific set (intra-module). + */ +export async function getIntraModuleCallEdges(filePaths: string[]): Promise { + if (filePaths.length === 0) return []; + + const fileList = filePaths.map(f => `'${f.replace(/'/g, "''")}'`).join(', '); + const rows = await executeQuery(REPO_ID, ` + MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b) + WHERE a.filePath IN [${fileList}] AND b.filePath IN [${fileList}] + RETURN DISTINCT a.filePath AS fromFile, a.name AS fromName, + b.filePath AS toFile, b.name AS toName + `); + + return rows.map(r => ({ + fromFile: r.fromFile || r[0], + fromName: r.fromName || r[1], + toFile: r.toFile || r[2], + toName: r.toName || r[3], + })); +} + +/** + * Get call edges crossing module boundaries (external calls from/to module files). + */ +export async function getInterModuleCallEdges(filePaths: string[]): Promise<{ + outgoing: CallEdge[]; + incoming: CallEdge[]; +}> { + if (filePaths.length === 0) return { outgoing: [], incoming: [] }; + + const fileList = filePaths.map(f => `'${f.replace(/'/g, "''")}'`).join(', '); + + const outRows = await executeQuery(REPO_ID, ` + MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b) + WHERE a.filePath IN [${fileList}] AND NOT b.filePath IN [${fileList}] + RETURN DISTINCT a.filePath AS fromFile, a.name AS fromName, + b.filePath AS toFile, b.name AS toName + LIMIT 30 + `); + + const inRows = await executeQuery(REPO_ID, ` + MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b) + WHERE NOT a.filePath IN [${fileList}] AND b.filePath IN [${fileList}] + RETURN DISTINCT a.filePath AS fromFile, a.name AS fromName, + b.filePath AS toFile, b.name AS toName + LIMIT 30 + `); + + return { + outgoing: outRows.map(r => ({ + fromFile: r.fromFile || r[0], + fromName: r.fromName || r[1], + toFile: r.toFile || r[2], + toName: r.toName || r[3], + })), + incoming: inRows.map(r => ({ + fromFile: r.fromFile || r[0], + fromName: r.fromName || r[1], + toFile: r.toFile || r[2], + toName: r.toName || r[3], + })), + }; +} + +/** + * Get processes (execution flows) that pass through a set of files. + * Returns top N by step count. + */ +export async function getProcessesForFiles(filePaths: string[], limit = 5): Promise { + if (filePaths.length === 0) return []; + + const fileList = filePaths.map(f => `'${f.replace(/'/g, "''")}'`).join(', '); + + // Find processes that have steps in the given files + const procRows = await executeQuery(REPO_ID, ` + MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) + WHERE s.filePath IN [${fileList}] + RETURN DISTINCT p.id AS id, p.heuristicLabel AS label, + p.processType AS type, p.stepCount AS stepCount + ORDER BY stepCount DESC + LIMIT ${limit} + `); + + const processes: ProcessInfo[] = []; + for (const row of procRows) { + const procId = row.id || row[0]; + const label = row.label || row[1] || procId; + const type = row.type || row[2] || 'unknown'; + const stepCount = row.stepCount || row[3] || 0; + + // Get the full step trace for this process + const stepRows = await executeQuery(REPO_ID, ` + MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process {id: '${procId.replace(/'/g, "''")}'}) + RETURN s.name AS name, s.filePath AS filePath, labels(s)[0] AS type, r.step AS step + ORDER BY r.step + `); + + processes.push({ + id: procId, + label, + type, + stepCount, + steps: stepRows.map(s => ({ + step: s.step || s[3] || 0, + name: s.name || s[0], + filePath: s.filePath || s[1], + type: s.type || s[2], + })), + }); + } + + return processes; +} + +/** + * Get all processes in the graph (for overview page). + */ +export async function getAllProcesses(limit = 20): Promise { + const procRows = await executeQuery(REPO_ID, ` + MATCH (p:Process) + RETURN p.id AS id, p.heuristicLabel AS label, + p.processType AS type, p.stepCount AS stepCount + ORDER BY stepCount DESC + LIMIT ${limit} + `); + + const processes: ProcessInfo[] = []; + for (const row of procRows) { + const procId = row.id || row[0]; + const label = row.label || row[1] || procId; + const type = row.type || row[2] || 'unknown'; + const stepCount = row.stepCount || row[3] || 0; + + const stepRows = await executeQuery(REPO_ID, ` + MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process {id: '${procId.replace(/'/g, "''")}'}) + RETURN s.name AS name, s.filePath AS filePath, labels(s)[0] AS type, r.step AS step + ORDER BY r.step + `); + + processes.push({ + id: procId, + label, + type, + stepCount, + steps: stepRows.map(s => ({ + step: s.step || s[3] || 0, + name: s.name || s[0], + filePath: s.filePath || s[1], + type: s.type || s[2], + })), + }); + } + + return processes; +} + +/** + * Get inter-module edges for overview architecture diagram. + * Groups call edges by source/target module. + */ +export async function getInterModuleEdgesForOverview( + moduleFiles: Record +): Promise> { + // Build file-to-module lookup + const fileToModule = new Map(); + for (const [mod, files] of Object.entries(moduleFiles)) { + for (const f of files) { + fileToModule.set(f, mod); + } + } + + const allEdges = await getInterFileCallEdges(); + const moduleEdgeCounts = new Map(); + + for (const edge of allEdges) { + const fromMod = fileToModule.get(edge.fromFile); + const toMod = fileToModule.get(edge.toFile); + if (fromMod && toMod && fromMod !== toMod) { + const key = `${fromMod}|||${toMod}`; + moduleEdgeCounts.set(key, (moduleEdgeCounts.get(key) || 0) + 1); + } + } + + return Array.from(moduleEdgeCounts.entries()) + .map(([key, count]) => { + const [from, to] = key.split('|||'); + return { from, to, count }; + }) + .sort((a, b) => b.count - a.count); +} diff --git a/gitnexus/src/core/wiki/html-viewer.ts b/gitnexus/src/core/wiki/html-viewer.ts new file mode 100644 index 000000000..e0f36a073 --- /dev/null +++ b/gitnexus/src/core/wiki/html-viewer.ts @@ -0,0 +1,331 @@ +/** + * HTML Viewer Generator for Wiki + * + * Produces a self-contained index.html that embeds all markdown pages, + * module tree, and metadata — viewable offline in any browser. + */ + +import fs from 'fs/promises'; +import path from 'path'; + +interface ModuleTreeNode { + name: string; + slug: string; + files: string[]; + children?: ModuleTreeNode[]; +} + +/** + * Generate the wiki HTML viewer (index.html) from existing markdown pages. + */ +export async function generateHTMLViewer( + wikiDir: string, + projectName: string, +): Promise { + // Load module tree + let moduleTree: ModuleTreeNode[] = []; + try { + const raw = await fs.readFile(path.join(wikiDir, 'module_tree.json'), 'utf-8'); + moduleTree = JSON.parse(raw); + } catch { /* will show empty nav */ } + + // Load meta + let meta: Record | null = null; + try { + const raw = await fs.readFile(path.join(wikiDir, 'meta.json'), 'utf-8'); + meta = JSON.parse(raw); + } catch { /* no meta */ } + + // Read all markdown files into a { slug: content } map + const pages: Record = {}; + const dirEntries = await fs.readdir(wikiDir); + for (const f of dirEntries.filter(f => f.endsWith('.md'))) { + const content = await fs.readFile(path.join(wikiDir, f), 'utf-8'); + pages[f.replace(/\.md$/, '')] = content; + } + + const html = buildHTML(projectName, moduleTree, pages, meta); + const outputPath = path.join(wikiDir, 'index.html'); + await fs.writeFile(outputPath, html, 'utf-8'); + return outputPath; +} + +// ─── HTML Builder ─────────────────────────────────────────────────────── + +function esc(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +function buildHTML( + projectName: string, + moduleTree: ModuleTreeNode[], + pages: Record, + meta: Record | null, +): string { + // Embed data as JSON inside the HTML + const pagesJSON = JSON.stringify(pages); + const treeJSON = JSON.stringify(moduleTree); + const metaJSON = JSON.stringify(meta); + + const parts: string[] = []; + + // ── Head ── + parts.push(''); + parts.push(''); + parts.push(''); + parts.push(''); + parts.push(''); + parts.push('' + esc(projectName) + ' — Wiki'); + parts.push('