spexcode 0.4.1 → 0.5.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.
Files changed (111) hide show
  1. package/README.md +37 -17
  2. package/package.json +9 -5
  3. package/spec-cli/README.md +23 -14
  4. package/spec-cli/hooks/dispatch.sh +7 -5
  5. package/spec-cli/hooks/harness.sh +19 -8
  6. package/spec-cli/src/anchors.ts +110 -18
  7. package/spec-cli/src/claude-headless.ts +271 -0
  8. package/spec-cli/src/cli.ts +112 -38
  9. package/spec-cli/src/client.ts +42 -11
  10. package/spec-cli/src/doctor.ts +174 -36
  11. package/spec-cli/src/gateway-auth.ts +194 -0
  12. package/spec-cli/src/gateway-hub.ts +360 -0
  13. package/spec-cli/src/gateway.ts +27 -19
  14. package/spec-cli/src/git.ts +12 -5
  15. package/spec-cli/src/graph.ts +54 -43
  16. package/spec-cli/src/graphStream.ts +265 -34
  17. package/spec-cli/src/guide.ts +115 -56
  18. package/spec-cli/src/harness-select.ts +20 -6
  19. package/spec-cli/src/harness.ts +295 -39
  20. package/spec-cli/src/help.ts +68 -28
  21. package/spec-cli/src/host.ts +610 -0
  22. package/spec-cli/src/identity-presets.d.ts +13 -0
  23. package/spec-cli/src/identity-presets.js +128 -0
  24. package/spec-cli/src/index.ts +158 -87
  25. package/spec-cli/src/init.ts +53 -25
  26. package/spec-cli/src/layout.ts +6 -5
  27. package/spec-cli/src/lint.ts +25 -121
  28. package/spec-cli/src/login-page.ts +12 -5
  29. package/spec-cli/src/materialize.ts +39 -13
  30. package/spec-cli/src/mentions.ts +16 -11
  31. package/spec-cli/src/message-stream.ts +147 -0
  32. package/spec-cli/src/opencode-headless.ts +95 -0
  33. package/spec-cli/src/opencode.ts +145 -0
  34. package/spec-cli/src/pi-harness.ts +124 -0
  35. package/spec-cli/src/pi-headless.ts +195 -0
  36. package/spec-cli/src/project-identity.ts +73 -0
  37. package/spec-cli/src/pty-bridge.ts +321 -521
  38. package/spec-cli/src/pty-helper.mjs +86 -0
  39. package/spec-cli/src/reaper.ts +16 -8
  40. package/spec-cli/src/reviewSnapshot.ts +23 -0
  41. package/spec-cli/src/reviews.ts +347 -0
  42. package/spec-cli/src/session-timeline.ts +14 -0
  43. package/spec-cli/src/sessions.ts +211 -75
  44. package/spec-cli/src/shim-runtime.ts +169 -0
  45. package/spec-cli/src/slash-commands.ts +76 -0
  46. package/spec-cli/src/source-files.ts +79 -0
  47. package/spec-cli/src/specs.ts +12 -7
  48. package/spec-cli/src/supervise.ts +37 -15
  49. package/spec-cli/src/tree.ts +8 -8
  50. package/spec-cli/src/uninstall.ts +56 -23
  51. package/spec-cli/templates/hooks/prepare-commit-msg +32 -18
  52. package/spec-cli/templates/spec/project/.plugins/commands/extract/spec.md +24 -18
  53. package/spec-cli/templates/spec/project/.plugins/commands/regroup/spec.md +3 -3
  54. package/spec-cli/templates/spec/project/.plugins/commands/rename/spec.md +11 -0
  55. package/spec-cli/templates/spec/project/.plugins/commands/spec.md +22 -8
  56. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +1 -1
  57. package/spec-cli/templates/spec/project/.plugins/commands/tidy/spec.md +29 -11
  58. package/spec-cli/templates/spec/project/.plugins/core/idle/spec.md +1 -1
  59. package/spec-cli/templates/spec/project/.plugins/core/mark-active/spec.md +2 -0
  60. package/spec-cli/templates/spec/project/.plugins/core/session-fail/spec.md +1 -1
  61. package/spec-cli/templates/spec/project/.plugins/core/spec-first/spec-first.sh +28 -39
  62. package/spec-cli/templates/spec/project/.plugins/core/spec-first/spec.md +5 -3
  63. package/spec-cli/templates/spec/project/.plugins/core/spec-of-file/spec.md +1 -1
  64. package/spec-cli/templates/spec/project/.plugins/core/spec.md +2 -2
  65. package/spec-cli/templates/spec/project/.plugins/core/stop-gate/spec.md +7 -1
  66. package/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +2 -2
  67. package/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +5 -5
  68. package/spec-cli/templates/spec/project/.plugins/prompts/spec.md +10 -6
  69. package/spec-cli/templates/spec/project/.plugins/skills/distill/spec.md +38 -50
  70. package/spec-cli/templates/spec/project/.plugins/skills/spec.md +9 -7
  71. package/spec-cli/templates/spec/project/.plugins/spec.md +17 -12
  72. package/spec-cli/templates/spec/project/spec.md +6 -5
  73. package/spec-cli/templates/spexcode.json +10 -2
  74. package/spec-dashboard/dist/assets/Dashboard-C_w_wdk5.js +27 -0
  75. package/spec-dashboard/dist/assets/EvalsPage-5_nfIYll.js +2 -0
  76. package/spec-dashboard/dist/assets/IssuesPage-By-u--95.js +1 -0
  77. package/spec-dashboard/dist/assets/MobileApp-CVEwjHr9.js +2 -0
  78. package/spec-dashboard/dist/assets/Modal-BqgvzMJD.js +1 -0
  79. package/spec-dashboard/dist/assets/PageScroll-B_dKCuXx.js +1 -0
  80. package/spec-dashboard/dist/assets/ProjectsPage-RVP8AqK4.js +1 -0
  81. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +32 -0
  82. package/spec-dashboard/dist/assets/SessionInterface-Bh3vq8SU.js +39 -0
  83. package/spec-dashboard/dist/assets/SessionWindow-BuJ5mzjC.js +13 -0
  84. package/spec-dashboard/dist/assets/Settings-B8KFocsz.js +1 -0
  85. package/spec-dashboard/dist/assets/TimelineChat-K0wdlweB.js +1 -0
  86. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +1 -0
  87. package/spec-dashboard/dist/assets/index-BKaTHjmU.js +41 -0
  88. package/spec-dashboard/dist/assets/index-DcnCaBAC.css +1 -0
  89. package/spec-dashboard/dist/assets/terminalFont-DmTPNdbU.js +1 -0
  90. package/spec-dashboard/dist/index.html +10 -10
  91. package/spec-dashboard/src/reviewFilters.js +279 -0
  92. package/spec-dashboard/src/reviewQuery.js +174 -0
  93. package/spec-dashboard/src/session.js +167 -0
  94. package/spec-eval/src/cli.ts +49 -28
  95. package/spec-eval/src/humanok.ts +1 -1
  96. package/spec-eval/src/matrix.ts +693 -0
  97. package/spec-eval/src/sessioneval.ts +726 -66
  98. package/spec-cli/src/migrate-table.ts +0 -408
  99. package/spec-cli/src/migrate.ts +0 -386
  100. package/spec-dashboard/dist/assets/Dashboard-C_fGmOKK.js +0 -27
  101. package/spec-dashboard/dist/assets/EvalsPage-Cnr1s3bq.js +0 -2
  102. package/spec-dashboard/dist/assets/FoldToggle-x9gtO1OQ.js +0 -1
  103. package/spec-dashboard/dist/assets/IssuesPage-5f_vL-JV.js +0 -1
  104. package/spec-dashboard/dist/assets/MobileApp-DEO1jgGM.js +0 -1
  105. package/spec-dashboard/dist/assets/SessionInterface-CAlbMOFR.js +0 -66
  106. package/spec-dashboard/dist/assets/SessionInterface-DYP7pi_n.css +0 -32
  107. package/spec-dashboard/dist/assets/SessionWindow-JYbpPwNB.js +0 -13
  108. package/spec-dashboard/dist/assets/Settings-DKb5Ji_X.js +0 -1
  109. package/spec-dashboard/dist/assets/index-BQu-oJ8J.js +0 -41
  110. package/spec-dashboard/dist/assets/index-BbMkwuix.css +0 -1
  111. package/spec-dashboard/dist/assets/launch-BM9GgvkX.js +0 -6
@@ -14,7 +14,8 @@
14
14
  type Entry = { line: string; body: string; see?: string }
15
15
 
16
16
  const SEL_NOTE = `SEL = session id (or unique id-prefix) | node id | branch — every session read/control verb
17
- accepts any of the three; on the list verbs (ls/watch), none (or @all) means every session.`
17
+ accepts any of the four; inside a session worktree, . means that worktree's session. On the list verbs (ls/watch),
18
+ none (or @all) means every session.`
18
19
 
19
20
  const ROUTING_NOTE = `Backend routing: every backend-touching verb accepts --api <url> (--port <n> = localhost sugar) to name
20
21
  its backend explicitly — the flag always wins. Bare, it resolves: worker env / the cwd project's live
@@ -24,9 +25,9 @@ const DOT_NOTE = `\`.\` as a node argument means the node THIS worktree works on
24
25
  node/<id> branch). One-shot payload reads (graph · spec search · session ls/show/review · eval ls ·
25
26
  scenario ls · issue ls/show/links) take --json.`
26
27
 
27
- const MENTION_NOTE = `Mentions: @session · [[node]] · @new work in ANY prompt, issue, or remark body — text passed as a CLI
28
- arg included. [[node]] names the topic node; @session hands the text to that live agent; @new spawns a
29
- fresh worker on the thread's node.`
28
+ const MENTION_NOTE = `Mentions: @session · [[node]] · @new / @new:<launcher> work in ANY prompt, issue, or remark body —
29
+ text passed as a CLI arg included. [[node]] names the topic node; @session hands the text to that live agent;
30
+ @new spawns a fresh worker on the thread's node (bare = configured default; :<launcher> = that named profile).`
30
31
 
31
32
  const ENTRIES: Record<string, Entry> = {
32
33
  // ── project verbs (implicit object = this project) ────────────────────────
@@ -44,12 +45,14 @@ derived status, title, and attention badges (drift:N · stale:N · issues:N · g
44
45
  see: 'spex spec search (find one node by intent) · spex session ls (just the sessions, as a table)',
45
46
  },
46
47
  init: {
47
- line: 'init [dir] adopt SpexCode on a repo: seed .spec + hooks + materialize [--preset name]',
48
- body: `Usage: spex init [dir=cwd] [--preset default]
48
+ line: 'init [dir] adopt SpexCode on a repo: seed .spec + hooks + materialize --harness <ids> [--preset name]',
49
+ body: `Usage: spex init [dir=cwd] --harness <id[,id]|plugin:<folder>> [--preset default]
49
50
 
50
51
  Scaffolds adoption in one shot: seeds a starter .spec tree (project root + .plugins plugins), plants
51
52
  spexcode.json, installs the git hooks, and materializes the harness artifacts (contract block +
52
- shims). Additive never overwrites your files. --preset picks the .plugins plugin tier (cumulative).
53
+ shims). --harness is REQUIRED the explicit choice of which harnesses materialize delivers into
54
+ (stamped as spexcode.json "harnesses"; only their launchers are seeded); a pre-existing "harnesses"
55
+ field satisfies it. Additive — never overwrites your files. --preset picks the .plugins plugin tier (cumulative).
53
56
  Footprint is fixed: materialized artifacts are never tracked — hidden via the per-clone .git/info/exclude, with
54
57
  a tracked/mixed CLAUDE.md/AGENTS.md covered by the clean/smudge filter (see spex guide footprint).`,
55
58
  see: 'spex guide (the full setup workflow) · spex uninstall (the inverse) · spex spec lint (adoption TODO)',
@@ -69,24 +72,26 @@ hooks yet (CI, a cloud agent): generated and excluded, the artifacts never arriv
69
72
  see: 'spex doctor (verify the materialized artifacts actually reach an agent)',
70
73
  },
71
74
  doctor: {
72
- line: 'doctor diagnose whether the workflow actually reaches this agent [--contract|--conflicts]',
73
- body: `Usage: spex doctor per-layer coverage report: preconditions · git-hook floor · contract ·
74
- hooks + handler existence · backend for every harness materialize delivers to
75
+ line: 'doctor diagnose spec health and whether the workflow reaches this agent [--contract|--conflicts]',
76
+ body: `Usage: spex doctor spec-health findings + delivery report: preconditions · git-hook floor ·
77
+ contract · hooks + handler existence · backend · footprint
75
78
  spex doctor --contract print the composed surface:system text any agent here reads
76
79
  spex doctor --conflicts detect double-delivery (loose artifacts beside the managed ones)
77
80
 
78
- Run it when a worker seems to be missing its contract or hooks — it names the broken layer and the
79
- repair, instead of you diffing materialized files by hand.`,
80
- see: 'spex materialize (re-materialize the artifacts doctor checks)',
81
+ Bare doctor is the opt-in, read-only health surface: it reports altitude and breadth findings without
82
+ putting them in the lint gate, then audits workflow delivery. Run it directly or let the tidy workflow
83
+ consume the same visible diagnosis.`,
84
+ see: 'spex spec lint (deterministic graph/contract gate) · spex materialize (repair delivery artifacts)',
81
85
  },
82
86
  uninstall: {
83
- line: 'uninstall [dir] surgical inverse of init removes generated artifacts, keeps your .spec [--hooks]',
87
+ line: 'uninstall [dir] remove all derived artifacts + local state; preserve tracked intent [--hooks]',
84
88
  body: `Usage: spex uninstall [dir=cwd] [--hooks]
85
89
 
86
- Removes every SpexCode-GENERATED artifact (harness shims · contract blocks · trust entries ·
87
- exclude/ignore blocks · global store · plugin bundle) and never your .spec/.plugins data or your own
88
- prose. Git hooks are preserved unless --hooks.`,
89
- see: 'spex init (re-adopt later your .spec survives)',
90
+ Removes all SpexCode-derived wiring and project-local state: contract blocks, harness shims,
91
+ generated skills/agents, plugin bundles, trust/filter/exclude entries, and the global per-project
92
+ store. Your tracked intent (.spec including .plugins, plus spexcode.json) and surrounding user prose
93
+ are preserved. Git hooks remain unless --hooks; that flag removes only unmodified canonical copies.`,
94
+ see: 'spex init (re-adopt later — your tracked intent survives)',
90
95
  },
91
96
  serve: {
92
97
  line: 'serve [api|ui] api (default) = the backend :8787 · ui = the dashboard :5173 on top of it',
@@ -106,6 +111,30 @@ projects coexist on one host). Loopback-only by default; --host 0.0.0.0 opens it
106
111
  still plain HTTP with no gate, so bind wide only on a network you trust.`,
107
112
  see: 'GET /health (backend liveness probe)',
108
113
  },
114
+ dashboard: {
115
+ line: 'dashboard ONE dashboard for every project you serve — no --api-port pairing [--port N=5173]',
116
+ body: `Usage: spex dashboard [--port N=5173] [--host H=127.0.0.1]
117
+
118
+ The HOST gateway: serves the built dashboard once and routes to EVERY backend the current user runs —
119
+ the multi-project hub engine plus the host registry on top. It continuously reconciles the per-project
120
+ endpoint records each \`spex serve\` publishes (validating each against the live backend's /api/instance
121
+ identity), keeps a durable known-project catalog, and proxies each project's API + SSE + terminal
122
+ socket under /p/<projectId>/* — the project is named in the path, so nothing is "current" and no
123
+ pairing flag exists.
124
+
125
+ Admin surface (hub-authorized: implicit from loopback until an admin password is set, then cookie
126
+ sessions): GET /projects (the validated list + gating state) · GET /projects/stream (SSE) ·
127
+ GET /projects/browse?path=… (read-only host folder picker) · POST /projects {root, initGit?, init?}
128
+ (explicit setup, then register) · GET|PUT /projects/<id>/config (raw portable
129
+ spexcode.json, revision-guarded) · POST /projects/<id>/init|doctor|serve
130
+ (run the real \`spex init\`/\`spex doctor\`, or start an offline project's backend, detached — a
131
+ backend never depends on this gateway staying up) · PUT|DELETE /projects/admin-password and
132
+ /projects/<id>/password (the gates). A gated project answers /p/<id>/login with the designed page.
133
+
134
+ Loopback-only by default; --host widens the bind — the admin surface stays locked to loopback until
135
+ an admin password exists, and ungated projects serve open.`,
136
+ see: 'spex serve (each project\'s backend) · spex serve ui (explicit one-backend pairing)',
137
+ },
109
138
 
110
139
  // ── the noun drawers ──────────────────────────────────────────────────────
111
140
  spec: {
@@ -131,7 +160,7 @@ commit since the spec's version touched an ANCHORED unit's lines, unacked — th
131
160
  drift; same-file selectors OR'd, one error naming the hit selectors) · one-govern (a node
132
161
  governs >1 DISTINCT file) · living (a "## vN" changelog heading) · id-format (an
133
162
  id char outside the whitelist — ascii [a-z0-9-] or a non-ascii unicode letter/number, CJK ok — or a
134
- leaf id reused) · mention (a [[id]] naming no node). Warns: altitude · breadth · coverage · drift
163
+ leaf id reused) · mention (a [[id]] naming no node). Warns: coverage · drift
135
164
  (UNANCHORED drift — always advisory, never blocks; on a scoped file's MISS, \`lint.scopedCodeMiss:
136
165
  "ignore"\` may silence it) · anchor (anchoring a type) · related-drift (a scoped related row warns
137
166
  per selector HIT, misses silent) · owners (whole-file governors only; scoped don't count) ·
@@ -147,10 +176,10 @@ edit the spec instead — same commit as the code.`,
147
176
  session: {
148
177
  line: 'session <verb> the worktree state machine: new · ls · watch · wait · review · merge · send · …',
149
178
  body: `Manager verbs (dispatch, monitor, land):
150
- spex session new "<prompt>" [--prompt-file <path>|-] [--node <id>] [--launcher <name>]
179
+ spex session new "<prompt>" [--prompt-file <path>|-] [--launcher <name>]
151
180
  Launch a worker in its own node worktree. Give it ONLY its task — the dev-flow contract
152
- reaches it through the materialized system prompt. --node (or the prompt's first [[id]],
153
- same effect) binds the session to that node. --prompt-file <path>|- carries a long prompt
181
+ reaches it through the materialized system prompt. The prompt's first [[id]] mention binds
182
+ the session to that node. --prompt-file <path>|- carries a long prompt
154
183
  without shell quoting (exclusive with the inline prompt). Then MONITOR it (wait/watch below).
155
184
  spex session ls [SEL…] [--status a,b] [--json] one-shot table of living sessions
156
185
  spex session watch [SEL…] [--as NAME] [--idle] [--interval N=5]
@@ -173,6 +202,7 @@ edit the spec instead — same commit as the code.`,
173
202
 
174
203
  Control another session (all take SEL):
175
204
  spex session send <SEL> "<msg>" deliver a message (fail-loud: a dead dispatch exits non-zero)
205
+ spex session interrupt <SEL> hard-interrupt the current turn through native harness control
176
206
  spex session send <SEL> --keys "<keys>"
177
207
  LAST RESORT: raw nav-mode keystrokes to a TUI dialog ("Up Up Enter", C-/M-/S- combos). The raw
178
208
  key surface is UNSTABLE and can confirm dangerous dialogs — don't reach for it unless a plain
@@ -196,21 +226,22 @@ Human escape hatch:
196
226
  show --capture / send. LOCAL-only (fails loud on a remote backend).
197
227
 
198
228
  ${SEL_NOTE}
199
- Manager verbs that WRITE (send/rename/resume/stop/close/merge) are PROJECT-BOUND: a backend serving
229
+ Manager verbs that WRITE (send/interrupt/rename/resume/stop/close/merge) are PROJECT-BOUND: a backend serving
200
230
  another project's repo refuses loudly — name the target with --api <url> to drive it on purpose.
201
231
  ${MENTION_NOTE}`,
202
232
  see: 'spex eval ls --session <SEL> (the session’s measured loss) · spex help eval',
203
233
  },
204
234
  eval: {
205
- line: 'eval <verb> the measurement system: add · ls · scenario ls · lint · ok · retract · clean',
235
+ line: 'eval <verb> the measurement system: add · ls · scenario ls · matrix · lint · ok · retract · clean',
206
236
  body: `Usage: spex eval add [<node>|.] [--scenario <name>] (--pass|--fail) [--note <text>]
207
237
  [--image <png> …repeatable] [--result <path|->] [--video <webm|mp4>] [--timeline <json>]
208
238
  spex eval ls [<node>|.] [--json] a node's eval timeline, newest first
209
239
  spex eval ls --session <SEL> [--json] a session's aggregate: its changed nodes' scores
210
240
  spex eval ls --session <SEL> --export [--open | --out <path>]
211
241
  spex eval scenario ls [<node>|.] [--unmeasured] [--json] declared scenarios; bare = every node
242
+ spex eval matrix <launcher> [--node <id>] [--rows k1,k2] the harness live-behavior matrix
212
243
  spex eval lint [--changed] measurement-layer findings (advisory, always exit 0)
213
- spex eval ok <node> [--scenario <name>] the HUMAN sign-off on the scenario's latest reading
244
+ spex eval ok <node> [--scenario <name>] the HUMAN sign-off on the scenario's latest measurement
214
245
  spex eval retract [<node>|.] [--scenario <name>] [--last | --ts <iso>] [--note <why>]
215
246
  spex eval clean [--keep-latest | --all] GC the content-addressed evidence cache
216
247
 
@@ -227,16 +258,22 @@ HTML artifact (diff · evidence inlined · gates) for CI/sharing.
227
258
  scenario ls — the DECLARED contracts (name · tags · normalized test reference · latest verdict), no evals: bare lists every
228
259
  measurable node's scenarios; --unmeasured keeps only the never-measured — the blind-spot worklist.
229
260
 
261
+ matrix — run the eight-row harness live-behavior matrix against a REAL dispatched session of the named
262
+ launcher (the harness-adapter acceptance rule, defined once in spec-eval/src/matrix.ts): it syncs the
263
+ rows into the \`<harness>-harness\` node's eval.md scenarios, drives one worker through undeclared-stop ·
264
+ pretooluse-block · ask-note · deliver-steer · resume · liveness · commit-gate · close-residue, and files
265
+ a per-row measurement with its evidence transcript. A new harness needs only its launcher + spec node.
266
+
230
267
  lint — the measurement layer's findings: malformed eval.md (eval-schema) · unmeasured (eval-missing) ·
231
268
  stale (eval-drift) · orphaned remark tracks (eval-dangling) · governed source with no eval.md
232
269
  (eval-coverage — the same name and shape as spec lint's coverage, one rule per layer) · over-owned
233
270
  files (eval-owners). --changed scopes to the nodes THIS branch touched. spec lint's errors block
234
271
  commits; eval lint is PURE ADVISORY, always exit 0 — a measurement gap never blocks anyone.
235
272
 
236
- ok — the human's reviewed-and-agreed mark on the scenario's LATEST reading: an appended, monotonic
237
- sign-off bound to that one immutable reading (a newer reading or staleness releases it on its own —
273
+ ok — the human's reviewed-and-agreed mark on the scenario's LATEST measurement: an appended, monotonic
274
+ sign-off bound to that one immutable measurement (a newer measurement or staleness releases it on its own —
238
275
  no un-ok exists). The evals feed default-hides a fresh, ok'd scenario; a governed session is refused
239
- (an agent's judgment on a reading is a remark, never a self-blessing).
276
+ (an agent's judgment on a measurement is a remark, never a self-blessing).
240
277
 
241
278
  retract — the sanctioned undo for a botched filing: APPENDS a retraction event (traceable, never
242
279
  deletes a line); the previous eval becomes latest again, or the scenario honestly returns to
@@ -324,6 +361,8 @@ Machine plumbing — called by generated hooks and launch scripts, never typed b
324
361
  nudge <node> the post-merge hook prints the issue nudge for a merged node
325
362
  codex-launch <sock> <cwd> [prompt…] backend-owned codex thread/start + first turn (launch script)
326
363
  codex-turn <sock> <threadId> <text…> fire a follow-up turn on an owned thread (tests/scripts)
364
+ claude-headless-run <id> <runtime> <cmd> -- <tail…> resident stream-json controller (launch script)
365
+ pi-headless-run <id> <runtime> <cmd> -- <tail…> resident pi text-mode controller (launch script)
327
366
 
328
367
  If you reached for one of these by hand, the porcelain you want is probably elsewhere: the trunk
329
368
  name also lives at GET /api/settings (.layout); sessions are driven with spex session new / session send;
@@ -363,6 +402,7 @@ Project verbs (implicit object = this project)
363
402
  ${ENTRIES.doctor.line}
364
403
  ${ENTRIES.uninstall.line}
365
404
  ${ENTRIES.serve.line}
405
+ ${ENTRIES.dashboard.line}
366
406
 
367
407
  Noun drawers
368
408
  ${ENTRIES.spec.line}