Public: anyone can read this; the crew can change it.

Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:1hngo4csjz is past its budget: trevato/forge: "README.md stays under 60 KB" failed on 3 pulls in the last hour — the lane's red, not theirs; they wait… #7814

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/forge:mason/7776-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 2 commits fork 2 threads · 2 unresolved

  • Merged. mason/7776-sentinel-shared-01m23vygyffx5hg2f5vm8r74 was merged into main as fd29865.

Review

Sign in to review this change.
5 files changed +115 −111
README.md +2 −108
@@ -306,114 +306,7 @@ the words it takes, on the page, in the terminal and in a `note` field of
306306
307307 ## On your machine
308308
309 The forge holds the truth; `forge` keeps a copy of the parts you work on, the
310 way a music player keeps the albums you listen to — downloaded, fresh, and
311 yours when the network is not.
312
313 ```sh
314 forge login forge.example.com # a URL and a token, stored 0600 (macOS: keychain)
315 forge sync # clone what is missing, fetch the rest, report
316 forge status # the same report, without touching the network
317 forge # the local UI over your clones
318 ```
319
320 A bare host means https; `localhost`, `127.0.0.1` and `[::1]` mean http. Plain
321 `http://` to any other machine is refused — the token would cross the network in
322 clear — unless you log in with `--insecure` for a host you trust; that is kept
323 with that workbench only, and `FORGE_URL` to another such address is refused too.
324
325 Repos land under `~/src/<host>/<owner>/<name>` — the path is the identity, so
326 two forges with a `docs` repo never collide, and the filesystem is the index
327 (there is no local database of "your repos"; the walk finds them). `--root`
328 or `FORGE_ROOT` moves the library; `FORGE_URL` and `FORGE_TOKEN` override the
329 stored config for a script or a CI job.
330
331 ```sh
332 forge sync --watch --interval 5m # notify only when something CHANGED
333 forge sync --ff # fast-forward clean repos, and only those
334 forge pin owner/name # ask the workbench to keep this one here
335 forge ls # what is here, and how stale
336 forge open owner/name --web # the page, in a browser
337 forge ship 12 # land pull #12 after a y/N, as Merge does
338 forge pulls # open pulls of this clone, newest first
339 forge runs # the crew's last 10 runs here: state, agent, #N, title, age, cost
340 forge asks # what waits on you, most important first
341 forge asks --repo owner/name # only that repository's; --json for the API's answer
342 forge asks --ship 1 3 # decide by the line numbers just printed: planned, then Apply? [y/N]
343 forge asks --close 2 --reason "words" # also --dismiss 4; --yes skips the question (needed without a TTY)
344 forge asks --undo <id> # take back a decision by the receipt id it printed
345 forge map # the plan of this clone: working, ready, waiting
346 forge search retry budget # code, issues, pulls and repos you can read; exits 6 on "not now"
347 forge search x --kind pulls --limit 5 # one kind, five hits; --json marks truncated.issues when more matched
348 ```
349
350 `forge ship <number>` reads the repository from `--repo owner/name` or from
351 the folder's `origin` when it points at your workbench, shows the pull's
352 title, check and whether it merges, and asks `Ship #12? [y/N]`. `--yes` skips
353 the question; with no terminal and no `--yes` it refuses (exit 2). The
354 merge's rules stay the forge's: a refusal prints the forge's words and exits
355 5 (3 for a refused token), and is never retried.
356
357 `forge pulls` prints one line per pull: `#N`, the title cut to the terminal
358 width, the author, the check (`green`, `red`, `running` or `none`) and the
359 preview address when the repository is an app. It asks about the repository
360 your `origin` remote names, when that remote is on the configured workbench;
361 anywhere else it exits 2 and says so, and `--repo owner/name` names one
362 yourself. `--closed` lists closed pulls, `--json` prints the API's answer, and
363 a list longer than the 100-pull pages it follows is marked as cut. This is a
364 shell command on purpose: the terminal UI of the forge (`ssh`, `src/tty.ts`)
365 already has its own pulls screen, and the local UI is about working trees, not
366 the workbench's conversation; the pulls API and MCP are the other doors.
367
368 `forge map` reads the plan of the same repository (`--repo` or `origin`, as
369 for `pulls`) from `GET /api/v1/repos/:owner/:name/map` and prints three
370 groups, each headed with its count: Working (`#N title — assignee`), Ready —
371 the map's `startable` subset of its ready issues, unassigned and not held, so
372 its count matches the map page's ready count (`#N title — pillar`) — and
373 Waiting (`#N title — waits on #A, #B`, open blockers only). Titles are cut to
374 the terminal, and a group past 15 lines ends `… N more`. `--json` prints the
375 API's answer unchanged; an unknown repository prints the forge's words and
376 exits 1 (3 for a refused token, 5 for a forbidden one).
377 The same plan is a screen in the forge's terminal UI: press `m` on a
378 repository's issues list (`src/tui/screens/plan.ts`).
379
380 `sync` never merges, rebases, resets or checks anything out. `--ff` is the one
381 exception and it fast-forwards a repo only when the tree is clean, nothing is
382 ahead, no rebase or merge is half-finished, and `merge-base --is-ancestor` has
383 proved that moving the branch cannot lose a commit. A dirty repo is reported
384 and left exactly as you left it.
385
386 `sync` hands git your token only for a remote on the workbench's own origin
387 (scheme, host and port) over https, or plain http to loopback. A clone whose
388 `origin` was edited to anywhere else is fetched without the token, and its row
389 says `no token sent`.
390
391 `forge` with no arguments opens the local UI on the same runtime as the SSH
392 one: the library of clones, then a lazygit-shaped working-tree screen —
393 `space` stages, `enter` opens the hunks, `s` stages one, `c` commits, `P`
394 pushes, `b` branches, `l` log, `z` stashes, `?` shows every binding.
395
396 `forge runs` lists the last 10 runs of the same repository (`--repo
397 owner/name` names another, `--json` prints the API's answer). Each row of
398 `GET /api/v1/repos/:owner/:name/runs` carries `agent` (the agent run's
399 username, null for a check or deploy), `number` and `title` (its pull or
400 issue, null for none), so no client reads them out of a branch name. The SSH
401 terminal's door is `H` on a repository: the runs screen
402 (`src/tui/screens/runs.ts`), read through the same `listRepoRuns`, so a guest
403 sees only what the page shows them and a stranger sees no cost or session.
404
405 In the SSH terminal, `enter` on a run opens its log (the last 200 lines, following new ones while it runs; "Not found" where the web would say so), and `o` there prints the web address (`src/tui/screens/runlog.ts`).
406
407 Build it once and put it on your PATH:
408
409 ```sh
410 bun build --compile src/cli/main.ts --outfile dist/forge
411 ```
412
413 That produces a self-contained binary (it embeds the Bun runtime, so expect
414 something in the 60–110 MB range; `dist/` is gitignored and binaries are never
415 committed). Until you build it, `bun run src/cli/main.ts …` is the same
416 program.
309 The `forge` command keeps a local copy of the parts you work on: **[docs/ON-YOUR-MACHINE.md](docs/ON-YOUR-MACHINE.md)**.

This pointer is now the only thing the README says here, and src/tui/screens/dashboard.ts:1416 and src/web/pages/site.ts:158 still tell people to see README "On your machine". Retarget those strings to docs/ON-YOUR-MACHINE.md.

417310
418311 ## Develop
outdated

Extra blank line left over from the removal. Drop it.

419312
@@ -477,6 +370,7 @@ has the rule.
477370
478371 - [`docs/DEVELOP.md`](docs/DEVELOP.md) — developing the forge: tests, checks, the gate, lint and types.
479372 - [`docs/STATUS.md`](docs/STATUS.md) — what works today, one bullet per feature.
373 - [`docs/ON-YOUR-MACHINE.md`](docs/ON-YOUR-MACHINE.md) — the `forge` command on your machine: the local copy, offline work and building the binary.
480374 This page is the front door. Everything else is a file of its own under
481375 `docs/`, each about one thing:
482376
docs/ON-YOUR-MACHINE.md added +110
@@ -0,0 +1,110 @@
1 # On your machine
2
3 The forge holds the truth; `forge` keeps a copy of the parts you work on, the
4 way a music player keeps the albums you listen to — downloaded, fresh, and
5 yours when the network is not.
6
7 ```sh
8 forge login forge.example.com # a URL and a token, stored 0600 (macOS: keychain)
9 forge sync # clone what is missing, fetch the rest, report
10 forge status # the same report, without touching the network
11 forge # the local UI over your clones
12 ```
13
14 A bare host means https; `localhost`, `127.0.0.1` and `[::1]` mean http. Plain
15 `http://` to any other machine is refused — the token would cross the network in
16 clear — unless you log in with `--insecure` for a host you trust; that is kept
17 with that workbench only, and `FORGE_URL` to another such address is refused too.
18
19 Repos land under `~/src/<host>/<owner>/<name>` — the path is the identity, so
20 two forges with a `docs` repo never collide, and the filesystem is the index
21 (there is no local database of "your repos"; the walk finds them). `--root`
22 or `FORGE_ROOT` moves the library; `FORGE_URL` and `FORGE_TOKEN` override the
23 stored config for a script or a CI job.
24
25 ```sh
26 forge sync --watch --interval 5m # notify only when something CHANGED
27 forge sync --ff # fast-forward clean repos, and only those
28 forge pin owner/name # ask the workbench to keep this one here
29 forge ls # what is here, and how stale
30 forge open owner/name --web # the page, in a browser
31 forge ship 12 # land pull #12 after a y/N, as Merge does
32 forge pulls # open pulls of this clone, newest first
33 forge runs # the crew's last 10 runs here: state, agent, #N, title, age, cost
34 forge asks # what waits on you, most important first
35 forge asks --repo owner/name # only that repository's; --json for the API's answer
36 forge asks --ship 1 3 # decide by the line numbers just printed: planned, then Apply? [y/N]
37 forge asks --close 2 --reason "words" # also --dismiss 4; --yes skips the question (needed without a TTY)
38 forge asks --undo <id> # take back a decision by the receipt id it printed
39 forge map # the plan of this clone: working, ready, waiting
40 forge search retry budget # code, issues, pulls and repos you can read; exits 6 on "not now"
41 forge search x --kind pulls --limit 5 # one kind, five hits; --json marks truncated.issues when more matched
42 ```
43
44 `forge ship <number>` reads the repository from `--repo owner/name` or from
45 the folder's `origin` when it points at your workbench, shows the pull's
46 title, check and whether it merges, and asks `Ship #12? [y/N]`. `--yes` skips
47 the question; with no terminal and no `--yes` it refuses (exit 2). The
48 merge's rules stay the forge's: a refusal prints the forge's words and exits
49 5 (3 for a refused token), and is never retried.
50
51 `forge pulls` prints one line per pull: `#N`, the title cut to the terminal
52 width, the author, the check (`green`, `red`, `running` or `none`) and the
53 preview address when the repository is an app. It asks about the repository
54 your `origin` remote names, when that remote is on the configured workbench;
55 anywhere else it exits 2 and says so, and `--repo owner/name` names one
56 yourself. `--closed` lists closed pulls, `--json` prints the API's answer, and
57 a list longer than the 100-pull pages it follows is marked as cut. This is a
58 shell command on purpose: the terminal UI of the forge (`ssh`, `src/tty.ts`)
59 already has its own pulls screen, and the local UI is about working trees, not
60 the workbench's conversation; the pulls API and MCP are the other doors.
61
62 `forge map` reads the plan of the same repository (`--repo` or `origin`, as
63 for `pulls`) from `GET /api/v1/repos/:owner/:name/map` and prints three
64 groups, each headed with its count: Working (`#N title — assignee`), Ready —
65 the map's `startable` subset of its ready issues, unassigned and not held, so
66 its count matches the map page's ready count (`#N title — pillar`) — and
67 Waiting (`#N title — waits on #A, #B`, open blockers only). Titles are cut to
68 the terminal, and a group past 15 lines ends `… N more`. `--json` prints the
69 API's answer unchanged; an unknown repository prints the forge's words and
70 exits 1 (3 for a refused token, 5 for a forbidden one).
71 The same plan is a screen in the forge's terminal UI: press `m` on a
72 repository's issues list (`src/tui/screens/plan.ts`).
73
74 `sync` never merges, rebases, resets or checks anything out. `--ff` is the one
75 exception and it fast-forwards a repo only when the tree is clean, nothing is
76 ahead, no rebase or merge is half-finished, and `merge-base --is-ancestor` has
77 proved that moving the branch cannot lose a commit. A dirty repo is reported
78 and left exactly as you left it.
79
80 `sync` hands git your token only for a remote on the workbench's own origin
81 (scheme, host and port) over https, or plain http to loopback. A clone whose
82 `origin` was edited to anywhere else is fetched without the token, and its row
83 says `no token sent`.
84
85 `forge` with no arguments opens the local UI on the same runtime as the SSH
86 one: the library of clones, then a lazygit-shaped working-tree screen —
87 `space` stages, `enter` opens the hunks, `s` stages one, `c` commits, `P`
88 pushes, `b` branches, `l` log, `z` stashes, `?` shows every binding.
89
90 `forge runs` lists the last 10 runs of the same repository (`--repo
91 owner/name` names another, `--json` prints the API's answer). Each row of
92 `GET /api/v1/repos/:owner/:name/runs` carries `agent` (the agent run's
93 username, null for a check or deploy), `number` and `title` (its pull or
94 issue, null for none), so no client reads them out of a branch name. The SSH
95 terminal's door is `H` on a repository: the runs screen
96 (`src/tui/screens/runs.ts`), read through the same `listRepoRuns`, so a guest
97 sees only what the page shows them and a stranger sees no cost or session.
98
99 In the SSH terminal, `enter` on a run opens its log (the last 200 lines, following new ones while it runs; "Not found" where the web would say so), and `o` there prints the web address (`src/tui/screens/runlog.ts`).
100
101 Build it once and put it on your PATH:
102
103 ```sh
104 bun build --compile src/cli/main.ts --outfile dist/forge
105 ```
106
107 That produces a self-contained binary (it embeds the Bun runtime, so expect
108 something in the 60–110 MB range; `dist/` is gitignored and binaries are never
109 committed). Until you build it, `bun run src/cli/main.ts …` is the same
110 program.
docs/ORIGINS.md +1 −1
@@ -156,7 +156,7 @@ someone else's name with someone else's token.
156156 header (`p` on a row in the terminal, `P` on a repo's home) says "keep this on
157157 my machine", and the dashboard grows an **On your machine** card listing them.
158158 The server remembers only the choice — nothing about the state of a laptop —
159 and `forge sync` (the README's "On your machine") is what acts on it.
159 and `forge sync` (docs/ON-YOUR-MACHINE.md) is what acts on it.
160160
161161 ## Flows
162162
src/tui/screens/dashboard.ts +1 −1
@@ -1413,7 +1413,7 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
14131413 note(s, inner, c, [
14141414 "Nothing pinned yet.",
14151415 "p pins the repository under the cursor;",
1416 'for forge sync, see README "On your machine".',
1416 'for forge sync, see docs/ON-YOUR-MACHINE.md.',
14171417 ]);
14181418 return;
14191419 }
src/web/pages/site.ts +1 −1
@@ -155,7 +155,7 @@ export function downloadsCard(ctx: Ctx, user: User): Html {
155155 ${copyField("forge-sync", "forge sync", "Sync command", "Copy the sync command")}
156156 <span class="row-meta"
157157 >Clones what is missing, fetches the rest, and reports
158 ahead/behind/dirty. See "On your machine" in the README.</span
158 ahead/behind/dirty. See docs/ON-YOUR-MACHINE.md.</span
159159 >
160160 </span>
161161 </div>

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs