Skip to content

divramod/shooter.nvim

Repository files navigation

shooter.nvim

A Neovim plugin for managing iterative development workflows with shots (numbered work items), tmux integration, and context-aware AI collaboration.

Table of Contents

Terminology

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

Features

  • 📝 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

Requirements

  • 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)

Installation

lazy.nvim

{
  '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,
}

packer.nvim

use {
  'divramod/shooter.nvim',
  requires = {
    'nvim-telescope/telescope.nvim',
    'nvim-lua/plenary.nvim',
  },
  config = function()
    require('shooter').setup()
  end,
}

Quick Start

  1. Create a new shots file:

    :ShooterCreate
    

    Enter a feature name when prompted. This creates a timestamped markdown file in plans/prompts/.

  2. 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
  3. 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
  4. View open shots:

    :ShooterOpenShots
    
    • Shows all unexecuted shots in current file
    • Press Tab to multi-select
    • Press 1-4 to send to Claude pane

Commands

Core Commands

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

Send Commands

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

Queue Commands

Command Description
:ShooterQueueAdd{1-4} Add shot to queue for pane N
:ShooterQueueView View and manage queue
:ShooterQueueClear Clear entire queue

File Movement

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

Default Keybindings

All keybindings use <space> prefix (customizable):

Core

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

Navigation

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

Send to Claude

Key Action
<space>1-4 Send shot to pane 1-4
<space><space>1-4 Send ALL open shots

File Movement (prefix: <space>m)

Key Action
<space>ma Archive
<space>md Done
<space>mb Backlog

Queue

Key Action
<space>q1-4 Queue for pane
<space>Q View queue

Configuration

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
  },
})

Context Files

Shooter.nvim injects context when sending shots to AI:

  1. Global Context (~/.config/shooter.nvim/shooter-context-global.md)

    • Shared across all projects
    • Your coding preferences, conventions, etc.
  2. Project Context (.shooter.nvim/shooter-context-project.md at git root)

    • Project-specific instructions
    • Auto-created from template on first use

Template System

Shooter uses customizable templates for the context instructions sent with each shot.

Template Locations (Priority Order)

  1. Project-specific: ./.shooter.nvim/shooter-context-instructions.md
  2. Global: ~/.config/shooter.nvim/shooter-context-instructions.md
  3. Plugin fallback: Built-in templates

For multi-shot sends, use shooter-context-instructions-multishot.md.

Template Variables

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

Example Custom Template

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

File Structure

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

Shot Format

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)

Health Check

:checkhealth shooter

Validates:

  • Dependencies (Telescope, tmux)
  • Context files
  • Directory structure
  • Queue file integrity

Sound Notifications

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 pop
  • Glass.aiff - glass clink
  • Ping.aiff - ping
  • Tink.aiff - light tap
  • Blow.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.

Tips

  1. Multi-select shots: In :ShooterOpenShots, press Tab to select multiple, then 1-4 to send all
  2. Context management: Edit ~/.config/shooter.nvim/shooter-context-global.md to customize AI instructions
  3. Queue workflow: Queue shots while waiting for AI response, then send batch later
  4. Oil integration: Works seamlessly with oil.nvim for file management
  5. File-based sending: Shots are sent via @filepath syntax 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.
  6. Sound notifications: Enable sound.enabled = true to hear a sound when shots are sent

Troubleshooting

Shot marked as sent but wasn't actually 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:

  1. Native vim undo: Press u in vim immediately after the marking happened. This will undo the header change like any other edit.

  2. 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

License

MIT

Credits

Refactored from divramod's dotfiles next-action workflow.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •