hephaestus/heph.nvim/lua/heph/command.lua
Erich Blume b97c387252
Some checks failed
Build / validate (pull_request) Failing after 3s
heph.nvim: context-item promotion + Dagger headless-nvim CI (slice 11c)
Backend (TDD):
- task.promote {container_id, item_ref, attention?, project?}: mint a committed
  task from the item_ref-th `- [ ]` context item (1-based, document order via a
  new extract::context_item_lines) and rewrite that source line into a [[link]]
  to it. Unit + rpc_socket tests.
- resolve_id now excludes canonical-context docs, so [[Task Title]] resolves to
  the task, not its identically-titled context doc (deterministic; a general fix
  surfaced by promotion's ULID-tiebreak ambiguity).

Plugin: :Heph promote / promote_under_cursor (save-if-dirty → compute item index
with a code-fence-aware scanner mirroring extract.rs → task.promote → reload the
rewritten buffer). e2e spec (f): promote a context line, assert the new task in
next, the source line became a link, and the container backlinks the task.

CI via Dagger: a test_nvim function bakes a pinned, arch-detected Neovim
(v0.11.2 — Debian's is too old for vim.uv) onto rust:1-bookworm, builds hephd,
and runs the self-contained shim suite (cargo + target cache volumes);
build.yaml calls `dagger call test-nvim`. run.lua now fails on zero specs (no
false-green). Validated end-to-end: passing suite → exit 0, failing spec →
Dagger exit 1.

117 Rust tests + 7 nvim e2e specs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 06:08:41 -07:00

128 lines
3.5 KiB
Lua

--- The `:Heph <subcommand>` user-command surface. Tactical/Organizational task
--- views (next/list/capture/...) arrive with slice 11b; this is the knowledge-
--- base core (journal, links).
local M = {}
local ATTENTIONS = { "white", "orange", "red", "blue" }
--- subcommand -> handler(args: string[])
M.subs = {
-- knowledge base
today = function()
require("heph.journal").open()
end,
journal = function(args)
require("heph.journal").open(args[1])
end,
follow = function()
require("heph.link").follow()
end,
open = function(args)
if args[1] then
require("heph.node").open(args[1])
end
end,
search = function(args)
local query = table.concat(args, " ")
if #query == 0 then
require("heph.util").notify("usage: :Heph search <query>", vim.log.levels.WARN)
return
end
local nodes = require("heph.rpc").call("search", { query = query })
require("heph.picker").select(nodes, {
prompt = "heph search: " .. query,
format = function(n)
return string.format("[%s] %s", n.kind, n.title)
end,
}, function(choice)
if choice then
require("heph.node").open(choice.id)
end
end)
end,
-- tasks
next = function(args)
require("heph.view").next({ scope = args[1] })
end,
list = function(args)
require("heph.view").list({ attention = args[1] })
end,
capture = function(args)
local title = table.concat(args, " ")
if #title == 0 then
require("heph.util").notify("usage: :Heph capture <title>", vim.log.levels.WARN)
return
end
require("heph.picker").select(ATTENTIONS, { prompt = "attention for: " .. title }, function(attention)
require("heph.task").capture(title, { attention = attention })
require("heph.util").notify("captured: " .. title)
end)
end,
attention = function(args)
if args[1] then
require("heph.task").set_attention_current(args[1])
else
require("heph.picker").select(ATTENTIONS, { prompt = "attention" }, function(choice)
if choice then
require("heph.task").set_attention_current(choice)
end
end)
end
end,
done = function()
require("heph.task").set_state_current("done")
end,
drop = function()
require("heph.task").set_state_current("dropped")
end,
skip = function()
require("heph.task").skip_current()
end,
promote = function(args)
require("heph.task").promote_under_cursor({ attention = args[1] })
end,
log = function(args)
require("heph.task").log_append_current(table.concat(args, " "))
end,
}
--- `:Heph` entry point.
function M.run(opts)
local args = opts.fargs
local sub = args[1]
if not sub then
require("heph.util").notify("usage: :Heph <" .. table.concat(M.names(), "|") .. ">", vim.log.levels.WARN)
return
end
local handler = M.subs[sub]
if not handler then
require("heph.util").notify("unknown subcommand: " .. sub, vim.log.levels.ERROR)
return
end
local ok, err = pcall(handler, vim.list_slice(args, 2))
if not ok then
require("heph.util").notify(tostring(err), vim.log.levels.ERROR)
end
end
--- Sorted subcommand names.
function M.names()
local names = vim.tbl_keys(M.subs)
table.sort(names)
return names
end
--- Completion: subcommand names at the first position.
function M.complete(arglead, cmdline, _cursorpos)
-- Only complete the subcommand token (first arg after :Heph).
if cmdline:match("^%s*Heph%s+%S*$") then
return vim.tbl_filter(function(n)
return n:find(arglead, 1, true) == 1
end, M.names())
end
return {}
end
return M