@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 +21 -0
- package/README.md +261 -2
- package/dist/agents.js +65 -0
- package/dist/app.js +2487 -0
- package/dist/builtin-themes.js +352 -0
- package/dist/choices.js +73 -0
- package/dist/cli-engines.js +537 -0
- package/dist/cli.js +429 -0
- package/dist/clipboard.js +56 -0
- package/dist/color.js +126 -0
- package/dist/config-edit.js +61 -0
- package/dist/config.js +406 -0
- package/dist/demo.js +81 -0
- package/dist/diffview.js +94 -0
- package/dist/engines.js +195 -0
- package/dist/export.js +29 -0
- package/dist/float.js +182 -0
- package/dist/import.js +260 -0
- package/dist/keys.js +589 -0
- package/dist/markdown.js +80 -0
- package/dist/palette.js +83 -0
- package/dist/picker.js +301 -0
- package/dist/review.js +158 -0
- package/dist/riftri.js +284 -0
- package/dist/session.js +460 -0
- package/dist/settings.js +174 -0
- package/dist/suggest.js +31 -0
- package/dist/theme-new.js +49 -0
- package/dist/theme.js +487 -0
- package/dist/tree.js +115 -0
- package/dist/version.js +45 -0
- package/dist/vimcolors.js +206 -0
- package/dist/worktree.js +357 -0
- package/package.json +64 -4
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
|
-
#
|
|
1
|
+
# vigent
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+

|
|
6
|
+
|
|
7
|
+

|
|
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
|
+

|
|
33
|
+
|
|
34
|
+

|
|
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
|
+

|
|
72
|
+
|
|
73
|
+

|
|
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
|
+

|
|
88
|
+
|
|
89
|
+

|
|
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
|
+

|
|
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
|
+

|
|
128
|
+
|
|
129
|
+

|
|
130
|
+
|
|
131
|
+

|
|
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
|
+

|
|
156
|
+
|
|
157
|
+

|
|
158
|
+
|
|
159
|
+

|
|
160
|
+
|
|
161
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
228
|
+
|
|
229
|
+

|
|
230
|
+
|
|
231
|
+

|
|
232
|
+
|
|
233
|
+

|
|
234
|
+
|
|
235
|
+

|
|
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
|
+
}
|