A Neovim plugin for managing iterative development workflows with shots (numbered work items), tmux integration, and context-aware AI collaboration.
- Terminology
- Features
- Requirements
- Installation
- Quick Start
- Commands
- Default Keybindings
- Configuration
- Context Files
- Template System
- File Structure
- Shot Format
- Health Check
- Sound Notifications
- Tips
- Troubleshooting
- License
- Credits
| Term | Description |
|---|---|
| Shots File | The markdown file you edit in Neovim containing multiple shots (e.g., 20260118_0516_feature.md) |
| Shot | A numbered work item within a shots file (e.g., ## shot 5) |
| Shot History File | The file sent to Claude via @filepath syntax, saved to ~/.config/shooter.nvim/history/ |
| Context File | Global or project-specific instructions injected with each shot |
- 📝 Shot-based workflow: Break down features into numbered, executable shots
- 🔄 Tmux integration: Send shots directly to Claude (or other AI) running in tmux panes
- 🎯 Context injection: Automatically includes general and project-specific context
- 📂 File organization: Move files between folders (archive, backlog, done, etc.)
- 🔍 Telescope integration: Powerful pickers for files, shots, and queues
- 📋 Queue system: Queue shots for later execution across multiple panes
- ✅ Shot tracking: Mark shots as executed with timestamps
- 🖼️ Image insertion: Reference images in your shots
- 🎨 Syntax highlighting: Visual distinction between open and done shots
- Neovim >= 0.9.0
- telescope.nvim
- plenary.nvim
- tmux (for sending to AI panes)
- Claude CLI or similar AI tool (optional, for send functionality)
- gp.nvim (optional, for voice dictation with
<space>e) - hal CLI (optional, for image picking with
<space>g)
{
'divramod/shooter.nvim',
dependencies = {
'nvim-telescope/telescope.nvim',
'nvim-lua/plenary.nvim',
},
cmd = {
'ShooterCreate', 'ShooterList', 'ShooterOpenShots',
'ShooterSend1', 'ShooterSendAll1', 'ShooterQueueView',
},
keys = {
{ '<space>n', '<cmd>ShooterCreate<cr>', desc = 'Shooter: Create file' },
{ '<space>o', '<cmd>ShooterOpenShots<cr>', desc = 'Shooter: Open shots' },
{ '<space>t', '<cmd>ShooterList<cr>', desc = 'Shooter: List files' },
{ '<space>1', '<cmd>ShooterSend1<cr>', desc = 'Shooter: Send to pane 1' },
{ '<space>h', '<cmd>ShooterHelp<cr>', desc = 'Shooter: Help' },
},
config = function()
require('shooter').setup({
-- Optional: override defaults
paths = {
global_context = '~/.config/shooter.nvim/shooter-context-global.md',
prompts_root = 'plans/prompts',
},
keymaps = {
enabled = true, -- Enable default keymaps
},
})
end,
}use {
'divramod/shooter.nvim',
requires = {
'nvim-telescope/telescope.nvim',
'nvim-lua/plenary.nvim',
},
config = function()
require('shooter').setup()
end,
}-
Create a new shots file:
:ShooterCreateEnter a feature name when prompted. This creates a timestamped markdown file in
plans/prompts/. -
Add shots to your file (latest shot at top):
# 2026-01-21 - Add user authentication ## shot 2 Create registration endpoint ## shot 1 Set up database schema for users table
-
Send a shot to Claude (in tmux):
- Place cursor in shot 2
- Press
<space>1(or:ShooterSend1) - If no Claude pane exists, one is automatically created to the left
- Shot content + context is sent to Claude
-
View open shots:
:ShooterOpenShots- Shows all unexecuted shots in current file
- Press
Tabto multi-select - Press
1-4to send to Claude pane
| Command | Description |
|---|---|
:ShooterCreate |
Create new shots file |
:ShooterList |
Telescope picker for all files |
:ShooterOpenShots |
List open shots in current file |
:ShooterHelp |
Show help |
:ShooterLast |
Open last edited file |
:ShooterNewShot |
Add new shot to current file |
:ShooterDeleteLastShot |
Delete the last unexecuted shot |
:ShooterNextShot |
Go to next open shot |
:ShooterPrevShot |
Go to previous open shot |
| Command | Description |
|---|---|
:ShooterSend{1-9} |
Send current shot to pane N |
:ShooterSendAll{1-9} |
Send all open shots to pane N |
:ShooterSendVisual{1-9} |
Send visual selection to pane N |
| Command | Description |
|---|---|
:ShooterQueueAdd{1-4} |
Add shot to queue for pane N |
:ShooterQueueView |
View and manage queue |
:ShooterQueueClear |
Clear entire queue |
| Command | Description |
|---|---|
:ShooterArchive |
Move to archive/ |
:ShooterBacklog |
Move to backlog/ |
:ShooterDone |
Move to done/ |
:ShooterPrompts |
Move to prompts/ (in-progress) |
:ShooterGitRoot |
Move to git root |
All keybindings use <space> prefix (customizable):
| Key | Action |
|---|---|
<space>n |
Create new shots file |
<space>o |
Open shots picker |
<space>t |
Telescope file list |
<space>l |
Open last file |
<space>s |
New shot |
<space>e |
New shot + whisper (requires gp.nvim) |
<space>d |
Delete last shot |
<space>h |
Help |
| Key | Action |
|---|---|
<space>] |
Next open shot |
<space>[ |
Previous open shot |
<space>. |
Toggle shot done/open |
<space>L |
Go to latest sent shot |
<space>u |
Undo latest sent shot marking |
<space>H |
Run health check |
| Key | Action |
|---|---|
<space>1-4 |
Send shot to pane 1-4 |
<space><space>1-4 |
Send ALL open shots |
| Key | Action |
|---|---|
<space>ma |
Archive |
<space>md |
Done |
<space>mb |
Backlog |
| Key | Action |
|---|---|
<space>q1-4 |
Queue for pane |
<space>Q |
View queue |
require('shooter').setup({
paths = {
-- Global context (shared across projects)
global_context = '~/.config/shooter.nvim/shooter-context-global.md',
-- Project context (at git root)
project_context = '.shooter.nvim/shooter-context-project.md',
-- Prompts directory
prompts_root = 'plans/prompts',
-- Queue file
queue_file = 'plans/prompts/.shot-queue.json',
},
tmux = {
delay = 0.2, -- Delay between sends (seconds)
max_panes = 9, -- Max supported panes
},
telescope = {
layout_strategy = 'vertical',
layout_config = {
width = 0.9,
height = 0.9,
preview_height = 0.5,
},
},
keymaps = {
enabled = true, -- Enable default keymaps
prefix = ' ', -- Main prefix
move_prefix = 'm', -- Move command prefix
},
sound = {
enabled = true, -- Play sound when shot is sent
file = '/System/Library/Sounds/Pop.aiff', -- macOS system sound
volume = 0.5, -- Volume 0.0-1.0
},
})Shooter.nvim injects context when sending shots to AI:
-
Global Context (
~/.config/shooter.nvim/shooter-context-global.md)- Shared across all projects
- Your coding preferences, conventions, etc.
-
Project Context (
.shooter.nvim/shooter-context-project.mdat git root)- Project-specific instructions
- Auto-created from template on first use
Shooter uses customizable templates for the context instructions sent with each shot.
- Project-specific:
./.shooter.nvim/shooter-context-instructions.md - Global:
~/.config/shooter.nvim/shooter-context-instructions.md - Plugin fallback: Built-in templates
For multi-shot sends, use shooter-context-instructions-multishot.md.
Use {{variable_name}} syntax in your templates:
| Variable | Description | Example |
|---|---|---|
{{shot_num}} |
Current shot number | 117 |
{{shot_nums}} |
Comma-separated (multishot) | 1, 2, 3 |
{{file_path}} |
File path (with ~ for home) | ~/dev/plans/prompts/file.md |
{{file_name}} |
Filename with extension | 20260118_0516_feature.md |
{{file_title}} |
Title from first # heading | 2026-01-18 - feature name |
{{repo_name}} |
Repository name from git | divramod/shooter.nvim |
{{repo_path}} |
Git root path (with ~) | ~/cod/shooter.nvim |
Create ~/.config/shooter.nvim/shooter-context-instructions.md:
# context
1. This is shot {{shot_num}} of "{{file_title}}" in {{repo_name}}.
2. Please read {{file_path}} for previous context.
3. You should not implement old shots.
4. Your current task is shot {{shot_num}}.See templates/VARIABLES.md for full documentation
plans/prompts/
├── 20260121_0930_add-auth.md # In-progress
├── archive/
│ └── 20260120_1015_setup-db.md # Completed
├── backlog/
│ └── 20260122_0800_refactor-api.md # Future work
├── done/
│ └── 20260119_1400_init-project.md # Finished
└── .shot-queue.json # Queue state
Shots are ordered with the latest at the top:
# 2026-01-21 - Feature Title
## shot 3
Next task to work on
## x shot 2 (2026-01-21 14:30:00)
Completed shot (marked with 'x' and timestamp)
## x shot 1 (2026-01-21 10:00:00)
First task (already done):checkhealth shooter
Validates:
- Dependencies (Telescope, tmux)
- Context files
- Directory structure
- Queue file integrity
Shooter can play a sound when a shot is successfully sent. This is useful for audible feedback when you're not looking at the screen.
Configuration:
sound = {
enabled = true, -- Enable/disable sound
file = '/System/Library/Sounds/Pop.aiff', -- Sound file path
volume = 0.5, -- Volume (0.0-1.0)
}Available macOS system sounds (in /System/Library/Sounds/):
Pop.aiff(default) - short popGlass.aiff- glass clinkPing.aiff- pingTink.aiff- light tapBlow.aiff,Bottle.aiff,Frog.aiff,Funk.aiff,Morse.aiff,Purr.aiff,Sosumi.aiff,Submarine.aiff
Custom sound: Use any .aiff, .mp3, or .wav file:
sound = {
enabled = true,
file = '~/.config/shooter.nvim/shot.mp3',
volume = 0.7,
}Test sound: Run :ShooterSoundTest to test your configuration.
Linux: Uses paplay (PulseAudio) instead of afplay.
- Multi-select shots: In
:ShooterOpenShots, pressTabto select multiple, then1-4to send all - Context management: Edit
~/.config/shooter.nvim/shooter-context-global.mdto customize AI instructions - Queue workflow: Queue shots while waiting for AI response, then send batch later
- Oil integration: Works seamlessly with oil.nvim for file management
- File-based sending: Shots are sent via
@filepathsyntax for reliability. The shot history file is saved to~/.config/shooter.nvim/history/<user>/<repo>/<filename>/shot-NNNN-<timestamp>.md. Note: This won't show blue like typed input in Claude Code, but Claude outputs the content at the start of each response for transparency. - Sound notifications: Enable
sound.enabled = trueto hear a sound when shots are sent
If a shot was marked as sent (header changed from ## shot N to ## x shot N (timestamp)) but the send failed due to a problem (e.g., tmux issue, Claude not responding), you have two options to undo the marking:
-
Native vim undo: Press
uin vim immediately after the marking happened. This will undo the header change like any other edit. -
Undo latest sent command: Press
<space>u(or:ShooterUndoLatestSent) to automatically find and undo the marking of the most recently sent shot. This is useful if you've made other edits since the marking and can't use native undo. The command:- Finds the shot with the most recent timestamp
- Changes
## x shot N (YYYY-MM-DD HH:MM:SS)back to## shot N - Saves the file
- Moves cursor to the undone shot
MIT
Refactored from divramod's dotfiles next-action workflow.