@vigent/cli 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KinfeMichael Tariku and the vigent contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,262 @@
1
- # Temporary Holding Version
1
+ # vigent
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A vim-style, lightweight terminal app for running several coding agents at once, built on the [pi](https://github.com/badlogic/pi-mono) engine. It can also drive the Claude Code, Codex and OpenCode CLIs you already have installed, with their own logins.
4
+
5
+ ![vigent with the default Neovim theme](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/vigent-default-theme.png)
6
+
7
+ ![vigent with the gruvbox theme, second session focused](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/vigent-gruvbox.png)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g @vigent/cli
13
+ ```
14
+
15
+ The package is `@vigent/cli` (npm doesn't accept the plain name `vigent`, which it finds too close to `vitest`); the command is `vigent`.
16
+
17
+ Requires Node.js 20 or newer and git. Then, inside any git repository:
18
+
19
+ ```bash
20
+ vigent # pi with ANTHROPIC_API_KEY (or OPENAI_API_KEY / GEMINI_API_KEY), or a signed-in CLI
21
+ vigent --engine claude # new agents run your installed Claude Code (also: codex, opencode, pi)
22
+ vigent --demo # scripted agents, nothing needed
23
+ vigent --help
24
+ ```
25
+
26
+ `vigent --version` shows which vigent runs (with the commit, when it runs from a clone). An older build may be missing a theme or command from these docs, for example `E185: Cannot find color scheme 'lazyvim'`. In a clone, `git pull && npm install` updates and rebuilds it, and vigent warns at startup when its build is older than its source. From npm, run `npm install -g @vigent/cli@latest`.
27
+
28
+ ### Start screen and which-key
29
+
30
+ Like LazyVim, vigent opens on a dashboard. Press `i` and type a task, `n` to name a new agent, `t` for themes, `s` for settings, `m` / `e` for model and effort, `c` to check engines, `?` for every key, or `q` to quit. Press `Space` (the leader) and pause, and a which-key panel lists every key that can follow and what it does. `Esc` closes it, and `:set nowhichkey` turns it off.
31
+
32
+ ![the dashboard, with the lazyvim theme over a translucent terminal](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/dashboard-lazyvim.png)
33
+
34
+ ![which-key after Space](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/which-key.png)
35
+
36
+ ### First agent
37
+
38
+ Inside a git repository, press `i`, type what you want done, and press Enter. An agent starts on it in its own worktree, using pi if it has an API key, or else the first signed-in CLI (Claude Code, Codex, OpenCode; `:checkhealth` shows which). Watch it work, press `i` again to follow up, and `Esc` to go back to NORMAL mode. `:new <task> --engine codex` starts more agents, and `]s` / `[s` or `1`–`9` switch between them.
39
+
40
+ ## From source
41
+
42
+ ```bash
43
+ git clone https://github.com/farming-labs/vigent && cd vigent
44
+ npm install
45
+ npm run build
46
+ npm run demo # scripted sessions, no API key needed
47
+ npm test # build and run the test suite
48
+ ```
49
+
50
+ ### Releasing
51
+
52
+ Bump `version` in package.json, merge, then push a matching tag (`git tag v0.2.0 && git push origin v0.2.0`), or run the **Release** workflow from the Actions tab. It tests and publishes to npm with the `NPM_TOKEN` repository secret, and skips a version that's already published.
53
+
54
+ ## Keys
55
+
56
+ | Mode | Key | Action |
57
+ |---|---|---|
58
+ | NORMAL | `i` / `a` / `o` | Enter INSERT and type a prompt for the focused agent (it waits in a queue if the agent is busy). With no agent yet, the prompt starts one |
59
+ | NORMAL | `j` `k` `gg` `G` `Ctrl-d` `Ctrl-u` `{` `}` | Scroll the transcript, or jump between messages |
60
+ | NORMAL | `]s` `[s` `1`–`9` `Ctrl-^` | Switch agents |
61
+ | NORMAL | `za` `zR` `zM` | Toggle, open or close tool-output folds |
62
+ | NORMAL | `/pattern` `?pattern` `n` `N` | Search the transcript, forward or backward |
63
+ | NORMAL | `V` / `v` | VISUAL: select lines with `j` `k`, `ib` for a code block, `y` to copy |
64
+ | NORMAL | `yy` / `yib` | Copy the agent's last reply / the nearest code block |
65
+ | NORMAL | `dd` | Kill the focused agent (asks first) |
66
+ | NORMAL | `<Space>n` `d` `m` `p` `q` `t` `D` `u` `s` | `:new`, `:diff`, `:merge`, `:pr`, `:queue`, colorscheme, `:dash`, `:tree`, `:settings` |
67
+ | INSERT | `Enter` / `Esc` | Send the prompt / back to NORMAL |
68
+
69
+ Copies go to the system clipboard through the terminal (OSC 52), so they work over SSH too. `:help` lists every active key, including yours.
70
+
71
+ ![Neovim-style intro screen](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/intro.png)
72
+
73
+ ![VISUAL mode on a markdown reply](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/visual-mode.png)
74
+
75
+ ## Engines: pi, Claude Code, Codex, OpenCode
76
+
77
+ pi runs inside vigent. Claude Code, Codex and OpenCode are the CLIs you may already use: vigent runs them as installed, in the agent's worktree, so each one uses its own login and settings. vigent never reads their credentials; it only runs their version and sign-in status commands to show what's connected.
78
+
79
+ ```vim
80
+ :new fix the race --engine codex " this agent runs Codex
81
+ :new docs pass --engine claude --model opus " a model for that CLI
82
+ :checkhealth " installed? signed in? how to connect the rest
83
+ ```
84
+
85
+ The sidebar's ENGINES block shows each one: `●` signed in, `◐` installed but not signed in (with the command to sign in, like `codex login`), `○` not installed. Without `--engine`, new agents use `engine` from the config. Its default, `"auto"`, picks pi if it has an API key, otherwise the first signed-in CLI. A CLI agent survives a restart too: vigent saves the CLI's conversation id and continues that conversation. Its earlier messages stay in the CLI's own history.
86
+
87
+ ![:checkhealth with Claude Code signed in and Codex waiting for codex login](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/checkhealth.png)
88
+
89
+ ![a Claude Code agent reopened after a restart, continuing its conversation](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/claude-code-agent.png)
90
+
91
+ | Engine | vigent runs | Default extra arguments |
92
+ |---|---|---|
93
+ | `claude` | `claude -p --output-format stream-json` | `--permission-mode acceptEdits` |
94
+ | `codex` | `codex exec --json` (`exec resume` to continue) | `-c sandbox_mode="workspace-write"` |
95
+ | `opencode` | `opencode run --format json --dir <worktree>` | none |
96
+
97
+ **Model and effort:** `:model` (`<Space>M`) opens a picker with the models the agent's engine offers, and you can type any name. `:effort` (`<Space>e`) sets the reasoning effort (pi: thinking level). Both apply from the next prompt, show in the winbar and survive a restart. With no agent open, they set the defaults for new agents.
98
+
99
+ ![:model on a Claude Code agent](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/model-picker.png)
100
+
101
+ Change the arguments or the model per engine in `[engines.<id>]` ([docs/config.md](https://github.com/farming-labs/vigent/blob/main/docs/config.md#engines)).
102
+
103
+ ## Worktrees
104
+
105
+ Every agent gets its own git worktree and branch, so agents never touch your checkout or each other's files:
106
+
107
+ ```vim
108
+ :new fix flaky checkout test " branch fix/flaky-checkout-test in ../<repo>-worktrees/fix-flaky-checkout-test
109
+ :new auth-refactor " branch refactor/auth
110
+ :diff " what the focused agent changed, new files included
111
+ :worktrees " list them
112
+ :kill " stop the agent, keep its work (:kill! also removes the worktree, safely)
113
+ ```
114
+
115
+ Branch names follow the usual conventions (`fix/…`, `feat/…`, `docs/…`, `refactor/…`) and are taken from the task you type. The sidebar and statusline show each agent's branch with live `+added −removed` counts. Removing a worktree goes through git's own safety checks, so uncommitted or unmerged work is never thrown away. Folder, branch pattern and base branch are set in `[worktree]` ([docs/config.md](https://github.com/farming-labs/vigent/blob/main/docs/config.md#worktree)).
116
+
117
+ ### Lightweight worktrees with riftri
118
+
119
+ A worktree is normally a full copy of your checkout, so ten agents on a big repo cost ten checkouts of disk. vigent installs [riftri](https://riftri.dev) with it (an optional dependency; `riftri` on your PATH works too). When riftri can, it makes each agent's worktree a real git linked worktree whose unchanged files share copy-on-write storage. Each new agent then costs almost no disk, and only the files it changes take space of their own. riftri uses APFS clones on macOS, Btrfs or reflink-enabled XFS (or OverlayFS) on Linux, and ReFS block clones on Windows.
120
+
121
+ - `:new` says `· riftri reflink` (or `apfs-clone`, `overlay-fs`, …) when riftri made the worktree. When it falls back to a plain git copy, it says so, and `:checkhealth` shows riftri's reason and its fix.
122
+ - `:checkhealth` and `:worktrees` show what the worktrees cost: for example, *4 worktrees: 58.0 MB of files in 11.7 MB on disk*.
123
+ - Removing (`:kill!`, `:worktree remove`) goes through `riftri worktree remove`, which refuses uncommitted changes like git does and journals the removal. After that, `riftri gc` frees storage that no agent uses any more.
124
+ - On restart, vigent inspects riftri's worktrees before reopening their agents. If an OverlayFS view lost its mount (after a reboot), vigent repairs it first.
125
+ - vigent falls back to git only when riftri declines with a receipt saying nothing was changed (unsupported volume, checkout or environment). Any other riftri failure is shown, never papered over. `[worktree] backend = "riftri"` requires riftri, and `"git"` turns it off.
126
+
127
+ ![:worktrees with every agent on riftri](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/riftri-worktrees.png)
128
+
129
+ ![agents in their own worktrees](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/worktrees-sidebar.png)
130
+
131
+ ![:diff](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/worktrees-diff.png)
132
+
133
+ ## Reviewing and landing work
134
+
135
+ ```vim
136
+ :diff " the agent's changes; ]c / [c jump between hunks
137
+ :merge " squash-merge its branch into yours: one commit, e.g. "fix: flaky checkout test"
138
+ :merge --no-squash " a regular merge commit instead
139
+ :pr " push the branch and open a pull request (gh), or print the compare URL
140
+ :dash " every agent at a glance; j/k and Enter to jump to one
141
+ :queue <prompt> " send a follow-up after the current run (:queue alone lists them)
142
+ :pause / :resume " hold an agent and its queue, then continue
143
+ :w [file] " save the transcript as Markdown
144
+ :diff split " side by side; s switches layouts inside the diff window
145
+ :attach <branch> " reopen an agent in a kept worktree (see :worktrees)
146
+ :tree " the conversation's branches: go back to any point and try again
147
+ ```
148
+
149
+ `:tree` (or `<Space>u`) shows the focused agent's whole conversation, including branches you left. Pick an earlier prompt to edit it and send it again as a new branch, or pick a reply to continue from there. Every branch is kept in pi's session file, so it's still there after a restart.
150
+
151
+ Agents survive a restart: quit vigent, start it again in the same repo, and the agents you didn't `:kill` come back with their conversations. Their records live in git's own worktree metadata (`.git/worktrees/<name>/vigent/`), not in your files, and are deleted along with the worktree.
152
+
153
+ `:merge` and `:pr` first commit anything the agent left uncommitted. `:merge` only touches your checkout when it's on the agent's base branch with no uncommitted changes, and a conflict is rolled back so nothing changes. After a merge, `:kill!` removes the worktree and the merged branch. `:q` won't quit while agents are running; `:q!` or `ZQ` does. Use `:new <task> --model <id>` to give one agent a different model.
154
+
155
+ ![:diff split](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/diff-split.png)
156
+
157
+ ![:tree](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/tree.png)
158
+
159
+ ![:merge](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/merge.png)
160
+
161
+ ![:dash](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/dashboard.png)
162
+
163
+ ## Configuration
164
+
165
+ Everything lives in `~/.config/vigent/config.toml`. `vigent config init` writes a commented starter file, and `vigent config check` validates it. Keys use vim notation, and the right-hand side can be an action, an ex command or other keys:
166
+
167
+ ```toml
168
+ theme = "tokyonight"
169
+ leader = "<Space>"
170
+
171
+ [ui]
172
+ sidebar_width = 32
173
+ timeoutlen = 500
174
+
175
+ [highlights]
176
+ Normal = { bg = "NONE" }
177
+
178
+ [keys.normal]
179
+ "<leader>t" = "theme.picker" # an action
180
+ "<leader>n" = ":new " # open the command line, ready to type
181
+ "<leader>d" = ":colorscheme default<CR>"
182
+ "<Tab>" = "session.next"
183
+ "H" = "gg" # other keys, with their built-in meaning
184
+ "q" = false # unbind
185
+
186
+ [keys.insert]
187
+ "jk" = "<Esc>"
188
+ ```
189
+
190
+ You can also change keys while vigent runs, with `:nmap`, `:imap`, `:cmap`, `:unmap` and `:let mapleader`. **[docs/config.md](https://github.com/farming-labs/vigent/blob/main/docs/config.md)** has every option, action and built-in key.
191
+
192
+ ![:help lists every key, yours first](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/keys-help.png)
193
+
194
+ ## Themes
195
+
196
+ vigent speaks Neovim's highlight groups (`Normal`, `StatusLine`, `CursorLine`, …), so your vim or Neovim theme carries straight over.
197
+
198
+ ```bash
199
+ vigent theme import # whatever colorscheme your nvim/vim config uses
200
+ vigent theme import kanagawa # any colorscheme your editor can load (plugins included)
201
+ vigent theme import ./molokai.vim # a .vim file, no editor needed
202
+ vigent theme new mine --from nord # start your own theme, pre-filled with nord's colors
203
+ vigent --theme lazyvim # LazyVim's look, on your terminal's own (transparent) background
204
+ ```
205
+
206
+ Inside vigent, `:colorscheme` opens a picker with live preview. `:hi Group guifg=… guibg=…` changes any color on the spot, `:set transparent` lets your terminal's background (its blur, opacity or wallpaper) show through any theme, and `:theme save mine` keeps what's on screen as your own theme file. To keep settings, put them in `~/.config/vigent/config.toml` (`vigent config init` writes a starter file):
207
+
208
+ ```toml
209
+ theme = "tokyonight"
210
+
211
+ [ui]
212
+ sidebar_width = 34
213
+ icons = "unicode" # or "ascii"
214
+ transparent = true # the terminal's background, with any theme
215
+
216
+ [highlights] # applied on top of any theme
217
+ Normal = { bg = "NONE" }
218
+ CursorLine = "guibg=#2a2f3a"
219
+ ```
220
+
221
+ 25 built in, including `lazyvim` (LazyVim's look, transparent), the tokyonight, catppuccin and rose-pine families, `dracula`, `onedark`, `everforest`, `nightfox`, `gruvbox`, `kanagawa`, `nord`, `monokai-pro`, `ayu-mirage`, and GitHub and solarized in dark and light. The picker shows each one's colors:
222
+
223
+ ![the colorscheme picker: every theme with its colors, dark / light / transparent](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/colorscheme-swatches.png)
224
+
225
+ **`:settings`** (`<Space>s`) changes the theme, transparency, layout, engine, worktrees and any key in one place. Press `w` and it's saved to config.toml:
226
+
227
+ ![:settings](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/settings.png)
228
+
229
+ ![the lazyvim theme over a translucent terminal: sidebar, transcript and floats show the background through](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/lazyvim-transparent.png)
230
+
231
+ ![colorscheme picker with live preview](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/colorscheme-picker.png)
232
+
233
+ ![habamax imported from Neovim](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/imported-habamax.png)
234
+
235
+ ![nord with :hi and :set changes applied live](https://raw.githubusercontent.com/farming-labs/vigent/main/docs/screenshots/live-customization.png)
236
+
237
+ The full guide covers importing, theme files, palettes, every option and every highlight group: **[docs/themes.md](https://github.com/farming-labs/vigent/blob/main/docs/themes.md)**.
238
+
239
+ ## Layout
240
+
241
+ ```
242
+ src/
243
+ cli.ts entry point, flags, `theme` and `config` subcommands
244
+ app.ts the modal TUI (modes, keys, sidebar, transcript, statusline, cmdline)
245
+ picker.ts floating :colorscheme picker
246
+ theme.ts highlight-group engine: resolution, overrides, theme files
247
+ builtin-themes.ts built-in themes
248
+ palette.ts builds a full theme from a small palette
249
+ import.ts imports colorschemes from nvim / vim (headless) or .vim files
250
+ vimcolors.ts :highlight syntax and the static .vim colorscheme reader
251
+ color.ts hex math, xterm-256 colors, vim color names
252
+ config.ts config.toml, ui options and :set
253
+ session.ts Session model; PiSession (real engine) and DemoSession
254
+ engines.ts which engines are installed and signed in (:checkhealth)
255
+ cli-engines.ts Claude Code / Codex / OpenCode adapters and CliSession
256
+ worktree.ts a git worktree per agent: branch names, create, remove, diff
257
+ riftri.ts copy-on-write worktrees through riftri's JSON contract
258
+ demo.ts scripted demo sessions
259
+ test/ node:test suites (npm test)
260
+ ```
261
+
262
+ See [DESIGN.md](https://github.com/farming-labs/vigent/blob/main/DESIGN.md) for the full plan and milestones.
package/dist/agents.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Agents survive a restart.
3
+ *
4
+ * Each agent's name, model, engine and pi conversation (or the id of its
5
+ * Claude Code / Codex / OpenCode conversation) are saved inside git's own
6
+ * record of its worktree (.git/worktrees/<name>/vigent/), not in your files.
7
+ * When vigent starts it reopens every agent whose worktree is still there.
8
+ * Removing the worktree (git worktree remove, :kill!) deletes this record
9
+ * with it, so nothing is left behind.
10
+ */
11
+ import { execFileSync } from "node:child_process";
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+ import { listWorktrees } from "./worktree.js";
15
+ /** vigent's folder in git's per-worktree admin dir. */
16
+ export function agentStateDir(worktreePath) {
17
+ const gitDir = execFileSync("git", ["rev-parse", "--absolute-git-dir"], {
18
+ cwd: worktreePath,
19
+ encoding: "utf8",
20
+ stdio: ["ignore", "pipe", "pipe"],
21
+ }).trim();
22
+ return path.join(gitDir, "vigent");
23
+ }
24
+ /** Where pi keeps this agent's conversation. */
25
+ export function sessionDir(worktreePath) {
26
+ return path.join(agentStateDir(worktreePath), "sessions");
27
+ }
28
+ export function saveAgentMeta(worktreePath, meta) {
29
+ const dir = agentStateDir(worktreePath);
30
+ fs.mkdirSync(dir, { recursive: true });
31
+ fs.writeFileSync(path.join(dir, "agent.json"), `${JSON.stringify(meta, null, "\t")}\n`);
32
+ }
33
+ export function loadAgentMeta(worktreePath) {
34
+ try {
35
+ const raw = JSON.parse(fs.readFileSync(path.join(agentStateDir(worktreePath), "agent.json"), "utf8"));
36
+ return typeof raw?.name === "string" && typeof raw?.model === "string" ? raw : undefined;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ /** Mark an agent stopped (:kill) or running again (:attach). */
43
+ export function setDetached(worktreePath, detached) {
44
+ const meta = loadAgentMeta(worktreePath);
45
+ if (!meta)
46
+ return;
47
+ if (detached)
48
+ meta.detached = true;
49
+ else
50
+ delete meta.detached;
51
+ saveAgentMeta(worktreePath, meta);
52
+ }
53
+ /** Agents to reopen at startup: vigent worktrees that have a saved, non-detached agent. */
54
+ export function restorableAgents(root, config) {
55
+ const out = [];
56
+ for (const w of listWorktrees(root, config)) {
57
+ if (!w.branch || !fs.existsSync(w.path))
58
+ continue;
59
+ const meta = loadAgentMeta(w.path);
60
+ if (!meta || meta.detached)
61
+ continue;
62
+ out.push({ meta, worktree: { branch: w.branch, path: w.path, base: meta.base } });
63
+ }
64
+ return out.sort((a, b) => a.meta.createdAt.localeCompare(b.meta.createdAt));
65
+ }