@rness/cli 0.11.0 → 0.13.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
@@ -20,7 +20,7 @@ Inside a workspace, every `rness` delegates to the copy pinned in
20
20
 
21
21
  ## Commands
22
22
 
23
- rness create [<org>] [--repos a,b] [--agent claude] [--pm npm|pnpm|yarn|bun] [--ssh|--https] [--skip-install] -y
23
+ rness create [<org>] [--provider github] [--repos a,b] [--agent claude] [--pm npm|pnpm|yarn|bun] [--ssh|--https] [--skip-install] -y
24
24
  rness create <name> --blank [--agent claude] [--pm npm|pnpm|yarn|bun] [--skip-install] -y
25
25
  rness add <repo> [--scopes apps/web,packages/ui] [--ssh|--https] -y
26
26
  rness sync [--all] [--scope <name>] [--check] [--pull] [--agent <name>] -y
@@ -29,6 +29,8 @@ Inside a workspace, every `rness` delegates to the copy pinned in
29
29
  rness logout
30
30
  rness context [--scope <name>] [--json]
31
31
  rness status [<tab>]
32
+ rness pulse create [-y]
33
+ rness pulse sync
32
34
  rness validate
33
35
  rness mcp
34
36
 
@@ -87,7 +89,7 @@ the files a given agent needs, for the agents the team declares in
87
89
  applies it once the repository has been trusted interactively. Verified
88
90
  with Claude Code 2.1.284 on 2026-09-29. Each clone also gets `.mcp.json`,
89
91
  which registers the MCP server; see "MCP server". The same settings file
90
- carries two hooks, and so does `.claude/settings.json` at the workspace
92
+ carries three hooks, and so does `.claude/settings.json` at the workspace
91
93
  root, which `sync` writes on every machine; see "Hooks". Both places also
92
94
  get a Claude Code plugin, `.claude/skills/rness/`, with one command:
93
95
  `/rness:status [tab]`, the tables of `rness status`. rness owns those
@@ -104,7 +106,7 @@ the files a given agent needs, for the agents the team declares in
104
106
  ### Hooks
105
107
 
106
108
  With `claude` in `agents`, each clone's `.claude/settings.json` and the
107
- workspace root's carry two Claude Code hooks, both run by the pinned copy:
109
+ workspace root's carry three Claude Code hooks, all run by the pinned copy:
108
110
 
109
111
  - **At session start** (`SessionStart`, every source): a line for the
110
112
  developer — `rness 0.10.0 · acme · scope web — 3 standards, 2 decisions`
@@ -116,7 +118,10 @@ workspace root's carry two Claude Code hooks, both run by the pinned copy:
116
118
  - **After an edit** (`PostToolUse` on `Edit|Write`): when the file is under
117
119
  `.rness/`, its front-matter problems — or those of `rness.json` — go back
118
120
  to the model, which fixes them in the same turn. Any other edit costs one
119
- path check. Stale blocks are left to `sync`.
121
+ path check. Stale blocks are left to `sync`. With a pulse declared, the
122
+ edit is then marked on the board, broken or not; see "Pulse".
123
+ - **At session end** (`SessionEnd`): with a pulse declared, clears the marks
124
+ this session set and syncs the board; see "Pulse". Without one, nothing.
120
125
 
121
126
  Each hook is one fixed `sh` line: it runs
122
127
  `<.rness>/node_modules/@rness/cli/dist/bin/rness.js hook <event>` when that
@@ -127,12 +132,190 @@ hand counts as missing, and the next `sync` adds a second one. The team's
127
132
  own hooks stay where they are.
128
133
 
129
134
  Claude Code runs hooks from a committed settings file without asking each
130
- developer, including in `claude -p`. These two read `.rness/` and nothing
131
- else: they write no file, run no install and reach no network. Review a
132
- change to them like code. Verified with Claude Code 2.1.284 on 2026-09-29:
133
- in a clone and at the root, the model received the context; after an edit
134
- that broke a status, it received the problem. The `sh` line is not verified
135
- on Windows.
135
+ developer, including in `claude -p`. These three read `.rness/`, write
136
+ nothing in the workspace and run no install. Only with a pulse declared do
137
+ they reach the network, through a detached `rness` process that uses your
138
+ login; when that process fails, it writes why to `pulse.json` in rness's
139
+ configuration directory (`~/.config/rness/` by default), which the next
140
+ session start reads, says once and deletes. See "Pulse". Review a change to
141
+ them like code. Verified with Claude Code 2.1.284 on 2026-09-29: in a clone
142
+ and at the root, the model received the context; after an edit that broke a
143
+ status, it received the problem. The `sh` line is not verified on Windows.
144
+
145
+ ### Pulse
146
+
147
+ rness pulse create # once per organization: the board, then a first sync
148
+ rness pulse sync # as often as wanted
149
+
150
+ A projection of rness's state on the organization's GitHub Projects: a
151
+ project named **Agent Pulse**, where the team sees — and reads — every
152
+ document of `.rness/`, and sees when an agent is at work on one. rness
153
+ writes the board; if it is not in rness, it does not belong there.
154
+
155
+ - **Items**: one per document of `rness status`, an issue of
156
+ `<org>/.rness` added to the project. GitHub shows it only to people who
157
+ can read `.rness`. Its title is the document's (`0016 — CLI: rness
158
+ status, …`) and its body the document (see **Body**). It carries the
159
+ label `rness`, made if missing; `-label:rness` leaves these issues out of
160
+ `.rness`'s Issues tab. An issue is open while its document exists. When
161
+ the document is gone, the issue is closed as not planned and its item
162
+ archived. Gone means gone for this clone's git: the path is in its
163
+ history and no longer in its working tree. A path it has never seen is
164
+ judged only on an issue this clone's sync opened — a document its agent
165
+ wrote, then renamed or deleted before any commit. The clone records the
166
+ issues it opens in its own git directory (`rness/opened`, per worktree,
167
+ never pushed), and forgets one as soon as its git has seen the path. Any
168
+ other — a teammate's new document, or yours from another clone, not
169
+ pulled yet — is left alone. An issue closed while its document exists —
170
+ by hand, or by a `fixes #12` in a `.rness` commit — is reopened at the
171
+ next sync. The document's status is its field, not the issue's state.
172
+ rness's items are those with a `Path`
173
+ whose content is an issue of `.rness`. A draft with a `Path` is rness's
174
+ from 0.12.0, and is converted into an issue of `.rness`. An issue of
175
+ `.rness` on the board, labelled `rness`, without `Path`, whose body's
176
+ first line is a document's first line, is one a stopped sync left: it
177
+ gets its `Path` and is brought up to date, and no second issue is made.
178
+ Anything else — an item without `Path`, an issue of another repository,
179
+ any other issue of `.rness` without `Path` — is the team's: never
180
+ edited, closed or archived. Two known limits. A clone behind the others
181
+ writes what its working tree holds — an older title, status or body —
182
+ until it pulls; the next up-to-date sync writes them back. A document
183
+ that lives only on a branch of `.rness` has its issue closed by a sync
184
+ from a branch without it, if git has seen the path or this clone opened
185
+ the issue; back on its branch, it gets a new issue (`.rness` is worked
186
+ on `main`, and branches of it are rare). The project is linked to
187
+ `.rness`, so Agent Pulse shows in its Projects tab. Comments are the
188
+ team's: a spec's discussion lives on its issue, and rness never writes
189
+ or deletes one. `pulse create` and `pulse sync` need Issues on
190
+ `.rness`. Without them they stop before
191
+ writing anything: `the pulse needs Issues on <org>/.rness: turn them on
192
+ in its Settings`.
193
+ - **Body**: the document, the same bytes for the same document. It starts
194
+ with a first line: the document's path and a link to the file. Then comes
195
+ the document without its front matter (the status is a field) and
196
+ without its first heading (the title). Links outside code are rewritten.
197
+ A link to another document becomes the URL of its issue, which opens in
198
+ the board's side panel. A link to any other file of `.rness` points to
199
+ it on GitHub at `main` (an image, raw). An absolute link, a link out of
200
+ `.rness`, or a link to no file stays as written. Outside code spans and
201
+ fenced blocks, `@name` is written `\@name`, and a bare `#12` gets a
202
+ zero-width space after `#`: a body notifies no one and points at no
203
+ wrong issue. A body holds at most 65,536 characters. A longer document
204
+ is cut at the last blank line that fits and ends with
205
+ `The rest: <link>`. A last line, invisible on GitHub,
206
+ `<!-- rness <digest> -->`, is how sync tells a body that changed.
207
+ - **Layout**: the fields `Status` (the union of the
208
+ collection fields' options: every contract status plus the ones found in
209
+ `.rness/`, in lifecycle order, at most 50), `Collection` (one option per tab of
210
+ `rness status`; not `Type`, which is GitHub's own issue-type field and
211
+ filter), `Agent` (`working`), `Session` (`claude · 1a2b3c4d`, plus the
212
+ agent type for a subagent) and `Path`. A board per directory, named as
213
+ `rness status` names its tab, filtered on its `Collection`, its columns
214
+ that collection's own steps: each collection has its own status field
215
+ (`ADR status`, `Specs status`, …; a directory without statuses is columned
216
+ by `Status`, which holds them all for the `All` table). `pulse sync`
217
+ remakes a board built by an earlier version (its URL changes once); a `Working` table, filtered on `Agent: working`. A first view named `View 1` (GitHub's default) becomes `All` when no `All` exists, on create and on sync, showing Title, Collection, Status and Session. Options are coloured (statuses by lifecycle: blue proposed, purple approved, yellow in progress, red blocked or rejected, yellow superseded, green done, gray abandoned; collections and `working` too); a sync recolours rness's options and leaves other options' colours alone. rness does
218
+ not set the project's visibility: the project gets GitHub's default for a
219
+ new organization project, which is private (`public: false`). Verified
220
+ with Claude Code 2.1.284 on 2026-09-29.
221
+ - **Login**: the pulse needs the `project` scope. `rness login` asks for it
222
+ when the workspace declares a pulse, or when `pulse create` needs it (in a
223
+ terminal it offers to log in; with `-y` or off a terminal it stops with
224
+ `the pulse needs a GitHub login: run rness login` or
225
+ `the pulse needs the project scope: run rness login`). A developer who
226
+ never uses the pulse grants nothing more than `repo read:org`. When GitHub
227
+ cannot be asked, the pulse says so (`cannot reach GitHub: …`, or GitHub's
228
+ own answer) instead of asking for a login. A classic `GITHUB_TOKEN` works
229
+ if it carries the scope; a fine-grained or GitHub App token reports no
230
+ scope, so the pulse refuses it whatever its permissions. An organization
231
+ that restricts OAuth apps must approve "Rness", as for its private
232
+ repositories.
233
+ - **`pulse create`** needs an `org` in `rness.json` (a blank workspace is
234
+ refused) and no pulse yet. It creates the project and at once writes
235
+ `"pulse": { "project": <number> }` (and the `provider`) into `rness.json`,
236
+ then adds the fields, options and views, and runs a first sync; its
237
+ `created` lines say what it added. If a step after the project fails, it
238
+ exits 1 with the pulse declared, and `rness pulse sync` completes the
239
+ layout. Commit `rness.json` in `.rness`. The number is that of the
240
+ project in the organization. A CLI older than 0.12.0 refuses the key, so
241
+ the pin moves first (`rness upgrade`). A workspace without a `provider`
242
+ whose repositories look like GitLab is refused before anything is
243
+ created: write `"provider": "github"` if the organization is on GitHub.
244
+ - **`pulse sync`** works in two passes. First every document gets its
245
+ issue — created, converted from a 0.12.0 draft, or reopened — with its
246
+ title, label and fields. Then the bodies whose digest differs are
247
+ written, since a body links to other documents' issues by number. It
248
+ also adds the option or the board that a new status or directory needs.
249
+ It says what it did to the issues, then to the items: `created 1 issue`,
250
+ `reopened 1 issue`, `synced 52 items: 1 created, 2 updated, 49
251
+ unchanged`.
252
+ - **One way**: rness reads the board and the issues only to find its items
253
+ and to tell a changed body. Anything changed by hand — a card moved, a
254
+ field, a title, a body, an issue closed — is written back at the next
255
+ sync, and never reaches `.rness/`. The board is not made read-only:
256
+ whoever runs an agent writes it with their own login, so they need Write
257
+ on the project. Read-only access for anyone else is set by hand, in the
258
+ project's "Manage access". The project's workflows are left alone.
259
+ GitHub turns on "Auto-add sub-issues to project" for a new project, so a
260
+ sub-issue added under a document's issue joins the board without `Path`,
261
+ as the team's item. A workflow someone turns on (`Item closed`, `Item
262
+ added to project`) may change a field of rness's items; the next sync
263
+ writes it back.
264
+ - **Hooks**, with `claude` in `agents` and a pulse declared: session start
265
+ marks the `In progress` plans of the session's scope `Agent: working`, with
266
+ the session; an edit of a document of `.rness/` marks it — one the agent has just written, with no item yet, gets its issue first
267
+ through a sync, and an edit of any other file of `.rness/` costs no sync;
268
+ the hooks' syncs run one at a time, an edit's skipped while another runs,
269
+ the session end's waiting its turn; session end clears
270
+ the marks of that session (its subagents' included) and syncs. Each one
271
+ starts a detached `rness` process and answers at once, so a slow network
272
+ never holds Claude Code; the session-end sync outlives Claude Code's exit.
273
+ Verified with Claude Code 2.1.284 on 2026-09-29: the detached process wrote
274
+ its file 3 s after `/exit`. It uses your login: with none, without the
275
+ scope, or when GitHub cannot be reached, nothing is sent, and the next
276
+ session start's banner says why, once:
277
+ `rness: pulse not updated — <reason>`, such as
278
+ `the pulse needs the project scope: run rness login` or
279
+ `cannot reach GitHub: …`. Two sessions marking one plan both write; the
280
+ last wins.
281
+ - **Rate**: rness sends one request at a time. A new document costs an
282
+ issue, its addition to the board, up to four fields, then its body. An
283
+ unchanged document costs nothing but its share of the listing, which
284
+ reads every item's body. On one of GitHub's rate limits rness waits as
285
+ long as GitHub says (`waiting 60 s — GitHub's rate limit`), 10 minutes
286
+ at most in all. Past that it stops, saying how many changes it did not
287
+ make, and the next sync makes them. Measured on `rness-dev` (54
288
+ documents, 2026-09-30): the migration from 0.12.0 took 169 s with no
289
+ wait; a sync with nothing to change, 4 s; the listing, one page of
290
+ 852 KB in about 1 s.
291
+ - **Not yet**: GitLab and Atlassian. `create` lists them, disabled; a
292
+ workspace whose `rness.json` names one is refused by `pulse`. `Waiting`
293
+ and `Review` statuses, several agents on one board and hooks for agents
294
+ other than Claude Code are not built.
295
+
296
+ ### Provider
297
+
298
+ `rness.json` records where the organization lives:
299
+ `"provider": "github"`, between `contract` and `org`. Every command talks to
300
+ it through one adapter; `git` still does what git does (clone, pull, push,
301
+ credentials), and the provider's API the rest (organizations, creating a
302
+ repository, the board).
303
+
304
+ - `rness create` asks `Where does your organization live?`: GitHub, and
305
+ GitLab and Atlassian (Bitbucket + Jira) shown disabled, coming later.
306
+ `--provider github` answers it off a terminal. A blank workspace asks
307
+ nothing. Joining an organization asks first as well, then takes the
308
+ provider its `rness.json` names.
309
+ - A workspace without the key is read from its first repository URL: a
310
+ GitLab host reads as `gitlab`, any other host — or no repository — as
311
+ `github`. Nothing to change: `pulse create` writes it, and refuses a
312
+ detected provider this version cannot talk to. The order of keys is
313
+ `contract`, `provider`, `org`, `agents`, `pulse`, `repos`, `scopes`.
314
+ - A provider _written_ in `rness.json` that this version cannot talk to
315
+ (`gitlab`) is refused by `validate`, `create`, `login` and `pulse`:
316
+ `provider "gitlab" is not supported by @rness/cli 0.13.0 (supported:
317
+ github)`. `add`, `sync` and the local commands never refuse. `null` is not a
318
+ value: an absent key means none.
136
319
 
137
320
  ### MCP server
138
321
 
@@ -184,7 +367,8 @@ anonymously.
184
367
  hours and is renewed on its own. `GITHUB_TOKEN`, then `GH_TOKEN`, win over
185
368
  it — CI needs no login.
186
369
  - rness asks for `repo` and `read:org`: GitHub has no read-only scope for
187
- private repositories. It only lists and clones.
370
+ private repositories. It only lists and clones. Where the workspace
371
+ declares a pulse it asks for `project` too; see "Pulse".
188
372
  - A new workspace has to reach GitHub before teammates can join it. Logged
189
373
  in, the `create` wizard offers to do it: it creates the private repository
190
374
  `<org>/.rness` and pushes the context with `git`. Declined, refused by
@@ -306,6 +490,76 @@ per repository, the files to commit there.
306
490
  Exit codes: 0 success, 1 failure, 2 usage — or a refusal without a TTY.
307
491
  `RNESS_DEBUG=1` adds stack traces; `RNESS_NO_DELEGATE=1` skips the delegation.
308
492
 
493
+ ## 0.13.0 — Agent Pulse: each document an issue of `.rness`, its content on the card
494
+
495
+ - Each item of Agent Pulse is an issue of `<org>/.rness`, labelled
496
+ `rness`, whose body is the document. A link to another document opens
497
+ its issue in the board's side panel. GitHub shows the items only to
498
+ people who can read `.rness`. See "Pulse".
499
+ - **Needs Issues on `.rness`**: without them `pulse create` and
500
+ `pulse sync` refuse, before writing anything.
501
+ - **Upgrading**: the first `pulse sync` with 0.13.0 — run by hand, or at
502
+ the end of a Claude Code session — does the migration. It converts every
503
+ draft that carries a `Path` into an issue of `.rness`: its id and fields
504
+ stay, so the boards do not change. It labels each issue, writes its
505
+ body, and links the project to `.rness`. GitHub notifies the people who
506
+ watch `.rness` of each new issue, once. Nothing is asked. Until every
507
+ developer's `.rness` is on 0.13.0, a session that ends on 0.12.0 syncs
508
+ as 0.12.0 did: it sees the issues as the team's and adds a draft per
509
+ document. The next 0.13.0 sync archives those drafts.
510
+ - The board is not made read-only, and its workflows are left alone. A
511
+ change made on the board is written back at the next sync. See "Pulse".
512
+ - When it did, `pulse sync` says `created 1 issue`,
513
+ `converted 52 drafts into issues of <org>/.rness` or `reopened 1 issue`.
514
+ It waits on GitHub's rate limits, 10 minutes at most in all.
515
+ - An edit of a document that has no item yet — a spec or a plan the agent
516
+ has just written — now gives it its issue, then marks it `working`.
517
+ Before, it was marked only once a sync had added it.
518
+ - A clone that has not pulled a teammate's new document — or the same
519
+ developer's, from another clone — leaves its item alone. 0.12.0 archived
520
+ it, and the next up-to-date sync made a second one. A document renamed
521
+ or deleted before any commit has its issue closed by the clone that
522
+ opened it, which records it in its git directory (`rness/opened`).
523
+ - The package's `Provider` interface changes:
524
+ - `checkIssues(org)` is new;
525
+ - `apply` returns the item and issue number of a create or a convert:
526
+ `Placed | null`;
527
+ - `BoardItem` gains a required `issue`: the item's issue of `.rness`
528
+ (number, state, label, body), or null;
529
+ - `Step` gains the kinds `convert`, `close` and `body`, and `update`
530
+ gains `reopen`;
531
+ - `items(board, { bodies: false })` lists the items without their
532
+ issues' bodies (`body: null`).
533
+
534
+ An implementation outside this package must adapt to each.
535
+
536
+ - Blocks are unchanged.
537
+ - Status colours: `Rejected` is red and `Superseded` yellow, in every
538
+ collection's field and in `Status`; a sync recolours them.
539
+
540
+ ## 0.12.0 — `rness pulse`; the provider
541
+
542
+ - `rness pulse create` and `rness pulse sync`: the Agent Pulse board on the
543
+ organization's GitHub Projects, a projection of `.rness/`. See "Pulse".
544
+ `rness login` asks for the `project` scope where the workspace declares a
545
+ pulse.
546
+ - `rness.json` gains two optional keys of contract 1: `provider` (`github`;
547
+ `gitlab` and `atlassian` are listed as not available) and
548
+ `pulse: { "project": <number> }`. A CLI older than 0.12.0 refuses them:
549
+ move the pin (`rness upgrade`) before either is written. `create` asks
550
+ `Where does your organization live?`, or takes `--provider github`. See
551
+ "Provider".
552
+ - With `claude` in `agents`, `sync` adds a third hook, `SessionEnd`
553
+ (`rness hook session-end`), to each clone's `.claude/settings.json` and to
554
+ the workspace root's. `rness upgrade` syncs, so it adds it; then commit
555
+ `.claude/settings.json` in each repository, as its next steps list. Until
556
+ then `sync --check` and `validate` report the hook missing. The two
557
+ existing hook lines do not change.
558
+ - With a pulse declared, the hooks mark and clear the plans an agent works
559
+ on, through a detached process. See "Hooks".
560
+ - One status field per collection (`ADR status`, `Specs status`, `Plans status`, …); each board's columns are that collection's steps and only them. `rness pulse sync` remakes a board built by an earlier version (its URL changes once) and renames a first view named `View 1` to `All` when no `All` exists.
561
+ - Blocks are unchanged.
562
+
309
563
  ## 0.11.0 — `rness status`; `/rness:status` in Claude Code
310
564
 
311
565
  - `rness status [tab]`: every decision, specification and plan — and any
package/dist/bin/rness.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  findDelegate,
7
7
  reportError,
8
8
  run
9
- } from "../chunk-LW4S5OLI.js";
9
+ } from "../chunk-4VTYSPZW.js";
10
10
 
11
11
  // src/bin/rness.ts
12
12
  import { pathToFileURL } from "url";