# ascent-research
[](https://crates.io/crates/ascent-research)
[](https://github.com/actionbook/ascent-research/releases)
> **Your agent's next step up. Every session picks up where you left off. Every turn goes higher.**
**One-line pitch.** `ascent-research` is an incremental research workflow CLI for AI agents: point it at a topic / source tree / Obsidian vault, and it will *keep* researching across sessions — fetching, citing, diagramming, and accreting a durable wiki you can come back to tomorrow and pick up exactly where you stopped.
```bash
ascent-research new "tokio internals 2026" --slug tokio --preset tech
ascent-research add-local ~/tokio/tokio/src/runtime --glob '**/*.rs'
ascent-research loop tokio --provider claude --iterations 12
ascent-research finish tokio --open # coverage -> HTML -> audit
# (next day)
ascent-research resume tokio && ascent-research loop tokio --iterations 8
```
Bookmark-ready: every session lives as plain files under
`~/.actionbook/ascent-research/<slug>/`, so Obsidian, VS Code, `grep`
and `git` all work.
## Author's positioning — an external handle for agent self-evolution
A Claude Code or Codex conversation ends. The agent forgets everything.
Next week you ask the same question — same search, same fetches, same
half-formed understanding.
I built `ascent-research` because I want my AI agents to **get smarter
over time, not reset every session**. The on-disk session (`session.md`,
`session.jsonl`, `wiki/`, `SCHEMA.md`) is the agent's external long-term
memory — survives process death, carries across tool switches, inspectable
and editable by the human. Every `loop` run isn't "research this topic
from scratch"; it's "continue the research we were doing, check what's
unused from last time, append to the pages you've already written."
The agent-facing surface (actions like `write_wiki_page`,
`append_wiki_page`, `digest_source`) exists specifically so the agent can
*accrete* rather than *overwrite*. The infra-enforced rules
(smell test, preserve_diagram_refs, figure-rich contract) exist so this
long-term memory stays clean without human QA every turn.
Whether you use it standalone or as a skill inside a coding-agent
instance, the pitch is the same: **stop throwing away your agent's
research work at the end of every conversation.** Keep it on disk. Let
the next turn stand on the last one's shoulders.
## Two ways to use it
`ascent-research` is a CLI that calls an LLM provider (Claude via
`cc-sdk`, Codex via `codex app-server`, or `fake` for tests). Which
process hosts the agent decides the usage shape:
### Standalone — ascent-research runs its own loop
Run the CLI directly; it spawns the provider itself and drives the
research loop end-to-end, no outer agent needed. Good for
batch / CI / "I just want a report."
```bash
ascent-research new "tokio internals" --slug tokio
ascent-research add-local ~/tokio/tokio/src
ascent-research loop tokio --provider claude --iterations 12
ascent-research finish tokio --open
```
### Skill — driven from a Claude Code or Codex instance
Drop the bundled skill into your Claude Code / Codex config and the
outer agent invokes the CLI per-turn as a tool. Good for interactive
sessions where you want to mix research with coding / writing work
in the same conversation, or want the outer agent to plan the
workflow (decide what to ingest, when to query, when to synthesize).
```bash
ln -s "$PWD/skills/ascent-research" ~/.claude/skills/ascent-research
# Then in a Claude Code session: /skill:ascent-research
# Or just describe the task — "research tokio's scheduler via source" —
# the skill triggers automatically.
```
Both modes share the same on-disk session format, so you can start
a session in standalone mode and later resume it from inside a
Claude Code / Codex instance, or vice versa.
---
## What's new in 0.4.2
- **Third LLM provider: OpenCode Go** ($10/mo subscription, OpenAI-
and Anthropic-compatible HTTP endpoints to DeepSeek / Kimi / GLM /
Qwen / MiniMax). Build with `--features provider-opencode-go`, set
`OPENCODE_API_KEY` + `ASR_OPENCODE_MODEL`, then
`ascent-research loop <slug> --provider opencode-go`.
- Useful when Claude Code Pro / ChatGPT Plus subscriptions are out of
reach (cost or payment-region constraints). All knobs are env-driven
(`ASR_OPENCODE_PROTOCOL`, `ASR_OPENCODE_TEMPERATURE`,
`ASR_OPENCODE_MAX_TOKENS`, `ASR_OPENCODE_TIMEOUT_MS`).
- Also works as `ASR_BILINGUAL_PROVIDER=opencode-go` for
`synthesize --bilingual` Chinese translation.
- **CLI defaults unchanged** — `--provider` still defaults to `fake`;
opt in to `opencode-go` explicitly.
- Thanks to [@Paul-Yuchao-Dong](https://github.com/Paul-Yuchao-Dong)
for raising the use case and contributing the initial design (PR #19).
See `CHANGELOG.md` for the full design rationale incl. what was
deliberately not adopted from the PR.
---
## What's new in 0.4.1
- **x.com / twitter.com tweet capture works end-to-end** through the V2
browser backend. v0.4.0's generic runcode JS returned ~160 bytes of
X's left-nav chrome only; 0.4.1 adds an `XTweet` runcode flavor that
waits for `article[data-testid="tweet"]` (not `networkidle`), scrolls
with **snapshot-collect across virtualized DOM** so the main tweet
isn't unmounted out of the result, and reads up to 25 thread articles
with image / video poster URLs inlined as markdown ``.
- **3 new tech preset rules**: `x-tweet-status`, `x-profile`,
`x-search-live` — explicit kinds for `route` debugging.
- **Bugfix in `md_parser::extract_http_links`**: markdown image syntax
`` is now correctly excluded from the cited-sources scan,
so embedding pictures of cited tweets no longer trips
`sources_hallucinated`.
Live impact (same URLs, before vs after):
| `x.com/<user>/status/<id>` (any) | 162 B chrome | **2-3 KB** main tweet + thread + media |
See `CHANGELOG.md` for full notes and `specs/x-com-tweet-runcode-flavor.spec.md`
for the design (33 BDD scenarios, lint 100%).
---
## What's new in 0.4.0
- **V2 Actionbook MCP backend is now the default** for browser-rendered
fetches (Cloud Worker at `edge.actionbook.dev/mcp` + Chrome extension
over WSS). Set `ACTIONBOOK_BACKEND=v1-cli` to keep the old local-CLI
path — it's a **permanent fallback**, not slated for removal.
- **Catalog seed pre-fetch**: every `add` / `batch` first probes the V2
catalog and seeds matching actions into the session wiki, so the
agent knows what's known about a site before navigating.
- **Composite source fetch**: one rule can fan out into N parts
(e.g. postagent metadata + browser rendered) merged under
`composite-v1`; short-circuits on first part failure with a labelled
`composite_failed_part` event.
- **3 new autoresearch actions** for the loop: `actionbook_search`,
`actionbook_manual`, `actionbook_run_code`.
- **New flags** on `add` / `batch`: `--frame-id`, `--run-code-args`,
`--reseed`, `--actionbook-backend`.
- **Default per-source timeout** raised from 30 s → 90 s (V2 server's
inner run-code budget is 60 s; extra 30 s covers edge overhead).
- Fixes: smell `www.` ↔ apex equivalence; CJK / UTF-8 docs now pass the
`add-local` text detector; user `--timeout > 60s` is no longer
silently truncated by the V2 server's hard cap.
See `CHANGELOG.md` for the full list and `docs/rfc/v2-session-export-to-postagent.md`
for the cross-tool RFC that didn't ship in this release.
---
## Why it's different
Five properties — each validated end-to-end across four live research
sessions (tokio internals, an Obsidian agent-SE series, a mixed
online-plus-local AI coding agents comparison, and self-research on
this repo):
### 0. Autoresearch lineage — 2-file resume, extended to reports
Inherits the core loop architecture from
[karpathy/autoresearch](https://github.com/karpathy/autoresearch)
and [pi-autoresearch](https://github.com/davebcn87/pi-autoresearch):
a fresh agent can resume any session from two files —
`session.md` (human-readable living doc) + `session.jsonl`
(append-only event log) — even after process death, context reset,
or a week of inactivity. Where the original autoresearch optimizes a
single scalar (training loss, bundle size, test speed) via
`edit → benchmark → keep-or-revert`, `ascent-research` generalizes
the same loop grammar to *research*:
`plan → fetch → digest → write_section / write_wiki_page / write_diagram`
producing a figure-rich report plus a durable cross-session wiki
instead of a single optimized number.
### 1. Incremental research — sessions resume, knowledge accretes
`ascent-research resume <slug>` picks up exactly where a prior turn
stopped. Wiki pages *accrue* via `append_wiki_page` — new findings
grow existing entity pages instead of overwriting them. Coverage
signals (`sources_unused`, `diagrams_referenced`, `wiki_pages`,
`wiki_total_bytes`) let each loop run know *what's still open* from
the previous turn, so it continues rather than restarts. One-shot
DR tools can't do this — when they finish, they're done.
### 2. Three-way ingest, one pipeline
`add` (HTTP via `postagent`) + `add-local` (file trees) + browser
fallback (via `actionbook browser` for JS-heavy pages) all flow
through the same smell-test → event-log → wiki → report path. A
single session can cite GitHub READMEs, arXiv papers, blog posts,
and your private Obsidian notes side-by-side in one wiki page's
sources list — the renderer doesn't care about URL scheme.
### 3. Figure-rich by contract
Narrative-only output is considered incomplete. The loop's system
prompt carries a non-negotiable FIGURE-RICH CONTRACT: target ≥ 1
hand-drawn SVG per numbered section, bidirectional rule that every
`` markdown reference must have a matching
`write_diagram` action and vice versa, infra-level guarantee that
section overwrites never drop figures. Every SVG is inline
(no external assets, no screenshots) and the HTML report has a
clickable wiki TOC + EN/ZH bilingual toggle.
### 4. Infra-enforced correctness + machine-readable errors
Agents can't "just summarize this for me." Every fetch runs through
a smell test at the CLI layer before the LLM sees it; rejections
become typed events. Overwrites preserve figures. Wiki writes are
append-safe. Coverage computes `sources_hallucinated` (URLs cited
but never fetched) as a `report_ready` blocker. Every error returns
a machine-readable code (`NO_ACTIVE_SESSION`, `SMELL_REJECTED`,
`DIAGRAM_OUT_OF_BOUNDS`, `WIKI_EMPTY`, …) so agents route recovery
deterministically without parsing prose.
---
## Install
```bash
git clone https://github.com/actionbook/ascent-research
cd ascent-research
# Full build (loop + Claude provider) — what live sessions need
cargo build -p ascent-research --release --features "autoresearch provider-claude provider-codex"
export PATH="$PWD/target/release:$PATH"
ascent-research --help
```
Alternative feature sets:
```bash
# Minimal — no autonomous loop, no LLM
cargo build -p ascent-research --release
# Loop with fake provider only (for scripted tests)
cargo build -p ascent-research --release --features autoresearch
# Loop with Codex instead of Claude
cargo build -p ascent-research --release --features "autoresearch provider-codex"
```
Prereqs for online ingest: Rust stable (edition 2024),
[`postagent`](https://github.com/actionbook/postagent) for HTTP API fetches,
and (for JS-heavy pages) the Actionbook Chrome extension — see below.
Neither is required if you only use `add-local`.
### Browser ingest backends
`ascent-research` picks between two actionbook backends based on
`ACTIONBOOK_BACKEND` (default `v2-mcp`):
| `v2-mcp` (default) | Cloud MCP at `edge.actionbook.dev/mcp` + Actionbook Chrome extension over WSS | `ACTIONBOOK_API_KEY` (an `ak_*` token), Chrome extension installed & signed in |
| `v1-cli` | Local `actionbook` CLI subprocess (offline-capable fallback, permanently supported) | The [`actionbook`](https://github.com/actionbook/actionbook) binary on `PATH`, and your Chrome profile reachable by it |
**V2 setup**:
1. Install the **Actionbook Cloud (v2)** Chrome extension (v0.2.0-alpha.4
or later). Unpacked-load it from `chrome://extensions` → Load unpacked.
2. Sign in via the extension popup.
3. Get an `ak_*` token from [actionbook.dev/dashboard/api-keys](https://actionbook.dev/dashboard/api-keys) and export it:
```bash
export ACTIONBOOK_API_KEY=ak_xxxxxxxxxxxxxxxx
```
⚠️ **Do not commit this token to git.** Prefer `.envrc` + direnv or a
secret manager. `ascent-research` never echoes the token in error
messages or logs.
**Recommended: dedicated Chrome profile.** V2 drives the page through
`chrome.debugger`, which Chromium will refuse to attach when other
extensions inject `chrome-extension://` content frames into the target
page (most password managers, AI sidebars, translation extensions, and
DevTools extensions do this). Symptom: every browser fetch fails with
`DEBUGGER_ATTACH_CONFLICT`.
The cleanest fix is a dedicated Chrome profile that only has the
Actionbook extension installed:
```bash
# macOS — launch a specific profile directly:
open -na "Google Chrome" --args --profile-directory="Profile 2"
```
(Replace `Profile 2` with the directory name shown at `chrome://version`
in your dedicated profile.)
**Other V2 env vars**:
| `ACTIONBOOK_BACKEND` | `v2-mcp` | `v1-cli` to revert to subprocess; unknown values are fatal |
| `ACTIONBOOK_MCP_ENDPOINT` | `https://edge.actionbook.dev/mcp` | Point at staging or a local worker |
| `ACTIONBOOK_API_KEY` | — | `ak_*` token (required for v2-mcp) |
| `ACTIONBOOK_BIN` | `actionbook` | V1 path only; ignored under v2-mcp |
| `ACTIONBOOK_BROWSER_SESSION` | — | V1: shared session name. V2: tab-handle prefix (lets multiple ascent instances share one Chrome without colliding) |
| `ACTIONBOOK_STDOUT_CAP` | 16 MB | Cap on per-call response body (both backends) |
---
## Three shapes of research
### A. Survey a topic from public sources
```bash
ascent-research new "state-space models 2026" --slug ssm --preset tech
ascent-research batch \
https://arxiv.org/abs/2111.00396 \
https://arxiv.org/abs/2312.00752 \
https://github.com/HazyResearch/state-spaces \
--concurrency 4
ascent-research loop ssm --provider claude --iterations 10
ascent-research finish ssm --bilingual --open
```
PDF export is opt-in. The default backend is local and free: it uses an
isolated headless Chromium when available, so the HTML stays on your machine
and no paid API is required:
```bash
ascent-research synthesize ssm --bilingual --pdf
```
The local backend discovers Playwright Chromium first. If it is not installed,
run `npx playwright install chromium` or set `ASR_PDF_CHROME_BIN` to a known
safe browser binary. Desktop Google Chrome is not auto-launched by default; set
`ASR_PDF_ALLOW_SYSTEM_CHROME=1` only if you explicitly want that fallback.
### B. Deep-dive a library's source tree
```bash
ascent-research new "axum internals" --slug axum --preset tech
ascent-research schema edit # set your "what to emphasize"
ascent-research add-local ~/axum/axum/src --glob '**/*.rs'
ascent-research loop axum --provider claude --iterations 12
ascent-research finish axum --open
```
### C. Structure your Obsidian vault
```bash
ascent-research new "my agent-SE notes" --slug notes --preset tech
ascent-research add-local ~/vault/agent-notes --glob '**/*.md'
ascent-research loop notes --provider claude --iterations 10
ascent-research wiki query "what's my stance on code review for AI?" \
--save-as my-code-review-stance
```
### D. Audit GitHub star-trust signals
`github-audit` creates a deterministic evidence artifact first; the LLM only
interprets that artifact and any follow-up public context. It reports a
human-facing trust score, machine-facing risk score/band, confidence, reasons,
and evidence, not a hard “fake/real” verdict.
```bash
ascent-research github-audit dagster-io/dagster \
--depth timeline --sample 500 --out audit.json --html audit.html
ascent-research new "dagster-io/dagster GitHub trust audit" \
--slug dagster-trust --preset github-trust --tag fact-check
ascent-research add-local audit.json --slug dagster-trust
ascent-research loop dagster-trust --provider claude --iterations 8
ascent-research finish dagster-trust --open
```
Use `audit.html` when the user needs the trust decision surface directly:
trust score, risk score, confidence, metric dashboard, reasons, and evidence
gaps. Use the research session only for contextual follow-up around that
deterministic score.
Full command reference, error-code triage, loop contracts, and scenario
playbooks: see [`skills/ascent-research/SKILL.md`](skills/ascent-research/SKILL.md).
---
## Session layout
Each project is one directory under `~/.actionbook/ascent-research/<slug>/`.
Everything is plain files — markdown, JSON lines, SVG, TOML — so your
editor / grep / git / Obsidian all work without a custom client.
| `session.md` | Narrative — numbered sections, overview, aside. Report spine. |
| `session.jsonl` | Append-only event log. Sources, attempts, loop steps. Authoritative. |
| `SCHEMA.md` | User-editable session guidance. Loop re-reads each turn. |
| `wiki/*.md` | Persistent entity / concept / analysis pages with cross-links. |
| `diagrams/*.svg` | Hand-drawn figures inlined into the HTML report. |
| `raw/` | Raw fetched content, one file per accepted source. |
| `report.html` | Rendered editorial output — wiki TOC, inline SVGs, optional bilingual toggle. |
| `report.pdf` | Optional local PDF export of `report.html`, created only by `synthesize --pdf` or `--pdf-output`. Defaults to isolated local Chromium. |
Override the root via `ACTIONBOOK_RESEARCH_HOME=/some/path`. Legacy
`~/.actionbook/research/` is read as a fallback so sessions from
v0.2 keep working.
---
## Agent integration
`skills/ascent-research/SKILL.md` is a bundled Claude Code / Codex skill
describing the full workflow with nine scenario playbooks, error-code
triage, and build-target matrix. Expose it on your global skill path:
```bash
ln -s "$PWD/skills/ascent-research" ~/.claude/skills/ascent-research
```
---
## Development
```bash
cargo test -p ascent-research # core suite
cargo test -p ascent-research --features autoresearch # + loop suite (fake provider)
```
All integration tests use a `FakeProvider` replaying scripted JSON
turns, so the full suite never hits a real LLM and needs no network.
---
## Project lineage
- Core 2-file resume loop inherited from
[karpathy/autoresearch](https://github.com/karpathy/autoresearch)
- Per-session wiki layer inspired by karpathy's
[LLM Wiki gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)
- Widget / dashboard pattern borrowed from
[pi-autoresearch](https://github.com/davebcn87/pi-autoresearch)
- Previously named `research-rs` (v0.1 / v0.2); renamed to
`ascent-research` in v0.3 to foreground the incremental-research story
---
## License
Apache-2.0.