{"id":46527322,"url":"https://github.com/mikeboiko/nvim-flow","last_synced_at":"2026-03-08T04:00:32.997Z","repository":{"id":341093395,"uuid":"1168877908","full_name":"mikeboiko/nvim-flow","owner":"mikeboiko","description":"neovim command runner","archived":false,"fork":false,"pushed_at":"2026-02-28T00:25:19.000Z","size":29,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-28T03:43:28.828Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mikeboiko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-27T22:39:05.000Z","updated_at":"2026-02-28T00:25:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mikeboiko/nvim-flow","commit_stats":null,"previous_names":["mikeboiko/nvim-flow"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mikeboiko/nvim-flow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeboiko%2Fnvim-flow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeboiko%2Fnvim-flow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeboiko%2Fnvim-flow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeboiko%2Fnvim-flow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mikeboiko","download_url":"https://codeload.github.com/mikeboiko/nvim-flow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeboiko%2Fnvim-flow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30244537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-08T00:58:18.660Z","status":"online","status_checked_at":"2026-03-08T02:00:06.215Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-06T21:00:28.268Z","updated_at":"2026-03-08T04:00:32.990Z","avatar_url":"https://github.com/mikeboiko.png","language":"Lua","readme":"# nvim-flow\n\n[![CI](https://github.com/mikeboiko/nvim-flow/actions/workflows/ci.yml/badge.svg)](https://github.com/mikeboiko/nvim-flow/actions/workflows/ci.yml)\n\n`nvim-flow` is a pure lua Neovim workflow runner for file-based commands defined in `.flow.yml`.\n\n## Quick start\n\nCreate a `.flow.yml` next to your project files:\n\n```yaml\ndemo.py:\n  cmd: python \"{{filepath}}\" --name mike\n```\n\nOpen the file in Neovim and run `:FlowRun` or `:FlowDebug` — nvim-flow resolves the command for the current file and executes it in a terminal split:\n\n![](https://vhs.charm.sh/vhs-5wLUhySW1bp6mOc67KGcET.gif)\n\n## Motivation\n\nI wanted a workflow that matches how I actually work in Neovim: simple YAML config, fast command resolution, and quick run/debug feedback without extra runtime dependencies.\n\n## Features\n\n- First-class `nvim-dap` integration through `:FlowDebug`\n- Flow source jump (`:FlowEdit`) to open the matched `.flow.yml` definition\n- File lock support (`:FlowToggleLock`)\n- Command preview in a floating window (`:FlowPreview`)\n- Python traceback -\u003e quickfix parser (`:FlowQuickfix`)\n- YAML config (`.flow.yml`)\n- Recursive flow discovery + merge (file dir -\u003e `$HOME`, closer wins)\n- Optional `match` arrays for reusable command definitions\n- Configurable keymaps through `setup()`\n\n## Installation (lazy.nvim)\n\n### Minimum\n\n```lua\nreturn {\n  { \"mikeboiko/nvim-flow\" },\n}\n```\n\n### Typical (with optional settings)\n\n```lua\nreturn {\n  {\n    \"mikeboiko/nvim-flow\",\n    event = { \"BufReadPost\", \"BufNewFile\" },\n    cmd = { \"FlowRun\", \"FlowDebug\", \"FlowEdit\", \"FlowToggleLock\", \"FlowPreview\", \"FlowQuickfix\" },\n    opts = {\n      config_file = \".flow.yml\",\n      terminal_height = 15,\n      terminal_position = \"top\",\n      edit_open_command = \"tabedit\",\n      stop_at_home = true,\n      show_command = true,\n      keymaps = {\n        run = \"\u003cCR\u003e\",\n        debug = \"\u003cleader\u003efd\",\n        edit = \"\u003cleader\u003efe\",\n        toggle_lock = \"\u003cleader\u003efl\",\n        preview = \"\u003cleader\u003efp\",\n        quickfix = \"\u003cleader\u003efq\",\n      },\n    },\n  },\n}\n```\n\n## Setup (optional)\n\n`setup()` is only needed when you want to override defaults.\nIf you skip setup, `nvim-flow` still works with built-in defaults.\n\n```lua\nrequire(\"nvim-flow\").setup({\n  config_file = \".flow.yml\",\n  terminal_height = 15,\n  terminal_position = \"top\", -- \"top\" | \"bottom\"\n  edit_open_command = \"tabedit\", -- e.g. \"tabedit\" | \"edit\" | \"split\" | \"vsplit\"\n  stop_at_home = true,\n  show_command = true,\n  keymaps = {\n    run = nil,\n    debug = nil,\n    edit = nil, -- suggested: \"\u003cleader\u003efe\"\n    toggle_lock = nil,\n    preview = nil,\n    quickfix = nil,\n  },\n})\n```\n\n## Optional parameters\n\n- `config_file` (`string`, default: `\".flow.yml\"`)\n  - Filename to search while walking directories upward.\n- `terminal_height` (`number`, default: `15`)\n  - Height of the terminal split used by `FlowRun`.\n- `terminal_position` (`\"top\" | \"bottom\"`, default: `\"top\"`)\n  - Where the terminal split opens.\n- `edit_open_command` (`string`, default: `\"tabedit\"`)\n  - Vim command used by `FlowEdit` to open the matched `.flow.yml` location (for example: `tabedit`, `edit`, `split`, `vsplit`).\n- `stop_at_home` (`boolean`, default: `true`)\n  - Stop recursive config search at `$HOME` instead of `/`.\n- `show_command` (`boolean`, default: `true`)\n  - Print resolved command before execution output.\n- `keymaps` (`table`, default: all `nil`)\n  - Optional mappings for:\n    - `run`\n    - `debug`\n    - `edit`\n    - `toggle_lock`\n    - `preview`\n    - `quickfix`\n  - Set any key to `nil` to leave it unmapped.\n\n## Commands\n\n- `:FlowRun` - run resolved flow command in a terminal split\n- `:FlowDebug` - resolve the same flow command and launch a matching `nvim-dap` debug session\n- `:FlowEdit` - open the matched `.flow.yml` file and jump to the resolved command line\n- `:FlowToggleLock[ {filepath}]` - toggle lock (or set lock to explicit path)\n- `:FlowSet {filepath}` - compatibility alias for setting lock directly\n- `:FlowPreview` - show resolved command for current (or locked) file\n- `:FlowQuickfix` - parse the last flow output as Python traceback and fill quickfix\n\n## FlowEdit behavior\n\n`FlowEdit` follows the same resolution pipeline as `FlowRun` / `FlowPreview`, then opens the corresponding `.flow.yml` and jumps to the resolved command line.\n\nBy default it opens in a new tab (`edit_open_command = \"tabedit\"`). Change `edit_open_command` if you prefer `edit`, `split`, or `vsplit`.\n\n## Debug integration (`nvim-dap`)\n\n`FlowDebug` uses the same command resolution pipeline as `FlowRun`, then parses the command to create a debug configuration for `nvim-dap` and calls `dap.continue()`.\n\nSupported command families include `python` / `python3`, `uv run ...` (including module mode), and `node`.\n\nExample:\n\n```yaml\npy:\n  cmd: python \"{{filepath}}\" --env dev\n```\n\nRunning `:FlowDebug` on a Python buffer resolves this flow, builds the debug launch config, and starts the debugger.\n\n## `.flow.yml` format\n\n### Basic mode\n\n```yaml\ndefault:\n  cmd: '{{filepath}}'\n\npy:\n  cmd: python \"{{filepath}}\"\n\nmain.py:\n  cmd: python \"{{filepath}}\" --mode=dev\n```\n\n### Advanced match mode\n\n```yaml\npython-group:\n  match: [py, pyw, 'test_*.py', 'scripts/']\n  cmd: python \"{{filepath}}\"\n```\n\nIf `match` is omitted, the top-level key is used as before.\n\n### Match priority\n\nResolution order:\n\n1. **basename** (e.g., `main.py`)\n2. **`match` entries**\n3. **folder name**\n4. **repo name**\n5. **extension** (`.py` then `py`)\n6. **`default`**\n\nExample definitions for each priority type:\n\n```yaml\nmain.py:\n  cmd: echo \"1 basename\"\n\npython-group:\n  match: [py, 'test_*.py']\n  cmd: echo \"2 match\"\n\ntests:\n  cmd: echo \"3 folder\"\n\nmy-repo:\n  cmd: echo \"4 repo\"\n\n.py:\n  cmd: echo \"5 extension-dot\"\n\npy:\n  cmd: echo \"5 extension\"\n\ndefault:\n  cmd: echo \"6 default\"\n```\n\nMini winner scenario:\n\n- For `/work/my-repo/tests/main.py`, `main.py` (basename) wins.\n- If the basename entry is removed, `match` entries are checked before folder/repo/extension/default.\n\nIf multiple `match` entries apply, `nvim-flow` uses deterministic precedence: nearest config file first, then YAML declaration order within that file.\n\n## Recursive merge behavior\n\nWhen running from `/a/b/c/file.py`, `nvim-flow` searches for `.flow.yml` in:\n\n- `/a/b/c/.flow.yml`\n- `/a/b/.flow.yml`\n- `/a/.flow.yml`\n- ... up to `$HOME/.flow.yml` (if `stop_at_home = true`)\n\nAll found configs are merged. Closer files override farther files.\n\n## Template variables\n\n`nvim-flow` expands these variables in `cmd`:\n\n- `{{filepath}}`\n- `{{dir}}`\n- `{{filename}}`\n- `{{ext}}`\n- `{{repo}}`\n- `{{folder}}`\n\n## Runner behavior\n\n- Default runner: terminal split (`runner: vim` or omitted)\n- Terminal split opens at the top by default; set `terminal_position = \"bottom\"` to open below.\n- With `show_command = true`, the separator line is sized to the command width (capped by terminal width).\n- Debug runner: `runner: debug` or `:FlowDebug` (requires `nvim-dap`)\n\n## Quickfix behavior\n\n`FlowQuickfix` parses the **last** `FlowRun` terminal output and extracts Python traceback lines:\n\n`File \"/path/file.py\", line 42, in ...`\n\nThen it populates and opens the quickfix list.\n\n## Testing\n\nThis plugin uses plenary's busted harness.\n\nRun tests:\n\n```bash\nnvim --headless -u tests/minimal_init.lua \\\n  -c \"PlenaryBustedDirectory tests/nvim-flow { minimal_init = 'tests/minimal_init.lua' }\" \\\n  -c \"qa\"\n```\n\n## Recording demo GIFs (VHS)\n\nThis repo includes a VHS tape at `vhs/nvim-flow-demo.tape` that records a demo using `./vhs/demo.py` and `.flow.yml`.\n\nThe demo shows:\n\n1. `:FlowRun` — execute the command in a terminal split\n2. Set a breakpoint with `\u003cspace\u003edb`, then `:FlowDebug` (`nvim-dap`)\n\nRun it with:\n\n```bash\nvhs vhs/nvim-flow-demo.tape\nvhs publish nvim-flow-demo.gif\n```\n\nThe tape outputs `vhs/nvim-flow-demo.gif`, which autoplays when embedded in README markdown.\n\n## Credit\n\nThis project was inspired by ideas from `vim-flow`, with substantial changes for this codebase and workflow:\nhttps://github.com/jonmorehouse/vim-flow\n","funding_links":[],"categories":["Code Runner"],"sub_categories":["Quickfix"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikeboiko%2Fnvim-flow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmikeboiko%2Fnvim-flow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikeboiko%2Fnvim-flow/lists"}