The primary surface begins (tech-spec §8): a Neovim plugin that is a thin
client of the local hephd over its unix-socket JSON-RPC.
- node.resolve {title} → Node|null (heph-core Store + dispatch): exact,
owner-scoped, non-tombstoned alias-then-title match — the same mapping that
materializes wiki links, so follow-link jumps to the node the stored link
points at (never fuzzy search). Unit + rpc_socket integration tests.
- heph.nvim/: vim.uv unix-socket JSON-RPC client (blocking call via vim.wait,
id-demuxed, partial-line buffered, luanil so JSON null → Lua nil; isolated
Sessions for tests). Buffer-backed nodes (heph://node/<id>, acwrite;
BufReadCmd→node.get / BufWriteCmd→node.update, whole-buffer body round-trips
exactly through the CRDT). [[wiki-link]] follow on <CR>. Daily journal.
:Heph command surface + completion.
- Headless e2e (§9): a self-contained busted-style runner (tests/e2e/runner.lua)
— no external plugins, no network, deterministic CI exit codes. Specs: journal
round-trip, follow-link (+ unresolved no-op), link-two-docs/backlink.
`make -C heph.nvim test` builds hephd and runs it.
Docs: heph-nvim reference card, §14 tracker (11a done; 11b/11c/11d queued),
changelog fragment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.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 11a)
| Command | Action |
|---|---|
:Heph today |
Open today's journal |
:Heph journal <YYYY-MM-DD> |
Open 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 |
Task/agenda views (:Heph next/list/capture, set-attention, done/drop),
the per-task log, and context-item promotion arrive in slices 11b/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 CI is
deterministic. make test builds the daemon and runs it; a deliberately
failing spec exits non-zero (no false-green).