Backend: enrich `list` to return titled RankedTask rows (title + canonical_context_id, via a shared ranked_from_row with `next`), so the Organizational view needs no N+1 node.get. TDD: query_surface test asserts list rows carry title + context id. Plugin: - view.lua: Tactical `next` + Organizational `list` rendered scratch buffers; <CR> opens the row's canonical-context doc. Narrowed the node autocmd to heph://node/* so view buffers (heph://next, heph://list) don't trip it. - task.lua: capture, set-attention, done/drop, skip, per-task log append, all resolving "the current task" from the buffer (a task node, or a context doc via its canonical-context backlink). - picker.lua: vim.ui.select with Telescope auto-upgrade (headless-safe). - command.lua: :Heph next/list/capture/attention/done/drop/skip/log/search. e2e: capture→next→open context→add/check checklist→done; recurring fresh-checklist (complete rolls forward in place, next occurrence all-unchecked — the §4.4 hard requirement). 6 specs green via `mise run test-nvim`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.2 KiB
| title | modified | tags | ||
|---|---|---|---|---|
| heph.nvim | 2026-06-01 |
|
heph.nvim
The primary user surface (tech-spec §8): a Neovim plugin that replaces
obsidian.nvim and is a thin client of the local hephd over its
unix-socket JSON-RPC. Notes, journals, and tasks are edited as ordinary
buffers; the daemon owns all storage and sync. Built in checkpointed slices on
feature/v1-prototype; this card tracks the stable surface as it lands.
Architecture
heph.nvim/lua/heph/ modules, each small and single-purpose:
| Module | Responsibility |
|---|---|
rpc |
libuv (vim.uv) unix-socket JSON-RPC client. A blocking call() is built over the async pipe by pumping the loop with vim.wait until the matching id returns. Demuxes responses by id; partial lines are buffered; JSON null decodes to Lua nil (luanil). A Session is one connection — the module keeps a default singleton and lets tests open isolated sessions. |
node |
Buffer-backed nodes. A node is a buffer named heph://node/<id> with buftype=acwrite; BufReadCmd loads the body via node.get, BufWriteCmd saves the whole buffer via node.update. |
link |
Parse the [[wiki-link]] under the cursor (mirroring extract.rs grammar) and follow it via node.resolve (exact, never fuzzy search). Unresolved links are allowed. |
journal |
Open/create a dated journal node (idempotent — deterministic id). |
daemon |
Locate / spawn / readiness-poll hephd (shared with the e2e harness). |
config / init |
setup(opts), socket resolution, default keymaps. |
command |
The :Heph <subcommand> dispatch + completion. |
Surfaces never touch SQLite — every operation is a daemon RPC (tech-spec §3). The plugin is mode-agnostic: Tactical/Strategic/Organizational are plugin-side compositions of daemon primitives, not daemon concepts.
Daemon RPC dependencies
Beyond the existing methods (tech-spec §6), the plugin relies on
node.resolve {title} → Node | null: an exact, owner-scoped,
non-tombstoned alias-then-title match — the same mapping the store uses to
materialize wiki links, so "follow link under cursor" jumps to the same
node the stored link points at.
Commands (as of slice 11b)
| Command | Action |
|---|---|
:Heph today / :Heph journal <YYYY-MM-DD> |
Open today's / a dated journal |
:Heph follow (also <CR> in a node buffer) |
Follow the [[link]] under the cursor |
:Heph open <id> |
Open a node buffer by id |
:Heph search <query> |
Full-text search; pick a result to open |
:Heph next [scope] |
Tactical "what is next?" view (<CR> opens a task's context) |
:Heph list [attention] |
Organizational survey of the outstanding set |
:Heph capture <title> |
Capture a committed task (pick attention) |
:Heph attention [color] |
Set the current task's attention |
:Heph done / :Heph drop / :Heph skip |
State change on the current task |
:Heph log <text> |
Append a breadcrumb to the current task's log |
"Current task" is resolved from the buffer: a task node, or a canonical-context
doc whose owning task is followed via its canonical-context backlink. The
next/list views render the titled rows the daemon returns (list enriched to
carry titles + the context id, so no N+1 node.get). Pickers use built-in
vim.ui.select, auto-upgrading to Telescope when installed.
Context-item promotion (:Heph promote) and the CI runner arrive in slice 11c.
Testing (tech-spec §9)
The headless e2e suite drives the plugin in nvim --headless against a real
hephd over a temp socket, asserting both buffer contents and resulting DB
state (via an isolated RPC session). It uses a self-contained busted-style
runner (tests/e2e/runner.lua) — no external plugins, no network — so it is
deterministic. mise run test-nvim builds the daemon and runs the suite against
system-installed Neovim; a deliberately failing spec exits non-zero (no
false-green). In CI the same suite runs inside a Dagger container that provides
Neovim + the Rust toolchain (slice 11c).