@xbghc/warden 0.7.0 → 0.9.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/README.md +74 -29
- package/dist/cli.js +332 -138
- package/dist/web/assets/index-BAWRMudD.js +198 -0
- package/dist/web/assets/index-DI521Osx.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/web/assets/index-Cr74Kv2S.css +0 -1
- package/dist/web/assets/index-DNRG_SPg.js +0 -198
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ with line comments you can copy back to the agent as a prompt.
|
|
|
6
6
|
- Runs as a single local process per repository (`127.0.0.1` only, no auth, no database).
|
|
7
7
|
- Diff sources: working tree, staged, working tree vs HEAD, a branch since it forked off its base (commits and
|
|
8
8
|
uncommitted work together), any commit, any two refs, and git worktrees.
|
|
9
|
+
- Worktrees are made and taken down from the page, one per agent branch, with the path ready to paste.
|
|
9
10
|
- Side-by-side **Unstaged** and **Staged** file lists: staged means reviewed. Stage from the UI by dragging
|
|
10
11
|
over the lines you have read (or a hunk, or a file), and unstage the same way from the Staged view.
|
|
11
12
|
- GitHub-style unified / side-by-side diff with syntax highlighting, collapsed file tree, lazy per-file loading, context expansion, virtual scrolling.
|
|
@@ -60,31 +61,36 @@ Several instances on the same repository can run at the same time.
|
|
|
60
61
|
| Worktree, everything since base | `worktree:<path>:base:<ref>` | same, run inside the worktree |
|
|
61
62
|
|
|
62
63
|
Refs accept anything git can resolve (`main`, `v1.2`, `HEAD~3`, a sha). `@` is `HEAD`.
|
|
63
|
-
Worktrees are discovered with `git worktree list` and
|
|
64
|
+
Worktrees are discovered with `git worktree list` (and made in the *Worktrees* tab, see below) and
|
|
65
|
+
share the review state of the main repository.
|
|
64
66
|
One whose directory is gone (git lists it as *prunable*) is not offered, and a remembered target
|
|
65
67
|
inside a removed worktree falls back to the working tree on the next load. The comments and viewed
|
|
66
68
|
flags kept under that worktree's key stay in the state file and come back if a worktree is created
|
|
67
69
|
at the same path again.
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
The sidebar is the navigation, and the only navigation: its three tabs — *变更*, *提交*, *Worktree* —
|
|
72
|
+
each fill the left column with the controls for what the middle is showing, and the top bar carries
|
|
73
|
+
none of it. There is no target switcher: the *变更* tab's two blocks *are* the working tree, a commit
|
|
74
|
+
is picked from the *提交* list, and the *审阅整条分支* and *对比两个 ref* forms below that tab's filters
|
|
75
|
+
open the other two. The sidebar names whatever is under review in its block header and puts a
|
|
72
76
|
*返回工作区* link above it.
|
|
73
77
|
|
|
74
78
|
`working`, `staged` and `all` are the three **local views** of one worktree. While any of them is
|
|
75
|
-
selected the sidebar shows two blocks —
|
|
79
|
+
selected the sidebar shows two blocks — 未暂存 (`working`) and 已暂存 (`staged`) — instead of a
|
|
76
80
|
single tree, and clicking a file switches to the view it belongs to. A file that is only partly
|
|
77
|
-
staged appears in both.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
staged appears in both. Above them sits the one figure in the app: how many of the files under review
|
|
82
|
+
are fully staged, which is what "staged means reviewed" amounts to. Switching between the three keeps
|
|
83
|
+
your comments, the draft you are typing and the current selection; only the diff is reloaded. `all`
|
|
84
|
+
has no control of its own (a saved `lastTarget` can still restore it; *审阅整条分支* against `@` shows
|
|
85
|
+
the same diff). Commit, range and `base` targets keep the single tree, and their figure counts 已读
|
|
86
|
+
instead, since nothing can be staged there.
|
|
81
87
|
|
|
82
88
|
`base:<ref>` is for a branch a coding agent has been working on, committing as it goes: one tree with
|
|
83
89
|
everything since the branch forked off `<ref>` — the commits plus whatever is still uncommitted or
|
|
84
90
|
untracked. The diff runs against the merge base, so commits that landed on `<ref>` after the fork are
|
|
85
|
-
not listed as reverted (which is what a plain `git diff <ref>` would do). The
|
|
86
|
-
|
|
87
|
-
review, otherwise `main` or `master` — and takes any ref.
|
|
91
|
+
not listed as reverted (which is what a plain `git diff <ref>` would do). The *相对于* field under
|
|
92
|
+
*审阅整条分支* suggests the base for you — the main worktree's branch when a sibling worktree is under
|
|
93
|
+
review, otherwise `main` or `master` — and takes any ref. Under it the form names the fork point (the
|
|
88
94
|
merge base with that ref, `GET /api/fork-point`) with how many commits the branch is ahead of it and how
|
|
89
95
|
many landed on the base since, and the list marks that commit with a *分叉自* chip and a rule above it:
|
|
90
96
|
what sits above is the branch's own work. Unlike the three local views, `base` keeps its own
|
|
@@ -92,6 +98,29 @@ pool of comments and a commit never deletes them: the round under review is not
|
|
|
92
98
|
commits, so a comment whose lines changed stays *orphaned*, snippet and all, until you have checked
|
|
93
99
|
the fix and delete or re-attach it.
|
|
94
100
|
|
|
101
|
+
## Worktrees
|
|
102
|
+
|
|
103
|
+
Agents do their best work each in a worktree of its own, and the sidebar's *Worktree* tab is
|
|
104
|
+
where those are made and taken down without a trip to the terminal:
|
|
105
|
+
|
|
106
|
+
- *新建 worktree*, the form in the sidebar, takes a branch and a base. A name that is not a branch yet becomes one from the
|
|
107
|
+
base (`main` or `master` unless you say otherwise; any ref goes) — `git worktree add -b <branch>
|
|
108
|
+
<path> <base>`; an existing branch is checked out as it is, unless another worktree already has it.
|
|
109
|
+
The path is suggested as a sibling of the main worktree named `<repo>-<branch>` (slashes become
|
|
110
|
+
dashes) and can be edited, within limits: it has to sit under the main worktree's parent directory,
|
|
111
|
+
outside every existing worktree, and be new or an empty directory.
|
|
112
|
+
- Each row names the checkout, its branch, and whether it is clean or how many paths `git status`
|
|
113
|
+
reports. *查看* switches the review to it (the kind of target carries over, as with the selector
|
|
114
|
+
in the top bar), *复制路径* is for the agent's prompt.
|
|
115
|
+
- *删除* always asks first, in the row it would remove, and runs `git worktree remove` without
|
|
116
|
+
`--force`: a worktree with uncommitted changes is not removed until you confirm again, since those
|
|
117
|
+
changes go with it, and whatever else git refuses without `--force` is put to you the same way.
|
|
118
|
+
*一并删除分支* is offered in that same confirmation, ticked by default, and takes the branch by
|
|
119
|
+
`git branch -d`: one that is not merged is kept and the toast says why. The comments and viewed
|
|
120
|
+
flags kept under the worktree's key stay in the state file, as before.
|
|
121
|
+
- A worktree whose directory was deleted behind git's back is listed struck through; *清理* drops that
|
|
122
|
+
one entry (`git worktree remove` handles it; nothing is pruned wholesale).
|
|
123
|
+
|
|
95
124
|
## Keyboard
|
|
96
125
|
|
|
97
126
|
| Key | Action |
|
|
@@ -101,7 +130,7 @@ the fix and delete or re-attach it.
|
|
|
101
130
|
| `n` / `p` | Next / previous hunk |
|
|
102
131
|
| `s` | Stage (Unstaged view) or unstage (Staged view) the picked lines |
|
|
103
132
|
| `Ctrl+Enter` | Save the comment being edited |
|
|
104
|
-
| `Esc` | Drop the picked lines / cancel editing /
|
|
133
|
+
| `Esc` | Drop the picked lines / cancel editing / cancel re-attach mode / shut the rail / back to 变更 |
|
|
105
134
|
|
|
106
135
|
## Staging
|
|
107
136
|
|
|
@@ -132,12 +161,18 @@ Limits, each reported as a plain error rather than a half-applied patch:
|
|
|
132
161
|
|
|
133
162
|
## Comments and export
|
|
134
163
|
|
|
135
|
-
Press the `+` that appears next to a line (drag to cover several lines) and write Markdown.
|
|
164
|
+
Press the `+` that appears next to a line (drag to cover several lines) and write Markdown. What you
|
|
165
|
+
write lands in the right-hand rail, which is shut until it has something to hold: it costs 360px of
|
|
166
|
+
the code column, which is most of it once the diff is side by side on a laptop. Writing a comment,
|
|
167
|
+
focusing one or re-attaching one opens it; so does the *评论* switch at the right of the top bar,
|
|
168
|
+
which carries the count and turns violet while any comment is still waiting to go back to the agent.
|
|
169
|
+
`Esc` shuts it again.
|
|
170
|
+
|
|
136
171
|
Each comment belongs to the `old` or `new` side of the diff and has a status:
|
|
137
172
|
|
|
138
|
-
- `active` — not yet exported
|
|
139
|
-
- `exported` — copied at least once (excluded from "copy all" unless *含已导出* is checked)
|
|
140
|
-
- `orphaned` — the code it referred to no longer exists in the current diff
|
|
173
|
+
- `active` (待导出) — not yet exported
|
|
174
|
+
- `exported` (已导出) — copied at least once (excluded from "copy all" unless *含已导出* is checked)
|
|
175
|
+
- `orphaned` (已失联) — the code it referred to no longer exists in the current diff
|
|
141
176
|
|
|
142
177
|
"复制评论" copies every active comment of the current target; each comment also has a "复制此条" button.
|
|
143
178
|
The clipboard format is fixed so an agent can read it directly:
|
|
@@ -187,7 +222,7 @@ them deletes on a moved HEAD.
|
|
|
187
222
|
Comment markers in the diff belong to one view; the rail's *全部* tab lists the whole pool and
|
|
188
223
|
*此文件* lists every comment on the open file regardless of which view it currently sits in.
|
|
189
224
|
|
|
190
|
-
|
|
225
|
+
*已读* is per view, so a half-staged file can be marked read on one side and not the other. It is
|
|
191
226
|
bound to a hash of the file's diff; when the diff changes the flag is dropped and the file is marked
|
|
192
227
|
*已变化*.
|
|
193
228
|
|
|
@@ -205,9 +240,9 @@ refreshes by hand.
|
|
|
205
240
|
|
|
206
241
|
Notes attached to a branch rather than to a line of code, for the things you notice while reviewing
|
|
207
242
|
that do not belong in a comment. They are not deleted when you commit. They sit in the right-hand
|
|
208
|
-
rail next to the comments, under the
|
|
243
|
+
rail next to the comments, under the *待办* tab, as a task list that works the way Google Tasks does:
|
|
209
244
|
|
|
210
|
-
-
|
|
245
|
+
- *添加待办* opens an empty row at the top; type the title and press Enter, and the next row opens
|
|
211
246
|
right under it. Esc or Backspace on an empty row drops it.
|
|
212
247
|
- Titles and details are edited where they are: click into the text. Enter at the end of a title
|
|
213
248
|
starts the next todo under it; Backspace on an emptied title deletes the todo. The details field
|
|
@@ -220,10 +255,10 @@ rail next to the comments, under the *Todo* tab, as a task list that works the w
|
|
|
220
255
|
- The picker at the top is the list selector: the current branch, any other branch that has todos,
|
|
221
256
|
or all of them with the branch on each row. A new todo goes to the branch being shown.
|
|
222
257
|
|
|
223
|
-
Issues (the
|
|
258
|
+
Issues (the rail's third tab) are the same list: closing an issue is ticking it, a
|
|
224
259
|
*已关闭* section holds the closed ones, and the comments linked to an issue sit under its open row
|
|
225
260
|
like subtasks, each with *跳转* and *解除关联*. Tick comments in the rail and the *创建 Issue* button
|
|
226
|
-
|
|
261
|
+
switches to that tab with the add row ready and the comments attached on creation; for an existing
|
|
227
262
|
issue, open its row and use *关联选中的评论*.
|
|
228
263
|
|
|
229
264
|
Each row has its own *复制* button, and there is deliberately no "copy all": a todo is one task to
|
|
@@ -280,18 +315,24 @@ move into the matching scope the first time the file is read.
|
|
|
280
315
|
Single user, local only. The server binds to `127.0.0.1` and executes git only through
|
|
281
316
|
`execFile('git', [...])`. Reads go through an argument whitelist (`rev-parse`, `diff`, `show`, `log`,
|
|
282
317
|
`worktree list`, `ls-files`, `status`, `merge-base`, `rev-list`) that refuses option-looking refs and any write-capable
|
|
283
|
-
flag; requests that would need anything else get HTTP 400. The
|
|
284
|
-
|
|
285
|
-
from the diff it just produced — the patch is never
|
|
286
|
-
and they are checked against the diff's hash
|
|
287
|
-
|
|
318
|
+
flag; requests that would need anything else get HTTP 400. The writes are few and each composes its
|
|
319
|
+
own arguments. `POST /api/targets/:key/stage` runs `git apply --cached` (with `--reverse` for the
|
|
320
|
+
Staged view) on a patch the server itself builds from the diff it just produced — the patch is never
|
|
321
|
+
taken from the request, only the line indices are, and they are checked against the diff's hash
|
|
322
|
+
first. `POST /api/worktrees` and `POST /api/worktrees/remove` run `git worktree add`, `git worktree
|
|
323
|
+
remove` and `git branch -d` with a branch name git has validated, a ref that resolves, and a
|
|
324
|
+
path confined to the main worktree's parent directory. Nothing writes to the working tree or HEAD of
|
|
325
|
+
an existing checkout; refs change only when a worktree is made (its new branch) or removed (its merged
|
|
326
|
+
branch, on request), and the review state lives outside the repository. A mutating request the
|
|
327
|
+
browser labels as coming from another site (`Sec-Fetch-Site: cross-site`) is refused with 403, so a
|
|
328
|
+
page from elsewhere cannot drive the server through the browser it is open in.
|
|
288
329
|
|
|
289
330
|
## Development
|
|
290
331
|
|
|
291
332
|
```sh
|
|
292
333
|
pnpm install
|
|
293
334
|
pnpm dev # API server on :4100 (tsx watch) + Vite dev server on :5173 with /api proxied
|
|
294
|
-
pnpm test # vitest: diff parser, staging patches, anchoring, comment scopes, watcher, todos, export, state, HTTP API
|
|
335
|
+
pnpm test # vitest: diff parser, staging patches, anchoring, comment scopes, watcher, todos, worktrees, export, state, HTTP API
|
|
295
336
|
pnpm typecheck
|
|
296
337
|
pnpm build # dist/web (Vite) + dist/cli.js (tsup, zero runtime dependencies)
|
|
297
338
|
node dist/cli.js path/to/repo
|
|
@@ -302,7 +343,7 @@ Layout:
|
|
|
302
343
|
```
|
|
303
344
|
bin/cli.ts argument parsing, start server, open browser
|
|
304
345
|
packages/shared types + target key / comment scope helpers (bundled into both sides)
|
|
305
|
-
packages/server Hono API, git wrapper, diff parser, staging patches, targets, anchoring, watcher, state, export, nvim
|
|
346
|
+
packages/server Hono API, git wrapper, diff parser, staging patches, targets, worktrees, anchoring, watcher, state, export, nvim
|
|
306
347
|
packages/web React + Vite UI (shiki highlighting, @tanstack/react-virtual, react-markdown)
|
|
307
348
|
test/ API integration tests on a generated git repository
|
|
308
349
|
```
|
|
@@ -321,3 +362,7 @@ to `main` and every pull request.
|
|
|
321
362
|
## License
|
|
322
363
|
|
|
323
364
|
MIT
|
|
365
|
+
|
|
366
|
+
## Storybook
|
|
367
|
+
|
|
368
|
+
Run `pnpm storybook` for isolated review and layout components at http://127.0.0.1:6006. Stories use in-memory fixtures and include interaction checks. Run `pnpm build:storybook` to build the static preview.
|