Merge pull request #76 from samvallad33/codex/opencode-sigill-salvage

[codex] Add OpenCode integration and safer startup
This commit is contained in:
Sam Valladares 2026-06-18 20:54:01 -05:00 committed by GitHub
commit 05f0050ad8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 722 additions and 48 deletions

View file

@ -14,7 +14,7 @@
Built on proven memory and retrieval ideas — FSRS-6 spaced repetition, prediction error gating, synaptic tagging, spreading activation, and memory consolidation — all running in a single Rust binary with a local dashboard. 100% local. Zero cloud.
[Quick Start](#quick-start) | [Dashboard](#-3d-memory-dashboard) | [How It Works](#-the-cognitive-science-stack) | [Tools](#-25-mcp-tools) | [Docs](docs/)
[Quick Start](#quick-start) | [Dashboard](#-3d-memory-dashboard) | [How It Works](#-the-cognitive-science-stack) | [Tools](#-25-mcp-tools) | [Docs](docs/) | [Roadmap](docs/ROADMAP.md)
</div>
@ -48,6 +48,9 @@ claude mcp add vestige vestige-mcp -s user
# Codex
codex mcp add vestige -- vestige-mcp
# OpenCode
npx @vestige/init
# 3. Test it
# "Remember that I prefer TypeScript over JavaScript"
# ...new session...
@ -142,6 +145,7 @@ Vestige speaks MCP, so any client that can register a stdio MCP server can use i
| **Xcode 26.3** | [Integration guide](docs/integrations/xcode.md) |
| **Cursor** | [Integration guide](docs/integrations/cursor.md) |
| **VS Code (Copilot)** | [Integration guide](docs/integrations/vscode.md) |
| **OpenCode** | [Integration guide](docs/integrations/opencode.md) |
| **JetBrains** | [Integration guide](docs/integrations/jetbrains.md) |
| **Windsurf** | [Integration guide](docs/integrations/windsurf.md) |
@ -422,7 +426,7 @@ vestige dashboard # Open 3D dashboard in browser
| [Storage Modes](docs/STORAGE.md) | Global, per-project, multi-instance |
| [CLAUDE.md Setup](docs/CLAUDE-SETUP.md) | Templates for proactive memory |
| [Configuration](docs/CONFIGURATION.md) | CLI commands, environment variables |
| [Integrations](docs/integrations/) | Codex, Xcode, Cursor, VS Code, JetBrains, Windsurf |
| [Integrations](docs/integrations/) | Codex, Xcode, Cursor, VS Code, OpenCode, JetBrains, Windsurf |
| [Changelog](CHANGELOG.md) | Version history |
---

View file

@ -121,7 +121,11 @@ candle-core = { version = "0.10.2", optional = true }
# its memory_mapping_allocator_gt template references the POSIX MAP_FAILED
# macro from <sys/mman.h>, which doesn't exist on MSVC. Tracked upstream in
# unum-cloud/usearch#746. Unpin when the upstream fix lands.
usearch = { version = "=2.23.0", optional = true }
#
# Disable default features so release binaries do not include SimSIMD's
# Haswell+/AVX2/FMA dispatch targets. Those kernels can trigger illegal
# instructions on older x86_64 CPUs that Vestige otherwise supports.
usearch = { version = "=2.23.0", default-features = false, optional = true }
# LRU cache for query embeddings
lru = "0.16"

View file

@ -115,6 +115,14 @@ impl EmbedderSend for FastembedEmbedder {
mod tests {
use super::*;
#[cfg(feature = "embeddings")]
fn is_model_unavailable(err: &EmbedderError) -> bool {
let msg = err.to_string();
msg.contains("Failed to retrieve")
|| msg.contains("model files can be downloaded")
|| msg.contains("Failed to initialize nomic-ai/nomic-embed-text-v1.5")
}
#[test]
fn embedder_reports_correct_name() {
let e = FastembedEmbedder::new();
@ -162,7 +170,14 @@ mod tests {
fn embedder_embed_smoke() {
let e = FastembedEmbedder::new();
let rt = tokio::runtime::Runtime::new().unwrap();
let vec = rt.block_on(e.embed("hello world")).expect("embed");
let vec = match rt.block_on(e.embed("hello world")) {
Ok(vec) => vec,
Err(err) if is_model_unavailable(&err) => {
eprintln!("skipping fastembed smoke; model unavailable: {err}");
return;
}
Err(err) => panic!("embed: {err}"),
};
assert_eq!(vec.len(), 256);
}
@ -172,9 +187,30 @@ mod tests {
let e = FastembedEmbedder::new();
let rt = tokio::runtime::Runtime::new().unwrap();
let texts = ["alpha beta", "gamma delta"];
let batch = rt.block_on(e.embed_batch(texts.as_ref())).expect("batch");
let seq_a = rt.block_on(e.embed(texts[0])).expect("seq a");
let seq_b = rt.block_on(e.embed(texts[1])).expect("seq b");
let batch = match rt.block_on(e.embed_batch(texts.as_ref())) {
Ok(batch) => batch,
Err(err) if is_model_unavailable(&err) => {
eprintln!("skipping fastembed batch smoke; model unavailable: {err}");
return;
}
Err(err) => panic!("batch: {err}"),
};
let seq_a = match rt.block_on(e.embed(texts[0])) {
Ok(vec) => vec,
Err(err) if is_model_unavailable(&err) => {
eprintln!("skipping fastembed sequential smoke; model unavailable: {err}");
return;
}
Err(err) => panic!("seq a: {err}"),
};
let seq_b = match rt.block_on(e.embed(texts[1])) {
Ok(vec) => vec,
Err(err) if is_model_unavailable(&err) => {
eprintln!("skipping fastembed sequential smoke; model unavailable: {err}");
return;
}
Err(err) => panic!("seq b: {err}"),
};
assert_eq!(batch[0], seq_a);
assert_eq!(batch[1], seq_b);
}

View file

@ -301,21 +301,6 @@ async fn main() {
let storage = match Storage::new(storage_path) {
Ok(s) => {
info!("Storage initialized successfully");
// Try to initialize embeddings early and log any issues
#[cfg(feature = "embeddings")]
{
if let Err(e) = s.init_embeddings() {
error!("Failed to initialize embedding service: {}", e);
error!("Smart ingest will fall back to regular ingest without deduplication");
error!(
"Hint: Check FASTEMBED_CACHE_PATH or ensure ~/.cache/vestige/fastembed is writable"
);
} else {
info!("Embedding service initialized successfully");
}
}
Arc::new(s)
}
Err(e) => {
@ -324,6 +309,40 @@ async fn main() {
}
};
// Initialize embeddings in the background so MCP clients can complete the
// stdio handshake quickly. First-run model downloads can otherwise exceed
// short client startup timeouts.
#[cfg(feature = "embeddings")]
{
let storage_clone = Arc::clone(&storage);
tokio::task::spawn_blocking(move || {
if let Err(e) = storage_clone.init_embeddings() {
error!("Failed to initialize embedding service: {}", e);
error!("Smart ingest will fall back to regular ingest without deduplication");
error!(
"Hint: Check FASTEMBED_CACHE_PATH or ensure ~/.cache/vestige/fastembed is writable"
);
} else {
info!("Embedding service initialized successfully");
#[cfg(feature = "vector-search")]
match storage_clone.generate_embeddings(None, false) {
Ok(result) => {
if result.successful > 0 || result.failed > 0 {
info!(
embeddings_generated = result.successful,
embeddings_failed = result.failed,
embeddings_skipped = result.skipped,
"Background embedding backfill complete"
);
}
}
Err(e) => warn!("Background embedding backfill failed: {}", e),
}
}
});
}
// Spawn periodic auto-consolidation so FSRS-6 decay scores stay fresh.
// Runs on startup (if needed) and then every N hours (default: 6).
// Configurable via VESTIGE_CONSOLIDATION_INTERVAL_HOURS env var.
@ -506,7 +525,7 @@ async fn main() {
}
// Load cross-encoder reranker in the background (downloads ~150MB on first run)
#[cfg(feature = "vector-search")]
#[cfg(all(feature = "vector-search", feature = "embeddings"))]
{
let cog_clone = Arc::clone(&cognitive);
tokio::spawn(async move {

View file

@ -76,6 +76,6 @@ call `memory` with `action="purge"` and `confirm=true`.
## Portability Notes
The same protocol applies to Claude Code, Codex, Cursor, VS Code, Xcode,
JetBrains, Windsurf, and any other client that can run a stdio MCP server. Claude
OpenCode, JetBrains, Windsurf, and any other client that can run a stdio MCP server. Claude
Code's Cognitive Sandwich hooks are optional companion files; they are not
required for normal Vestige memory.

View file

@ -228,20 +228,42 @@ Add to `%APPDATA%\Claude\claude_desktop_config.json`:
}
```
### Opencode TUI/Desktop
### OpenCode
You can put it at [various different](https://opencode.ai/docs/config/#locations) locations. I recommend `opencode.json` in the project folder.
OpenCode supports global and project-local config. For a project-local setup, add to `opencode.json`:
```json
{
"mcpServers": {
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"command": "vestige-mcp"
"type": "local",
"command": ["vestige-mcp"],
"enabled": true,
"timeout": 10000
}
}
}
```
For isolated per-project memory, pass the data directory in the command array:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["vestige-mcp", "--data-dir", "./.vestige"],
"enabled": true,
"timeout": 10000
}
}
}
```
See the [OpenCode integration guide](integrations/opencode.md) for global config, verification, and troubleshooting.
---
## Custom Data Directory

View file

@ -782,22 +782,16 @@ This helps trace why you know something.
<details>
<summary><b>"What's planned for future versions?"</b></summary>
Based on codebase exploration, these features exist in various stages:
See the public [Vestige Roadmap](ROADMAP.md) for the current adoption plan. The
near-term focus is reducing first-user confusion before expanding the feature
surface:
| Feature | Status | Description |
|---------|--------|-------------|
| Memory Dreams | Partial | Automated offline consolidation |
| Reconsolidation | Planned | Update memories when accessed |
| Memory Chains | Partial | Link related memories explicitly |
| Adaptive Embedding | Planned | Re-embed old memories with better models |
| Cross-Project Learning | Planned | Share patterns across codebases |
**Community wishlist** (from Reddit):
- Stream ingestion mode
- GUI for memory browsing
- Export/import formats
- Sync between devices (encrypted)
- Team collaboration features
- first-time memory migration and atomic memory guidance
- configurable MCP output fields and output profiles
- clearer merge/supersede controls
- code/docstring memory workflows
- goals and milestones distinct from intentions
- guided import dry runs and review queues
Contributions welcome!
</details>

142
docs/ROADMAP.md Normal file
View file

@ -0,0 +1,142 @@
# Vestige Roadmap
> Public adoption roadmap for making Vestige easier to start, easier to trust,
> and easier to configure.
Last updated: June 7, 2026
Vestige already has the core primitives for durable local memory: `search`,
`session_context`, `smart_ingest`, `memory`, `intention`, `codebase`,
`deep_reference`, suppression, portable storage, and the dashboard. The next
product step is reducing first-user confusion so more people can get value from
those primitives without inventing their own fragile memory vocabulary.
This roadmap turns early community feedback into a staged plan.
## Principles
- Make first use obvious. A new user should know what to import, how atomic each
memory should be, and which tool to use for current session context.
- Keep memory legible. Agents and humans should understand whether a memory was
created, reinforced, updated, superseded, suppressed, or purged.
- Prefer progressive disclosure. The default MCP response should be lean, with
explicit ways to request more detail.
- Keep local-first behavior. New onboarding, code memory, and configuration
features must not require a cloud service.
- Optimize for many users. Defaults should work for non-experts, while power
users can tune fields, merge behavior, and formats.
## Already Shipped, Needs Clearer Guidance
| Area | Current State | Next Documentation Fix |
|------|---------------|------------------------|
| Session startup | `session_context` combines memories, intentions, status, predictions, and codebase context. | Update all agent setup templates to make `session_context` the default startup call. |
| Batch memory saves | `smart_ingest` batch mode defaults to `batchMergePolicy="force_create"` so caller-separated items stay separate. | Document when to use batch force-create vs smart merge. |
| Device migration | `portable-export`, `portable-import`, and `sync` preserve exact Vestige storage state. | Separate device migration from first-time document import so users do not confuse them. |
| Supersede semantics | Supersede demotes the old memory and creates a new one; it does not purge the old memory. | Add plain-language vocabulary for create, update, supersede, suppress, demote, and purge. |
## Phase 1: Onboarding And Memory Hygiene
Target: make the first 30 minutes with Vestige hard to mess up.
| Work | Outcome |
|------|---------|
| First-time memory migration guide | Users can import notes/docs without Claude tagging everything as `verified` or flattening unrelated facts together. |
| Atomic memory guide | Clear examples for one fact, one preference, one decision, one bug fix, one source note, and one code pattern per memory. |
| Default tag vocabulary | Recommended tags for source quality, confidence, project, type, urgency, and lifecycle without overloading words like `verified`. |
| Smart vs force-create guide | Agents know when to use `forceCreate`, `batchMergePolicy="force_create"`, or normal PE gating. |
| Updated agent templates | Claude, Codex, Cursor, VS Code, Xcode, OpenCode, JetBrains, and Windsurf templates start with `session_context` and use the same memory vocabulary. |
Planned docs:
- `docs/MIGRATION.md`
- `docs/MEMORY-HYGIENE.md`
- revised `docs/AGENT-MEMORY-PROTOCOL.md`
- revised `docs/CLAUDE-SETUP.md`
## Phase 2: Configurable Output
Target: let users control context cost without losing important evidence.
| Work | Outcome |
|------|---------|
| Field masks for MCP results | Users can drop fields they never want in model context, such as temporal hints, scores, or timestamps. |
| Output profiles | Presets like `lean`, `default`, `audit`, and `research` tune result size and metadata detail. |
| Markdown output mode | Users can request compact Markdown summaries when that is more context-efficient than JSON. |
| Context reinstatement controls | `contextReinstatement` becomes opt-in or configurable, and temporal hints are based on stored memory context when available. |
| Per-tool defaults | Users can define default detail level, result limit, and response shape for search, timeline, codebase, and session context. |
Likely implementation paths:
- config file under the active Vestige data directory
- environment-variable override for simple deployments
- MCP parameters still win over defaults for one-off calls
## Phase 3: Merge And Supersede Controls
Target: make memory mutation predictable.
| Work | Outcome |
|------|---------|
| Merge policy configuration | Users can keep some tags or node types atomic while allowing others to merge. |
| Prediction Error threshold knobs | Advanced users can tune create/update/reinforce boundaries without recompiling. |
| Merge previews before mutation | Agents can show what would change before updating an existing durable memory. |
| Safer consolidation dedup | Consolidation respects user-configured atomic tags and source boundaries. |
| Friendlier lifecycle labels | Agent-facing copy explains that superseded memories are old versions, not destroyed records. |
## Phase 4: Code Memory
Target: make code memories useful without blending source code, docstrings, and
human project notes into one noisy search space.
| Work | Outcome |
|------|---------|
| Code memory import guide | Developers know when to save patterns/decisions versus code entities or docstrings. |
| Exposed code entity workflow | The existing core `CodeEntity` concept becomes usable through MCP or CLI. |
| Docstring/code symbol ingestion | Users can ingest functions, types, modules, docstrings, and call-site notes with source file provenance. |
| Code/prose retrieval separation | Search can filter or rank code memories separately from user preferences and project decisions. |
| Codebase dashboard review | Developers can inspect imported code memories and remove noisy entries. |
## Phase 5: Goals And Milestones
Target: support durable direction without pretending every future task is just a
reminder.
| Work | Outcome |
|------|---------|
| Goal primitive | Non-fading, manually pivoted goals that survive normal memory decay. |
| Milestone tracking | Goals can have milestones, status, evidence, and blockers. |
| Goal-aware session context | `session_context` can include active goals when relevant. |
| Manual pivot semantics | Agents can update goals only when the user explicitly pivots, completes, or cancels them. |
| Dashboard surface | Users can inspect active, completed, paused, and cancelled goals. |
This is distinct from `intention`: intentions are reminders triggered by time,
topic, file, event, or context. Goals are longer-lived direction and should not
fire as reminders unless the user attaches an intention.
## Phase 6: Guided Import Tools
Target: turn "I have 300 notes" into a reliable workflow.
| Work | Outcome |
|------|---------|
| Import dry run | Vestige previews proposed memories, tags, node types, and merge decisions before writing. |
| Source-aware import | Imported memories keep file/source provenance and confidence metadata. |
| Chunking strategies | Users choose atomic facts, section summaries, decision records, or source notes. |
| Review queue | Users can approve, edit, split, merge, or reject proposed memories. |
| Post-import health pass | Vestige recommends consolidation, duplicate review, or tag cleanup after import. |
## Non-Goals
- Do not auto-store every conversation turn by default.
- Do not require cloud services for memory creation, search, or configuration.
- Do not hide irreversible deletion. `purge` must stay explicit.
- Do not make code ingestion pollute general personal memory by default.
- Do not make advanced tuning required for ordinary users.
## How To Read This Roadmap
This is directional, not a release guarantee. The priority is adoption: fewer
surprises, clearer defaults, and better tool descriptions before adding complex
new surfaces. Community feedback that reveals a confusing first-use path should
usually become either a documentation fix, a safer default, or a guided workflow.

View file

@ -73,8 +73,8 @@ Vestige is organized as:
HTTP MCP is disabled unless the user passes `--http`, passes `--http-port`, or
sets `VESTIGE_HTTP_ENABLED=1`. The stdio MCP server remains the portable default
for Claude Code, Codex, Cursor, VS Code, Xcode, JetBrains, Windsurf, and other
clients.
for Claude Code, Codex, Cursor, VS Code, Xcode, OpenCode, JetBrains, Windsurf,
and other clients.
Purge is implemented transactionally in storage and surfaced through the MCP
`memory` tool. `memory(action="purge", confirm=true)` is the explicit hard

View file

@ -145,6 +145,7 @@ codex mcp add vestige -- /usr/local/bin/vestige-mcp
| Xcode 26.3 | [Setup](./xcode.md) |
| Cursor | [Setup](./cursor.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| OpenCode | [Setup](./opencode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Windsurf | [Setup](./windsurf.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |

View file

@ -135,6 +135,7 @@ Cursor does not surface MCP server errors in the UI. Test by running the command
| Xcode 26.3 | [Setup](./xcode.md) |
| Codex | [Setup](./codex.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| OpenCode | [Setup](./opencode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Windsurf | [Setup](./windsurf.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |

View file

@ -123,6 +123,7 @@ In **Settings > Tools > MCP Server**, click the expansion arrow next to your cli
| Cursor | [Setup](./cursor.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| Codex | [Setup](./codex.md) |
| OpenCode | [Setup](./opencode.md) |
| Windsurf | [Setup](./windsurf.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |
| Claude Desktop | [Setup](../CONFIGURATION.md#claude-desktop-macos) |

View file

@ -0,0 +1,233 @@
# OpenCode
> Give OpenCode persistent local memory across TUI, CLI, and desktop sessions.
OpenCode supports local MCP servers through its `mcp` config. Add Vestige once and your OpenCode agents can remember project decisions, architecture context, preferences, and previous fixes between sessions.
Verified with OpenCode `1.16.2` on June 8, 2026.
---
## Why OpenCode Users Add Vestige
OpenCode is strong at driving real coding work from the terminal. The painful gap is continuity: the next session often has to rediscover what the previous session already learned. Vestige gives OpenCode a local memory layer through MCP, so the agent can reuse the project context that should not be trapped in one chat transcript.
Useful memories include:
- project decisions: "we use Axum handlers thinly and keep database logic in storage modules"
- preferences: "prefer small focused PRs and explicit verification receipts"
- architecture context: "the dashboard talks to the MCP server through the Axum backend and WebSocket events"
- bug fixes: "OpenCode rejects `mcpServers`; use top-level `mcp.vestige` with a command array"
- workflow state: "PR #67 was merged, but the config shape needed correction before promotion"
Vestige is local-first. Memories are stored in SQLite on your machine, can be scoped globally or per project, and are retrieved with tools like `vestige_session_context`, `vestige_search`, `vestige_smart_ingest`, and `vestige_deep_reference`.
---
## Setup
### 1. Install Vestige
```bash
npm install -g vestige-mcp-server@latest
```
Verify the binary:
```bash
vestige-mcp --version
```
If you prefer not to install globally, use `npx` directly in the OpenCode command array:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"],
"enabled": true,
"timeout": 60000
}
}
}
```
The higher timeout is for the first cold `npx` run, which may need to download the npm package before OpenCode can connect. If you install `vestige-mcp-server` globally, `10000` is enough for normal startup.
If `npx` times out against an older published Vestige build, install globally once and use `command: ["vestige-mcp"]`. The current integration keeps the MCP handshake fast by moving embedding startup work into the background.
### 2. Add Vestige To OpenCode
For global use across projects, create or edit:
```bash
mkdir -p ~/.config/opencode
${EDITOR:-vi} ~/.config/opencode/opencode.json
```
Add:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["vestige-mcp"],
"enabled": true,
"timeout": 10000
}
}
}
```
OpenCode also supports project-local config. Put the same block in `opencode.json` at the repo root when you want the setting checked in with a project.
For a custom config file, set `OPENCODE_CONFIG=/path/to/opencode.json` before launching OpenCode.
### 3. Verify
Restart OpenCode, then validate the resolved config and MCP server list:
```bash
opencode debug config
opencode mcp list
```
You should see `vestige` listed. In a session, ask:
> "What MCP tools can you use?"
Vestige tools should be available with the `vestige_` prefix, such as `vestige_search`, `vestige_smart_ingest`, `vestige_session_context`, and `vestige_deep_reference`.
---
## First Use
In OpenCode:
> "Remember that this project uses Rust with Axum and SQLite."
Start a new OpenCode session, then ask:
> "What stack does this project use?"
It remembers.
---
## Project-Specific Memory
To isolate memory per repo, add `--data-dir` to OpenCode's command array:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["vestige-mcp", "--data-dir", "./.vestige"],
"enabled": true,
"timeout": 10000
}
}
}
```
For an absolute path:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["/usr/local/bin/vestige-mcp", "--data-dir", "/Users/you/projects/my-app/.vestige"],
"enabled": true,
"timeout": 10000
}
}
}
```
---
## Automatic Setup
If `opencode` is installed or `~/.config/opencode` exists, Vestige's installer can add the global config automatically:
```bash
npx @vestige/init
```
The installer writes a backup before modifying an existing config file. It also migrates Vestige entries copied from older `mcpServers` examples into OpenCode's current `mcp.vestige` shape.
---
## Troubleshooting
<details>
<summary>Vestige tools do not appear</summary>
1. Verify OpenCode can see configured MCP servers:
```bash
opencode debug config
opencode mcp list
```
2. Verify the binary is on your path:
```bash
which vestige-mcp
```
3. Use an absolute binary path if OpenCode cannot resolve `vestige-mcp`.
4. Restart OpenCode after changing `opencode.json`.
5. Keep `timeout` at `10000` or higher for installed binaries. If you use the direct `npx` command, use `60000` so the first cold npm download does not fail OpenCode startup.
</details>
<details>
<summary>Config does not validate</summary>
OpenCode uses the top-level `mcp` key. Do not use the `mcpServers` shape from Claude Desktop, Cursor, or Windsurf.
If you copied an older Vestige example that used `mcpServers`, rerun:
```bash
npx @vestige/init
```
Correct:
```json
{
"mcp": {
"vestige": {
"type": "local",
"command": ["vestige-mcp"],
"timeout": 10000
}
}
}
```
</details>
<details>
<summary>Too many MCP tools in context</summary>
OpenCode loads MCP tools alongside built-in tools. If you have many MCP servers enabled, disable unused servers or restrict MCP tools per agent in your OpenCode config.
</details>
---
## Also Works With
| IDE | Guide |
|-----|-------|
| Codex | [Setup](./codex.md) |
| Cursor | [Setup](./cursor.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Windsurf | [Setup](./windsurf.md) |
| Xcode 26.3 | [Setup](./xcode.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |
| Claude Desktop | [Setup](../CONFIGURATION.md#claude-desktop-macos) |

View file

@ -153,6 +153,7 @@ Every team member with Vestige installed will automatically get memory-enabled C
| Xcode 26.3 | [Setup](./xcode.md) |
| Cursor | [Setup](./cursor.md) |
| Codex | [Setup](./codex.md) |
| OpenCode | [Setup](./opencode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Windsurf | [Setup](./windsurf.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |

View file

@ -149,6 +149,7 @@ If you have many MCP servers and exceed 100 total tools, Cascade will ignore exc
| Cursor | [Setup](./cursor.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| Codex | [Setup](./codex.md) |
| OpenCode | [Setup](./opencode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Claude Code | [Setup](../CONFIGURATION.md#claude-code-one-liner) |
| Claude Desktop | [Setup](../CONFIGURATION.md#claude-desktop-macos) |

View file

@ -252,6 +252,7 @@ Vestige uses the MCP standard — the same memory works across all your tools:
| Claude Desktop | [Setup](../CONFIGURATION.md#claude-desktop-macos) |
| Cursor | [Setup](./cursor.md) |
| VS Code (Copilot) | [Setup](./vscode.md) |
| OpenCode | [Setup](./opencode.md) |
| JetBrains | [Setup](./jetbrains.md) |
| Windsurf | [Setup](./windsurf.md) |

View file

@ -0,0 +1,123 @@
# OpenCode Adoption Plan
Status: Vestige was tested with OpenCode `1.16.2` on June 8, 2026. The working config uses OpenCode's top-level `mcp.vestige` schema, not `mcpServers`.
Public promotion started:
- Vestige PR #70: `https://github.com/samvallad33/vestige/pull/70`
- OpenCode issue: `https://github.com/anomalyco/opencode/issues/31402`
- OpenCode docs/ecosystem PR: `https://github.com/anomalyco/opencode/pull/31405`
- awesome-opencode PR: `https://github.com/awesome-opencode/awesome-opencode/pull/418`
- opencode.cafe listing request: `https://github.com/R44VC0RP/opencode.cafe/issues/6`
- OpenCode persistent memory comment: `https://github.com/anomalyco/opencode/issues/16077#issuecomment-4652064625`
## Release Gate
- PR #67 is merged upstream and should be treated as the contributor-driven starting point.
- Ship the corrected OpenCode config docs and `@vestige/init` migration from stale `mcpServers.vestige` to `mcp.vestige`.
- Ship the background embedding initialization fix before making direct `npx` the main OpenCode install path. A cold published `2.1.23` package can still time out while OpenCode waits for tools.
- After release, verify all three OpenCode paths again:
- installed binary: `command: ["vestige-mcp"]`
- project memory: `command: ["vestige-mcp", "--data-dir", "./.vestige"]`
- direct npm: `command: ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"]` with `timeout: 60000`
## Official OpenCode PR
Target repo: `https://github.com/anomalyco/opencode`
Files:
- `packages/web/src/content/docs/mcp-servers.mdx`
- `packages/web/src/content/docs/ecosystem.mdx`
MCP docs snippet:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"],
"enabled": true,
"timeout": 60000
}
}
}
```
Ecosystem row:
```md
| [Vestige](https://github.com/samvallad33/vestige) | Local MCP memory server for OpenCode that remembers project decisions, preferences, and previous fixes across sessions |
```
Positioning: local, inspectable MCP memory for OpenCode. Avoid claiming Vestige fixes OpenCode's process memory or session resume behavior.
## Awesome OpenCode
Target repo: `https://github.com/awesome-opencode/awesome-opencode`
Suggested entry, with category to confirm against maintainer preference (`data/projects/vestige.yaml` or `data/resources/vestige.yaml`):
```yaml
name: Vestige
repo: https://github.com/samvallad33/vestige
tagline: Local persistent memory for OpenCode
description: Local MCP server that lets OpenCode remember project decisions, preferences, architecture context, and previous fixes across sessions.
scope:
- global
- project
tags:
- mcp
- memory
- local-first
- sqlite
- opencode
min_version: 1.16.2
homepage: https://github.com/samvallad33/vestige/blob/main/docs/integrations/opencode.md
installation: |
npm install -g vestige-mcp-server@latest
npx @vestige/init
```
## MCP Directories
Current state:
- Official MCP Registry already lists `io.github.samvallad33/vestige` at `https://registry.modelcontextprotocol.io/v0/servers?search=vestige`.
- Smithery already lists Vestige and indexes 25 tools: `https://smithery.ai/server/@samvallad33/vestige`.
- Glama already lists Vestige, but the listing needs a refresh/fix if it shows no tools: `https://glama.ai/mcp/servers/samvallad33/vestige`.
- `mcp.so` does not show Vestige under the expected slugs yet; submit manually at `https://mcp.so/submit`.
Priority order:
1. Official MCP Registry: `https://github.com/modelcontextprotocol/registry`
2. Awesome MCP Servers: `https://github.com/punkpeye/awesome-mcp-servers`
3. Glama MCP directory: `https://glama.ai/mcp/servers`
4. Smithery: `https://smithery.ai`
5. PulseMCP: `https://www.pulsemcp.com`
Registry metadata is mostly ready: `server.json` exists and `packages/vestige-mcp-npm/package.json` has `mcpName: "io.github.samvallad33/vestige"`. Publish only when the package version and `server.json` version match the released npm package.
## Community Launch
Use tested technical copy, not hype:
> Vestige now works with OpenCode as a local MCP memory server. It gives OpenCode persistent memory for project decisions, preferences, architecture context, and previous fixes across sessions. Install with `npm install -g vestige-mcp-server@latest`, run `npx @vestige/init`, then verify with `opencode mcp list`.
High-signal channels after release:
- OpenCode Discord: `https://opencode.ai/discord`
- opencode.cafe MCP Server listing: `https://opencode.cafe`
- OpenCode memory-related GitHub issues, only where directly relevant
- Hacker News and Lobsters with a technical post about the tested OpenCode integration and failure modes
- npm keyword/discovery after the next package release includes `opencode`
## Proof Checklist
- `opencode debug config` accepts `mcp.vestige`.
- `opencode mcp list` shows `vestige connected`.
- Stale `mcpServers.vestige` examples fail in OpenCode and are migrated by `@vestige/init`.
- OpenCode tools are prefixed as `vestige_search`, `vestige_smart_ingest`, `vestige_session_context`, and `vestige_deep_reference`.
- The OpenCode guide says `timeout: 60000` for direct `npx` and `timeout: 10000` for installed binaries.

View file

@ -105,6 +105,21 @@ const IDE_CONFIGS = {
note: 'Tip: For project-level config, create .vscode/mcp.json with {"servers": {"vestige": ...}}',
},
'OpenCode': {
detect: () => {
try {
execSync(PLATFORM === 'win32' ? 'where opencode' : 'which opencode', { stdio: 'ignore' });
return true;
} catch {
return fs.existsSync(path.join(HOME, '.config', 'opencode'));
}
},
configPath: () => path.join(HOME, '.config', 'opencode', 'opencode.json'),
format: 'opencode',
key: 'mcp',
note: 'Tip: For project-level memory, add the same mcp.vestige block to an opencode.json in your repo root.',
},
'Xcode 26.3': {
detect: () => {
if (PLATFORM !== 'darwin') return false;
@ -152,7 +167,10 @@ function findBinary() {
// npm global install location
(() => {
try {
const npmPrefix = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
const npmPrefix = execSync('npm prefix -g', {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
}).trim();
return path.join(npmPrefix, 'bin', 'vestige-mcp');
} catch { return null; }
})(),
@ -164,7 +182,11 @@ function findBinary() {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore'],
}).trim();
if (result) candidates.unshift(result);
const firstMatch = result
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)[0];
if (firstMatch) candidates.unshift(firstMatch);
} catch {}
for (const candidate of candidates) {
@ -272,6 +294,16 @@ function buildVestigeConfig(binaryPath) {
};
}
function buildOpenCodeConfig(binaryPath) {
return {
type: 'local',
command: [binaryPath],
enabled: true,
timeout: 10000,
environment: {},
};
}
function buildXcodeConfig(binaryPath) {
return {
projects: {
@ -324,6 +356,28 @@ function injectConfig(ide, ideName, binaryPath) {
return false;
}
config.mcp.servers.vestige = buildVestigeConfig(binaryPath);
} else if (ide.format === 'opencode') {
// OpenCode uses top-level "mcp" entries with command arrays.
if (!config.$schema) config.$schema = 'https://opencode.ai/config.json';
if (!config.mcp) config.mcp = {};
let migratedOpenCodeConfig = false;
if (config.mcpServers && config.mcpServers.vestige) {
delete config.mcpServers.vestige;
migratedOpenCodeConfig = true;
if (Object.keys(config.mcpServers).length === 0) {
delete config.mcpServers;
}
console.log(` [migrate] ${ideName} — moved vestige from mcpServers to mcp`);
}
if (config.mcp.vestige) {
if (!migratedOpenCodeConfig) {
console.log(` [skip] ${ideName} — already configured`);
return false;
}
// Preserve the valid OpenCode entry while still writing the stale-key cleanup.
} else {
config.mcp.vestige = buildOpenCodeConfig(binaryPath);
}
} else {
// Standard mcpServers format (Cursor, Claude Desktop, JetBrains, Windsurf)
const key = ide.key || 'mcpServers';
@ -383,7 +437,7 @@ function main() {
if (detected.length === 0) {
console.log(' No supported IDEs found.');
console.log('');
console.log('Supported: Claude Code, Claude Desktop, Cursor, VS Code, Xcode, JetBrains, Windsurf');
console.log('Supported: Claude Code, Claude Desktop, Cursor, VS Code, OpenCode, Xcode, JetBrains, Windsurf');
process.exit(1);
}

View file

@ -13,6 +13,7 @@
"claude",
"copilot",
"cursor",
"opencode",
"xcode",
"jetbrains",
"windsurf",

View file

@ -54,6 +54,40 @@ codex mcp add vestige -- vestige-mcp
Then restart your MCP client.
**OpenCode**
Add to `~/.config/opencode/opencode.json` or a project-local `opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["vestige-mcp"],
"enabled": true,
"timeout": 10000
}
}
}
```
Prefer the installed `vestige-mcp` command for OpenCode. If you run Vestige directly through `npx`, use a longer first-run timeout because npm may need to download the package before OpenCode can connect:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"vestige": {
"type": "local",
"command": ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"],
"enabled": true,
"timeout": 60000
}
}
}
```
## Usage with Claude Desktop
Add to your Claude Desktop configuration:

View file

@ -14,6 +14,7 @@
"keywords": [
"mcp",
"claude",
"opencode",
"ai",
"memory",
"vestige",

View file

@ -258,6 +258,7 @@ async function main() {
console.log(' 1. Add vestige-mcp to any MCP-compatible agent.');
console.log(' Claude Code: claude mcp add vestige vestige-mcp -s user');
console.log(' Codex: codex mcp add vestige -- vestige-mcp');
console.log(' OpenCode: npx @vestige/init, or add mcp.vestige to ~/.config/opencode/opencode.json');
console.log(' 2. Restart your MCP client.');
console.log(' 3. Test with: "remember that my preferred editor is VS Code"');
console.log('');