Skip to content

Latest commit

 

History

History
160 lines (124 loc) · 6.98 KB

File metadata and controls

160 lines (124 loc) · 6.98 KB

CLAUDE.md — Maintainer guide for toolpath.nvim

What this is

Neovim plugin that records editing history as Toolpath Path documents. Thin Lua wrapper (~500 lines) around path track CLI subcommands. The plugin owns undo tree mapping and VCS awareness; the CLI owns diffing, session state, and document building.

Project layout

lua/toolpath/init.lua     ← all plugin logic (setup, tracking, git, async queue)
lua/toolpath/health.lua   ← :checkhealth toolpath
plugin/toolpath.lua       ← user command definitions
doc/toolpath.txt          ← Neovim :help documentation
README.md                 ← user-facing docs
local/                    ← design docs from the toolpath CLI team (not shipped)
docs/                     ← design docs we authored (not shipped)

Version

M.version in lua/toolpath/init.lua is the single source of truth. Bump it on every user-visible change. It's displayed in :ToolpathStatus and :checkhealth toolpath.

Checklist for any change

  1. Update lua/toolpath/init.lua — the code itself.
  2. Update doc/toolpath.txt — if you added/changed/removed any command, Lua API function, config option, buffer variable, or behavior. The help file is the authoritative reference. Keep the contents table section numbers in sync.
  3. Update README.md — if the change is user-facing. The README mirrors the help doc but is less exhaustive. Update the commands table, Lua API listing, config examples, and any relevant prose sections.
  4. Update plugin/toolpath.lua — if you added/changed/removed a user command.
  5. Update lua/toolpath/health.lua — if you added new dependencies, config requirements, or internal accessors.
  6. Bump M.version — in lua/toolpath/init.lua.
  7. Run ./scripts/test.sh — syntax checks all Lua files and runs the test suites. Add tests for any new pure functions or API changes.

CLI interface

The plugin shells out to path track <subcommand>. The current subcommands and their args are documented in local/track-source-annotate.md and docs/cli-graph-output.md. Key points:

  • init — creates session, returns session path on stdout. Accepts --base-uri and --base-ref for VCS anchoring.
  • step — records an edit. Accepts --source (JSON vcsSource). Async in normal flow, sync during gap replay (on scratch buffer).
  • visit — caches content for undo/redo navigation. No --source.
  • note — sets intent on head step. Still works, not deprecated.
  • annotate — generalized note. Can target any step, set intent, source, refs.
  • close — exports Path document on stdout and deletes session file.
  • export — like close but non-destructive.

All CLI communication is via args + stdin (buffer content) + stdout (results). No temp files, no sockets.

Architecture decisions

Plugin owns VCS awareness. The CLI is VCS-agnostic. The plugin runs git rev-parse and passes structured data via --base-uri, --base-ref, and --source JSON. This keeps the CLI editor-agnostic and VCS-agnostic.

Git cache refreshed on events, not keystrokes. FocusGained and ShellCmdPost trigger cache refresh. Never run git commands in the on_text_changed hot path.

Never modify buffer contents. The plugin is a passive observer — it reads buffer text via nvim_buf_get_lines but must never use vim.cmd("undo"), nvim_buf_set_lines, or any other API that mutates a tracked buffer. Gap replay uses a hidden scratch buffer with wundo/rundo to clone the undo tree. This prevents interference with the user's editing, LSP, and other plugins.

Async for step/visit, sync for everything else. The on_text_changed handler enqueues step and visit calls on a per-buffer serial queue using vim.system() callbacks. Gap replay (intermediate undo entries) is sync because it replays on a scratch buffer. init, close, note, annotate, and export are sync.

Stop order matters. M.stop() must: (1) delete the augroup first to prevent new events, (2) drain the async queue synchronously, (3) then call close. Getting this wrong causes races or lost steps.

Archive only if edits were made. stop() checks step_count > 0 before writing to archive/. Viewing a file without editing it shouldn't produce archive files.

Directory layout for session data

record.dir (e.g. ~/.local/share/toolpath) gets this structure:

<dir>/
├── README.md    ← auto-generated on first use, explains the directory
├── live/        ← active session state files (temporary, CLI manages lifecycle)
└── archive/     ← exported Path documents (<filename>.<timestamp>.path.json)

Common pitfalls

  • unpack vs table.unpack: Neovim's LuaJIT has unpack as a global. Standard Lua 5.2+ uses table.unpack. We use unpack throughout.
  • Buffer-local autocmds + global events: VimLeavePre is global but our autocmd uses buffer = bufnr. It fires for the active buffer on quit; other buffers get BufUnload. Combined with once = true, each buffer's stop handler fires exactly once.
  • vim.b variable cleanup: Always nil out buffer variables in clear_buf_vars() when stopping. Stale vim.b.toolpath_tracking = true on an untracked buffer would confuse statusline consumers.
  • vim.validate positional form: We use the Neovim 0.10+ positional form vim.validate("name", value, "type", optional). The older table form is deprecated.

Testing

Run all tests locally:

./scripts/test.sh

This runs luac -p syntax checks on all Lua files, then two test suites via nvim --headless -l:

  • test/test_internals.lua — pure function tests for format_base_uri, build_parent_map, and find_step_ancestor (exported via M._internals).
  • test/test_api.lua — public API surface tests (version format, _setup_done state, statusline(), _get_bin(), config validation).

The test harness is test/harness.lua (~50 lines, no external dependencies).

Adding a new test: open the appropriate test/test_*.lua file and add a T.test("name", function() ... end) block. Use T.eq(a, b) for deep equality.

CI: GitHub Actions (.github/workflows/test.yml) runs the test script on every push to main and every PR, against Neovim stable and nightly.

Manual verification (not covered by automated tests)

  1. :checkhealth toolpath — all checks pass
  2. Open a file — vim.b.toolpath_tracking should be true (with auto mode)
  3. Make edits — :ToolpathStatus shows step count incrementing, no UI lag
  4. :ToolpathStop — clean shutdown, no errors
  5. Check archive/ — file appears with correct name pattern

Coordination with the toolpath CLI

Design documents in local/ come from the CLI team. When the CLI adds new flags or changes behavior, update local/ with their docs and adapt the plugin accordingly. The docs/ directory contains design docs we authored for the CLI team (e.g. cli-graph-output.md was our original proposal; local/track-source-annotate.md is what they actually implemented).