diff --git a/.changeset/README.md b/.changeset/README.md
new file mode 100644
index 0000000..12460a9
--- /dev/null
+++ b/.changeset/README.md
@@ -0,0 +1,8 @@
+# Changesets
+
+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
+with multi-package repos, or single-package repos to help you version and publish your code. You can
+find the full documentation for it [in the repository](https://github.com/changesets/changesets)
+
+We have a quick list of common questions to get you started engaging with this project in
+[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
diff --git a/.changeset/config.json b/.changeset/config.json
new file mode 100644
index 0000000..2be13d4
--- /dev/null
+++ b/.changeset/config.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
+ "changelog": "@changesets/cli/changelog",
+ "commit": false,
+ "fixed": [],
+ "linked": [],
+ "access": "public",
+ "baseBranch": "main",
+ "updateInternalDependencies": "patch",
+ "ignore": []
+}
diff --git a/.changeset/six-balloons-hope.md b/.changeset/six-balloons-hope.md
new file mode 100644
index 0000000..9a6ed4a
--- /dev/null
+++ b/.changeset/six-balloons-hope.md
@@ -0,0 +1,6 @@
+---
+"code-recall": patch
+"code-recall-tui": patch
+---
+
+feat: add code recall tui
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 67f6c05..d635da4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,7 +7,7 @@ on:
branches: [main]
jobs:
- test:
+ ci:
runs-on: ubuntu-latest
steps:
@@ -28,4 +28,4 @@ jobs:
run: bun run build
- name: Test
- run: bun test
+ run: bun run test
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 73762e1..2de16f3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,12 +2,17 @@ name: Release
on:
push:
- tags: [ v* ]
+ branches: [main]
+concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
- publish:
+ release:
+ name: Release
runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
steps:
- uses: actions/checkout@v4
@@ -21,9 +26,17 @@ jobs:
run: bun install
- name: Run tests
- run: bun test
+ run: bun run test
- - name: Publish to npm
- run: bun publish --access public
+ - name: Build packages
+ run: bun run build
+
+ - name: Create Release Pull Request or Publish
+ id: changesets
+ uses: changesets/action@v1
+ with:
+ version: bun run version
+ publish: bun run release
env:
- NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
index e33b810..8d939ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,36 +1,40 @@
-# dependencies (bun install)
-node_modules
-
-# output
-out
-dist
-*.tgz
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-# code coverage
-coverage
-*.lcov
-
-# logs
-logs
-_.log
-report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+# Dependencies
+node_modules
+.pnp
+.pnp.js
-# dotenv environment variable files
+# Local env files
.env
+.env.local
.env.development.local
.env.test.local
.env.production.local
-.env.local
-# caches
-.eslintcache
-.cache
-*.tsbuildinfo
+# Testing
+coverage
+
+# Turbo
+.turbo
+
+# Vercel
+.vercel
+
+# Build Outputs
+.next/
+out/
+build
+dist
+
-# IntelliJ based IDEs
-.idea
+# Debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
-# Finder (MacOS) folder config
+# Misc
.DS_Store
+*.pem
.code-recall/
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..24d7cc6
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 71c603b..7b46e28 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,7 @@
{
- "editor.defaultFormatter": "biomejs.biome",
- "editor.formatOnSave": true
+ "editor.defaultFormatter": "biomejs.biome",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports.biome": "explicit"
+ }
}
diff --git a/README.md b/README.md
index f303647..dc54721 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
-

-
+

**Semantic memory for AI coding agents**
@@ -11,353 +10,51 @@ Give your AI assistant persistent memory that learns from your project's history
[](https://bun.sh)
[](https://modelcontextprotocol.io)
-[Features](#features) • [Installation](#installation) • [Quick Start](#quick-start) • [Tools](#mcp-tools) • [Best Practices](#best-practices) • [Architecture](#architecture)
-
---
-## The Problem
-
-AI coding assistants are incredibly powerful, but they have a critical limitation: **they forget everything between sessions**. Every time you start a new conversation, your AI assistant has no memory of:
-
-- Past architectural decisions and *why* they were made
-- Patterns that work well in your codebase
-- Approaches that failed and should be avoided
-- Project-specific rules and guardrails
-
-You end up repeating context, re-explaining decisions, and sometimes watching the AI make the same mistakes twice.
-
-### Why not just use CLAUDE.md?
-
-The typical solution is adding context to `.md` files. **It doesn't scale:**
-
-- Context window has limits — your instructions compete with actual code
-- Loads everything even when you only need part of it
-- No semantic search ("state management" won't find "Zustand")
-- Can't learn from failures or evolve automatically
-
-## The Solution
-
-**code-recall** is an [MCP server](https://modelcontextprotocol.io) that gives AI agents persistent, semantic memory. It stores observations, decisions, and learnings in a local SQLite database with vector search capabilities, allowing your AI to:
-
-- **Remember** past decisions and their outcomes
-- **Learn** from failures (failed approaches are boosted in future searches)
-- **Follow rules** with semantic matching guardrails
-- **Understand** your code structure through tree-sitter analysis
-
-All data stays local on your machine. No cloud, no telemetry, fully private.
-
----
-
-## Features
+## Packages
-### Semantic Memory
-Store and retrieve observations using hybrid search that combines:
-- **Vector similarity** (embeddings) - finds conceptually related memories
-- **Full-text search** (BM25) - matches exact keywords
-- **Temporal decay** - recent memories rank higher
-- **Failure boost** - past failures surface prominently to prevent repeating mistakes
+This monorepo contains two independent packages:
-### Rules Engine
-Create guardrails that are matched semantically against actions:
-```
-Trigger: "adding API endpoint"
-→ Must do: ["Add rate limiting", "Write integration tests"]
-→ Must not: ["Skip authentication"]
-→ Ask first: ["Is this a breaking change?"]
-```
-
-### Code Analysis
-Parse TypeScript/JavaScript files with tree-sitter to extract:
-- Classes, methods, and functions
-- Interfaces and type aliases
-- Import statements
-- JSDoc documentation
+### [`code-recall`](./apps/server) - MCP Server
-Link memories to specific code entities for contextual recall.
+[](https://www.npmjs.com/package/code-recall)
----
-
-## Installation
-
-### Prerequisites
-
-- **[Bun](https://bun.sh)** v1.0 or higher
-- **macOS**: SQLite with extension support (via Homebrew: `brew install sqlite`)
-- **Linux**: Works out of the box
-
-### Install from npm
+The core MCP server that gives AI agents persistent, semantic memory. It stores observations, decisions, and learnings in a local SQLite database with vector search, full-text search, and a rules engine.
```bash
bun install -g code-recall
```
----
-
-## Quick Start
-
-### 1. Add to Claude Code
-
```bash
claude mcp add code-recall -- bunx code-recall
```
-### 2. Restart Claude Code
-
-The server starts automatically when you open Claude Code.
-
-### 3. Start Using Memory
-
-Once connected, Claude can store observations, search memories, check rules, and more using the [MCP tools](#mcp-tools) below.
-
-### Configuration with Other MCP Clients
-
-Add to your MCP client configuration:
-
-```json
-{
- "mcpServers": {
- "code-recall": {
- "command": "bunx",
- "args": ["code-recall"]
- }
- }
-}
-```
-
----
-
-## MCP Tools
-
-code-recall provides 8 tools that AI agents can use:
-
-### `store_observation`
-
-Store a new observation in memory with automatic conflict detection.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `category` | `decision` \| `pattern` \| `warning` \| `learning` | Type of observation |
-| `content` | `string` | The main content |
-| `rationale` | `string?` | Why this decision was made |
-| `tags` | `string[]?` | Tags for categorization |
-| `file_path` | `string?` | Associated file path |
-
-```json
-{
- "category": "decision",
- "content": "Use JWT for authentication",
- "rationale": "Stateless auth enables horizontal scaling",
- "tags": ["auth", "architecture"]
-}
-```
-
-### `search_memory`
-
-Search memories using semantic similarity.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `query` | `string` | Search query |
-| `limit` | `number?` | Max results (default: 10) |
-| `category` | `string?` | Filter by category |
-| `file_path` | `string?` | Filter by file path |
-
-### `get_briefing`
-
-Get a session start briefing with stats, warnings, and failed approaches.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `focus_areas` | `string[]?` | Topics to pre-fetch context for |
-
-### `set_rule`
-
-Create a new rule/guardrail with semantic trigger matching.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `trigger` | `string` | Action pattern that triggers this rule |
-| `must_do` | `string[]?` | Required actions |
-| `must_not` | `string[]?` | Forbidden actions |
-| `ask_first` | `string[]?` | Questions to consider |
-
-### `check_rules`
-
-Check which rules apply to an action.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `action` | `string` | The action you're about to take |
-
-### `record_outcome`
-
-Record whether a decision worked or failed.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `memory_id` | `number` | ID of the memory |
-| `outcome` | `string` | Description of what happened |
-| `worked` | `boolean` | Whether it was successful |
-
-### `list_rules`
-
-List all active rules. No parameters.
-
-### `analyze_structure`
-
-Analyze a source file and extract its code structure.
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `file_path` | `string` | Path to the file |
-| `include_types` | `string[]?` | Filter entity types |
-
----
-
-## Recommended Workflow
-
-```
-┌─────────────────────────────────────────────────────────┐
-│ SESSION START │
-│ │ │
-│ ▼ │
-│ get_briefing(focus_areas) │
-│ │ │
-└─────────────────────────┼───────────────────────────────┘
- │
-┌─────────────────────────┼───────────────────────────────┐
-│ BEFORE CHANGES │
-│ │ │
-│ ┌──────────────┴──────────────┐ │
-│ ▼ ▼ │
-│ check_rules(action) search_memory(query) │
-│ │
-└─────────────────────────────────────────────────────────┘
- │
-┌─────────────────────────┼───────────────────────────────┐
-│ AFTER DECISIONS │
-│ │ │
-│ ▼ │
-│ store_observation(category, content) │
-│ │
-└─────────────────────────────────────────────────────────┘
- │
-┌─────────────────────────┼───────────────────────────────┐
-│ AFTER IMPLEMENTATION │
-│ │ │
-│ ▼ │
-│ record_outcome(memory_id, worked) │
-│ │
-└─────────────────────────────────────────────────────────┘
-```
-
----
-
-## Best Practices
-
-### Claude Code Skill
+### [`code-recall-tui`](./apps/tui) - Terminal UI
-For optimal integration with Claude Code, add our Skill to your project. [Skills](https://code.claude.com/docs/en/skills) teach Claude when and how to use code-recall automatically.
+[](https://www.npmjs.com/package/code-recall-tui)
-**Install the Skill:**
+A terminal UI for browsing and exploring your code-recall database. View memories, rules, code entities, and search through everything your AI agent has learned.
```bash
-# From your project root
-mkdir -p .claude/skills/code-recall
-curl -o .claude/skills/code-recall/SKILL.md \
- https://raw.githubusercontent.com/AbianS/code-recall/main/.claude/skills/code-recall/SKILL.md
+bun install -g code-recall-tui
```
-Or create `.claude/skills/code-recall/SKILL.md` manually with [our skill template](https://github.com/AbianS/code-recall/blob/main/.claude/skills/code-recall/SKILL.md).
-
-### What the Skill Does
-
-Once installed, Claude will automatically:
-
-- **Session start**: Call `get_briefing` to load context and warnings
-- **Before changes**: Check `search_memory` and `check_rules` for relevant past decisions
-- **After decisions**: Store important choices with `store_observation`
-- **After implementation**: Record outcomes with `record_outcome`
-
-This creates a feedback loop where Claude learns from past successes and failures in your project.
-
----
-
-## Architecture
-
-```
-code-recall/
-├── src/
-│ ├── index.ts # Entry point
-│ ├── server.ts # MCP server and tools
-│ ├── database/
-│ │ ├── index.ts # DatabaseManager (SQLite + sqlite-vec)
-│ │ └── schema.ts # Database schema
-│ ├── memory/
-│ │ ├── index.ts # MemoryManager
-│ │ ├── embeddings.ts # Local embeddings (@xenova/transformers)
-│ │ └── search.ts # Hybrid search implementation
-│ ├── rules/
-│ │ └── index.ts # RulesEngine with semantic matching
-│ └── code/
-│ ├── index.ts # Code analysis coordinator
-│ ├── parser.ts # tree-sitter WASM parser
-│ └── extractors/ # Language-specific extractors
-├── tests/ # Comprehensive test suite
-└── .code-recall/ # Data directory (auto-created)
- └── memory.db # SQLite database
+```bash
+code-recall-tui
```
---
-## Technical Stack
-
-| Component | Technology |
-|-----------|------------|
-| **Runtime** | [Bun](https://bun.sh) |
-| **Database** | SQLite + [sqlite-vec](https://github.com/asg017/sqlite-vec) |
-| **Embeddings** | [@xenova/transformers](https://huggingface.co/docs/transformers.js) (all-MiniLM-L6-v2, 384d) |
-| **Full-text Search** | SQLite FTS5 |
-| **Code Parsing** | [web-tree-sitter](https://github.com/nickelproject/nickel/blob/main/packages/tree-sitter/README.md) |
-| **Protocol** | [MCP](https://modelcontextprotocol.io) (stdio transport) |
-
-### Search Algorithm
-
-The hybrid search combines multiple signals:
-
-| Signal | Weight | Description |
-|--------|--------|-------------|
-| Vector similarity | 50% | Cosine similarity of embeddings |
-| Full-text score | 30% | BM25 ranking |
-| Recency | 15% | Exponential time decay |
-| Failure boost | 5% | Failed decisions rank higher |
-
----
-
-## Development
-
-```bash
-# Run in development mode (with watch)
-bun run dev
-
-# Run tests
-bun test
-
-# Run specific test file
-bun test tests/memory/search.test.ts
-```
+## Quick Start
-### Test Coverage
+1. **Install the MCP server** and add it to your AI coding agent
+2. **Start coding** -- the agent will automatically store decisions, patterns, warnings, and learnings
+3. **Browse your data** with the TUI to see what your agent has learned
-The project includes 210+ tests covering:
-- Database operations and vector search
-- Embedding generation and similarity
-- Hybrid search algorithm
-- Rules engine semantic matching
-- Code analysis and extraction
-- MCP server tools
+All data stays local on your machine. No cloud, no telemetry, fully private.
---
@@ -371,12 +68,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
----
-
## License
MIT License - see [LICENSE](LICENSE) for details.
-
----
-
-with 💖 by AbianS
diff --git a/apps/server/.gitignore b/apps/server/.gitignore
new file mode 100644
index 0000000..ba79468
--- /dev/null
+++ b/apps/server/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
\ No newline at end of file
diff --git a/apps/server/README.md b/apps/server/README.md
new file mode 100644
index 0000000..4203205
--- /dev/null
+++ b/apps/server/README.md
@@ -0,0 +1,382 @@
+
+
+# code-recall
+
+**Semantic memory for AI coding agents**
+
+Give your AI assistant persistent memory that learns from your project's history.
+
+[](https://www.npmjs.com/package/code-recall)
+[](https://opensource.org/licenses/MIT)
+[](https://bun.sh)
+[](https://modelcontextprotocol.io)
+
+[Features](#features) • [Installation](#installation) • [Quick Start](#quick-start) • [Tools](#mcp-tools) • [Best Practices](#best-practices) • [Architecture](#architecture)
+
+
+
+---
+
+## The Problem
+
+AI coding assistants are incredibly powerful, but they have a critical limitation: **they forget everything between sessions**. Every time you start a new conversation, your AI assistant has no memory of:
+
+- Past architectural decisions and *why* they were made
+- Patterns that work well in your codebase
+- Approaches that failed and should be avoided
+- Project-specific rules and guardrails
+
+You end up repeating context, re-explaining decisions, and sometimes watching the AI make the same mistakes twice.
+
+### Why not just use CLAUDE.md?
+
+The typical solution is adding context to `.md` files. **It doesn't scale:**
+
+- Context window has limits — your instructions compete with actual code
+- Loads everything even when you only need part of it
+- No semantic search ("state management" won't find "Zustand")
+- Can't learn from failures or evolve automatically
+
+## The Solution
+
+**code-recall** is an [MCP server](https://modelcontextprotocol.io) that gives AI agents persistent, semantic memory. It stores observations, decisions, and learnings in a local SQLite database with vector search capabilities, allowing your AI to:
+
+- **Remember** past decisions and their outcomes
+- **Learn** from failures (failed approaches are boosted in future searches)
+- **Follow rules** with semantic matching guardrails
+- **Understand** your code structure through tree-sitter analysis
+
+All data stays local on your machine. No cloud, no telemetry, fully private.
+
+---
+
+## Features
+
+### Semantic Memory
+Store and retrieve observations using hybrid search that combines:
+- **Vector similarity** (embeddings) - finds conceptually related memories
+- **Full-text search** (BM25) - matches exact keywords
+- **Temporal decay** - recent memories rank higher
+- **Failure boost** - past failures surface prominently to prevent repeating mistakes
+
+### Rules Engine
+Create guardrails that are matched semantically against actions:
+```
+Trigger: "adding API endpoint"
+→ Must do: ["Add rate limiting", "Write integration tests"]
+→ Must not: ["Skip authentication"]
+→ Ask first: ["Is this a breaking change?"]
+```
+
+### Code Analysis
+Parse TypeScript/JavaScript files with tree-sitter to extract:
+- Classes, methods, and functions
+- Interfaces and type aliases
+- Import statements
+- JSDoc documentation
+
+Link memories to specific code entities for contextual recall.
+
+---
+
+## Installation
+
+### Prerequisites
+
+- **[Bun](https://bun.sh)** v1.0 or higher
+- **macOS**: SQLite with extension support (via Homebrew: `brew install sqlite`)
+- **Linux**: Works out of the box
+
+### Install from npm
+
+```bash
+bun install -g code-recall
+```
+
+---
+
+## Quick Start
+
+### 1. Add to Claude Code
+
+```bash
+claude mcp add code-recall -- bunx code-recall
+```
+
+### 2. Restart Claude Code
+
+The server starts automatically when you open Claude Code.
+
+### 3. Start Using Memory
+
+Once connected, Claude can store observations, search memories, check rules, and more using the [MCP tools](#mcp-tools) below.
+
+### Configuration with Other MCP Clients
+
+Add to your MCP client configuration:
+
+```json
+{
+ "mcpServers": {
+ "code-recall": {
+ "command": "bunx",
+ "args": ["code-recall"]
+ }
+ }
+}
+```
+
+---
+
+## MCP Tools
+
+code-recall provides 8 tools that AI agents can use:
+
+### `store_observation`
+
+Store a new observation in memory with automatic conflict detection.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `category` | `decision` \| `pattern` \| `warning` \| `learning` | Type of observation |
+| `content` | `string` | The main content |
+| `rationale` | `string?` | Why this decision was made |
+| `tags` | `string[]?` | Tags for categorization |
+| `file_path` | `string?` | Associated file path |
+
+```json
+{
+ "category": "decision",
+ "content": "Use JWT for authentication",
+ "rationale": "Stateless auth enables horizontal scaling",
+ "tags": ["auth", "architecture"]
+}
+```
+
+### `search_memory`
+
+Search memories using semantic similarity.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `query` | `string` | Search query |
+| `limit` | `number?` | Max results (default: 10) |
+| `category` | `string?` | Filter by category |
+| `file_path` | `string?` | Filter by file path |
+
+### `get_briefing`
+
+Get a session start briefing with stats, warnings, and failed approaches.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `focus_areas` | `string[]?` | Topics to pre-fetch context for |
+
+### `set_rule`
+
+Create a new rule/guardrail with semantic trigger matching.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `trigger` | `string` | Action pattern that triggers this rule |
+| `must_do` | `string[]?` | Required actions |
+| `must_not` | `string[]?` | Forbidden actions |
+| `ask_first` | `string[]?` | Questions to consider |
+
+### `check_rules`
+
+Check which rules apply to an action.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `action` | `string` | The action you're about to take |
+
+### `record_outcome`
+
+Record whether a decision worked or failed.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `memory_id` | `number` | ID of the memory |
+| `outcome` | `string` | Description of what happened |
+| `worked` | `boolean` | Whether it was successful |
+
+### `list_rules`
+
+List all active rules. No parameters.
+
+### `analyze_structure`
+
+Analyze a source file and extract its code structure.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `file_path` | `string` | Path to the file |
+| `include_types` | `string[]?` | Filter entity types |
+
+---
+
+## Recommended Workflow
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ SESSION START │
+│ │ │
+│ ▼ │
+│ get_briefing(focus_areas) │
+│ │ │
+└─────────────────────────┼───────────────────────────────┘
+ │
+┌─────────────────────────┼───────────────────────────────┐
+│ BEFORE CHANGES │
+│ │ │
+│ ┌──────────────┴──────────────┐ │
+│ ▼ ▼ │
+│ check_rules(action) search_memory(query) │
+│ │
+└─────────────────────────────────────────────────────────┘
+ │
+┌─────────────────────────┼───────────────────────────────┐
+│ AFTER DECISIONS │
+│ │ │
+│ ▼ │
+│ store_observation(category, content) │
+│ │
+└─────────────────────────────────────────────────────────┘
+ │
+┌─────────────────────────┼───────────────────────────────┐
+│ AFTER IMPLEMENTATION │
+│ │ │
+│ ▼ │
+│ record_outcome(memory_id, worked) │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Best Practices
+
+### Claude Code Skill
+
+For optimal integration with Claude Code, add our Skill to your project. [Skills](https://code.claude.com/docs/en/skills) teach Claude when and how to use code-recall automatically.
+
+**Install the Skill:**
+
+```bash
+# From your project root
+mkdir -p .claude/skills/code-recall
+curl -o .claude/skills/code-recall/SKILL.md \
+ https://raw.githubusercontent.com/AbianS/code-recall/main/.claude/skills/code-recall/SKILL.md
+```
+
+Or create `.claude/skills/code-recall/SKILL.md` manually with [our skill template](https://github.com/AbianS/code-recall/blob/main/.claude/skills/code-recall/SKILL.md).
+
+### What the Skill Does
+
+Once installed, Claude will automatically:
+
+- **Session start**: Call `get_briefing` to load context and warnings
+- **Before changes**: Check `search_memory` and `check_rules` for relevant past decisions
+- **After decisions**: Store important choices with `store_observation`
+- **After implementation**: Record outcomes with `record_outcome`
+
+This creates a feedback loop where Claude learns from past successes and failures in your project.
+
+---
+
+## Architecture
+
+```
+code-recall/
+├── src/
+│ ├── index.ts # Entry point
+│ ├── server.ts # MCP server and tools
+│ ├── database/
+│ │ ├── index.ts # DatabaseManager (SQLite + sqlite-vec)
+│ │ └── schema.ts # Database schema
+│ ├── memory/
+│ │ ├── index.ts # MemoryManager
+│ │ ├── embeddings.ts # Local embeddings (@xenova/transformers)
+│ │ └── search.ts # Hybrid search implementation
+│ ├── rules/
+│ │ └── index.ts # RulesEngine with semantic matching
+│ └── code/
+│ ├── index.ts # Code analysis coordinator
+│ ├── parser.ts # tree-sitter WASM parser
+│ └── extractors/ # Language-specific extractors
+├── tests/ # Comprehensive test suite
+└── .code-recall/ # Data directory (auto-created)
+ └── memory.db # SQLite database
+```
+
+---
+
+## Technical Stack
+
+| Component | Technology |
+|-----------|------------|
+| **Runtime** | [Bun](https://bun.sh) |
+| **Database** | SQLite + [sqlite-vec](https://github.com/asg017/sqlite-vec) |
+| **Embeddings** | [@xenova/transformers](https://huggingface.co/docs/transformers.js) (all-MiniLM-L6-v2, 384d) |
+| **Full-text Search** | SQLite FTS5 |
+| **Code Parsing** | [web-tree-sitter](https://github.com/nickelproject/nickel/blob/main/packages/tree-sitter/README.md) |
+| **Protocol** | [MCP](https://modelcontextprotocol.io) (stdio transport) |
+
+### Search Algorithm
+
+The hybrid search combines multiple signals:
+
+| Signal | Weight | Description |
+|--------|--------|-------------|
+| Vector similarity | 50% | Cosine similarity of embeddings |
+| Full-text score | 30% | BM25 ranking |
+| Recency | 15% | Exponential time decay |
+| Failure boost | 5% | Failed decisions rank higher |
+
+---
+
+## Development
+
+```bash
+# Run in development mode (with watch)
+bun run dev
+
+# Run tests
+bun test
+
+# Run specific test file
+bun test tests/memory/search.test.ts
+```
+
+### Test Coverage
+
+The project includes 210+ tests covering:
+- Database operations and vector search
+- Embedding generation and similarity
+- Hybrid search algorithm
+- Rules engine semantic matching
+- Code analysis and extraction
+- MCP server tools
+
+---
+
+## Contributing
+
+Contributions are welcome! Please feel free to submit a Pull Request.
+
+1. Fork the repository
+2. Create your feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'Add amazing feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+---
+
+## License
+
+MIT License - see [LICENSE](LICENSE) for details.
+
+---
+
+with 💖 by AbianS
diff --git a/apps/server/package.json b/apps/server/package.json
new file mode 100644
index 0000000..cfab747
--- /dev/null
+++ b/apps/server/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "code-recall",
+ "version": "1.0.0",
+ "description": "Ultra-fast MCP server for semantic memory and code analysis",
+ "module": "src/index.ts",
+ "type": "module",
+ "bin": {
+ "code-recall": "src/index.ts"
+ },
+ "files": [
+ "src",
+ "README.md",
+ "LICENSE"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/AbianS/code-recall.git"
+ },
+ "keywords": [
+ "mcp",
+ "memory",
+ "ai",
+ "coding-agent",
+ "semantic-search",
+ "sqlite",
+ "bun"
+ ],
+ "author": "AbianS",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/AbianS/code-recall/issues"
+ },
+ "homepage": "https://github.com/AbianS/code-recall#readme",
+ "engines": {
+ "bun": ">=1.0.0"
+ },
+ "scripts": {
+ "start": "bun --bun run src/index.ts",
+ "dev": "bun --bun --watch run src/index.ts",
+ "build": "bun --bun build src/index.ts --outdir dist --target bun",
+ "test": "bun --bun test",
+ "lint": "biome check src tests",
+ "lint:fix": "biome check --write src tests"
+ },
+ "dependencies": {
+ "@modelcontextprotocol/sdk": "1.25.2",
+ "@xenova/transformers": "2.17.2",
+ "sqlite-vec": "0.1.7-alpha.2",
+ "tree-sitter-javascript": "0.25.0",
+ "tree-sitter-typescript": "0.23.2",
+ "web-tree-sitter": "0.26.3",
+ "zod": "3.25.76"
+ },
+ "devDependencies": {
+ "@types/bun": "1.3.6"
+ },
+ "peerDependencies": {
+ "typescript": "5.9.3"
+ }
+}
diff --git a/src/code/extractors/javascript.ts b/apps/server/src/code/extractors/javascript.ts
similarity index 100%
rename from src/code/extractors/javascript.ts
rename to apps/server/src/code/extractors/javascript.ts
diff --git a/src/code/extractors/typescript.ts b/apps/server/src/code/extractors/typescript.ts
similarity index 100%
rename from src/code/extractors/typescript.ts
rename to apps/server/src/code/extractors/typescript.ts
diff --git a/src/code/index.ts b/apps/server/src/code/index.ts
similarity index 100%
rename from src/code/index.ts
rename to apps/server/src/code/index.ts
diff --git a/src/code/parser.ts b/apps/server/src/code/parser.ts
similarity index 100%
rename from src/code/parser.ts
rename to apps/server/src/code/parser.ts
diff --git a/src/code/types.ts b/apps/server/src/code/types.ts
similarity index 100%
rename from src/code/types.ts
rename to apps/server/src/code/types.ts
diff --git a/src/database/index.ts b/apps/server/src/database/index.ts
similarity index 100%
rename from src/database/index.ts
rename to apps/server/src/database/index.ts
diff --git a/src/database/migrations/0001_initial.ts b/apps/server/src/database/migrations/0001_initial.ts
similarity index 100%
rename from src/database/migrations/0001_initial.ts
rename to apps/server/src/database/migrations/0001_initial.ts
diff --git a/src/database/migrations/index.ts b/apps/server/src/database/migrations/index.ts
similarity index 100%
rename from src/database/migrations/index.ts
rename to apps/server/src/database/migrations/index.ts
diff --git a/src/database/migrations/types.ts b/apps/server/src/database/migrations/types.ts
similarity index 100%
rename from src/database/migrations/types.ts
rename to apps/server/src/database/migrations/types.ts
diff --git a/src/database/migrations/utils.ts b/apps/server/src/database/migrations/utils.ts
similarity index 100%
rename from src/database/migrations/utils.ts
rename to apps/server/src/database/migrations/utils.ts
diff --git a/src/database/schema.ts b/apps/server/src/database/schema.ts
similarity index 100%
rename from src/database/schema.ts
rename to apps/server/src/database/schema.ts
diff --git a/src/index.ts b/apps/server/src/index.ts
old mode 100644
new mode 100755
similarity index 100%
rename from src/index.ts
rename to apps/server/src/index.ts
diff --git a/src/memory/embeddings.ts b/apps/server/src/memory/embeddings.ts
similarity index 100%
rename from src/memory/embeddings.ts
rename to apps/server/src/memory/embeddings.ts
diff --git a/src/memory/index.ts b/apps/server/src/memory/index.ts
similarity index 100%
rename from src/memory/index.ts
rename to apps/server/src/memory/index.ts
diff --git a/src/memory/search.ts b/apps/server/src/memory/search.ts
similarity index 100%
rename from src/memory/search.ts
rename to apps/server/src/memory/search.ts
diff --git a/src/rules/index.ts b/apps/server/src/rules/index.ts
similarity index 100%
rename from src/rules/index.ts
rename to apps/server/src/rules/index.ts
diff --git a/src/server.ts b/apps/server/src/server.ts
similarity index 100%
rename from src/server.ts
rename to apps/server/src/server.ts
diff --git a/tests/code/analyzer.test.ts b/apps/server/tests/code/analyzer.test.ts
similarity index 100%
rename from tests/code/analyzer.test.ts
rename to apps/server/tests/code/analyzer.test.ts
diff --git a/tests/code/javascript.test.ts b/apps/server/tests/code/javascript.test.ts
similarity index 100%
rename from tests/code/javascript.test.ts
rename to apps/server/tests/code/javascript.test.ts
diff --git a/tests/code/parser.test.ts b/apps/server/tests/code/parser.test.ts
similarity index 100%
rename from tests/code/parser.test.ts
rename to apps/server/tests/code/parser.test.ts
diff --git a/tests/code/typescript.test.ts b/apps/server/tests/code/typescript.test.ts
similarity index 100%
rename from tests/code/typescript.test.ts
rename to apps/server/tests/code/typescript.test.ts
diff --git a/tests/database/entities.test.ts b/apps/server/tests/database/entities.test.ts
similarity index 100%
rename from tests/database/entities.test.ts
rename to apps/server/tests/database/entities.test.ts
diff --git a/tests/database/manager.test.ts b/apps/server/tests/database/manager.test.ts
similarity index 100%
rename from tests/database/manager.test.ts
rename to apps/server/tests/database/manager.test.ts
diff --git a/tests/database/migrations.test.ts b/apps/server/tests/database/migrations.test.ts
similarity index 100%
rename from tests/database/migrations.test.ts
rename to apps/server/tests/database/migrations.test.ts
diff --git a/tests/database/vectors.test.ts b/apps/server/tests/database/vectors.test.ts
similarity index 100%
rename from tests/database/vectors.test.ts
rename to apps/server/tests/database/vectors.test.ts
diff --git a/tests/memory/embeddings.test.ts b/apps/server/tests/memory/embeddings.test.ts
similarity index 100%
rename from tests/memory/embeddings.test.ts
rename to apps/server/tests/memory/embeddings.test.ts
diff --git a/tests/memory/manager.test.ts b/apps/server/tests/memory/manager.test.ts
similarity index 100%
rename from tests/memory/manager.test.ts
rename to apps/server/tests/memory/manager.test.ts
diff --git a/tests/memory/search.test.ts b/apps/server/tests/memory/search.test.ts
similarity index 100%
rename from tests/memory/search.test.ts
rename to apps/server/tests/memory/search.test.ts
diff --git a/tests/rules/engine.test.ts b/apps/server/tests/rules/engine.test.ts
similarity index 100%
rename from tests/rules/engine.test.ts
rename to apps/server/tests/rules/engine.test.ts
diff --git a/tests/server/tools.test.ts b/apps/server/tests/server/tools.test.ts
similarity index 100%
rename from tests/server/tools.test.ts
rename to apps/server/tests/server/tools.test.ts
diff --git a/tests/setup.ts b/apps/server/tests/setup.ts
similarity index 100%
rename from tests/setup.ts
rename to apps/server/tests/setup.ts
diff --git a/tsconfig.json b/apps/server/tsconfig.json
similarity index 100%
rename from tsconfig.json
rename to apps/server/tsconfig.json
diff --git a/apps/tui/README.md b/apps/tui/README.md
new file mode 100644
index 0000000..38cd497
--- /dev/null
+++ b/apps/tui/README.md
@@ -0,0 +1,127 @@
+
+
+# code-recall-tui
+
+**Terminal UI for browsing your code-recall database**
+
+Explore memories, rules, code entities, and search through everything your AI agent has learned.
+
+[](https://www.npmjs.com/package/code-recall-tui)
+[](https://opensource.org/licenses/MIT)
+[](https://bun.sh)
+
+
+
+---
+
+## Installation
+
+```bash
+bun install -g code-recall-tui
+```
+
+## Usage
+
+Run from the root of a project that has a `.code-recall/memory.db` file:
+
+```bash
+code-recall-tui
+```
+
+Or specify a project path:
+
+```bash
+code-recall-tui --project /path/to/your/project
+```
+
+You can also set the project path via environment variable:
+
+```bash
+CODE_RECALL_PROJECT_PATH=/path/to/project code-recall-tui
+```
+
+---
+
+## Screens
+
+### Dashboard
+
+Overview of your code-recall data at a glance:
+- Total memories, rules, failed decisions, and warnings
+- Category breakdown (decisions, patterns, warnings, learnings)
+- Recent activity feed
+- Failed approaches panel
+
+### Memories
+
+Browse all stored memories with category filtering:
+- Filter by: All, Decisions, Patterns, Warnings, Learnings
+- Navigate tabs with `[` and `]`
+- Press Enter to view full memory details
+
+### Memory Detail
+
+Full view of a single memory including:
+- Category and outcome status
+- Content, rationale, and context
+- Tags, file path, and recorded outcome
+
+### Rules
+
+Browse all active rules/guardrails with their trigger patterns and constraint counts.
+
+### Rule Detail
+
+Full view of a rule showing:
+- Trigger pattern
+- Must Do constraints
+- Must Not constraints
+- Ask First questions
+
+### Code Entities
+
+Browse analyzed source files and their extracted code structure:
+- Split view: file list + entity details
+- Color-coded entity types (classes, functions, interfaces, etc.)
+- Signatures and line ranges
+
+### Search
+
+Full-text search across all memories using SQLite FTS5.
+
+---
+
+## Keyboard Shortcuts
+
+| Key | Action |
+|-----|--------|
+| `1` | Go to Dashboard |
+| `2` | Go to Memories |
+| `3` | Go to Rules |
+| `4` | Go to Code Entities |
+| `/` | Go to Search |
+| `Escape` | Go back |
+| `Enter` | Select / Open detail |
+| `Tab` | Toggle panel focus (where applicable) |
+| `[` / `]` | Switch tabs (Memories screen) |
+| `q` | Quit |
+
+---
+
+## Requirements
+
+- **[Bun](https://bun.sh)** v1.0 or higher
+- An existing `.code-recall/memory.db` database (created by the [code-recall](https://www.npmjs.com/package/code-recall) MCP server)
+- **macOS**: SQLite with extension support (via Homebrew: `brew install sqlite`)
+
+---
+
+## How It Works
+
+`code-recall-tui` opens your `.code-recall/memory.db` database in **read-only mode** -- it never modifies your data. It's a standalone viewer that is completely independent from the code-recall MCP server.
+
+---
+
+## License
+
+MIT License - see [LICENSE](../../LICENSE) for details.
diff --git a/apps/tui/package.json b/apps/tui/package.json
new file mode 100644
index 0000000..0143f88
--- /dev/null
+++ b/apps/tui/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "code-recall-tui",
+ "version": "1.0.0",
+ "description": "Terminal UI for browsing code-recall memories, rules, and code entities",
+ "type": "module",
+ "main": "dist/index.js",
+ "bin": {
+ "code-recall-tui": "dist/index.js"
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "start": "bun run src/index.ts",
+ "dev": "bun --watch run src/index.ts",
+ "build": "bun build src/index.ts --outdir dist --target bun",
+ "lint": "biome check src",
+ "lint:fix": "biome check --write src"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/AbianS/code-recall.git",
+ "directory": "apps/tui"
+ },
+ "keywords": [
+ "code-recall",
+ "tui",
+ "terminal",
+ "cli",
+ "memory",
+ "ai",
+ "mcp",
+ "sqlite",
+ "bun"
+ ],
+ "author": "AbianS",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/AbianS/code-recall/issues"
+ },
+ "homepage": "https://github.com/AbianS/code-recall/tree/main/apps/tui#readme",
+ "engines": {
+ "bun": ">=1.0.0"
+ },
+ "dependencies": {
+ "@opentui/core": "0.1.75"
+ },
+ "devDependencies": {
+ "@types/bun": "1.3.6"
+ },
+ "peerDependencies": {
+ "typescript": "5.9.3"
+ }
+}
diff --git a/apps/tui/src/app.ts b/apps/tui/src/app.ts
new file mode 100644
index 0000000..2d15f0d
--- /dev/null
+++ b/apps/tui/src/app.ts
@@ -0,0 +1,200 @@
+import type { CliRenderer, KeyEvent } from "@opentui/core";
+import { BoxRenderable } from "@opentui/core";
+import { createHeader } from "./components/header.ts";
+import { createStatusBar } from "./components/status-bar.ts";
+import type { DataStore } from "./data/store.ts";
+import { DashboardScreen } from "./screens/dashboard.ts";
+import { EntitiesScreen } from "./screens/entities.ts";
+import { MemoriesScreen } from "./screens/memories.ts";
+import { MemoryDetailScreen } from "./screens/memory-detail.ts";
+import { RuleDetailScreen } from "./screens/rule-detail.ts";
+import { RulesScreen } from "./screens/rules.ts";
+import { SearchScreen } from "./screens/search.ts";
+import { BG_PRIMARY } from "./theme.ts";
+import type {
+ AppContext,
+ NavigateParams,
+ Screen,
+ ScreenEntry,
+ ScreenName,
+} from "./types.ts";
+
+const SCREEN_LABELS: Record = {
+ dashboard: "Dashboard",
+ memories: "Memories",
+ "memory-detail": "Memory Detail",
+ rules: "Rules",
+ "rule-detail": "Rule Detail",
+ entities: "Code Entities",
+ search: "Search",
+};
+
+const SCREEN_HINTS: Partial> = {
+ "memory-detail": "Esc:Back j/k:Scroll",
+ "rule-detail": "Esc:Back j/k:Scroll",
+};
+
+export class App implements AppContext {
+ renderer: CliRenderer;
+ store: DataStore;
+
+ private header: ReturnType;
+ private statusBar: ReturnType;
+ private contentArea: BoxRenderable;
+ private currentScreen: Screen | null = null;
+ private history: ScreenEntry[] = [];
+
+ constructor(renderer: CliRenderer, store: DataStore, projectPath: string) {
+ this.renderer = renderer;
+ this.store = store;
+
+ // Build persistent layout frame
+ const root = new BoxRenderable(renderer, {
+ id: "root",
+ width: "100%",
+ height: "100%",
+ flexDirection: "column",
+ backgroundColor: BG_PRIMARY,
+ });
+
+ this.header = createHeader(renderer, projectPath);
+ this.contentArea = new BoxRenderable(renderer, {
+ id: "content-area",
+ flexGrow: 1,
+ width: "100%",
+ flexDirection: "column",
+ backgroundColor: BG_PRIMARY,
+ });
+ this.statusBar = createStatusBar(renderer);
+
+ root.add(this.header.renderable);
+ root.add(this.contentArea);
+ root.add(this.statusBar.renderable);
+
+ renderer.root.add(root);
+
+ // Global keyboard handler
+ renderer.keyInput.on("keypress", (key: KeyEvent) => {
+ this.handleGlobalKey(key);
+ });
+ }
+
+ start() {
+ this.navigateTo("dashboard");
+ }
+
+ navigateTo(name: ScreenName, params?: NavigateParams) {
+ // Push current to history (but not if going back)
+ if (this.currentScreen) {
+ this.history.push({
+ name: this.currentScreen.name,
+ });
+ if (this.history.length > 10) {
+ this.history.shift();
+ }
+ }
+
+ this.switchScreen(name, params);
+ }
+
+ goBack() {
+ const prev = this.history.pop();
+ if (prev) {
+ this.switchScreen(prev.name, prev.params);
+ }
+ }
+
+ private switchScreen(name: ScreenName, params?: NavigateParams) {
+ // Destroy current screen
+ if (this.currentScreen) {
+ this.currentScreen.destroy();
+ this.currentScreen = null;
+ }
+
+ // Clear content area children
+ for (const child of this.contentArea.getChildren()) {
+ child.destroy();
+ }
+ const screen = this.createScreen(name, params);
+ this.currentScreen = screen;
+
+ // Mount
+ screen.mount(this.contentArea);
+
+ // Update chrome
+ this.header.update(SCREEN_LABELS[name] ?? name);
+ this.statusBar.update(SCREEN_HINTS[name]);
+ }
+
+ private createScreen(name: ScreenName, params?: NavigateParams): Screen {
+ switch (name) {
+ case "dashboard":
+ return new DashboardScreen(this);
+ case "memories":
+ return new MemoriesScreen(this, params);
+ case "memory-detail":
+ return new MemoryDetailScreen(this, params!);
+ case "rules":
+ return new RulesScreen(this);
+ case "rule-detail":
+ return new RuleDetailScreen(this, params!);
+ case "entities":
+ return new EntitiesScreen(this);
+ case "search":
+ return new SearchScreen(this, params);
+ default:
+ return new DashboardScreen(this);
+ }
+ }
+
+ private handleGlobalKey(key: KeyEvent) {
+ // Let the current screen handle it first
+ if (this.currentScreen?.onKeypress?.(key)) {
+ return;
+ }
+
+ // Don't process global shortcuts when an input is focused
+ if (
+ this.renderer.currentFocusedRenderable &&
+ "placeholder" in this.renderer.currentFocusedRenderable
+ ) {
+ return;
+ }
+
+ switch (key.name) {
+ case "1":
+ if (this.currentScreen?.name !== "dashboard") {
+ this.navigateTo("dashboard");
+ }
+ break;
+ case "2":
+ if (this.currentScreen?.name !== "memories") {
+ this.navigateTo("memories");
+ }
+ break;
+ case "3":
+ if (this.currentScreen?.name !== "rules") {
+ this.navigateTo("rules");
+ }
+ break;
+ case "4":
+ if (this.currentScreen?.name !== "entities") {
+ this.navigateTo("entities");
+ }
+ break;
+ case "/":
+ if (this.currentScreen?.name !== "search") {
+ this.navigateTo("search");
+ }
+ break;
+ case "q":
+ this.store.close();
+ this.renderer.destroy();
+ process.exit(0);
+ break;
+ case "escape":
+ this.goBack();
+ break;
+ }
+ }
+}
diff --git a/apps/tui/src/components/category-badge.ts b/apps/tui/src/components/category-badge.ts
new file mode 100644
index 0000000..ff31372
--- /dev/null
+++ b/apps/tui/src/components/category-badge.ts
@@ -0,0 +1,10 @@
+import { CATEGORY_COLORS, CATEGORY_ICONS, FG_SECONDARY } from "../theme.ts";
+
+export function categoryLabel(category: string): string {
+ const icon = CATEGORY_ICONS[category] ?? "\u25cb";
+ return `${icon} ${category.toUpperCase()}`;
+}
+
+export function categoryColor(category: string): string {
+ return CATEGORY_COLORS[category] ?? FG_SECONDARY;
+}
diff --git a/apps/tui/src/components/empty-state.ts b/apps/tui/src/components/empty-state.ts
new file mode 100644
index 0000000..a01676b
--- /dev/null
+++ b/apps/tui/src/components/empty-state.ts
@@ -0,0 +1,22 @@
+import type { CliRenderer } from "@opentui/core";
+import { BoxRenderable, TextRenderable } from "@opentui/core";
+import { FG_MUTED } from "../theme.ts";
+
+export function createEmptyState(renderer: CliRenderer, message: string) {
+ const box = new BoxRenderable(renderer, {
+ id: "empty-state",
+ width: "100%",
+ flexGrow: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ });
+
+ const text = new TextRenderable(renderer, {
+ content: message,
+ fg: FG_MUTED,
+ attributes: 4, // ITALIC
+ });
+
+ box.add(text);
+ return box;
+}
diff --git a/apps/tui/src/components/header.ts b/apps/tui/src/components/header.ts
new file mode 100644
index 0000000..28649da
--- /dev/null
+++ b/apps/tui/src/components/header.ts
@@ -0,0 +1,59 @@
+import type { CliRenderer } from "@opentui/core";
+import { BoxRenderable, TextRenderable } from "@opentui/core";
+import {
+ BG_SECONDARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ FG_PRIMARY,
+ FG_SECONDARY,
+} from "../theme.ts";
+
+export function createHeader(renderer: CliRenderer, projectPath: string) {
+ const container = new BoxRenderable(renderer, {
+ id: "header",
+ height: 3,
+ width: "100%",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ backgroundColor: BG_SECONDARY,
+ border: true,
+ borderStyle: "heavy",
+ borderColor: COLOR_BORDER,
+ paddingLeft: 1,
+ paddingRight: 1,
+ });
+
+ const title = new TextRenderable(renderer, {
+ id: "header-title",
+ content: " code-recall ",
+ fg: COLOR_ACCENT,
+ attributes: 1, // BOLD
+ });
+
+ const breadcrumb = new TextRenderable(renderer, {
+ id: "header-breadcrumb",
+ content: "Dashboard",
+ fg: FG_PRIMARY,
+ });
+
+ const truncatedPath =
+ projectPath.length > 40 ? `...${projectPath.slice(-37)}` : projectPath;
+ const path = new TextRenderable(renderer, {
+ id: "header-path",
+ content: truncatedPath,
+ fg: FG_SECONDARY,
+ attributes: 4, // ITALIC
+ });
+
+ container.add(title);
+ container.add(breadcrumb);
+ container.add(path);
+
+ return {
+ renderable: container,
+ update(screenName: string) {
+ breadcrumb.content = screenName;
+ },
+ };
+}
diff --git a/apps/tui/src/components/memory-card.ts b/apps/tui/src/components/memory-card.ts
new file mode 100644
index 0000000..ae89344
--- /dev/null
+++ b/apps/tui/src/components/memory-card.ts
@@ -0,0 +1,29 @@
+import type { MemoryRow } from "../types.ts";
+import { categoryLabel } from "./category-badge.ts";
+import { outcomeLabel } from "./outcome-badge.ts";
+
+export function formatMemoryOption(memory: MemoryRow) {
+ const badge = categoryLabel(memory.category);
+ const content =
+ memory.content.length > 60
+ ? `${memory.content.slice(0, 57)}...`
+ : memory.content;
+
+ const parts: string[] = [];
+ if (memory.tags) {
+ try {
+ const tags = JSON.parse(memory.tags) as string[];
+ if (tags.length > 0) parts.push(`Tags: ${tags.join(", ")}`);
+ } catch {}
+ }
+ if (memory.file_path) {
+ parts.push(`File: ${memory.file_path}`);
+ }
+ parts.push(outcomeLabel(memory.worked));
+
+ return {
+ name: `[${badge}] ${content}`,
+ description: parts.join(" | "),
+ value: memory.id,
+ };
+}
diff --git a/apps/tui/src/components/outcome-badge.ts b/apps/tui/src/components/outcome-badge.ts
new file mode 100644
index 0000000..2f95cd7
--- /dev/null
+++ b/apps/tui/src/components/outcome-badge.ts
@@ -0,0 +1,18 @@
+import {
+ COLOR_FAILURE,
+ COLOR_PENDING,
+ COLOR_SUCCESS,
+ OUTCOME_ICONS,
+} from "../theme.ts";
+
+export function outcomeLabel(worked: number | null): string {
+ if (worked === 1) return `${OUTCOME_ICONS.worked} WORKED`;
+ if (worked === 0) return `${OUTCOME_ICONS.failed} FAILED`;
+ return `${OUTCOME_ICONS.pending} PENDING`;
+}
+
+export function outcomeColor(worked: number | null): string {
+ if (worked === 1) return COLOR_SUCCESS;
+ if (worked === 0) return COLOR_FAILURE;
+ return COLOR_PENDING;
+}
diff --git a/apps/tui/src/components/rule-card.ts b/apps/tui/src/components/rule-card.ts
new file mode 100644
index 0000000..85ad45e
--- /dev/null
+++ b/apps/tui/src/components/rule-card.ts
@@ -0,0 +1,28 @@
+import type { RuleRow } from "../types.ts";
+
+function parseJsonArray(raw: string | null): string[] {
+ if (!raw) return [];
+ try {
+ const parsed = JSON.parse(raw);
+ return Array.isArray(parsed) ? parsed : [];
+ } catch {
+ return [];
+ }
+}
+
+export function formatRuleOption(rule: RuleRow) {
+ const mustDo = parseJsonArray(rule.must_do);
+ const mustNot = parseJsonArray(rule.must_not);
+ const askFirst = parseJsonArray(rule.ask_first);
+
+ const parts: string[] = [];
+ if (mustDo.length > 0) parts.push(`+ ${mustDo.length} must-do`);
+ if (mustNot.length > 0) parts.push(`- ${mustNot.length} must-not`);
+ if (askFirst.length > 0) parts.push(`? ${askFirst.length} ask-first`);
+
+ return {
+ name: `"${rule.trigger}"`,
+ description: parts.join(" | ") || "No constraints defined",
+ value: rule.id,
+ };
+}
diff --git a/apps/tui/src/components/stat-box.ts b/apps/tui/src/components/stat-box.ts
new file mode 100644
index 0000000..b71b753
--- /dev/null
+++ b/apps/tui/src/components/stat-box.ts
@@ -0,0 +1,44 @@
+import type { CliRenderer } from "@opentui/core";
+import { BoxRenderable, TextRenderable } from "@opentui/core";
+import {
+ BG_SECONDARY,
+ COLOR_BORDER,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+
+export function createStatBox(
+ renderer: CliRenderer,
+ opts: { value: number; label: string; color: string },
+) {
+ const box = new BoxRenderable(renderer, {
+ id: `stat-${opts.label.toLowerCase().replace(/\s+/g, "-")}`,
+ flexGrow: 1,
+ height: 6,
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ backgroundColor: BG_SECONDARY,
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ paddingLeft: 1,
+ paddingRight: 1,
+ });
+
+ const value = new TextRenderable(renderer, {
+ content: String(opts.value),
+ fg: opts.color,
+ attributes: 1, // BOLD
+ });
+
+ const label = new TextRenderable(renderer, {
+ content: opts.label,
+ fg: FG_SECONDARY,
+ });
+
+ box.add(value);
+ box.add(label);
+
+ return box;
+}
diff --git a/apps/tui/src/components/status-bar.ts b/apps/tui/src/components/status-bar.ts
new file mode 100644
index 0000000..553ebb4
--- /dev/null
+++ b/apps/tui/src/components/status-bar.ts
@@ -0,0 +1,34 @@
+import type { CliRenderer } from "@opentui/core";
+import { BoxRenderable, TextRenderable } from "@opentui/core";
+import { BG_TERTIARY, FG_SECONDARY } from "../theme.ts";
+
+const DEFAULT_HINTS =
+ "1:Dashboard 2:Memories 3:Rules 4:Entities /:Search q:Quit";
+
+export function createStatusBar(renderer: CliRenderer) {
+ const container = new BoxRenderable(renderer, {
+ id: "status-bar",
+ height: 1,
+ width: "100%",
+ flexDirection: "row",
+ alignItems: "center",
+ backgroundColor: BG_TERTIARY,
+ paddingLeft: 1,
+ paddingRight: 1,
+ });
+
+ const hints = new TextRenderable(renderer, {
+ id: "status-hints",
+ content: DEFAULT_HINTS,
+ fg: FG_SECONDARY,
+ });
+
+ container.add(hints);
+
+ return {
+ renderable: container,
+ update(text?: string) {
+ hints.content = text ?? DEFAULT_HINTS;
+ },
+ };
+}
diff --git a/apps/tui/src/data/store.ts b/apps/tui/src/data/store.ts
new file mode 100644
index 0000000..9de5ecb
--- /dev/null
+++ b/apps/tui/src/data/store.ts
@@ -0,0 +1,199 @@
+import { Database } from "bun:sqlite";
+import { existsSync } from "node:fs";
+import { join } from "node:path";
+import type { CodeEntityRow, MemoryRow, RuleRow, Stats } from "../types.ts";
+
+const HOMEBREW_SQLITE_PATHS = [
+ "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib",
+ "/usr/local/opt/sqlite/lib/libsqlite3.dylib",
+];
+
+let sqliteConfigured = false;
+
+function configureSQLite(): void {
+ if (sqliteConfigured) return;
+ if (process.platform === "darwin") {
+ const path = HOMEBREW_SQLITE_PATHS.find((p) => existsSync(p));
+ if (path) {
+ Database.setCustomSQLite(path);
+ }
+ }
+ sqliteConfigured = true;
+}
+
+export class DataStore {
+ private db: Database;
+ readonly dbPath: string;
+
+ constructor(projectPath: string) {
+ configureSQLite();
+ this.dbPath = join(projectPath, ".code-recall", "memory.db");
+ if (!existsSync(this.dbPath)) {
+ throw new Error(
+ `No code-recall database found at ${this.dbPath}\nRun the code-recall MCP server first to initialize the database.`,
+ );
+ }
+ this.db = new Database(this.dbPath, { readonly: true });
+ }
+
+ // --- Memories ---
+
+ getRecentMemories(limit = 50): MemoryRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM memories WHERE archived = 0 ORDER BY created_at DESC LIMIT ?",
+ )
+ .all(limit) as MemoryRow[];
+ }
+
+ getMemoriesByCategory(category: string, limit = 50): MemoryRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM memories WHERE category = ? AND archived = 0 ORDER BY created_at DESC LIMIT ?",
+ )
+ .all(category, limit) as MemoryRow[];
+ }
+
+ getMemoryById(id: number): MemoryRow | null {
+ return (
+ (this.db
+ .query("SELECT * FROM memories WHERE id = ?")
+ .get(id) as MemoryRow | null) ?? null
+ );
+ }
+
+ getFailedMemories(limit = 20): MemoryRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM memories WHERE worked = 0 AND archived = 0 ORDER BY updated_at DESC LIMIT ?",
+ )
+ .all(limit) as MemoryRow[];
+ }
+
+ getPinnedMemories(): MemoryRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM memories WHERE pinned = 1 AND archived = 0 ORDER BY created_at DESC",
+ )
+ .all() as MemoryRow[];
+ }
+
+ searchByFullText(query: string, limit = 20): MemoryRow[] {
+ return this.db
+ .query(
+ `SELECT m.* FROM memories m
+ JOIN memories_fts fts ON m.id = fts.rowid
+ WHERE memories_fts MATCH ? AND m.archived = 0
+ ORDER BY rank
+ LIMIT ?`,
+ )
+ .all(query, limit) as MemoryRow[];
+ }
+
+ getMemoriesByFilePath(filePath: string): MemoryRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM memories WHERE file_path = ? AND archived = 0 ORDER BY created_at DESC",
+ )
+ .all(filePath) as MemoryRow[];
+ }
+
+ // --- Rules ---
+
+ getActiveRules(): RuleRow[] {
+ return this.db
+ .query("SELECT * FROM rules WHERE active = 1 ORDER BY created_at DESC")
+ .all() as RuleRow[];
+ }
+
+ getRuleById(id: number): RuleRow | null {
+ return (
+ (this.db
+ .query("SELECT * FROM rules WHERE id = ?")
+ .get(id) as RuleRow | null) ?? null
+ );
+ }
+
+ // --- Code Entities ---
+
+ getCodeEntitiesByFile(filePath: string): CodeEntityRow[] {
+ return this.db
+ .query(
+ "SELECT * FROM code_entities WHERE file_path = ? ORDER BY start_line ASC",
+ )
+ .all(filePath) as CodeEntityRow[];
+ }
+
+ getDistinctEntityFiles(): string[] {
+ const rows = this.db
+ .query(
+ "SELECT DISTINCT file_path FROM code_entities ORDER BY file_path ASC",
+ )
+ .all() as Array<{ file_path: string }>;
+ return rows.map((r) => r.file_path);
+ }
+
+ // --- Stats ---
+
+ getStats(): Stats {
+ const total = (
+ this.db
+ .query("SELECT COUNT(*) as count FROM memories WHERE archived = 0")
+ .get() as { count: number }
+ ).count;
+
+ const categories = this.db
+ .query(
+ "SELECT category, COUNT(*) as count FROM memories WHERE archived = 0 GROUP BY category",
+ )
+ .all() as Array<{ category: string; count: number }>;
+
+ const byCategory: Record = {};
+ for (const row of categories) {
+ byCategory[row.category] = row.count;
+ }
+
+ const totalRules = (
+ this.db
+ .query("SELECT COUNT(*) as count FROM rules WHERE active = 1")
+ .get() as { count: number }
+ ).count;
+
+ const recentDecisions = (
+ this.db
+ .query(
+ "SELECT COUNT(*) as count FROM memories WHERE category = 'decision' AND archived = 0 AND created_at >= datetime('now', '-7 days')",
+ )
+ .get() as { count: number }
+ ).count;
+
+ const failedDecisions = (
+ this.db
+ .query(
+ "SELECT COUNT(*) as count FROM memories WHERE worked = 0 AND archived = 0",
+ )
+ .get() as { count: number }
+ ).count;
+
+ const totalWarnings = (
+ this.db
+ .query(
+ "SELECT COUNT(*) as count FROM memories WHERE category = 'warning' AND archived = 0",
+ )
+ .get() as { count: number }
+ ).count;
+
+ return {
+ totalMemories: total,
+ byCategory,
+ totalRules,
+ recentDecisions,
+ failedDecisions,
+ totalWarnings,
+ };
+ }
+
+ close(): void {
+ this.db.close();
+ }
+}
diff --git a/apps/tui/src/index.ts b/apps/tui/src/index.ts
new file mode 100644
index 0000000..49a2029
--- /dev/null
+++ b/apps/tui/src/index.ts
@@ -0,0 +1,61 @@
+#!/usr/bin/env bun
+import { existsSync } from "node:fs";
+import { resolve } from "node:path";
+import { createCliRenderer } from "@opentui/core";
+import { App } from "./app.ts";
+import { DataStore } from "./data/store.ts";
+
+function getProjectPath(): string {
+ // Check CLI args
+ const args = process.argv.slice(2);
+ const projectIdx = args.indexOf("--project");
+ if (projectIdx >= 0 && args[projectIdx + 1]) {
+ return resolve(args[projectIdx + 1]!);
+ }
+
+ // Check env var
+ if (process.env.CODE_RECALL_PROJECT_PATH) {
+ return resolve(process.env.CODE_RECALL_PROJECT_PATH);
+ }
+
+ // Default to cwd
+ return process.cwd();
+}
+
+async function main() {
+ const projectPath = getProjectPath();
+ const dbPath = `${projectPath}/.code-recall/memory.db`;
+
+ if (!existsSync(dbPath)) {
+ console.error(
+ `\n Error: No code-recall database found.\n\n` +
+ ` Looked at: ${dbPath}\n\n` +
+ ` Run the code-recall MCP server first to initialize the database,\n` +
+ ` or specify a project path:\n\n` +
+ ` code-recall-tui --project /path/to/project\n`,
+ );
+ process.exit(1);
+ }
+
+ let store: DataStore;
+ try {
+ store = new DataStore(projectPath);
+ } catch (err) {
+ console.error(
+ `\n Error: Could not open database.\n ${err instanceof Error ? err.message : err}\n`,
+ );
+ process.exit(1);
+ }
+
+ const renderer = await createCliRenderer({
+ exitOnCtrlC: true,
+ });
+
+ const app = new App(renderer, store, projectPath);
+ app.start();
+}
+
+main().catch((err) => {
+ console.error("Fatal error:", err);
+ process.exit(1);
+});
diff --git a/apps/tui/src/screens/dashboard.ts b/apps/tui/src/screens/dashboard.ts
new file mode 100644
index 0000000..a2cc209
--- /dev/null
+++ b/apps/tui/src/screens/dashboard.ts
@@ -0,0 +1,229 @@
+import type { KeyEvent } from "@opentui/core";
+import { BoxRenderable, SelectRenderable, TextRenderable } from "@opentui/core";
+import { categoryColor } from "../components/category-badge.ts";
+import { createEmptyState } from "../components/empty-state.ts";
+import { formatMemoryOption } from "../components/memory-card.ts";
+import { createStatBox } from "../components/stat-box.ts";
+import {
+ BG_SECONDARY,
+ BG_TERTIARY,
+ CATEGORY_ICONS,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ COLOR_FAILURE,
+ COLOR_WARNING,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, Screen } from "../types.ts";
+
+export class DashboardScreen implements Screen {
+ readonly name = "dashboard" as const;
+ private container: BoxRenderable | null = null;
+ private recentSelect: SelectRenderable | null = null;
+ private ctx: AppContext;
+
+ constructor(ctx: AppContext) {
+ this.ctx = ctx;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer, store } = this.ctx;
+ const stats = store.getStats();
+ const recentMemories = store.getRecentMemories(10);
+ const failedMemories = store.getFailedMemories(5);
+
+ this.container = new BoxRenderable(renderer, {
+ id: "dashboard",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "column",
+ gap: 1,
+ padding: 1,
+ });
+
+ // --- Stats row ---
+ const statsRow = new BoxRenderable(renderer, {
+ id: "stats-row",
+ width: "100%",
+ height: 6,
+ flexDirection: "row",
+ gap: 1,
+ });
+
+ statsRow.add(
+ createStatBox(renderer, {
+ value: stats.totalMemories,
+ label: "Memories",
+ color: COLOR_ACCENT,
+ }),
+ );
+ statsRow.add(
+ createStatBox(renderer, {
+ value: stats.totalRules,
+ label: "Rules",
+ color: COLOR_ACCENT,
+ }),
+ );
+ statsRow.add(
+ createStatBox(renderer, {
+ value: stats.failedDecisions,
+ label: "Failed",
+ color: COLOR_FAILURE,
+ }),
+ );
+ statsRow.add(
+ createStatBox(renderer, {
+ value: stats.totalWarnings,
+ label: "Warnings",
+ color: COLOR_WARNING,
+ }),
+ );
+
+ this.container.add(statsRow);
+
+ // --- Middle row: categories + recent ---
+ const middleRow = new BoxRenderable(renderer, {
+ id: "middle-row",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "row",
+ gap: 1,
+ });
+
+ // Category breakdown panel
+ const categoryPanel = new BoxRenderable(renderer, {
+ id: "category-panel",
+ width: "35%",
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: " Categories ",
+ titleAlignment: "left",
+ padding: 1,
+ gap: 1,
+ backgroundColor: BG_SECONDARY,
+ });
+
+ for (const cat of ["decision", "pattern", "warning", "learning"]) {
+ const count = stats.byCategory[cat] ?? 0;
+ const icon = CATEGORY_ICONS[cat] ?? "";
+ const row = new BoxRenderable(renderer, {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ width: "100%",
+ });
+ row.add(
+ new TextRenderable(renderer, {
+ content: `${icon} ${cat.charAt(0).toUpperCase() + cat.slice(1)}`,
+ fg: categoryColor(cat),
+ }),
+ );
+ row.add(
+ new TextRenderable(renderer, {
+ content: String(count),
+ fg: FG_PRIMARY,
+ attributes: 1,
+ }),
+ );
+ categoryPanel.add(row);
+ }
+
+ middleRow.add(categoryPanel);
+
+ // Recent activity panel
+ const recentPanel = new BoxRenderable(renderer, {
+ id: "recent-panel",
+ flexGrow: 1,
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: " Recent Activity ",
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ });
+
+ if (recentMemories.length === 0) {
+ recentPanel.add(
+ createEmptyState(renderer, "No memories yet. Start using code-recall!"),
+ );
+ } else {
+ this.recentSelect = new SelectRenderable(renderer, {
+ id: "recent-select",
+ options: recentMemories.map(formatMemoryOption),
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ selectedBackgroundColor: BG_TERTIARY,
+ selectedTextColor: FG_PRIMARY,
+ textColor: FG_SECONDARY,
+ descriptionColor: FG_MUTED,
+ selectedDescriptionColor: FG_SECONDARY,
+ showDescription: true,
+ flexGrow: 1,
+ width: "100%",
+ });
+
+ this.recentSelect.on("itemSelected", () => {
+ const opt = this.recentSelect?.getSelectedOption();
+ if (opt?.value != null) {
+ this.ctx.navigateTo("memory-detail", { memoryId: opt.value });
+ }
+ });
+
+ recentPanel.add(this.recentSelect);
+ this.recentSelect.focus();
+ }
+
+ middleRow.add(recentPanel);
+ this.container.add(middleRow);
+
+ // --- Failed approaches panel ---
+ if (failedMemories.length > 0) {
+ const failedPanel = new BoxRenderable(renderer, {
+ id: "failed-panel",
+ width: "100%",
+ height: Math.min(failedMemories.length + 3, 8),
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_FAILURE,
+ title: " Failed Approaches ",
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ padding: 1,
+ gap: 0,
+ });
+
+ for (const mem of failedMemories) {
+ const content =
+ mem.content.length > 80
+ ? `${mem.content.slice(0, 77)}...`
+ : mem.content;
+ failedPanel.add(
+ new TextRenderable(renderer, {
+ content: `\u2717 ${content}`,
+ fg: COLOR_FAILURE,
+ }),
+ );
+ }
+
+ this.container.add(failedPanel);
+ }
+
+ contentArea.add(this.container);
+ }
+
+ onKeypress(_key: KeyEvent): boolean {
+ return false;
+ }
+
+ destroy() {
+ this.container?.destroy();
+ this.container = null;
+ this.recentSelect = null;
+ }
+}
diff --git a/apps/tui/src/screens/entities.ts b/apps/tui/src/screens/entities.ts
new file mode 100644
index 0000000..245801f
--- /dev/null
+++ b/apps/tui/src/screens/entities.ts
@@ -0,0 +1,288 @@
+import type { KeyEvent } from "@opentui/core";
+import {
+ BoxRenderable,
+ ScrollBoxRenderable,
+ SelectRenderable,
+ TextRenderable,
+} from "@opentui/core";
+import { createEmptyState } from "../components/empty-state.ts";
+import {
+ BG_SECONDARY,
+ BG_TERTIARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ COLOR_DECISION,
+ COLOR_LEARNING,
+ COLOR_PATTERN,
+ COLOR_WARNING,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, Screen } from "../types.ts";
+
+const ENTITY_TYPE_COLORS: Record = {
+ class: COLOR_DECISION,
+ interface: COLOR_DECISION,
+ type: COLOR_LEARNING,
+ function: COLOR_PATTERN,
+ method: COLOR_PATTERN,
+ variable: COLOR_WARNING,
+ import: FG_MUTED,
+};
+
+export class EntitiesScreen implements Screen {
+ readonly name = "entities" as const;
+ private container: BoxRenderable | null = null;
+ private fileSelect: SelectRenderable | null = null;
+ private entityPanel: BoxRenderable | null = null;
+ private ctx: AppContext;
+ private files: string[] = [];
+ private focusOnEntities = false;
+
+ constructor(ctx: AppContext) {
+ this.ctx = ctx;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer, store } = this.ctx;
+ this.files = store.getDistinctEntityFiles();
+
+ this.container = new BoxRenderable(renderer, {
+ id: "entities-screen",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "column",
+ padding: 1,
+ gap: 1,
+ });
+
+ // Header
+ const headerRow = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ });
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: "Analyzed Files",
+ fg: FG_PRIMARY,
+ attributes: 1,
+ }),
+ );
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: `${this.files.length} files`,
+ fg: FG_MUTED,
+ }),
+ );
+ this.container.add(headerRow);
+
+ if (this.files.length === 0) {
+ this.container.add(
+ createEmptyState(
+ renderer,
+ "No code entities analyzed yet. Use analyze_structure via MCP.",
+ ),
+ );
+ contentArea.add(this.container);
+ return;
+ }
+
+ // Split view
+ const splitRow = new BoxRenderable(renderer, {
+ id: "split-row",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "row",
+ gap: 1,
+ });
+
+ // File list (left)
+ const filePanel = new BoxRenderable(renderer, {
+ id: "file-panel",
+ width: "35%",
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: " Files ",
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ });
+
+ this.fileSelect = new SelectRenderable(renderer, {
+ id: "file-select",
+ options: this.files.map((f) => ({
+ name: shortenPath(f),
+ description: "",
+ value: f,
+ })),
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ selectedBackgroundColor: BG_TERTIARY,
+ selectedTextColor: COLOR_ACCENT,
+ textColor: FG_SECONDARY,
+ showDescription: false,
+ showScrollIndicator: true,
+ flexGrow: 1,
+ width: "100%",
+ });
+
+ this.fileSelect.on("selectionChanged", () => {
+ this.loadEntities();
+ });
+
+ filePanel.add(this.fileSelect);
+ splitRow.add(filePanel);
+
+ // Entity panel (right)
+ this.entityPanel = new BoxRenderable(renderer, {
+ id: "entity-panel",
+ flexGrow: 1,
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: " Entities ",
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ });
+
+ splitRow.add(this.entityPanel);
+ this.container.add(splitRow);
+
+ // Load first file
+ this.loadEntities();
+ this.fileSelect.focus();
+
+ contentArea.add(this.container);
+ }
+
+ private loadEntities() {
+ const opt = this.fileSelect?.getSelectedOption();
+ if (!opt?.value || !this.entityPanel) return;
+
+ const { renderer, store } = this.ctx;
+ const filePath = opt.value as string;
+ const entities = store.getCodeEntitiesByFile(filePath);
+
+ // Clear entity panel
+ for (const child of this.entityPanel.getChildren()) {
+ child.destroy();
+ }
+
+ // Update panel title
+ this.entityPanel.title = ` ${shortenPath(filePath)} `;
+
+ if (entities.length === 0) {
+ this.entityPanel.add(
+ createEmptyState(renderer, "No entities in this file."),
+ );
+ return;
+ }
+
+ const scrollBox = new ScrollBoxRenderable(renderer, {
+ id: "entity-scroll",
+ flexGrow: 1,
+ width: "100%",
+ scrollY: true,
+ backgroundColor: BG_SECONDARY,
+ contentOptions: {
+ flexDirection: "column",
+ gap: 0,
+ width: "100%",
+ },
+ });
+
+ for (const entity of entities) {
+ const row = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ gap: 1,
+ paddingLeft: 1,
+ });
+
+ const typeColor = ENTITY_TYPE_COLORS[entity.entity_type] ?? FG_SECONDARY;
+ row.add(
+ new TextRenderable(renderer, {
+ content: `[${entity.entity_type}]`.padEnd(12),
+ fg: typeColor,
+ attributes: 1,
+ }),
+ );
+
+ const displayName = entity.qualified_name || entity.name;
+ row.add(
+ new TextRenderable(renderer, {
+ content: displayName,
+ fg: FG_PRIMARY,
+ }),
+ );
+
+ row.add(
+ new TextRenderable(renderer, {
+ content: `L${entity.start_line}-${entity.end_line}`,
+ fg: FG_MUTED,
+ }),
+ );
+
+ scrollBox.add(row);
+
+ if (entity.signature) {
+ const sigRow = new BoxRenderable(renderer, {
+ width: "100%",
+ paddingLeft: 14,
+ });
+ sigRow.add(
+ new TextRenderable(renderer, {
+ content:
+ entity.signature.length > 70
+ ? `${entity.signature.slice(0, 67)}...`
+ : entity.signature,
+ fg: FG_MUTED,
+ attributes: 4,
+ }),
+ );
+ scrollBox.add(sigRow);
+ }
+ }
+
+ this.entityPanel.add(scrollBox);
+ }
+
+ onKeypress(key: KeyEvent): boolean {
+ if (key.name === "tab" && this.fileSelect) {
+ if (this.focusOnEntities) {
+ this.fileSelect.focus();
+ this.focusOnEntities = false;
+ } else {
+ // Find the scrollbox in entity panel and focus it
+ const children = this.entityPanel?.getChildren() ?? [];
+ for (const child of children) {
+ if ("scrollBy" in child) {
+ child.focus();
+ this.focusOnEntities = true;
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ destroy() {
+ this.container?.destroy();
+ this.container = null;
+ this.fileSelect = null;
+ this.entityPanel = null;
+ }
+}
+
+function shortenPath(filePath: string): string {
+ const parts = filePath.split("/");
+ if (parts.length <= 3) return filePath;
+ return `.../${parts.slice(-3).join("/")}`;
+}
diff --git a/apps/tui/src/screens/memories.ts b/apps/tui/src/screens/memories.ts
new file mode 100644
index 0000000..a5fabad
--- /dev/null
+++ b/apps/tui/src/screens/memories.ts
@@ -0,0 +1,195 @@
+import type { KeyEvent } from "@opentui/core";
+import {
+ BoxRenderable,
+ SelectRenderable,
+ TabSelectRenderable,
+ TextRenderable,
+} from "@opentui/core";
+import { formatMemoryOption } from "../components/memory-card.ts";
+import {
+ BG_PRIMARY,
+ BG_SECONDARY,
+ BG_TERTIARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type {
+ AppContext,
+ MemoryRow,
+ NavigateParams,
+ Screen,
+} from "../types.ts";
+
+const TABS = [
+ { name: "All", description: "", value: "all" },
+ { name: "Decisions", description: "", value: "decision" },
+ { name: "Patterns", description: "", value: "pattern" },
+ { name: "Warnings", description: "", value: "warning" },
+ { name: "Learnings", description: "", value: "learning" },
+];
+
+export class MemoriesScreen implements Screen {
+ readonly name = "memories" as const;
+ private container: BoxRenderable | null = null;
+ private tabSelect: TabSelectRenderable | null = null;
+ private memorySelect: SelectRenderable | null = null;
+ private countText: TextRenderable | null = null;
+ private emptyState: BoxRenderable | null = null;
+ private ctx: AppContext;
+ private initialCategory: string | undefined;
+
+ constructor(ctx: AppContext, params?: NavigateParams) {
+ this.ctx = ctx;
+ this.initialCategory = params?.category;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer } = this.ctx;
+
+ this.container = new BoxRenderable(renderer, {
+ id: "memories-screen",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "column",
+ padding: 1,
+ gap: 1,
+ });
+
+ // Tab bar
+ const tabRow = new BoxRenderable(renderer, {
+ id: "tab-row",
+ width: "100%",
+ height: 3,
+ flexDirection: "row",
+ alignItems: "center",
+ gap: 1,
+ });
+
+ this.tabSelect = new TabSelectRenderable(renderer, {
+ id: "memory-tabs",
+ options: TABS,
+ height: 1,
+ flexGrow: 1,
+ backgroundColor: BG_PRIMARY,
+ textColor: FG_SECONDARY,
+ selectedBackgroundColor: COLOR_ACCENT,
+ selectedTextColor: "#ffffff",
+ focusedBackgroundColor: BG_PRIMARY,
+ focusedTextColor: FG_PRIMARY,
+ showDescription: false,
+ wrapSelection: true,
+ });
+
+ this.countText = new TextRenderable(renderer, {
+ id: "memory-count",
+ content: "",
+ fg: FG_MUTED,
+ });
+
+ tabRow.add(this.tabSelect);
+ tabRow.add(this.countText);
+ this.container.add(tabRow);
+
+ // Memory list container
+ const listPanel = new BoxRenderable(renderer, {
+ id: "memory-list-panel",
+ flexGrow: 1,
+ width: "100%",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ backgroundColor: BG_SECONDARY,
+ });
+
+ this.memorySelect = new SelectRenderable(renderer, {
+ id: "memory-select",
+ options: [],
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ selectedBackgroundColor: BG_TERTIARY,
+ selectedTextColor: FG_PRIMARY,
+ textColor: FG_SECONDARY,
+ descriptionColor: FG_MUTED,
+ selectedDescriptionColor: FG_SECONDARY,
+ showDescription: true,
+ showScrollIndicator: true,
+ flexGrow: 1,
+ width: "100%",
+ });
+
+ this.memorySelect.on("itemSelected", () => {
+ const opt = this.memorySelect?.getSelectedOption();
+ if (opt?.value != null) {
+ this.ctx.navigateTo("memory-detail", { memoryId: opt.value });
+ }
+ });
+
+ listPanel.add(this.memorySelect);
+ this.container.add(listPanel);
+
+ // Tab change handler
+ this.tabSelect.on("selectionChanged", () => {
+ this.loadMemories();
+ });
+
+ // Set initial tab
+ if (this.initialCategory) {
+ const idx = TABS.findIndex((t) => t.value === this.initialCategory);
+ if (idx >= 0) {
+ this.tabSelect.setSelectedIndex(idx);
+ }
+ }
+
+ // Load initial data
+ this.loadMemories();
+ this.memorySelect.focus();
+
+ contentArea.add(this.container);
+ }
+
+ private loadMemories() {
+ const tab = this.tabSelect?.getSelectedOption();
+ const category = tab?.value;
+
+ let memories: MemoryRow[];
+ if (!category || category === "all") {
+ memories = this.ctx.store.getRecentMemories(100);
+ } else {
+ memories = this.ctx.store.getMemoriesByCategory(category, 100);
+ }
+
+ const options = memories.map(formatMemoryOption);
+ if (this.memorySelect) {
+ this.memorySelect.options = options;
+ this.memorySelect.setSelectedIndex(0);
+ }
+
+ if (this.countText) {
+ this.countText.content = `${memories.length} memories`;
+ }
+ }
+
+ onKeypress(key: KeyEvent): boolean {
+ if (key.name === "[" || key.name === "]") {
+ if (this.tabSelect) {
+ if (key.name === "[") this.tabSelect.moveLeft();
+ else this.tabSelect.moveRight();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ destroy() {
+ this.container?.destroy();
+ this.container = null;
+ this.tabSelect = null;
+ this.memorySelect = null;
+ this.countText = null;
+ this.emptyState = null;
+ }
+}
diff --git a/apps/tui/src/screens/memory-detail.ts b/apps/tui/src/screens/memory-detail.ts
new file mode 100644
index 0000000..cb99997
--- /dev/null
+++ b/apps/tui/src/screens/memory-detail.ts
@@ -0,0 +1,241 @@
+import type { KeyEvent } from "@opentui/core";
+import {
+ BoxRenderable,
+ ScrollBoxRenderable,
+ TextRenderable,
+} from "@opentui/core";
+import { categoryColor, categoryLabel } from "../components/category-badge.ts";
+import { outcomeColor, outcomeLabel } from "../components/outcome-badge.ts";
+import {
+ BG_PRIMARY,
+ BG_SECONDARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, NavigateParams, Screen } from "../types.ts";
+
+export class MemoryDetailScreen implements Screen {
+ readonly name = "memory-detail" as const;
+ private scrollBox: ScrollBoxRenderable | null = null;
+ private ctx: AppContext;
+ private memoryId: number;
+
+ constructor(ctx: AppContext, params: NavigateParams) {
+ this.ctx = ctx;
+ this.memoryId = params.memoryId!;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer, store } = this.ctx;
+ const memory = store.getMemoryById(this.memoryId);
+
+ if (!memory) {
+ const errBox = new BoxRenderable(renderer, {
+ width: "100%",
+ flexGrow: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ });
+ errBox.add(
+ new TextRenderable(renderer, {
+ content: `Memory #${this.memoryId} not found`,
+ fg: FG_MUTED,
+ }),
+ );
+ contentArea.add(errBox);
+ return;
+ }
+
+ this.scrollBox = new ScrollBoxRenderable(renderer, {
+ id: "memory-detail-scroll",
+ width: "100%",
+ flexGrow: 1,
+ scrollY: true,
+ padding: 1,
+ backgroundColor: BG_PRIMARY,
+ contentOptions: {
+ flexDirection: "column",
+ gap: 1,
+ width: "100%",
+ },
+ });
+
+ // --- Header row: category + outcome ---
+ const headerRow = new BoxRenderable(renderer, {
+ id: "detail-header",
+ width: "100%",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ });
+
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: `Memory #${memory.id} ${categoryLabel(memory.category)}`,
+ fg: categoryColor(memory.category),
+ attributes: 1,
+ }),
+ );
+
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: outcomeLabel(memory.worked),
+ fg: outcomeColor(memory.worked),
+ attributes: 1,
+ }),
+ );
+
+ this.scrollBox.add(headerRow);
+
+ // --- Dates ---
+ const dateRow = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ gap: 4,
+ });
+ dateRow.add(
+ new TextRenderable(renderer, {
+ content: `Created: ${formatDate(memory.created_at)}`,
+ fg: FG_MUTED,
+ }),
+ );
+ dateRow.add(
+ new TextRenderable(renderer, {
+ content: `Updated: ${formatDate(memory.updated_at)}`,
+ fg: FG_MUTED,
+ }),
+ );
+ this.scrollBox.add(dateRow);
+
+ // --- Content panel ---
+ this.scrollBox.add(createPanel(renderer, "Content", memory.content));
+
+ // --- Rationale panel ---
+ if (memory.rationale) {
+ this.scrollBox.add(createPanel(renderer, "Rationale", memory.rationale));
+ }
+
+ // --- Context panel ---
+ if (memory.context) {
+ this.scrollBox.add(createPanel(renderer, "Context", memory.context));
+ }
+
+ // --- Tags ---
+ if (memory.tags) {
+ try {
+ const tags = JSON.parse(memory.tags) as string[];
+ if (tags.length > 0) {
+ const tagsRow = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ gap: 1,
+ });
+ tagsRow.add(
+ new TextRenderable(renderer, {
+ content: "Tags:",
+ fg: FG_SECONDARY,
+ attributes: 1,
+ }),
+ );
+ tagsRow.add(
+ new TextRenderable(renderer, {
+ content: tags.join(", "),
+ fg: COLOR_ACCENT,
+ }),
+ );
+ this.scrollBox.add(tagsRow);
+ }
+ } catch {}
+ }
+
+ // --- File path ---
+ if (memory.file_path) {
+ const fileRow = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ gap: 1,
+ });
+ fileRow.add(
+ new TextRenderable(renderer, {
+ content: "File:",
+ fg: FG_SECONDARY,
+ attributes: 1,
+ }),
+ );
+ fileRow.add(
+ new TextRenderable(renderer, {
+ content: memory.file_path,
+ fg: FG_PRIMARY,
+ }),
+ );
+ this.scrollBox.add(fileRow);
+ }
+
+ // --- Outcome panel ---
+ if (memory.outcome) {
+ this.scrollBox.add(createPanel(renderer, "Outcome", memory.outcome));
+ }
+
+ this.scrollBox.focus();
+ contentArea.add(this.scrollBox);
+ }
+
+ onKeypress(key: KeyEvent): boolean {
+ if (key.name === "escape") {
+ this.ctx.goBack();
+ return true;
+ }
+ return false;
+ }
+
+ destroy() {
+ this.scrollBox?.destroy();
+ this.scrollBox = null;
+ }
+}
+
+function createPanel(
+ renderer: import("@opentui/core").CliRenderer,
+ title: string,
+ content: string,
+) {
+ const panel = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: ` ${title} `,
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ padding: 1,
+ });
+
+ panel.add(
+ new TextRenderable(renderer, {
+ content,
+ fg: FG_PRIMARY,
+ }),
+ );
+
+ return panel;
+}
+
+function formatDate(iso: string): string {
+ try {
+ const d = new Date(iso);
+ return d.toLocaleString("en-US", {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+ } catch {
+ return iso;
+ }
+}
diff --git a/apps/tui/src/screens/rule-detail.ts b/apps/tui/src/screens/rule-detail.ts
new file mode 100644
index 0000000..12dd754
--- /dev/null
+++ b/apps/tui/src/screens/rule-detail.ts
@@ -0,0 +1,209 @@
+import type { KeyEvent } from "@opentui/core";
+import {
+ BoxRenderable,
+ ScrollBoxRenderable,
+ TextRenderable,
+} from "@opentui/core";
+import {
+ BG_PRIMARY,
+ BG_SECONDARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ COLOR_FAILURE,
+ COLOR_SUCCESS,
+ COLOR_WARNING,
+ FG_MUTED,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, NavigateParams, Screen } from "../types.ts";
+
+function parseJsonArray(raw: string | null): string[] {
+ if (!raw) return [];
+ try {
+ const parsed = JSON.parse(raw);
+ return Array.isArray(parsed) ? parsed : [];
+ } catch {
+ return [];
+ }
+}
+
+export class RuleDetailScreen implements Screen {
+ readonly name = "rule-detail" as const;
+ private scrollBox: ScrollBoxRenderable | null = null;
+ private ctx: AppContext;
+ private ruleId: number;
+
+ constructor(ctx: AppContext, params: NavigateParams) {
+ this.ctx = ctx;
+ this.ruleId = params.ruleId!;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer, store } = this.ctx;
+ const rule = store.getRuleById(this.ruleId);
+
+ if (!rule) {
+ const errBox = new BoxRenderable(renderer, {
+ width: "100%",
+ flexGrow: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ });
+ errBox.add(
+ new TextRenderable(renderer, {
+ content: `Rule #${this.ruleId} not found`,
+ fg: FG_MUTED,
+ }),
+ );
+ contentArea.add(errBox);
+ return;
+ }
+
+ this.scrollBox = new ScrollBoxRenderable(renderer, {
+ id: "rule-detail-scroll",
+ width: "100%",
+ flexGrow: 1,
+ scrollY: true,
+ padding: 1,
+ backgroundColor: BG_PRIMARY,
+ contentOptions: {
+ flexDirection: "column",
+ gap: 1,
+ width: "100%",
+ },
+ });
+
+ // Trigger
+ this.scrollBox.add(
+ new TextRenderable(renderer, {
+ content: `Rule #${rule.id}`,
+ fg: FG_MUTED,
+ }),
+ );
+ this.scrollBox.add(
+ new TextRenderable(renderer, {
+ content: `"${rule.trigger}"`,
+ fg: COLOR_ACCENT,
+ attributes: 1,
+ }),
+ );
+
+ // Date
+ this.scrollBox.add(
+ new TextRenderable(renderer, {
+ content: `Created: ${formatDate(rule.created_at)}`,
+ fg: FG_MUTED,
+ }),
+ );
+
+ const mustDo = parseJsonArray(rule.must_do);
+ const mustNot = parseJsonArray(rule.must_not);
+ const askFirst = parseJsonArray(rule.ask_first);
+
+ // Must Do
+ if (mustDo.length > 0) {
+ this.scrollBox.add(
+ createConstraintPanel(renderer, "Must Do", mustDo, "+", COLOR_SUCCESS),
+ );
+ }
+
+ // Must Not
+ if (mustNot.length > 0) {
+ this.scrollBox.add(
+ createConstraintPanel(
+ renderer,
+ "Must Not",
+ mustNot,
+ "-",
+ COLOR_FAILURE,
+ ),
+ );
+ }
+
+ // Ask First
+ if (askFirst.length > 0) {
+ this.scrollBox.add(
+ createConstraintPanel(
+ renderer,
+ "Ask First",
+ askFirst,
+ "?",
+ COLOR_WARNING,
+ ),
+ );
+ }
+
+ if (mustDo.length === 0 && mustNot.length === 0 && askFirst.length === 0) {
+ this.scrollBox.add(
+ new TextRenderable(renderer, {
+ content: "No constraints defined for this rule.",
+ fg: FG_MUTED,
+ attributes: 4,
+ }),
+ );
+ }
+
+ this.scrollBox.focus();
+ contentArea.add(this.scrollBox);
+ }
+
+ onKeypress(key: KeyEvent): boolean {
+ if (key.name === "escape") {
+ this.ctx.goBack();
+ return true;
+ }
+ return false;
+ }
+
+ destroy() {
+ this.scrollBox?.destroy();
+ this.scrollBox = null;
+ }
+}
+
+function createConstraintPanel(
+ renderer: import("@opentui/core").CliRenderer,
+ title: string,
+ items: string[],
+ prefix: string,
+ color: string,
+) {
+ const panel = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ title: ` ${title} `,
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ padding: 1,
+ gap: 0,
+ });
+
+ for (const item of items) {
+ panel.add(
+ new TextRenderable(renderer, {
+ content: ` ${prefix} ${item}`,
+ fg: color,
+ }),
+ );
+ }
+
+ return panel;
+}
+
+function formatDate(iso: string): string {
+ try {
+ const d = new Date(iso);
+ return d.toLocaleString("en-US", {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+ } catch {
+ return iso;
+ }
+}
diff --git a/apps/tui/src/screens/rules.ts b/apps/tui/src/screens/rules.ts
new file mode 100644
index 0000000..5275dca
--- /dev/null
+++ b/apps/tui/src/screens/rules.ts
@@ -0,0 +1,119 @@
+import type { KeyEvent } from "@opentui/core";
+import { BoxRenderable, SelectRenderable, TextRenderable } from "@opentui/core";
+import { createEmptyState } from "../components/empty-state.ts";
+import { formatRuleOption } from "../components/rule-card.ts";
+import {
+ BG_SECONDARY,
+ BG_TERTIARY,
+ COLOR_BORDER,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, Screen } from "../types.ts";
+
+export class RulesScreen implements Screen {
+ readonly name = "rules" as const;
+ private container: BoxRenderable | null = null;
+ private ruleSelect: SelectRenderable | null = null;
+ private ctx: AppContext;
+
+ constructor(ctx: AppContext) {
+ this.ctx = ctx;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer, store } = this.ctx;
+ const rules = store.getActiveRules();
+
+ this.container = new BoxRenderable(renderer, {
+ id: "rules-screen",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "column",
+ padding: 1,
+ gap: 1,
+ });
+
+ // Header
+ const headerRow = new BoxRenderable(renderer, {
+ width: "100%",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ });
+
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: "Active Guardrails",
+ fg: FG_PRIMARY,
+ attributes: 1,
+ }),
+ );
+
+ headerRow.add(
+ new TextRenderable(renderer, {
+ content: `${rules.length} rules`,
+ fg: FG_MUTED,
+ }),
+ );
+
+ this.container.add(headerRow);
+
+ // Rule list
+ const listPanel = new BoxRenderable(renderer, {
+ id: "rule-list-panel",
+ flexGrow: 1,
+ width: "100%",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ backgroundColor: BG_SECONDARY,
+ });
+
+ if (rules.length === 0) {
+ listPanel.add(createEmptyState(renderer, "No rules defined yet."));
+ } else {
+ this.ruleSelect = new SelectRenderable(renderer, {
+ id: "rule-select",
+ options: rules.map(formatRuleOption),
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ selectedBackgroundColor: BG_TERTIARY,
+ selectedTextColor: FG_PRIMARY,
+ textColor: FG_SECONDARY,
+ descriptionColor: FG_MUTED,
+ selectedDescriptionColor: FG_SECONDARY,
+ showDescription: true,
+ showScrollIndicator: true,
+ flexGrow: 1,
+ width: "100%",
+ itemSpacing: 1,
+ });
+
+ this.ruleSelect.on("itemSelected", () => {
+ const opt = this.ruleSelect?.getSelectedOption();
+ if (opt?.value != null) {
+ this.ctx.navigateTo("rule-detail", { ruleId: opt.value });
+ }
+ });
+
+ listPanel.add(this.ruleSelect);
+ this.ruleSelect.focus();
+ }
+
+ this.container.add(listPanel);
+ contentArea.add(this.container);
+ }
+
+ onKeypress(_key: KeyEvent): boolean {
+ return false;
+ }
+
+ destroy() {
+ this.container?.destroy();
+ this.container = null;
+ this.ruleSelect = null;
+ }
+}
diff --git a/apps/tui/src/screens/search.ts b/apps/tui/src/screens/search.ts
new file mode 100644
index 0000000..9c21fc7
--- /dev/null
+++ b/apps/tui/src/screens/search.ts
@@ -0,0 +1,213 @@
+import {
+ BoxRenderable,
+ InputRenderable,
+ KeyEvent,
+ SelectRenderable,
+ TextRenderable,
+} from "@opentui/core";
+import { createEmptyState } from "../components/empty-state.ts";
+import { formatMemoryOption } from "../components/memory-card.ts";
+import {
+ BG_SECONDARY,
+ BG_TERTIARY,
+ COLOR_ACCENT,
+ COLOR_BORDER,
+ FG_MUTED,
+ FG_PRIMARY,
+ FG_SECONDARY,
+ PANEL_BORDER,
+} from "../theme.ts";
+import type { AppContext, NavigateParams, Screen } from "../types.ts";
+
+export class SearchScreen implements Screen {
+ readonly name = "search" as const;
+ private container: BoxRenderable | null = null;
+ private input: InputRenderable | null = null;
+ private resultSelect: SelectRenderable | null = null;
+ private resultCount: TextRenderable | null = null;
+ private emptyBox: BoxRenderable | null = null;
+ private resultsPanel: BoxRenderable | null = null;
+ private ctx: AppContext;
+ private inputFocused = true;
+
+ constructor(ctx: AppContext, _params?: NavigateParams) {
+ this.ctx = ctx;
+ }
+
+ mount(contentArea: BoxRenderable) {
+ const { renderer } = this.ctx;
+
+ this.container = new BoxRenderable(renderer, {
+ id: "search-screen",
+ width: "100%",
+ flexGrow: 1,
+ flexDirection: "column",
+ padding: 1,
+ gap: 1,
+ });
+
+ // Search input bar
+ const searchBar = new BoxRenderable(renderer, {
+ id: "search-bar",
+ width: "100%",
+ height: 3,
+ flexDirection: "row",
+ alignItems: "center",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_ACCENT,
+ title: " Search ",
+ titleAlignment: "left",
+ backgroundColor: BG_SECONDARY,
+ paddingLeft: 1,
+ paddingRight: 1,
+ });
+
+ this.input = new InputRenderable(renderer, {
+ id: "search-input",
+ placeholder: "Type a query and press Enter...",
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ textColor: FG_PRIMARY,
+ width: "100%",
+ });
+
+ this.input.on("enter", () => {
+ this.executeSearch();
+ });
+
+ searchBar.add(this.input);
+ this.container.add(searchBar);
+
+ // Results area
+ this.resultsPanel = new BoxRenderable(renderer, {
+ id: "results-panel",
+ flexGrow: 1,
+ width: "100%",
+ flexDirection: "column",
+ border: true,
+ borderStyle: PANEL_BORDER,
+ borderColor: COLOR_BORDER,
+ backgroundColor: BG_SECONDARY,
+ });
+
+ this.resultCount = new TextRenderable(renderer, {
+ id: "result-count",
+ content: "",
+ fg: FG_MUTED,
+ paddingLeft: 1,
+ });
+ this.resultsPanel.add(this.resultCount);
+
+ this.emptyBox = createEmptyState(
+ renderer,
+ "Type a query above and press Enter to search memories.",
+ );
+ this.resultsPanel.add(this.emptyBox);
+
+ this.container.add(this.resultsPanel);
+ this.input.focus();
+
+ contentArea.add(this.container);
+ }
+
+ private executeSearch() {
+ const query = this.input?.value?.trim();
+ if (!query || !this.resultsPanel) return;
+
+ const { renderer, store } = this.ctx;
+
+ try {
+ const results = store.searchByFullText(query, 30);
+
+ // Remove old results
+ if (this.resultSelect) {
+ this.resultSelect.destroy();
+ this.resultSelect = null;
+ }
+ if (this.emptyBox) {
+ this.emptyBox.destroy();
+ this.emptyBox = null;
+ }
+
+ if (this.resultCount) {
+ this.resultCount.content = ` ${results.length} result${results.length !== 1 ? "s" : ""} for "${query}"`;
+ }
+
+ if (results.length === 0) {
+ this.emptyBox = createEmptyState(
+ renderer,
+ "No results found. Try a different query.",
+ );
+ this.resultsPanel.add(this.emptyBox);
+ return;
+ }
+
+ this.resultSelect = new SelectRenderable(renderer, {
+ id: "result-select",
+ options: results.map(formatMemoryOption),
+ backgroundColor: BG_SECONDARY,
+ focusedBackgroundColor: BG_SECONDARY,
+ selectedBackgroundColor: BG_TERTIARY,
+ selectedTextColor: FG_PRIMARY,
+ textColor: FG_SECONDARY,
+ descriptionColor: FG_MUTED,
+ selectedDescriptionColor: FG_SECONDARY,
+ showDescription: true,
+ showScrollIndicator: true,
+ flexGrow: 1,
+ width: "100%",
+ });
+
+ this.resultSelect.on("itemSelected", () => {
+ const opt = this.resultSelect?.getSelectedOption();
+ if (opt?.value != null) {
+ this.ctx.navigateTo("memory-detail", { memoryId: opt.value });
+ }
+ });
+
+ this.resultsPanel.add(this.resultSelect);
+
+ // Focus the results
+ this.resultSelect.focus();
+ this.inputFocused = false;
+ } catch {
+ if (this.resultCount) {
+ this.resultCount.content = ` Search error. Try simpler terms (avoid special characters).`;
+ }
+ }
+ }
+
+ onKeypress(key: KeyEvent): boolean {
+ if (key.name === "tab") {
+ if (this.inputFocused && this.resultSelect) {
+ this.resultSelect.focus();
+ this.inputFocused = false;
+ } else if (this.input) {
+ this.input.focus();
+ this.inputFocused = true;
+ }
+ return true;
+ }
+ if (key.name === "escape") {
+ if (!this.inputFocused && this.input) {
+ this.input.focus();
+ this.inputFocused = true;
+ return true;
+ }
+ this.ctx.goBack();
+ return true;
+ }
+ return false;
+ }
+
+ destroy() {
+ this.container?.destroy();
+ this.container = null;
+ this.input = null;
+ this.resultSelect = null;
+ this.resultCount = null;
+ this.emptyBox = null;
+ this.resultsPanel = null;
+ }
+}
diff --git a/apps/tui/src/theme.ts b/apps/tui/src/theme.ts
new file mode 100644
index 0000000..9f112f1
--- /dev/null
+++ b/apps/tui/src/theme.ts
@@ -0,0 +1,52 @@
+// Background colors
+export const BG_PRIMARY = "#0d1117";
+export const BG_SECONDARY = "#161b22";
+export const BG_TERTIARY = "#21262d";
+export const BG_HOVER = "#30363d";
+
+// Text colors
+export const FG_PRIMARY = "#e6edf3";
+export const FG_SECONDARY = "#8b949e";
+export const FG_MUTED = "#484f58";
+
+// Category colors
+export const COLOR_DECISION = "#58a6ff";
+export const COLOR_PATTERN = "#3fb950";
+export const COLOR_WARNING = "#d29922";
+export const COLOR_LEARNING = "#bc8cff";
+
+// Outcome colors
+export const COLOR_SUCCESS = "#3fb950";
+export const COLOR_FAILURE = "#f85149";
+export const COLOR_PENDING = "#8b949e";
+
+// Accent & borders
+export const COLOR_ACCENT = "#58a6ff";
+export const COLOR_BORDER = "#30363d";
+export const COLOR_BORDER_FOCUS = "#58a6ff";
+
+// Category color lookup
+export const CATEGORY_COLORS: Record = {
+ decision: COLOR_DECISION,
+ pattern: COLOR_PATTERN,
+ warning: COLOR_WARNING,
+ learning: COLOR_LEARNING,
+};
+
+// Category icons
+export const CATEGORY_ICONS: Record = {
+ decision: "\u25cf",
+ pattern: "\u25a0",
+ warning: "\u25b2",
+ learning: "\u2605",
+};
+
+// Outcome icons
+export const OUTCOME_ICONS: Record = {
+ worked: "\u2713",
+ failed: "\u2717",
+ pending: "\u25cb",
+};
+
+// Border style
+export const PANEL_BORDER = "rounded" as const;
diff --git a/apps/tui/src/types.ts b/apps/tui/src/types.ts
new file mode 100644
index 0000000..0c13f11
--- /dev/null
+++ b/apps/tui/src/types.ts
@@ -0,0 +1,89 @@
+import type { BoxRenderable, CliRenderer, KeyEvent } from "@opentui/core";
+import type { DataStore } from "./data/store.ts";
+
+export type ScreenName =
+ | "dashboard"
+ | "memories"
+ | "memory-detail"
+ | "rules"
+ | "rule-detail"
+ | "entities"
+ | "search";
+
+export interface Screen {
+ readonly name: ScreenName;
+ mount(container: BoxRenderable): void;
+ destroy(): void;
+ onKeypress?(key: KeyEvent): boolean;
+}
+
+export interface NavigateParams {
+ memoryId?: number;
+ ruleId?: number;
+ category?: string;
+ filePath?: string;
+ searchQuery?: string;
+}
+
+export interface ScreenEntry {
+ name: ScreenName;
+ params?: NavigateParams;
+}
+
+export interface AppContext {
+ renderer: CliRenderer;
+ store: DataStore;
+ navigateTo(screen: ScreenName, params?: NavigateParams): void;
+ goBack(): void;
+}
+
+// Database row types (standalone, no server dependency)
+export interface MemoryRow {
+ id: number;
+ category: "decision" | "pattern" | "warning" | "learning";
+ content: string;
+ rationale: string | null;
+ context: string | null;
+ tags: string | null;
+ file_path: string | null;
+ outcome: string | null;
+ worked: number | null;
+ pinned: number;
+ archived: number;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface RuleRow {
+ id: number;
+ trigger: string;
+ must_do: string | null;
+ must_not: string | null;
+ ask_first: string | null;
+ active: number;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface CodeEntityRow {
+ id: number;
+ file_path: string;
+ entity_type: string;
+ name: string;
+ qualified_name: string | null;
+ signature: string | null;
+ docstring: string | null;
+ start_line: number;
+ end_line: number;
+ file_hash: string;
+ created_at: string;
+}
+
+export interface Stats {
+ totalMemories: number;
+ byCategory: Record;
+ totalRules: number;
+ recentDecisions: number;
+ failedDecisions: number;
+ totalWarnings: number;
+}
diff --git a/apps/tui/tsconfig.json b/apps/tui/tsconfig.json
new file mode 100644
index 0000000..12f643d
--- /dev/null
+++ b/apps/tui/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "allowJs": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/biome.json b/biome.json
index 233c05e..553be08 100644
--- a/biome.json
+++ b/biome.json
@@ -5,6 +5,12 @@
"clientKind": "git",
"useIgnoreFile": true
},
+ "css": {
+ "parser": {
+ "cssModules": false,
+ "tailwindDirectives": true
+ }
+ },
"files": {
"ignoreUnknown": false
},
@@ -22,7 +28,8 @@
"recommended": true,
"style": {
"useTemplate": "off",
- "noNonNullAssertion": "off"
+ "noNonNullAssertion": "off",
+ "useImportType": "off"
},
"correctness": {
"noUnusedPrivateClassMembers": "off"
diff --git a/bun.lock b/bun.lock
index db60576..d3048c1 100644
--- a/bun.lock
+++ b/bun.lock
@@ -3,7 +3,20 @@
"configVersion": 1,
"workspaces": {
"": {
+ "name": "code-recall-monorepo",
+ "devDependencies": {
+ "@biomejs/biome": "2.3.11",
+ "@changesets/cli": "2.29.8",
+ "turbo": "2.7.4",
+ "typescript": "5.9.3",
+ },
+ },
+ "apps/server": {
"name": "code-recall",
+ "version": "1.0.0",
+ "bin": {
+ "code-recall": "src/index.ts",
+ },
"dependencies": {
"@modelcontextprotocol/sdk": "1.25.2",
"@xenova/transformers": "2.17.2",
@@ -14,15 +27,32 @@
"zod": "3.25.76",
},
"devDependencies": {
- "@biomejs/biome": "2.3.11",
- "@types/bun": "1.2.15",
+ "@types/bun": "1.3.6",
+ },
+ "peerDependencies": {
+ "typescript": "5.9.3",
+ },
+ },
+ "apps/tui": {
+ "name": "code-recall-tui",
+ "version": "1.0.0",
+ "bin": {
+ "code-recall-tui": "src/index.ts",
+ },
+ "dependencies": {
+ "@opentui/core": "0.1.75",
+ },
+ "devDependencies": {
+ "@types/bun": "1.3.6",
},
"peerDependencies": {
- "typescript": "5",
+ "typescript": "5.9.3",
},
},
},
"packages": {
+ "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
+
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
@@ -41,12 +71,130 @@
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
- "@hono/node-server": ["@hono/node-server@1.19.8", "", { "peerDependencies": { "hono": "^4" } }, "sha512-0/g2lIOPzX8f3vzW1ggQgvG5mjtFBDBHFAzI5SFAi2DzSqS9luJwqg9T6O/gKYLi+inS7eNxBeIFkkghIPvrMA=="],
+ "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.14", "", { "dependencies": { "@changesets/config": "^3.1.2", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA=="],
+
+ "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.9", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ=="],
+
+ "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="],
+
+ "@changesets/cli": ["@changesets/cli@2.29.8", "", { "dependencies": { "@changesets/apply-release-plan": "^7.0.14", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.2", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.14", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.6", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.2", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA=="],
+
+ "@changesets/config": ["@changesets/config@3.1.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/logger": "^0.1.1", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog=="],
+
+ "@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "^0.1.5" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="],
+
+ "@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ=="],
+
+ "@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.14", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.9", "@changesets/config": "^3.1.2", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.6", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g=="],
+
+ "@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="],
+
+ "@changesets/git": ["@changesets/git@3.0.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.8", "spawndamnit": "^3.0.1" } }, "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw=="],
+
+ "@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="],
+
+ "@changesets/parse": ["@changesets/parse@0.4.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^4.1.1" } }, "sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA=="],
+
+ "@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="],
+
+ "@changesets/read": ["@changesets/read@0.6.6", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.2", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg=="],
+
+ "@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="],
+
+ "@changesets/types": ["@changesets/types@6.1.0", "", {}, "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA=="],
+
+ "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="],
+
+ "@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="],
+
+ "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
"@huggingface/jinja": ["@huggingface/jinja@0.2.2", "", {}, "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA=="],
+ "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="],
+
+ "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="],
+
+ "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="],
+
+ "@jimp/file-ops": ["@jimp/file-ops@1.6.0", "", {}, "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ=="],
+
+ "@jimp/js-bmp": ["@jimp/js-bmp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "bmp-ts": "^1.0.9" } }, "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw=="],
+
+ "@jimp/js-gif": ["@jimp/js-gif@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "gifwrap": "^0.10.1", "omggif": "^1.0.10" } }, "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g=="],
+
+ "@jimp/js-jpeg": ["@jimp/js-jpeg@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "jpeg-js": "^0.4.4" } }, "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA=="],
+
+ "@jimp/js-png": ["@jimp/js-png@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "pngjs": "^7.0.0" } }, "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg=="],
+
+ "@jimp/js-tiff": ["@jimp/js-tiff@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "utif2": "^4.1.0" } }, "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw=="],
+
+ "@jimp/plugin-blit": ["@jimp/plugin-blit@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA=="],
+
+ "@jimp/plugin-blur": ["@jimp/plugin-blur@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw=="],
+
+ "@jimp/plugin-circle": ["@jimp/plugin-circle@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw=="],
+
+ "@jimp/plugin-color": ["@jimp/plugin-color@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "tinycolor2": "^1.6.0", "zod": "^3.23.8" } }, "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA=="],
+
+ "@jimp/plugin-contain": ["@jimp/plugin-contain@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ=="],
+
+ "@jimp/plugin-cover": ["@jimp/plugin-cover@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA=="],
+
+ "@jimp/plugin-crop": ["@jimp/plugin-crop@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang=="],
+
+ "@jimp/plugin-displace": ["@jimp/plugin-displace@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q=="],
+
+ "@jimp/plugin-dither": ["@jimp/plugin-dither@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0" } }, "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ=="],
+
+ "@jimp/plugin-fisheye": ["@jimp/plugin-fisheye@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA=="],
+
+ "@jimp/plugin-flip": ["@jimp/plugin-flip@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg=="],
+
+ "@jimp/plugin-hash": ["@jimp/plugin-hash@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "any-base": "^1.1.0" } }, "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q=="],
+
+ "@jimp/plugin-mask": ["@jimp/plugin-mask@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA=="],
+
+ "@jimp/plugin-print": ["@jimp/plugin-print@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/types": "1.6.0", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", "simple-xml-to-json": "^1.2.2", "zod": "^3.23.8" } }, "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A=="],
+
+ "@jimp/plugin-quantize": ["@jimp/plugin-quantize@1.6.0", "", { "dependencies": { "image-q": "^4.0.0", "zod": "^3.23.8" } }, "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg=="],
+
+ "@jimp/plugin-resize": ["@jimp/plugin-resize@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA=="],
+
+ "@jimp/plugin-rotate": ["@jimp/plugin-rotate@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw=="],
+
+ "@jimp/plugin-threshold": ["@jimp/plugin-threshold@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w=="],
+
+ "@jimp/types": ["@jimp/types@1.6.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg=="],
+
+ "@jimp/utils": ["@jimp/utils@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "tinycolor2": "^1.6.0" } }, "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA=="],
+
+ "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="],
+
+ "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="],
+
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="],
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
+ "@opentui/core": ["@opentui/core@0.1.75", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.75", "@opentui/core-darwin-x64": "0.1.75", "@opentui/core-linux-arm64": "0.1.75", "@opentui/core-linux-x64": "0.1.75", "@opentui/core-win32-arm64": "0.1.75", "@opentui/core-win32-x64": "0.1.75", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-8ARRZxSG+BXkJmEVtM2DQ4se7DAF1ZCKD07d+AklgTr2mxCzmdxxPbOwRzboSQ6FM7qGuTVPVbV4O2W9DpUmoA=="],
+
+ "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.75", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gGaGZjkFpqcXJk6321JzhRl66pM2VxBlI470L8W4DQUW4S6iDT1R9L7awSzGB4Cn9toUl7DTV8BemaXZYXV4SA=="],
+
+ "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.75", "", { "os": "darwin", "cpu": "x64" }, "sha512-tPlvqQI0whZ76amHydpJs5kN+QeWAIcFbI8RAtlAo9baj2EbxTDC+JGwgb9Fnt0/YQx831humbtaNDhV2Jt1bw=="],
+
+ "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.75", "", { "os": "linux", "cpu": "arm64" }, "sha512-nVxIQ4Hqf84uBergDpWiVzU6pzpjy6tqBHRQpySxZ2flkJ/U6/aMEizVrQ1jcgIdxZtvqWDETZhzxhG0yDx+cw=="],
+
+ "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.75", "", { "os": "linux", "cpu": "x64" }, "sha512-1CnApef4kxA+ORyLfbuCLgZfEjp4wr3HjFnt7FAfOb73kIZH82cb7JYixeqRyy9eOcKfKqxLmBYy3o8IDkc4Rg=="],
+
+ "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.75", "", { "os": "win32", "cpu": "arm64" }, "sha512-j0UB95nmkYGNzmOrs6GqaddO1S90R0YC6IhbKnbKBdjchFPNVLz9JpexAs6MBDXPZwdKAywMxtwG2h3aTJtxng=="],
+
+ "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.75", "", { "os": "win32", "cpu": "x64" }, "sha512-ESpVZVGewe3JkB2TwrG3VRbkxT909iPdtvgNT7xTCIYH2VB4jqZomJfvERPTE0tvqAZJm19mHECzJFI8asSJgQ=="],
+
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
@@ -67,20 +215,38 @@
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
- "@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+
+ "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
"@types/long": ["@types/long@4.0.2", "", {}, "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="],
"@types/node": ["@types/node@25.0.8", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg=="],
+ "@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="],
+
"@xenova/transformers": ["@xenova/transformers@2.17.2", "", { "dependencies": { "@huggingface/jinja": "^0.2.2", "onnxruntime-web": "1.14.0", "sharp": "^0.32.0" }, "optionalDependencies": { "onnxruntime-node": "1.14.0" } }, "sha512-lZmHqzrVIkSvZdKZEx7IYY51TK0WDrC8eR0c5IMnBsO8di8are1zzw8BlLhyO2TklZKLN5UffNGs1IJwT6oOqQ=="],
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
+ "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
+
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
+
+ "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="],
+
"b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="],
"bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="],
@@ -97,13 +263,31 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+ "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="],
+
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
+ "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="],
+
"body-parser": ["body-parser@2.2.2", "", { "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" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
- "bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
+ "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
+
+ "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
+
+ "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
+
+ "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="],
+
+ "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-X+PjwJUWenUmdQBP8EtdItMyieQ6Nlpn+BH518oaouDiSnWj5+b0Y7DNDZJq7Ezom4EaxmqL/uGYZK3aCQ7CXg=="],
+
+ "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-zMLs2YIGB+/jxrYFXaFhVKX/GBt05UTF45lc9srcHc9JXGjEj+12CIo1CHLTAWatXMTqt0Jsu6ukWEoWVT/ayA=="],
+
+ "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
@@ -111,8 +295,16 @@
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
+ "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
+
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
+ "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
+
+ "code-recall": ["code-recall@workspace:apps/server"],
+
+ "code-recall-tui": ["code-recall-tui@workspace:apps/tui"],
+
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
@@ -141,8 +333,14 @@
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
+ "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="],
+
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+ "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
+
+ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
@@ -151,6 +349,8 @@
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
+ "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
+
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
@@ -159,28 +359,48 @@
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
+ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
+
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
+
+ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
+
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
+ "exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="],
+
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
"express": ["express@5.2.1", "", { "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" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
+ "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="],
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
+ "fast-glob": ["fast-glob@3.3.3", "", { "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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
+
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
+ "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
+
+ "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
"finalhandler": ["finalhandler@2.1.1", "", { "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" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
+ "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
+
"flatbuffers": ["flatbuffers@1.12.0", "", {}, "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
@@ -189,16 +409,26 @@
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
+ "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="],
+
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+ "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
+
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
+ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
+
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
"guid-typescript": ["guid-typescript@1.0.9", "", {}, "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
@@ -209,10 +439,16 @@
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
+ "human-id": ["human-id@4.1.3", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q=="],
+
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
+
+ "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
+
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
@@ -221,24 +457,54 @@
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
+ "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="],
+
+ "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="],
+
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+ "jimp": ["jimp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-gif": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-blur": "1.6.0", "@jimp/plugin-circle": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-contain": "1.6.0", "@jimp/plugin-cover": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-displace": "1.6.0", "@jimp/plugin-dither": "1.6.0", "@jimp/plugin-fisheye": "1.6.0", "@jimp/plugin-flip": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/plugin-mask": "1.6.0", "@jimp/plugin-print": "1.6.0", "@jimp/plugin-quantize": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/plugin-rotate": "1.6.0", "@jimp/plugin-threshold": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg=="],
+
"jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
+ "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
+
+ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
+
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
+ "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
+
+ "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
+
+ "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="],
+
"long": ["long@4.0.0", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="],
+ "marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
+
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
@@ -249,6 +515,8 @@
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
+ "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
@@ -265,6 +533,8 @@
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
+ "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
+
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
@@ -277,18 +547,62 @@
"onnxruntime-web": ["onnxruntime-web@1.14.0", "", { "dependencies": { "flatbuffers": "^1.12.0", "guid-typescript": "^1.0.9", "long": "^4.0.0", "onnx-proto": "^4.0.4", "onnxruntime-common": "~1.14.0", "platform": "^1.3.6" } }, "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw=="],
+ "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="],
+
+ "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="],
+
+ "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
+
+ "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
+
+ "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="],
+
+ "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
+
+ "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="],
+
+ "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
+
+ "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
+
+ "parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="],
+
+ "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="],
+
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
+ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
+
+ "peek-readable": ["peek-readable@4.1.0", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="],
+
+ "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="],
+
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
+ "planck": ["planck@1.4.2", "", { "peerDependencies": { "stage-js": "^1.0.0-alpha.12" } }, "sha512-mNbhnV3g8X2rwGxzcesjmN8BDA6qfXgQxXVMkWau9MCRlQY0RLNEkyHlVp6yFy/X6qrzAXyNONCnZ1cGDLrNew=="],
+
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
+ "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="],
+
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
+ "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
+
+ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
+
"protobufjs": ["protobufjs@6.11.4", "", { "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/long": "^4.0.1", "@types/node": ">=13.7.0", "long": "^4.0.0" }, "bin": { "pbjs": "bin/pbjs", "pbts": "bin/pbts" } }, "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
@@ -297,22 +611,38 @@
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
+ "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
+
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
+ "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="],
+
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
+ "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="],
+
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
+ "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
+
+ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
+ "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="],
+
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"send": ["send@1.2.1", "", { "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" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
@@ -335,12 +665,22 @@
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "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" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
+ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
+ "simple-xml-to-json": ["simple-xml-to-json@1.2.3", "", {}, "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA=="],
+
+ "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
+
+ "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="],
+
+ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
+
"sqlite-vec": ["sqlite-vec@0.1.7-alpha.2", "", { "optionalDependencies": { "sqlite-vec-darwin-arm64": "0.1.7-alpha.2", "sqlite-vec-darwin-x64": "0.1.7-alpha.2", "sqlite-vec-linux-arm64": "0.1.7-alpha.2", "sqlite-vec-linux-x64": "0.1.7-alpha.2", "sqlite-vec-windows-x64": "0.1.7-alpha.2" } }, "sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ=="],
"sqlite-vec-darwin-arm64": ["sqlite-vec-darwin-arm64@0.1.7-alpha.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw=="],
@@ -353,36 +693,72 @@
"sqlite-vec-windows-x64": ["sqlite-vec-windows-x64@0.1.7-alpha.2", "", { "os": "win32", "cpu": "x64" }, "sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ=="],
+ "stage-js": ["stage-js@1.0.0-alpha.18", "", {}, "sha512-Mh+pbkfxA6NXlDrcutP8vp1Zg04pDRcC8D39UXKZzEcQeBPOZ4SRUSkIsF26aoODUZ4CSQRY7shXc1Avb0wZKA=="],
+
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
+
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
+ "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="],
+
"tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="],
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
+ "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
+
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
+ "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="],
+
+ "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
+ "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="],
+
"tree-sitter-javascript": ["tree-sitter-javascript@0.25.0", "", { "dependencies": { "node-addon-api": "^8.3.1", "node-gyp-build": "^4.8.4" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-1fCbmzAskZkxcZzN41sFZ2br2iqTYP3tKls1b/HKGNPQUVOpsUxpmGxdN/wMqAk3jYZnYBR1dd/y/0avMeU7dw=="],
"tree-sitter-typescript": ["tree-sitter-typescript@0.23.2", "", { "dependencies": { "node-addon-api": "^8.2.2", "node-gyp-build": "^4.8.2", "tree-sitter-javascript": "^0.23.1" }, "peerDependencies": { "tree-sitter": "^0.21.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
+ "turbo": ["turbo@2.7.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.4", "turbo-darwin-arm64": "2.7.4", "turbo-linux-64": "2.7.4", "turbo-linux-arm64": "2.7.4", "turbo-windows-64": "2.7.4", "turbo-windows-arm64": "2.7.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bkO4AddmDishzJB2ze7aYYPaejMoJVfS0XnaR6RCdXFOY8JGJfQE+l9fKiV7uDPa5Ut44gmOWJL3894CIMeH9g=="],
+
+ "turbo-darwin-64": ["turbo-darwin-64@2.7.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDR30ltfkSsRfGzABBckvl1nz1cZ3ssTujvdj+TPwOweeDRvZ0e06t5DS0rmRBvyKpgGs42K/EK6Mn2qLlFY9A=="],
+
+ "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7sjqXtOL/+nYWPvcDGWhi8wf8M8mZHHB8XEzw2VX7VJrS8IGHyJHGD1AYfDvhAEcr7pnk3gGifz3/xyhI655w=="],
+
+ "turbo-linux-64": ["turbo-linux-64@2.7.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GofFOxRO/IhG8BcPyMSSB3Y2+oKQotsaYbHxL9yD6JPb20/o35eo+zUSyazOtilAwDHnak5dorAJFoFU8MIg2A=="],
+
+ "turbo-linux-arm64": ["turbo-linux-arm64@2.7.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+RQKgNjksVPxYAyAgmDV7w/1qj++qca+nSNTAOKGOfJiDtSvRKoci89oftJ6anGs00uamLKVEQ712TI/tfNAIw=="],
+
+ "turbo-windows-64": ["turbo-windows-64@2.7.4", "", { "os": "win32", "cpu": "x64" }, "sha512-rfak1+g+ON3czs1mDYsCS4X74ZmK6gOgRQTXjDICtzvR4o61paqtgAYtNPofcVsMWeF4wvCajSeoAkkeAnQ1kg=="],
+
+ "turbo-windows-arm64": ["turbo-windows-arm64@2.7.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-1ZgBNjNRbDu/fPeqXuX9i26x3CJ/Y1gcwUpQ+Vp7kN9Un6RZ9kzs164f/knrjcu5E+szCRexVjRSJay1k5jApA=="],
+
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+ "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
+
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
+ "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
+
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
@@ -393,16 +769,44 @@
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
+ "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="],
+
+ "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
+
+ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
+
+ "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
+
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
+ "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="],
+
+ "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
+
+ "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="],
+
+ "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
+
+ "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="],
+
+ "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
+
"prebuild-install/tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
+ "read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
+
+ "readable-web-to-node-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+
"sharp/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
"tree-sitter-typescript/tree-sitter-javascript": ["tree-sitter-javascript@0.23.1", "", { "dependencies": { "node-addon-api": "^8.2.2", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.21.1" }, "optionalPeers": ["tree-sitter"] }, "sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA=="],
"prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
+
+ "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
+
+ "readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
}
}
diff --git a/package.json b/package.json
index 1b69863..cbe344d 100644
--- a/package.json
+++ b/package.json
@@ -1,61 +1,27 @@
{
- "name": "code-recall",
- "version": "1.0.0",
- "description": "Ultra-fast MCP server for semantic memory and code analysis",
- "module": "src/index.ts",
- "type": "module",
- "bin": {
- "code-recall": "src/index.ts"
- },
- "files": [
- "src",
- "README.md",
- "LICENSE"
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/AbianS/code-recall.git"
- },
- "keywords": [
- "mcp",
- "memory",
- "ai",
- "coding-agent",
- "semantic-search",
- "sqlite",
- "bun"
- ],
- "author": "AbianS",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/AbianS/code-recall/issues"
- },
- "homepage": "https://github.com/AbianS/code-recall#readme",
- "engines": {
- "bun": ">=1.0.0"
- },
+ "name": "code-recall-monorepo",
+ "private": true,
"scripts": {
- "start": "bun run src/index.ts",
- "dev": "bun --watch run src/index.ts",
- "build": "bun build src/index.ts --outdir dist --target bun",
- "test": "bun test",
- "lint": "biome check src tests",
- "lint:fix": "biome check --write src tests"
- },
- "dependencies": {
- "@modelcontextprotocol/sdk": "1.25.2",
- "@xenova/transformers": "2.17.2",
- "sqlite-vec": "0.1.7-alpha.2",
- "tree-sitter-javascript": "0.25.0",
- "tree-sitter-typescript": "0.23.2",
- "web-tree-sitter": "0.26.3",
- "zod": "3.25.76"
+ "build": "bun --bun turbo run build",
+ "dev": "bun --bun turbo run dev",
+ "test": "bun --bun turbo run test",
+ "lint": "turbo run lint",
+ "lint:fix": "turbo run lint:fix",
+ "changeset": "changeset",
+ "version": "changeset version",
+ "release": "bun --bun turbo run build && changeset publish"
},
"devDependencies": {
"@biomejs/biome": "2.3.11",
- "@types/bun": "1.2.15"
+ "@changesets/cli": "2.29.8",
+ "turbo": "2.7.4",
+ "typescript": "5.9.3"
+ },
+ "engines": {
+ "bun": ">=1.0.0"
},
- "peerDependencies": {
- "typescript": "5"
- }
+ "packageManager": "bun@1.3.5",
+ "workspaces": [
+ "apps/*"
+ ]
}
diff --git a/turbo.json b/turbo.json
new file mode 100644
index 0000000..03f960a
--- /dev/null
+++ b/turbo.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://turborepo.dev/schema.json",
+ "ui": "tui",
+ "tasks": {
+ "build": {
+ "dependsOn": ["^build"],
+ "inputs": ["$TURBO_DEFAULT$", ".env*"],
+ "outputs": ["dist/**"]
+ },
+ "test": {
+ "dependsOn": ["^build"],
+ "inputs": ["$TURBO_DEFAULT$", "src/**", "tests/**"],
+ "outputs": []
+ },
+ "lint": {
+ "inputs": ["$TURBO_DEFAULT$", "src/**", "tests/**"],
+ "outputs": []
+ },
+ "lint:fix": {
+ "inputs": ["$TURBO_DEFAULT$", "src/**", "tests/**"],
+ "outputs": []
+ },
+ "check-types": {
+ "dependsOn": ["^check-types"]
+ },
+ "dev": {
+ "cache": false,
+ "persistent": true
+ }
+ }
+}