thurview 0.15.0 → 0.16.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 CHANGED
@@ -1,82 +1,123 @@
1
1
  # thurview
2
2
 
3
- Guided, evidence-anchored reviews of agent-written code. A coding agent studies
4
- a branch, pull request or commit range and writes a short document in which
5
- every claim is anchored to an exact file and line range at a pinned commit. You
6
- read it in your browser, ask the agent questions, comment on the code, and
7
- approve or send it back.
3
+ **Your coding agent writes the review; you read it in the browser, anchored to
4
+ the code, and approve it or send it back.**
5
+
6
+ [![CI](https://github.com/Thurbeen/thurview/actions/workflows/ci.yml/badge.svg)](https://github.com/Thurbeen/thurview/actions/workflows/ci.yml)
7
+ [![npm](https://img.shields.io/npm/v/thurview)](https://www.npmjs.com/package/thurview)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ![thurview demo: the reader follows an anchor into the code, comments on a line
11
+ range in the diff, reads the agent's answer and requests changes](./media/thurview-demo.gif)
12
+
13
+ _The reader's half of it; the agent is working off camera._
14
+
15
+ - **Every claim is anchored to code.** The prose links to an exact file and
16
+ line range at a pinned commit, and the reader opens that code beside the
17
+ text — no hunting for what a sentence is about.
18
+ - **You read it in your browser, not in a comment thread.** An argument in the
19
+ order somebody chose to make it, with the code, the diff between the pinned
20
+ commits and the system around it one click away — instead of forty remarks
21
+ in the order they happened to be written.
22
+ - **You ask, and the agent answers in the document.** A question goes to the
23
+ agent — at once if one is listening, queued if not — and the answer lands
24
+ in the same thread; a comment waits for your decision. Then you approve the
25
+ change or send it back with the comments attached.
26
+ - **A code graph answers what the diff cannot.** Who calls the symbol that
27
+ moved, which tests reach it, where the change landed in the system and what
28
+ sits next to it. thurview builds the graph itself with tree-sitter from the
29
+ pinned commits, for TypeScript, JavaScript, Python, Go, Rust, Java and Elixir.
30
+ - **The evidence is checked, not taken on trust.** An anchor whose lines
31
+ do not exist at the pinned commit, a call stack frame asserting a call the
32
+ diff does not show, an interface annotation for a symbol the change never
33
+ moved, a trust boundary crossing that resolves to nothing — publishing
34
+ rejects each one rather than rendering it.
8
35
 
9
36
  ## Install
10
37
 
11
- Give your agent the skill, with the [skills](https://github.com/vercel-labs/skills)
12
- CLI - it works with Claude Code, Codex, Cursor, OpenCode and every agent that
13
- reads the Agent Skills format:
38
+ One command gives your agent every skill this repository ships, through the
39
+ [skills](https://github.com/vercel-labs/skills) CLI - it works with Claude
40
+ Code, Codex, Cursor, OpenCode and every agent that reads the Agent Skills
41
+ format:
14
42
 
15
43
  ```sh
16
44
  npx skills@latest add https://github.com/Thurbeen/thurview \
17
- --skill thurview --agent universal claude-code --global --yes
45
+ --skill '*' --agent universal claude-code --global --yes
18
46
  ```
19
47
 
20
- The skill reaches the `thurview` command through `npx`, so the requirements
21
- are Node 22 or later and git (`gh` for pull requests, `glab` for merge
22
- requests). Then, in any repository, ask your agent:
48
+ `--skill '*'` takes all four; quote the star so your shell does not expand it
49
+ against the current directory. The skills reach the `thurview` command through
50
+ `npx`, so the requirements are Node 22 or later and git (`gh` for pull
51
+ requests, `glab` for merge requests). Then, in any repository, ask your agent:
23
52
 
24
53
  ```text
25
54
  Use the thurview skill to review my current branch against up-to-date main
26
55
  and open it.
27
56
  ```
28
57
 
29
- ![thurview demo: the reader follows an anchor into the code, comments on a line
30
- range in the diff, reads the agent's answer and requests changes](./media/thurview-demo.gif)
31
-
32
- The clip is the reader's half, the agent working off camera: following an anchor
33
- from the prose into the code, opening a call stack frame, commenting on a line
34
- range of the diff, seeing on the map what the change added, reading in the
35
- threads panel the answer to a question already asked, and sending the review
36
- back with changes requested.
58
+ ## The four skills
59
+
60
+ Three kinds of document, and one companion that writes none:
61
+
62
+ - **`thurview`** - a change that is already written: a branch, a pull request,
63
+ a commit range. The document carries the diff, the commits and the interface
64
+ delta, and the reader approves it or sends it back.
65
+ - **`thurview-explain`** - a codebase, or one subsystem of it, at a single
66
+ pinned commit. No diff and nothing to approve: it surfaces the architecture
67
+ well enough for the reader to spot design problems, and states what it did
68
+ not examine.
69
+ - **`thurview-design`** - a change that is not written yet: a design, an
70
+ architecture proposal, an implementation plan. It anchors on the code as it
71
+ stands and declares what it would build as proposals, each attached to the
72
+ code it lands in today.
73
+ - **`thurview-fix`** - no browser and no reader. It reviews, fixes what it is
74
+ sure of behind the repository's own tests and lint, and reports the rest -
75
+ optionally as inline comments on the change request.
37
76
 
38
77
  <details>
39
- <summary><b>Other ways to install</b> - pin the skill to a release, install the
40
- command from npm, run from a checkout, session hooks, the thurview-explain,
41
- thurview-design and thurview-fix skills</summary>
78
+ <summary><b>Other ways to install</b> - one skill at a time, pin to a release,
79
+ install the command from npm, run from a checkout, session hooks, and coming
80
+ from an older install</summary>
42
81
 
43
82
  `--global` installs for your user, so one install covers every repository.
44
- `universal` puts the one real copy in `~/.agents/skills/thurview`, the directory
45
- no single agent owns, and every other agent you name gets a symlink to it, such
46
- as `~/.claude/skills/thurview` → `../../.agents/skills/thurview`, so an update
47
- lands everywhere at once. Swap `claude-code` for any agent the skills CLI
48
- supports, but keep `universal` and at least one more: with `--yes` and a single
49
- target, the CLI copies instead of linking.
50
-
51
- That form tracks this repository's default branch: `skills update` takes
52
- whatever `main` holds, which can be ahead of the released command. To pin the
53
- skill to a release instead, install it from the tag, which the skill lock
54
- records and later updates keep:
83
+ `universal` puts the one real copy of each skill under `~/.agents/skills/`,
84
+ the directory no single agent owns, and every other agent you name gets a
85
+ symlink to it - `~/.claude/skills/thurview` →
86
+ `../../.agents/skills/thurview`, and the same for the other three - so an
87
+ update lands everywhere at once. Swap `claude-code` for any agent the skills
88
+ CLI supports, but keep `universal` and at least one more: with `--yes` and a
89
+ single target, the CLI copies instead of linking.
90
+
91
+ `--skill` also takes names, one or several, when you do not want all four:
92
+ `--skill thurview`, or `--skill thurview thurview-fix`.
93
+
94
+ The untagged URL above tracks this repository's default branch:
95
+ `skills update` takes whatever `main` holds, which can be ahead of the
96
+ released command. To pin the skills to a release instead, install from the
97
+ tag, which the skill lock records and later updates keep:
55
98
 
56
99
  ```sh
57
- npx skills@latest add https://github.com/Thurbeen/thurview/tree/v0.10.0/skills/thurview \
58
- --agent universal claude-code --global --yes
100
+ npx skills@latest add https://github.com/Thurbeen/thurview/tree/v0.15.0 \
101
+ --skill '*' --agent universal claude-code --global --yes
59
102
  ```
60
103
 
61
104
  Releases tag without committing, so nothing moves the tag above: swap in the
62
105
  [latest release](https://github.com/Thurbeen/thurview/releases/latest).
63
106
 
64
- The npm package ships the same skill, so `thurview setup skill` links the copy
65
- that matches the command you have installed. Use that when you want the two to
66
- move together.
107
+ The npm package ships the same skills, so `thurview setup skill` links the
108
+ copies that match the command you have installed. Use that when you want the
109
+ two to move together.
67
110
 
68
- Install the command itself, rather than leaving the skill to reach it through
111
+ Install the command itself, rather than leaving the skills to reach it through
69
112
  `npx` on every run:
70
113
 
71
114
  ```sh
72
115
  npm install -g thurview # or: pnpm add -g thurview
73
116
  ```
74
117
 
75
- The review reasons over a code graph thurview builds itself from the pinned
76
- commits with tree-sitter, so nothing else needs installing. `thurview graph`
77
- answers which interfaces the change moved, what it reaches, who calls a
78
- symbol, what tests cover it and how files cluster, for TypeScript,
79
- JavaScript, Python, Go, Rust, Java and Elixir.
118
+ Nothing else needs installing: the code graph is built from the pinned commits
119
+ with tree-sitter. `thurview graph` answers which interfaces the change moved,
120
+ what it reaches, who calls a symbol, what tests cover it and how files cluster.
80
121
 
81
122
  To run from a checkout instead:
82
123
 
@@ -89,43 +130,27 @@ npm link # puts `thurview` on PATH
89
130
  Optional, for ambient context: `thurview setup hooks` installs a
90
131
  SessionStart hook for Claude Code, Codex and OpenCode, so every session opens
91
132
  with the reviews of its working directory. `thurview setup skill` links the
92
- skill from this checkout instead of the `skills` CLI copy; use one or the
133
+ skills from this checkout instead of the `skills` CLI copies; use one or the
93
134
  other.
94
135
 
95
- Also available: `thurview-explain` and `thurview-design`, which author the
96
- explainer and design kinds described [below](#how-it-works), and
97
- `thurview-fix`, the browserless companion skill described
98
- [below](#review-and-fix).
136
+ **Coming from an older install.** A skill name is an address, so nothing
137
+ renames or splits one in place - the command above adds what is missing, and
138
+ what is stale has to go.
99
139
 
100
- ```sh
101
- npx skills@latest add https://github.com/Thurbeen/thurview \
102
- --skill thurview-explain --agent universal claude-code --global --yes
103
- npx skills@latest add https://github.com/Thurbeen/thurview \
104
- --skill thurview-design --agent universal claude-code --global --yes
105
- npx skills@latest add https://github.com/Thurbeen/thurview \
106
- --skill thurview-fix --agent universal claude-code --global --yes
107
- ```
140
+ `thurview-explain` used to be a second kind inside the `thurview` skill, so an
141
+ install made before the split does not have it. Under the `skills` CLI,
142
+ updating `thurview` adds no second skill; run the install command above, which
143
+ takes all four. Under `thurview setup skill`, update the command first -
144
+ `thurview update`, or pull and rebuild the checkout you linked from - and run
145
+ `thurview setup skill` again: it links every skill the installed command
146
+ carries, so it picks the new one up on its own.
108
147
 
109
- `thurview-explain` was a second kind inside the `thurview` skill, so an install
110
- made before the split does not have it under that name. Which command adds it
111
- depends on the route you installed by, and mixing the two is the thing to
112
- avoid. Under the `skills` CLI a skill is installed by name and updating
113
- `thurview` adds no second one, so run the first command above. Under
114
- `thurview setup skill`, update the command first - `thurview update`, or pull
115
- and rebuild the checkout you linked from - and run `thurview setup skill`
116
- again: it links every skill the installed command carries, so it picks the new
117
- one up on its own.
118
-
119
- `thurview-fix` was called `review-fix`. A skill name is an address, so nothing
120
- updates the old one in place: an install made before the rename keeps answering
121
- to `/review-fix` from a copy that will never change again, and one made with
122
- `thurview setup skill` leaves a symlink pointing at a directory this repository
123
- no longer ships. Remove it and add the skill under its new name:
148
+ `thurview-fix` was called `review-fix`, and an install made before the rename
149
+ keeps answering to `/review-fix` from a copy that will never change again.
150
+ Remove it:
124
151
 
125
152
  ```sh
126
153
  npx skills@latest remove --global review-fix
127
- npx skills@latest add https://github.com/Thurbeen/thurview \
128
- --skill thurview-fix --agent universal claude-code --global --yes
129
154
  ```
130
155
 
131
156
  For a `thurview setup skill` install, delete the stale link -
@@ -141,20 +166,14 @@ and serves it in your browser: the walkthrough, live code peeks, the diff,
141
166
  commits, and a software map. You ask questions, leave anchored comments, and
142
167
  approve or request changes. The agent answers and republishes.
143
168
 
144
- It also explains a codebase. A **code explainer** is the same document over a
145
- different unit: one pinned commit instead of a range, so a reader can see the
146
- architecture well enough to spot design problems themselves. It has no diff and
147
- nothing to approve, it surfaces structure rather than grading it, and it states
148
- what it did not examine.
169
+ An explainer and a design run the same loop over a different unit. An
170
+ explainer pins one commit instead of a range, so it has no diff and nothing to
171
+ approve. A design pins the commit it argues from: its anchors land on the code
172
+ as it stands, never on code that does not exist yet, and what it would build
173
+ rides beside them as proposals.
149
174
 
150
- And it reads a plan. A **design** is the same document over a change that is
151
- not written yet: one pinned commit — the code it argues from — anchors on the
152
- code as it stands, and what it would build declared as proposals, each attached
153
- to the code it lands in today. An anchor never points at code that does not
154
- exist; the reader approves the design or sends it back.
155
-
156
- It does not review the code for you. It helps you understand it fast enough
157
- to review it yourself.
175
+ The document does not review the code for you. It helps you understand the
176
+ code fast enough to judge it yourself.
158
177
 
159
178
  ```mermaid
160
179
  flowchart LR
@@ -168,6 +187,16 @@ flowchart LR
168
187
 
169
188
  ## What the reader sees
170
189
 
190
+ The home page is a queue: every review, explainer and design, grouped by
191
+ repository and ordered by whose turn it is - a decision not yet posted to its
192
+ change request first, then documents waiting for your reading, then ones whose
193
+ change request moved past the pin, then ones waiting for the agent, then ones
194
+ not published yet. A row bound to a change request also says whether the pin is
195
+ still the head, what you decided and whether it reached the forge, and whether
196
+ CI is a real gate there, as of the last `forge` command and with that age on
197
+ screen. The browser never calls the forge; explainers and designs carry those
198
+ columns empty.
199
+
171
200
  Prose with every claim anchored to code, opened beside the text:
172
201
 
173
202
  ![The review document, with a call stack diff, a storage view and an anchored
package/dist/cli.js CHANGED
@@ -21,6 +21,7 @@ import { startServer } from "./server/server.js";
21
21
  import { parseFlags, helpFor, str, bool } from "./flags.js";
22
22
  import { forgeFor, repoOf, summariseCi, } from "./forge/index.js";
23
23
  import { parseSubmission, longComments, buildPass } from "./forge/submission.js";
24
+ import { recordForgeFacts, ciFacts } from "./queue.js";
24
25
  import { VERSION } from "./version.js";
25
26
  const execFileP = promisify(execFile);
26
27
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -756,6 +757,7 @@ const commands = {
756
757
  let base;
757
758
  let head;
758
759
  let title = str(p, "title") ?? "";
760
+ let pinned = null;
759
761
  const b = existing?.binding;
760
762
  const pr = str(p, "pr");
761
763
  if (pr || b?.kind === "pr") {
@@ -779,6 +781,7 @@ const commands = {
779
781
  base = await g.mergeBase(worktree, baseRef, head);
780
782
  binding = { kind: "pr", name: cr.number, url: cr.url, forge: forge.id };
781
783
  title ||= cr.title;
784
+ pinned = { repo, cr };
782
785
  }
783
786
  else if (str(p, "base") || str(p, "head") || b?.kind === "range") {
784
787
  const [bb, hh] = b?.kind === "range" && !str(p, "base") && !str(p, "head")
@@ -857,6 +860,8 @@ const commands = {
857
860
  await writeReview(review);
858
861
  }
859
862
  }
863
+ if (pinned)
864
+ await recordForgeFacts(review, pinned.repo, pinned.cr);
860
865
  const stat = await g.shortStat(worktree, base, head);
861
866
  const dir = reviewDir(review.id);
862
867
  return {
@@ -1648,6 +1653,7 @@ const commands = {
1648
1653
  const checks = await ctx.forge.checks(ctx.repo, ctx.cr);
1649
1654
  const baseline = await ctx.forge.baseline(ctx.repo, ctx.cr.baseBranch).catch(() => null);
1650
1655
  const ci = summariseCi(checks, baseline, ctx.cr.baseBranch);
1656
+ await recordForgeFacts(ctx.review, ctx.repo, ctx.cr, { ci: ciFacts(ci) });
1651
1657
  const shown = bool(p, "full") ? checks : checks.filter((c) => c.state !== "passed");
1652
1658
  const hidden = checks.length - shown.length;
1653
1659
  const help = [
@@ -1869,6 +1875,9 @@ const commands = {
1869
1875
  ],
1870
1876
  };
1871
1877
  const posted = await ctx.forge.submit(ctx.repo, ctx.cr, submission);
1878
+ await recordForgeFacts(ctx.review, ctx.repo, ctx.cr, {
1879
+ posted: { at: now(), verdict: posted.verdict, head: ctx.cr.head },
1880
+ });
1872
1881
  return {
1873
1882
  submitted: {
1874
1883
  forge: ctx.forge.id,