opencode-memory

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenCode Memory Browser

OpenCode 记忆浏览器

Lightweight, read-only access to your local OpenCode history. No injection, no bloat — just the ability to look things up when it would help.
This skill is specifically about OpenCode data stored on the local machine. It is not for ChatGPT history, Claude cloud history, generic browser history, or external memory products.
All data lives in a local SQLite database and plain files. You query them directly using
sqlite3
via bash. No bundled scripts or external dependencies needed.
轻量级、只读方式访问你的本地OpenCode历史记录。无注入、无冗余——仅在需要时提供查询能力。
该功能专门针对存储在本地设备上的OpenCode数据,不适用于ChatGPT历史、Claude云端历史、通用浏览器历史或其他外部记忆产品。
所有数据都存储在本地SQLite数据库和纯文本文件中。你可以通过bash使用
sqlite3
直接查询,无需捆绑脚本或外部依赖。

When to Use

使用场景

Auto-trigger (agent decides)

自动触发(由Agent决定)

  • You are resuming work on a project and suspect prior sessions exist.
  • The user references something done previously ("we did this before", "last time", "that plan we made").
  • A recurring issue suggests checking if it was encountered before.
  • The user asks about the state of plans, past decisions, or previous approaches.
  • You need context that might exist in history but is not in the current session.
  • 你正在恢复某个项目的工作,且怀疑存在过往会话记录。
  • 用户提及之前做过的事情(如“我们之前做过这个”、“上次”、“我们制定的那个计划”)。
  • 遇到重复出现的问题,需要检查之前是否遇到过。
  • 用户询问计划状态、过往决策或之前的解决方案。
  • 你需要当前会话中没有的、可能存在于历史记录中的上下文信息。

User-triggered (explicit request)

用户触发(明确请求)

  • "Check my history"
  • "What did we do in the last session?"
  • "Show me my plans"
  • "Search for when we discussed X"
  • "What projects have I worked on?"
  • "Look at previous conversations about Y"
  • “查看我的历史记录”
  • “我们上次会话做了什么?”
  • “展示我的计划”
  • “搜索我们讨论X的记录”
  • “我都做过哪些项目?”
  • “查看关于Y的过往对话”

Do NOT use when

请勿使用的场景

  • The task is clearly brand new with no relevant history.
  • Fresh repo context (files, git log) is sufficient.
  • The user explicitly says they don't care about prior work.
  • 任务是全新的,无相关历史记录。
  • 最新的仓库上下文(文件、git日志)已足够回答问题。
  • 用户明确表示不关心过往工作内容。

Storage Locations

存储位置

Database:       ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/opencode.db
Plans:          ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/plans/*.md
Session diffs:  ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/storage/session_diff/<session-id>.json
Prompt history: ${XDG_STATE_HOME:-$HOME/.local/state}/opencode/prompt-history.jsonl
The database path respects
$XDG_DATA_HOME
if set (default:
~/.local/share
).
Database:       ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/opencode.db
Plans:          ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/plans/*.md
Session diffs:  ${XDG_DATA_HOME:-$HOME/.local/share}/opencode/storage/session_diff/<session-id>.json
Prompt history: ${XDG_STATE_HOME:-$HOME/.local/state}/opencode/prompt-history.jsonl
数据库路径遵循
$XDG_DATA_HOME
环境变量(默认路径:
~/.local/share
)。

Database Schema (what matters)

数据库架构(关键内容)

  • project
    id
    (text PK),
    worktree
    (path),
    name
    (often NULL, derive from worktree basename)
  • session
    id
    (text, e.g.
    ses_xxx
    ),
    project_id
    (FK),
    parent_id
    (NULL = main session, set = subagent),
    title
    ,
    summary
    ,
    time_created
    ,
    time_updated
  • message
    id
    ,
    session_id
    (FK),
    data
    (JSON with
    $.role
    =
    "user"
    or
    "assistant"
    ),
    time_created
  • part
    id
    ,
    message_id
    (FK),
    session_id
    (FK),
    data
    (JSON with
    $.type
    =
    "text"
    and
    $.text
    = content)
Timestamps are Unix milliseconds. Use
datetime(col/1000, 'unixepoch', 'localtime')
to display them.
  • project
    id
    (文本类型主键)、
    worktree
    (路径)、
    name
    (通常为NULL,可从worktree的basename推导)
  • session
    id
    (文本类型,例如
    ses_xxx
    )、
    project_id
    (外键)、
    parent_id
    (NULL表示主会话,有值表示子Agent会话)、
    title
    summary
    time_created
    time_updated
  • message
    id
    session_id
    (外键)、
    data
    (JSON格式,
    $.role
    值为
    "user"
    "assistant"
    )、
    time_created
  • part
    id
    message_id
    (外键)、
    session_id
    (外键)、
    data
    (JSON格式,
    $.type
    值为
    "text"
    $.text
    为内容)
时间戳为Unix毫秒格式。使用
datetime(col/1000, 'unixepoch', 'localtime')
进行格式转换显示。

Ready-to-Use Queries

即用型查询语句

All queries use
sqlite3
in read-only mode. Always run via bash.
Shorthand used below:
DATA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/opencode"
STATE_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/opencode"
DB="$DATA_ROOT/opencode.db"
DB_URI="file:${DB}?mode=ro"
所有查询均使用
sqlite3
的只读模式,始终通过bash执行。
以下使用简写变量:
DATA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/opencode"
STATE_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/opencode"
DB="$DATA_ROOT/opencode.db"
DB_URI="file:${DB}?mode=ro"

Quick summary

快速概览

bash
sqlite3 "$DB_URI" "
  SELECT 'projects', COUNT(*) FROM project
  UNION ALL SELECT 'sessions (main)', COUNT(*) FROM session WHERE parent_id IS NULL
  UNION ALL SELECT 'sessions (total)', COUNT(*) FROM session
  UNION ALL SELECT 'messages', COUNT(*) FROM message
  UNION ALL SELECT 'todos', COUNT(*) FROM todo;
"
bash
sqlite3 "$DB_URI" "
  SELECT 'projects', COUNT(*) FROM project
  UNION ALL SELECT 'sessions (main)', COUNT(*) FROM session WHERE parent_id IS NULL
  UNION ALL SELECT 'sessions (total)', COUNT(*) FROM session
  UNION ALL SELECT 'messages', COUNT(*) FROM message
  UNION ALL SELECT 'todos', COUNT(*) FROM todo;
"

List projects

列出项目

bash
sqlite3 "$DB_URI" "
  SELECT
    COALESCE(p.name, CASE WHEN p.worktree = '/' THEN '(global)' ELSE REPLACE(p.worktree, RTRIM(p.worktree, REPLACE(p.worktree, '/', '')), '') END) AS name,
    p.worktree,
    (SELECT COUNT(*) FROM session s WHERE s.project_id = p.id AND s.parent_id IS NULL) AS sessions
  FROM project p
  ORDER BY p.time_updated DESC
  LIMIT 10;
"
bash
sqlite3 "$DB_URI" "
  SELECT
    COALESCE(p.name, CASE WHEN p.worktree = '/' THEN '(global)' ELSE REPLACE(p.worktree, RTRIM(p.worktree, REPLACE(p.worktree, '/', '')), '') END) AS name,
    p.worktree,
    (SELECT COUNT(*) FROM session s WHERE s.project_id = p.id AND s.parent_id IS NULL) AS sessions
  FROM project p
  ORDER BY p.time_updated DESC
  LIMIT 10;
"

List recent sessions

列出近期会话

bash
sqlite3 "$DB_URI" "
  SELECT
    s.id,
    COALESCE(s.title, 'untitled') AS title,
    COALESCE(p.name, CASE WHEN p.worktree = '/' THEN '(global)' ELSE REPLACE(p.worktree, RTRIM(p.worktree, REPLACE(p.worktree, '/', '')), '') END) AS project,
    datetime(s.time_updated/1000, 'unixepoch', 'localtime') AS updated,
    (SELECT COUNT(*) FROM message m WHERE m.session_id = s.id) AS msgs
  FROM session s
  LEFT JOIN project p ON p.id = s.project_id
  WHERE s.parent_id IS NULL
  ORDER BY s.time_updated DESC
  LIMIT 10;
"
bash
sqlite3 "$DB_URI" "
  SELECT
    s.id,
    COALESCE(s.title, 'untitled') AS title,
    COALESCE(p.name, CASE WHEN p.worktree = '/' THEN '(global)' ELSE REPLACE(p.worktree, RTRIM(p.worktree, REPLACE(p.worktree, '/', '')), '') END) AS project,
    datetime(s.time_updated/1000, 'unixepoch', 'localtime') AS updated,
    (SELECT COUNT(*) FROM message m WHERE m.session_id = s.id) AS msgs
  FROM session s
  LEFT JOIN project p ON p.id = s.project_id
  WHERE s.parent_id IS NULL
  ORDER BY s.time_updated DESC
  LIMIT 10;
"

Sessions for a specific project

特定项目的会话

Replace the worktree path with the actual project path:
bash
sqlite3 "$DB_URI" "
  SELECT s.id, COALESCE(s.title, 'untitled'),
    datetime(s.time_updated/1000, 'unixepoch', 'localtime')
  FROM session s
  JOIN project p ON p.id = s.project_id
  WHERE p.worktree = '/path/to/project'
    AND s.parent_id IS NULL
  ORDER BY s.time_updated DESC
  LIMIT 10;
"
To find the worktree for the current directory:
git rev-parse --show-toplevel
将工作树路径替换为实际项目路径:
bash
sqlite3 "$DB_URI" "
  SELECT s.id, COALESCE(s.title, 'untitled'),
    datetime(s.time_updated/1000, 'unixepoch', 'localtime')
  FROM session s
  JOIN project p ON p.id = s.project_id
  WHERE p.worktree = '/path/to/project'
    AND s.parent_id IS NULL
  ORDER BY s.time_updated DESC
  LIMIT 10;
"
获取当前目录的工作树路径:
git rev-parse --show-toplevel

Read messages from a session

读取会话中的消息

Replace the session ID:
bash
sqlite3 "$DB_URI" "
  SELECT
    json_extract(m.data, '$.role') AS role,
    datetime(m.time_created/1000, 'unixepoch', 'localtime') AS time,
    GROUP_CONCAT(json_extract(p.data, '$.text'), char(10)) AS text
  FROM message m
  LEFT JOIN part p ON p.message_id = m.id
    AND json_extract(p.data, '$.type') = 'text'
  WHERE m.session_id = 'SESSION_ID_HERE'
  GROUP BY m.id
  ORDER BY m.time_created ASC
  LIMIT 50;
"
替换会话ID:
bash
sqlite3 "$DB_URI" "
  SELECT
    json_extract(m.data, '$.role') AS role,
    datetime(m.time_created/1000, 'unixepoch', 'localtime') AS time,
    GROUP_CONCAT(json_extract(p.data, '$.text'), char(10)) AS text
  FROM message m
  LEFT JOIN part p ON p.message_id = m.id
    AND json_extract(p.data, '$.type') = 'text'
  WHERE m.session_id = 'SESSION_ID_HERE'
  GROUP BY m.id
  ORDER BY m.time_created ASC
  LIMIT 50;
"

Search across all conversations

跨所有对话搜索

Replace the search term:
bash
sqlite3 "$DB_URI" "
  SELECT
    s.id AS session_id,
    COALESCE(s.title, 'untitled') AS title,
    json_extract(m.data, '$.role') AS role,
    datetime(m.time_created/1000, 'unixepoch', 'localtime') AS time,
    substr(json_extract(p.data, '$.text'), 1, 200) AS snippet
  FROM part p
  JOIN message m ON m.id = p.message_id
  JOIN session s ON s.id = m.session_id
  WHERE s.parent_id IS NULL
    AND json_extract(p.data, '$.type') = 'text'
    AND json_extract(p.data, '$.text') LIKE '%SEARCH_TERM%'
  ORDER BY m.time_created DESC
  LIMIT 10;
"
替换搜索关键词:
bash
sqlite3 "$DB_URI" "
  SELECT
    s.id AS session_id,
    COALESCE(s.title, 'untitled') AS title,
    json_extract(m.data, '$.role') AS role,
    datetime(m.time_created/1000, 'unixepoch', 'localtime') AS time,
    substr(json_extract(p.data, '$.text'), 1, 200) AS snippet
  FROM part p
  JOIN message m ON m.id = p.message_id
  JOIN session s ON s.id = m.session_id
  WHERE s.parent_id IS NULL
    AND json_extract(p.data, '$.type') = 'text'
    AND json_extract(p.data, '$.text') LIKE '%SEARCH_TERM%'
  ORDER BY m.time_created DESC
  LIMIT 10;
"

List saved plans

列出已保存的计划

bash
ls -lt "$DATA_ROOT"/plans/*.md 2>/dev/null | head -20
To read a specific plan:
bash
cat "$DATA_ROOT"/plans/FILENAME.md
bash
ls -lt "$DATA_ROOT"/plans/*.md 2>/dev/null | head -20
读取特定计划:
bash
cat "$DATA_ROOT"/plans/FILENAME.md

Show recent prompt history

显示近期提示词历史

bash
tail -20 "$STATE_ROOT"/prompt-history.jsonl
Each line is a JSON object. The user's input is typically in the
input
or
text
field.
bash
tail -20 "$STATE_ROOT"/prompt-history.jsonl
每行是一个JSON对象。用户输入通常位于
input
text
字段中。

Workflow

工作流程

Quick recall (most common)

快速调取(最常用)

  1. Run the summary query to see what's available.
  2. If you need sessions for the current project, get the worktree with
    git rev-parse --show-toplevel
    , then run the project sessions query.
  3. If you need a specific topic, run the search query.
  4. If you need full conversation detail, run the messages query with the session ID.
  1. 运行概览查询,查看可用的历史记录。
  2. 如果需要当前项目的会话记录,使用
    git rev-parse --show-toplevel
    获取工作树路径,然后运行特定项目会话查询。
  3. 如果需要特定主题的内容,运行搜索查询。
  4. 如果需要完整对话细节,使用会话ID运行消息查询。

Plan review

计划回顾

  1. List plans with
    ls -lt "$DATA_ROOT"/plans/*.md
    .
  2. Read a plan with
    cat "$DATA_ROOT"/plans/<filename>.md
    .
  1. 使用
    ls -lt "$DATA_ROOT"/plans/*.md
    列出所有计划。
  2. 使用
    cat "$DATA_ROOT"/plans/<filename>.md
    读取指定计划。

Deep investigation

深度调查

  1. Run projects to see all tracked repos.
  2. Run sessions for a specific project.
  3. Run messages for full conversation content.
  4. Cross-reference with search across all projects.
  1. 运行项目列表查询,查看所有被追踪的仓库。
  2. 运行特定项目的会话列表查询。
  3. 运行消息查询获取完整对话内容。
  4. 结合跨项目搜索查询进行交叉参考。

Critical Rules

重要规则

  1. Read-only. Never write to or modify the database or any OpenCode files.
  2. Use bash + sqlite3. Do not try to read
    opencode.db
    with the Read tool — it is a binary file. Always query via
    sqlite3
    in bash.
  3. Don't dump everything. Use
    LIMIT
    and
    LIKE
    to keep output focused. The database can contain tens of thousands of messages.
  4. Summarize for the user. After retrieving data, distill the relevant parts. Don't paste raw query output.
  5. Respect privacy. Session history may contain sensitive data. Only surface what is relevant to the current task.
  6. Set path variables first. At the start of any memory lookup, set
    DATA_ROOT
    ,
    STATE_ROOT
    ,
    DB
    , and
    DB_URI
    exactly as shown above so the commands work on XDG and non-XDG setups and keep SQLite access read-only.
  1. 只读模式。绝不能写入或修改数据库或任何OpenCode文件。
  2. 使用bash + sqlite3。不要尝试用读取工具直接读取
    opencode.db
    ——它是二进制文件。始终通过bash中的
    sqlite3
    进行查询。
  3. 不要导出全部数据。使用
    LIMIT
    LIKE
    语句聚焦输出内容。数据库可能包含数万条消息。
  4. 为用户总结内容。获取数据后,提炼相关部分。不要直接粘贴原始查询输出。
  5. 尊重隐私。会话历史可能包含敏感数据。仅展示与当前任务相关的内容。
  6. 先设置路径变量。在进行任何记忆查询前,严格按照上述示例设置
    DATA_ROOT
    STATE_ROOT
    DB
    DB_URI
    变量,确保命令在XDG和非XDG环境下都能正常工作,并保持SQLite的只读访问模式。

Fallback: Web UI

备选方案:Web界面

If the user needs visual dashboards or a browsable interface:
  1. Check if OpenCode web is running:
    curl -s http://127.0.0.1:4096/api/health 2>/dev/null || echo "not running"
  2. If running, direct the user to
    http://127.0.0.1:4096
    .
  3. If not running, suggest
    opencode web
    .
  4. Note:
    opencode.local
    only works with mDNS enabled (
    opencode web --mdns
    ). Don't assume it exists.
如果用户需要可视化仪表盘或可浏览的界面:
  1. 检查OpenCode Web服务是否运行:
    curl -s http://127.0.0.1:4096/api/health 2>/dev/null || echo "not running"
  2. 如果正在运行,引导用户访问
    http://127.0.0.1:4096
  3. 如果未运行,建议用户执行
    opencode web
    启动服务。
  4. 注意:
    opencode.local
    仅在启用mDNS时可用(执行
    opencode web --mdns
    ),不要默认假设它存在。

Deep Reference

深度参考

See references/storage-format.md for the full storage layout, all table schemas, and additional query examples.
查看references/storage-format.md获取完整存储布局、所有表结构以及更多查询示例。