Files
obelisk/README.md
T

207 lines
9.3 KiB
Markdown
Raw Normal View History

<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/obelisk-wordmark-d.svg">
<img src=".github/assets/obelisk-wordmark-l2.svg" alt="Obelisk" width="540">
</picture>
2026-05-30 03:27:03 +08:00
[![stars](https://img.shields.io/github/stars/tommy0103/obelisk?style=flat-square)](https://github.com/tommy0103/obelisk/stargazers)
[![version](https://img.shields.io/github/v/tag/tommy0103/obelisk?label=version&style=flat-square)](https://github.com/tommy0103/obelisk/releases)
[![license](https://img.shields.io/badge/license-AGPL--3.0-blue.svg?style=flat-square)](LICENSE)
2026-05-30 03:27:03 +08:00
Every past Claude Code and Codex session -- queryable by your agent, browsable by you.
</div>
<br />
## Two sides of the same index
Obelisk has two sides that share one SQLite index:
**Skill side** — an agent skill that lets coding agents search and query their own session history. The agent writes JS queries, runs them locally, answers in plain language.
**App side** — an Electron desktop app for humans to browse sessions, manage memories, view usage stats, and see weekly recap cards.
Both read from the same `~/.obelisk/obelisk.sqlite` database. The indexer reads Claude Code transcripts from `~/.claude/projects` and Codex transcripts from `~/.codex/sessions`.
## Codex support
Obelisk indexes Claude Code and Codex into the same SQLite schema instead of keeping separate databases. Rows carry a `source` value (`claude` or `codex`), and Codex IDs are prefixed with `codex:` so they cannot collide with Claude session IDs.
Codex root threads become normal Obelisk sessions. Codex child threads are attached through the same `subagents` table when parent-thread metadata is available. Codex does not emit Claude-style workflow metadata, so workflow tables may be empty for Codex-only history.
For live app refresh, Obelisk watches `~/.claude/projects` and `~/.codex/sessions`. It does not watch the whole `~/.codex` root. Codex's `session_index.jsonl` is used as lightweight title/update metadata during indexing, not as the message transcript source.
## Skill: agent-first retrieval
<div align="center">
<img src=".github/assets/demo.png" alt="Obelisk App" width="720">
</div>
You can use obelisk like:
```
/obelisk 上次 auth bug 最后到底改了哪些文件,为什么这么改
/obelisk 这个文件最近在哪些 sessions 里被反复修改
/obelisk 找出最近失败的 tool calls,它们分别发生在哪些任务里
/obelisk 那个 review workflow 的 subagents 各自结论是什么
/obelisk recap this week
```
### Install
```bash
npx skills add tommy0103/obelisk-skill
```
Or manually: copy `obelisk-skill/` into your project's `.claude/skills/`
Then in any Claude Code session:
```
/obelisk <your question>
```
First run builds the index (~5 seconds for 100 sessions). After that it rebuilds incrementally.
### How it works
```
You ask a question
Agent writes a JS query against the SQLite index
Runs it via node $SKILL_DIR/scripts/runtime.js --query <script>
Reads the JSON result, answers in natural language
```
Core API: `search()`, `context()`, `sql()`, plus structured helpers (`sessions`, `memories`, `summaries`, `workflows`, `failures`, `fileHistory`, etc).
### Memory layer
When a retrieval produces a conclusion worth keeping, the agent proposes a markdown memory file. After user approval, it registers the file with `runtime.js --attune <script>`. Memories are recalled via `memories()` in future sessions — a synthesis cache, not a replacement for raw evidence.
## App: A surface for human
A companion desktop app for browsing what the skill indexes.
<div align="center">
<img src=".github/assets/app-screenshot.png" alt="Obelisk App" width="720">
</div>
- **Sessions** — browse all sessions with search, project filtering, readable tool calls (diffs, terminal output, file viewers)
- **Memory** — list and detail views for registered memory files
- **Activity** — GitHub-style heatmap, weekly/cumulative token charts
- **Recap** — shareable weekly/monthly recap cards with archetype theming
- **Settings** — data source configuration, auto-refresh, rebuild index
macOS only. Download from [Releases](https://github.com/tommy0103/obelisk/releases).
## What gets indexed
| Layer | Source | What's captured |
|-------|--------|----------------|
| **Sessions** | Claude `<project>/<sessionId>.jsonl`; Codex `sessions/YYYY/MM/DD/*.jsonl` | Title, project, timestamps, git branch, source |
| **Messages** | user + assistant turns | Full text, model, token usage, parent chain |
| **Tool calls** | every tool invocation | Tool name, input, file paths |
| **Subagents** | Claude `subagents/agent-<id>.jsonl`; Codex child threads | Agent type, description, full conversation |
| **Workflows** | Claude `workflows/wf_<runId>.json` | Script, result, agent count |
| **Workflow agents** | Claude `subagents/workflows/wf_<runId>/` | Per-agent transcripts |
| **Memories** | registered markdown files | Conclusions linked to source sessions |
Full-text search via FTS5 covers all layers.
## Structure
```
packages/core/ # @obelisk/core npm workspace (TypeScript + ESM)
├── src/
│ ├── providers/
│ │ ├── types.ts # Provider + IndexRecord contract
│ │ ├── claude.ts # Claude Code adapter (line-incremental)
│ │ └── codex.ts # Codex adapter (full-reparse)
│ ├── persist.ts # Binding-agnostic record writer (upsert/merge)
│ ├── tx.ts # Write transaction + connection config
│ ├── write-coordinator.ts # Bounded retry policy
│ ├── writer-lease.ts # Cross-process single-writer lease (SQLite lock DB)
│ ├── core.ts # buildIndex / searchText / executeQuery / executeAttune
│ ├── indexer.ts # Skill orchestration (discover → persist → finalize)
│ ├── parsing.ts # Pure helpers (node:sqlite-free, app-consumable)
│ ├── db.ts # node:sqlite lifecycle + migrations
│ ├── query.ts # Query/attune sandbox API (helpers)
│ ├── runtime.ts # Thin CLI shell (--build/--search/--query/--attune)
│ └── schema.sql # SQLite schema (single source of truth)
├── package.json
└── dist/ # Generated package JS, declarations, and schema
references/ # Agent-readable docs (progressive disclosure)
├── schema.md
├── api-reference.md
├── query-patterns.md
├── retrieval-semantics.md
├── pitfalls.md
├── recap-patterns.md
├── recap-writing.md
└── recap/ # Per-card pattern + writing references
├── overview.md
├── pattern1-cover.md … pattern5-closing.md
└── writing1-cover.md … writing5-closing.md
app/ # Electron desktop app (electron-vite + Vue)
├── src/main/ # TypeScript main process (consumes shared core)
├── src/preload/ # CJS preload (sandbox)
├── src/renderer/ # Vue renderer
└── electron.vite.config.ts
packaging/ # Skill publish infrastructure
├── skill-package.json
├── skill-README.md
├── skill-LICENSE # MIT (relicensed for the skill artifact)
└── publish-skill.sh
SKILL.md # Skill definition (installed with the artifact)
CONTEXT.md # Project glossary
docs/adr/ # Architecture decision records (00010006)
```
The optional `/obelisk recap` flow is loaded only for explicit `/obelisk recap` intent.
It starts at `references/recap/overview.md` and proceeds card-by-card:
- `references/recap/pattern1-cover.md` + `references/recap/writing1-cover.md`
- `references/recap/pattern2-thinking.md` + `references/recap/writing2-thinking.md`
- `references/recap/pattern3-vibe.md` + `references/recap/writing3-vibe.md`
- `references/recap/pattern4-workflow.md` + `references/recap/writing4-workflow.md`
- `references/recap/pattern5-closing.md` + `references/recap/writing5-closing.md`
### Generated build outputs
- `packages/core/dist/` is produced by `npm run build:core`. It is the compiled
`@obelisk/core` package: JavaScript, type declarations, and `schema.sql`.
- `dist/obelisk-skill/` is produced by `npm run build:skill`. It is the
install-ready skill artifact: readable plain JavaScript under `scripts/`,
`SKILL.md`, references, and the skill package metadata.
Both directories are generated and should not be edited by hand. The Electron
app imports `packages/core/src/` directly so electron-vite can bundle Core.
## Implementation Notes
The index rebuilds incrementally — only new or modified JSONL files are re-parsed.
When the optional app is running, it is the active indexer: it watches Claude
2026-07-10 18:10:45 +08:00
project files and builds in a worker thread. A fresh `__app_heartbeat__` alone
means the daemon owns writes, so the skill remains read-only; a separate SQLite
writer lease prevents cross-process writes from overlapping. The
`__app_last_successful_build__` marker records index freshness, not ownership.
Zero npm dependencies. Uses Node 22's built-in node:sqlite with FTS5. The entire runtime is ~400 lines.
20K lines of scattered JSONL → something the agent can search() and sql() against in milliseconds.
---
## License
AGPL-3.0 @tommy0103