A navigation guide for AI agents working in this codebase.
lua/askCode/ ← all plugin logic lives here
├── init.lua ← public API + conversation state (start here)
├── config.lua ← dot-notation config system
├── runner.lua ← thin vim.fn.jobstart wrapper
├── ui.lua ← window/buffer creation and updates
├── utils.lua ← file I/O, buffer reads, replacement parsing, debug log
└── agents/
├── init.lua ← agent registry (maps name → module)
├── gemini.lua ← Gemini CLI integration
├── kiro.lua ← Kiro CLI integration
├── opencode.lua ← OpenCode CLI integration
└── claude.lua ← Claude CLI integration
plugin/askCode.lua ← Neovim commands + <Plug> keymaps (entry point)
tests/ ← MiniTest suite (one file per module)
scripts/minimal_init.lua ← headless Neovim init used by make test
CODE-STANDARDS.md ← authoritative coding standards and architecture
.agents/add-new-agent.md ← step-by-step plan for adding a new agent
.claude/skills/add-agent/ ← Claude Code skill that automates adding a new agent
- Feature work: start in
lua/askCode/init.lua— it orchestrates all modules - Adding an agent: follow
.agents/add-new-agent.md, or invoke the/add-agentClaude Code skill — createlua/askCode/agents/<name>.lua, register inagents/init.lua - Config changes:
lua/askCode/config.lua— updateM.defaultand annotations - UI changes:
lua/askCode/ui.lua—show_windowandupdate_window - Commands/keymaps:
plugin/askCode.lua
Every agent module must implement:
function M.prepare_command(prompt) → string -- shell command to run
function M.parse_response(raw_output) → string|nil -- clean the CLI outputRegister in agents/init.lua:
M.agents.myagent = require("askCode.agents.myagent")Then users set agent = "myagent" in their config.
stdout_buffered = trueis passed torunner.run_commandfor all agent calls — responses are collected in full beforeon_exitfires, not streamed line-by-line.stdin = "null"is the default inrunner.run_command— prevents interactive CLIs from blocking on stdin.on_stderris only wired for thekiroagent inM.ask— Kiro writes its response to stderr, not stdout. Note:follow_upandask_replacedo not wireon_stderr, so kiro follow-ups are currently broken.- UI updates use
vim.schedule()— allupdate_windowcalls are deferred to avoid crossing the async boundary unsafely. - Conversation history is a temp file, not in-memory —
vim.fn.tempname()path stored instate.history_file; full history is re-read and re-sent on every follow-up. - Config merging uses
"keep"strategy —vim.tbl_deep_extend("keep", changes, current)means user values win over defaults, not the other way around. opencodeagent uses a temp file for the prompt — multiline prompts can't be safely passed viashellescapethroughsh -c; the agent writes the prompt to a temp file and uses"$(cat <tmpfile>)"in the command. It also requires--format jsonso the process exits cleanly.claudeagent uses a temp file for the prompt — same rationale as opencode; the prompt is written to a temp file and passed toclaude -p "$(cat <tmpfile>)". The-pflag makes Claude CLI non-interactive and outputs plain text, soparse_responseonly trims whitespace with no JSON parsing.
make test— runs the full test suite via headless Neovimmake test_file FILE=tests/test_foo.lua— runs a single test filemake deps/mini.nvim— clones mini.nvim intodeps/(required before first test run)
.github/workflows/luarocks.yml— publishes to LuaRocks on release.github/workflows/release-please.yml— automates changelog and GitHub releases
Tests use MiniTest.new_child_neovim() — each test case restarts a fresh child Neovim process. See .agents/summary/writing-unit-tests.md for the full pattern. Key points:
pre_casehook must callchild.restart({ "-u", "scripts/minimal_init.lua" })- Use
child.lua()to execute,child.lua_get()to retrieve values - Mock
io.popento avoid real CLI calls in agent tests
kiro.luainternal comments andvim.notifystrings still reference "AmazonQ" — cosmetic but misleading.on_stderris not wired infollow_uporask_replace— kiro agent follow-ups and replacements silently return empty responses.output_formatconfig field exists in the schema but has no effect anywhere.vim.g.askcode_rangeis set inplugin/askCode.luaforAskCodeReplacebut never read — dead code.debuglogging only coversinit.lua; agents and ui emit no debug output.
Use the /add-agent Claude Code skill to add a new agent in one step:
/add-agent <agent_name> <cli_invocation>
Example:
/add-agent claude "claude -p"
The skill reads the full implementation plan from .agents/add-new-agent.md and executes all steps automatically: creates the agent module, registers it, updates docs, writes unit tests, and verifies with make test.
To add an agent manually without the skill, follow .agents/add-new-agent.md step by step.
Full documentation is in .agents/summary/:
index.md— navigation guide with per-file summariesarchitecture.md— layer diagrams and state machinecomponents.md— per-module function referenceinterfaces.md— API contracts and config schemadata_models.md— data structure definitionsworkflows.md— end-to-end sequence diagramsdependencies.md— Neovim API usage and external requirements